[๐Ÿ’  Platinum 4] 1099์ค„์งœ๋ฆฌ ์ปจํŠธ๋ž™ํŠธ์—์„œ ์ง„์งœ ์ฝ”๋“œ๋Š” 40์ค„ โ€” DreamHack dreamthereum ํ’€์ด

2026-08-17ยท1๋ถ„ ์ฝ๊ธฐยท

[๐Ÿ’  Platinum 4] 1099์ค„์งœ๋ฆฌ ์ปจํŠธ๋ž™ํŠธ์—์„œ ์ง„์งœ ์ฝ”๋“œ๋Š” 40์ค„ โ€” DreamHack dreamthereum ํ’€์ด

๋“œ๋ฆผํ•ต dreamthereum(ํ”Œ๋ž˜ํ‹ฐ๋„˜ 4, ๋ฆฌ๋ฒ„์‹ฑ)์€ 256๊ฐœ์˜ ๋”๋ฏธ ํ•จ์ˆ˜๋กœ ์ฑ„์šด ์Šค๋งˆํŠธ์ปจํŠธ๋ž™ํŠธ๋‹ค. flagChecker๊ฐ€ flag[i+2] = f(flag[i], flag[i+1])์„ ๊ฐ•์ œํ•˜๋Š” ์‚ฌ์Šฌ ๊ตฌ์กฐ๋ผ ์•ž ๋‘ ๊ธ€์ž๋งŒ ์ •ํ•˜๋ฉด 31๊ธ€์ž๊ฐ€ ์ „๋ถ€ ๋”ฐ๋ผ ๋‚˜์˜ค๊ณ , f๋Š” "dummy<A>(<ํƒ€์ž…>)" ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ keccak256 ์•ž 4๋ฐ”์ดํŠธ๋ฅผ 16์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€์˜€๋‹ค. ํ›„๋ณด 256๊ฐœ๋ฅผ ์ „์ˆ˜๋กœ ๋Œ๋ ค flag๋ฅผ ๋ณต์›ํ•˜๊ณ  foundry๋กœ ๋„์šด ์‹ค์ œ EVM์—์„œ flagChecker()๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ๊นŒ์ง€ ํ™•์ธํ–ˆ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” dreamthereum ๋ถ„๋ฅ˜: reversing (์Šค๋งˆํŠธ์ปจํŠธ๋ž™ํŠธ) ๋‚œ์ด๋„: ๐Ÿ’  Platinum 4 FLAG: DH{ABCB69B964FA423A7BCB69B964FA423}

๋ฌธ์ œ ์„ค๋ช…์€ ์งง๋‹ค. "๋“œ๋ฆผ๋”๋ฆฌ์›€์˜ ์„ธ์ƒ์—์„œ๋Š” ์Šค๋งˆํŠธ์ปจํŠธ๋ž™ํŠธ ๋ฐฐํฌ ์‚ฌ์ด์ฆˆ ์ œํ•œ๋„ ์‹คํ–‰ํ•  gas limit๋„ ์—†๋‹ค"๋ฉด์„œ, ๋ฐฐํฌ๋œ ์ปจํŠธ๋ž™ํŠธ์˜ flagChecker ๋ฉ”์†Œ๋“œ๊ฐ€ true๋ฅผ ๋Œ๋ ค์ฃผ๋Š” ์ž…๋ ฅ์„ ์ฐพ์œผ๋ผ๊ณ  ํ•œ๋‹ค. ์ฐธ๊ณ  ๋งํฌ๋กœ 4byte.directory๋ฅผ ๊ฑธ์–ด ๋†จ๋‹ค. ํ•จ์ˆ˜ ์…€๋ ‰ํ„ฐ ์ด์•ผ๊ธฐ๋ผ๋Š” ๋œป์ด๋‹ค.

๋ฐ›์€ ํŒŒ์ผ์€ challenge.sol ํ•˜๋‚˜. 1099์ค„์ด๋‹ค.

file challenge.sol; wc -l challenge.sol; grep -c "function dummy" challenge.sol; grep -oE "dummy[0-9A-F]\(" challenge.sol | sort -u | tr "\n" " "; echo

challenge.sol ์ •์ฐฐ โ€” file/wc/grep์œผ๋กœ ํ™•์ธํ•œ ๊ฒฐ๊ณผ. 1099์ค„ ASCII ์†Œ์Šค์— function dummy ์„ ์–ธ์ด 256๊ฐœ ๋“ค์–ด ์žˆ๊ณ , ํ•จ์ˆ˜ ์ด๋ฆ„์€ dummy0๋ถ€ํ„ฐ dummyF๊นŒ์ง€ 16์ข…๋ฅ˜๋ฟ์ด๋‹ค
challenge.sol ์ •์ฐฐ โ€” file/wc/grep์œผ๋กœ ํ™•์ธํ•œ ๊ฒฐ๊ณผ. 1099์ค„ ASCII ์†Œ์Šค์— function dummy ์„ ์–ธ์ด 256๊ฐœ ๋“ค์–ด ์žˆ๊ณ , ํ•จ์ˆ˜ ์ด๋ฆ„์€ dummy0๋ถ€ํ„ฐ dummyF๊นŒ์ง€ 16์ข…๋ฅ˜๋ฟ์ด๋‹ค

function dummy๊ฐ€ 256๊ฐœ. ์ด๋ฆ„์€ dummy0~dummyF 16๊ฐœ๋ฟ์ธ๋ฐ ์„ ์–ธ์ด 256๊ฐœ๋ผ๋Š” ๊ฑด, ์ด๋ฆ„ ํ•˜๋‚˜๋‹น 16๊ฐœ์”ฉ ์˜ค๋ฒ„๋กœ๋“œ๋ผ ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค. ๋ฐฐํฌ ์‚ฌ์ด์ฆˆ ์ œํ•œ์ด ์—†๋‹ค๋Š” ์„ค๋ช…์ด ์—ฌ๊ธฐ์„œ ๊ฑธ๋ฆฐ๋‹ค โ€” ์‹ค์ œ ์ด๋”๋ฆฌ์›€์ด๋ผ๋ฉด EIP-170์˜ 24KB ์ฝ”๋“œ ์ œํ•œ์— ๊ฑธ๋ ค ๋ฐฐํฌ ์ž์ฒด๊ฐ€ ์•ˆ ๋œ๋‹ค.


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…dreamthereum
๋‚œ์ด๋„๐Ÿ’  Platinum 4
๋ถ„๋ฅ˜reversing (EVM / Solidity)
์ œ๊ณต ํŒŒ์ผchallenge.sol (1099์ค„, ์„œ๋ฒ„ ์—†์Œ)
์ŠคํƒSolidity >=0.7.0 <0.9.0, EVM
ํ•ต์‹ฌ ๊ธฐ๋ฒ•ํ•จ์ˆ˜ ์…€๋ ‰ํ„ฐ(keccak256 ์•ž 4๋ฐ”์ดํŠธ)๋ฅผ ๊ฐ’์œผ๋กœ ์“ฐ๋Š” ์ƒํƒœ ์ „์ด + ์‚ฌ์Šฌ ์ œ์•ฝ์œผ๋กœ ํƒ์ƒ‰ ๊ณต๊ฐ„์„ 256์œผ๋กœ ์ถ•์†Œ

ํ’€์ด ํ๋ฆ„์€ ์ด๋ ‡๋‹ค. 256๊ฐœ ๋”๋ฏธ ํ•จ์ˆ˜๋Š” ์ „๋ถ€ TABLE2[uint32(msg.sig) % TABLE2.length]๋งŒ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ป๋ฐ๊ธฐ๋‹ค. flagChecker๋Š” ์ž…๋ ฅ 31๋ฐ”์ดํŠธ๋ฅผ ํ›‘์œผ๋ฉด์„œ internalCall(flag[i], flag[i+1])์˜ ๊ฒฐ๊ณผ๊ฐ€ flag[i+2]์™€ ๊ฐ™์€์ง€ ๊ณ„์† ํ™•์ธํ•œ๋‹ค. ์ฆ‰ ์•ž ๋‘ ๊ธ€์ž๋ฅผ ๊ณ ๋ฅด๋ฉด ๋‚˜๋จธ์ง€ 29๊ธ€์ž๋Š” ๊ณ„์‚ฐ์œผ๋กœ ์ •ํ•ด์ง„๋‹ค. ํ›„๋ณด๋Š” 16ร—16 = 256๊ฐœ๋ฟ์ด๊ณ , ๋งˆ์ง€๋ง‰์— ๊ฑธ๋ฆฐ keccak256 ๋น„๊ต๊ฐ€ ๊ทธ์ค‘ ํ•˜๋‚˜๋งŒ ๋‚จ๊ธด๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” EVM ํ•จ์ˆ˜ ์…€๋ ‰ํ„ฐ

์ด๋”๋ฆฌ์›€์—์„œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ calldata ๋งจ ์•ž 4๋ฐ”์ดํŠธ๊ฐ€ ํ•จ์ˆ˜ ์…€๋ ‰ํ„ฐ๋‹ค. ๊ฐ’์€ ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋ฌธ์ž์—ด์˜ keccak256 ํ•ด์‹œ ์•ž 4๋ฐ”์ดํŠธ๋‹ค.

selector = keccak256("transfer(address,uint256)")[0:4] = 0xa9059cbb

