#! /usr/bin/env bash
# chmod +x copy_challenge.sh
# ./copy_challenge.sh NewChallengeName
preamble=/home/plotchy/code/ctf/paradigmctf-2022
cp $preamble/test/TestTemplate.t.sol $preamble/test/$1.t.sol
cp $preamble/script/ScriptTemplate.s.sol $preamble/script/$1.s.sol
mkdir -p $preamble/src/$1/public/contracts
cp -r $preamble/src/chal_name/public/contracts/Exploit.sol $preamble/src/$1/public/contracts/Exploit.sol
sed -i "s/chal_name/$1/g" $preamble/test/$1.t.sol
sed -i "s/TestTemplate/$1/g" $preamble/test/$1.t.sol
sed -i "s/ScriptTemplate/$1/g" $preamble/test/$1.t.sol
sed -i "s/chal_name/$1/g" $preamble/script/$1.s.sol
sed -i "s/TestTemplate/$1/g" $preamble/script/$1.s.sol
sed -i "s/ScriptTemplate/$1/g" $preamble/script/$1.s.sol
sed -i "s/chal_name/$1/g" $preamble/src/$1/public/contracts/Exploit.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "forge-std/Test.sol";
import "../src/chal_name/public/contracts/Setup.sol";
import "../src/chal_name/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/TestTemplate.t.sol --mc Tester --fork-url $ANVIL_URL -vvvvv
# Debug command
forge test --mp ./test/TestTemplate.t.sol --mc Tester --debug <function> --fork-url $ANVIL_URL
# Script command
forge script script/ScriptTemplate.s.sol:Scripter --rpc-url $ANVIL_URL --private-key $PRIVATE_KEY_1 --broadcast -vvvvv
# Debug broadcasted tx:
cast run <TXHASH> -d --rpc-url $ANVIL_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;
Challenge challenge;
address payable challengeAddress;
Exploit exploit;
address payable exploitAddress;
function setUp() public {
setup = new Setup{value: 50 ether}();
setupAddress = payable(address(setup));
challenge = setup.challenge();
challengeAddress = payable(address(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 {
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/chal_name/public/contracts/exploit.etk";
etkCode = vm.ffi(inputs);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "forge-std/Script.sol";
import "../src/chal_name/public/contracts/Setup.sol";
import "../src/chal_name/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/TestTemplate.t.sol --mc Tester --fork-url $ANVIL_URL -vvvvv
# Debug command
forge test --mp ./test/TestTemplate.t.sol --mc Tester --debug <function> --fork-url $ANVIL_URL
# Script command
forge script script/ScriptTemplate.s.sol:Scripter --rpc-url $ANVIL_URL --private-key $PRIVATE_KEY_1 --broadcast -vvvvv
# Debug broadcasted tx:
cast run <TXHASH> -d --rpc-url $ANVIL_URL
# Exploratory
forge inspect <path>:<ContractName> storage --pretty
https://ethervm.io/decompile
https://library.dedaub.com/decompile
panoramix <bytecode>
*/
contract Scripter is Script {
Challenge challenge;
address payable challengeAddress;
address payable setupAddress;
address payable exploitAddress;
function setUp() public {}
function run() external {
vm.startBroadcast();
Setup setup = new Setup{value: 100 ether}();
setupAddress = payable(address(setup));
challenge = setup.challenge();
challengeAddress = payable(address(challenge));
Exploit exploit = new Exploit{value: 100 ether}(setup, challenge);
exploitAddress = payable(address(exploit));
exploit.finalize();
// // alternatively use etk code as exploit
// bytes memory etkCode = etkLoad();
// address _addr;
// assembly {
// _addr := create(0, add(initcode, 0x20), mload(initcode))
// }
// exploitAddress = address(_addr);
vm.stopBroadcast();
}
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/chal_name/public/contracts/exploit.etk";
etkCode = vm.ffi(inputs);
}
}
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import './Setup.sol';
import './Challenge.sol';
contract Exploit {
Setup public setup;
address payable public setupAddress;
Challenge public challenge;
address payable public challengeAddress;
constructor(Setup _setup, Challenge _challenge) payable {
setup = _setup;
setupAddress = payable(address(setup));
challenge = _challenge;
challengeAddress = payable(address(challenge));
}
function finalize() external {
}
function checkSolved() public view returns(bool) {
return setup.isSolved();
}
fallback() external payable {
}
}
# set up storage here
# This is a universal constructor - 600b8038035981835939f3
%push(runtimeBytecode) # %push(label) will push the pc of the label startpoint [start]
dup1 # [start, start]
codesize # [codesize, start, start]
sub # [lengthRun, start]
msize # [0, lengthRun, start]
dup2 # [lengthRun, 0, lengthRun, start]
dup4 # [start, lengthRun, 0, lengthRun, start]
msize
codecopy #[0, start, lengthRun, 0, lengthRun, start]
return #[0, lengthRun, ...]
runtimeBytecode:
# your runtime bytecode
pragma solidity ^0.8.4;
// Used when you need to locally deploy/test a non-sourced contract.
contract evmSetup {
address payable public challengeAddress;
constructor() payable {
// gather creation code through .bin file
bytes memory initcode = hex"6080604052000000"; // ..... this is creationCode
address _addr;
assembly {
_addr := create(0, add(initcode, 0x20), mload(initcode))
}
/* version with constructor args:
bytes memory initcode = hex"6080604052000000";
bytes memory args1 = hex"0000000000000000000000000000000000000000000000000000000000000001";
bytes memory args2 = hex"0000000000000000000000000000000000000000000000000000000000000002";
bytes memory initcode_w_args = abi.encodePacked(initcode, args1, args2);
address _addr;
assembly {
_addr := create(0, add(initcode_w_args, 0x20), mload(initcode_w_args))
}
*/
challengeAddress = payable(_addr);
}
function isSolved() public view returns (bool) {
return challengeAddress.balance == 0;
}
}
# Basic conversion of etk to bytecode
eas /src/chal_name/public/contracts/exploit.etk
#! /usr/bin/env bash
# For use with vm.ffi()
# saved as ./script/compile.sh
echo "0x$(eas $1)"
0x4e59b44847b379578588920cA78FbF26c0B4956C
CREATE2 crunching
./ERADICATE2.x64 -A 0x4e59b44847b379578588920cA78FbF26c0B4956C -i <path/to/bytecode> --matching c0ffee
function testCREATE2() public {
bytes32 salt = 0x38f3364199653d393a7c5152d5cedbbe4e45bf616f4219edaa5e89fbd09f208b;
bytes memory etkCode = etkLoad();
bytes memory data = bytes.concat(salt, etkCode);
CREATE2_FACTORY.call(data);
console2.logBytes(address(0xc0fFeE0925e2b93b24eF8B2f7C2adE3dd15Ee8e8).code);
}