Solidity — How Was I Able To Hack OpenZepplin Challange Level 19 — Alien Codex

Stav Alfi
4 min readApr 11, 2023

Well that was the hardest so far.

Let’s get into it.

Level: https://ethernaut.openzeppelin.com/level/19

The easy part:

  1. understanding the task — make your self the owner of the contract
  2. be able to call all the functions — just call `make_contact` and you are good to go.

The hard part:

How to acomplish the task — this contract looks solid.

Storage Layout

Recap: all the solidity class fields are saved in a sequental order (except for dynamic arrays and maps).

So our contract has 3 properties:

  1. address ownable (I’m guessing it doesn’t have anything else in the Ownable-05.sol file)
  2. bool public contact
  3. bytes32[] public codex

I won’t explain here about memory slots (go and read about it if you don’t know what that are).

The first 2 fields share the first slot: 0.

The third property is located in the second slot: 1. But it’s a dynamic array so in the second slot, the value is just the length of the array. After we made an overflow (calling `retract`), the second slot should contrain the value: 0-1 = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff.

Important to note that array length is always uint256 so it will always take a full slot.

Reading the slots value:

curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_getStorageAt", "params": ["0x2ACDe8bc8567D49CF2Fe54999d4d4A1cd1a9fFEA", "0x0", "latest"], "id": 1}' http://localhost:8547

the 0x0 is the slot number you want to read.

In solidity, all the fields (static or dynamic) are located from address 0x000…0 (0) to 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff (2²⁵⁶-1).

Slot’s are just addresses in memory!

So the ownable field is located at address 0x000.

So if we could change the value of slot 0, we could modify who the owner is.

But how?

mmmm… need more clues?

More Clues

Everytime you see that, think about over/undeflows: uint256(0–1) === ²²⁵⁶-1

How is that going to help us? Search for places in the code that has a potential over/underflows:

By calling it, the length of the array will be equal to ²²⁵⁶-1.

So it means that now the array is covering all the memory of our solidity program!

We can use the following function to override any memory we want:

Now the question is, what should be i so I can reach any memory we want.

Well, arrays cells are located sequentally so we need to understand the address of the first cell of the array:

Source: https://programtheblockchain.com/posts/2018/03/09/understanding-ethereum-smart-contract-storage/

In simple words, the address of the first cell of an array is: keccak256(slot-of-the-array)

Let’s use `chisel` (solidity CLI tool provided by https://github.com/zenstruck/foundry) to run some commands:

Now we know that the array located in 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6.

So how much cells we need to move until we reach cell zero (0x000..0)?

Step by step:

  1. We are at: 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

2. We need to react 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

3. Then we need to add 1 so there will be an overflow and we will be at address 0x000…0

How do we solve 2? 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff — 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6=0x4ef1d2ad89edf8c4d91132028e8195cdf30bb4b5053d4f8cd260341d4805f309

How do we solve 3? 0x4ef1d2ad89edf8c4d91132028e8195cdf30bb4b5053d4f8cd260341d4805f309+1 = 0x4ef1d2ad89edf8c4d91132028e8195cdf30bb4b5053d4f8cd260341d4805f30a

All in all, if we reach cell number 0x4ef1d2ad89edf8c4d91132028e8195cdf30bb4b5053d4f8cd260341d4805f30a, we can modify the first solidity slot where the ownable field is.

But before doing the following:

We need to remember that the first slot contain 2 fields:

  1. address ownable (I’m guessing it doesn’t have anything else in the Ownable-05.sol file)
  2. bool public contact

So we should run:

My player address is 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

Why the `contact` field is located before the `ownable`? I will leave that to you for additional reading.

— —

To see the differnce between the value of the first slot before and after the change:

My player address is 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "eth_getStorageAt", "params": ["0x2ACDe8bc8567D49CF2Fe54999d4d4A1cd1a9fFEA", "0x0", "latest"], "id": 1}' http://localhost:8547

The 0x0 is the slot number you want to read.

Enjoy!

--

--