์‹œ๊ทธ๋‹ˆ์ฒ˜์— ๋“ค์–ด๊ฐ€๋Š” ๊ฑด ํ•จ์ˆ˜ ์ด๋ฆ„๊ณผ ์ธ์ž ํƒ€์ž…๋ฟ์ด๋‹ค. ์ธ์ž ์ด๋ฆ„๋„, ๋ฐ˜ํ™˜ ํƒ€์ž…๋„, view ๊ฐ™์€ ์ˆ˜์‹์ž๋„ ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ™์€ ์ด๋ฆ„์„ ํƒ€์ž…๋งŒ ๋ฐ”๊ฟ” ์˜ค๋ฒ„๋กœ๋“œํ•˜๋ฉด ์…€๋ ‰ํ„ฐ๊ฐ€ ์ „๋ถ€ ๋‹ฌ๋ผ์ง„๋‹ค. 4byte.directory๋Š” ์ด 4๋ฐ”์ดํŠธ๋ฅผ ์›๋ž˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋กœ ๋˜๋Œ๋ ค ๋ณด๋Š” ๊ณต๊ฐœ DB๋‹ค.

์ด ๋ฌธ์ œ๋Š” ๊ทธ ์…€๋ ‰ํ„ฐ๋ฅผ "์ฃผ์†Œ"๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ’์œผ๋กœ ์“ด๋‹ค. ๋”๋ฏธ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋Š” ์ผ์€ ์ž๊ธฐ๋ฅผ ๋ถ€๋ฅธ ์…€๋ ‰ํ„ฐ๋ฅผ 32๋น„ํŠธ ์ •์ˆ˜๋กœ ์ฝ์–ด 16์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋ฅผ ํ…Œ์ด๋ธ” ์ธ๋ฑ์Šค๋กœ ์“ฐ๋Š” ๊ฒƒ๋ฟ์ด๋‹ค.

internalCall ๋„์‹ โ€” A์™€ B ๋‘ ๊ธ€์ž๋กœ dummy ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋ฌธ์ž์—ด์„ ์กฐ๋ฆฝํ•˜๊ณ , keccak256 ์•ž 4๋ฐ”์ดํŠธ๋ฅผ uint32๋กœ ์ฝ์–ด 16์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋กœ TABLE2๋ฅผ ์ธ๋ฑ์‹ฑํ•ด ๋‹ค์‹œ 16์ง„ ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ์–ป๋Š” ๊ฒฝ๋กœ
internalCall ๋„์‹ โ€” A์™€ B ๋‘ ๊ธ€์ž๋กœ dummy ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋ฌธ์ž์—ด์„ ์กฐ๋ฆฝํ•˜๊ณ , keccak256 ์•ž 4๋ฐ”์ดํŠธ๋ฅผ uint32๋กœ ์ฝ์–ด 16์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋กœ TABLE2๋ฅผ ์ธ๋ฑ์‹ฑํ•ด ๋‹ค์‹œ 16์ง„ ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ์–ป๋Š” ๊ฒฝ๋กœ


๐Ÿ”ฌ ์ฝ”๋“œ ์ •์ฐฐ โ€” 1099์ค„ ์ค‘ 1024์ค„์ด ๋ฏธ๋ผ

๋”๋ฏธ ์„ ์–ธ์„ ๋นผ๊ณ  ๋‚˜๋ฉด ๋‚จ๋Š” ๊ฒŒ ๋ช‡ ์ค„ ์•ˆ ๋œ๋‹ค. function ์„ ์–ธ๊ณผ ๋‘ ํ…Œ์ด๋ธ”, ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰ ๋น„๊ต์‹๋งŒ ๋ฝ‘์•„ ๋ดค๋‹ค.

grep -n "function \|TABLE1 =\|TABLE2 =\|keccak256" challenge.sol | grep -v dummy

grep์œผ๋กœ ์ถ”๋ฆฐ challenge.sol์˜ ๋น„-๋”๋ฏธ ์ฝ”๋“œ โ€” TABLE1๊ณผ TABLE2 ์„ ์–ธ, sliceUint/concatenate/getArgsFromB/flagChecker/internalCall ๋‹ค์„ฏ ํ•จ์ˆ˜, ๊ทธ๋ฆฌ๊ณ  1083ํ–‰์˜ keccak256 ๋น„๊ต ์ƒ์ˆ˜ 0xe6a9e5bc๊ฐ€ ํ•œ ํ™”๋ฉด์— ๋ณด์ธ๋‹ค
grep์œผ๋กœ ์ถ”๋ฆฐ challenge.sol์˜ ๋น„-๋”๋ฏธ ์ฝ”๋“œ โ€” TABLE1๊ณผ TABLE2 ์„ ์–ธ, sliceUint/concatenate/getArgsFromB/flagChecker/internalCall ๋‹ค์„ฏ ํ•จ์ˆ˜, ๊ทธ๋ฆฌ๊ณ  1083ํ–‰์˜ keccak256 ๋น„๊ต ์ƒ์ˆ˜ 0xe6a9e5bc๊ฐ€ ํ•œ ํ™”๋ฉด์— ๋ณด์ธ๋‹ค

์ง„์งœ ์ฝ”๋“œ๋Š” 40์ค„ ๋‚จ์ง“์ด๋‹ค. ๋‘ ํ…Œ์ด๋ธ”๋ถ€ํ„ฐ ๋ณด์ž.

string[] TABLE1 = ["uint256", "bytes", "bytes4", "bytes1", "bytes32", "uint8",
                   "uint16", "uint248", "string", "uint240", "uint224", "uint192",
                   "uint24", "uint32", "uint56", "uint72"];
uint8[] TABLE2 = [0x41,0x42,0x43,0x44,0x45,0x46,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39];

TABLE1์€ 16๊ฐœ ํƒ€์ž… ์ด๋ฆ„, TABLE2๋Š” 'A''F' ๋‹ค์Œ์— '0''9'๊ฐ€ ์˜ค๋Š” 16๊ฐœ ๋ฌธ์ž๋‹ค. ๋‘˜ ๋‹ค 16์ง„ ๋ฌธ์ž ํ•˜๋‚˜๋กœ ๊ท€๊ฒฐ๋œ๋‹ค๋Š” ๊ฒŒ ํฌ์ธํŠธ๋‹ค. ์ˆœ์„œ์— ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค. TABLE2[0]์€ '0'์ด ์•„๋‹ˆ๋ผ 'A'๋‹ค.

๋”๋ฏธ ํ•จ์ˆ˜ 256๊ฐœ๋Š” ์ „๋ถ€ ๊ฐ™์€ ๋ชธํ†ต์„ ๊ฐ–๋Š”๋‹ค.

function dummyA(uint192) external view returns (uint8) {
    return TABLE2[uint32(msg.sig) % TABLE2.length];
}

์ธ์ž๋Š” ์ด๋ฆ„์กฐ์ฐจ ์—†๋‹ค. ๊ฐ’์„ ์“ฐ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์€ ์–ด๋–ค ์…€๋ ‰ํ„ฐ๋กœ ๋ถˆ๋ ธ๋Š”๊ฐ€์—๋งŒ ์˜์กดํ•œ๋‹ค.

ํ•ต์‹ฌ ์„ธ ํ•จ์ˆ˜

function getArgsFromB(uint8 x) internal view returns (string memory) {
    require(x >= 0x41 && x < 0x41+6 || x >= 0x30 && x <= 0x39, "BOOOOM!");
    if (x>=0x41)
        return TABLE1[x-0x41 + 0xa];
    return TABLE1[x-0x30];
}

'0''9'๋Š” 09๋กœ, 'A''F'๋Š” 1015๋กœ ์ ‘ํžŒ๋‹ค. 16์ง„ ๋ฌธ์ž๋ฅผ ๊ทธ ๊ฐ’์œผ๋กœ ์ฝ๋Š” ๊ฒƒ๊ณผ ์ •ํ™•ํžˆ ๊ฐ™๋‹ค. ๋ฒ”์œ„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด BOOOOM!์œผ๋กœ revertํ•œ๋‹ค.

function internalCall(uint8 A, uint8 B) internal onlyOwner returns (uint8) {
     string memory callMethod = "dummy";
     callMethod = concatenate(callMethod, A);              // ์ด๋ฆ„ ํ•œ ๊ธ€์ž
     callMethod = concatenate(callMethod, "(");
     callMethod = concatenate(callMethod, getArgsFromB(B)); // ์ธ์ž ํƒ€์ž…
     callMethod = concatenate(callMethod, ")");
 
    (bool success, bytes memory returnData) = address(this).call(abi.encodeWithSignature(callMethod, 0x0));
    require(success, "Bamb!");
 
    return uint8(sliceUint(returnData, 0));
}

๋ฌธ์ž์—ด๋กœ ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ์กฐ๋ฆฝํ•ด abi.encodeWithSignature๋กœ ๋„˜๊ธด๋‹ค. ์ด๋•Œ ๊ณ„์‚ฐ๋˜๋Š” ์…€๋ ‰ํ„ฐ๊ฐ€ ๊ณง ๊ทธ ๋”๋ฏธ ํ•จ์ˆ˜์˜ msg.sig๊ฐ€ ๋˜๊ณ , ๋”๋ฏธ๋Š” ๊ทธ๊ฑธ 16์œผ๋กœ ๋‚˜๋ˆˆ ๋‚˜๋จธ์ง€๋กœ TABLE2๋ฅผ ์ฐ์–ด ๋Œ๋ ค์ค€๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ๊นŒ internalCall(A, B)๋Š” ๋ฌธ์ž ๋‘ ๊ฐœ๋ฅผ ๋ฐ›์•„ ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ๋ฑ‰๋Š” ํ•จ์ˆ˜๋‹ค. ์ปจํŠธ๋ž™ํŠธ ์•ˆ์—์„œ ๋ฒŒ์–ด์ง€๋Š” ์ž๊ธฐ ํ˜ธ์ถœ์ด์ง€๋งŒ, ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฑด keccak256๋ฟ์ด๋ผ ์˜คํ”„๋ผ์ธ์—์„œ ๊ทธ๋Œ€๋กœ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

