๋ฌธ์ : DreamHack โ locked present ๋ถ๋ฅ: reversing ยท crypto ๋์ด๋: ๐ฅ Silver 1 FLAG:
DH{W0w_U_kn0w_4b0ut_AES?}
๋ฌธ์ ์ค๋ช ์ ๋ ์ค์ด ์ ๋ถ๋ค. "์ ๋ฌผ ์์๋ ์ ๊ฒจ์์ด ๋น๋ฐ๋ฒํธ๋ฅผ ์ ๋ ฅํด์ผ๋ง ์ด ์ ์์ต๋๋ค. ์๋ฌผ์ ์ ํ๋ก๊ทธ๋จ์ ๋ถ์ํ์ฌ ๋น๋ฐ๋ฒํธ๋ฅผ ์์๋ด์ธ์."
๋ฐฐํฌ๋ณธ๋ ํ์ผ ํ๋๋ฟ์ด๋ค. 14KB ์ง๋ฆฌ prob.

๋ฌธ์ ํ์ด์ง ๋งจ ์์ "ํด๋น ๋ฌธ์ ๋ 2022 Christmas CTF ์ ์ถ์ ๋ ๋ฌธ์ ์ ๋๋ค" ๋ผ๊ณ ์ ํ ์๋ค. ๋์ค์ ๋์ค๋ ๋ง์คํฐํค๊ฐ ์ ํฌ๋ฆฌ์ค๋ง์ค ์ธ์ฌ์ธ์ง๋ ์ฌ๊ธฐ์ ์ด๋ฏธ ์๊ณ ๋ผ ์๋ ์ ์ด๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | locked present |
| ๋์ด๋ | ๐ฅ Silver 1 |
| ๋ถ๋ฅ | reversing ยท crypto |
| ์ ๊ณต ํ์ผ | prob (14,528 B ยท ELF 64-bit PIE ยท stripped) |
| ์คํ | C / glibc, gcc 9.4.0 (Ubuntu 20.04) |
| ํต์ฌ ์ทจ์ฝ์ | AES ๋ผ์ด๋ ํจ์์์ MixColumns ๋ง ๋น ์ ธ ํ์ฐ(diffusion)์ด 0 |
ํ์ด ํ๋ฆ์ ์ด๋ ๋ค. stripped ๋ฐ์ด๋๋ฆฌ์์ ํจ์ ์ผ๊ณฑ ๊ฐ๋ฅผ ๋ณต์ํ๋ 5ร5 ๋ธ๋ก์ ์ฐ๋ AES ๋ณํ์ด ๋์จ๋ค. S-box ๋ Rcon ๋ ์ง์ง AES ํ๋ฅผ ๊ทธ๋๋ก ์ฐ๋๋ฐ ๋ผ์ด๋ ํจ์์ MixColumns ๊ฐ ์๋ค. ๊ทธ ์๋ฆฌ๊ฐ ๋น๋ฉด ์ํธ๊ฐ ๋ฐ์ดํธ ๋จ์๋ก ์ชผ๊ฐ์ง๊ณ , ๋ผ์ด๋ ์๊ฐ 10 ์ด๋ผ ๋จ์ ์ด ํ์ ๋ง์ ์์๋๋ค. ๋จ๋ ๊ฑด ์๋ฆฌ๋ง๋ค ๋ ๋ฆฝ์ธ XOR ํ ํ๋๋ฟ์ด๋ผ ๊ทธ๋ฅ ๊ฑฐ๊พธ๋ก ๊ฐ์ผ๋ฉด ๋น๋ฐ๋ฒํธ๊ฐ ๋์จ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ AES ๋ผ์ด๋๊ฐ ํ๋ ๋ค ๊ฐ์ง
AES ์ ํ ๋ผ์ด๋๋ ๋ค ๋จ๊ณ๋ค. SubBytes ๋ก ๋ฐ์ดํธ๋ฅผ S-box ์ ํต๊ณผ์ํค๊ณ , ShiftRows ๋ก ํ์ ๋ฐ๊ณ , MixColumns ๋ก ์ด์ ์๊ณ , AddRoundKey ๋ก ๋ผ์ด๋ํค๋ฅผ XOR ํ๋ค.
์ด ์ค MixColumns ๋ฅผ ๋บ ์ ์ ์ ๋ถ ์๋ฆฌ ๋จ์๋ก ๋ ผ๋ค. SubBytes ๋ ๋ฐ์ดํธ ํ๋๋ฅผ ๋ฐ์ดํธ ํ๋๋ก ๋ฐ๊พธ๊ณ , ShiftRows ๋ ์๋ฆฌ๋ง ์ฎ๊ธฐ๊ณ , AddRoundKey ๋ ๊ฐ์ ์๋ฆฌ๋ผ๋ฆฌ XOR ํ๋ค. ์ด๋ ๊ฒ๋ ๋ ๋ฐ์ดํธ๋ฅผ ์์ง ์๋๋ค.

์ถ์ฒ: Wikimedia Commons, Matt Crypto, public domain
๋ฐ์ดํธ๋ฅผ ์ค์ ๋ก ์๋ ๊ฑด MixColumns ํ๋๋ฟ์ด๋ค. ์ด ํ๋๋ฅผ GF(2^8) ์์ ๋คํญ์ c(x) ์ ๊ณฑํด์, ๊ทธ ์ด์ ๋ค ๋ฐ์ดํธ๊ฐ ์๋ก์๊ฒ ๋ฒ์ง๊ฒ ํ๋ค.

