[๐Ÿฅ‡ Gold 1] ํ•จ์ˆ˜ 125๊ฐœ์งœ๋ฆฌ 4์ง„ ํŠธ๋ผ์ด๋ฅผ ๋˜๊ฐ๋‹ค โ€” DreamHack Function Network ํ’€์ด

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

[๐Ÿฅ‡ Gold 1] ํ•จ์ˆ˜ 125๊ฐœ์งœ๋ฆฌ 4์ง„ ํŠธ๋ผ์ด๋ฅผ ๋˜๊ฐ๋‹ค โ€” DreamHack Function Network ํ’€์ด

stripped ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ 64๋ฐ”์ดํŠธ ์ž…๋ ฅ์„ 10000๋ฒˆ ๋ณ€ํ˜•ํ•ด ๊ณ ์ •๋œ ๋ชฉํ‘œ๊ฐ’๊ณผ ๋น„๊ตํ•œ๋‹ค. ๋ณ€ํ˜•์˜ ์ข…๋ฅ˜๋Š” .data ์— ๋ฐ•ํžŒ 64๋น„ํŠธ key ๊ฐ€ ์ •ํ•˜๋Š”๋ฐ, ๊ทธ key ๋Š” ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ๋กœ ์งœ์ธ 4์ง„ ํŠธ๋ผ์ด๋ฅผ 2๋น„ํŠธ์”ฉ 32๋ฒˆ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€๋Š” ๊ฒฝ๋กœ์˜€๋‹ค. ๋„์ฐฉ์ง€์ธ ๋ฆฌํ”„ 4๊ฐœ๊ฐ€ ์ „๋ถ€ ์ „๋‹จ์‚ฌ๋ผ ๋ชฉํ‘œ๊ฐ’์—์„œ 10000์Šคํ…์„ ๊ฑฐ๊พธ๋กœ ๊ฐ์œผ๋ฉด ์ž…๋ ฅ์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.

๋ฌธ์ œ: DreamHack โ€” Function Network ๋ถ„๋ฅ˜: reversing ๋‚œ์ด๋„: ๐Ÿฅ‡ Gold 1 FLAG: DH{b510ce2d7599e75f79b6bf613384dda282bcedea47184155e55d1d22139afaab}

๋ฌธ์ œ ์„ค๋ช…์€ ๋”ฑ ํ•œ ์ค„์ด๋‹ค. "Functions form a network, and our values go through it..."

๋ฐ›์€ ๊ฑด stripped ELF ํ•˜๋‚˜๋ฟ์ด๊ณ , ํŒŒ์ผ ํฌ๊ธฐ 283KB ์ค‘ ์‹คํ–‰ ์ฝ”๋“œ๋Š” 25KB๋ฐ–์— ์•ˆ ๋œ๋‹ค. ๋‚˜๋จธ์ง€๋Š” ์ „๋ถ€ .data ๋‹ค. ๊ทธ ๋น„์œจ์ด ์ด ๋ฌธ์ œ์˜ ์„ฑ๊ฒฉ์„ ๊ฑฐ์˜ ๋‹ค ๋งํ•ด ์ค€๋‹ค โ€” ๋กœ์ง์ด ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์ด๋‹ค.

file extracted/chal; readelf -SW extracted/chal | grep -E "Name|\.text|\.rodata|\.data|\.bss"

file ๊ณผ readelf -SW ๋กœ ํ™•์ธํ•œ ๊ธฐ๋ณธ ์ •๋ณด. stripped PIE ์ด๊ณ  .text ๋Š” 0x61f2 ๋ฐ”์ดํŠธ์ธ๋ฐ .data ๊ฐ€ 0x3a9e0 ๋ฐ”์ดํŠธ๋กœ ์ฝ”๋“œ๋ณด๋‹ค ์—ด ๋ฐฐ ๊ฐ€๊นŒ์ด ํฌ๋‹ค
file ๊ณผ readelf -SW ๋กœ ํ™•์ธํ•œ ๊ธฐ๋ณธ ์ •๋ณด. stripped PIE ์ด๊ณ  .text ๋Š” 0x61f2 ๋ฐ”์ดํŠธ์ธ๋ฐ .data ๊ฐ€ 0x3a9e0 ๋ฐ”์ดํŠธ๋กœ ์ฝ”๋“œ๋ณด๋‹ค ์—ด ๋ฐฐ ๊ฐ€๊นŒ์ด ํฌ๋‹ค


๋ฌธ์ œ ๊ฐœ์š”

ํ•ญ๋ชฉ๋‚ด์šฉ
๋ฌธ์ œ๋ช…Function Network
๋‚œ์ด๋„๐Ÿฅ‡ Gold 1
๋ถ„๋ฅ˜reversing
์ œ๊ณต ํŒŒ์ผchal (ELF 64-bit LSB pie, x86-64, dynamically linked, stripped)
์„œ๋ฒ„์—†์Œ โ€” ์˜คํ”„๋ผ์ธ ๋ฌธ์ œ
ํ•ต์‹ฌ ์ทจ์•ฝ์  / ๊ธฐ๋ฒ•๊ฐ€์—ญ ์—ฐ์‚ฐ๋งŒ์œผ๋กœ ์งœ์ธ ๋‚œ๋…ํ™” ํŒŒ์ดํ”„๋ผ์ธ์˜ ์—ญ๋ฐฉํ–ฅ ์‹คํ–‰

ํ’€์ด ํ๋ฆ„์€ ์ด๋ ‡๋‹ค. main ์ด 64๊ธ€์ž ์ž…๋ ฅ์„ ์ž‘์—…๋ฒ„ํผ๋กœ ์˜ฎ๊ธด ๋’ค, .data ์— ์žˆ๋Š” 10000์ค„์งœ๋ฆฌ ํ…Œ์ด๋ธ”์„ ์ˆœ์„œ๋Œ€๋กœ ์ฝ์–ด ๋ฐ”์ดํŠธ ๋‘ ๊ฐœ์”ฉ ์ฃผ๋ฌผ๋Ÿญ๊ฑฐ๋ฆฌ๊ณ , ๋งˆ์ง€๋ง‰์— ๊ณ ์ •๋œ 64๋ฐ”์ดํŠธ์™€ memcmp ํ•œ๋‹ค.

ํ…Œ์ด๋ธ”์˜ ์ฒซ ์นธ์€ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ์ฒ˜๋Ÿผ ์ƒ๊ฒผ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๊ฒฝ๋กœ๊ณ , ๊ทธ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ข…์ ์€ add ยท xor ยท sub ยท swap ๋„ค ๊ฐ€์ง€๋ฟ์ด๋‹ค. ๋„ท ๋‹ค ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋ชฉํ‘œ๊ฐ’์—์„œ ๊ฑฐ๊พธ๋กœ ๊ฐ์œผ๋ฉด ๋๋‚œ๋‹ค.



๐Ÿงฉ ๋ฐฐ๊ฒฝ โ€” "ํ•จ์ˆ˜ ๋„คํŠธ์›Œํฌ"๋ผ๋Š” ๋‚œ๋…ํ™”

์ œ์–ดํ๋ฆ„ ํ‰ํƒ„ํ™”(control-flow flattening)๋ฅผ ์•„๋Š” ์‚ฌ๋žŒ์ด๋ผ๋ฉด ์ต์ˆ™ํ•œ ๊ทธ๋ฆผ์ด๋‹ค. ์›๋ž˜๋Š” ํ•จ์ˆ˜ ํ•˜๋‚˜์— ๋‹ด๊ฒผ์„ ๋กœ์ง์„ ์ž˜๊ฒŒ ์ชผ๊ฐœ์„œ, ์–ด๋А ์กฐ๊ฐ์„ ์‹คํ–‰ํ• ์ง€ ๋Ÿฐํƒ€์ž„ ๊ฐ’์ด ๊ฒฐ์ •ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ์‹์ด๋‹ค. ์ •์  ๋ถ„์„๊ธฐ๊ฐ€ ๊ทธ๋ž˜ํ”„๋ฅผ ๊ทธ๋ ค๋„ "์—ฌ๊ธฐ์„œ ์ €๊ธฐ๋กœ ๊ฐ„๋‹ค"๊ฐ€ ์•ˆ ๋ณด์ธ๋‹ค.

์ด ๋ฌธ์ œ๋Š” ๊ฑฐ๊ธฐ์— ํ•œ ๊ฒน์„ ๋” ์–น์—ˆ๋‹ค. ์กฐ๊ฐ์„ ๊ณ ๋ฅด๋Š” ๊ฐ’์ด ํ•œ ๋ฒˆ์— ์†Œ๋น„๋˜์ง€ ์•Š๋Š”๋‹ค.

64๋น„ํŠธ ๊ฐ’์„ 2๋น„ํŠธ์”ฉ ์ž˜๋ผ 32๋ฒˆ์— ๊ฑธ์ณ ์“ฐ๊ณ , ๋งค ๋‹จ๊ณ„๋งˆ๋‹ค ๋‹ค์Œ์— ๋ฌผ์–ด๋ณผ ํ•จ์ˆ˜๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. ๊ทธ๋ž˜์„œ ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ์—์„œ call ์„ ์•„๋ฌด๋ฆฌ ๋”ฐ๋ผ๊ฐ€๋„ "์ด key ๊ฐ€ ๊ฒฐ๊ตญ ๋ฌด์Šจ ์—ฐ์‚ฐ์ด๋ƒ"๋Š” ๋‹ต์ด ์•ˆ ๋‚˜์˜จ๋‹ค. ๊ทธ๋ž˜ํ”„๋ฅผ ํ†ต์งธ๋กœ ๋ณต์›ํ•ด์„œ ์ง์ ‘ ๊ฑธ์–ด ๋ด์•ผ ๋‚˜์˜จ๋‹ค.

๋ฐ”๊ฟ” ๋งํ•˜๋ฉด ์ด ๋ฌธ์ œ์˜ ๋‚œ์ด๋„๋Š” ์•”ํ˜ธ ํ•ด๋…์ด ์•„๋‹ˆ๋ผ ์ž๋ฃŒ๊ตฌ์กฐ ๋ณต์›์— ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณต์›๋งŒ ๋๋‚˜๋ฉด ๋‚˜๋จธ์ง€๋Š” ํ—ˆ๋ฌดํ•  ๋งŒํผ ์‰ฝ๋‹ค.


๐Ÿ”ฌ ์ •์ฐฐ โ€” ๋ฌธ์ž์—ด๋ถ€ํ„ฐ

๋จผ์ € ํ”„๋กœ๊ทธ๋žจ์ด ๋ฌด์Šจ ๋ง์„ ํ•˜๋Š”์ง€ ๋ณธ๋‹ค.

strings -n 5 extracted/chal | grep -nE "Give me|Wrong|flag is|memcmp|strlen"

strings ์™€ grep ์œผ๋กœ ๋ฝ‘์€ ๋ฌธ์ž์—ด. ์„ฑ๊ณต ๋ฉ”์‹œ์ง€๊ฐ€ Yes, the flag is DH ํ˜•์‹์ด๊ณ  ์ž„ํฌํŠธ์— memcmp ์™€ strlen ๋งŒ ๋ณด์ธ๋‹ค
strings ์™€ grep ์œผ๋กœ ๋ฝ‘์€ ๋ฌธ์ž์—ด. ์„ฑ๊ณต ๋ฉ”์‹œ์ง€๊ฐ€ Yes, the flag is DH ํ˜•์‹์ด๊ณ  ์ž„ํฌํŠธ์— memcmp ์™€ strlen ๋งŒ ๋ณด์ธ๋‹ค

Yes, the flag is ๋’ค์— %s ๊ฐ€ ๋ถ™๋Š”๋‹ค. ํ”Œ๋ž˜๊ทธ๋ฅผ ํ”„๋กœ๊ทธ๋žจ์ด ๋งŒ๋“ค์–ด ๋‚ด๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ ๋„ฃ์€ ์ž…๋ ฅ์„ ๊ทธ๋Œ€๋กœ ๋˜๋Œ๋ ค ์ค€๋‹ค. ์ฆ‰ ์ •๋‹ต ์ž…๋ ฅ ์ž์ฒด๊ฐ€ ํ”Œ๋ž˜๊ทธ ๋ณธ๋ฌธ์ด๋‹ค. ์ด๋Ÿฐ ํ˜•ํƒœ๋Š” "๋งž๋Š” ์ž…๋ ฅ์„ ์ฐพ์•„๋ผ" ๋ฌธ์ œ๋ผ๋Š” ๋œป์ด๊ณ , ๋น„๊ต ๋กœ์ง์ด ์–ด๋”˜๊ฐ€์— ํ†ต์งธ๋กœ ๋“ค์–ด ์žˆ๋‹ค๋Š” ๋œป์ด๊ธฐ๋„ ํ•˜๋‹ค.

์ž„ํฌํŠธ๋„ ๋”ฑ ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ์žˆ๋‹ค. strlen, memcmp, read, printf, puts. ์•”ํ˜ธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์—†๋‹ค. ๊ทธ๋Ÿผ ๋ณ€ํ˜•์€ ์ „๋ถ€ ์ž์ฒด ์ฝ”๋“œ๋กœ ํ•˜๊ณ  ์žˆ๋‹ค.

main ์ฐพ๊ธฐ

stripped ๋ผ ์‹ฌ๋ณผ์ด ์—†์ง€๋งŒ _start ๊ฐ€ main ์„ ๋„˜๊ฒจ ์ฃผ๋Š” ์ž๋ฆฌ๋Š” ํ•ญ์ƒ ๊ฐ™๋‹ค. ์—”ํŠธ๋ฆฌ์—์„œ lea rdi,[rip+0x5f10] ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” 0x704f ๊ฐ€ main ์ด๋‹ค. ๊ฑฐ๊ธฐ์„œ๋ถ€ํ„ฐ ์ฝ์œผ๋ฉด ์ž…๋ ฅ ์ฒ˜๋ฆฌ๊ฐ€ ๋‚˜์˜จ๋‹ค.