function flagChecker(bytes calldata flag) external returns (bool) {
    uint8 ret;
    bytes memory output = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // output.length == 31
 
    for(uint i=0; i<output.length - 2; i++) {
        ret = internalCall(uint8(flag[i]), uint8(flag[i+1]));
        output[i] = flag[i];
        output[i+1] = flag[i+1];
        output[i+2] = bytes1(ret);
        require(ret == uint8(flag[i+2]), "BOBOBOBO!!!");
    }
 
    // https://emn178.github.io/online-tools/keccak_256.html
    return keccak256(abi.encodePacked("DH{", output, "}")) == bytes32(0xe6a9e5bcd806b56f7ab007dbb1e881f5b5746f00cb940b8083be861a257f7806);
}

output์€ 31๋ฐ”์ดํŠธ๊ณ  ๋ฃจํ”„๋Š” i = 0..28์„ ๋ˆ๋‹ค. ๋งค ํšŒ flag[i+2]๋ฅผ ์ฝ์œผ๋ฏ€๋กœ ์ž…๋ ฅ์€ ์ตœ์†Œ 31๋ฐ”์ดํŠธ์—ฌ์•ผ ํ•˜๊ณ , ๋ฃจํ”„๊ฐ€ ๋๋‚˜๋ฉด output์€ flag์˜ ์•ž 31๋ฐ”์ดํŠธ ๊ทธ ์ž์ฒด๊ฐ€ ๋œ๋‹ค.


๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ์…€๋ ‰ํ„ฐ๊ฐ€ ๊ณง ์ „์ดํ•จ์ˆ˜๋‹ค

internalCall์„ ํŒŒ์ด์ฌ์œผ๋กœ ์˜ฎ๊ธฐ๋ฉด ์ด๋ ‡๊ฒŒ ๋œ๋‹ค.

f(A, B) = TABLE2[ uint32(keccak256("dummy" + A + "(" + TABLE1[hex(B)] + ")")[:4]) % 16 ]

์—ฌ๊ธฐ์„œ ๋‚ด ๊ณ„์‚ฐ์ด ๋งž๋Š”์ง€๋ถ€ํ„ฐ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค. ์…€๋ ‰ํ„ฐ๋Š” ์ง์ ‘ keccak์„ ๋Œ๋ ค๋„ ๋˜์ง€๋งŒ, foundry์˜ cast๊ฐ€ ๊ฐ™์€ ์ผ์„ ํ•ด ์ฃผ๋‹ˆ ๊ต์ฐจ๊ฒ€์ฆ์œผ๋กœ ์“ฐ๊ธฐ ์ข‹๋‹ค. cast keccak์œผ๋กœ ์ตœ์ข… ๋น„๊ต ๋Œ€์ƒ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ํ™•์ธํ–ˆ๋‹ค.

for s in "dummyA(uint192)" "dummy0(uint72)" "dummyD(bytes1)"; do printf "%-18s -> " "$s"; docker run --rm -e FOUNDRY_DISABLE_NIGHTLY_WARNING=1 --entrypoint cast ghcr.io/foundry-rs/foundry:latest sig "$s"; done; docker run --rm -e FOUNDRY_DISABLE_NIGHTLY_WARNING=1 --entrypoint cast ghcr.io/foundry-rs/foundry:latest keccak "DH{ABCB69B964FA423A7BCB69B964FA423}"

foundry cast๋กœ ์…€๋ ‰ํ„ฐ์™€ ํ•ด์‹œ๋ฅผ ๊ต์ฐจ๊ฒ€์ฆํ•œ ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด โ€” dummyA(uint192)๋Š” 0xab128832, dummy0(uint72)๋Š” 0xbbda911b, dummyD(bytes1)๋Š” 0x4a68a17d์ด๊ณ  cast keccak์ด ์ฐ์€ ํ”Œ๋ž˜๊ทธ ํ•ด์‹œ๊ฐ€ ์ปจํŠธ๋ž™ํŠธ ์ƒ์ˆ˜ 0xe6a9e5bcโ€ฆ7806๊ณผ ๊ฐ™๋‹ค
foundry cast๋กœ ์…€๋ ‰ํ„ฐ์™€ ํ•ด์‹œ๋ฅผ ๊ต์ฐจ๊ฒ€์ฆํ•œ ํ„ฐ๋ฏธ๋„ ํ™”๋ฉด โ€” dummyA(uint192)๋Š” 0xab128832, dummy0(uint72)๋Š” 0xbbda911b, dummyD(bytes1)๋Š” 0x4a68a17d์ด๊ณ  cast keccak์ด ์ฐ์€ ํ”Œ๋ž˜๊ทธ ํ•ด์‹œ๊ฐ€ ์ปจํŠธ๋ž™ํŠธ ์ƒ์ˆ˜ 0xe6a9e5bcโ€ฆ7806๊ณผ ๊ฐ™๋‹ค

์…‹ ๋‹ค ํŒŒ์ด์ฌ์œผ๋กœ ๊ณ„์‚ฐํ•œ ๊ฐ’๊ณผ ๊ฐ™๊ณ , cast keccak์ด ๋ฑ‰์€ ํ•ด์‹œ๋Š” ์†Œ์Šค 1083ํ–‰์˜ ์ƒ์ˆ˜์™€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•œ๋‹ค.

์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์‹ค์ œ๋กœ ๋งŒ๋“  ABI์—์„œ๋„ ๊ฐ™์€ ๊ฐ’์ด ๋‚˜์˜ค๋Š”์ง€ ํ™•์ธํ–ˆ๋‹ค. forge inspect๋กœ ๋ฉ”์†Œ๋“œ ํ‘œ๋ฅผ ๋ฝ‘์œผ๋ฉด ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๊ฐ€์ง„ ์…€๋ ‰ํ„ฐ๊ฐ€ ์ „๋ถ€ ๋‚˜์˜จ๋‹ค.

docker run --rm -e FOUNDRY_DISABLE_NIGHTLY_WARNING=1 -v "$PWD":/w -w /w --entrypoint forge ghcr.io/foundry-rs/foundry:latest inspect src/Challenge.sol:Challenge methods | tee methods.txt | grep -E "dummyA\(uint192\)|dummy0\(uint72\)|dummyD\(bytes1\)|flagChecker"; grep -c "| dummy" methods.txt

forge inspect๊ฐ€ ๋ฝ‘์€ Challenge ์ปจํŠธ๋ž™ํŠธ์˜ ๋ฉ”์†Œ๋“œ ์…€๋ ‰ํ„ฐ ํ‘œ โ€” dummy0(uint72) bbda911b, dummyA(uint192) ab128832, dummyD(bytes1) 4a68a17d, flagChecker(bytes) 6f085333์ด ๋ณด์ด๊ณ  dummy ๊ณ„์—ด ๋ฉ”์†Œ๋“œ๊ฐ€ 256๊ฐœ๋กœ ์ง‘๊ณ„๋œ๋‹ค
forge inspect๊ฐ€ ๋ฝ‘์€ Challenge ์ปจํŠธ๋ž™ํŠธ์˜ ๋ฉ”์†Œ๋“œ ์…€๋ ‰ํ„ฐ ํ‘œ โ€” dummy0(uint72) bbda911b, dummyA(uint192) ab128832, dummyD(bytes1) 4a68a17d, flagChecker(bytes) 6f085333์ด ๋ณด์ด๊ณ  dummy ๊ณ„์—ด ๋ฉ”์†Œ๋“œ๊ฐ€ 256๊ฐœ๋กœ ์ง‘๊ณ„๋œ๋‹ค

dummy ๋ฉ”์†Œ๋“œ๊ฐ€ 256๊ฐœ, ์…€๋ ‰ํ„ฐ๋„ ๊ทธ๋Œ€๋กœ๋‹ค. ์—ฌ๊ธฐ๊นŒ์ง€ ์˜ค๋ฉด f๋Š” ์ˆœ์ˆ˜ํ•œ ํ‘œ ํ•˜๋‚˜๋กœ ํ™•์ •๋œ๋‹ค.



๐ŸŽฏ ์‚ฌ์Šฌ ๊ตฌ์กฐ โ€” ์•ž ๋‘ ๊ธ€์ž๊ฐ€ ์ „๋ถ€๋‹ค

flagChecker์˜ require๋ฅผ ๋‹ค์‹œ ๋ณด์ž.

require(ret == uint8(flag[i+2]), "BOBOBOBO!!!");

ret์€ f(flag[i], flag[i+1])์ด๋‹ค. ๊ทธ๋Ÿฌ๋‹ˆ ์ด ์กฐ๊ฑด์€ ๊ณง flag[i+2] = f(flag[i], flag[i+1])์ด๋ผ๋Š” ์ ํ™”์‹์ด๋‹ค. i๊ฐ€ 0๋ถ€ํ„ฐ 28๊นŒ์ง€ ๋„๋‹ˆ๊นŒ flag[2]๋ถ€ํ„ฐ flag[30]๊นŒ์ง€ 29๊ธ€์ž๊ฐ€ ์•ž ๋‘ ๊ธ€์ž์—์„œ ์—ฐ์‡„์ ์œผ๋กœ ๊ฒฐ์ •๋œ๋‹ค.

flagChecker์˜ ์‚ฌ์Šฌ ๊ตฌ์กฐ ๋„์‹ โ€” ํญ 2์˜ ์ฐฝ์ด ํ•œ ์นธ์”ฉ ๋ฏธ๋„๋Ÿฌ์ง€๋ฉฐ f(flag[i], flag[i+1])์ด flag[i+2]๋ฅผ ํ™•์ •ํ•˜๊ณ , ์ž์œ ๋กœ์šด ๊ฑด ๋งจ ์•ž ๋‘ ๊ธ€์ž๋ฟ์ด๋ผ ํ›„๋ณด๊ฐ€ 16ร—16 = 256๊ฐœ๋กœ ์ค„์–ด๋“ ๋‹ค
flagChecker์˜ ์‚ฌ์Šฌ ๊ตฌ์กฐ ๋„์‹ โ€” ํญ 2์˜ ์ฐฝ์ด ํ•œ ์นธ์”ฉ ๋ฏธ๋„๋Ÿฌ์ง€๋ฉฐ f(flag[i], flag[i+1])์ด flag[i+2]๋ฅผ ํ™•์ •ํ•˜๊ณ , ์ž์œ ๋กœ์šด ๊ฑด ๋งจ ์•ž ๋‘ ๊ธ€์ž๋ฟ์ด๋ผ ํ›„๋ณด๊ฐ€ 16ร—16 = 256๊ฐœ๋กœ ์ค„์–ด๋“ ๋‹ค

