CTF walkthrough, Ethernaut, #15 Gatekeeper Two

The goal is the same – pass through 3 gates and register as an entrant.

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

contract GatekeeperTwo {
    address public entrant;

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

    modifier gateTwo() {
        uint256 x;
        assembly {
            x := extcodesize(caller())
        }
        require(x == 0);
        _;
    }

    modifier gateThree(bytes8 _gateKey) {
        unchecked {
            require(
                uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^
                    uint64(_gateKey) ==
                    uint64(0) - 1
            );
        }
        _;
    }

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

Analysis & Solution

Gate 1

We already know how to pass the gateOne – by using an intermediate contract.

Gate 2

 modifier gateTwo() {
    uint256 x;
    assembly {
        x := extcodesize(caller())
    }
    require(x == 0);
    _;
}

Gate 3

modifier gateThree(bytes8 _gateKey) {
    unchecked {
        require(
            uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^
                uint64(_gateKey) ==
                uint64(0) - 1
        );
    }
    _;
}

References