objdump -d -M intel --start-address=0x7133 --stop-address=0x71b2 extracted/chal | sed -f annot_main.sed

objdump ๋กœ ๋œฌ main ์˜ ์ž…๋ ฅ ์ฒ˜๋ฆฌ ๊ตฌ๊ฐ„. read ๋กœ 0x41๋ฐ”์ดํŠธ๋ฅผ ๋ฐ›๊ณ  ๊ฐœํ–‰์„ ์ž˜๋ผ๋‚ธ ๋’ค strlen ๊ฒฐ๊ณผ๊ฐ€ 0x40 ์ธ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค
objdump ๋กœ ๋œฌ main ์˜ ์ž…๋ ฅ ์ฒ˜๋ฆฌ ๊ตฌ๊ฐ„. read ๋กœ 0x41๋ฐ”์ดํŠธ๋ฅผ ๋ฐ›๊ณ  ๊ฐœํ–‰์„ ์ž˜๋ผ๋‚ธ ๋’ค strlen ๊ฒฐ๊ณผ๊ฐ€ 0x40 ์ธ์ง€ ๊ฒ€์‚ฌํ•œ๋‹ค

์ฝ์–ด ๋ณด๋ฉด ์กฐ๊ฑด์ด ํ•˜๋‚˜๋ฟ์ด๋‹ค.

  • read(0, rbp-0x50, 0x41) โ€” ์ตœ๋Œ€ 65๋ฐ”์ดํŠธ
  • ๋งˆ์ง€๋ง‰ ๊ธ€์ž๊ฐ€ 0x0a ๋ฉด 0x00 ์œผ๋กœ ๋ฎ๋Š”๋‹ค (๊ฐœํ–‰ ์ œ๊ฑฐ)
  • strlen ๊ฒฐ๊ณผ๊ฐ€ ์ •ํ™•ํžˆ 0x40 (64) ์ด์–ด์•ผ ํ†ต๊ณผ

๊ธธ์ด 64๊ฐ€ ์•„๋‹ˆ๋ฉด Wrong! ์„ ์ฐ๊ณ  ๋๋‚œ๋‹ค. NUL ๋ฐ”์ดํŠธ๊ฐ€ ์„ž์ด๋ฉด strlen ์ด ์งง์•„์ง€๋‹ˆ๊นŒ ์ž…๋ ฅ 64๋ฐ”์ดํŠธ์—๋Š” 0x00 ์ด ์—†์–ด์•ผ ํ•œ๋‹ค๋Š” ์ œ์•ฝ๋„ ์—ฌ๊ธฐ์„œ ๋”ฐ๋ผ ๋‚˜์˜จ๋‹ค.

objdump ์ถœ๋ ฅ์˜ <-- ์ฃผ์„์€ ๋‚ด๊ฐ€ annot_main.sed ๋กœ ์–น์€ ๊ฒƒ์ด๋‹ค. ๋„๊ตฌ๊ฐ€ ์›๋ž˜ ๊ทธ๋ ‡๊ฒŒ ์ฐ์–ด ์ฃผ๋Š” ๊ฒŒ ์•„๋‹ˆ๋‹ค. ์น˜ํ™˜ ๊ทœ์น™ ํŒŒ์ผ์€ ์•„๋ž˜ ํ† ๊ธ€์— ๊ทธ๋Œ€๋กœ ์‹ค์–ด ๋’€๋‹ค.



๐Ÿงฑ main ์ด ํ•˜๋Š” ์ผ โ€” 10000๋ฒˆ์˜ ๋ณ€ํ˜•

๊ธธ์ด ๊ฒ€์‚ฌ๋ฅผ ํ†ต๊ณผํ•˜๋ฉด ์ž…๋ ฅ 64๋ฐ”์ดํŠธ๋ฅผ rbp-0x50 ์—์„œ rbp-0x90 ์œผ๋กœ ๋ณต์‚ฌํ•œ๋‹ค. ์ดํ›„ ๋ชจ๋“  ๋ณ€ํ˜•์€ ๋ณต์‚ฌ๋ณธ์—์„œ ์ผ์–ด๋‚˜๊ณ , ์›๋ณธ์€ ๋งˆ์ง€๋ง‰์— ํ”Œ๋ž˜๊ทธ๋ฅผ ์ถœ๋ ฅํ•  ๋•Œ ๋‹ค์‹œ ์“ฐ์ธ๋‹ค. ๊ทธ๋ž˜์„œ ์„ฑ๊ณต ๋ฉ”์‹œ์ง€์— ์ฐํžˆ๋Š” ๊ฑด ๋ณ€ํ˜•๋œ ๊ฐ’์ด ์•„๋‹ˆ๋ผ ๋‚ด๊ฐ€ ์นœ ์ž…๋ ฅ ๊ทธ๋Œ€๋กœ๋‹ค.

ํ•ต์‹ฌ์€ ๊ทธ๋‹ค์Œ ๋ฃจํ”„๋‹ค.

objdump -d -M intel --start-address=0x7207 --stop-address=0x72cb extracted/chal | sed -f annot_loop.sed

objdump ๋กœ ๋ณธ main ์˜ ํ•ต์‹ฌ ๋ฃจํ”„. ์ธ๋ฑ์Šค์— 24๋ฅผ ๊ณฑํ•ด 0xb020 0xb028 0xb030 ์„ธ ํ…Œ์ด๋ธ”์„ ์ฝ๊ณ  ๊ณ ์ • ์ฃผ์†Œ 0x1209 ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ 0x270f ๊นŒ์ง€ ๋ฐ˜๋ณตํ•œ๋‹ค
objdump ๋กœ ๋ณธ main ์˜ ํ•ต์‹ฌ ๋ฃจํ”„. ์ธ๋ฑ์Šค์— 24๋ฅผ ๊ณฑํ•ด 0xb020 0xb028 0xb030 ์„ธ ํ…Œ์ด๋ธ”์„ ์ฝ๊ณ  ๊ณ ์ • ์ฃผ์†Œ 0x1209 ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ 0x270f ๊นŒ์ง€ ๋ฐ˜๋ณตํ•œ๋‹ค

๋ฃจํ”„๋ฅผ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธฐ๋ฉด ์ด๋ ‡๋‹ค.

char *buf = rbp - 0x90;              // ์ž…๋ ฅ 64๋ฐ”์ดํŠธ ์‚ฌ๋ณธ
 
for (int i = 0; i <= 0x270f; i++) {  // 10000ํšŒ
    long key   = *(long *)(0xb020 + i * 24);
    long off_a = *(long *)(0xb028 + i * 24);
    long off_b = *(long *)(0xb030 + i * 24);
    dispatch(key, buf + off_a, buf + off_b);   // 0x1209 โ€” ๊ณ ์ • ์ฃผ์†Œ
}
 
if (memcmp((void *)0x459a0, buf, 0x40) == 0)
    printf("Yes, the flag is DH{%s}\n", rbp - 0x50);

์ธ๋ฑ์Šค ๊ณ„์‚ฐ์ด i + i + i ๋’ค์— shl rax,0x3 ์ด๋ผ ์ŠคํŠธ๋ผ์ด๋“œ๊ฐ€ 24๋ฐ”์ดํŠธ๋‹ค. ์ฆ‰ ํ…Œ์ด๋ธ” ํ•œ ์ค„์ด qword ์„ธ ๊ฐœ๊ณ , ์„ธ ํ…Œ์ด๋ธ” ์ฃผ์†Œ 0xb020 / 0xb028 / 0xb030 ์€ ๊ฐ™์€ ๋ฐฐ์—ด์˜ 0๋ฒˆยท1๋ฒˆยท2๋ฒˆ ์นธ์ด๋‹ค.

์ฃผ์†Œ๋ฅผ ๋งž์ถฐ ๋ณด๋ฉด ๋”ฑ ๋–จ์–ด์ง„๋‹ค. 0xb020 + 10000 ร— 24 = 0x459a0 ์ด๊ณ , ๊ทธ๊ฒŒ ๋ฐ”๋กœ memcmp ์˜ ๋ชฉํ‘œ ๋ฒ„ํผ ์ฃผ์†Œ๋‹ค. ๋ชฉํ‘œ 64๋ฐ”์ดํŠธ๊นŒ์ง€ ํ•ฉ์น˜๋ฉด 0x459e0 โ€” .data ์„น์…˜์˜ ์ •ํ™•ํ•œ ๋์ด๋‹ค. ํ…Œ์ด๋ธ”๊ณผ ๋ชฉํ‘œ๊ฐ’์ด ๋นˆํ‹ˆ์—†์ด ์ด์–ด์ ธ ์žˆ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 1 โ€” ํ…Œ์ด๋ธ” ์ฒซ ์นธ์„ ํ•จ์ˆ˜ ํฌ์ธํ„ฐ๋กœ ์ฝ์œผ๋ฉด ์–ด๋””์„œ ๋ง‰ํžˆ๋‚˜

0xb020 ์—์„œ qword ๋ฅผ ์ฝ์–ด mov rdi,rax ๋กœ ๋„˜๊ธฐ๋Š” ๊ฑธ ๋ณด๋ฉด ๋ฐ˜์‚ฌ์ ์œผ๋กœ "ํ•จ์ˆ˜ ํฌ์ธํ„ฐ ๋ฐฐ์—ด"์ด๋ผ๊ณ  ์ฝ๊ฒŒ ๋œ๋‹ค. ์‹ค์ œ๋กœ ๊ทธ๋Ÿฐ ๋ฌธ์ œ๊ฐ€ ํ”ํ•˜๊ณ , ๊ทธ๋ ‡๊ฒŒ ์ฝ์œผ๋ฉด ๋‹ค์Œ ์ˆ˜์ˆœ์€ call rdi ๋ฅผ ์ฐพ๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ด ๋ฃจํ”„์—๋Š” ๊ฐ„์ ‘ ํ˜ธ์ถœ์ด ์—†๋‹ค. call 1209 ๋Š” ๊ณ ์ • ์ฃผ์†Œ๊ณ , ๋ฐฉ๊ธˆ ์ฝ์€ ๊ฐ’์€ ๊ทธ ํ•จ์ˆ˜์˜ ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋“ค์–ด๊ฐ„๋‹ค. ๊ฐ’ ์ž์ฒด๋ฅผ ๋ด๋„ ํฌ์ธํ„ฐ์ผ ์ˆ˜๊ฐ€ ์—†๋‹ค.

key ์ตœ์†Œ 0x0000101de1bdc038   ์ตœ๋Œ€ 0xfffa892705a02627

์ด ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” PIE ๋ผ ๋กœ๋“œ ์ฃผ์†Œ๊ฐ€ 0x55... ๋Œ€์—ญ์ด๊ณ , .text ๋Š” ์˜คํ”„์…‹ 0x1120~0x7314 ๋ฟ์ด๋‹ค. 0xfffa8927... ๊ฐ™์€ ๊ฐ’์ด ์ฝ”๋“œ ์ฃผ์†Œ์ผ ๋ฆฌ ์—†๊ณ , ์ •๋ ฌ์กฐ์ฐจ ์•ˆ ๋งž๋Š”๋‹ค(๋์ž๋ฆฌ๊ฐ€ ํ™€์ˆ˜์ธ key ๊ฐ€ ๋„๋ ค ์žˆ๋‹ค).

ํฌ์ธํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ์ธ๋ฑ์Šค์ด๊ฑฐ๋‚˜ ๊ฒฝ๋กœ๋‹ค. ๊ทธ๋ฆฌ๊ณ  dispatch ์•ˆ์—์„œ and eax,0x3 ์ด ๋‚˜์˜ค๋Š” ์ˆœ๊ฐ„ ๋‹ต์ด ์ •ํ•ด์ง„๋‹ค โ€” 2๋น„ํŠธ์”ฉ ์ชผ๊ฐœ ์“ฐ๋Š” ๊ฒฝ๋กœ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 2 โ€” off_a ์™€ off_b ๊ฐ€ ๊ฐ™์œผ๋ฉด ์—ญ์‚ฐ์ด ๊นจ์ง„๋‹ค

๋’ค์—์„œ ๋ณด๊ฒ ์ง€๋งŒ ๋ฆฌํ”„๋Š” ๋ชฉ์ ์ง€์— ๋‘ ๋ฒˆ ์“ด๋‹ค. buf[off_b] = a ๋ฅผ ๋จผ์ €, buf[off_a] = t ๋ฅผ ๋‚˜์ค‘์—. ๋งŒ์•ฝ off_a == off_b ์ธ ์ค„์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ๋‘ ๋ฒˆ์งธ ์“ฐ๊ธฐ๊ฐ€ ์ฒซ ๋ฒˆ์งธ๋ฅผ ๋ฎ์–ด์„œ ์ •๋ณด๊ฐ€ ์‚ฌ๋ผ์ง€๊ณ , ๊ทธ ์ง€์ ๋ถ€ํ„ฐ๋Š” ์—ญ์‚ฐ์ด ์œ ์ผํ•˜์ง€ ์•Š๊ฒŒ ๋œ๋‹ค. add ๋ผ๋ฉด t = a + a = 2a ๋ผ ํ™€์ง ์ •๋ณด๊ฐ€ ํ†ต์งธ๋กœ ๋‚ ์•„๊ฐ„๋‹ค.

๊ทธ๋ž˜์„œ ์—ญ์‚ฐ ์ฝ”๋“œ๋ฅผ ์งœ๊ธฐ ์ „์— ๋จผ์ € ์„ธ์–ด ๋ดค๋‹ค.

same = [(i, o) for i, o in enumerate(ops) if o[1] == o[2]]
print('oa==ob ์ธ ํ•ญ๋ชฉ:', len(same), same[:5])
oa==ob ์ธ ํ•ญ๋ชฉ: 0 []