์ถ์ฒ: Wikimedia Commons, Matt Crypto, public domain
ShiftRows ์ MixColumns ๋ฅผ ํฉ์ณ์ผ ๋น๋ก์ "๋ ๋ผ์ด๋๋ง ์ง๋๋ฉด ์ ๋ ฅ ํ ๋นํธ๊ฐ ์ถ๋ ฅ ์ ์ฒด์ ์ํฅ์ ์ค๋ค"๋ ์ฑ์ง์ด ์ ๋ค. ๋ ์ค ํ๋๋ง ๋น ์ ธ๋ ๊ทธ๊ฒ ๋ฌด๋์ง๋ค.
์ด ๋ฌธ์ ๊ฐ ์ ํํ ๊ทธ ์ด์ผ๊ธฐ๋ค.
๐ฌ ์ ์ฐฐ โ ์ฌ๋ณผ ์๋ 14KB
๋จผ์ ํ์ผ ์ ๋ณด์ ๋์ ๋๋ ๋ฌธ์์ด๋ถํฐ ๋ณธ๋ค.
file extracted/prob; nm extracted/prob; strings -n 5 extracted/prob | grep -nE "password|wrong|correct|merry"
PIE, stripped, ์ฌ๋ณผ 0๊ฐ. ์ธ ๋งํ ๋ฌธ์์ด์ ๋ท๋ฟ์ธ๋ฐ ๋ง์ง๋ง์ด ๋์ ๊ฑธ๋ฆฐ๋ค. a_merry_christmas_for_you โ ์ธ์ด ๋ณด๋ฉด ์ ํํ 25๊ธ์๋ค. ๋์ค์ ์ด๊ฒ ๊ทธ๋๋ก ๋ง์คํฐํค๋ก ์ฐ์ธ๋ค.
main ์ ์ง์
์ ์ด __libc_start_main ์ ๋๊ธฐ๋ 0x1757 ์ด๋ค. ์๋จธ๋ฆฌ๋ถํฐ ๋ณธ๋ค.
objdump -d -M intel extracted/prob --start-address=0x1775 --stop-address=0x17af | sed -E -f annot_main.sed์ฃผ์์ ๋ด๊ฐ annot_main.sed ๋ก ์น์ ๊ฒ์ด๊ณ , ๋๋จธ์ง ์ค์ objdump ์๋ณธ ๊ทธ๋๋ก๋ค. ์นํ ๊ท์น ํ์ผ์ ์ด๋ ๊ฒ ์๊ฒผ๋ค.
s|(mov edi,0x113)|\1 <-- 275 = 25๋ฐ์ดํธ x ๋ผ์ด๋ํค 11๊ฐ|
s|(call [0-9a-f]+ <malloc@plt>)|\1 <-- ๋ผ์ด๋ํค ๋ฒํผ|
s|(lea rdi,\[rip\+0x9ad\].*)|\1 <-- "%s"|
s|(call [0-9a-f]+ <__isoc99_scanf@plt>)|\1 <-- ์
๋ ฅ (๊ธธ์ด ๊ฒ์ฌ ์์)|
s|(call 1254 <exit@plt\+0x164>)|\1 <-- key_expand(rk, .data 0x4030)|
s|(call 16e2 <exit@plt\+0x5f2>)|\1 <-- encrypt(state, rk)|
s|(lea rsi,\[rip\+0x27f8\].*)|\1 <-- ๋ง์คํฐํค .data 0x4030|
s|(lea rax,\[rip\+0x2756\].*)|\1 <-- ์ ๋ต ์ํธ๋ฌธ .data 0x4010|
s|(cmp cl,al)|\1 <-- ํ ๋ฐ์ดํธ์ฉ ๋น๊ต|
s|(je 18da <exit@plt\+0x7ea>)|\1 <-- ๊ฐ์ผ๋ฉด ๋ค์ ๋ฐ์ดํธ|
s|(call [0-9a-f]+ <exit@plt>)|\1 <-- ์ฒซ ๋ถ์ผ์น์์ ์ฆ์ ์ข
๋ฃ|
malloc(0x113) = 275 ๋ฐ์ดํธ. 25 ๋ก ๋๋๋ฉด 11 ์ด๋ค. 25๋ฐ์ดํธ์ง๋ฆฌ ๋ผ์ด๋ํค 11๊ฐ ๋ผ๊ณ ์ฝ์ผ๋ฉด ๋ผ์ด๋ 10๊ฐ์ง๋ฆฌ ๋ธ๋ก์ํธ๊ฐ ๋๋ค. ์
๋ ฅ์ scanf("%s") ๋ก ๊ธธ์ด ๊ฒ์ฌ ์์ด ๋ฐ๋๋ค.
๊ทธ๋ค์ main ์ ์
๋ ฅ 25๋ฐ์ดํธ๋ฅผ ์คํ์ ๋ค๋ฅธ ๋ฒํผ๋ก 5ร5 ๋ก ์ฎ๊ธฐ๊ณ , ํจ์ ๋ ๊ฐ๋ฅผ ๋ถ๋ฅธ๋ค. ํ๋๋ .data 0x4030 ์ ์ธ์๋ก ๋ฐ๊ณ (ํค ํ์ฅ), ํ๋๋ ๊ทธ ๊ฒฐ๊ณผ์ ์ํ๋ฅผ ๋ฐ๋๋ค(์ํธํ). ์ํธํ ํจ์ 0x16e2 ๋ฅผ ์ด์ด ๋ณด๋ฉด ๊ตฌ์กฐ๊ฐ ๊ทธ๋๋ก ๋๋ฌ๋๋ค.
objdump -d -M intel extracted/prob --start-address=0x16e2 --stop-address=0x1757 | sed -E -f annot_enc.seds|(mov edi,0x0)|\1 <-- ๋ผ์ด๋ 0|
s|(call 14a7 <exit@plt\+0x3b7>)|\1 <-- add_round_key|
s|(call 1572 <exit@plt\+0x482>)|\1 <-- sub_bytes (S-box)|
s|(call 15fb <exit@plt\+0x50b>)|\1 <-- shift_cols (์ด ํ์ )|
s|(cmp BYTE PTR \[rbp-0x1\],0xa)|\1 <-- ๋ผ์ด๋ 1..10, MixColumns ์์|
๋ผ์ด๋ ํ๋์ call ์ด ์ธ ๋ฒ๋ฟ์ด๋ค. 0x1572(SubBytes), 0x15fb(์๋ฆฌ ์ด๋), 0x14a7(AddRoundKey). ๋ค ๋ฒ์งธ๊ฐ ์๋ค. AES ๋ผ๋ฉด ์ฌ๊ธฐ MixColumns ๊ฐ ์์ด์ผ ํ๋ค.
0x15fb ์ด ๋ฌด์จ ์๋ฆฌ ์ด๋์ธ์ง ํ์ธํ๋ค.
objdump -d -M intel extracted/prob --start-address=0x1619 --stop-address=0x16ca | sed -E -f annot_cols.seds|(movzx eax,BYTE PTR \[rdx\+rax\*1\])|\1 <-- t = st\[i\]|
s|(mov BYTE PTR \[rdx\+rax\*1\],cl)$|\1 <-- st\[i\] = st\[i+5\]|
s|(movzx ecx,BYTE PTR \[rdx\+rax\*1\+0xa\])|\1 <-- st\[i+10\]|
s|(mov BYTE PTR \[rdx\+rax\*1\+0x5\],cl)|\1 <-- st\[i+5\] = st\[i+10\]|
s|(mov BYTE PTR \[rdx\+rax\*1\+0xa\],cl)|\1 <-- st\[i+10\] = st\[i+15\]|
s|(mov BYTE PTR \[rdx\+rax\*1\+0xf\],cl)|\1 <-- st\[i+15\] = st\[i+20\]|
s|(mov BYTE PTR \[rdx\+rax\*1\+0x14\],cl)|\1 <-- st\[i+20\] = t|
s|(cmp al,BYTE PTR \[rbp-0x3\])|\1 <-- ์์ชฝ ๋ฃจํ๋ฅผ i ๋ฒ (์ด i ๋ i ์นธ)|
๊ฑด๋๋ฆฌ๋ ์คํ์
์ด i, i+5, i+10, i+15, i+20 ์ด๋ค. ์ํ๋ฅผ st[5*ํ + ์ด] ๋ก ์ก์ผ๋ฉด ์ด ๋ค์ฏ์ ๊ฐ์ ์ด์ด๋ค. ์ฆ AES ์ ShiftRows ๋ฅผ ์ ์นํ ๋ชจ์ โ ํ์ด ์๋๋ผ ์ด์ ๋ฏผ๋ค. ๋ฐ๊นฅ ๋ฃจํ๊ฐ i ๋ฅผ 0๋ถํฐ 4๊น์ง ๋๋ฆฌ๊ณ ์์ชฝ์์ i ๋ฒ ๋ฐ๋ณตํ๋, ์ด i ๋ i ์นธ ์๋ก ๊ฐ๋ค.
๋ง์ง๋ง์ผ๋ก ํ๋ฅผ ํ์ธํ๋ค.
objdump -s -j .rodata --start-address=0x2020 --stop-address=0x2060 extracted/prob; objdump -s -j .data extracted/prob
0x2020:8d 01 02 04 08 10 20 40 80 1b 36โ ํ์ค AES Rcon ์ด๋ค. ์ ๋์0x8d๊น์ง ๊ทธ๋๋ก๋ค.0x2040:63 7c 77 7b f2 6b 6f c5 โฆโ ํ์ค AES S-box.0x4010: ์ ๋ต ์ํธ๋ฌธ 25๋ฐ์ดํธ37a6b227โฆe20e.0x4030:a_merry_christmas_for_youโ 25๋ฐ์ดํธ ๋ง์คํฐํค.
ํ๋ฅผ ์ง์ ๋ง๋ค์ง ์๊ณ ์ง์ง AES ๊ฒ์ ๊ทธ๋๋ก ๊ฐ์ ธ๋ค ์ด ์ ์ด๋ค. ๊ทธ๋์ ๊ฒ๋ณด๊ธฐ์ AES ์ธ๋ฐ, ์ ์ ํ์ฐ์ ๋ด๋นํ๋ ๋จ๊ณ๋ง ๋น ์ ธ ์๋ค.
๐ฃ ํต์ฌ โ MixColumns ๊ฐ ์์ผ๋ฉด ๋จ๋ ๊ฒ ์๋ค
MixColumns ๋ฅผ ๋นผ๋ฉด ๋ผ์ด๋ ํจ์๋ ์ธ ์กฐ๊ฐ์ด ๋๋๋ฐ, ์ ๋ค ์๋ฆฌ ๋จ์ ์ฐ์ฐ์ด๋ค.
| ๋จ๊ณ | ํ๋ ์ผ | ๋ฐ์ดํธ๋ฅผ ์๋๊ฐ |
|---|---|---|
| SubBytes | st[x] = SBOX[st[x]] | ์๋์ค โ ์๋ฆฌ ์์์ ๊ฐ๋ง ๋ฐ๋๋ค |
| ShiftCols | ์ด j ๋ฅผ j ์นธ ์๋ก | ์๋์ค โ ๊ฐ์ ๊ทธ๋๋ก ์๋ฆฌ๋ง ์ฎ๊ธด๋ค |
| AddRoundKey | st[x] ^= rk[25r + x] | ์๋์ค โ ๊ฐ์ ์๋ฆฌ๋ผ๋ฆฌ XOR |
์ธ ๊ฐ๋ฅผ ์๋ฌด๋ฆฌ ๊ฒน์ณ ์์๋ "ํ๋ฌธ ๋ฐ์ดํธ ํ๋ โ ์ํธ๋ฌธ ๋ฐ์ดํธ ํ๋" ๋ผ๋ ๊ด๊ณ๊ฐ ์ ์ง๋๋ค. ๋ผ์ด๋๋ฅผ ์ด ๋ฒ ๋๋ฆฌ๋ฉด ์๋ฆฌ๊ฐ ์ฌ๊ธฐ์ ๊ธฐ ์ฎ๊ฒจ ๋ค๋ ๋ฟ, ์๋ก ์์ด์ง๋ ์๋๋ค.
๊ทธ๋ฐ๋ฐ ์ด ๋ฌธ์ ๋ ํ ์ ๋ ๋ฌ๋ค. ์ด ํ์ ๋ง์ ์์๋๋ค.
์ด j ๋ ๋ผ์ด๋๋ง๋ค j ์นธ์ฉ ์๋ก ๊ฐ๋ค. ์ด์ ๊ธธ์ด๊ฐ 5 ์ด๋ฏ๋ก 5๋ผ์ด๋๊ฐ ์ง๋๋ฉด 5j ์นธ, ์ฆ ์ ํํ j ๋ฐํด๋ฅผ ๋์ ์ ์๋ฆฌ๋ค. ๋ผ์ด๋๊ฐ 10 ์ด๋ ๋ ๋ฐํด๋ค.

