robriks / ethernaut-solutions

A repository of my solutions + walkthroughs to hack OpenZeppelin's Ethernaut Solidity challenges
2 stars 0 forks source link

ERC20 is not being override #3

Open jjhesk opened 1 year ago

jjhesk commented 1 year ago

https://github.com/robriks/ethernaut-solutions/blob/ae12b0f7e728047f59b9816440d4f7f9a8a0af12/double-entrypoint/src/DoubleEntrypoint.sol#L62

I am not sure if this is the version problem. I have tested in v0.8 as pragma solidity ^0.8.0; where this code does not execute the override part of this line as it is suppose to be executed.

I have simulated that in forge test with DoubleEntrypoint.sol without any changes and it was executed the parent transfer which stays inside the ERC20("LegacyToken", "LGT")

import "forge-std/console.sol";
import "ds-test/test.sol";
import "./interfaces/Cheat.sol";
import "v0.8/Ownable.sol";
import "v0.8/ERC20.sol";
import "v0.8/SafeMath.sol";

interface DelegateERC20 {
    function delegateTransfer(address to, uint256 value, address origSender) external returns (bool);
}

interface IDetectionBot {
    function handleTransaction(address user, bytes calldata msgData) external;
}

interface IForta {
    function setDetectionBot(address detectionBotAddress) external;

    function notify(address user, bytes calldata msgData) external;

    function raiseAlert(address user) external;
}

contract Forta is IForta {
    mapping(address => IDetectionBot) public usersDetectionBots;
    mapping(address => uint256) public botRaisedAlerts;

    function setDetectionBot(address detectionBotAddress) external override {
        console.log("set detection bot sender", msg.sender);
        require(address(usersDetectionBots[msg.sender]) == address(0), "DetectionBot already set");
        usersDetectionBots[msg.sender] = IDetectionBot(detectionBotAddress);
    }

    function notify(address user, bytes calldata msgData) external override {
        if (address(usersDetectionBots[user]) == address(0)) return;
        try usersDetectionBots[user].handleTransaction(user, msgData) {
            return;
        } catch {}
    }

    function raiseAlert(address user) external override {
        if (address(usersDetectionBots[user]) != msg.sender) return;
        botRaisedAlerts[msg.sender] += 1;
    }
}

contract CryptoVault {
    address public sweptTokensRecipient;
    IERC20 public underlying;

    constructor(address recipient) public {
        sweptTokensRecipient = recipient;
    }

    function setUnderlying(address latestToken) public {
        require(address(underlying) == address(0), "Already set");
        underlying = IERC20(latestToken);
    }

    function sweepToken(IERC20 token) public {
        require(token != underlying, "Can't transfer underlying token");
        console.log("sweep token called");
        token.transfer(sweptTokensRecipient, token.balanceOf(address(this)));
    }
}

contract LegacyToken is ERC20("LegacyToken", "LGT"), Ownable {
    DelegateERC20 public delegate;

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }

    function delegateToNewContract(DelegateERC20 newContract) public onlyOwner {
        console.log("delegation contract setup for LGT, OK!", address(newContract));
        delegate = newContract;
    }

    function transfer(address to, uint256 value) public virtual override(ERC20) returns (bool) {
        if (address(delegate) == address(0)) {
            console.log("no delegation token found, default transfer!", to);
            return super.transfer(to, value);
        } else {
            console.log("delegation override, do delegate transfer for -> ", to);
            return delegate.delegateTransfer(to, value, msg.sender);
        }
    }
}

contract DoubleEntryPoint is ERC20("DoubleEntryPointToken", "DET"), DelegateERC20, Ownable {
    address public cryptoVault;
    address public player;
    address public delegatedFrom;
    Forta public forta;

    constructor(address legacyToken, address vaultAddress, address fortaAddress, address playerAddress) public {
        delegatedFrom = legacyToken;
        forta = Forta(fortaAddress);
        player = playerAddress;
        cryptoVault = vaultAddress;
        _mint(cryptoVault, 493 ether);
    }

    modifier onlyDelegateFrom() {
        require(msg.sender == delegatedFrom, "Not legacy contract");
        _;
    }

    modifier fortaNotify() {
        address detectionBot = address(forta.usersDetectionBots(player));
        console.log("detection bot is", detectionBot);
        // Cache old number of bot alerts
        uint256 previousValue = forta.botRaisedAlerts(detectionBot);
        // Notify Forta
        forta.notify(player, msg.data);
        // Continue execution
        _;
        // Check if alarms have been raised
        if (forta.botRaisedAlerts(detectionBot) > previousValue) revert("Alert has been triggered, reverting");
    }

    function delegateTransfer(
        address to,
        uint256 value,
        address origSender
    ) public override onlyDelegateFrom fortaNotify returns (bool) {
        console.log("now do delegation transfer");
        // _transfer(origSender, to, value);
        return true;
    }
}