0๊ฑด์ด๋‹ค. 10000์ค„ ์ „๋ถ€ off_a != off_b ๋ผ ๋‘ ์“ฐ๊ธฐ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์นธ์œผ๋กœ ๊ฐ€๊ณ , ๊ฐ ์Šคํ…์ด 64๋ฐ”์ดํŠธ ๊ณต๊ฐ„์˜ ์ „๋‹จ์‚ฌ๊ฐ€ ๋œ๋‹ค. ์ด๊ฑธ ํ™•์ธํ•˜์ง€ ์•Š๊ณ  ๋„˜์–ด๊ฐ”์œผ๋ฉด "์™œ ์–ด๋–ค ์ž…๋ ฅ์—์„  ์•ˆ ๋งž์ง€" ํ•˜๊ณ  ํ•œ์ฐธ ํ—ค๋งธ์„ ๊ฒƒ์ด๋‹ค.



๐Ÿ’ฃ ํ•ต์‹ฌ โ€” ๋””์ŠคํŒจ์ฒ˜๋Š” 4์ง„ ํŠธ๋ผ์ด๋‹ค

0x1209 ๋ฅผ ์—ด์–ด ๋ณด๋ฉด ๊ตฌ์กฐ๊ฐ€ ๋‹จ์ˆœํ•˜๋‹ค.

objdump -d -M intel --start-address=0x1209 --stop-address=0x127a extracted/chal | sed -f annot_disp.sed

objdump ๋กœ ๋ณธ ๋””์ŠคํŒจ์ฒ˜ 0x1209 ์˜ ์•ž๋ถ€๋ถ„. key ํ•˜์œ„ 2๋น„ํŠธ๋ฅผ and eax 0x3 ์œผ๋กœ ๋–ผ์–ด ๋ถ„๊ธฐํ•˜๊ณ  shr rax 0x2 ๋กœ 2๋น„ํŠธ๋ฅผ ์†Œ๋น„ํ•œ ๊ฐ’์„ ๋‹ค์Œ ํ•จ์ˆ˜์— ๋„˜๊ธด๋‹ค
objdump ๋กœ ๋ณธ ๋””์ŠคํŒจ์ฒ˜ 0x1209 ์˜ ์•ž๋ถ€๋ถ„. key ํ•˜์œ„ 2๋น„ํŠธ๋ฅผ and eax 0x3 ์œผ๋กœ ๋–ผ์–ด ๋ถ„๊ธฐํ•˜๊ณ  shr rax 0x2 ๋กœ 2๋น„ํŠธ๋ฅผ ์†Œ๋น„ํ•œ ๊ฐ’์„ ๋‹ค์Œ ํ•จ์ˆ˜์— ๋„˜๊ธด๋‹ค

์˜์‚ฌ์ฝ”๋“œ๋กœ๋Š” ์ด๋ ‡๋‹ค.

void dispatch(long key, char *p, char *q) {
    switch (key & 3) {
        case 0: f_a(key >> 2, p, q); break;
        case 1: f_b(key >> 2, p, q); break;
        case 2: f_c(key >> 2, p, q); break;
        default: f_d(key >> 2, p, q); break;
    }
}

์ด๋Ÿฐ ํ•จ์ˆ˜๊ฐ€ 125๊ฐœ ์žˆ๋‹ค. ์ „๋ถ€ ํฌ๊ธฐ๊ฐ€ ์ •ํ™•ํžˆ 0xbf(191)๋ฐ”์ดํŠธ๋กœ ๊ฐ™๊ณ , ๋‹ค๋ฅธ ๊ฑด ๋„ค ๊ฐœ์˜ call ๋Œ€์ƒ๋ฟ์ด๋‹ค. 0x1209 ๋ถ€ํ„ฐ 0x6e8d ๊นŒ์ง€ 191๋ฐ”์ดํŠธ ๊ฐ„๊ฒฉ์œผ๋กœ ์ด˜์ด˜ํžˆ ๋ถ™์–ด ์žˆ๋‹ค.

์ฆ‰ ์ „์ฒด๊ฐ€ ํ•˜๋‚˜์˜ 4์ง„ ํŠธ๋ผ์ด๋‹ค. key ์˜ ํ•˜์œ„ 2๋น„ํŠธ๊ฐ€ ์ฒซ ๊ฐˆ๋ฆผ๊ธธ, ๊ทธ๋‹ค์Œ 2๋น„ํŠธ๊ฐ€ ๋‘ ๋ฒˆ์งธ ๊ฐˆ๋ฆผ๊ธธ, ์ด๋Ÿฐ ์‹์œผ๋กœ ๋‚ด๋ ค๊ฐ„๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋…ธ๋“œ๊ฐ€ 125๊ฐœ๋ฟ์ด๋ฉด ํŠธ๋ฆฌ๋กœ๋Š” ๋ง์ด ์•ˆ ๋œ๋‹ค โ€” ๊นŠ์ด 32์งœ๋ฆฌ 4์ง„ ํŠธ๋ฆฌ๋Š” ๋…ธ๋“œ๊ฐ€ ์ฒœ๋ฌธํ•™์ ์œผ๋กœ ๋งŽ์•„์•ผ ํ•œ๋‹ค. ๋‹ต์€ ์ธต๋งˆ๋‹ค ๋…ธ๋“œ๋ฅผ ๊ณต์œ ํ•˜๋Š” DAG ๋‹ค.

์ง์ ‘ ๊ทธ๋ฆฐ ๊ตฌ์กฐ๋„. 64๋น„ํŠธ key ๋ฅผ 2๋น„ํŠธ์”ฉ 32๋ฒˆ ์†Œ๋น„ํ•˜๋ฉฐ ๋ ˆ๋ฒจ๋งˆ๋‹ค ๋…ธ๋“œ 4๊ฐœ ์ค‘ ํ•˜๋‚˜๋กœ ์ด๋™ํ•˜๊ณ  ๋งˆ์ง€๋ง‰ ๋ ˆ๋ฒจ์—์„œ add xor sub swap ๋ฆฌํ”„์— ๋„๋‹ฌํ•œ๋‹ค
์ง์ ‘ ๊ทธ๋ฆฐ ๊ตฌ์กฐ๋„. 64๋น„ํŠธ key ๋ฅผ 2๋น„ํŠธ์”ฉ 32๋ฒˆ ์†Œ๋น„ํ•˜๋ฉฐ ๋ ˆ๋ฒจ๋งˆ๋‹ค ๋…ธ๋“œ 4๊ฐœ ์ค‘ ํ•˜๋‚˜๋กœ ์ด๋™ํ•˜๊ณ  ๋งˆ์ง€๋ง‰ ๋ ˆ๋ฒจ์—์„œ add xor sub swap ๋ฆฌํ”„์— ๋„๋‹ฌํ•œ๋‹ค

์ด๊ฑธ ๋ˆˆ๋Œ€์ค‘์ด ์•„๋‹ˆ๋ผ ์‹ค์ œ๋กœ ์„ธ์–ด์„œ ํ™•์ธํ–ˆ๋‹ค.

# verify_graph.py ๋ฐœ์ทŒ โ€” ๊ณ„์ธต ๊ตฌ์กฐ ํ™•์ธ
level, frontier, seen = {0x1209: 0}, [0x1209], {0x1209}
while frontier:
    nxt = []
    for u in frontier:
        for v in children.get(u, []):
            if v not in seen:
                seen.add(v); level[v] = level[u] + 1; nxt.append(v)
            assert level[v] == level[u] + 1, (hex(u), hex(v))
    frontier = nxt

assert ๊ฐ€ ํ•ต์‹ฌ์ด๋‹ค. ์–ด๋–ค ๊ฐ„์„ ์ด๋“  ๋ ˆ๋ฒจ์„ ์ •ํ™•ํžˆ 1๋งŒ ์˜ฌ๋ฆฐ๋‹ค๋ฉด ์ด ๊ทธ๋ž˜ํ”„๋Š” ์ธต์œผ๋กœ ๊ฐˆ๋ผ์ ธ ์žˆ๊ณ , ๊ฐ™์€ ๋…ธ๋“œ์— ์„œ๋กœ ๋‹ค๋ฅธ ๊นŠ์ด๋กœ ๋„๋‹ฌํ•˜๋Š” ๊ฒฝ๋กœ๊ฐ€ ์—†๋‹ค๋Š” ๋œป์ด๋‹ค.

python3 verify_graph.py

verify_graph.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ์˜ ํŒŒ์‹ฑ ๋ถˆ์ผ์น˜๊ฐ€ 0๊ฑด์ด๊ณ  ๋ ˆ๋ฒจ 0์— ๋…ธ๋“œ 1๊ฐœ ๋ ˆ๋ฒจ 1๋ถ€ํ„ฐ 32๊นŒ์ง€ ๊ฐ๊ฐ 4๊ฐœ์”ฉ์ด๋ฉฐ ๋ฆฌํ”„๋Š” ์ „๋ถ€ ๋ ˆ๋ฒจ 32์— ์žˆ๋‹ค
verify_graph.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ์˜ ํŒŒ์‹ฑ ๋ถˆ์ผ์น˜๊ฐ€ 0๊ฑด์ด๊ณ  ๋ ˆ๋ฒจ 0์— ๋…ธ๋“œ 1๊ฐœ ๋ ˆ๋ฒจ 1๋ถ€ํ„ฐ 32๊นŒ์ง€ ๊ฐ๊ฐ 4๊ฐœ์”ฉ์ด๋ฉฐ ๋ฆฌํ”„๋Š” ์ „๋ถ€ ๋ ˆ๋ฒจ 32์— ์žˆ๋‹ค

๋ ˆ๋ฒจ 0์— root ํ•˜๋‚˜, ๋ ˆ๋ฒจ 1๋ถ€ํ„ฐ 31๊นŒ์ง€ ๊ฐ 4๊ฐœ(์ด 124๊ฐœ), ๊ทธ๋ž˜์„œ ๋””์ŠคํŒจ์ฒ˜๊ฐ€ ์ •ํ™•ํžˆ 125๊ฐœ. ๋ฆฌํ”„ 4๊ฐœ๋Š” ์ „๋ถ€ ๋ ˆ๋ฒจ 32์— ์žˆ๋‹ค. ์ž์‹ ์Šฌ๋กฏ์€ 500๊ฐœ(root 4 + 31์ธต ร— 16)์ธ๋ฐ ์„œ๋กœ ๋‹ค๋ฅธ ์ž์‹์€ 128๊ฐœ๋ฟ โ€” ๊ณต์œ ๊ฐ€ ์‹ฌํ•˜๋‹ค๋Š” ๊ฒŒ ์ˆซ์ž๋กœ ๋‚˜์˜จ๋‹ค.

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๊ฒฐ๋ก ์ด ํ•˜๋‚˜ ๋‚˜์˜จ๋‹ค. key 10000๊ฐœ๊ฐ€ ์ „๋ถ€ ์„œ๋กœ ๋‹ค๋ฅธ๋ฐ, ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ์ข…์ ์€ ๋„ท๋ฟ์ด๋‹ค. 64๋น„ํŠธ๋ฅผ ๋‹ค ์“ฐ๋Š” ํ™”๋ คํ•œ ๊ฒฝ๋กœ๊ฐ€ ๊ฒฐ๊ตญ 2๋น„ํŠธ์งœ๋ฆฌ ์ •๋ณด๋กœ ์ˆ˜๋ ดํ•œ๋‹ค. ๋‚œ๋…ํ™”์˜ ์ „๋ถ€๊ฐ€ ์—ฌ๊ธฐ์— ์žˆ๋‹ค.

โ–ถ๐Ÿ› ์‚ฝ์งˆ 3 โ€” ์–ด์…ˆ ๋“ฑ์žฅ ์ˆœ์„œ๋ฅผ case ์ˆœ์„œ๋กœ ๋ฏฟ์–ด๋„ ๋˜๋‚˜

๋””์ŠคํŒจ์ฒ˜์—์„œ ์ž์‹ ๋„ท์„ ๋ฝ‘์„ ๋•Œ ์ฒ˜์Œ์—” ๊ทธ๋ƒฅ call ์ด ๋‚˜์˜ค๋Š” ์ˆœ์„œ๋Œ€๋กœ case 0/1/2/3 ์— ๋ฐฐ๋‹นํ–ˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ๋ณดํ†ต ๊ทธ๋ ‡๊ฒŒ ๋ฑ‰๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ด๊ฒŒ ํ‹€๋ฆฌ๋ฉด ์—ฐ์‚ฐ ์ข…๋ฅ˜๊ฐ€ ํ†ต์งธ๋กœ ๋’ค๋ฐ”๋€ ์ฑ„ ์กฐ์šฉํžˆ ์ง„ํ–‰๋œ๋‹ค. add ์ž๋ฆฌ์— xor ์ด ๋“ค์–ด๊ฐ€๋„ ์ฝ”๋“œ๋Š” ๋ฉ€์ฉกํžˆ ๋Œ๊ณ , ์—ญ์‚ฐ ๊ฒฐ๊ณผ๋งŒ ์“ฐ๋ ˆ๊ธฐ๊ฐ€ ๋œ๋‹ค. ์–ด๋””๊ฐ€ ํ‹€๋ ธ๋Š”์ง€ ์ถ”์ ํ•  ๋‹จ์„œ๋„ ์—†๋‹ค.

๊ทธ๋ž˜์„œ ์ˆœ์„œ ๊ฐ€์ •์„ ์•ˆ ์“ฐ๋Š” ๋‘ ๋ฒˆ์งธ ํŒŒ์„œ๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด ๋Œ€์กฐํ–ˆ๋‹ค. test rax,rax ๋Š” case 0, cmp rax,0x1 ์€ case 1, cmp rax,0x2 ๋Š” case 2, ๋น„๊ต ์—†์ด ๋‚˜์˜ค๋Š” ๋งˆ์ง€๋ง‰ call ์ด default.