๊ฒŒ๋‹ค๊ฐ€ ๋‘ ๊ธ€์ž ๋ชจ๋‘ 16์ง„ ๋ฌธ์ž์—ฌ์•ผ ํ•œ๋‹ค. B ์ž๋ฆฌ๋Š” getArgsFromB๊ฐ€ BOOOOM!์œผ๋กœ ๋ง‰๊ณ , A ์ž๋ฆฌ๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ์ด๋ฆ„์ด๋ฉด call์ด ์‹คํŒจํ•ด Bamb!๋กœ ๋ง‰ํžŒ๋‹ค. ๊ทธ๋ฆฌ๊ณ  f์˜ ์ถœ๋ ฅ์€ TABLE2 ์›์†Œ๋ผ ์—ญ์‹œ 16์ง„ ๋ฌธ์ž๋‹ค. ๊ฒฐ๊ตญ 31๊ธ€์ž ์ „๋ถ€๊ฐ€ 0-9A-F๋‹ค.

์ „์ดํ‘œ๋ฅผ ์‹ค์ œ๋กœ ๋ฝ‘์•„ ๋ดค๋‹ค. ํ–‰์ด ํ•จ์ˆ˜ ์ด๋ฆ„ A, ์—ด์ด ์ธ์ž ํƒ€์ž… B๋‹ค.

#!/usr/bin/env python3
"""internalCall(A,B) ์ „์ดํ‘œ๋ฅผ 16x16 ์œผ๋กœ ์ถœ๋ ฅํ•œ๋‹ค. solve.py ์™€ ๊ฐ™์€ ๋กœ์ง."""
from solve import ALPHABET, TABLE1, TABLE2, hexval, selector
 
print("  ์˜ˆ์‹œ ์‹œ๊ทธ๋‹ˆ์ฒ˜ โ†’ ์…€๋ ‰ํ„ฐ โ†’ %16 โ†’ ๋ฐ˜ํ™˜๋ฌธ์ž")
for a, b in (("A", "B"), ("0", "F"), ("D", "3")):
    sig = "dummy%s(%s)" % (a, TABLE1[hexval(b)])
    sel = selector(sig)
    print("    %-22s 0x%08x  %%16=%2d  -> '%s'"
          % (sig, sel, sel % 16, chr(TABLE2[sel % 16])))
 
print()
print("  f(A,B) ์ „์ดํ‘œ   (ํ–‰=A: ํ•จ์ˆ˜์ด๋ฆ„, ์—ด=B: ์ธ์žํƒ€์ž…)")
print("      " + "  ".join(ALPHABET))
for a in ALPHABET:
    row = []
    for b in ALPHABET:
        sig = "dummy%s(%s)" % (a, TABLE1[hexval(b)])
        row.append(chr(TABLE2[selector(sig) % 16]))
    print("   %s  %s" % (a, "  ".join(row)))
python3 table.py

table.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” dummyA(uint192)๊ฐ€ 0xab128832๋ผ์„œ %16=2์ด๊ณ  TABLE2[2]='C'๊ฐ€ ๋˜๋Š” ์˜ˆ์‹œ ์„ธ ์ค„๊ณผ, 16ร—16 ์ „์ดํ‘œ ์ „์ฒด๊ฐ€ ์ถœ๋ ฅ๋๋‹ค. ํ‘œ์˜ ๋ชจ๋“  ์นธ์ด 0-9A-F ๋ฌธ์ž ํ•˜๋‚˜๋‹ค
table.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” dummyA(uint192)๊ฐ€ 0xab128832๋ผ์„œ %16=2์ด๊ณ  TABLE2[2]='C'๊ฐ€ ๋˜๋Š” ์˜ˆ์‹œ ์„ธ ์ค„๊ณผ, 16ร—16 ์ „์ดํ‘œ ์ „์ฒด๊ฐ€ ์ถœ๋ ฅ๋๋‹ค. ํ‘œ์˜ ๋ชจ๋“  ์นธ์ด 0-9A-F ๋ฌธ์ž ํ•˜๋‚˜๋‹ค

(A, B) = ('A', 'B')๋ฉด 'C'. ์‹ค์ œ flag๊ฐ€ ABC๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ด์œ ๊ฐ€ ์ด ํ•œ ์นธ์— ๋“ค์–ด ์žˆ๋‹ค.

โ–ถ๐Ÿ› ํ•จ์ • โ€” ์ฝ๊ธฐ ์‰ฌ์šด ์˜ค๋… ์„ธ ๊ฐ€์ง€, ์ฆ์ƒ์€ ์ „๋ถ€ 'ํ›„๋ณด 0๊ฐœ'

์ด ๋ฌธ์ œ๋Š” ์ค‘๊ฐ„ ํ”ผ๋“œ๋ฐฑ์ด ์—†๋‹ค. require๊ฐ€ and ๋ˆ„์‚ฐ๋„ ์•„๋‹ˆ๊ณ  ๋ถ€๋ถ„ ์ ์ˆ˜๋„ ์—†์–ด์„œ, ๋ชจ๋ธ์„ ํ‹€๋ฆฌ๊ฒŒ ์„ธ์šฐ๋ฉด 256๊ฐœ๋ฅผ ๋‹ค ๋Œ๋ ค๋„ ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๊ฑธ๋ฆฐ๋‹ค. ๋ฐ˜๋Œ€๋กœ ๋งํ•˜๋ฉด ํ›„๋ณด๊ฐ€ ์ •ํ™•ํžˆ 1๊ฐœ ๋‚˜์˜ค๋Š”์ง€๊ฐ€ ๋ชจ๋ธ ๊ฒ€์ฆ์ด๋‹ค. ํ—ท๊ฐˆ๋ฆฌ๊ธฐ ์‰ฌ์šด ์„ธ ์ง€์ ์„ ์ผ๋ถ€๋Ÿฌ ํ‹€๋ฆฌ๊ฒŒ ๊ตฌํ˜„ํ•ด ๋Œ๋ ค ๋ดค๋‹ค.

#!/usr/bin/env python3
"""์ฝ๊ธฐ ์‰ฌ์šด ์˜ค๋… ์„ธ ๊ฐ€์ง€๋ฅผ ์‹ค์ œ๋กœ ๋Œ๋ ค ๋ณธ๋‹ค. ์ „๋ถ€ ํ›„๋ณด 0๊ฐœ๊ฐ€ ๋‚˜์˜จ๋‹ค.
 
  A) TABLE2 ๋ฅผ '์ž์—ฐ์Šค๋Ÿฌ์šด' 16์ง„ ์ˆœ์„œ '0'..'9','A'..'F' ๋กœ ์ฝ๋Š” ๊ฒฝ์šฐ
  B) ์…€๋ ‰ํ„ฐ๋ฅผ ๋ฆฌํ‹€์—”๋””์–ธ uint32 ๋กœ ์ฝ๋Š” ๊ฒฝ์šฐ
  C) ๊ฒ€์‚ฌ ๋Œ€์ƒ ๋ฌธ์ž์—ด ๊ธธ์ด๋ฅผ 31 ์ด ์•„๋‹ˆ๋ผ 32 ๋กœ ์žก๋Š” ๊ฒฝ์šฐ
"""
from solve import ALPHABET, TABLE1, TABLE2, TARGET, hexval, k256, selector
 
NATURAL = [ord(c) for c in "0123456789ABCDEF"]      # ์˜ค๋… A ์šฉ
 
 
def count_hits(table2, endian="big", length=31):
    tbl = {}
    for a in ALPHABET:
        for b in ALPHABET:
            sig = "dummy%s(%s)" % (a, TABLE1[hexval(b)])
            raw = k256(sig.encode())[:4]
            sel = int.from_bytes(raw, endian)
            tbl[(a, b)] = chr(table2[sel % 16])
    hits = []
    for c0 in ALPHABET:
        for c1 in ALPHABET:
            s = [c0, c1]
            while len(s) < length:
                s.append(tbl[(s[-2], s[-1])])
            out = "".join(s)
            if int.from_bytes(k256(b"DH{" + out.encode() + b"}"), "big") == TARGET:
                hits.append(out)
    return hits
 
 
for label, kw in (
    ("A) TABLE2 ๋ฅผ '0'..'9','A'..'F' ์ˆœ์„œ๋กœ ์˜ค๋…", dict(table2=NATURAL)),
    ("B) ์…€๋ ‰ํ„ฐ๋ฅผ ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ์˜ค๋…      ", dict(table2=TABLE2, endian="little")),
    ("C) ๊ธธ์ด๋ฅผ 32 ๋กœ ์˜ค๋…                 ", dict(table2=TABLE2, length=32)),
    ("์ •๋‹ต ๋ชจ๋ธ                            ", dict(table2=TABLE2)),
):
    hits = count_hits(**kw)
    print("  %s -> ํ›„๋ณด %d๊ฐœ %s" % (label, len(hits), ("DH{%s}" % hits[0]) if hits else ""))
python3 pitfall.py

