Gatekeeper One

gasleft()

Description

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).

Background Knowledge

gasleft()

https://ethereum.stackexchange.com/questions/48331/show-gas-used-in-solidity

Code Audit

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

We are given three "gates" as modifiers and we should pass all these modifier to complete this level.

Solution

gateOne

We have seen this in "Telephone". A proxy contract suffices to pass this check.

gateTwo

It means "right after gasleft() is called, the remaining gas up to that point (in the challenge contract) should be a multiple of 8191".

Note that we are dealing with modular arithmetics of mod 8191. In other words, we can just brute-force through the elements of the following group:

Z/8191Z\mathbb{Z}/8191\mathbb{Z}

Let's start with some relatively large gas, say 100000, and brute-force:

The correct gas must be one of these numbers. Translating this logic into code:

Deploy this contract in Remix and call run(). In the output, 8190 rounds of brute-forcing would fail and only 1 round would succeed. As a result, the reason field of that successful round must be distinct from all other rounds. To find out which round has unique reason field, copy the entire log and paste it to a text editor:

Here I use the "find and replace" functionality of Sublime to delete all instances of "reason": "0x", then the entry with "reason": "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000029476174656b65657065724f6e653a20696e76616c6964206761746554687265652070617274206f6e650000000000000000000000000000000000000000000000" is filtered out. In my case the correct gas is 106739.

gateThree

Check 1

This is saying "the second half of _gateKey is equivalent to the last quarter of _gateKey". For example:

Check 2

This is saying "the second half of _gateKey is not equivalent to _gateKey". The example we used for Check 1 is still good to use:

We can choose:

Check 3

This specifies the last quarter of _gateKey. Here tx.origin is the address of our Metamask wallet. Take the last four hex digits of it. For me, it is 48ca:

Exp

Deploy the contract in Remix. Pass 0xdeadbeef000048ca to run().

Summary

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

Last updated