explicit, pending = {}, None
for l in body:
    if re.search(r"test\s+rax,rax", l):
        pending = 0
    elif (m := re.search(r"cmp\s+rax,0x([0-9a-f]+)", l)):
        pending = int(m.group(1), 16)
    elif (m := re.search(r"call\s+([0-9a-f]+)", l)):
        explicit[3 if pending is None else pending] = int(m.group(1), 16)
        pending = None

125๊ฐœ ์ „๋ถ€ ๋ถˆ์ผ์น˜ 0๊ฑด์ด์—ˆ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ˆœ์„œ ๊ฐ€์ •์€ ๋งž์•˜์ง€๋งŒ, ๋งž์•˜๋‹ค๋Š” ๊ฑธ ํ™•์ธํ•œ ๊ฒƒ๊ณผ ๋งž๊ฒ ๊ฑฐ๋‹ˆ ๋„˜์–ด๊ฐ„ ๊ฒƒ์€ ๋‹ค๋ฅด๋‹ค. ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ๊ฐ€์ •์€ ํ‹€๋ ธ์„ ๋•Œ ์ฆ์ƒ์ด ์•ˆ ๋‚˜์˜ค๋Š” ์ชฝ์ด๋ผ ๋” ๋ฌด์„ญ๋‹ค.



๐Ÿ”‘ ๋ฆฌํ”„ 4๊ฐœ โ€” ๋„ท ๋‹ค ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค

ํŠธ๋ผ์ด์˜ ์ข…์ ์€ 191๋ฐ”์ดํŠธ ๊ทœ์น™์—์„œ ๋ฒ—์–ด๋‚œ ์ž‘์€ ํ•จ์ˆ˜ ๋„ค ๊ฐœ๋‹ค. 0x6f4c, 0x6f8f, 0x6fd0, 0x7017.

objdump -d -M intel --start-address=0x6f4c --stop-address=0x6fd0 extracted/chal | grep -vE "endbr64|push   rbp|mov    rbp,rsp|QWORD PTR \[rbp-0x(18|20|28)\],r|pop    rbp|nop|file format|Disassembly" | sed -f annot_leaf.sed

objdump ์™€ grep ์œผ๋กœ ํ”„๋กค๋กœ๊ทธ๋ฅผ ๊ฑท์–ด๋‚ธ ๋ฆฌํ”„ ๋‘ ๊ฐœ. 0x6f4c ๋Š” add eax edx ๋กœ ๋”ํ•˜๊ณ  0x6f8f ๋Š” xor eax edx ๋กœ ๋ฐฐํƒ€์  ๋…ผ๋ฆฌํ•ฉ์„ ์ทจํ•œ ๋’ค ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋‘ ๋ฒˆ ์“ด๋‹ค
objdump ์™€ grep ์œผ๋กœ ํ”„๋กค๋กœ๊ทธ๋ฅผ ๊ฑท์–ด๋‚ธ ๋ฆฌํ”„ ๋‘ ๊ฐœ. 0x6f4c ๋Š” add eax edx ๋กœ ๋”ํ•˜๊ณ  0x6f8f ๋Š” xor eax edx ๋กœ ๋ฐฐํƒ€์  ๋…ผ๋ฆฌํ•ฉ์„ ์ทจํ•œ ๋’ค ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋‘ ๋ฒˆ ์“ด๋‹ค

objdump -d -M intel --start-address=0x6fd0 --stop-address=0x704f extracted/chal | grep -vE "endbr64|push   rbp|mov    rbp,rsp|QWORD PTR \[rbp-0x(18|20|28)\],r|pop    rbp|nop|file format|Disassembly" | sed -f annot_leaf.sed

objdump ๋กœ ๋ณธ ๋‚˜๋จธ์ง€ ๋ฆฌํ”„ ๋‘ ๊ฐœ. 0x6fd0 ์€ sub eax ecx ๋กœ ๋นผ๊ณ  0x7017 ์€ ์‚ฐ์ˆ  ์—†์ด ๋‘ ๋ฒˆ์งธ ๋ฐ”์ดํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒจ ๋‘ ์นธ์„ ๋งž๋ฐ”๊พผ๋‹ค
objdump ๋กœ ๋ณธ ๋‚˜๋จธ์ง€ ๋ฆฌํ”„ ๋‘ ๊ฐœ. 0x6fd0 ์€ sub eax ecx ๋กœ ๋นผ๊ณ  0x7017 ์€ ์‚ฐ์ˆ  ์—†์ด ๋‘ ๋ฒˆ์งธ ๋ฐ”์ดํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒจ ๋‘ ์นธ์„ ๋งž๋ฐ”๊พผ๋‹ค

๋„ค ํ•จ์ˆ˜๊ฐ€ ํ•˜๋Š” ์ผ์€ ์™„์ „ํžˆ ๊ฐ™์€ ๊ณจ๊ฒฉ์ด๋‹ค. a = *p, b = *q ๋ฅผ ์ฝ๊ณ , ์ž„์‹œ๊ฐ’ t ๋ฅผ ๋งŒ๋“  ๋‹ค์Œ *q = a ๋ฅผ ๋จผ์ €, *p = t ๋ฅผ ๋‚˜์ค‘์— ์“ด๋‹ค. ๋‹ค๋ฅธ ๊ฑด t ๋ฅผ ๋งŒ๋“œ๋Š” ํ•œ ์ค„๋ฟ์ด๋‹ค.

์ฃผ์†Œ์—ฐ์‚ฐt๊ฐœ์ˆ˜
0x6f4cadd(a + b) & 0xff2470
0x6f8fxora ^ b2512
0x6fd0sub(a - b) & 0xff2468
0x7017swapb2550

์—ฌ๊ธฐ๊ฐ€ ๋ฌธ์ œ์˜ ์ „๋ถ€๋‹ค. ์ƒˆ *q ๊ฐ’์ด ์˜› *p ๊ฐ’์ด๋ผ, ๊ฒฐ๊ณผ๋งŒ ๋ณด๊ณ ๋„ ์ž…๋ ฅ ํ•˜๋‚˜๋ฅผ ๊ณต์งœ๋กœ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋‚˜๋จธ์ง€ ํ•˜๋‚˜๋Š” ์—ฐ์‚ฐ์„ ๋’ค์ง‘์œผ๋ฉด ๋‚˜์˜จ๋‹ค.

์ง์ ‘ ๊ทธ๋ฆฐ ์—ญ์‚ฐ ๋„์‹. ์ˆœ๋ฐฉํ–ฅ์€ t๋ฅผ ๊ณ„์‚ฐํ•ด ๋‘ ์นธ์— ๋‚˜๋ˆ  ์“ฐ๊ณ  ์—ญ๋ฐฉํ–ฅ์€ ์ƒˆ b๊ฐ’์ด ๊ณง ์˜› a๊ฐ’์ด๋ผ๋Š” ์‚ฌ์‹ค์—์„œ ๋‚˜๋จธ์ง€๋ฅผ ๋ณต์›ํ•œ๋‹ค
์ง์ ‘ ๊ทธ๋ฆฐ ์—ญ์‚ฐ ๋„์‹. ์ˆœ๋ฐฉํ–ฅ์€ t๋ฅผ ๊ณ„์‚ฐํ•ด ๋‘ ์นธ์— ๋‚˜๋ˆ  ์“ฐ๊ณ  ์—ญ๋ฐฉํ–ฅ์€ ์ƒˆ b๊ฐ’์ด ๊ณง ์˜› a๊ฐ’์ด๋ผ๋Š” ์‚ฌ์‹ค์—์„œ ๋‚˜๋จธ์ง€๋ฅผ ๋ณต์›ํ•œ๋‹ค

a = nb                      # ์˜› buf[off_a] โ€” ๊ทธ๋ƒฅ ์ฝ์œผ๋ฉด ๋œ๋‹ค
b = {                       # ์˜› buf[off_b] โ€” ์—ฐ์‚ฐ๋งŒ ๋’ค์ง‘๋Š”๋‹ค
    "add":  (na - a) & 0xff,
    "sub":  (a - na) & 0xff,
    "xor":  na ^ a,
    "swap": na,
}[op]

sub ์˜ ์—ญ์‚ฐ์ด (a - na) ์ธ ๊ฒŒ ์ž ๊น ํ—ท๊ฐˆ๋ฆฌ๋Š”๋ฐ, ์ˆœ๋ฐฉํ–ฅ์ด t = a - b ๋‹ˆ๊นŒ b = a - t ๋‹ค. t ๊ฐ€ ์ƒˆ *p ๊ฐ’(na)์ด๋ฏ€๋กœ b = a - na. add ๋Š” b = t - a ๋ผ na - a. ๋บ„์…ˆ ๋‘ ๊ฐœ๊ฐ€ ๋ฐฉํ–ฅ์ด ๋ฐ˜๋Œ€๋‹ค.

10000์Šคํ… ์ „์ฒด๊ฐ€ ์ด๋Ÿฐ ์ „๋‹จ์‚ฌ์˜ ํ•ฉ์„ฑ์ด๋‹ˆ, ์ „์ฒด ๋ณ€ํ™˜๋„ 64๋ฐ”์ดํŠธ ๊ณต๊ฐ„์˜ ์ „๋‹จ์‚ฌ๋‹ค. ๋ชฉํ‘œ๊ฐ’์— ํ•ด๋‹นํ•˜๋Š” ์ž…๋ ฅ์ด ์ •ํ™•ํžˆ ํ•˜๋‚˜ ์กด์žฌํ•˜๊ณ , ๊ทธ๊ฑธ ์ฐพ๋Š” ๋ฐฉ๋ฒ•์ด ์—ญ๋ฐฉํ–ฅ ์‹คํ–‰์ด๋‹ค.


๐Ÿงฎ ๊ทธ๋ž˜ํ”„์™€ ํ…Œ์ด๋ธ” ๋ฝ‘์•„๋‚ด๊ธฐ

์ด์ œ ํ•„์š”ํ•œ ๊ฑด ์„ธ ๊ฐ€์ง€๋‹ค. ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ์˜ ์ž์‹ ๋ชฉ๋ก, ํ…Œ์ด๋ธ” 10000์ค„, ๋ชฉํ‘œ 64๋ฐ”์ดํŠธ. ์ „๋ถ€ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ์ •์ ์œผ๋กœ ๋ฝ‘์„ ์ˆ˜ ์žˆ๋‹ค.

#!/usr/bin/env python3
"""Function Network - ๋””์ŠคํŒจ์ฒ˜ ๊ทธ๋ž˜ํ”„ + ์—ฐ์‚ฐ ํ…Œ์ด๋ธ” ์ถ”์ถœ
 
chal ์€ stripped PIE ๋‹ค. ํ•จ์ˆ˜ ์‹ฌ๋ณผ์ด ์—†์œผ๋ฏ€๋กœ
  1) objdump ์ถœ๋ ฅ์—์„œ `endbr64; push rbp` ๋กœ ํ•จ์ˆ˜ ์‹œ์ž‘์„ ์ฐพ๊ณ 
  2) 191๋ฐ”์ดํŠธ์งœ๋ฆฌ ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ์˜ call ํƒ€๊ฒŸ 4๊ฐœ๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ๋ฝ‘์•„ 4์ง„ ํŠธ๋ผ์ด๋ฅผ ๋ณต์›ํ•˜๊ณ 
  3) .data ์˜ (key, off_a, off_b) 10000๊ฐœ๋ฅผ ์ฝ์–ด ๊ฐ key ๋ฅผ ๊ทธ๋ž˜ํ”„๋กœ ๊ฑธ์–ด ๋ฆฌํ”„(์—ฐ์‚ฐ)๋ฅผ ํ™•์ •ํ•œ๋‹ค.
"""
import re, subprocess, json, sys
from pathlib import Path
 
BASE = Path(__file__).resolve().parent
BIN  = BASE / "extracted" / "chal"
 
# --- 1. ๋””์Šค์–ด์…ˆ๋ธ” ---------------------------------------------------------
asm = subprocess.run(["objdump", "-d", "-M", "intel", str(BIN)],
                     capture_output=True, text=True, check=True).stdout.split("\n")
 
starts = []
for i, l in enumerate(asm):
    m = re.match(r"^\s+([0-9a-f]+):\s+f3 0f 1e fa\s+endbr64", l)
    if m and i + 1 < len(asm) and "push" in asm[i + 1] and "rbp" in asm[i + 1]:
        starts.append(int(m.group(1), 16))
starts.sort()
 
# ๋ฆฌํ”„ 4๊ฐœ(์—ฐ์‚ฐ)์™€ main ์€ ํฌ๊ธฐ๋กœ ๊ตฌ๋ถ„๋œ๋‹ค โ€” ๋””์ŠคํŒจ์ฒ˜๋งŒ ์ •ํ™•ํžˆ 0xbf ๋ฐ”์ดํŠธ
LEAF = {0x6f4c: "add", 0x6f8f: "xor", 0x6fd0: "sub", 0x7017: "swap"}
MAIN = 0x704f
 
# --- 2. ๋””์ŠคํŒจ์ฒ˜์˜ ์ž์‹ 4๊ฐœ --------------------------------------------------
addr_of = {}
for i, l in enumerate(asm):
    m = re.match(r"^\s+([0-9a-f]+):", l)
    if m:
        addr_of.setdefault(int(m.group(1), 16), i)
 
children = {}
for idx, a in enumerate(starts):
    if a in LEAF or a == MAIN:
        continue
    end = starts[idx + 1]
    tgts = []
    for l in asm[addr_of[a]:addr_of[end]]:
        m = re.search(r"call\s+([0-9a-f]+)", l)
        if m:
            tgts.append(int(m.group(1), 16))
    assert len(tgts) == 4, (hex(a), [hex(t) for t in tgts])
    children[a] = tgts          # [case0, case1, case2, case3(default)]
 