pitfall.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” TABLE2 ์ˆœ์„œ ์˜ค๋…, ์…€๋ ‰ํ„ฐ ๋ฆฌํ‹€์—”๋””์–ธ ์˜ค๋…, ๊ธธ์ด 32 ์˜ค๋… ์„ธ ๊ฒฝ์šฐ๋Š” ๋ชจ๋‘ ํ›„๋ณด 0๊ฐœ์ด๊ณ  ์ •๋‹ต ๋ชจ๋ธ๋งŒ ํ›„๋ณด 1๊ฐœ๋กœ ํ”Œ๋ž˜๊ทธ ๋ณธ๋ฌธ ABCB69B964FA423A7BCB69B964FA423 ์„ ๋ฝ‘๋Š”๋‹ค
pitfall.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” TABLE2 ์ˆœ์„œ ์˜ค๋…, ์…€๋ ‰ํ„ฐ ๋ฆฌํ‹€์—”๋””์–ธ ์˜ค๋…, ๊ธธ์ด 32 ์˜ค๋… ์„ธ ๊ฒฝ์šฐ๋Š” ๋ชจ๋‘ ํ›„๋ณด 0๊ฐœ์ด๊ณ  ์ •๋‹ต ๋ชจ๋ธ๋งŒ ํ›„๋ณด 1๊ฐœ๋กœ ํ”Œ๋ž˜๊ทธ ๋ณธ๋ฌธ ABCB69B964FA423A7BCB69B964FA423 ์„ ๋ฝ‘๋Š”๋‹ค

TABLE2๋Š” 'A'๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋‹ˆ '0'..'9','A'..'F' ์ˆœ์„œ๋กœ ์ฝ์œผ๋ฉด ์–ด๊ธ‹๋‚œ๋‹ค. bytes4๋ฅผ uint32๋กœ ๋ฐ”๊พธ๋Š” Solidity ๋ณ€ํ™˜์€ ๋น„ํŠธ ํ‘œํ˜„์„ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฏ€๋กœ ๋น…์—”๋””์–ธ์ด๊ณ , ๋ฆฌํ‹€์—”๋””์–ธ์œผ๋กœ ์ฝ์œผ๋ฉด ๋‚˜๋จธ์ง€๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. ๊ฒ€์‚ฌ ๋Œ€์ƒ์€ ์ž…๋ ฅ ์ „์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ 31๋ฐ”์ดํŠธ์งœ๋ฆฌ output์ด๋‹ค. ์…‹ ๋‹ค ์ฆ์ƒ์ด ๋˜‘๊ฐ™์ด "์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋‚˜์˜ด"์ด๋ผ, ์–ด๋””๊ฐ€ ํ‹€๋ ธ๋Š”์ง€ ์•Œ๋ ค ์ฃผ๋Š” ์‹ ํ˜ธ๊ฐ€ ์—†๋‹ค.


๐Ÿš€ Full Exploit

์ „์ˆ˜๋ผ๊ณ  ํ•ด ๋ด์•ผ 256๊ฐœ๋‹ค. ๊ฐ ์‹œ์ž‘์Œ์œผ๋กœ 31๊ธ€์ž๋ฅผ ์ „๊ฐœํ•˜๊ณ  keccak256("DH{" + s + "}")๋ฅผ ๋ชฉํ‘œ ์ƒ์ˆ˜์™€ ๋น„๊ตํ•œ๋‹ค.

#!/usr/bin/env python3
"""dreamthereum (Dreamhack, Platinum 4 / reversing) โ€” solver
 
challenge.sol ์˜ flagChecker ๋Š” flag ๋ฅผ 31๋ฐ”์ดํŠธ๋กœ ์ฝ์œผ๋ฉด์„œ
    flag[i+2] == internalCall(flag[i], flag[i+1])   (i = 0 .. 28)
์„ ๊ฐ•์ œํ•œ๋‹ค. internalCall ์€ "dummy<A>(<TABLE1[hex(B)]>)" ๋ผ๋Š” ์‹œ๊ทธ๋‹ˆ์ฒ˜๋ฅผ ๋ฌธ์ž์—ด๋กœ
์กฐ๋ฆฝํ•ด ์ž๊ธฐ ์ž์‹ ์„ call ํ•˜๊ณ , ๋ถˆ๋ฆฐ ๋”๋ฏธ ํ•จ์ˆ˜๋Š” TABLE2[uint32(msg.sig) % 16] ์„ ๋Œ๋ ค์ค€๋‹ค.
 
์ฆ‰ f(A,B) = TABLE2[ uint32(keccak256("dummy"+A+"("+TABLE1[hex(B)]+")")[:4]) % 16 ] ์ด๊ณ ,
31๊ธ€์ž ์ „์ฒด๊ฐ€ ์•ž ๋‘ ๊ธ€์ž๋งŒ์œผ๋กœ ๊ฒฐ์ •๋œ๋‹ค โ†’ ํ›„๋ณด๋Š” 16*16 = 256๊ฐœ๋ฟ.
๋งˆ์ง€๋ง‰ ๊ด€๋ฌธ keccak256("DH{" + output + "}") == 0xe6a9...7806 ๋กœ ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅธ๋‹ค.
"""
from Crypto.Hash import keccak
 
TABLE1 = ["uint256", "bytes", "bytes4", "bytes1", "bytes32", "uint8",
          "uint16", "uint248", "string", "uint240", "uint224", "uint192",
          "uint24", "uint32", "uint56", "uint72"]
# 0x41..0x46 = 'A'..'F', 0x30..0x39 = '0'..'9'
TABLE2 = [0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
          0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39]
 
ALPHABET = "0123456789ABCDEF"          # getArgsFromB ๊ฐ€ ํ—ˆ์šฉํ•˜๋Š” ๋ฌธ์ž ์ง‘ํ•ฉ
TARGET = 0xe6a9e5bcd806b56f7ab007dbb1e881f5b5746f00cb940b8083be861a257f7806
 
 
def k256(data: bytes) -> bytes:
    h = keccak.new(digest_bits=256)
    h.update(data)
    return h.digest()
 
 
def hexval(ch: str) -> int:
    """getArgsFromB: '0'-'9' -> 0..9, 'A'-'F' -> 10..15"""
    o = ord(ch)
    if 0x41 <= o < 0x41 + 6:
        return o - 0x41 + 0xa
    if 0x30 <= o <= 0x39:
        return o - 0x30
    raise ValueError("BOOOOM!")
 
 
def selector(sig: str) -> int:
    """abi.encodeWithSignature ์˜ ์•ž 4๋ฐ”์ดํŠธ = uint32(msg.sig)"""
    return int.from_bytes(k256(sig.encode())[:4], "big")
 
 
def build_table():
    """f[A][B] = internalCall(A, B) ์˜ ๋ฐ˜ํ™˜ ๋ฌธ์ž"""
    table = {}
    for a in ALPHABET:
        for b in ALPHABET:
            sig = "dummy%s(%s)" % (a, TABLE1[hexval(b)])
            table[(a, b)] = chr(TABLE2[selector(sig) % len(TABLE2)])
    return table
 
 
def expand(c0: str, c1: str, table, length: int = 31) -> str:
    s = [c0, c1]
    while len(s) < length:
        s.append(table[(s[-2], s[-1])])
    return "".join(s)
 
 
def main():
    table = build_table()
 
    # 256๊ฐœ ์‹œ์ž‘์Œ ์ „์ˆ˜
    hits = []
    for c0 in ALPHABET:
        for c1 in ALPHABET:
            out = expand(c0, c1, table)
            digest = k256(b"DH{" + out.encode() + b"}")
            if int.from_bytes(digest, "big") == TARGET:
                hits.append(out)
 
    for out in hits:
        flag = "DH{%s}" % out
        print("[+] output   =", out, "(len %d)" % len(out))
        print("[+] keccak   =", k256(flag.encode()).hex())
        print("[+] FLAG     =", flag)
 
    if not hits:
        print("[-] ํ›„๋ณด ์—†์Œ")
        return
 
    # ์ž์ฒด๊ฒ€์‚ฐ: ์ฐพ์€ flag ๋ฅผ flagChecker ๋กœ์ง ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ๋Œ๋ ค ๋ณธ๋‹ค
    out = hits[0]
    for i in range(len(out) - 2):
        assert table[(out[i], out[i + 1])] == out[i + 2], i
    print("[+] self-check: flag[i+2] == f(flag[i], flag[i+1]) for all i โ€” OK")
 
 
if __name__ == "__main__":
    main()
python3 solve.py

solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” 31๊ธ€์ž output ABCB69B964FA423A7BCB69B964FA423, ๊ทธ keccak ํ•ด์‹œ e6a9e5bcโ€ฆ7806, ์ตœ์ข… FLAG, ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์Šฌ ์กฐ๊ฑด์„ ๋ชจ๋“  i์— ๋Œ€ํ•ด ๋‹ค์‹œ ๊ฒ€์‚ฌํ•œ self-check OK๊ฐ€ ์ถœ๋ ฅ๋๋‹ค
solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” 31๊ธ€์ž output ABCB69B964FA423A7BCB69B964FA423, ๊ทธ keccak ํ•ด์‹œ e6a9e5bcโ€ฆ7806, ์ตœ์ข… FLAG, ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์Šฌ ์กฐ๊ฑด์„ ๋ชจ๋“  i์— ๋Œ€ํ•ด ๋‹ค์‹œ ๊ฒ€์‚ฌํ•œ self-check OK๊ฐ€ ์ถœ๋ ฅ๋๋‹ค

DH{ABCB69B964FA423A7BCB69B964FA423}

๋ณต์›๋œ ๋ฌธ์ž์—ด์„ ๋ณด๋ฉด A7 ์ดํ›„ BCB69B964FA423์ด ๊ทธ๋Œ€๋กœ ๋ฐ˜๋ณต๋œ๋‹ค. ์ „์ดํ•จ์ˆ˜๊ฐ€ ์œ ํ•œํ•œ ํ‘œ๋ผ์„œ ์ฐฝ์ด ์ด๋ฏธ ์ง€๋‚˜์˜จ ์Œ์„ ๋‹ค์‹œ ๋งŒ๋‚˜๋ฉด ๊ทธ๋•Œ๋ถ€ํ„ฐ ์ฃผ๊ธฐ์— ๊ฐ‡ํžŒ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์ฃผ๊ธฐ 14๋กœ ์ˆœํ™˜ํ–ˆ๋‹ค.



