DEV Community

Cover image for Vault - Level 08
Stefan Alfbo
Stefan Alfbo

Posted on

Vault - Level 08

Problem statement

Unlock the vault to pass the level!

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

Solution

Start with creating a new contract for the current level by clicking on the button, Get new instance. Remember to have enough eth in the connected wallet and that it's connected to the Sepolia network.

Open up the developer tool in your browser (F12) and in the console write this code.

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

With the password in our hands we can call the unlock function on the contract.

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

Sign the transaction and wait for it to go through. Finish up the challenge by clicking on the button, Submit instance, to commit and update the progress on the ethernaut contract.

Explanation

It's important to know that everything is readable on the blockchain, unless you encrypt the data to obfuscate it.

The lesson here is to understand the visibility of state variables in a Solidity contract. private only defines that other contracts can't read or modify the state variable. From the Solidity documentation we can also read the following warning:

Making something private or internal only prevents other contracts from reading or modifying the information, but it will still be visible to the whole world outside of the blockchain.

With this knowledge we only need to figure out how to read the value from the deployed contract. There is a JSON-RPC API implemented in each Ethereum node in order for applications to interact with Ethereum, more about the API can be found here. Thankfully we have access to the web3js library on the web application which enables us to use this API.

By using the function, getStorageAt, that is defined in the API we can read the value from a storage position at a given address.

web3.eth.getStorageAt(address, position [, defaultBlock] [, callback])
Enter fullscreen mode Exit fullscreen mode

web3js has the details about the parameters needed for the function. By using the function like this.

await web3.eth.getStorageAt(contract.address, 0)
Enter fullscreen mode Exit fullscreen mode

We will get the first position of the storage. Since the contract looks like this.

contract Vault {
  bool public locked;
  bytes32 private password;
Enter fullscreen mode Exit fullscreen mode

Its not unreasonable to think that 0 points to the variable locked. We can check it by converting the hex number in the result to a number and then cast it to a boolean.

Boolean(await web3.eth.getStorageAt(contract.address, 0).then(r => web3.utils.hexToNumber(r)))
Enter fullscreen mode Exit fullscreen mode

The output of that code seems correct. If we do the same exercise with the next position. This time we will convert it to a ASCII string instead, since we think it's the password state variable.

await web3.eth.getStorageAt(contract.address, 1).then(r => web3.utils.hexToAscii(r))
Enter fullscreen mode Exit fullscreen mode

That gives us the string, A very strong secret password :), which seems to be exactly what we are looking for.

Now this code looks breakable for us.

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

We have the password so now there is no problem to fulfill the condition to set the private state variable, locked, to false.

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

In the solution section we stored the password in the hex format received from the getStorageAt call, so there is no need here to convert the variable to bytes32 which is the type expected by the function.

Last, the take away from the level author.

Take away

Resources

  • Vault - This challenge
  • Remix - Web based IDE for Solidity
  • Solidity - Solidity documentation

Top comments (0)