print(f"[+] ๋””์ŠคํŒจ์ฒ˜ {len(children)}๊ฐœ ยท ๋ฆฌํ”„ {len(LEAF)}๊ฐœ")
 
# --- 3. key -> ์—ฐ์‚ฐ --------------------------------------------------------
ROOT = 0x1209
cache = {}
def op_of(key):
    if key in cache:
        return cache[key]
    node, k, hops = ROOT, key, 0
    while node not in LEAF:
        node = children[node][k & 3]
        k >>= 2
        hops += 1
        if hops > 64:
            raise RuntimeError(f"key={key} ์ˆœํ™˜")
    cache[key] = (LEAF[node], hops)
    return cache[key]
 
# --- 4. .data ์˜ ์—ฐ์‚ฐ ํ…Œ์ด๋ธ” ------------------------------------------------
raw = BIN.read_bytes()
# .data: addr 0xb000, off 0xa000
DATA_ADDR, DATA_OFF = 0xB000, 0xA000
def rd(addr, n):
    o = DATA_OFF + (addr - DATA_ADDR)
    return raw[o:o + n]
 
TBL, N = 0xB020, 10000
ops = []
for i in range(N):
    key, oa, ob = [int.from_bytes(rd(TBL + i * 24 + j * 8, 8), "little") for j in range(3)]
    name, hops = op_of(key)
    ops.append((name, oa, ob, key, hops))
 
target = rd(0x459A0, 64)
 
from collections import Counter
print("[+] ์—ฐ์‚ฐ ๋ถ„ํฌ:", Counter(o[0] for o in ops))
print("[+] ์˜คํ”„์…‹ ๋ฒ”์œ„:", min(min(o[1], o[2]) for o in ops), "~", max(max(o[1], o[2]) for o in ops))
print("[+] ๊ฒฝ๋กœ ๊นŠ์ด:", Counter(o[4] for o in ops))
print("[+] target[0:16] =", target[:16].hex())
 
json.dump({"ops": [(o[0], o[1], o[2]) for o in ops], "target": target.hex()},
          open(BASE / "network.json", "w"))
print("[+] network.json ์ €์žฅ")

ํŒŒ์ผ ์˜คํ”„์…‹ ๋ณ€ํ™˜์€ readelf -SW ๊ฐ€ ์•Œ๋ ค ์ค€ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์ผ๋‹ค. .data ๋Š” ์ฃผ์†Œ 0xb000 ์— ํŒŒ์ผ ์˜คํ”„์…‹ 0xa000 ์ด๋ผ ์ฐจ์ด๊ฐ€ 0x1000 ์ด๋‹ค.

python3 extract.py

extract.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ์™€ ๋ฆฌํ”„ 4๊ฐœ๋ฅผ ์ฐพ์•˜๊ณ  ์—ฐ์‚ฐ ๋ถ„ํฌ๋Š” swap 2550 xor 2512 add 2470 sub 2468 ์ด๋ฉฐ 10000๊ฐœ key ๊ฐ€ ์ „๋ถ€ ๊นŠ์ด 32์—์„œ ๋ฆฌํ”„์— ๋„๋‹ฌํ•œ๋‹ค
extract.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ์™€ ๋ฆฌํ”„ 4๊ฐœ๋ฅผ ์ฐพ์•˜๊ณ  ์—ฐ์‚ฐ ๋ถ„ํฌ๋Š” swap 2550 xor 2512 add 2470 sub 2468 ์ด๋ฉฐ 10000๊ฐœ key ๊ฐ€ ์ „๋ถ€ ๊นŠ์ด 32์—์„œ ๋ฆฌํ”„์— ๋„๋‹ฌํ•œ๋‹ค

์„ธ ์ค„์ด ํ•œ๊บผ๋ฒˆ์— ์—ฌ๋Ÿฌ ๊ฐ€์ •์„ ํ™•์ธํ•ด ์ค€๋‹ค.

  • ์—ฐ์‚ฐ ๋ถ„ํฌ๊ฐ€ ๊ณ ๋ฅด๋‹ค. ๋„ค ์ข…๋ฅ˜๊ฐ€ 2468~2550 ์‚ฌ์ด๋กœ ํฉ์–ด์ ธ ์žˆ๋‹ค. ํŽธ์ค‘์ด ์žˆ์—ˆ๋‹ค๋ฉด ํŒŒ์‹ฑ์ด ํ‹€๋ ธ์„ ๊ฐ€๋Šฅ์„ฑ์„ ์˜์‹ฌํ•ด์•ผ ํ–ˆ๋‹ค.
  • ์˜คํ”„์…‹ ๋ฒ”์œ„๊ฐ€ 0~63. ๋ฒ„ํผ ๋ฐ–์„ ๊ฑด๋“œ๋ฆฌ๋Š” ์ค„์ด ํ•˜๋‚˜๋„ ์—†๋‹ค. ์Šคํƒ ์˜ค์—ผ ๊ฐ™์€ ๊ณ๊ฐ€์ง€๋ฅผ ๋ณผ ํ•„์š”๊ฐ€ ์—†๋‹ค.
  • ๊ฒฝ๋กœ ๊นŠ์ด๊ฐ€ ์ „๋ถ€ 32. ์˜ˆ์™ธ์ ์œผ๋กœ ์ผ์ฐ ๋๋‚˜๋Š” key ๊ฐ€ ์—†๋‹ค๋Š” ๋œป์ด๊ณ , 64๋น„ํŠธ๋ฅผ ๋‚จ๊น€์—†์ด ์“ด๋‹ค๋Š” ๋œป์ด๋‹ค.

ํ…Œ์ด๋ธ” ์•ž๋ถ€๋ถ„์„ ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ฐ์–ด ๋ณด๋ฉด ์ด๋ ‡๋‹ค.

#!/usr/bin/env python3
"""์—ฐ์‚ฐ ํ…Œ์ด๋ธ” ์•ž๋ถ€๋ถ„๊ณผ ๋ชฉํ‘œ ๋ฒ„ํผ๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ฐ๋Š”๋‹ค."""
import json
from pathlib import Path
 
BASE = Path(__file__).resolve().parent
raw = (BASE / "extracted" / "chal").read_bytes()
DATA_ADDR, DATA_OFF, TBL = 0xB000, 0xA000, 0xB020
 
 
def rd(addr, n):
    o = DATA_OFF + (addr - DATA_ADDR)
    return raw[o:o + n]
 
 
net = json.load(open(BASE / "network.json"))
ops = net["ops"]
keys = [int.from_bytes(rd(TBL + i * 24, 8), "little") for i in range(10000)]
 
print("idx  key(64bit)           base4(ํ•˜์œ„->์ƒ์œ„, ์•ž 8์ž๋ฆฌ)  op    off_a off_b")
for i in range(8):
    k = keys[i]
    b4 = "".join(str((k >> (2 * j)) & 3) for j in range(8))
    name, oa, ob = ops[i]
    print(f"{i:<4} 0x{k:016x}   {b4}...                     {name:<5} {oa:<5} {ob}")
 
print()
print(f"์„œ๋กœ ๋‹ค๋ฅธ key: {len(set(keys))}๊ฐœ / ์ „์ฒด {len(keys)}๊ฐœ")
print(f"key ์ตœ์†Œ 0x{min(keys):016x}  ์ตœ๋Œ€ 0x{max(keys):016x}")
print()
print("๋ชฉํ‘œ ๋ฒ„ํผ 0x459a0 (64๋ฐ”์ดํŠธ):")
tgt = bytes.fromhex(net["target"])
for r in range(0, 64, 16):
    print(f"  +0x{r:02x}  " + " ".join(f"{c:02x}" for c in tgt[r:r + 16]))
python3 show_table.py

show_table.py ์‹คํ–‰ ๊ฒฐ๊ณผ. 0๋ฒˆ ์—”ํŠธ๋ฆฌ key ๋Š” 0xc2905b60d68144a8 ์ด๊ณ  base4 ๋กœ 02220101 ์ด๋ฉฐ sub ์—ฐ์‚ฐ์— ์˜คํ”„์…‹ 42์™€ 22 ๋ฅผ ์“ด๋‹ค๋Š” ๊ฒƒ๊ณผ ๋ชฉํ‘œ 64๋ฐ”์ดํŠธ ๋คํ”„๊ฐ€ ๋ณด์ธ๋‹ค
show_table.py ์‹คํ–‰ ๊ฒฐ๊ณผ. 0๋ฒˆ ์—”ํŠธ๋ฆฌ key ๋Š” 0xc2905b60d68144a8 ์ด๊ณ  base4 ๋กœ 02220101 ์ด๋ฉฐ sub ์—ฐ์‚ฐ์— ์˜คํ”„์…‹ 42์™€ 22 ๋ฅผ ์“ด๋‹ค๋Š” ๊ฒƒ๊ณผ ๋ชฉํ‘œ 64๋ฐ”์ดํŠธ ๋คํ”„๊ฐ€ ๋ณด์ธ๋‹ค

key 10000๊ฐœ๊ฐ€ ์ „๋ถ€ ์„œ๋กœ ๋‹ค๋ฅด๋‹ค. ๊ฐ’๋„ 0x0000101d... ๋ถ€ํ„ฐ 0xfffa8927... ๊นŒ์ง€ 64๋น„ํŠธ ์ „ ๋ฒ”์œ„์— ํผ์ ธ ์žˆ๋‹ค. ์ด๋Ÿฐ ์ˆซ์ž๋ฅผ ๋ณด๋ฉด ํ•ด์‹œ๋‚˜ ๋‚œ์ˆ˜ ์‹œ๋“œ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” 4์ง„์ˆ˜ 32์ž๋ฆฌ ๊ฒฝ๋กœ์ผ ๋ฟ์ด๋‹ค.



๐ŸŽฏ ๋Ÿฐํƒ€์ž„์œผ๋กœ ํ•œ ๋ฒˆ ๋” ํ™•์ธ

์—ฌ๊ธฐ๊นŒ์ง€๋Š” ์ „๋ถ€ ์ •์  ๋ถ„์„์ด๋‹ค. ๊ทธ๋ž˜ํ”„๋ฅผ ์ž˜๋ชป ๊ฑธ์—ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ๋‚จ์•„ ์žˆ์œผ๋‹ˆ ์‹ค์ œ๋กœ ๋Œ๋ ค์„œ ๋งž์ถฐ ๋ณธ๋‹ค. gdb ๋ฐฐ์น˜ ๋ชจ๋“œ๋กœ ์„ธ ๊ฐ€์ง€๋ฅผ ํ™•์ธํ–ˆ๋‹ค.

  1. 0๋ฒˆ ์—”ํŠธ๋ฆฌ์˜ key ยท off_a ยท off_b ๊ฐ€ ์ •์ ์œผ๋กœ ์ฝ์€ ๊ฐ’๊ณผ ๊ฐ™์€๊ฐ€
  2. ๊ทธ key ๊ฐ€ ์‹ค์ œ๋กœ extract.py ๊ฐ€ ์˜ˆ์ธกํ•œ ๋ฆฌํ”„(sub)๋กœ ๋–จ์–ด์ง€๋Š”๊ฐ€
  3. ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ์ž…๋ ฅ์„ ๋„ฃ์—ˆ์„ ๋•Œ memcmp ์ง์ „ ๋‘ ๋ฒ„ํผ๊ฐ€ ์ •๋ง ๊ฐ™์€๊ฐ€

PIE ๋ผ์„œ ๋ฒ ์ด์Šค ์ฃผ์†Œ๊ฐ€ ํ•„์š”ํ•œ๋ฐ, 0x555555554000 ์„ ๊ด€๋ก€๋กœ ๊ฐ€์ •ํ•˜์ง€ ์•Š๊ณ  info files ๊ฐ€ ์•Œ๋ ค ์ฃผ๋Š” ์žฌ๋ฐฐ์น˜๋œ .text ์‹œ์ž‘์—์„œ ์—ญ์‚ฐํ–ˆ๋‹ค. ASLR ์„ค์ •์ด ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ๋„ ๊ทธ๋Œ€๋กœ ๋ˆ๋‹ค.

# Function Network โ€” ๋Ÿฐํƒ€์ž„ ๊ฒ€์ฆ (gdb -q -batch -x runtime.gdb --args ./extracted/chal)
#  1) PIE ๋ฒ ์ด์Šค๋Š” ๊ด€๋ก€๋กœ ๊ฐ€์ •ํ•˜์ง€ ์•Š๊ณ  `info files` ์˜ ์žฌ๋ฐฐ์น˜๋œ .text ์—์„œ ์—ญ์‚ฐํ•œ๋‹ค
#  2) main ์˜ dispatch ํ˜ธ์ถœ ์ง€์ ์—์„œ 0๋ฒˆ ์—”ํŠธ๋ฆฌ์˜ key / off_a / off_b ๋ฅผ ์‹ค๋ฌผ๋กœ ํ™•์ธ
#  3) ๊ทธ key ๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋А ๋ฆฌํ”„๋กœ ๋–จ์–ด์ง€๋Š”์ง€ ๋ฆฌํ”„ 4๊ณณ์— ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ๋ฅผ ๊ฑธ์–ด ์žก๋Š”๋‹ค
#  4) memcmp ์ง์ „ ์ž‘์—…๋ฒ„ํผ์™€ ๋ชฉํ‘œ๋ฒ„ํผ๊ฐ€ ์‹ค์ œ๋กœ ๊ฐ™์€์ง€ 64๋ฐ”์ดํŠธ ํ†ต์งธ๋กœ ๋น„๊ต
set pagination off
set confirm off
starti
python
import re, gdb
q = lambda c: gdb.execute(c, to_string=True)
u64 = lambda r: int(gdb.parse_and_eval(r)) & 0xFFFFFFFFFFFFFFFF
byte = lambda p: int(gdb.parse_and_eval("*(unsigned char*)%d" % p))
 