โœ… ์‹ค์ œ EVM ์œ„์—์„œ ํ™•์ธ

๊ณ„์‚ฐ์œผ๋กœ ๋‚˜์˜จ ๊ฐ’์ด๋‹ˆ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์ง„์งœ true๋ฅผ ์ฃผ๋Š”์ง€๋„ ๋ด์•ผ ํ•œ๋‹ค. ๋ฌธ์ œ ์„œ๋ฒ„๊ฐ€ ์—†๋Š” ์œ ํ˜•์ด๋ผ ๋กœ์ปฌ์—์„œ EVM์„ ๋„์› ๋‹ค. foundry ์ด๋ฏธ์ง€๋ฅผ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฉด solc ์„ค์น˜๊นŒ์ง€ ์•Œ์•„์„œ ํ•œ๋‹ค.

์—ฌ๊ธฐ์„œ ๊ฑธ๋ฆฌ๋Š” ๊ฒŒ internalCall์˜ onlyOwner๋‹ค. owner๋Š” ์ƒ์„ฑ์ž์—์„œ msg.sender๋กœ ๋ฐ•ํžˆ๋ฏ€๋กœ, ํ…Œ์ŠคํŠธ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์ง์ ‘ Challenge๋ฅผ ๋ฐฐํฌํ•˜๋ฉด ๊ทธ ํ…Œ์ŠคํŠธ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ owner๊ฐ€ ๋œ๋‹ค. forge-std ์—†์ด ์ˆœ์ˆ˜ Solidity๋งŒ์œผ๋กœ ์งฐ๋‹ค.

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
 
import "../src/Challenge.sol";
 
// forge-std ์—†์ด ๋„๋Š” ํ…Œ์ŠคํŠธ. ์ด ์ปจํŠธ๋ž™ํŠธ๊ฐ€ Challenge ๋ฅผ ๋ฐฐํฌํ•˜๋ฏ€๋กœ owner == ์ด ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๋˜์–ด
// internalCall ์˜ onlyOwner ๋ฅผ ํ†ต๊ณผํ•œ๋‹ค.
contract FlagTest {
    Challenge c;
 
    function setUp() public {
        c = new Challenge();
    }
 
    // ์ •๋‹ต flag โ†’ flagChecker ๊ฐ€ true
    function test_correct_flag_returns_true() public {
        bool ok = c.flagChecker(bytes("ABCB69B964FA423A7BCB69B964FA423"));
        require(ok, "flagChecker returned false");
    }
 
    // ๋งˆ์ง€๋ง‰ ํ•œ ๊ธ€์ž๋งŒ ๋ฐ”๊ฟ”๋„ i=28 ์˜ require ์—์„œ BOBOBOBO!!! ๋กœ revert
    function test_last_byte_flipped_reverts_BOBOBOBO() public {
        try c.flagChecker(bytes("ABCB69B964FA423A7BCB69B964FA424")) returns (bool ok) {
            require(!ok, "tampered flag was accepted");
        } catch Error(string memory reason) {
            require(
                keccak256(bytes(reason)) == keccak256(bytes("BOBOBOBO!!!")),
                "unexpected revert reason"
            );
        }
    }
 
    // 16์ง„ ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ ๋ฐ”์ดํŠธ๊ฐ€ ์ธ์žํƒ€์ž… ์ž๋ฆฌ์— ์˜ค๋ฉด getArgsFromB ๊ฐ€ BOOOOM!
    function test_non_hex_byte_reverts_BOOOOM() public {
        try c.flagChecker(bytes("AZCB69B964FA423A7BCB69B964FA423")) returns (bool ok) {
            require(!ok, "non-hex flag was accepted");
        } catch Error(string memory reason) {
            require(
                keccak256(bytes(reason)) == keccak256(bytes("BOOOOM!")),
                "unexpected revert reason"
            );
        }
    }
}
docker run --rm -e FOUNDRY_DISABLE_NIGHTLY_WARNING=1 -v "$PWD":/w -w /w --entrypoint forge ghcr.io/foundry-rs/foundry:latest test -vv

forge test ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solc 0.8.36์œผ๋กœ ์ปดํŒŒ์ผํ•œ ๋’ค ์ •๋‹ต flag๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ…Œ์ŠคํŠธ, ๋งˆ์ง€๋ง‰ ๊ธ€์ž๋ฅผ ๋ฐ”๊พธ๋ฉด BOBOBOBO๋กœ revertํ•˜๋Š” ํ…Œ์ŠคํŠธ, ๋น„-16์ง„ ๋ฐ”์ดํŠธ๊ฐ€ BOOOOM์œผ๋กœ revertํ•˜๋Š” ํ…Œ์ŠคํŠธ 3๊ฐœ๊ฐ€ ๋ชจ๋‘ PASSํ–ˆ๋‹ค
forge test ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solc 0.8.36์œผ๋กœ ์ปดํŒŒ์ผํ•œ ๋’ค ์ •๋‹ต flag๊ฐ€ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ…Œ์ŠคํŠธ, ๋งˆ์ง€๋ง‰ ๊ธ€์ž๋ฅผ ๋ฐ”๊พธ๋ฉด BOBOBOBO๋กœ revertํ•˜๋Š” ํ…Œ์ŠคํŠธ, ๋น„-16์ง„ ๋ฐ”์ดํŠธ๊ฐ€ BOOOOM์œผ๋กœ revertํ•˜๋Š” ํ…Œ์ŠคํŠธ 3๊ฐœ๊ฐ€ ๋ชจ๋‘ PASSํ–ˆ๋‹ค

3๊ฐœ ์ „๋ถ€ ํ†ต๊ณผ. ๋ฐ˜ํ™˜๊ฐ’๊ณผ ์‹คํŒจ ๊ฒฝ๋กœ๊นŒ์ง€ ์‹ค์ œ EVM์—์„œ ํ™•์ธ๋œ ์…ˆ์ด๋‹ค.

ํ˜ธ์ถœ ํŠธ๋ ˆ์ด์Šค๋ฅผ ๋ณด๋ฉด ์ปจํŠธ๋ž™ํŠธ ์•ˆ์—์„œ ๋ฌด์Šจ ์ผ์ด ๋ฒŒ์–ด์ง€๋Š”์ง€ ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค. -vvvv๋กœ ์ผœ๊ณ , ๊ฐ€์šด๋ฐ ๋ฐ˜๋ณต ๊ตฌ๊ฐ„์€ ์ž˜๋ผ์„œ ๋ดค๋‹ค.

docker run --rm -e FOUNDRY_DISABLE_NIGHTLY_WARNING=1 -v "$PWD":/w -w /w --entrypoint forge ghcr.io/foundry-rs/foundry:latest test --match-test test_correct_flag_returns_true -vvvv > trace.txt 2>&1; sed -n "/^Traces:/,+13p" trace.txt; echo "    ...(์ค‘๋žต: dummy ํ˜ธ์ถœ์€ ๋ชจ๋‘ 29ํšŒ)..."; tail -9 trace.txt

forge test -vvvv ํ˜ธ์ถœ ํŠธ๋ ˆ์ด์Šค โ€” flagChecker ์•ˆ์—์„œ dummyA, dummyB, dummyC, dummyB, dummy6, dummy9๊ฐ€ ์ฐจ๋ก€๋กœ ํ˜ธ์ถœ๋˜๊ณ  ๊ฐ๊ฐ 67, 66, 54, 57, 66์„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๋งˆ์ง€๋ง‰์— flagChecker๊ฐ€ Return true๋กœ ๋๋‚œ๋‹ค
forge test -vvvv ํ˜ธ์ถœ ํŠธ๋ ˆ์ด์Šค โ€” flagChecker ์•ˆ์—์„œ dummyA, dummyB, dummyC, dummyB, dummy6, dummy9๊ฐ€ ์ฐจ๋ก€๋กœ ํ˜ธ์ถœ๋˜๊ณ  ๊ฐ๊ฐ 67, 66, 54, 57, 66์„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๋งˆ์ง€๋ง‰์— flagChecker๊ฐ€ Return true๋กœ ๋๋‚œ๋‹ค

์ฒซ ํ˜ธ์ถœ์ด Challenge::dummyA(0) โ†’ Return 67์ด๋‹ค. 67์€ 0x43, ๊ณง 'C'๋‹ค. ์•ž์—์„œ ํ‘œ๋กœ ๋ฝ‘์€ f('A','B') = 'C'๊ฐ€ EVM ์•ˆ์—์„œ ๊ทธ๋Œ€๋กœ ์žฌํ˜„๋œ๋‹ค. ํŠธ๋ ˆ์ด์Šค ๋์˜ โ† [Return] true๊ฐ€ flagChecker์˜ ๋ฐ˜ํ™˜๊ฐ’์ด๋‹ค.

๋งˆ์ง€๋ง‰ ๊ด€๋ฌธ์ธ keccak256๋„ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์„์— ์ ํžŒ ๊ทธ ์˜จ๋ผ์ธ ๋„๊ตฌ๋กœ ํ•œ ๋ฒˆ ๋” ํ™•์ธํ–ˆ๋‹ค. ์ถœ์ œ์ž๊ฐ€ challenge.sol 1082ํ–‰์— URL์„ ๋‚จ๊ฒจ ๋’€๋‹ค.