๋ง๋ก๋ง ํ๋ฉด ๋ฏธ๋ฅ์ง ์์ผ๋ ์ซ์๋ก ํ์ธํ๋ค. 1๋นํธ๋ฅผ ๋ค์ง์์ ๋ ์ํธ๋ฌธ ๋ฐ์ดํธ๊ฐ ๋ช ๊ฐ๋ ๋ฐ๋๋์ง ์ธ๊ณ , ์๋ฆฌ ๋์์ด ์ค์ ๋ก ์ด๋ป๊ฒ ๋๋์ง ๋ฝ๊ณ , ๋ง์ง๋ง์ผ๋ก ํค๋ฅผ ์ ํ ๋ชจ๋ฅธ๋ค๊ณ ๊ฐ์ ํ์ ๋ ์ฝ๋๋ถ์ ๋ง๋ค ์ ์๋์ง๊น์ง ํ ๋ฒ์ ๋ณธ๋ค.
#!/usr/bin/env python3
"""์ด ๋ฌธ์ ์ ๊ธ์๊ฐ 'MixColumns ์์'์ด๋ผ๋ ๊ฒ์ ์์น๋ก ํ์ธํ๋ค.
ํ์ฐ(diffusion)์ด ์์ผ๋ฉด ์ํธ๋ฌธ ๋ฐ์ดํธ ํ๋๊ฐ ํ๋ฌธ ๋ฐ์ดํธ ํ๋์๋ง ์์กดํ๋ค.
๊ทธ๋ฌ๋ฉด ํค๋ฅผ ๋ชฐ๋ผ๋ ์ ํํ๋ฌธ 256๊ฐ๋ก ์ฝ๋๋ถ์ ํต์งธ๋ก ๋ง๋ค ์ ์๋ค.
"""
from Crypto.Cipher import AES
import cipher
rk = cipher.key_expand()
BASE = bytes(range(25))
def flipped(pt, bit):
b = bytearray(pt)
b[bit // 8] ^= 1 << (bit % 8)
return bytes(b)
print("=== 1. ๋์ฌํ ํจ๊ณผ (1๋นํธ ๋ค์ง๊ณ ๋ฐ๋ ๋ฐ์ดํธ ์) ===")
c0 = cipher.encrypt(BASE, rk)
worst = 0
for bit in range(25 * 8):
c1 = cipher.encrypt(flipped(BASE, bit), rk)
n = sum(x != y for x, y in zip(c0, c1))
worst = max(worst, n)
print(f"locked present : 200๋นํธ ์ ๋ถ ์ํ -> ๋ฐ๋ ๋ฐ์ดํธ ์ต๋ {worst} / 25")
aes = AES.new(b"a_merry_christma", AES.MODE_ECB) # ๋น๊ต์ฉ ์ง์ง AES-128
p0 = bytes(range(16))
a0 = aes.encrypt(p0)
aworst, abest = 0, 16
for bit in range(16 * 8):
b = bytearray(p0)
b[bit // 8] ^= 1 << (bit % 8)
n = sum(x != y for x, y in zip(a0, aes.encrypt(bytes(b))))
aworst, abest = max(aworst, n), min(abest, n)
print(f"์ง์ง AES-128 : 128๋นํธ ์ ๋ถ ์ํ -> ๋ฐ๋ ๋ฐ์ดํธ {abest}~{aworst} / 16")
print()
print("=== 2. ํ๋ฌธ ์๋ฆฌ -> ์ํธ๋ฌธ ์๋ฆฌ ์์ด ===")
perm = {}
for i in range(25):
b = bytearray(BASE)
b[i] ^= 0xFF
diff = [j for j in range(25) if cipher.encrypt(bytes(b), rk)[j] != c0[j]]
assert len(diff) == 1, f"์๋ฆฌ {i} ๊ฐ {len(diff)}๊ณณ์ ๋ฒ์ก๋ค"
perm[i] = diff[0]
print(" ํ๋ฌธ idx -> ์ํธ๋ฌธ idx :", ", ".join(f"{i}->{perm[i]}" for i in range(25)))
print()
print("=== 3. ํค ์์ด ์ฝ๋๋ถ ๋ณต๊ตฌ (์ ํํ๋ฌธ 256๊ฐ) ===")
# ์๋ฆฌ๋ง๋ค 'ํ๋ฌธ ๋ฐ์ดํธ v -> ์ํธ๋ฌธ ๋ฐ์ดํธ' ํ๋ฅผ ํต์งธ๋ก ๋ง๋ ๋ค.
table = [dict() for _ in range(25)]
for v in range(256):
ct = cipher.encrypt(bytes([v]) * 25, rk)
for i in range(25):
table[i][ct[perm[i]]] = v
flag = bytes(table[i][cipher.CIPHER[perm[i]]] for i in range(25))
print(f" ์ง์ 256ํ๋ก ๋ณต์ํ ํ๋ฌธ: {flag!r}")
assert flag == cipher.decrypt(cipher.CIPHER, rk)
print(" -> .data ์ ๋ง์คํฐํค๋ฅผ ํ ๋ฒ๋ ์ฝ์ง ์๊ณ ๊ฐ์ ๊ฐ์ด ๋์จ๋ค")python3 diffusion.py
์ธ ์ค ๋ค ์์๋๋ก๋ค.
- ๋์ฌํ ํจ๊ณผ: 200๋นํธ๋ฅผ ์ ๋ถ ๋ค์ง์ด ๋ด๋ ๋ฐ๋๋ ์ํธ๋ฌธ ๋ฐ์ดํธ๋ ์ต๋ 1๊ฐ. ๋น๊ต์ฉ์ผ๋ก ๊ฐ์ด ๋๋ฆฐ ์ง์ง AES-128 ์ 128๋นํธ ์ด๋ ๊ฒ์ ๋ค์ง์ด๋ 16๋ฐ์ดํธ ์ค 15~16๊ฐ๊ฐ ๋ฐ๋๋ค.
- ์๋ฆฌ ์์ด:
0โ0, 1โ1, โฆ 24โ24. ์์๋๋ก ํญ๋ฑ์ด๋ค. - ์ฝ๋๋ถ: ์๋ฆฌ๋ง๋ค "ํ๋ฌธ ๋ฐ์ดํธ โ ์ํธ๋ฌธ ๋ฐ์ดํธ" ํ๊ฐ ๋
๋ฆฝ์ด๋ฏ๋ก, ๊ฐ์ ๋ฐ์ดํธ๋ก ์ฑ์ด ํ๋ฌธ 256๊ฐ๋ง ๋ฃ์ด ๋ณด๋ฉด ํ 25๊ฐ๊ฐ ํต์งธ๋ก ์์ฑ๋๋ค. ์ ๋จ๊ณ์์ ์๋ฆฌ ๋์์ ํ์ธํ๋ ๋ฐ ์ด 25ํ๊น์ง ์ธ๋ฉด 281ํ๋ค.
.data์ ๋ง์คํฐํค๋ฅผ ํ ๋ฒ๋ ์ฝ์ง ์๊ณ ๊ฐ์ ํ๋ฌธ์ด ๋์จ๋ค.
๋ง์ง๋ง ํญ๋ชฉ์ด ์ด ์ค๊ณ์ ์ง์ง ๋ฌธ์ ๋ค. ํค๋ฅผ ์จ๊ฒจ ๋ด์ผ ์์ฉ์ด ์๋ค. ์ํธํ ์ค๋ผํด๋ง ์์ผ๋ฉด 300ํ ๋จ์งํ ์ง์๋ก ์ฌ์ ์ด ๋ค ํธ๋ฆฐ๋ค.
๐ฏ ํ์ด โ ๋๊ฐ๊ธฐ
๊ตฌ์กฐ๋ฅผ ์์์ผ๋ ๋๊ฐ์ผ๋ฉด ๋๋ค. ๋จผ์ ๋ฐ์ด๋๋ฆฌ์ ๋ฃจํด์ ๊ทธ๋๋ก ์ฎ๊ธด๋ค. S-boxยทRconยท์ํธ๋ฌธยทํค๋ ํ๋์ฝ๋ฉํ์ง ์๊ณ ELF ์์ ์ง์ ์ฝ๊ฒ ํ๋ค โ ์ฎ๊ฒจ ์ ๋ค ํ๋ฆฌ๋ ๊ฑธ ๋ง๊ณ , ๋์ค์ ๋๊ฐ ๋ค์ ๋๋ ค๋ ๊ฐ์ ๊ฐ์ด ๋์จ๋ค.
#!/usr/bin/env python3
"""locked present (DreamHack, Silver 1) - prob ๋ฐ์ด๋๋ฆฌ์ ์ํธ ๋ฃจํด์ ๊ทธ๋๋ก ์ฌ๊ตฌํํ๋ค.
์์(S-box / Rcon / ์ํธ๋ฌธ / ๋ง์คํฐํค)๋ ํ๋์ฝ๋ฉํ์ง ์๊ณ ELF ์์ ์ง์ ์ฝ๋๋ค.
๋ฐ์ด๋๋ฆฌ๋ PIE ๋ผ objdump ๊ฐ ๋ณด์ฌ ์ฃผ๋ 0x2040 ๊ฐ์ ์ฃผ์๊ฐ ๊ณง ํ์ผ ์คํ์
์ด๋ค
(.rodata: vaddr 0x2000 == file offset 0x2000, .data: vaddr 0x4000 == file offset 0x3000).
"""
import struct
from pathlib import Path
BIN = Path(__file__).resolve().parent / "extracted" / "prob"
def read_sections(path):
"""ELF ์น์
ํค๋๋ฅผ ์ง์ ํ์ฑํด vaddr -> file offset ๋ณํํ๋ฅผ ๋ง๋ ๋ค."""
blob = path.read_bytes()
e_shoff, = struct.unpack_from("<Q", blob, 0x28)
e_shentsize, = struct.unpack_from("<H", blob, 0x3A)
e_shnum, = struct.unpack_from("<H", blob, 0x3C)
e_shstrndx, = struct.unpack_from("<H", blob, 0x3E)
def sh(i):
off = e_shoff + i * e_shentsize
name, _typ, _flags, addr, offset, size = struct.unpack_from("<IIQQQQ", blob, off)
return name, addr, offset, size
_, _, stroff, _ = sh(e_shstrndx)
out = {}
for i in range(e_shnum):
name, addr, offset, size = sh(i)
end = blob.index(b"\0", stroff + name)
out[blob[stroff + name:end].decode()] = (addr, offset, size)
return blob, out
BLOB, SECTIONS = read_sections(BIN)
def at(vaddr, n):
"""๊ฐ์์ฃผ์ vaddr ์์ n ๋ฐ์ดํธ๋ฅผ ์ฝ๋๋ค."""
for _name, (addr, offset, size) in SECTIONS.items():
if addr and addr <= vaddr < addr + size:
delta = vaddr - addr
return BLOB[offset + delta:offset + delta + n]
raise ValueError(f"0x{vaddr:x} ๋ฅผ ๋ด์ ์น์
์ด ์๋ค")
SBOX = at(0x2040, 256) # 0x1572 sub_bytes ๊ฐ ์ฐธ์กฐ
RCON = at(0x2020, 16) # 0x1336 key_expand ๊ฐ ์ฐธ์กฐ
CIPHER = at(0x4010, 25) # main 0x18b3 ์ด ๋น๊ตํ๋ ์ ๋ต
KEY = at(0x4030, 25) # main 0x1831 ์ด key_expand ๋ก ๋๊ธฐ๋ ๋ง์คํฐํค
INV_SBOX = bytes(SBOX.index(i) for i in range(256))
NR = 10 # encrypt 0x174d: ๋ผ์ด๋ 1..10
BS = 25 # 5x5 ์ํ
def key_expand(key=KEY):
"""0x1254 ๋ฅผ ๊ทธ๋๋ก ์ฎ๊ธด ๊ฒ. ๋ผ์ด๋ํค 11๊ฐ(25*11=275=malloc(0x113)) ๋ฅผ ๋ง๋ ๋ค."""
rk = bytearray(25 * (NR + 1))
rk[0:25] = key[0:25]
temp = bytearray(rk[15:20]) # 0x12ac: rk[i+0xf] โ ๋ง์ง๋ง ํ(20..24)์ด ์๋๋ผ ํ 3์ด๋ค
for n in range(1, NR + 1):
temp = temp[1:] + temp[:1] # 0x11e9 rot5: ์ขํ์ 1
for k in range(5): # 0x12f0 ๋ฃจํ
rk[25 * n + k] = rk[25 * (n - 1) + k] ^ SBOX[temp[k]] ^ RCON[n]
for i in range(1, 5): # 0x138e ๋ฃจํ
for j in range(5):
rk[25 * n + 5 * i + j] = (rk[25 * n + 5 * (i - 1) + j]
^ rk[25 * (n - 1) + 5 * i + j])
temp[j] = rk[25 * n + 5 * i + j]
return bytes(rk)
def add_round_key(st, rk, r):
"""0x14a7. state[5i+j] ^= rk[25r + 5i + j]"""
for x in range(BS):
st[x] ^= rk[25 * r + x]
def sub_bytes(st):
"""0x1572. 25๋ฐ์ดํธ ์ ๋ถ S-box ์นํ"""
for x in range(BS):
st[x] = SBOX[st[x]]
def inv_sub_bytes(st):
for x in range(BS):
st[x] = INV_SBOX[st[x]]
def shift_cols(st):
"""0x15fb. ์ด i(=์ธ๋ฑ์ค i, i+5, i+10, i+15, i+20)๋ฅผ i ๋ฒ '์๋ก' ํ์ ."""
for i in range(5):
col = [st[5 * k + i] for k in range(5)]
col = col[i:] + col[:i]
for k in range(5):
st[5 * k + i] = col[k]
def inv_shift_cols(st):
for i in range(5):
col = [st[5 * k + i] for k in range(5)]
col = col[-i:] + col[:-i] if i else col
for k in range(5):
st[5 * k + i] = col[k]
def encrypt(pt, rk):
"""0x16e2. ARK(0) ๋ค์ r=1..10 ์ผ๋ก SubBytes -> ShiftCols -> ARK."""
st = bytearray(pt)
add_round_key(st, rk, 0)
for r in range(1, NR + 1):
sub_bytes(st)
shift_cols(st)
add_round_key(st, rk, r)
return bytes(st)
def decrypt(ct, rk):
"""encrypt ๋ฅผ ์ญ์์ผ๋ก ๋๊ฐ๋๋ค. MixColumns ๊ฐ ์์ด ๊ฐ ๋จ๊ณ๊ฐ ์ ๋ถ ๋ฐ์ดํธ ๋จ์๋ก ๊ฐ์ญ."""
st = bytearray(ct)
for r in range(NR, 0, -1):
add_round_key(st, rk, r)
inv_shift_cols(st)
inv_sub_bytes(st)
add_round_key(st, rk, 0)
return bytes(st)ํค ํ์ฅ์์ ์ง์ง AES ์ ๋ค๋ฅธ ์ง์ ์ด ๋ ์๋๋ฐ, ๋ ๋ค objdump ๋ฅผ ํ ์ค์ฉ ๋ฐ๋ผ๊ฐ์ผ ๋ณด์ธ๋ค.
- ์ด๊ธฐ
temp๋ฅผrk[20:25](๋ง์ง๋ง ์๋)๊ฐ ์๋๋ผrk[15:20]์์ ๊ฐ์ ธ์จ๋ค.0x12af์add eax,0xf๊ฐ ๊ทธ๊ฒ์ด๋ค. - Rcon ์ ์ฒซ ๋ฐ์ดํธ์๋ง XOR ํ์ง ์๊ณ 5๋ฐ์ดํธ ์ ๋ถ์ XOR ํ๋ค.
0x12f0๋ฃจํ๊ฐk์ ๋ฌด๊ดํ๊ฒRCON[n]์ ์๋๋ค.
๋ณตํธ๋ ๋ผ์ด๋๋ฅผ ์ญ์์ผ๋ก ๋๊ฐ๋ ๊ฒ๋ฟ์ด๋ค.
#!/usr/bin/env python3
"""locked present โ ์ ๋ต ์ํธ๋ฌธ์ ๋ณตํธํด ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฝ๋๋ค.
MixColumns ๊ฐ ์์ผ๋ฏ๋ก SubBytes(๋ฐ์ดํธ ๋จ์ ์ ๋จ์ฌ) / ShiftCols(์๋ฆฌ ์์ด) /
AddRoundKey(XOR) ์
๋ค ๊ฐ๋ณ์ ์ผ๋ก ๋๊ฐ์ ์ ์๋ค. ํค๊ฐ .data ์ ๋ฐํ ์์ผ๋
๋ผ์ด๋ํค๋ฅผ ๊ทธ๋๋ก ๋ง๋ค์ด 10๋ผ์ด๋๋ฅผ ์ญ์์ผ๋ก ํ๋ฉด ๋์ด๋ค.
"""
import cipher
rk = cipher.key_expand()
pt = cipher.decrypt(cipher.CIPHER, rk)
print(f"[*] ๋ผ์ด๋ํค {len(rk)}๋ฐ์ดํธ = 25 x {cipher.NR + 1}")
print(f"[*] ์ํธ๋ฌธ : {cipher.CIPHER.hex()}")
print(f"[*] ๋ณตํธ๊ฒฐ๊ณผ: {pt!r}")
# ๋๊ฐ์ ๊ฐ์ ๋ค์ ์ํธํํด์ .data ์ ์ ๋ต๊ณผ ์์ ํ ๊ฐ์์ง ํ์ธํ๋ค.
back = cipher.encrypt(pt, rk)
assert back == cipher.CIPHER, "์ฌ์ํธํ ๋ถ์ผ์น โ ๊ตฌํ์ด ํ๋ ธ๋ค"
print(f"[+] ์ฌ์ํธํ ๊ฒ์ฆ: {back.hex()} == .data@0x4010 OK")
print()
print(f"password = {pt.decode()}")python3 solve.py
DH{W0w_U_kn0w_4b0ut_AES?} โ ์ ํํ 25๋ฐ์ดํธ๋ค. ๋ธ๋ก ํ๋์ ๋ฑ ๋ง์ผ๋ ํจ๋ฉ ๊ณ ๋ฏผ๋ ์์๋ ๊ฒ์ด๋ค.
๐ ์ฝ์ง โ ์กฐ์ฉํ ํ๋ฆฌ๋ ์ค๋ ๋ค ๊ฐ์ง
์ด ๋ฌธ์ ๋ ํ๋ ค๋ ์์ธ๊ฐ ์ ๋๋ค. ์ด๋ป๊ฒ ์๋ชป ์ฝ๋ 25๋ฐ์ดํธ์ง๋ฆฌ ๊ทธ๋ด๋ฏํ ๋ฐ์ดํธ์ด์ด ๋์ค๊ณ , ๊ทธ๊ฒ ๋ต์ด ์๋ ๋ฟ์ด๋ค. ์ค์ ๋ก ํค๋งจ ์ง์ ๋ค์ ๊ทธ๋๋ก ์ฌํํด ๋ดค๋ค.
#!/usr/bin/env python3
"""์ด ๋ฌธ์ ์์ '์กฐ์ฉํ ํ๋ฆฌ๋' ์ค๋
4์ข
์ ์ค์ ๋ก ๋๋ ค ๋ณธ๋ค.
๋ท ๋ค ์์ธ๊ฐ ์ ๋๋ค. 25๋ฐ์ดํธ์ง๋ฆฌ ๊ทธ๋ด๋ฏํ ๋ฐ์ดํธ์ด์ด ๋์ค๊ณ , ๊ทธ๊ฒ flag ๊ฐ ์๋ ๋ฟ์ด๋ค.
์ฆ ๋์ผ๋ก๋ ๋ชป ์ก๊ณ '์ฌ์ํธํํด์ .data ์ ๋์กฐ' ํ๋ ๊ฒ์ฆ๋ง์ด ํ๋ณ๊ธฐ๋ค.
"""
import cipher
REAL = b"DH{W0w_U_kn0w_4b0ut_AES?}"
def report(name, pt):
printable = all(0x20 <= b < 0x7F for b in pt)
verdict = "์ ๋ต" if pt == REAL else "์ค๋ต"
print(f" [{verdict}] {name}")
print(f" -> {pt!r}")
print(f" ์ถ๋ ฅ๊ฐ๋ฅ๋ฌธ์๋ง์ธ๊ฐ: {printable} / ์์ธ ๋ฐ์: ์์")
def expand(temp_src=slice(15, 20), rcon_all=True):
rk = bytearray(25 * 11)
rk[0:25] = cipher.KEY
temp = bytearray(rk[temp_src])
for n in range(1, 11):
temp = temp[1:] + temp[:1]
for k in range(5):
v = rk[25 * (n - 1) + k] ^ cipher.SBOX[temp[k]]
if rcon_all or k == 0:
v ^= cipher.RCON[n]
rk[25 * n + k] = v
for i in range(1, 5):
for j in range(5):
rk[25 * n + 5 * i + j] = (rk[25 * n + 5 * (i - 1) + j]
^ rk[25 * (n - 1) + 5 * i + j])
temp[j] = rk[25 * n + 5 * i + j]
return bytes(rk)
def dec(rk, inv_shift):
st = bytearray(cipher.CIPHER)
for r in range(10, 0, -1):
cipher.add_round_key(st, rk, r)
inv_shift(st)
cipher.inv_sub_bytes(st)
cipher.add_round_key(st, rk, 0)
return bytes(st)
def inv_cols_down(st): # ์ ๋ต: ์ํธํ๊ฐ '์๋ก' ๋๋ ธ์ผ๋ ๋ณตํธ๋ '์๋๋ก'
cipher.inv_shift_cols(st)
def inv_cols_up(st): # ํจ์ 3: ๋ฐฉํฅ์ ๋ฐ๋๋ก
cipher.shift_cols(st)
def inv_rows(st): # ํจ์ 4: ์ด์ด ์๋๋ผ 'ํ'์ ๋๋ฆฐ๋ค๊ณ ์ฝ์ (์ง์ง AES ์ ShiftRows)
for i in range(5):
row = [st[5 * i + k] for k in range(5)]
row = row[-i:] + row[:-i] if i else row
for k in range(5):
st[5 * i + k] = row[k]
print("์ ๋ต ๊ตฌํ")
report("temp=rk[15:20] ยท Rcon 5๋ฐ์ดํธ ์ ๋ถ ยท ์ด์ ์๋๋ก", dec(expand(), inv_cols_down))
print("\nํจ์ 1 โ ์ง์ง AES ์ฒ๋ผ '๋ง์ง๋ง ์๋'(rk[20:25])๋ก ์์ํ๋ค๊ณ ์ฝ์")
report("temp=rk[20:25]", dec(expand(temp_src=slice(20, 25)), inv_cols_down))
print("\nํจ์ 2 โ ์ง์ง AES ์ฒ๋ผ Rcon ์ ์ฒซ ๋ฐ์ดํธ์๋ง XOR")
report("Rcon on k==0 only", dec(expand(rcon_all=False), inv_cols_down))
print("\nํจ์ 3 โ ShiftCols ์ญ๋ฐฉํฅ (10๋ผ์ด๋๊ฐ ์์๋๋ ๋ฐฉํฅ์ ์๊ด์๋ค๊ณ ์ฐฉ๊ฐ)")
report("์ด์ ์๋ก ๋๋๋ฆผ", dec(expand(), inv_cols_up))
print("\nํจ์ 4 โ ์ด์ด ์๋๋ผ ํ์ ๋๋ฆฐ๋ค๊ณ ์ฝ์ (AES ์ ShiftRows ๋ก ์ค๋
)")
report("ํ ํ์ ", dec(expand(), inv_rows))
print("\n์ฐธ๊ณ โ ๋ธ๋ก์ด 25๋ฐ์ดํธ๋ผ ์ง์ง AES ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ์ด์ ๋ชป ์ด๋ค")
try:
from Crypto.Cipher import AES
AES.new(cipher.KEY, AES.MODE_ECB)
except ValueError as e:
print(f" Crypto.Cipher.AES.new(25๋ฐ์ดํธ ํค) -> ValueError: {e}")python3 traps.py
โถ๐ ์ฝ์ง 1 โ ์ง์ง AES ์ฒ๋ผ '๋ง์ง๋ง ์๋'๋ก ์์ํ๋ค๊ณ ์ฝ์๋ค
AES-128 ํค ํ์ฅ์ W[4] = W[0] ^ g(W[3]) ์ฒ๋ผ ์ง์ ์๋๋ฅผ ๋ฌผ๊ณ ์์ํ๋ค. 25๋ฐ์ดํธ ํค๋ฅผ 5์๋๋ก ๋ณด๋ฉด ๋ง์ง๋ง ์๋๋ rk[20:25] ์ฌ์ผ ์์ฐ์ค๋ฝ๋ค.
๊ทธ๋ฐ๋ฐ ์ด ๋ฐ์ด๋๋ฆฌ๋ rk[15:20] ์ ๊ฐ์ ธ์จ๋ค. ๋ผ์ด๋ ๋ฃจํ ๋์์ temp ๋ฅผ ๊ฐฑ์ ํ ๋๋ ๋ง์ง๋ง ํ(i=4)์ ์ฐ๋ฉด์, ์ต์ด ํ ๋ฒ๋ง ํ 3 ์ ์ด๋ค. ์ถ์ ์์ ์ค์์ธ์ง ์๋์ธ์ง๋ ์ ์ ์์ง๋ง ๊ทธ๋๋ก ์ฎ๊ฒจ์ผ ๋ง๋๋ค.
0x12ac ์ ์ธ ์ค์ด ๊ทผ๊ฑฐ๋ค. add eax,0xf ์ 0xf = 15.
12ac: mov eax,DWORD PTR [rbp-0x1c] ; i
12af: add eax,0xf ; i + 15 <- 20 ์ด ์๋๋ค
12b2: mov edx,eax์ด๊ฑธ 20 ์ผ๋ก ์ฝ์ผ๋ฉด ๋ผ์ด๋ํค ์ด ๊ฐ๊ฐ ์ ๋ถ ์ด๊ธ๋๋๋ฐ, ๋ณตํธ๋ ๋ฉ์ฉกํ ๋๋๊ณ b'\xb7\x06\x82Lโฆ' ์ด ๋์จ๋ค. ๊ธธ์ด๋ 25 ๋ฐ์ดํธ๋ค.
โถ๐ ์ฝ์ง 2 โ Rcon ์ ์ฒซ ๋ฐ์ดํธ์๋ง XOR ํ๋ค
AES ์ Rcon ์ ์๋์ ์ต์์ ๋ฐ์ดํธ ํ๋์๋ง ๋ค์ด๊ฐ๋ค. ์ต๊ด๋๋ก if k == 0 ์ ๋ถ์๋๋ ์ญ์ ์กฐ์ฉํ ํ๋ ธ๋ค.
๋ฐ์ด๋๋ฆฌ๋ k ๋ฅผ 0๋ถํฐ 4๊น์ง ๋๋ ๋ฃจํ ์์์ ๋งค๋ฒ RCON[n] ์ ์๋๋ค. 0x1333~0x1362 ๊ตฌ๊ฐ์ k ๋ก ๊ฐ๋ผ์ง๋ ๋ถ๊ธฐ๊ฐ ์๋ค.
โถ๐ ์ฝ์ง 3 โ ์ด ํ์ ๋ฐฉํฅ์ ๋ฐ๋๋ก ์ก์๋ค
"์ด์ฐจํผ 10๋ผ์ด๋๋ฉด ์์๋๋ ๋ฐฉํฅ์ ์๊ด์์ง ์๋" ๋ผ๊ณ ์ ๊น ์๊ฐํ๋ค. ํ๋ ธ๋ค.
์๋ฆฌ ์์ด์ ์ต์ข ๊ฒฐ๊ณผ๋ ํญ๋ฑ์ด ๋ง์ง๋ง, ๋ผ์ด๋ ์ฌ์ด์ฌ์ด์ AddRoundKey ๊ฐ ๋ผ์ด๋ ๋ค. ๋ผ์ด๋ํค ๋ฐ์ดํธ๊ฐ ์ด๋ ์๋ฆฌ์ ์นํ๋์ง๋ ๊ทธ ์์ ์ ๋ฐฐ์ด์ ๋ฌ๋ ค ์์ผ๋ฏ๋ก, ๋ฐฉํฅ์ ๋ค์ง์ผ๋ฉด ์ค๊ฐ ๋์์ด ์ ๋ถ ๋ฌ๋ผ์ง๋ค.
๊ฒฐ๊ณผ๋ ์ญ์ ์์ธ ์๋ 25๋ฐ์ดํธ ์ฐ๋ ๊ธฐ๋ค.
โถ๐ ์ฝ์ง 4 โ ์ด์ด ์๋๋ผ ํ์ ๋๋ฆฐ๋ค๊ณ ์ฝ์๋ค
๊ฐ์ฅ ๊ทธ๋ด๋ฏํ ์ค๋
์ด๋ค. AES ๋ฅผ ์๋ ์ฌ๋์ผ์๋ก 0x15fb ์ ๋ณด๊ณ "์ ShiftRows ๊ตฌ๋" ํ๊ณ ๋์ด๊ฐ๊ธฐ ์ฝ๋ค.
๊ตฌ๋ถ์ ์ ์คํ์
๊ฐ๊ฒฉ ํ๋๋ค. ํ์ ๋๋ฆฐ๋ค๋ฉด ์ธ์ ํ i, i+1, i+2 โฆ ๋ฅผ ๊ฑด๋๋ ค์ผ ํ๋๋ฐ, ์ค์ ๋ก๋ i, i+5, i+10 โฆ ์ด๋ค. 5์นธ์ฉ ๊ฑด๋๋ฐ๋ ์ด์ด๋ค.
ํ ๊ฐ์ง ๋. 25๋ฐ์ดํธ ๋ธ๋ก์ด๋ผ ์ง์ง AES ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ ์ด์ ๋ชป ์ด๋ค. Crypto.Cipher.AES.new ์ 25๋ฐ์ดํธ ํค๋ฅผ ์ฃผ๋ฉด Incorrect AES key length (25 bytes) ๋ก ๊ฑฐ๋ถ๋นํ๋ค. "AES ๋๊น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ํ๋ฉด ๋๊ฒ ์ง" ๋ผ๋ ๊ธธ์ ์ฒ์๋ถํฐ ๋งํ ์๋ ์
์ด๋ค.
โ ๊ฒ์ฆ โ ๋์ด ์๋๋ผ ๋ฐ์ด๋๋ฆฌ๊ฐ ํ์ ํ๊ฒ
์ฝ์ง ๋ท์ด ์ ๋ถ "๊ทธ๋ด๋ฏํ 25๋ฐ์ดํธ"์๋ค๋ ๊ฒ ์์ ์ด๋ค. ๋์ผ๋ก ๋ณด๊ณ ๋ง๋ค๊ณ ํ๋ฉด ์ ๋๋ค. ํ์ ์ ๋๊ตฌ์ ๋๊ธฐ๊ณ ์ธ ๊ฐ์ง ๊ฒฝ๋ก๋ก ๋ฐ๋ก ํ์ธํ๋ค.
1) ์ฌ๊ตฌ์ฑํ C ์์ค๊ฐ ๊ฐ์ ์ํธ๋ฌธ์ ๋ธ๋ค
๋์ปดํ์ผ ๊ฒฐ๊ณผ๋ฅผ C ๋ก ๋ค์ ์จ์, ๊ฐ์ ํ๋ฅผ ์ฝ๊ณ ๊ฐ์ ์ํธ๋ฌธ์ด ๋์ค๋์ง ๋ณธ๋ค. ํจ์ ์ด๋ฆ์ ํ๋ ์ผ์ ๋ณด๊ณ ๋ถ์๊ณ , ์ธ๋ฑ์ค ๊ณ์ฐ์ objdump ๊ทธ๋๋ก๋ค.
/* locked present โ ๋์ปดํ์ผ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ์ฎ๊ธด ์ฌ๊ตฌ์ฑ ์์ค.
*
* ์๋ณธ prob ์ stripped ๋ผ ์ด๋ฆ์ด ์๋ค. ์๋ ์ด๋ฆ์ ํ๋ ์ผ์ ๋ณด๊ณ ๋ถ์ธ ๊ฒ์ด๊ณ ,
* ์ฐ์ฐ ์์ยท์ธ๋ฑ์ค ๊ณ์ฐ์ objdump ์ถ๋ ฅ ๊ทธ๋๋ก๋ค.
*
* gcc -O0 -no-pie -o recon recon.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NR 10 /* encrypt 0x174d: ๋ผ์ด๋ 1..10 */
#define BS 25 /* 5 x 5 ์ํ */
/* .rodata 0x2040 โ ํ์ค AES S-box */
static unsigned char SBOX[256];
/* .rodata 0x2020 โ ํ์ค AES Rcon (8d 01 02 04 08 10 20 40 80 1b 36) */
static unsigned char RCON[16];
/* .data 0x4030 */
static unsigned char KEY[BS];
/* .data 0x4010 โ ์ ๋ต ์ํธ๋ฌธ */
static unsigned char CIPHER[BS];
/* 0x11e9 โ 5๋ฐ์ดํธ๋ฅผ ์ผ์ชฝ์ผ๋ก 1์นธ ํ์ */
static void rot5(unsigned char *p)
{
unsigned char t = p[0];
p[0] = p[1]; p[1] = p[2]; p[2] = p[3]; p[3] = p[4]; p[4] = t;
}
/* 0x1254 โ ๋ผ์ด๋ํค 11๊ฐ(275๋ฐ์ดํธ, main ์ malloc(0x113)) */
static void key_expand(unsigned char *rk, const unsigned char *key)
{
unsigned char temp[5];
int n, i, j, k;
for (i = 0; i <= 24; i++)
rk[i] = key[i];
/* ์ฌ๊ธฐ๊ฐ ์ง์ง AES ์ ๋ค๋ฅด๋ค: ๋ง์ง๋ง ํ(20..24)์ด ์๋๋ผ ํ 3(15..19)์ ์ง๋๋ค */
for (i = 0; i <= 4; i++)
temp[i] = rk[i + 15];
for (n = 1; n <= NR; n++) {
rot5(temp);
for (k = 0; k <= 4; k++)
/* Rcon ์ด ์ฒซ ๋ฐ์ดํธ๊ฐ ์๋๋ผ 5๋ฐ์ดํธ ์ ๋ถ์ XOR ๋๋ค */
rk[25 * n + k] = rk[25 * (n - 1) + k] ^ SBOX[temp[k]] ^ RCON[n];
for (i = 1; i <= 4; i++) {
for (j = 0; j <= 4; j++) {
rk[25 * n + 5 * i + j] =
rk[25 * n + 5 * (i - 1) + j] ^ rk[25 * (n - 1) + 5 * i + j];
temp[j] = rk[25 * n + 5 * i + j];
}
}
}
}
/* 0x14a7 */
static void add_round_key(unsigned char r, unsigned char *st, const unsigned char *rk)
{
unsigned char i, j;
for (i = 0; i <= 4; i++)
for (j = 0; j <= 4; j++)
st[5 * i + j] ^= rk[25 * r + 5 * i + j];
}
/* 0x1572 */
static void sub_bytes(unsigned char *st)
{
unsigned char i, j;
for (i = 0; i <= 4; i++)
for (j = 0; j <= 4; j++)
st[5 * j + i] = SBOX[st[5 * j + i]];
}
/* 0x15fb โ ์ด i ๋ฅผ i ๋ฒ ์๋ก ํ์ . AES ์ ShiftRows ๋ฅผ ์ ์นํ ๋ชจ์์ด๋ค */
static void shift_cols(unsigned char *st)
{
unsigned char i, c, t;
for (i = 0; i <= 4; i++) {
for (c = 0; c < i; c++) {
t = st[i];
st[i] = st[i + 5];
st[i + 5] = st[i + 10];
st[i + 10] = st[i + 15];
st[i + 15] = st[i + 20];
st[i + 20] = t;
}
}
}
/* 0x16e2 โ MixColumns ๊ฐ ์๋ค */
static void encrypt(unsigned char *st, const unsigned char *rk)
{
unsigned char r;
add_round_key(0, st, rk);
for (r = 1; r <= NR; r++) {
sub_bytes(st);
shift_cols(st);
add_round_key(r, st, rk);
}
}
/* ์๋ณธ ๋ฐ์ด๋๋ฆฌ์์ ํ๋ฅผ ๊ทธ๋๋ก ๋ ์จ๋ค (ํ๋์ฝ๋ฉ ๋์ ) */
static void load_tables(const char *path)
{
FILE *f = fopen(path, "rb");
if (!f) { perror("fopen"); exit(2); }
fseek(f, 0x2040, SEEK_SET); fread(SBOX, 1, 256, f);
fseek(f, 0x2020, SEEK_SET); fread(RCON, 1, 16, f);
fseek(f, 0x3010, SEEK_SET); fread(CIPHER, 1, BS, f); /* .data vaddr 0x4010 -> off 0x3010 */
fseek(f, 0x3030, SEEK_SET); fread(KEY, 1, BS, f);
fclose(f);
}
int main(int argc, char **argv)
{
unsigned char rk[25 * (NR + 1)];
unsigned char st[BS];
int i;
load_tables(argc > 1 ? argv[1] : "extracted/prob");
key_expand(rk, KEY);
if (argc > 2) { /* ์์ ํ๋ฌธ์ ์ํธํํด ๋ณธ๋ค */
memcpy(st, argv[2], BS);
} else { /* ๊ธฐ๋ณธ: ์ฐ๋ฆฌ๊ฐ ์ฐพ์ ๋น๋ฐ๋ฒํธ */
memcpy(st, "DH{W0w_U_kn0w_4b0ut_AES?}", BS);
}
printf("plain : %.25s\n", st);
encrypt(st, rk);
printf("cipher : ");
for (i = 0; i < BS; i++) printf("%02x", st[i]);
printf("\n.data : ");
for (i = 0; i < BS; i++) printf("%02x", CIPHER[i]);
printf("\nmatch : %s\n", memcmp(st, CIPHER, BS) == 0 ? "YES" : "no");
return memcmp(st, CIPHER, BS) != 0;
}gcc -O0 -no-pie -o recon recon.c && ./recon extracted/prob
2) ์ค์ ํ๋ก์ธ์ค์ ๋ผ์ด๋๋ณ ์ํ๋ฅผ ๋ฌ๋ค
์ฌ๊ตฌํ์ด ๋ง๋์ง๋ ๊ฒฐ๊ตญ ์ง์ง ์คํ๊ณผ ๋์กฐํด์ผ ํ๋ค. gdb ๋ก encrypt ์์ ๋ธ๋ ์ดํฌ๋ฅผ ๊ฑธ๊ณ ๋ผ์ด๋๋ง๋ค ์ํ 25๋ฐ์ดํธ๋ฅผ ํต์งธ๋ก ์ฝ๋๋ค. PIE ๋ผ ๋ก๋ ๋ฒ ์ด์ค๋ ๊ด๋ก๋ก ๊ฐ์ ํ์ง ์๊ณ info proc mappings ์์ ์ง์ ์ฝ์๋ค.
# ์ค์ prob ํ๋ก์ธ์ค์ ๋ผ์ด๋๋ณ ์ํ๋ฅผ ๊ทธ๋๋ก ๋ ์ ํ์ด์ฌ ์ฌ๊ตฌํ๊ณผ ๋์กฐํ๋ค.
# gdb -batch -x trace_rounds.gdb
set confirm off
set pagination off
file ./extracted/prob
starti
python
# PIE ๋ฒ ์ด์ค๋ฅผ ๊ด๋ก๋ก ๊ฐ์ ํ์ง ์๋๋ค โ ๋งคํ์์ ์ง์ ์ฝ๋๋ค
maps = gdb.execute("info proc mappings", to_string=True)
base = next(int(l.split()[0], 16) for l in maps.splitlines() if l.rstrip().endswith("/prob"))
print("PIE base = %s" % hex(base))
gdb.execute("break *%s" % hex(base + 0x170F)) # encrypt: AddRoundKey(0) ์งํ
gdb.execute("break *%s" % hex(base + 0x1743)) # encrypt: ๊ฐ ๋ผ์ด๋ AddRoundKey(r) ์งํ
gdb.execute("run < ./pw.txt", to_string=True)
inf = gdb.selected_inferior()
print("master key .data@0x4030 : %r" % bytes(inf.read_memory(base + 0x4030, 25)))
print("cipher .data@0x4010 : %s" % bytes(inf.read_memory(base + 0x4010, 25)).hex())
print("round key[0..24] : %s" %
bytes(inf.read_memory(int(gdb.parse_and_eval("*(unsigned long*)($rbp-0x20)")), 25)).hex())
for r in range(11):
ptr = int(gdb.parse_and_eval("*(unsigned long*)($rbp-0x18)"))
print("round %2d state : %s" % (r, bytes(inf.read_memory(ptr, 25)).hex()))
if r < 10:
gdb.execute("continue", to_string=True)
end
quitgdb -batch -x trace_rounds.gdb
๋ผ์ด๋ 10 ์ ์ํ๊ฐ .data 0x4010 ์ ์ํธ๋ฌธ๊ณผ ๊ฐ๋ค. ๋น์ฐํ์ง๋ง ์ค๊ฐ ์ํ ๊ฐ๊ฐ ๋ ์ค์ํ๋ค. ๊ทธ๊ฑธ ํ์ด์ฌ ์ฌ๊ตฌํ๊ณผ ํ ์ค์ฉ ๋์กฐํ๋ค.
#!/usr/bin/env python3
"""gdb ๋ก ๋ฌ ์ค์ ํ๋ก์ธ์ค์ ๋ผ์ด๋๋ณ ์ํ์ ํ์ด์ฌ ์ฌ๊ตฌํ์ ํ ๋ผ์ด๋์ฉ ๋์กฐํ๋ค.
gdb_rounds.txt ๋ trace_rounds.gdb ๊ฐ ๋ง๋ ๋ค:
gdb -batch -x trace_rounds.gdb | grep -E '^round +[0-9]+ state' > gdb_rounds.txt
"""
import re
from pathlib import Path
import cipher
live = {}
for line in Path("gdb_rounds.txt").read_text().splitlines():
m = re.match(r"round\s+(\d+) state : ([0-9a-f]{50})", line)
if m:
live[int(m.group(1))] = bytes.fromhex(m.group(2))
rk = cipher.key_expand()
st = bytearray(b"DH{W0w_U_kn0w_4b0ut_AES?}")
cipher.add_round_key(st, rk, 0)
ok = True
for r in range(11):
if r:
cipher.sub_bytes(st)
cipher.shift_cols(st)
cipher.add_round_key(st, rk, r)
same = bytes(st) == live[r]
ok &= same
print(f"round {r:2d} py={bytes(st).hex()} gdb={'์ผ์น' if same else '๋ถ์ผ์น'}")
print()
print("11๊ฐ ๋ผ์ด๋ ์ํ ์ ๋ถ ์ผ์น" if ok else "๋ถ์ผ์น ๋ฐ์ โ ์ฌ๊ตฌํ์ด ํ๋ ธ๋ค")
assert okgdb -batch -x trace_rounds.gdb | grep -E "^round +[0-9]+ state" > gdb_rounds.txt && python3 verify_rounds.py
11๊ฐ ๋ผ์ด๋๊ฐ ์ ๋ถ ์ผ์นํ๋ค. ์ค๊ฐ ์ํ๊น์ง ๋ง์์ผ๋ ํค ํ์ฅยทS-boxยท์ด ํ์ ์ ๋ค ์ ๋๋ก ์ฎ๊ธด ๊ฒ์ด๋ค.
3) ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ์ ์ง์ ๋ฃ๋๋ค
๋ง์ง๋ง์ ๋ฌธ์ ๊ฐ ์ค์ค๋ก ๋ตํ๊ฒ ํ๋ค. ๋์กฐ๊ตฐ์ผ๋ก ํ ๊ธ์ ๋ฐ๊พผ ๊ฒ๊ณผ ํ ๊ธ์ ๋บ ๊ฒ๋ ๊ฐ์ด ๋ฃ์๋ค.
#!/usr/bin/env python3
"""์ฐพ์๋ธ ๋น๋ฐ๋ฒํธ๋ฅผ ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ์ ์ค์ ๋ก ๋ฃ์ด ํ์ ์ ๋ฐ๋๋ค.
ํ ๊ธ์๋ง ๋ฐ๊พผ ๋์กฐ๊ตฐ์ ๊ฐ์ด ๋๋ ค 'correct' ๊ฐ ์ฐ์ฐ์ด ์๋์ ๋ณด์ธ๋ค.
"""
import subprocess
PW = "DH{W0w_U_kn0w_4b0ut_AES?}"
CASES = [
("์ ๋ต", PW),
("๋ง์ง๋ง S ๋ฅผ s ๋ก", PW.replace("AES", "AEs")),
("๊ธธ์ด 24 (๋ง์ง๋ง } ์ ๊ฑฐ)", PW[:-1]),
]
for label, pw in CASES:
r = subprocess.run(["./extracted/prob"], input=pw + "\n",
capture_output=True, text=True)
print(f"[{label}]")
print(f" ์
๋ ฅ : {pw}")
print(f" ์ถ๋ ฅ : {r.stdout.strip()}")
print(f" ์ข
๋ฃ์ฝ๋: {r.returncode}")python3 verify.py
๐ ํค๋ฅผ ๋ชฐ๋ผ๋ ์ด๋ฆฐ๋ค โ ์กฐ๊ธฐ์ข ๋ฃ ์ค๋ผํด
์ฌ๊ธฐ๊น์ง๋ .data ์ ๋ง์คํฐํค๋ฅผ ์ฝ์ด์ ํผ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ์ด ๋ฌธ์ ๋ ์ํธ๋ฅผ ํ ์ค๋ ์ญ์ฐํ์ง ์๊ณ ๋ ๋ซ๋ฆฐ๋ค. main ์ ๋ง์ง๋ง ๋น๊ต ๋ฃจํ๊ฐ ์๋ ๊ณณ์ด๋ค.
objdump -d -M intel extracted/prob --start-address=0x1868 --stop-address=0x1910 | sed -E -f annot_main.sed
cmp cl,al ์ด ์ด๊ธ๋๋ฉด ๊ณง๋ฐ๋ก puts("wrong") + exit(1) ์ด๋ค. ๋จ์ ๋ฐ์ดํธ๋ ๋น๊ต์กฐ์ฐจ ํ์ง ์๋๋ค. ์ฆ ๋ช ๋ฒ์งธ์์ ํ๋ ธ๋์ง๊ฐ ํ๋ก๊ทธ๋จ ์ํ์ ๋จ๋๋ค. ํ๋ฉด์๋ ์ด๋ ์ชฝ์ด๋ wrong ๋ง ์ฐํ์ง๋ง, ๋ฃจํ ๋ณ์ i(rbp-0xa0)์ j(rbp-0x9c)๋ฅผ ์ฝ์ผ๋ฉด ์ฒซ ๋ถ์ผ์น ์์น๊ฐ ๊ทธ๋๋ก ๋์จ๋ค.
์ฌ๊ธฐ์ ์์์ ํ์ธํ ์ฑ์ง์ด ๋ถ๋๋ค. ํ์ฐ์ด 0 ์ด๊ณ ์๋ฆฌ ์์ด์ด ํญ๋ฑ์ด๋, ์๋ฆฌ p ์ ๋น๊ต ๊ฒฐ๊ณผ๋ ์
๋ ฅ ๋ฐ์ดํธ p ํ๋์๋ง ๋ฌ๋ ค ์๋ค. ๊ทธ๋ฌ๋ฉด ์์์๋ถํฐ ํ ๊ธ์์ฉ ํ์ ํด ๋๊ฐ ์ ์๋ค.
gdb ๋ฅผ ๋ถ์ฌ ์๋ํํ๋ค. run ์ ์ธ์
์์์ ๋ฐ๋ณตํ๋ ๋ฐฉ์์ด๋ผ gdb ๊ธฐ๋ ๋น์ฉ์ด ํ ๋ฒ๋ง ๋ ๋ค.
# gdb -batch -x oracle_attack.py ๋ก ๋๋ฆฐ๋ค.
#
# main ์ ๋ง์ง๋ง ๋น๊ต ๋ฃจํ(0x1874~0x18d5)๋ ์ฒซ ๋ถ์ผ์น์์ ๊ณง๋ฐ๋ก puts("wrong") + exit(1) ํ๋ค.
# ์ฆ "๋ช ๋ฒ์งธ ๋ฐ์ดํธ๊น์ง ๋ง์๋๊ฐ"๊ฐ ํ๋ก๊ทธ๋จ ๋ด๋ถ ์ํ์ ๋จ๋๋ค.
# ์ํธ๋ฅผ ํ ์ค๋ ์ญ์ฐํ์ง ์๊ณ ์ด ์กฐ๊ธฐ์ข
๋ฃ๋ง ์ด์ฉํด ๋น๋ฐ๋ฒํธ๋ฅผ ์๋ฆฌ๋ณ๋ก ๋ณต๊ตฌํ๋ค.
import string, time
WRONG = 0x18C4 # lea rdi,"wrong" โ ์ฒซ ๋ถ์ผ์น ์ง์
DONE = 0x18FE # lea rdi,"correct" โ 25๋ฐ์ดํธ ์ ๋ถ ์ผ์น
IDX_I = 0xA0 # main ๋ฐ๊นฅ ๋ฃจํ ๋ณ์ i ๋ rbp-0xa0
IDX_J = 0x9C # ์์ชฝ ๋ฃจํ ๋ณ์ j ๋ rbp-0x9c
INFILE = "/tmp/lp_oracle_in"
gdb.execute("set confirm off")
gdb.execute("set pagination off")
gdb.execute("file ./extracted/prob")
gdb.execute("starti", to_string=True)
# PIE ๋ผ ๋ก๋ ๋ฒ ์ด์ค๋ฅผ ๊ด๋ก๋ก ๊ฐ์ ํ์ง ์๊ณ ์ค์ ๋งคํ์์ ์ฝ๋๋ค.
maps = gdb.execute("info proc mappings", to_string=True)
base = next(int(l.split()[0], 16) for l in maps.splitlines() if l.rstrip().endswith("/prob"))
print(f"[*] PIE base = {hex(base)}")
gdb.execute(f"break *{hex(base + WRONG)}", to_string=True)
gdb.execute(f"break *{hex(base + DONE)}", to_string=True)
gdb.execute("kill", to_string=True)
def probe(guess: bytes) -> int:
"""guess ๋ฅผ ๋ฃ๊ณ '๋ช ๋ฒ์งธ ๋ฐ์ดํธ์์ ์ฒ์ ํ๋ ธ๋์ง'๋ฅผ ๋๋ ค์ค๋ค. 25 ๋ฉด ์ ๋ถ ์ ๋ต."""
with open(INFILE, "wb") as f:
f.write(guess + b"\n")
gdb.execute(f"run < {INFILE}", to_string=True)
stopped_at = int(gdb.parse_and_eval("$pc"))
if stopped_at == base + DONE:
gdb.execute("kill", to_string=True)
return 25
i = int(gdb.parse_and_eval(f"*(int*)($rbp-{hex(IDX_I)})"))
j = int(gdb.parse_and_eval(f"*(int*)($rbp-{hex(IDX_J)})"))
gdb.execute("kill", to_string=True)
return 5 * i + j
# scanf("%s") ๋ผ ๊ณต๋ฐฑ/NUL ์ ์ ์ด์ ๋ชป ๋ฃ๋๋ค. ํ์์ ์ถ๋ ฅ ๊ฐ๋ฅ ๋ฌธ์๋ก ์ถฉ๋ถํ๋ค.
ALPHABET = (string.ascii_letters + string.digits + "{}_?!@#$%^&*()-+=[]<>.,:;/\\|~`'\"").encode()
FILLER = b"A"
t0 = time.time()
known = b""
runs = 0
for pos in range(25):
for c in ALPHABET:
cand = known + bytes([c]) + FILLER * (24 - pos)
runs += 1
if probe(cand) > pos:
known += bytes([c])
print(f"[{pos:2d}] {chr(c)!r} ํ์ -> {known.decode()}")
break
else:
print(f"[!] ์๋ฆฌ {pos} ์์ ํ๋ณด ์์ง")
break
print()
print(f"password = {known.decode()}")
print(f"[*] ๋ฐ์ด๋๋ฆฌ ์คํ {runs}ํ / {time.time() - t0:.1f}์ด โ ๋ณตํธ ๋ฃจํด์ ํ ์ค๋ ์ ์ผ๋ค")
print(f"[*] ์ต์ข
ํ์ธ: probe(password) = {probe(known)} / 25")
gdb.execute("quit")gdb -batch -x oracle_attack.py | grep -E "^\[|^password"
๋ฐ์ด๋๋ฆฌ ์คํ 1,046ํ / 95์ด. ๋ณตํธ ๋ฃจํด์ ํ ์ค๋ ์ ์ผ๋๋ฐ ๊ฐ์ ๊ฐ์ด ๋์๋ค.
25์๋ฆฌ๋ฅผ ์ ์๋ก ๋ค์ง๋ฉด 94^25 ์ธ๋ฐ, ์๋ฆฌ๋ณ๋ก ์๋ฆฌ๋ 25 ร 94 ๋ก ๋จ์ด์ง๋ค. ํ์ฐ์ด ์์๋ค๋ฉด ์ด ๊ณต๊ฒฉ์ ์ ์ด์ ์ฑ๋ฆฝํ์ง ์๋๋ค. ํ ๋ฐ์ดํธ๋ง ๋ฐ๊ฟ๋ ์ํธ๋ฌธ ์ ์ฒด๊ฐ ๋ค์งํ์ "์์์ ๋ช ๋ฒ์งธ๊น์ง ๋ง์๋ค"๋ ๊ฐ๋
์์ฒด๊ฐ ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ ๋ฆฌํ๋ฉด ์ด ๋ฐ์ด๋๋ฆฌ๋ ๊ฐ์ ๊ธ์๋ก ๋ ๋ฒ ์ด๋ฆฐ๋ค.
| ๊ฒฝ๋ก | ํ์ํ ๊ฒ | ๋น์ฉ |
|---|---|---|
| ๋ผ์ด๋ ์ญ์ฐ | .data ์ ๋ง์คํฐํค | ์ฆ์ |
| ์ ํํ๋ฌธ ์ฝ๋๋ถ | ์ํธํ ์ค๋ผํด | ์ง์ 256ํ |
| ์กฐ๊ธฐ์ข ๋ฃ ์ค๋ผํด | ๊ฒ์ฆ ๋ฐ์ด๋๋ฆฌ๋ง | ์คํ 1,046ํ ยท 95์ด |
์ธ ๋ฒ์งธ๊ฐ ์ ์ผ ๊ณ ์ฝํ๋ค. ํค๋ ์๊ณ ๋ฆฌ์ฆ๋ ๋ชฐ๋ผ๋ ๋๊ณ , ์คํ ํ์ผ ํ๋๋ง ์์ผ๋ฉด ๋๋ค.
๐ ์ฌํ
์์ ํด๋๋ง ์์ผ๋ฉด ์ฒ์๋ถํฐ ๋๊น์ง ํ ๋ฒ์ ๋๋ค.
#!/usr/bin/env bash
# locked present (DreamHack, Silver 1) โ ํ ๋ฐฉ ์ฌํ.
# cipher.py ์ฌ๊ตฌํ์ผ๋ก .data ์ ์ํธ๋ฌธ์ ๋ณตํธ โ ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ๋ก ํ์ ๊น์ง.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
echo "== 1) ์ฌ๊ตฌํ์ผ๋ก ๋ณตํธ =="
OUT=$(timeout 120 python3 solve.py 2>&1) || true
echo "$OUT"
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
echo
echo "== 2) ์ฌ๊ตฌ์ฑ C ์์ค๋ก ๊ฐ์ ์ํธ๋ฌธ์ด ๋์ค๋์ง =="
gcc -O0 -no-pie -o recon recon.c
./recon extracted/prob | tail -2
echo
echo "== 3) ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ์ ์ค์ ์
๋ ฅ =="
chmod +x extracted/prob
python3 verify.py | head -4
RC=$(python3 - "$FLAG" <<'PY'
import subprocess, sys
r = subprocess.run(["./extracted/prob"], input=sys.argv[1] + "\n",
capture_output=True, text=True)
print(r.returncode)
PY
)
echo
if [ -n "$FLAG" ] && [ "$RC" = "0" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo "โ
PASS $FLAG"
exit 0
fi
echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '${EXPECT:-๋ฏธ๊ธฐ๋ก}' / ๋ฐ์ด๋๋ฆฌ rc: $RC)"
exit 1./reproduce.sh
๋ก์ปฌ ํ๊ฒฝ์ ์ด๋ ๋ค.
| ํญ๋ชฉ | ๊ฐ |
|---|---|
| OS | Ubuntu ๊ธฐ๋ฐ Linux 6.17 x86-64 |
| gcc | 9.4.0 ์ผ๋ก ๋น๋๋ ๋ฐฐํฌ๋ณธ / ์ฌ๊ตฌ์ฑ์ ๋ก์ปฌ gcc -O0 -no-pie |
| gdb | GNU gdb 16.3 (ํ์ด์ฌ ํ์ฅ ์ฌ์ฉ) |
| python | 3.13 ยท pycryptodome (๋น๊ต์ฉ AES-128 ์๋ง ํ์) |
| ๋์ | extracted/prob sha256 065408f1439584c7bd4d37c3e353538241686a358ad442511c5bfbac954cfd25 |
๐ ๊ฒฐ๋ก
MixColumns ๋ ์ฅ์์ด ์๋๋ค.
์ด ๋ฌธ์ ์ ์ํธ๋ ํ๋ฅผ ํ๋๋ ๋์ถฉ ๋ง๋ค์ง ์์๋ค. S-box ๋ Rcon ๋ ์ง์ง AES ๊ฒ์ ๊ทธ๋๋ก ์ด๋ค. ๋ผ์ด๋๋ 10๋ฒ ๋๋ค. ๊ทธ๋ฐ๋ฐ ๋ฐ์ดํธ๋ฅผ ์๋ ๋จ๊ณ ํ๋๊ฐ ๋น ์ง์ ๋จ์ ๊ฑด ์๋ฆฌ๋ง๋ค ๋ ๋ฆฝ์ธ XOR ํ 25๊ฐ์๋ค. ๋ผ์ด๋๋ฅผ 100๋ฒ ๋๋ ค๋ ๊ฒฐ๊ณผ๋ ๊ฐ๋ค. ์๋ ํ์ฐ์ ๋ฐ๋ณต์ผ๋ก ๋ง๋ค์ด์ง์ง ์๋๋ค.
๋ธ๋ก ํฌ๊ธฐ์ ๋ผ์ด๋ ์๋ฅผ ๊ฐ์ด ๋ด์ผ ํ๋ค.
์ด ํ์ ์ด ํต์งธ๋ก ์์๋ ๊ฑด ์ฐ์ฐ์ด๋ค. ์ด ๊ธธ์ด๊ฐ 5, ๋ผ์ด๋๊ฐ 10 ์ด๋ผ 10j โก 0 (mod 5) ๊ฐ ๋๋ค. ๋ผ์ด๋๋ฅผ 11 ๋ก ์ก์์ผ๋ฉด ์๋ฆฌ ์์ด์ด ์ด์ ์์์ ๊ฒ์ด๋ค. ๋ฌผ๋ก ๊ทธ๋๋ ๋ฐ์ดํธ๋ ์ ์์ด๋ ๊ทผ๋ณธ์ ๊ทธ๋๋ก์ง๋ง, ์ด๋ฐ ์์ผ๋ก ํ๋ผ๋ฏธํฐ๊ฐ ์๋ก ๋ง๋ฌผ๋ ค ์ฑ์ง์ ์ฃฝ์ด๋ ์ผ์ ์์ ์ํธ์์ ํํ๋ค.
"ํ๋ ค๋ ํฐ๊ฐ ์ ๋๋ค"๊ฐ ์ด ๋ฌธ์ ์ ๋์ด๋๋ค.
ํค ํ์ฅ์ ์์ ์๋, Rcon ์ ๋ช ๋ฐ์ดํธ์ ๋ฃ๋์ง, ํ์ ๋ฐฉํฅ, ํ์ด๋ ์ด์ด๋ โ ๋ค ๊ตฐ๋ฐ๋ฅผ ์๋ชป ์ฝ์ด๋ ์์ธ ํ๋ ์ ๋๊ณ 25๋ฐ์ดํธ๊ฐ ๋์จ๋ค. ๊ฒฐ๊ตญ ํ์ ์ ์ฌ์ํธํ ๊ฒฐ๊ณผ๊ฐ .data ์ ๊ฐ์์ง, gdb ๋ก ๋ฌ ์ค์ ๋ผ์ด๋ ์ํ์ ๋ง๋์ง, ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ๊ฐ correct ๋ฅผ ๋ฑ๋์ง๋ก๋ง ํ ์ ์๋ค. ์์ ์ํธ๋ฅผ ๋๊ฐ์ ๋๋ ๊ฒ์ฆ์ ๋จผ์ ์ค๊ณํด ๋๋ ๊ฒ ๋ซ๋ค.
๊ฒ์ฆ ๋ก์ง์์๋ ์ ๋ณด๊ฐ ์๋ค.
์ํธ๋ฅผ ์๋ฌด๋ฆฌ ์ ๋ง๋ค์ด๋ ๋น๊ต๋ฅผ ์ฒซ ๋ถ์ผ์น์์ ๋์ผ๋ฉด ์๋ฆฌ๋ณ ์ค๋ผํด์ด ์๊ธด๋ค. ์ฌ๊ธฐ์๋ ํ์ฐ์ด 0 ์ด๋ผ ๊ทธ๊ฒ ๊ณง๋ฐ๋ก ์ ์ฒด ๋ณต๊ตฌ๋ก ์ด์ด์ก์ง๋ง, ํ์ฐ์ด ๋ฉ์ฉกํ ์ํธ๋ผ๋ ์ด๋ฐ ์กฐ๊ธฐ์ข ๋ฃ๋ HMAC ๊ฒ์ฆ ๊ฐ์ ๋ฐ์ ์ค์ ๋ฌธ์ ๊ฐ ๋๋ค. ์์ ์๊ฐ ๋น๊ต๋ฅผ ์ฐ๋ ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