Ethernaut#8 - Vault

Ethernaut #8 - Vault

#8

Unlock the vault to pass the level!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 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;
}
}
}

Contract Breakdown

Let us breakdown the contract to understand what each function and piece of code does. The code consists of a single contract Vault compiled with solidity version ^0.8.0. It is a fairly simple contract.

Variables
1
2
bool public locked;
bytes32 private password;

locked is a public boolean that indicates whether the vault is locked or not. password is a private variable of type bytes32.

Constructor
1
2
3
4
constructor(bytes32 _password) {
locked = true;
password = _password;
}

The constructor sets locked to true and stores the _password that is passed during deployment.

Functions
1
2
3
4
5
function unlock(bytes32 _password) public {
if (password == _password) {
locked = false;
}
}

The unlock function takes a bytes32 _password and compares it to the stored password. If they match, it sets locked to false.

Solution

The key to this challenge is understanding what private actually means in Solidity.

Marking a variable as private does NOT mean it is hidden or secret. It only means other contracts cannot read it directly through Solidity. All data on the blockchain is publicly visible. Anyone can read the raw storage of any contract.

Every piece of data stored on the Ethereum blockchain is transparent. The private keyword only restricts access at the Solidity level (other contracts can’t call it), but the actual storage slots can be read by anyone using tools like web3.eth.getStorageAt.

The contract has two state variables. locked is a bool and occupies storage slot 0. password is a bytes32 and occupies storage slot 1.

Steps to pass:

  • Read the password from storage slot 1 using web3.eth.getStorageAt(instance, 1) from the Ethernaut console.
  • This will return the bytes32 value of the password.
  • Call the unlock function with this value.
  • On Remix, load the contract at the instance address and call unlock with the password we just read.

Never store sensitive data on-chain, even if it’s marked private. The blockchain is a public ledger and all storage is readable. If you need to store secrets, consider using commit-reveal schemes or storing hashed values with salts.