emn178 Keccak-256 ์˜จ๋ผ์ธ ๋„๊ตฌ ํ™”๋ฉด โ€” ์ž…๋ ฅ์ฐฝ์— DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ”Œ๋ž˜๊ทธ ๋ฌธ์ž์—ด ABCB69B964FA423A7BCB69B964FA423 ์„ ๋„ฃ์ž ์ถœ๋ ฅ์ด e6a9e5bcd806b56f7ab007dbb1e881f5b5746f00cb940b8083be861a257f7806์œผ๋กœ ์ปจํŠธ๋ž™ํŠธ ์ƒ์ˆ˜์™€ ์ผ์น˜ํ•œ๋‹ค
emn178 Keccak-256 ์˜จ๋ผ์ธ ๋„๊ตฌ ํ™”๋ฉด โ€” ์ž…๋ ฅ์ฐฝ์— DH ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ”Œ๋ž˜๊ทธ ๋ฌธ์ž์—ด ABCB69B964FA423A7BCB69B964FA423 ์„ ๋„ฃ์ž ์ถœ๋ ฅ์ด e6a9e5bcd806b56f7ab007dbb1e881f5b5746f00cb940b8083be861a257f7806์œผ๋กœ ์ปจํŠธ๋ž™ํŠธ ์ƒ์ˆ˜์™€ ์ผ์น˜ํ•œ๋‹ค


โ™ป๏ธ ์žฌํ˜„

๋ฌธ์ œ ํด๋”๋งŒ ์žˆ์œผ๋ฉด flag๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๋‚˜์˜ค๊ฒŒ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‚จ๊ฒผ๋‹ค. solve.py๋Š” keccak๋งŒ ์žˆ์œผ๋ฉด ๋˜๊ณ (ํŒŒ์ด์ฌ pycryptodome), docker์— foundry ์ด๋ฏธ์ง€๊ฐ€ ์žˆ์œผ๋ฉด EVM ๊ฒ€์ฆ๊นŒ์ง€ ์ด์–ด์„œ ๋ˆ๋‹ค.

#!/usr/bin/env bash
# dreamthereum (Dreamhack, Platinum 4 / reversing) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
#   solve.py ๋กœ flag ๋ฅผ ๋ณต์›ํ•˜๊ณ , docker foundry ๊ฐ€ ์žˆ์œผ๋ฉด ์‹ค์ œ EVM ์œผ๋กœ ํ•œ ๋ฒˆ ๋” ๊ฒ€์ฆํ•œ๋‹ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
# 1) ์˜คํ”„๋ผ์ธ ๋ณต์› โ€” keccak256 ๋งŒ ์žˆ์œผ๋ฉด ๋œ๋‹ค (pycryptodome)
OUT=$(timeout 300 python3 solve.py 2>&1) || true
echo "$OUT"
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
 
# 2) (์„ ํƒ) ์‹ค์ œ EVM ๊ฒ€์ฆ โ€” foundry ์ด๋ฏธ์ง€๊ฐ€ ์ด๋ฏธ ๋ฐ›์•„์ ธ ์žˆ์„ ๋•Œ๋งŒ ๋ˆ๋‹ค
if command -v docker >/dev/null 2>&1 && docker image inspect ghcr.io/foundry-rs/foundry:latest >/dev/null 2>&1; then
  echo
  echo "--- forge test (์‹ค์ œ EVM) ---"
  ( cd evm && timeout 600 docker run --rm -v "$PWD":/w -w /w \
      --entrypoint forge ghcr.io/foundry-rs/foundry:latest test -vv ) || true
  docker run --rm -v "$PWD/evm":/w alpine:latest rm -rf /w/out /w/cache >/dev/null 2>&1 || true
else
  echo
  echo "(docker/foundry ์—†์Œ โ€” EVM ๊ฒ€์ฆ์€ ๊ฑด๋„ˆ๋›ด๋‹ค. solve.py ๋งŒ์œผ๋กœ flag ๋Š” ๋‚˜์˜จ๋‹ค)"
fi
 
echo
if [ -n "$FLAG" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
  echo "โœ… PASS  $FLAG"; exit 0
fi
echo "โŒ FAIL (์–ป์€ ๊ฐ’: '${FLAG:-์—†์Œ}' / ๊ธฐ๋Œ€: '$EXPECT')"; exit 1
./reproduce.sh

reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solve.py๊ฐ€ flag๋ฅผ ๋ณต์›ํ•˜๊ณ  ์ด์–ด์„œ forge test 3๊ฐœ๊ฐ€ ๋ชจ๋‘ PASSํ•œ ๋’ค ๋งˆ์ง€๋ง‰ ์ค„์— PASS ์™€ ํ”Œ๋ž˜๊ทธ ๋ณธ๋ฌธ ABCB69B964FA423A7BCB69B964FA423 ์ด ์ฐํ˜”๋‹ค
reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ โ€” solve.py๊ฐ€ flag๋ฅผ ๋ณต์›ํ•˜๊ณ  ์ด์–ด์„œ forge test 3๊ฐœ๊ฐ€ ๋ชจ๋‘ PASSํ•œ ๋’ค ๋งˆ์ง€๋ง‰ ์ค„์— PASS ์™€ ํ”Œ๋ž˜๊ทธ ๋ณธ๋ฌธ ABCB69B964FA423A7BCB69B964FA423 ์ด ์ฐํ˜”๋‹ค

๋กœ์ปฌ ํ™˜๊ฒฝ: Ubuntu 6.17 ์ปค๋„ / Python 3.13 + pycryptodome / Docker 29.7.2 + ghcr.io/foundry-rs/foundry:latest(solc 0.8.36 ์ž๋™ ์„ค์น˜). challenge.sol์€ evm/src/Challenge.sol๋กœ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•ด์„œ ์“ด๋‹ค.



๐Ÿ“ ๊ฒฐ๋ก 

์…€๋ ‰ํ„ฐ๋Š” ์ฃผ์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ ๊ฐ’์œผ๋กœ๋„ ์“ธ ์ˆ˜ ์žˆ๋‹ค

์ด ๋ฌธ์ œ์˜ ์žฌ๋ฏธ๋Š” keccak256 ํ•ด์‹œ์˜ ์•ž 4๋ฐ”์ดํŠธ๋ฅผ ๋‚œ์ˆ˜ํ‘œ์ฒ˜๋Ÿผ ๊ตด๋ฆฐ ๋ฐ ์žˆ๋‹ค. ํ•จ์ˆ˜ ์ด๋ฆ„๊ณผ ์ธ์ž ํƒ€์ž…์ด ํ•œ ๊ธ€์ž์”ฉ ๋Œ€์‘๋˜๋‹ˆ "๋ฌธ์ž ๋‘ ๊ฐœ โ†’ ๋ฌธ์ž ํ•˜๋‚˜"๋ผ๋Š” ์ „์ด๊ฐ€ ๋งŒ๋“ค์–ด์ง€๊ณ , ๊ทธ ์ „์ด๊ฐ€ ์‚ฌ์Šฌ๋กœ ์—ฎ์ด๋ฉด์„œ 31๊ธ€์ž์งœ๋ฆฌ ๋‹ต์ด ์•ž ๋‘ ๊ธ€์ž๋กœ ์••์ถ•๋œ๋‹ค. ๋ฏธ๋ผ 1024์ค„์€ ์ด ๊ตฌ์กฐ๋ฅผ ๋ฎ๊ธฐ ์œ„ํ•œ ๊ป๋ฐ๊ธฐ๋‹ค.

ํƒ์ƒ‰ ๊ณต๊ฐ„์„ ์ค„์ด๋Š” ๊ฑด ์ œ์•ฝ ์ž์ฒด๋‹ค

require๊ฐ€ ๋งค ํšŒ ๋‹ค์Œ ๊ธ€์ž๋ฅผ ๋ชป ๋ฐ•๋Š” ์ˆœ๊ฐ„ ์ž์œ ๋„๋Š” ๋‘ ๊ธ€์ž๋กœ ๋–จ์–ด์ง„๋‹ค. ์ด๋Ÿฐ ๋ฌธ์ œ์—์„œ ๋จผ์ € ๋ณผ ๊ฒƒ์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์˜ ๋ณต์žก๋„๊ฐ€ ์•„๋‹ˆ๋ผ ์ž…๋ ฅ์˜ ์–ด๋А ๋ถ€๋ถ„์ด ์ •๋ง ์ž์œ ๋กœ์šด๊ฐ€๋‹ค. ๋ฐ˜๋Œ€๋กœ ๋ถ€๋ถ„ ์ •๋‹ต ์‹ ํ˜ธ๊ฐ€ ์ „ํ˜€ ์—†๋‹ค๋Š” ์  ๋•Œ๋ฌธ์—, ๋ชจ๋ธ์ด ํ‹€๋ฆฌ๋ฉด ํ›„๋ณด 0๊ฐœ๋ผ๋Š” ๊ฒฐ๊ณผ๋งŒ ๋‚จ๋Š”๋‹ค. ๊ทธ๋ž˜์„œ "ํ›„๋ณด๊ฐ€ ์ •ํ™•ํžˆ 1๊ฐœ"๋ผ๋Š” ์‚ฌ์‹ค์ด ๊ณง ๋ชจ๋ธ ๊ฒ€์ฆ์ด ๋œ๋‹ค.

์˜จ์ฒด์ธ์— ์˜ฌ๋ผ๊ฐ„ ๊ฒƒ์— ๋น„๋ฐ€์€ ์—†๋‹ค

flagChecker๋Š” onlyOwner๋กœ ๊ฐ์‹ธ์—ฌ ์žˆ์–ด ์•„๋ฌด๋‚˜ ๋ถ€๋ฅผ ์ˆ˜ ์—†์ง€๋งŒ, ๊ฒ€์ฆ์— ์“ฐ์ด๋Š” ์ƒ์ˆ˜์™€ ๋กœ์ง์€ ์ „๋ถ€ ์ฝ”๋“œ ์•ˆ์— ์žˆ๋‹ค. ์ปจํŠธ๋ž™ํŠธ ๋ฐ”์ดํŠธ์ฝ”๋“œ๋Š” ๋ˆ„๊ตฌ๋‚˜ ์ฝ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ ‘๊ทผ ์ œ์–ด๋Š” ์‹คํ–‰ ๊ถŒํ•œ์„ ๋ง‰์„ ๋ฟ ์ •๋ณด๋ฅผ ๊ฐ์ถ”์ง€ ๋ชปํ•œ๋‹ค. ๋‹ต์„ ์ปจํŠธ๋ž™ํŠธ ์•ˆ์—์„œ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด ๋‘๋ฉด ๊ทธ ๊ณ„์‚ฐ์€ ์˜คํ”„๋ผ์ธ์—์„œ๋„ ๋˜‘๊ฐ™์ด ๋Œ์•„๊ฐ„๋‹ค.

์˜ค๋ฒ„๋กœ๋”ฉ๊ณผ ์…€๋ ‰ํ„ฐ ์ถฉ๋Œ

๊ฐ™์€ ์ด๋ฆ„์„ 16๊ฐ€์ง€ ํƒ€์ž…์œผ๋กœ ์˜ค๋ฒ„๋กœ๋“œํ•ด๋„ ์…€๋ ‰ํ„ฐ๊ฐ€ ๊ฒน์น˜์ง€ ์•Š์•˜๊ธฐ์— ์ด ๊ตฌ์กฐ๊ฐ€ ์„ฑ๋ฆฝํ•œ๋‹ค. ์‹ค์ œ ์„œ๋น„์Šค ์ปจํŠธ๋ž™ํŠธ๋ผ๋ฉด ๋ฐ˜๋Œ€๋กœ ์œ„ํ—˜ ์‹ ํ˜ธ๋‹ค. 4๋ฐ”์ดํŠธ๋Š” ์ถฉ๋Œ์ด ์–ผ๋งˆ๋“ ์ง€ ๋งŒ๋“ค์–ด์งˆ ์ˆ˜ ์žˆ๋Š” ๊ณต๊ฐ„์ด๊ณ , ํ”„๋ก์‹œ ํŒจํ„ด์—์„œ ์…€๋ ‰ํ„ฐ ์ถฉ๋Œ์€ ์‹ค์ œ ์‚ฌ๊ณ ๋กœ ์ด์–ด์ง„ ์ ์ด ์žˆ๋‹ค. ๋ฐฐํฌ ์ „์— forge inspect <์ปจํŠธ๋ž™ํŠธ> methods๋กœ ์…€๋ ‰ํ„ฐ ํ‘œ๋ฅผ ๋ฝ‘์•„ ๋‘๋ฉด ์ตœ์†Œํ•œ ๊ฒน์นจ์€ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๊ธ€์ด ๋„์›€์ด ๋๋‚˜์š”?

Comments

๋Œ“๊ธ€

0๊ฐœ

๋Œ“๊ธ€์„ ๋‚จ๊ธฐ๋ ค๋ฉด ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ด์š”. (๋„ค์ด๋ฒ„ ยท ๊ตฌ๊ธ€ ๊ณ„์ •)

๋Œ“๊ธ€ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘โ€ฆ

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿ’  Platinum 3] ํ•œ๊ธ€ ๊ฒฉ์ž๊ฐ€ ํ†ต์งธ๋กœ 2D ๋ฐ”์ดํŠธ์ฝ”๋“œ โ€” DreamHack ๋‚˜๋ž๋ง์‹ธ๋ฏธ ํ’€์ด
blog