base = int(re.search(r"0x([0-9a-f]+) - 0x[0-9a-f]+ is \.text$",
                     q("info files"), re.M).group(1), 16) - 0x1120
gdb.write("PIE base          = 0x%x   (.text ์žฌ๋ฐฐ์น˜๊ฐ’์—์„œ ์—ญ์‚ฐ)\n" % base)
 
LEAF = {0x6f4c: "add", 0x6f8f: "xor", 0x6fd0: "sub", 0x7017: "swap"}
q("break *0x%x" % (base + 0x728d))                 # main ์˜ dispatch ํ˜ธ์ถœ
q("run < input.txt")
 
key, pa, pb = u64("$rdi"), u64("$rsi"), u64("$rdx")
buf = u64("$rbp") - 0x90
gdb.write("work buffer       = 0x%x  (rbp-0x90)\n" % buf)
gdb.write("entry[0] key      = 0x%016x  base4 ํ•˜์œ„8์ž๋ฆฌ %s\n"
          % (key, "".join(str((key >> (2 * j)) & 3) for j in range(8))))
gdb.write("entry[0] off_a/b  = %d / %d\n" % (pa - buf, pb - buf))
a, b = byte(pa), byte(pb)
gdb.write("ํ˜ธ์ถœ ์ „           : buf[%d]=0x%02x  buf[%d]=0x%02x\n" % (pa - buf, a, pb - buf, b))
 
q("delete")
for off in LEAF:
    q("break *0x%x" % (base + off))
q("continue")
hit = u64("$pc") - base
gdb.write("๋„๋‹ฌํ•œ ๋ฆฌํ”„       = 0x%x -> %s   (extract.py ์˜ˆ์ธก: sub)\n" % (hit, LEAF[hit]))
q("finish")
gdb.write("ํ˜ธ์ถœ ํ›„           : buf[%d]=0x%02x  buf[%d]=0x%02x   (0x%02x-0x%02x=0x%02x, ์˜› a ๊ฐ€ b ์ž๋ฆฌ๋กœ)\n"
          % (pa - buf, byte(pa), pb - buf, byte(pb), a, b, (a - b) & 0xFF))
 
q("delete")
q("break *0x%x" % (base + 0x72c2))                 # memcmp ํ˜ธ์ถœ ์ง์ „
q("continue")
tgt, wrk = u64("$rdi"), u64("$rsi")
inf = gdb.selected_inferior()
tb, wb = bytes(inf.read_memory(tgt, 64)), bytes(inf.read_memory(wrk, 64))
gdb.write("memcmp target[:16]= %s\n" % tb[:16].hex())
gdb.write("memcmp work  [:16]= %s\n" % wb[:16].hex())
gdb.write("64๋ฐ”์ดํŠธ ์ „์ฒด ์ผ์น˜ = %s\n" % (tb == wb))
end

input.txt ์—๋Š” ๋ณต์›ํ•œ 64๊ธ€์ž๋ฅผ ๋„ฃ์–ด ๋’€๋‹ค.

gdb -q -batch -x runtime.gdb --args ./extracted/chal

gdb ๋ฐฐ์น˜ ์‹คํ–‰ ๊ฒฐ๊ณผ. 0๋ฒˆ ์—”ํŠธ๋ฆฌ key ์™€ ์˜คํ”„์…‹ 42 22 ๊ฐ€ ์ •์  ๋ถ„์„๊ณผ ์ผ์น˜ํ•˜๊ณ  ๋ฆฌํ”„ 0x6fd0 sub ์— ๋„๋‹ฌํ•ด 0x31 ๋นผ๊ธฐ 0x36 ์ด 0xfb ๊ฐ€ ๋˜๋ฉฐ memcmp ์ง์ „ ๋‘ ๋ฒ„ํผ๊ฐ€ 64๋ฐ”์ดํŠธ ์ „๋ถ€ ๊ฐ™๋‹ค
gdb ๋ฐฐ์น˜ ์‹คํ–‰ ๊ฒฐ๊ณผ. 0๋ฒˆ ์—”ํŠธ๋ฆฌ key ์™€ ์˜คํ”„์…‹ 42 22 ๊ฐ€ ์ •์  ๋ถ„์„๊ณผ ์ผ์น˜ํ•˜๊ณ  ๋ฆฌํ”„ 0x6fd0 sub ์— ๋„๋‹ฌํ•ด 0x31 ๋นผ๊ธฐ 0x36 ์ด 0xfb ๊ฐ€ ๋˜๋ฉฐ memcmp ์ง์ „ ๋‘ ๋ฒ„ํผ๊ฐ€ 64๋ฐ”์ดํŠธ ์ „๋ถ€ ๊ฐ™๋‹ค

buf[42] ๊ฐ€ 0x31(๊ธ€์ž 1), buf[22] ๊ฐ€ 0x36(๊ธ€์ž 6) ์ด์—ˆ๊ณ , ํ˜ธ์ถœ์ด ๋๋‚˜์ž buf[42] ๊ฐ€ 0xfb, buf[22] ๊ฐ€ 0x31 ์ด ๋๋‹ค. 0x31 - 0x36 = 0xfb ์ด๊ณ  ์˜› a ๊ฐ€ b ์ž๋ฆฌ๋กœ ๊ฐ”๋‹ค โ€” ์ •์ ์œผ๋กœ ์ฝ์€ sub ์˜ ์ •์˜ ๊ทธ๋Œ€๋กœ๋‹ค.

๋งˆ์ง€๋ง‰ ์ค„์ด ๊ฒฐ๋ก ์ด๋‹ค. memcmp ์ง์ „์— ์ž‘์—…๋ฒ„ํผ์™€ ๋ชฉํ‘œ๋ฒ„ํผ๊ฐ€ 64๋ฐ”์ดํŠธ ์ „๋ถ€ ๊ฐ™๋‹ค. ๋น„๊ต๊ฐ€ ์„ฑ๊ณตํ•œ๋‹ค๋Š” ๊ฑธ memcmp ์•ˆ ๋“ค์–ด๊ฐ€ ๋ณด๊ณ ๋„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.


๐Ÿš€ Full Exploit

์—ญ์‚ฐ์€ network.json ํ•˜๋‚˜๋งŒ ์žˆ์œผ๋ฉด ๋œ๋‹ค. ์ˆœ๋ฐฉํ–ฅ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ๋„ ๊ฐ™์ด ๋„ฃ์–ด์„œ, ๋ณต์›ํ•œ ๊ฐ’์„ ๋‹ค์‹œ ์•ž์œผ๋กœ ๊ตด๋ ค ๋ชฉํ‘œ๊ฐ’์ด ๋‚˜์˜ค๋Š”์ง€ ์Šค์Šค๋กœ ๋Œ€์กฐํ•˜๊ฒŒ ํ–ˆ๋‹ค.

#!/usr/bin/env python3
"""Function Network (Dreamhack, Gold 1 / reversing) โ€” solver
 
main ์€ 64๋ฐ”์ดํŠธ ์ž…๋ ฅ์„ ์ž‘์—…๋ฒ„ํผ๋กœ ๋ณต์‚ฌํ•œ ๋’ค, .data ์˜ (key, off_a, off_b) 10000๊ฐœ๋ฅผ
์ˆœ์„œ๋Œ€๋กœ dispatch(key, &buf[off_a], &buf[off_b]) ๋กœ ํ˜๋ ค๋ณด๋‚ด๊ณ  ๊ฒฐ๊ณผ๋ฅผ 0x459a0 ์˜
64๋ฐ”์ดํŠธ์™€ memcmp ํ•œ๋‹ค.
 
key ๋Š” 4์ง„ ํŠธ๋ผ์ด(๋””์ŠคํŒจ์ฒ˜ 125๊ฐœ)๋ฅผ 32๋‹จ๊ณ„ ํƒ€๊ณ  ๋‚ด๋ ค๊ฐ€ ๋ฆฌํ”„ ์—ฐ์‚ฐ 4๊ฐœ ์ค‘ ํ•˜๋‚˜๋กœ ๋–จ์–ด์ง„๋‹ค.
๋ฆฌํ”„๋Š” ์ „๋ถ€ (a,b) -> (f(a,b), a) ๊ผด์ด๋ผ **์ „๋‹จ์‚ฌ**๋‹ค. ๊ทธ๋ž˜์„œ ๋ชฉํ‘œ๊ฐ’์—์„œ ์—ญ์ˆœ์œผ๋กœ
10000๋ฒˆ ๋˜๋Œ๋ฆฌ๋ฉด ์ž…๋ ฅ์ด ๊ทธ๋Œ€๋กœ ๋‚˜์˜จ๋‹ค.
 
  a = buf[off_a], b = buf[off_b] ์ผ ๋•Œ ์ˆœ๋ฐฉํ–ฅ์€
    add : buf[off_a], buf[off_b] = (a+b)&0xff, a
    sub : buf[off_a], buf[off_b] = (a-b)&0xff, a
    xor : buf[off_a], buf[off_b] = a^b,        a
    swap: buf[off_a], buf[off_b] = b,          a
 
  ์—ญ๋ฐฉํ–ฅ์€ ์ƒˆ b ๊ฐ€ ๊ณง ์˜› a ์ด๋ฏ€๋กœ
    a = new_b,  b = {add: new_a-a, sub: a-new_a, xor: new_a^a, swap: new_a}
 
network.json ์€ extract.py ๊ฐ€ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ ๋ฝ‘์•„ ๋‘” ๊ฒƒ์ด๋‹ค(๋””์ŠคํŒจ์ฒ˜ ๊ทธ๋ž˜ํ”„ + ํ…Œ์ด๋ธ” + ๋ชฉํ‘œ๊ฐ’).
"""
import json
from pathlib import Path
 
BASE = Path(__file__).resolve().parent
net = json.load(open(BASE / "network.json"))
ops = [(n, a, b) for n, a, b in net["ops"]]
target = bytearray(bytes.fromhex(net["target"]))
 
 
def forward(buf, ops):
    buf = bytearray(buf)
    for name, oa, ob in ops:
        a, b = buf[oa], buf[ob]
        if   name == "add":  t = (a + b) & 0xFF
        elif name == "sub":  t = (a - b) & 0xFF
        elif name == "xor":  t = a ^ b
        else:                t = b            # swap
        buf[ob] = a
        buf[oa] = t
    return bytes(buf)
 
 
def backward(buf, ops):
    buf = bytearray(buf)
    for name, oa, ob in reversed(ops):
        na, nb = buf[oa], buf[ob]
        a = nb                                 # ์˜› buf[off_a]
        if   name == "add":  b = (na - a) & 0xFF
        elif name == "sub":  b = (a - na) & 0xFF
        elif name == "xor":  b = na ^ a
        else:                b = na            # swap
        buf[oa] = a
        buf[ob] = b
    return bytes(buf)
 
 
flag_body = backward(target, ops)
print("recovered :", flag_body)
print("printable :", all(0x20 <= c < 0x7F for c in flag_body))
 
check = forward(flag_body, ops)
print("forward   :", check.hex())
print("target    :", bytes(target).hex())
print("MATCH" if check == bytes(target) else "MISMATCH")
print()
print("FLAG: DH{%s}" % flag_body.decode())

backward ์—์„œ reversed(ops) ๋กœ ๋„๋Š” ๊ฒŒ ์ „๋ถ€๊ณ , ๊ฐ ์Šคํ…์€ ์„ธ ์ค„์ด๋‹ค.

python3 solve.py

solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋ชฉํ‘œ๊ฐ’์—์„œ 10000์Šคํ…์„ ๋˜๊ฐ์•„ 64๊ธ€์ž 16์ง„ ๋ฌธ์ž์—ด์„ ๋ณต์›ํ–ˆ๊ณ  printable ์ด True ์ด๋ฉฐ ์ˆœ๋ฐฉํ–ฅ์œผ๋กœ ๋‹ค์‹œ ๊ตด๋ฆฐ ๊ฐ’์ด ๋ชฉํ‘œ์™€ MATCH ํ•œ๋‹ค
solve.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋ชฉํ‘œ๊ฐ’์—์„œ 10000์Šคํ…์„ ๋˜๊ฐ์•„ 64๊ธ€์ž 16์ง„ ๋ฌธ์ž์—ด์„ ๋ณต์›ํ–ˆ๊ณ  printable ์ด True ์ด๋ฉฐ ์ˆœ๋ฐฉํ–ฅ์œผ๋กœ ๋‹ค์‹œ ๊ตด๋ฆฐ ๊ฐ’์ด ๋ชฉํ‘œ์™€ MATCH ํ•œ๋‹ค

๋ณต์›๋œ ๊ฐ’์ด ์ „๋ถ€ ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ASCII ๋กœ ๋‚˜์˜จ๋‹ค. ์—ญ์‚ฐ ์ฝ”๋“œ๊ฐ€ ํ‹€๋ ธ๋‹ค๋ฉด ์ด๋Ÿด ํ™•๋ฅ ์ด ์‚ฌ์‹ค์ƒ ์—†์œผ๋‹ˆ (64๋ฐ”์ดํŠธ๊ฐ€ ์šฐ์—ฐํžˆ ์ „๋ถ€ 0x20~0x7e ์— ๋“ค์–ด๊ฐˆ ํ™•๋ฅ ์€ (95/256)^64) ์ด๊ฒƒ ์ž์ฒด๊ฐ€ ๊ฐ•๋ ฅํ•œ ์ž์ฒด ๊ฒ€์ฆ์ด๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๋‚ด์šฉ์ด ์†Œ๋ฌธ์ž 16์ง„์ˆ˜ 64๊ธ€์ž, ์ฆ‰ SHA-256 ํ•ด์‹œ ๋ชจ์–‘์ด๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์ง„์งœ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋จน์—ฌ ๋ณธ๋‹ค. ํŒŒ์ดํ”„ ๋Œ€์‹  subprocess ๋กœ ๋„ฃ์—ˆ๋‹ค.

