The creator of this contract was careful enough to protect the sensitive areas of its storage. Unlock this contract to beat the level.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract Privacy {
bool public locked = true;
uint256 public ID = block.timestamp;
uint8 private flattening = 10;
uint8 private denomination = 255;
uint16 private awkwardness = uint16(block.timestamp);
bytes32[3] private data;
constructor(bytes32[3] memory _data) {
data = _data;
}
function unlock(bytes16 _key) public {
require(_key == bytes16(data[2]));
locked = false;
}
/*
A bunch of super advanced solidity algorithms...
*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^ ,---/V\
`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*. ~|__(o.o)
^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*' UU UU
*/
}
Analysis Link to heading
Let’s review the unlock
function:
function unlock(bytes16 _key) public {
require(_key == bytes16(data[2]));
locked = false;
}
So the goal is to find out what’s in the data[2]
. This level
is similar to the #9 Vault: we must unlock the contract by
reading a private state variable that contains the key. From the
Solidity docs we know that contract state variables are stored
contiguosly (except for dynamically-sized arrays and mappings)
and items that need less than 32 bytes (256 bits) are packed
into a single storage slot.
Solution Link to heading
The first 5 state variables take exactly 3 storage slots.
We want to read the bytes16(data[2))
which has slot index 5
:
function exploit() internal override {
vm.startPrank(player);
bytes32 key = vm.load(address(level), bytes32(uint256(5)));
level.unlock(bytes16(key));
vm.stopPrank();
}
In Foundry we use vm.load
to
load the value from storage slot with a specific index.
Lessons learned Link to heading
- All “private” data is public on the blockchain. Just because
you set the visibility to
private
orinternal
doesn’t mean that it becomes secret. Anyone can read the storage of any contract ever existed on EVM blockchain. - Encrypt/hash any sensitive data that you want to store in a smart contract.
References Link to heading
- Layout of State Variables in Storage
- SWC-136 – Unencrypted Private Data On-Chain (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, #14 Gatekeeper One!