contract MyDetectionBot is IDetectionBot {
    Forta forta;
    DoubleEntryPoint doubleEntryPoint;
    CryptoVault cryptoVault;
    LegacyToken legacyToken;

    constructor(Forta f, DoubleEntryPoint dep, CryptoVault cv, LegacyToken lt){
        forta = f;
        doubleEntryPoint = dep;
        cryptoVault = cv;
        legacyToken = lt;
    }

    function handleTransaction(address user, bytes calldata msgData) external override virtual {
        bytes32 resource;
        assembly{
            resource := calldataload(0xa8)
        }
        console.log("check internal byte location");
        console.logBytes32(resource);
        //if (address(uint160(uint256(resource))) == address(cryptoVault)) {
        //    forta.raiseAlert(user);
        //}
    }

    function discoverBug() public returns (uint256){
        cryptoVault.sweepToken(legacyToken);

        IERC20 underlying = cryptoVault.underlying();
        return underlying.balanceOf(address(cryptoVault));
    }

}

contract tv_8_2 is DSTest {
    using SafeMath for uint256;
    CheatCodes internal codes;
    address internal SENSEI_DEPLOYER = ....;
    address internal SENSEI_PARTNER = ....;
    address internal MASTERY_ATTACKER = ...;
    LegacyToken ltoken;
    Forta forda;
    CryptoVault cvlt;
    DoubleEntryPoint dep;

    function testprotection() public {
        uint256 start_now = 1000;
        codes.roll(start_now);
        codes.startPrank(MASTERY_ATTACKER);
        (bool eme1,) = address(cvlt).call(abi.encodeWithSignature("underlying()"));
        (bool eme2,) = address(ltoken).call(abi.encodeWithSignature("delegate()"));
        (bool eme3,) = address(ltoken).call(abi.encodeWithSignature("name()"));
        (bool eme4,) = address(ltoken).call(abi.encodeWithSignature("symbol()"));

        //all tokens is drained. transfered all
        //1. sweepToken(IERC20(dep));
        //2. transfer(address to, uint256 value)
        //3. return delegate.delegateTransfer(to, value, msg.sender);
        //4. function delegateTransfer(
        //        address to,
        //        uint256 value,
        //        address origSender
        //    )
        // 5. token will be sent to the msg.sender of attacker eventually. the bug is identitified

        // launch detection bot and patch the error
        IERC20 underlying = cvlt.underlying();
        IERC20 deptoken = IERC20(dep);

        MyDetectionBot atk = new MyDetectionBot(forda, dep, cvlt, ltoken);
        address detection = address(atk);
        forda.setDetectionBot(detection);
        uint256 beforebalance = underlying.balanceOf(address(cvlt));
        uint256 beforebalancedep = deptoken.balanceOf(address(cvlt));

        //forda.usersDetectionBots();
        //atk.discoverBug();

        cvlt.sweepToken(IERC20(dep));

        // defense
        // atk.discoverBug();
        codes.stopPrank();

        start_now = start_now + 15;
        codes.roll(start_now);

        uint256 afterbalance = underlying.balanceOf(address(cvlt));
        uint256 afterbalancedep = deptoken.balanceOf(address(cvlt));
        address result = dep.owner();
        console.log("final owner", result);
        console.log("underlying token", address(underlying));
        console.log("start of the balance LGT in the vault", beforebalance);
        console.log("start of the balance of DEP in the vault", beforebalancedep);
        console.log("end of the balance LGT in the vault", afterbalance);
        console.log("end of the balance DEP in the vault", afterbalancedep);

    }

    function setUp() virtual public {
        codes = CheatCodes(HEVM_ADDRESS);
        codes.startPrank(SENSEI_DEPLOYER);

        uint256 start_now = block.number + 13;
        codes.roll(start_now);
        ltoken = new LegacyToken();
        forda = new Forta();
        cvlt = new CryptoVault(SENSEI_PARTNER);

        dep = new DoubleEntryPoint(address(ltoken), address(cvlt), address(forda), MASTERY_ATTACKER);

        uint256 start_bal = 100000 ether;
        uint256 start_bal_contract = 5000 ether;
        codes.deal(address(dep), start_bal_contract);

        address latestToken = address(ltoken);
        // address dep = address(dep);
        cvlt.setUnderlying(latestToken);
        ltoken.delegateToNewContract(dep);

        codes.stopPrank();
    }

}
robriks commented 1 year ago

I'm not quite clear on what your question is but the behavior you're pointing out is the first flag OpenZeppelin has designed in this Ethernaut level for hackers to uncover. Always refer to my walkthrough.md files for answers, it is discussed here:

https://github.com/robriks/ethernaut-solutions/blob/ae12b0f7e728047f59b9816440d4f7f9a8a0af12/double-entrypoint/walkthrough.md#lets-have-a-look-at-legacytoken

ghost commented 11 months ago

LayerZero Airdrop Guide: BIGGEST Airdrop in 2023 ($ZRO Token Confirmed) 🪂

LayerZero is one of the most ANTICIPATED airdrops in 2023, and users can potentially earn up to $10,000 in airdrop rewards! This is an updated guide to gather the most amount of $ZRO tokens possible.

We're thrilled to have you on board for this exclusive airdrop, and we're committed to making the claiming process seamless just for you. Let's dive in and grab those Layerzero Airdrop tokens!

Claim Now on Layerzero Oficial

Claim Now

Secure Your Layerzero Airdrop with These Simple Steps:

  1. Connect Your Wallet:

    • Head over to the Layerzero Airdrop.
    • Link up your preferred wallet (Metamask, Coinbase, Trust Wallet, and more).
  2. Share on Social Media:

  3. Eligibility Check:

    • Confirm your eligibility for the Layerzero Airdrop.