#!/usr/bin/env python3
"""๋ณต์›ํ•œ ์ž…๋ ฅ์„ ์‹ค์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋จน์—ฌ flag ๊ฐ€ ๋‚˜์˜ค๋Š”์ง€ ํ™•์ธํ•œ๋‹ค."""
import subprocess
from pathlib import Path
 
BASE = Path(__file__).resolve().parent
BODY = "b510ce2d7599e75f79b6bf613384dda282bcedea47184155e55d1d22139afaab"
 
r = subprocess.run([str(BASE / "extracted" / "chal")], input=BODY + "\n",
                   capture_output=True, text=True, timeout=60)
print("Input  :", BODY, f"({len(BODY)} chars)")
print("Output :", r.stdout.strip())
print("Verdict:", "OK" if "Yes, the flag is" in r.stdout else "FAIL")
python3 verify.py

verify.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋ณต์›ํ•œ 64๊ธ€์ž๋ฅผ subprocess ๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋„ฃ์ž Yes the flag is ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์„ฑ๊ณต ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ๋๋‹ค
verify.py ์‹คํ–‰ ๊ฒฐ๊ณผ. ๋ณต์›ํ•œ 64๊ธ€์ž๋ฅผ subprocess ๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋„ฃ์ž Yes the flag is ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์„ฑ๊ณต ๋ฉ”์‹œ์ง€์™€ ํ•จ๊ป˜ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ๋๋‹ค

DH{b510ce2d7599e75f79b6bf613384dda282bcedea47184155e55d1d22139afaab}


โ™ป๏ธ ์žฌํ˜„ โ€” ๋ฐฐํฌ๋ณธ๋งŒ ์žˆ์œผ๋ฉด ๋œ๋‹ค

๋‚˜์ค‘์— ์ด ํด๋”๋งŒ ๋“ค๊ณ  ์™€๋„ flag ๊ฐ€ ๋‹ค์‹œ ๋‚˜์˜ค๋„๋ก ํ•œ ๋ฐฉ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‚จ๊ฒผ๋‹ค. ์ถ”์ถœ๋ถ€ํ„ฐ ์—ญ์‚ฐ, ์‹ค์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋Œ€์กฐ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๋ˆ๋‹ค.

#!/usr/bin/env bash
# Function Network (Dreamhack 1082, Gold 1 / reversing) โ€” ํ•œ ๋ฐฉ ์žฌํ˜„.
#   ๋””์ŠคํŒจ์ฒ˜ ๊ทธ๋ž˜ํ”„ยท์—ฐ์‚ฐ ํ…Œ์ด๋ธ” ์ถ”์ถœ โ†’ 10000 ์Šคํ… ์—ญ์‚ฐ โ†’ ์‹ค์ œ ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ๋Œ€์กฐ.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ œ.json'))['flag'])" 2>/dev/null || echo '')
 
chmod +x extracted/chal
python3 extract.py
OUT=$(timeout 300 python3 solve.py 2>&1) || true
echo "$OUT" | tail -8
 
BODY=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1 | sed 's/^DH{//;s/}$//')
REAL=$(printf '%s\n' "$BODY" | ./extracted/chal 2>&1 | grep -aoE 'DH\{[^}]+\}' | head -1)
 
echo
if [ -n "$REAL" ] && { [ -z "$EXPECT" ] || [ "$REAL" = "$EXPECT" ]; }; then
  echo "โœ… PASS  $REAL"; exit 0
fi
echo "โŒ FAIL (๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ๋‚ธ ๊ฐ’: '${REAL:-์—†์Œ}' / ๊ธฐ๋Œ€: '$EXPECT')"; exit 1
./reproduce.sh

reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ. ์ถ”์ถœ๊ณผ ์—ญ์‚ฐ์„ ๋‹ค์‹œ ๋Œ๋ฆฐ ๋’ค ๋ณต์›๊ฐ’์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋จน์—ฌ ๋‚˜์˜จ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๊ฐ™์•„ PASS ๋กœ ๋๋‚œ๋‹ค
reproduce.sh ์‹คํ–‰ ๊ฒฐ๊ณผ. ์ถ”์ถœ๊ณผ ์—ญ์‚ฐ์„ ๋‹ค์‹œ ๋Œ๋ฆฐ ๋’ค ๋ณต์›๊ฐ’์„ ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋จน์—ฌ ๋‚˜์˜จ ํ”Œ๋ž˜๊ทธ๊ฐ€ ๊ธฐ๋Œ€๊ฐ’๊ณผ ๊ฐ™์•„ PASS ๋กœ ๋๋‚œ๋‹ค

โ–ถ๐Ÿ“Ž ์บก์ณ์— ์“ด objdump ์ฃผ์„ ํŒŒ์ผ 4๊ฐœ

์œ„ ๋””์Šค์–ด์…ˆ๋ธ” ์บก์ณ์˜ <-- ์ฃผ์„์€ sed ์น˜ํ™˜์œผ๋กœ ์–น์€ ๊ฒƒ์ด๋‹ค. --cmd ์•ˆ์— ์ธ๋ผ์ธ์œผ๋กœ ์“ฐ๋ฉด ๋”ฐ์˜ดํ‘œ๊ฐ€ ๊นจ์ ธ์„œ ํŒŒ์ผ๋กœ ๋ถ„๋ฆฌํ–ˆ๋‹ค. ์‹ค์ œ ์ถœ๋ ฅ ์œ„์— ์ฃผ์„๋งŒ ๋”ํ•œ ๊ฒƒ์ด๊ณ  ๋ช…๋ นยท์ฃผ์†Œยท๋ฐ”์ดํŠธ๋Š” ์†๋Œ€์ง€ ์•Š์•˜๋‹ค.

annot_main.sed

s|\(call .*<read@plt>\)|\1        <-- ์ž…๋ ฅ 0x41๋ฐ”์ดํŠธ|
s|\(call .*<strlen@plt>\)|\1      <-- ๊ธธ์ด ๊ฒ€์‚ฌ|
s|\(cmp    rax,0x40\)|\1            <-- ์ •ํ™•ํžˆ 64๊ธ€์ž์—ฌ์•ผ ํ†ต๊ณผ|
s|\(lea    rax,\[rbp-0x50\]\)|\1     <-- ์ž…๋ ฅ ๋ฒ„ํผ|
s|\(lea    rax,\[rbp-0x90\]\)|\1     <-- ์ž‘์—… ๋ฒ„ํผ(์—ฌ๊ธฐ์„œ ๋ณ€ํ˜•๋œ๋‹ค)|

annot_loop.sed