[๐Ÿ’  Platinum 3] ํ•œ๊ธ€ ๊ฒฉ์ž๊ฐ€ ํ†ต์งธ๋กœ 2D ๋ฐ”์ดํŠธ์ฝ”๋“œ โ€” DreamHack ๋‚˜๋ž๋ง์‹ธ๋ฏธ ํ’€์ด

ํ•œ๊ธ€๋งŒ ๋นฝ๋นฝํ•œ ํ…์ŠคํŠธ ํŒŒ์ผ๊ณผ C++ ๋ฐ”์ด๋„ˆ๋ฆฌ ํ•˜๋‚˜๊ฐ€ ๋ฐฐํฌ๋œ๋‹ค. ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ๊ทธ ํŒŒ์ผ์„ 53ํ–‰ 34์—ด ๊ฒฉ์ž๋กœ ์ฝ์–ด ์ดˆ์„ฑ์„ ๋ช…๋ น, ์ค‘์„ฑ์„ ์ด๋™ ๋ฐฉํ–ฅ, ์ข…์„ฑ์„ ํ”ผ์—ฐ์‚ฐ์ž๋กœ ํ•ด์„ํ•˜๋Š” 2D VM ์ด์—ˆ๋‹ค. ๊ฒฉ์ž๋ฅผ ๋””์Šค์–ด์…ˆ๋ธ”ํ•ด ๋ณด๋‹ˆ ์ขŒ์ƒ๋‹จ 6x6 ์ด ์†Œ์ˆ˜ ์Šค๋„์ฟ  ํผ์ฆํŒ์ด๊ณ , ๋นˆ์นธ 17๊ฐœ๊ฐ€ ๊ณง ์ž…๋ ฅ 17๋ฐ”์ดํŠธ์˜€๋‹ค.
#dreamhack#ctf#reversing+6
2026-08-20#dreamhack +3
[๐Ÿฅˆ Silver 3] 2MB ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ฝ”๋“œ๋Š” 509๋ฐ”์ดํŠธ๋ฟ์ด์—ˆ๋‹ค โ€” DreamHack Branches and Leaves ํ’€์ด
blog

[๐Ÿฅˆ Silver 3] 2MB ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ฝ”๋“œ๋Š” 509๋ฐ”์ดํŠธ๋ฟ์ด์—ˆ๋‹ค โ€” DreamHack Branches and Leaves ํ’€์ด

๋ฌธ์ œ ์„ค๋ช…์ด ์‹œ ๋„ค ์ค„๋ฟ์ธ ๋ฆฌ๋ฒ„์‹ฑ. 2.1MB์งœ๋ฆฌ stripped ELF๋ฅผ ์—ด์–ด ๋ณด๋ฉด ์ฝ”๋“œ๋Š” 509๋ฐ”์ดํŠธ๊ณ  ๋‚˜๋จธ์ง€ ์ „๋ถ€๊ฐ€ ๋ฐฐ์—ด๋กœ ์ ‘์€ ๊นŠ์ด 16 ์ด์ง„ํŠธ๋ฆฌ๋‹ค. ์ž…๋ ฅ 4๊ธ€์ž๊ฐ€ 16๋น„ํŠธ๋กœ ํŽด์ ธ ํŠธ๋ฆฌ๋ฅผ 16๋ฒˆ ๋‚ด๋ ค๊ฐ€๊ณ , ๊ทธ ๋ฆฌํ”„ 65536๊ฐœ๊ฐ€ ์„œ๋กœ ๊ฒน์น˜์ง€ ์•Š์•„์„œ ๊ธฐ๋Œ€๊ฐ’๋งˆ๋‹ค ์—ญ์ƒ์ด ์ •ํ™•ํžˆ ํ•˜๋‚˜๋กœ ์ •ํ•ด์ง„๋‹ค.
#dreamhack#ctf#reversing+5
2026-08-19#dreamhack +3
[๐Ÿ’  Platinum 4] ํ•œ ํ”„๋กœ์„ธ์Šค์—์„œ 32๋น„ํŠธ์™€ 64๋น„ํŠธ๊ฐ€ ๋ฒˆ๊ฐˆ์•„ ๋ˆ๋‹ค โ€” DreamHack conquergent ํ’€์ด
blog

[๐Ÿ’  Platinum 4] ํ•œ ํ”„๋กœ์„ธ์Šค์—์„œ 32๋น„ํŠธ์™€ 64๋น„ํŠธ๊ฐ€ ๋ฒˆ๊ฐˆ์•„ ๋ˆ๋‹ค โ€” DreamHack conquergent ํ’€์ด

๋ฐฐํฌ ํŒŒ์ผ์€ 17KB์งœ๋ฆฌ stripped ELF ํ•˜๋‚˜. 32๋น„ํŠธ i386 ๋ฐ”์ด๋„ˆ๋ฆฌ์ธ๋ฐ .text ์•ˆ์— retf ๊ฐ€ 32๊ฐœ ๋ฐ•ํ˜€ ์žˆ๋‹ค. CS ์…€๋ ‰ํ„ฐ๋ฅผ 0x33 ์œผ๋กœ ๋ฐ€์–ด ๋„ฃ๊ณ  far return ํ•˜๋Š” Heaven's Gate ๋กœ, ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ 64๋น„ํŠธ ๊ฐ€์ ฏ 14์ข…์„ ๊ตด๋ ค ์ž…๋ ฅ 64๋ฐ”์ดํŠธ๋ฅผ qword 8๊ฐœ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค. objdump ๊ฐ€ dec eax ๋กœ ๋ณด์—ฌ ์ฃผ๋Š” 0x48 ์€ REX.W ์ ‘๋‘์‚ฌ์˜€๋‹ค. ๊ฐ€์ ฏ ๊ทœ์•ฝ๊ณผ ์ƒ์ˆ˜ ํ…Œ์ด๋ธ”์„ ๋„๊ตฌ๋กœ ๋ฝ‘์•„ ์„ธ ๋‹จ๊ณ„ ํŒŒ์ดํ”„๋ผ์ธ์„ ๋ณต์›ํ•˜๊ณ , ๊ณฑ์…ˆ ์ƒ์ˆ˜๊ฐ€ ์ „๋ถ€ ํ™€์ˆ˜๋ผ๋Š” ์ ์„ ์ด์šฉํ•ด mod 2^64 ์—ญ์›์œผ๋กœ ๋˜๊ฐ์•„ ํ”Œ๋ž˜๊ทธ๋ฅผ ๋ณต์›ํ–ˆ๋‹ค.
#dreamhack#ctf#reversing+6
2026-08-18#dreamhack +3