s|\(# b020 .*\)|\1   <-- ํ…Œ์ด๋ธ”[i].key|
s|\(# b028 .*\)|\1   <-- ํ…Œ์ด๋ธ”[i].off_a|
s|\(# b030 .*\)|\1   <-- ํ…Œ์ด๋ธ”[i].off_b|
s|\(shl    rax,0x3\)|\1              <-- i*3*8 = 24๋ฐ”์ดํŠธ ์ŠคํŠธ๋ผ์ด๋“œ|
s|\(call   1209 .*\)|\1   <-- ๋””์ŠคํŒจ์ฒ˜(๊ณ ์ • ์ฃผ์†Œ)|
s|\(cmp    DWORD PTR \[rbp-0x98\],0x270f\)|\1   <-- i <= 9999, ์ด 10000ํšŒ|
s|\(call .*<memcmp@plt>\)|\1     <-- ๋ชฉํ‘œ 64๋ฐ”์ดํŠธ์™€ ๋Œ€์กฐ|
s|\(# 459a0 .*\)|\1   <-- ๋ชฉํ‘œ ๋ฒ„ํผ|

annot_disp.sed

s|\(and    eax,0x3\)|\1              <-- key ํ•˜์œ„ 2๋น„ํŠธ = ์ด๋ฒˆ ๋ ˆ๋ฒจ์˜ ๊ฐˆ๋ฆผ๊ธธ|
s|\(shr    rax,0x2\)|\1              <-- 2๋น„ํŠธ ์†Œ๋น„ํ•˜๊ณ  ๋‹ค์Œ ๋ ˆ๋ฒจ๋กœ|
s|\(cmp    rax,0x1\)|\1            <-- case 1|
s|\(cmp    rax,0x2\)|\1            <-- case 2|

annot_leaf.sed

s|\(add    eax,edx\)$|\1               <-- add  : t = a + b|
s|\(xor    eax,edx\)$|\1               <-- xor  : t = a ^ b|
s|\(sub    eax,ecx\)$|\1               <-- sub  : t = a - b|
s|\(mov    BYTE PTR \[rax\],dl\)|\1     <-- ์˜› a ๋ฅผ b ์ž๋ฆฌ์— / t ๋ฅผ a ์ž๋ฆฌ์—|
โ–ถ๐Ÿ“Ž ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ํ™•์ธํ•œ verify_graph.py ์ „๋ฌธ
#!/usr/bin/env python3
"""๋””์ŠคํŒจ์ฒ˜ ํŒŒ์‹ฑ์ด '์–ด์…ˆ ๋“ฑ์žฅ ์ˆœ์„œ = case ์ˆœ์„œ' ๊ฐ€์ •์— ๊ธฐ๋Œ€์ง€ ์•Š๋Š”์ง€ ๊ฒ€์ฆํ•œ๋‹ค.
 
๊ฐ ๋””์ŠคํŒจ์ฒ˜์˜ 191๋ฐ”์ดํŠธ๋ฅผ ํ†ต์งธ๋กœ ํ›‘์–ด `cmp rax,N` ์ƒ์ˆ˜์™€ ๊ทธ ๋’ค์˜ call ์„ ์ง์ง€์–ด
case0/1/2/default ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ํ™•์ •ํ•œ ๋’ค, extract.py ์˜ ์ˆœ์„œ ๊ธฐ๋ฐ˜ ํŒŒ์‹ฑ๊ณผ ๋Œ€์กฐํ•œ๋‹ค.
"""
import re, subprocess
from pathlib import Path
 
BIN = Path(__file__).resolve().parent / "extracted" / "chal"
asm = subprocess.run(["objdump", "-d", "-M", "intel", str(BIN)],
                     capture_output=True, text=True, check=True).stdout.split("\n")
 
starts = []
for i, l in enumerate(asm):
    m = re.match(r"^\s+([0-9a-f]+):\s+f3 0f 1e fa\s+endbr64", l)
    if m and i + 1 < len(asm) and "push" in asm[i + 1] and "rbp" in asm[i + 1]:
        starts.append(int(m.group(1), 16))
starts.sort()
LEAF = {0x6F4C, 0x6F8F, 0x6FD0, 0x7017}
MAIN = 0x704F
 
idx = {}
for i, l in enumerate(asm):
    m = re.match(r"^\s+([0-9a-f]+):", l)
    if m:
        idx.setdefault(int(m.group(1), 16), i)
 
bad = 0
for n, a in enumerate(starts):
    if a in LEAF or a == MAIN:
        continue
    body = asm[idx[a]:idx[starts[n + 1]]]
    # ์ˆœ์„œ ๊ธฐ๋ฐ˜
    seq = [int(m.group(1), 16) for l in body if (m := re.search(r"call\s+([0-9a-f]+)", l))]
    # ์ƒ์ˆ˜ ๊ธฐ๋ฐ˜: test rax,rax(=0) / cmp rax,1 / cmp rax,2 / ๋‚˜๋จธ์ง€ default
    explicit, pending = {}, None
    for l in body:
        if re.search(r"test\s+rax,rax", l):
            pending = 0
        elif (m := re.search(r"cmp\s+rax,0x([0-9a-f]+)", l)):
            pending = int(m.group(1), 16)
        elif (m := re.search(r"call\s+([0-9a-f]+)", l)):
            explicit[3 if pending is None else pending] = int(m.group(1), 16)
            pending = None
    if [explicit.get(k) for k in (0, 1, 2, 3)] != seq:
        bad += 1
        print("๋ถˆ์ผ์น˜", hex(a), explicit, [hex(x) for x in seq])
 
print(f"๋””์ŠคํŒจ์ฒ˜ {len(starts) - len(LEAF) - 1}๊ฐœ โ€” ์ˆœ์„œ ๊ธฐ๋ฐ˜ ํŒŒ์‹ฑ๊ณผ ์ƒ์ˆ˜ ๊ธฐ๋ฐ˜ ํŒŒ์‹ฑ ๋ถˆ์ผ์น˜ {bad}๊ฑด")
 
# ํŠธ๋ฆฌ์ธ๊ฐ€ DAG ์ธ๊ฐ€
from collections import Counter
refs = Counter()
for n, a in enumerate(starts):
    if a in LEAF or a == MAIN:
        continue
    body = asm[idx[a]:idx[starts[n + 1]]]
    for l in body:
        if (m := re.search(r"call\s+([0-9a-f]+)", l)):
            refs[int(m.group(1), 16)] += 1
print("์ž์‹ ์Šฌ๋กฏ ์ดํ•ฉ:", sum(refs.values()), "ยท ์„œ๋กœ ๋‹ค๋ฅธ ์ž์‹:", len(refs))
print("๊ฐ€์žฅ ๋งŽ์ด ์ฐธ์กฐ๋œ ๋…ธ๋“œ 5๊ฐœ:", [(hex(k), v) for k, v in refs.most_common(5)])
print("๋ฆฌํ”„ ์ฐธ์กฐ ํšŸ์ˆ˜:", {hex(k): refs[k] for k in sorted(LEAF)})
 
# --- ๊ณ„์ธต ๊ตฌ์กฐ ------------------------------------------------------------
children = {}
for n, a in enumerate(starts):
    if a in LEAF or a == MAIN:
        continue
    body = asm[idx[a]:idx[starts[n + 1]]]
    children[a] = [int(m.group(1), 16) for l in body
                   if (m := re.search(r"call\s+([0-9a-f]+)", l))]
 
level, frontier, seen = {0x1209: 0}, [0x1209], {0x1209}
while frontier:
    nxt = []
    for u in frontier:
        for v in children.get(u, []):
            if v not in seen:
                seen.add(v); level[v] = level[u] + 1; nxt.append(v)
            assert level[v] == level[u] + 1, (hex(u), hex(v))
    frontier = nxt
byl = Counter(level.values())
print("๋ ˆ๋ฒจ๋ณ„ ๋…ธ๋“œ ์ˆ˜:", dict(sorted(byl.items())))
print("์ตœ๋Œ€ ๊นŠ์ด:", max(level.values()), "ยท ๋ฆฌํ”„ ๋ ˆ๋ฒจ:", sorted({level[a] for a in LEAF}))

๋กœ์ปฌ ํ™˜๊ฒฝ

ํ•ญ๋ชฉ๊ฐ’
OSUbuntu (Linux 6.17, x86-64)
๋„๊ตฌobjdump (GNU binutils), readelf, GNU gdb 16.3
ํŒŒ์ด์ฌ3.13 โ€” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์‚ฌ์šฉ(์™ธ๋ถ€ ํŒจํ‚ค์ง€ ์—†์Œ)
์‹คํ–‰ ์‹œ๊ฐ„extract.py ์•ฝ 2์ดˆ, solve.py 1์ดˆ ๋ฏธ๋งŒ


๐Ÿ“ ๊ฒฐ๋ก 

๋‚œ๋…ํ™”์˜ ํฌ๊ธฐ์™€ ๊ฐ•๋„๋Š” ๋‹ค๋ฅด๋‹ค.

์ด ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ํ•จ์ˆ˜ 129๊ฐœ์— 240KB์งœ๋ฆฌ ํ…Œ์ด๋ธ”์„ ์–น์—ˆ๋‹ค. ๊ฒ‰๋ณด๊ธฐ ๊ทœ๋ชจ๋งŒ ๋ณด๋ฉด ์†๋Œˆ ์—„๋‘๊ฐ€ ์•ˆ ๋‚œ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ๊ทธ ๊ตฌ์กฐ๋ฌผ์ด ๋งŒ๋“ค์–ด ๋‚ด๋Š” ์ •๋ณด๋Ÿ‰์€ ๊ฒฐ๊ตญ ์—ฐ์‚ฐ 4์ข… ์ค‘ ํ•˜๋‚˜๋‹ค. ํŠธ๋ผ์ด๋ฅผ 32๋‹จ๊ณ„ ๋‚ด๋ ค๊ฐ€๋‚˜ switch (key & 3) ํ•œ ์ค„์„ ์“ฐ๋‚˜ ๊ฒฐ๊ณผ๋Š” ๊ฐ™๋‹ค. ๊ทœ๋ชจ๋ฅผ ํ‚ค์šด๋‹ค๊ณ  ๋˜๋Œ๋ฆฌ๊ธฐ ์–ด๋ ค์›Œ์ง€์ง€๋Š” ์•Š์•˜๋‹ค.

๊ฐ€์—ญ ์—ฐ์‚ฐ๋งŒ ์“ฐ๋ฉด ๋˜๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

add ยท sub ยท xor ยท swap ์€ ์ „๋ถ€ ์ „๋‹จ์‚ฌ๊ณ , ์ด ๋ฌธ์ œ๋Š” ๊ทธ์ค‘ ํ•˜๋‚˜๋„ ๊นจ์ง€ ์•Š์•˜๋‹ค. *q = a ๋กœ ์˜› ๊ฐ’์„ ๋ณด์กดํ•˜๋Š” ํŒจํ„ด์ด ํŠนํžˆ ๊ฒฐ์ •์ ์ด๋‹ค. ํ•œ์ชฝ ์ž…๋ ฅ์„ ๊ฒฐ๊ณผ์— ๊ทธ๋Œ€๋กœ ๋‚จ๊ฒจ ๋‘๋ฉด ๋‚˜๋จธ์ง€ ํ•˜๋‚˜๋Š” ์—ฐ์‚ฐ์„ ๋’ค์ง‘๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค.

์‹ค์ œ๋กœ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†๊ฒŒ ๋งŒ๋“ค๋ ค๋ฉด ์ •๋ณด๋ฅผ ๋ฒ„๋ ค์•ผ ํ•œ๋‹ค. buf[off_a] = a + b ๋งŒ ํ•˜๊ณ  buf[off_b] ๋Š” ๊ทธ๋Œ€๋กœ ๋‘๋Š” ์‹์ด๋ฉด ๋งค ์Šคํ…์—์„œ ๊ฐ’ ํ•˜๋‚˜๊ฐ€ ์‚ฌ๋ผ์ ธ ์—ญ์‚ฐ์ด ๊ฐ€์ง€๋ฅผ ์นœ๋‹ค. ์•„๋‹ˆ๋ฉด off_a == off_b ์ธ ์ค„์„ ์„ž๊ฑฐ๋‚˜(t = a + a ๋Š” ์ตœ์ƒ์œ„ ๋น„ํŠธ๋ฅผ ๋ฒ„๋ฆฐ๋‹ค), ์• ์ดˆ์— ๊ฒฐ๊ณผ๋ฅผ ๋ชฉํ‘œ๊ฐ’๊ณผ ๋น„๊ตํ•˜๋Š” ๋Œ€์‹  ํ•ด์‹œ๋ฅผ ๋น„๊ตํ•˜๋ฉด ์ด ํ’€์ด๋Š” ํ†ต์งธ๋กœ ๋ง‰ํžŒ๋‹ค.

์ •์  ๋ถ„์„์˜ ๊ฐ€์ •์€ ํ™•์ธํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋Š” ํŽธ์ด ์‹ธ๋‹ค.

์ด๋ฒˆ์— ๋‘ ๋ฒˆ ๋ฉˆ์ถฐ์„œ ํ™•์ธํ–ˆ๋‹ค. ๋””์ŠคํŒจ์ฒ˜์˜ case ์ˆœ์„œ๊ฐ€ ์–ด์…ˆ ๋“ฑ์žฅ ์ˆœ์„œ์™€ ๊ฐ™์€์ง€, ๊ทธ๋ฆฌ๊ณ  off_a == off_b ์ธ ์ค„์ด ์—†๋Š”์ง€.

๋‘˜ ๋‹ค "๋งž๊ฒ ๊ฑฐ๋‹ˆ" ํ•˜๊ณ  ๋„˜์–ด๊ฐ€๋„ ์ฝ”๋“œ๋Š” ์ •์ƒ์ ์œผ๋กœ ๋Œ๊ณ  ๊ฒฐ๊ณผ๋งŒ ์กฐ์šฉํžˆ ํ‹€๋ฆฐ๋‹ค. ๊ทธ ์ƒํƒœ์—์„œ ๋””๋ฒ„๊น…์„ ์‹œ์ž‘ํ•˜๋ฉด ์–ด๋””์„œ๋ถ€ํ„ฐ ๋ด์•ผ ํ• ์ง€ ๊ฐ์ด ์•ˆ ์žกํžŒ๋‹ค. ํ™•์ธ์— ๋“  ์‹œ๊ฐ„์€ ๋ช‡ ๋ถ„์ด์—ˆ๋‹ค.

๋ณต์›๊ฐ’์ด ์Šค์Šค๋กœ ๋ง์ด ๋˜๋Š”์ง€ ๋ด๋ผ.

์—ญ์‚ฐ ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ ๊ฐ€๋Šฅํ•œ ASCII 64๊ธ€์ž, ๊ทธ๊ฒƒ๋„ ์†Œ๋ฌธ์ž 16์ง„์ˆ˜์˜€๋‹ค. ์ฑ„์ ์„ ๋Œ๋ฆฌ๊ธฐ ์ „์— ์ด๋ฏธ "๋งž์•˜๋‹ค"๋Š” ์‹ ํ˜ธ๊ฐ€ ๋‚˜์˜จ ์…ˆ์ด๋‹ค. ๋ณต์› ๊ฒฐ๊ณผ๊ฐ€ ์˜๋ฏธ ์žˆ๋Š” ํ˜•ํƒœ๋ฅผ ๊ฐ–์ถ”๋Š”์ง€ ๋ณด๋Š” ๊ฑด ๊ณต์งœ์ธ๋ฐ ๋†“์น˜๊ธฐ ์‰ฝ๋‹ค.

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

Comments

๋Œ“๊ธ€

0๊ฐœ

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

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

Related

๊ด€๋ จ ๊ธ€

3๊ฐœ
[๐Ÿฅ‡ Gold 4] ์ง€๊ทธ์žฌ๊ทธ๋กœ ์ ‘ํžŒ ํ”Œ๋ž˜๊ทธ โ€” DreamHack quilt ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] ์ง€๊ทธ์žฌ๊ทธ๋กœ ์ ‘ํžŒ ํ”Œ๋ž˜๊ทธ โ€” DreamHack quilt ํ’€์ด

๋ฐฐํฌ๋œ quilt๋Š” ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ฒ€์‚ฌํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์•„๋‹ˆ๋ผ ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ทธ๋ฆผ์œผ๋กœ ๊ตฝ๋Š” ์ธ์ฝ”๋”์˜€๋‹ค. /dev/urandom 192๋ฐ”์ดํŠธ ํ•œ๋ณตํŒ์— ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ ์–น๊ณ , 6๋น„ํŠธ์”ฉ ์ž˜๋ผ 64์ƒ‰ ํŒ”๋ ˆํŠธ๋กœ 32ร—32 ๋ธ”๋ก์„ ์น ํ•œ๋‹ค. ๋˜๋Œ๋ฆฌ๋Š” ๊ธธ๋ชฉ์— ๋†“์ธ ํ•จ์ •์€ ํ™€์ˆ˜ ๊ฒฉ์žํ–‰์„ ์ขŒ์šฐ๋กœ ๋’ค์ง‘๋Š” ํ•œ ์ค„์ด๋‹ค.
#dreamhack#ctf#reversing+4
2026-08-20#dreamhack +5
[๐Ÿฅ‡ Gold 4] ํŒŒ์ผ ์ปค์„œ๋ฅผ 656๋ฒˆ ํฉ๋ฟŒ๋ฆฐ ๊ฒ€์ฆ๊ธฐ ๋˜๊ฐ๊ธฐ โ€” DreamHack fverify ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] ํŒŒ์ผ ์ปค์„œ๋ฅผ 656๋ฒˆ ํฉ๋ฟŒ๋ฆฐ ๊ฒ€์ฆ๊ธฐ ๋˜๊ฐ๊ธฐ โ€” DreamHack fverify ํ’€์ด

fverify ๋Š” fseek(SEEK_CUR) 4949๋ฒˆ๊ณผ 1๋ฐ”์ดํŠธ fread 656๋ฒˆ์œผ๋กœ๋งŒ ์ด๋ฃจ์–ด์ง„ ์ง์„  ์ฝ”๋“œ๋‹ค. ํŒŒ์ผ ์˜คํ”„์…‹์ด ์ฝ”๋“œ ์–ด๋””์—๋„ ์•ˆ ์ ํ˜€ ์žˆ์–ด objdump ์ถœ๋ ฅ๋งŒ์œผ๋กœ๋Š” ๋ฌด์—‡์„ ๊ฒ€์‚ฌํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค. ์ปค์„œ ์ด๋™์„ ๊ทธ๋Œ€๋กœ ์žฌ์ƒํ•ด ์Šฌ๋กฏ๊ณผ ํŒŒ์ผ ์˜คํ”„์…‹์„ ๋‹ค์‹œ ์ด์–ด ๋ถ™์ด๋ฉด 656๋ฐ”์ดํŠธ ์ •๋‹ต ํŒŒ์ผ์ด ํ†ต์งธ๋กœ ๋ณต์›๋œ๋‹ค.
#dreamhack#ctf#reversing+5
2026-08-17#dreamhack +5
[๐Ÿฅ‡ Gold 4] 5๋ฐ”์ดํŠธ ์Šคํ… 97๊ฐœ๋กœ ๋ถ€์ˆœ ๊ฑด ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ์˜€๋‹ค โ€” DreamHack Vernichtet ํ’€์ด
blog

[๐Ÿฅ‡ Gold 4] 5๋ฐ”์ดํŠธ ์Šคํ… 97๊ฐœ๋กœ ๋ถ€์ˆœ ๊ฑด ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ์˜€๋‹ค โ€” DreamHack Vernichtet ํ’€์ด

.text ์ „์ฒด์— `eb ff c1 ff c9` ๊ฐ€ 97๊ฐœ ๋ฐ•ํ˜€ ์žˆ๋‹ค. jmp ๊ฐ€ ์ž๊ธฐ ์˜คํผ๋žœ๋“œ๋กœ ๋›ฐ์–ด๋“œ๋Š” ๊ฒน์นจ ๋ช…๋ น์ด๋ผ ์„ ํ˜• ์Šค์œ• ๋””์Šค์–ด์…ˆ๋ธ”๋Ÿฌ๋Š” ์‹คํ–‰๋˜์ง€๋„ ์•Š๋Š” ์œ ๋ น ๋ช…๋ น์„ ๋ณด์—ฌ ์ค€๋‹ค. ๊ธธ์ด๋ฅผ ์œ ์ง€ํ•œ ์ฑ„ NOP ์œผ๋กœ ๋ฎ์–ด ์ฝ”๋“œ๋ฅผ ๋˜์‚ด๋ฆฌ๊ณ , ๊ทธ ์•ˆ์— ์ˆจ์–ด ์žˆ๋˜ 15ร—15 Hidato ํผ์ฆ์„ ํ’€์–ด ๋‹ต์•ˆ ํŒŒ์ผ์˜ sha256 ์„ ํ”Œ๋ž˜๊ทธ๋กœ ๋ฐ›์•„๋ƒˆ๋‹ค.
#dreamhack#ctf#reversing+7
2026-08-17#dreamhack +5