๋ฌธ์ : DreamHack โ snow ๋ถ๋ฅ: reversing ๋์ด๋: ๐ฅ Silver 1 FLAG:
DH{F805042AC5E59FD07ABEF1E9B2EF}
๋ฌธ์ ์ค๋ช
์ ๋ฑ ํ ์ค, "๋์ด ๋ ์ฉ~" ์ด๋ค. ํํธ๋ ์๊ณ ์ฒจ๋ถ๋ prob ํ๋๋ฟ์ด๋ค. ์ฐ์ ์ด๋ค ํ์ผ์ธ์ง๋ถํฐ ๋ดค๋ค.
file prob && readelf -h -d prob | grep -E "Type:|Entry|NEEDED|BIND_NOW|FLAGS" && objdump -T prob | grep -E "strcpy|memcmp|strlen|printf"
์ ๋ชฉ์ด ๋(snow)์ด๋ผ ์ฒซ์ธ์์ ํฌ๋ฆฌ์ค๋ง์ค ๋๋ด ๊ฐ์๋๋ฐ, ์ค์ ๋ก๋ ์ด๋ฆ์ด ๊ทธ๋๋ก ๋ต์ด์๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | snow |
| ๋์ด๋ | ๐ฅ Silver 1 |
| ๋ถ๋ฅ | reversing |
| ์ ๊ณต ํ์ผ | prob (x86-64 PIE ELF, stripped, 16,568 bytes) |
| ์๋ฒ | ์์ โ ์คํ๋ผ์ธ ๋ฌธ์ |
| ํต์ฌ ๊ธฐ๋ฒ | SNOW 3G ์ฌ๊ตฌํ + ์๊ธฐ์ฐธ์กฐ IV ๋ฅผ 8๋นํธ ํ์์ผ๋ก ์ ๊ธฐ |
ํ๋ฆ์ ์ด๋ ๋ค. ๋ฐ์ด๋๋ฆฌ๋ 32๊ธ์์ง๋ฆฌ ์ธ์๋ฅผ ๋ฐ์ ์ด๋ค ๋ณํ์ ๊ฑฐ์น ๋ค .data ์ ๋ฐํ 32๋ฐ์ดํธ์ memcmp ํ๋ค.
๊ทธ ๋ณํ์ ์ฐ์ด๋ ๋์๋ 3GPP ํ์ค ์คํธ๋ฆผ ์ํธ SNOW 3G ์ ํค์คํธ๋ฆผ์ด๊ณ , ํค๋ ์์ค์ ๋ฐํ ์๋ค.
๋ฌธ์ ๋ IV ๊ฐ ์ฐ๋ฆฌ ์ ๋ ฅ์ ์ 16๋ฐ์ดํธ๋ผ๋ ๊ฒ. ๋ณต์ํ๋ ค๋ ๊ฐ์ด ์๊ธฐ ์์ ์ ์ํธํ์ ๊ด์ฌํ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ LFSR ํ๋๋ก ๋์๋ฅผ ๋ง๋ ๋ค๋ ๊ฒ
์คํธ๋ฆผ ์ํธ๋ ํค์ IV ๋ก ๊ธด ๋์์ด(ํค์คํธ๋ฆผ)์ ๋ฝ์ ํ๋ฌธ์ XOR ํ๋ ๋ฐฉ์์ด๋ค. ๊ทธ ๋์์ด์ ๋ผ๋๋ก ๊ฐ์ฅ ์ค๋ ์ฐ์ธ ๊ฒ ์ ํ ๋๋จน์ ์ํํธ ๋ ์ง์คํฐ(LFSR)๋ค.
๋ ์ง์คํฐ๋ฅผ ํ ์นธ์ฉ ๋ฐ๋ฉด์, ๋ฐ๋ ค๋๋ ์๋ฆฌ์๋ ์ ํด์ง ๋ช ๊ฐ ์๋ฆฌ๋ฅผ XOR ํ ๊ฐ์ ์ฑ์ ๋ฃ๋๋ค. ๊ทธ๋ฆผ์ผ๋ก ๋ณด๋ฉด ํ๋์ ๋ค์ด์จ๋ค.

์ถ์ฒ: Wikimedia Commons, KCAuXy4p, CC0
LFSR ๋ง ์ฐ๋ฉด ์ฃผ๊ธฐ๋ ๊ธธ์ง๋ง ์ ํ์ด๋ผ ๊ทธ๋๋ก๋ ์์ ํ์ง ์๋ค. ๊ทธ๋์ SNOW ๊ณ์ด์ LFSR ์์ ๋น์ ํ ์ ํ์ํ๊ธฐ๊ณ(FSM)๋ฅผ ์น๋๋ค.
LFSR ์ 32๋นํธ ์๋ 16๊ฐ(s0~s15)๋ก ๋์ด ์๊ณ , FSM ์ 32๋นํธ ๋ ์ง์คํฐ ์ธ ๊ฐ(R1, R2, R3)๋ฅผ ๊ตด๋ฆฐ๋ค. SNOW 3G ๋ ์ด ๊ตฌ์กฐ๋ก 3GPP ์ UEA2/UIA2 โ LTE ์ ์ํธํยท๋ฌด๊ฒฐ์ฑ ์๊ณ ๋ฆฌ์ฆ โ ์ ๋ค์ด๊ฐ ์๋ ํ์ค์ด๋ค.
์ด ๋ฌธ์ ์ ๋ฐ์ด๋๋ฆฌ๊ฐ ํต์งธ๋ก ๊ทธ ๊ตฌ์กฐ๋ค.

์ฌ๊ธฐ์ ์ฐ๋ฆฌ๊ฐ ์ธ ์ถ๋ ฅ์ ๋งจ ์ผ์ชฝ ์์๋ค. 32๋นํธ ์๋ z ๊ฐ ๋์ค๋๋ฐ, ๋ฌธ์ ๋ ๊ทธ ์ค ์ตํ์ 1๋นํธ๋ง ์ด๋ค. ์ด ๋ญ๋น๊ฐ ๊ณง ํ์ด์ ์ด์ ์๋ค.
๐ฌ ์ ์ฐฐ โ ์ฌ๋ณผ์ด ํ๋๋ ์๋ค
file ๊ฒฐ๊ณผ๋๋ก stripped PIE ๋ค. import ๋ชฉ๋ก์ด ์งง์์ ์คํ๋ ค ์๊ธฐ๊ฐ ๋นจ๋๋ค. strcpy, strlen, printf, puts, memcmp, exit, __stack_chk_fail. ํ์ผ ์
์ถ๋ ฅ๋ ๋คํธ์ํฌ๋ ์๋ค. ์ธ์ ํ๋ ๋ฐ์์ ๊ณ์ฐํ๊ณ ๋น๊ตํ๋ ๊ฒ ์ ๋ถ๋ผ๋ ๋ป์ด๋ค.
๋ฌธ์์ด์ ๋ณด๋ฉด ๋ ๋ถ๋ช ํด์ง๋ค.
strings -a -t x prob | grep -E "Merr|y-Ch|rist|mas|usage:|length of|Correct|Wrong"
usage: %s <string>, length of string must be 32., Correct!, Wrong... ์ ์์๋๋ก๋ค.
๋์ ๊ฑธ๋ฆฌ๋ ๊ฑด ์์ชฝ ๋ค ์ค์ด๋ค. mas!, rist, y-Ch, Merr. ๋ถ์ฌ ์ฝ์ผ๋ฉด Merry-Christmas! ์ด๊ณ , ์กฐ๊ฐ์ด ๋ฑ 4๋ฐ์ดํธ์ฉ ๋๊ฒจ ์๋ค.
๊ทธ๋ฆฌ๊ณ ์คํ์
์ด .rodata(0x2000๋)๊ฐ ์๋๋ผ .text ํ๋ณตํ์ธ 0x1af2 ๋ถํฐ 7๋ฐ์ดํธ ๊ฐ๊ฒฉ์ด๋ค. mov DWORD PTR [rbp-N], imm32 ๋ช
๋ น์ ์ฆ์น๊ฐ ์๋ฆฌ๋ผ๋ ๋ป์ด๋ค. ๋ฌธ์์ด ์์๊ฐ ์๋๋ผ ์ฝ๋์ ๋ฐํ 32๋นํธ ์ ์ ๋ค ๊ฐ๋ค.
32๋นํธ ์๋ ๋ค ๊ฐ = 128๋นํธ. ๊ทธ๋ฆฌ๊ณ ๋ฌธ์ ์ด๋ฆ์ snow. ์ด์ฏค์์ SNOW 3G ๋ฅผ ์์ฌํ๊ณ ๋์ค์ด์ ๋ธ๋ก ๋ค์ด๊ฐ๋ค.
๐งฑ ๊ฐ๋ฃจ์์ฒด ๊ณฑ์ โ ๊ฐ์ฅ ์์ชฝ๋ถํฐ
objdump ๋ก ํ์ผ๋ฉด ํจ์ ๊ฒฝ๊ณ๊ฐ endbr64 + push rbp ๋ก ๊น๋ํ๊ฒ ์กํ๋ค. ๊ฐ์ฅ ์์ชฝ์ ์๋ 0x1209 ๋ถํฐ ๋ดค๋ค.
์ฃผ์์ ์น๊ธฐ ์ํด ์นํ ๊ท์น์ ํ์ผ๋ก ๋ง๋ค์ด ๋๋ค. --cmd ์์ sed 's|...|...|' ๋ฅผ ์ธ๋ผ์ธ์ผ๋ก ๋ฃ์ผ๋ฉด ๋ฐ์ดํ๊ฐ ๊นจ์ง๋ฏ๋ก -f ๋ก ๋ถ๋ฅด๋ ํธ์ด ์์ ํ๋ค.
# annot_mulx.sed
s|(c1 e8 07.*shr +eax,0x7)$|\1 <-- V ์ ์ต์์ ๋นํธ๋ฅผ ๋ฝ๋๋ค|
s|(83 e0 01.*and +eax,0x1)$|\1 <-- MSB ๋ง ๋จ๊ธด๋ค|
s|(83 7d fc 01.*cmp +DWORD PTR \[rbp-0x4\],0x1)$|\1 <-- MSB ๊ฐ 1์ด๋ฉด ๊ฐ์ฐ์ด ํ์|
s|(01 c0.*add +eax,eax)$|\1 <-- V << 1|
s|(33 45 e8.*xor +eax,DWORD PTR \[rbp-0x18\])$|\1 <-- ^ c : ๊ธฐ์ฝ๋คํญ์์ผ๋ก ์ ๋๋ค|objdump -d -M intel --start-address=0x1209 --stop-address=0x1240 prob | sed -E -f annot_mulx.sed
์ฝ์ด ๋ณด๋ฉด ์ด๋ฐ ํจ์๋ค.
uint8_t MULx(uint8_t V, uint8_t c) {
if (V & 0x80) return ((V << 1) & 0xff) ^ c;
else return (V << 1) & 0xff;
}GF(2^8) ์์ x ๋ฅผ ๊ณฑํ๋ ์ฐ์ฐ, ํํ xtime ์ด๋ผ ๋ถ๋ฅด๋ ๊ฒ์ด๋ค. ๋ค๋ง ๊ธฐ์ฝ๋คํญ์ ์์ c ๊ฐ ํ๋์ฝ๋ฉ์ด ์๋๋ผ ์ธ์๋ก ๋ค์ด์จ๋ค. AES ๊ตฌํ์ด๋ผ๋ฉด 0x1b ๊ฐ ์ฝ๋์ ๋ฐํ ์์์ ํ
๋ฐ ๊ทธ๋ ์ง ์๋ค๋ ๊ฒ ์ฒซ ๋ฒ์งธ ๋จ์์๋ค.
๋ฐ๋ก ๋ค 0x1240 ์ ์ด ํจ์๋ฅผ i ๋ฒ ์ฌ๊ท ํธ์ถํ๋ค. i == 0 ์ด๋ฉด ๊ทธ๋๋ก ๋ฐํํ๊ณ , ์๋๋ฉด MULx(MULxPOW(V, i-1, c), c). SNOW 3G ๋ช
์ธ์ MULxPOW(V, i, c) ๊ทธ๋๋ก๋ค.
์ด ๋ ํจ์๊ฐ ์๋ ๋์ค๋ ๋ชจ๋ ๊ฒ์ ๋ถํ์ด ๋๋ค.
๐ฃ ์์ ๋ ๊ฐ โ S1 ๊ณผ S2
0x128b ๊ณผ 0x1530 ์ ๊ตฌ์กฐ๊ฐ ๋๊ฐ๊ณ ์ฐธ์กฐํ๋ ํ ์ด๋ธ๊ณผ ์์๋ง ๋ค๋ฅด๋ค. 32๋นํธ ์๋๋ฅผ ๋ฐ์ดํธ ๋ท์ผ๋ก ์ชผ๊ฐ๊ณ , ๊ฐ ๋ฐ์ดํธ๋ก 256์ํธ๋ฆฌ ํ ์ด๋ธ์ ์กฐํํ ๋ค, ๊ฒฐ๊ณผ ๋ท์ GF ๊ณฑ์ ๊ณผ XOR ๋ก ์์ด ๋ค์ 32๋นํธ๋ก ํฉ์น๋ค.
์ฒซ ๋ฒ์งธ ํ์ด ์ด๋ป๊ฒ ๋ง๋ค์ด์ง๋์ง ๋ณด์.
objdump -d -M intel --start-address=0x12c9 --stop-address=0x135b prob | sed -E -f annot_s1.sed
์ ๋ฆฌํ๋ฉด r0 = 2ยทT[b0] ^ T[b1] ^ T[b2] ^ 3ยทT[b3] ์ด๋ค. ๋๋จธ์ง ์ธ ํ๋ ๊ฐ์ ๊ณ์๋ฅผ ํ ์นธ์ฉ ๋๋ ค ์ด๋ค.
โถ๐ ์ฝ์ง 1 โ ์ด๊ฑธ AES MixColumns ๋ก ์ฝ์๋ค
๊ณ์๊ฐ 2์ 3์ผ๋ก๋ง ์ด๋ฃจ์ด์ง 4x4 ์ํํ๋ ฌ์ ํ ์ด๋ธ์ด AES S-box๋ผ๋ฉด, ๋ฐ์ฌ์ ์ผ๋ก MixColumns๋ฅผ ๋ ์ฌ๋ฆฌ๊ฒ ๋๋ค. ์ค์ ๋ก ๋๋ ์ฒ์์ "AES ๋ผ์ด๋ ํ๋๋ฅผ ์์ผ๋ก ํธ ๊ฒ"์ผ๋ก ์ฝ์๋ค.
๊ทธ๋ฐ๋ฐ AES MixColumns ์ ์ฒซ ํ์ (2, 3, 1, 1) ์ด๋ค. ์ฌ๊ธฐ ๋์จ ๊ฑด (2, 1, 1, 3) ์ด๋ค. ๋์ ์๋ก ์ ์น ๊ด๊ณ๋ผ์ ๊ฒ๋ณด๊ธฐ ์ธ์์ด ๊ฑฐ์ ๊ฐ๊ณ , ๊ทธ๋์ ๋ ํท๊ฐ๋ฆฐ๋ค.
๊ณ์๋ง AES ์ชฝ์ผ๋ก ๋ฐ๊ฟ ๋๊ณ ๋๊ฐ์ ํ์์ ๋๋ ค ๋ดค๋ค.
# sapjil_mixcolumns.py โ ๊ณ์๋ฅผ AES MixColumns ๋ก ๋ฐ๊ฟจ์ ๋
class MixColumnsBox(Snow3G):
def _box(self, w, T, c):
w0, w1, w2, w3 = (w >> 24) & 0xFF, (w >> 16) & 0xFF, (w >> 8) & 0xFF, w & 0xFF
t0, t1, t2, t3 = T[w0], T[w1], T[w2], T[w3]
m = lambda v: self.MULx(v, c)
# AES MixColumns: ๊ฐ ํ์ด (2,3,1,1) ์ ํ ์นธ์ฉ ๋๋ ค ์ด๋ค
r0 = m(t0) ^ (m(t1) ^ t1) ^ t2 ^ t3
r1 = t0 ^ m(t1) ^ (m(t2) ^ t2) ^ t3
r2 = t0 ^ t1 ^ m(t2) ^ (m(t3) ^ t3)
r3 = (m(t0) ^ t0) ^ t1 ^ t2 ^ m(t3)
return ((r0 << 24) | (r1 << 16) | (r2 << 8) | r3) & snow3g.M32python3 sapjil_mixcolumns.py
ํ๋ณด 0๊ฐ. ์ด ๋ฌธ์ ๋ ๋ต์ด ๋ง์๋์ง ํ๋ ธ๋์ง๋ฅผ ์๋ ค ์ฃผ๋ ์ ํธ๊ฐ ๋ง์ง๋ง memcmp ํ๋๋ฟ์ด๋ผ, ์ค๊ฐ์ ํ ๊ธ์๋ผ๋ ์ด๊ธ๋๋ฉด ์ด๋ ๊ฒ ์ ๋ฉธํ๋ค. ๋ถ๋ถ์ ์๊ฐ ์๋ค๋ ๊ฒ ์คํ๋ ค ๋คํ์ด์๋ค โ ์ ๋งคํ๊ฒ ๋ฐ์ฏค ๋ง์ ํค๋งค๋ ์ผ์ ์๋ค.
๋ ์์๊ฐ ์ฐธ์กฐํ๋ ํ
์ด๋ธ์ .data ์ ๊ทธ๋๋ก ์๋ค. .data ๋ vaddr 0x4000 ์ด ํ์ผ ์คํ์
0x3000 ์ด๋ผ ๊ณ์ฐ์ด ๊ฐ๋จํ๋ค.
# dump_tables.py โ .data ์์ SR/SQ ํ
์ด๋ธ๊ณผ memcmp ์ ๋ต 32๋ฐ์ดํธ๋ฅผ ๊บผ๋ธ๋ค
import struct
BIN = "prob"
DELTA = 0x1000 # vaddr -> file offset
data = open(BIN, "rb").read()
def dwords(vaddr, n):
off = vaddr - DELTA
return list(struct.unpack_from("<%dI" % n, data, off))
SR = dwords(0x4020, 256) # S1 ์ด ์ฐธ์กฐํ๋ ํ
์ด๋ธ
SQ = dwords(0x4420, 256) # S2 ๊ฐ ์ฐธ์กฐํ๋ ํ
์ด๋ธ
TARGET = data[0x4820 - DELTA: 0x4820 - DELTA + 32]
print("SR[0:16] =", " ".join("%02x" % v for v in SR[:16]))
print("SQ[0:16] =", " ".join("%02x" % v for v in SQ[:16]))
print("SR ์ต๋๊ฐ =", hex(max(SR)), "/ SQ ์ต๋๊ฐ =", hex(max(SQ)))
print("SR ์ ์์ด์ธ๊ฐ:", sorted(SR) == list(range(256)))
print("SQ ์ ์์ด์ธ๊ฐ:", sorted(SQ) == list(range(256)))
# AES S-box ์ฒซ ์ค๊ณผ ๋์กฐ (63 7c 77 7b f2 6b 6f c5 30 01 67 2b fe d7 ab 76)
aes = [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76]
print("SR == AES S-box ์ฒซ 16๊ฐ:", SR[:16] == aes)
print()
print("TARGET(32B) =", TARGET.hex())
print("TARGET ascii =", "".join(chr(c) if 32 <= c < 127 else "." for c in TARGET))python3 dump_tables.py
0x4020 ์ AES S-box ๋ค. 0x4420 ์ 25 24 73 67 d7 ae 5c 30 ... ์ผ๋ก ์์ํ๋ ๋ ๋ค๋ฅธ 256 ์์ด์ธ๋ฐ, ์ด๊ฒ SNOW 3G ๋ช
์ธ์ SQ ๋ฐ์ค๋ค. ๋ ์์๊ฐ ๊ฐ๊ฐ c = 0x1b ์ c = 0x69 ๋ฅผ ์ฐ๋ ๊ฒ๊น์ง ๋ช
์ธ์ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ 0x4820 ์ 32๋ฐ์ดํธ๊ฐ memcmp ์๋, ์ฆ ์ฐ๋ฆฌ๊ฐ ๋๋ฌํด์ผ ํ ๋ชฉํ๋ค. ASCII ๋ก๋ ์๋ฌด ์๋ฏธ๊ฐ ์๋ค.
๐งญ alpha ๊ณฑ์ โ ์ง๋ฌธ์ ์ฌ๊ธฐ ์์๋ค
์ฌ๊ธฐ๊น์ง๋ "AES ๋ถํ์ ์ฐ๋ ๋ญ๊ฐ"์ผ ๋ฟ์ด๋ค. ๊ฒฐ์ ํ๋ 0x1845 ์๋ค.
objdump -d -M intel --start-address=0x1855 --stop-address=0x18bc prob | sed -E -f annot_alpha.sed
MULxPOW(c, 23, 0xa9), MULxPOW(c, 245, 0xa9), MULxPOW(c, 48, 0xa9), MULxPOW(c, 239, 0xa9).
์ด ๋ค ์ซ์ ์กฐํฉ์ ๋ค๋ฅธ ๋ฐ์ ๋์ฌ ์๊ฐ ์๋ค. SNOW 3G ๋ช
์ธ์ MULalpha ๊ทธ๋๋ก๋ค. ๋ฐ๋ก ์๋ 0x18bc ๋ 16, 39, 6, 64 ๋ฅผ ์ฐ๋๋ฐ ๊ทธ๊ฑด DIValpha ๋ค.
์ฌ๊ธฐ์๋ถํฐ๋ ๋ช ์ธ๋ฅผ ์์ ๋๊ณ ๋์กฐํ๋ ์์ ์ด ๋๋ค. 0x17d3 ์ FSM ํด๋ญ์ด๋ค.
F = (s15 + R1) ^ R2; // 32๋นํธ ๋ง์
์ mod 2^32
r = R2 + (R3 ^ s5);
R3 = S2(R2);
R2 = S1(R1);
R1 = r;
return F;0x1933 ์ LFSR ํด๋ญ์ธ๋ฐ, ์ฒซ ์ธ์๊ฐ 0x69(๋ฌธ์ 'i') ๋ 0x6b(๋ฌธ์ 'k') ๋๋ก ์ด๊ธฐํ ๋ชจ๋์ ํค์คํธ๋ฆผ ๋ชจ๋๋ฅผ ๊ฐ๋ฅธ๋ค.
v = (s0 << 8) ^ MULalpha(s0 >> 24) ^ s2 ^ (s11 >> 8) ^ DIValpha(s11 & 0xff);
if (mode == 'i') v ^= F;
for (i = 0; i < 15; i++) s[i] = s[i+1];
s15 = v;๋ช ์ธ์ ํ ๊ธ์๋ ๋ค๋ฅด์ง ์๋ค. ๋ฌธ์ ์ด๋ฆ snow ๋ ๋๋ด์ด ์๋๋ผ ๊ทธ๋ฅ ์ ๋ต ํ๊ธฐ์๋ค.
๐ ํค์ IV โ ์ฌ๊ธฐ๊ฐ ๊ธ์๋ค
์ด์ ์์์ ๋ณธ Merry-Christmas! ์กฐ๊ฐ์ด ์ด๋๋ก ๋ค์ด๊ฐ๋์ง ๋ณผ ์ฐจ๋ก๋ค.
objdump -d -M intel --start-address=0x1aef --stop-address=0x1b6c prob | sed -E -f annot_init.sed
ํค๋ k[0]=0x2173616d, k[1]=0x74736972, k[2]=0x68432d79, k[3]=0x7272654d ๋ก ๊ณ ์ ์ด๋ค. ๋ฆฌํ์๋์ธ ๋ฐ์ดํธ๋ก ํด๋ฉด mas! rist y-Ch Merr ์ด๊ณ , ์๋๋ฅผ ๋ค์์๋ถํฐ ์ฝ์ผ๋ฉด Merry-Christmas! ๊ฐ ๋๋ค.
IV ๋ ๋ค๋ฅด๋ค. rbp-0x38 ์ ๋ค์ด์จ ํฌ์ธํฐ โ ์ฆ ์ฌ์ฉ์ ์
๋ ฅ ๋ฒํผ โ ์์ 0, 4, 8, 12 ์คํ์
์ 32๋นํธ๋ฅผ ๊ทธ๋๋ก ์ฝ๋๋ค.
์ ๋ฆฌํ๋ฉด LFSR ์ด๊ธฐ ์ํ๋ ์ด๋ ๊ฒ ์ฑ์์ง๋ค. F ๋ 0xffffffff ๋ค.
| ์ฌ๋กฏ | ๊ฐ | ์ฌ๋กฏ | ๊ฐ |
|---|---|---|---|
| s15 | k[3] ^ IV[0] | s7 | k[3] |
| s14 | k[2] | s6 | k[2] |
| s13 | k[1] | s5 | k[1] |
| s12 | k[0] ^ IV[1] | s4 | k[0] |
| s11 | k[3] ^ F | s3 | k[3] ^ F |
| s10 | k[2] ^ F ^ IV[2] | s2 | k[2] ^ F |
| s9 | k[1] ^ F ^ IV[3] | s1 | k[1] ^ F |
| s8 | k[0] ^ F | s0 | k[0] ^ F |
๊ทธ๋ค์ 32๋ผ์ด๋๋ฅผ ์ด๊ธฐํ ๋ชจ๋๋ก ๋๋ฆฌ๊ณ , ๋ง์ง๋ง์ ClockFSM() ์ ํ ๋ฒ ๋ ๋ถ๋ฅด๊ณ ๋ฐํ๊ฐ์ ๋ฒ๋ฆฐ๋ค. 0x1c22 ์ ๊ทธ ํ ์ค์ด ์๊ณ ์๊ณ ๋ก ์ดํ ๋ชจ๋ ํค์คํธ๋ฆผ์ด ๋ฌ๋ผ์ง๋ค.
โถ๐ ์ฝ์ง 2 โ ๋ช ์ธ ๊ฐ๊ฐ๋๋ก ๋๋ฉด ์ ๋๋ ๋ ์๋ฆฌ
ํ์ค ๋ฌธ์๋ฅผ ์์ ๋๊ณ ์ฎ๊ธฐ๋ค ๋ณด๋ฉด ์์ด ๋จผ์ ๋๊ฐ๋ ์๋ฆฌ๊ฐ ์๋ค. ๋ ๊ฐ๋ฅผ ์ค์ ๋ก ํ๋ฆฌ๊ฒ ๋ง๋ค์ด ๋๋ ค ๋ดค๋ค.
ํ๋๋ ์์์ ๋งํ ์ด๊ธฐํ ๊ผฌ๋ฆฌ์ ClockFSM() ์ด๋ค. 32๋ผ์ด๋๋ก ๋๋ฌ๋ค๊ณ ์๊ฐํ๊ธฐ ์ฝ๋ค.
๋ค๋ฅธ ํ๋๋ IV ์๋์ ๋ฐ์ดํธ ์์๋ค. ๋ช
์ธ์์ IV ๋ IV[0] ์ด ์ต์์์ธ ๊ฒ์ฒ๋ผ ํ๊ธฐ๋๋๋ฐ, ์ด ๋ฐ์ด๋๋ฆฌ๋ mov eax, DWORD PTR [rax] ๋ก ๊ทธ๋ฅ ์ฝ๋๋ค. x86 ์ด๋ ๋ฆฌํ์๋์ธ์ด๋ค. ๋น
์๋์ธ์ผ๋ก ์กฐ๋ฆฝํ๋ฉด ์กฐ์ฉํ ๋ค๋ฅธ ๊ฐ์ด ๋๋ค.
# sapjil_variants.py โ ์ด๊ธฐํ ๊ผฌ๋ฆฌ๋ฅผ ๋นผ๊ฑฐ๋ IV ๋ฅผ ๋น
์๋์ธ์ผ๋ก ์ฝ์ผ๋ฉด
def main():
_, _, TARGET = load_tables("prob")
for name, cls, be in (("binary ", Snow3G, False),
("no_tail", NoTailFSM, False),
("be_iv ", Snow3G, True)):
surv, printable = run(cls, TARGET, be)
head = printable[0].decode() if printable else "-"
print("%s ์๊ธฐ๋ฌด๋ชจ์ %d๊ฐ / printable %d๊ฐ ์ 16๋ฐ์ดํธ: %s" %
(name, len(surv), len(printable), head))python3 sapjil_variants.py
๋ ๋ค printable ํ๋ณด 0๊ฐ๋ค.
์ฌ๋ฏธ์๋ ๊ฑด ์๊ธฐ๋ฌด๋ชจ์ ํ๋ณด๊ฐ 3๊ฐ, 2๊ฐ์ฉ์ ์ฌ์ ํ ๋์จ๋ค๋ ์ ์ด๋ค. "๋ช ๊ฐ ์ด์๋จ์๋ค"๋ ๊ฒ ์์ฒด๋ ์๋ฌด ๋ณด์ฆ์ด ์ ๋๋ค. ์ด์๋จ์ ๊ฐ์ด ์ฝ์ ์ ์๋ ๊ธ์์ธ๊ฐ๊น์ง ๋ด์ผ ๋น๋ก์ ์ ํธ๊ฐ ๋๋ค.
๋ง๋ถ์ด๋ฉด, ํธ์ถ ์์๋ ํ๋ ค๋ ํฐ๊ฐ ์ ๋๋ค. ๋ช ์ธ๋ ์๋๋ฅผ ๋จผ์ ๋ฝ๊ณ LFSR ์ ๋ฏธ๋๋ฐ ์ด ๋ฐ์ด๋๋ฆฌ๋ ๋ฐ๋๋ค. ๊ทธ๋ฐ๋ฐ ๋ช ์ธ๊ฐ ์ด๊ธฐํ ์งํ ํด๋ญ์ ํ ๋ฒ ๋ ๋ฃ์ผ๋ผ๊ณ ํ๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ๊ฐ์ ์์ด์ด ๋์จ๋ค. ์ด๊ฑด ํ์ธํด ๋ณด๊ณ ๋์์ผ ์์๋ค.
๐ฏ main ์ด ํ๋ ์ผ
์ด์ ๋ง์ง๋ง ์กฐ๊ฐ์ด๋ค.
objdump -d -M intel --start-address=0x1d22 --stop-address=0x1db8 prob | sed -E -f annot_main.sed
C ๋ก ์ฎ๊ธฐ๋ฉด ์ด๊ฒ ์ ๋ถ๋ค.
if (argc != 2) { printf("usage: %s <string>", argv[0]); exit(-1); }
if (strlen(argv[1]) != 32) { puts("length of string must be 32."); exit(-1); }
char buf[32] = {0};
strcpy(buf, argv[1]);
Initialize(buf); // IV = buf[0..15]
for (int i = 0; i < 16; i++) {
uint16_t *w = (uint16_t *)(buf + 2*i);
*w ^= 0x3f2c;
if (GenerateKeystream() & 1) // 32๋นํธ ์๋์์ ์ตํ์ 1๋นํธ๋ง
*w = ~*w;
}
puts(memcmp(target, buf, 32) == 0 ? "Correct!" : "Wrong...");32๋นํธ ํค์คํธ๋ฆผ ์๋๋ฅผ ํต์งธ๋ก ๋ฝ์ ๋๊ณ 1๋นํธ๋ง ์ด๋ค. ๋๋จธ์ง 31๋นํธ๋ ๋ฒ๋ฆฐ๋ค.
์ํธํ์ ์ผ๋ก๋ ์๊น์ง๋ง, ํธ๋ ์ชฝ์์๋ ์ด๊ฒ ์ ๋ถ๋ค. ์๋ ํ๋๋ฅผ ๋๋๋ฆฌ๋ ๋ฐ ํ์ํ ์ ๋ณด๊ฐ ํ ๋นํธ๋ฟ์ด๋ผ๋ ๋ป์ด๋๊น.
๐ก ํต์ฌ โ ์๊ธฐ์ฐธ์กฐ๋ฅผ 8๋นํธ๋ก ์ ๋๋ค
์ฒ์ ์ด ๊ตฌ์กฐ๋ฅผ ๋ณด๊ณ ๋ ์๊ฐ์ "๋ญ์ด ๋จผ์ ๋"์๋ค. ํค์คํธ๋ฆผ์ ์์์ผ ์ ๋ ฅ์ ๋๋๋ฆฌ๋๋ฐ, ํค์คํธ๋ฆผ์ ๊ทธ ์ ๋ ฅ์ ์ 16๋ฐ์ดํธ๋ก ๋ง๋ค์ด์ง๋ค.
๊ทธ๋ฐ๋ฐ ์กฐ๊ธ๋ง ๋ค์ฌ๋ค๋ณด๋ฉด ๊ณ ๋ฆฌ๊ฐ ์๊ฐ๋ณด๋ค ์๋ค.
๋ชฉํ ์๋๋ฅผ t_i, ํค์คํธ๋ฆผ ์๋๋ฅผ z_i ๋ผ ํ์. ๋ณํ์ XOR ํ ๋ฒ๊ณผ ์กฐ๊ฑด๋ถ NOT ํ ๋ฒ๋ฟ์ด๊ณ ๋ ๋ค ์๊ธฐ์ญ์์ด๋ค. ๊ทธ๋์ ์ญ์ฐ์ ์ด๋ ๊ฒ ๋๋๋ค.
z_i ์ ์ตํ์ ๋นํธ๊ฐ 0 ์ด๋ฉด ์๋ ์๋ = t_i ^ 0x3f2c
z_i ์ ์ตํ์ ๋นํธ๊ฐ 1 ์ด๋ฉด ์๋ ์๋ = (~t_i) ^ 0x3f2c์๋ ํ๋๋น ๋ฏธ์ง์๋ ๋ฑ 1๋นํธ๋ค.
๊ทธ๋ฆฌ๊ณ IV ๋ฅผ ์ด๋ฃจ๋ ๊ฒ์ ์ 16๋ฐ์ดํธ, ์ฆ ์๋ 07 ์ด๋ค. ์ด ์ฌ๋ ๊ฐ๋ง ์ ํด์ง๋ฉด ํค์คํธ๋ฆผ ์ ์ฒด๊ฐ ๊ฒฐ์ ๋๋ค. ๋ค์ชฝ ์๋ 815 ๋ IV ์ ์๋ฌด ์ํฅ์ ์ฃผ์ง ์๋๋ค.
๊ทธ๋ฌ๋ ์ ์ฐจ๋ ์ด๋ ๊ฒ ๋๋ค.
b0..b7์ฌ๋ ๋นํธ๋ฅผ ๊ฐ์ ํ๋ค โ ๊ฒฝ์ฐ์ ์ 256.- ๊ทธ ๊ฐ์ ์ผ๋ก ์ 16๋ฐ์ดํธ๋ฅผ ์ญ์ฐํด IV ๋ฅผ ๋ง๋ ๋ค.
- ๊ทธ IV ๋ก SNOW 3G ๋ฅผ ์ค์ ๋ก ๋๋ ค
z_0..z_7์ ๋ฝ๋๋ค. - ๋ฝํ ์ตํ์ ๋นํธ๋ค์ด ๊ฐ์ ๊ณผ ๊ฐ์์ง ๋ณธ๋ค. ๋ค๋ฅด๋ฉด ๊ทธ ๊ฐ์ ์ ์๊ธฐ๋ชจ์์ด๋ ๋ฒ๋ฆฐ๋ค.
- ์ด์๋จ์ ๊ฐ์ ์์
z_8..z_15๋ก ๋ค 16๋ฐ์ดํธ๋ฅผ ๋ง์ ์ญ์ฐํ๋ค.
2^128 ์ฒ๋ผ ๋ณด์ด๋ ๋ฌธ์ ๊ฐ 2^8 ์ด ๋๋ค. ๋ ธํธ๋ถ์์ 1์ด๋ ์ ๊ฑธ๋ฆฐ๋ค.
๐ Full Exploit
SNOW 3G ๋ถ๋ถ์ ํ์ผ์ ๋๋ ๋๋ค. ๋ช ์ธ๋ฅผ ์ฎ๊ธด ๊ฒ ์๋๋ผ ๋์ค์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฎ๊ธด ๊ฒ์ด๋ผ, ์์์ ํ์ธํ ์ฃผ์๋ฅผ ์ฃผ์์ ๋จ๊ฒผ๋ค.
# snow3g.py
"""prob ์์ ๋ฐํ ์๋ SNOW 3G ๋ฅผ ํ์ด์ฌ์ผ๋ก ๊ทธ๋๋ก ์ฎ๊ธด ๊ฒ.
๋ฐ์ด๋๋ฆฌ์์ ํ์ธํ ๋์ ๊ด๊ณ:
0x1209 MULx(V, c) 0x1240 MULxPOW(V, i, c)
0x128b S1(w) <- SR @0x4020, c=0x1b
0x1530 S2(w) <- SQ @0x4420, c=0x69
0x1845 MULalpha(c) 0x18bc DIValpha(c)
0x17d3 ClockFSM() 0x1933 ClockLFSR(mode, F) mode 0x69='i' / 0x6b='k'
0x1adf Initialize(buf) 0x1c2f GenerateKeystream()
LFSR s0..s15 ๋ .bss 0x4860, R1/R2/R3 ๋ 0x48a0/0x48a4/0x48a8 (์ ๋ถ 0์ผ๋ก ์์).
"""
import struct
DELTA = 0x1000 # .data: vaddr 0x4000 == file offset 0x3000
M32 = 0xFFFFFFFF
# ๋ฐ์ด๋๋ฆฌ ์ ๊ณ ์ ํค. ์๋ ๋จ์๋ก ๋ค์งํ ์์ด k[3]..k[0] ์ ์ด์ด ๋ถ์ด๋ฉด "Merry-Christmas!".
KEY = [0x2173616D, 0x74736972, 0x68432D79, 0x7272654D]
def load_tables(path="prob"):
data = open(path, "rb").read()
sr = list(struct.unpack_from("<256I", data, 0x4020 - DELTA))
sq = list(struct.unpack_from("<256I", data, 0x4420 - DELTA))
target = data[0x4820 - DELTA: 0x4820 - DELTA + 32]
return sr, sq, target
class Snow3G:
def __init__(self, sr, sq):
self.SR, self.SQ = sr, sq
# --- GF(2^8) ํฌํผ -------------------------------------------------
@staticmethod
def MULx(V, c):
return ((V << 1) & 0xFF) ^ c if V & 0x80 else (V << 1) & 0xFF
def MULxPOW(self, V, i, c):
for _ in range(i):
V = self.MULx(V, c)
return V & 0xFF
def _box(self, w, T, c):
"""S1/S2 ๊ณตํต ๊ณจ๊ฒฉ โ ํ
์ด๋ธ๊ณผ ๊ธฐ์ฝ๋คํญ์ ์์๋ง ๋ค๋ฅด๋ค."""
w0, w1, w2, w3 = (w >> 24) & 0xFF, (w >> 16) & 0xFF, (w >> 8) & 0xFF, w & 0xFF
t0, t1, t2, t3 = T[w0], T[w1], T[w2], T[w3]
m = lambda v: self.MULx(v, c)
r0 = m(t0) ^ t1 ^ t2 ^ m(t3) ^ t3
r1 = m(t0) ^ t0 ^ m(t1) ^ t2 ^ t3
r2 = t0 ^ m(t1) ^ t1 ^ m(t2) ^ t3
r3 = t0 ^ t1 ^ m(t2) ^ t2 ^ m(t3)
return ((r0 << 24) | (r1 << 16) | (r2 << 8) | r3) & M32
def S1(self, w):
return self._box(w, self.SR, 0x1B)
def S2(self, w):
return self._box(w, self.SQ, 0x69)
def MULalpha(self, c):
return ((self.MULxPOW(c, 23, 0xA9) << 24) | (self.MULxPOW(c, 245, 0xA9) << 16) |
(self.MULxPOW(c, 48, 0xA9) << 8) | self.MULxPOW(c, 239, 0xA9))
def DIValpha(self, c):
return ((self.MULxPOW(c, 16, 0xA9) << 24) | (self.MULxPOW(c, 39, 0xA9) << 16) |
(self.MULxPOW(c, 6, 0xA9) << 8) | self.MULxPOW(c, 64, 0xA9))
# --- ์ํ ๊ฐฑ์ ----------------------------------------------------
def ClockFSM(self):
s = self.s
F = ((s[15] + self.R1) & M32) ^ self.R2
r = (self.R2 + (self.R3 ^ s[5])) & M32
self.R3 = self.S2(self.R2)
self.R2 = self.S1(self.R1)
self.R1 = r
return F
def ClockLFSR(self, mode, F=0):
s = self.s
v = (((s[0] << 8) & M32) ^ self.MULalpha(s[0] >> 24) ^ s[2] ^
(s[11] >> 8) ^ self.DIValpha(s[11] & 0xFF))
if mode == "i":
v ^= F
self.s = s[1:] + [v & M32]
# --- ํค ์ค์ผ์ค / ํค์คํธ๋ฆผ ----------------------------------------
def initialize(self, iv):
k, F = KEY, 0xFFFFFFFF
self.s = [
k[0] ^ F, k[1] ^ F, k[2] ^ F, k[3] ^ F, # s0..s3
k[0], k[1], k[2], k[3], # s4..s7
k[0] ^ F, k[1] ^ F ^ iv[3], k[2] ^ F ^ iv[2], k[3] ^ F, # s8..s11
k[0] ^ iv[1], k[1], k[2], k[3] ^ iv[0], # s12..s15
]
self.R1 = self.R2 = self.R3 = 0
for _ in range(32):
self.ClockLFSR("i", self.ClockFSM())
self.ClockFSM() # ์ด๊ธฐํ ๋ง์ง๋ง ํ ๋ฒ โ ๋ฐํ๊ฐ์ ๋ฒ๋ฆฐ๋ค
def keystream_word(self):
self.ClockLFSR("k")
return (self.ClockFSM() ^ self.s[0]) & M32ํ์ด ๋ณธ์ฒด๋ ์์์ ์ ๋ฆฌํ ๋ค์ฏ ๋จ๊ณ๋ฅผ ๊ทธ๋๋ก ์ฎ๊ธด ๊ฒ์ด๋ค.
# solve.py
"""snow (Dreamhack, Silver 1 / reversing) ํ์ด.
main ์ด ํ๋ ์ผ:
strlen(argv[1]) == 32 ๊ฐ์ โ 32๋ฐ์ดํธ๋ฅผ ์คํ ๋ฒํผ๋ก strcpy
Initialize(buf) โ IV = buf[0:16] (u32 4๊ฐ, ๋ฆฌํ์๋์ธ)
for i in 0..15:
w = *(u16*)(buf+2i); w ^= 0x3f2c; *(u16*)(buf+2i) = w
z = GenerateKeystream()
if z & 1: *(u16*)(buf+2i) = ~w
memcmp(TARGET, buf, 32) == 0 โ "Correct!"
ํต์ฌ: ํค์คํธ๋ฆผ์ IV, ์ฆ **์
๋ ฅ์ ์ 16๋ฐ์ดํธ์๋ง** ์์กดํ๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ ์ 16๋ฐ์ดํธ๋
์๋ 0~7 ์ด๊ณ , ๊ฐ ์๋์ ์ญ์ฐ์ z_i ์ LSB ํ ๋นํธ๋ง ์๋ฉด ๋๋๋ค.
๋ฐ๋ผ์ b0..b7 8๋นํธ(=256๊ฐ์ง)๋ง ๊ฐ์ ํ๋ฉด ์ 16๋ฐ์ดํธ๊ฐ ์ ํด์ง๊ณ , ๊ทธ๊ฑธ๋ก ์ค์
ํค์คํธ๋ฆผ์ ๋๋ ค ๊ฐ์ ์ด ์๊ธฐ๋ชจ์์ด ์๋์ง ํ์ธํ๋ฉด ๋๋ค. ์ด์๋จ์ ํ๋ณด์์
z_8..z_15 ๋ก ๋ท 16๋ฐ์ดํธ๋ฅผ ๋ง์ ์ญ์ฐํ๋ค.
"""
import struct
import sys
from snow3g import Snow3G, load_tables
XOR = 0x3F2C
def words_of(buf):
return list(struct.unpack("<16H", buf))
def invert(target_w, bit):
"""์ ๋ต ์๋์ z ์ LSB ๋ก ์๋ ์
๋ ฅ ์๋๋ฅผ ๋๋๋ฆฐ๋ค."""
w = (~target_w) & 0xFFFF if bit else target_w
return w ^ XOR
def main():
SR, SQ, TARGET = load_tables(sys.argv[1] if len(sys.argv) > 1 else "prob")
tw = words_of(TARGET)
print("TARGET :", TARGET.hex())
hits = []
for guess in range(256):
bits = [(guess >> i) & 1 for i in range(8)]
head = b"".join(struct.pack("<H", invert(tw[i], bits[i])) for i in range(8))
iv = list(struct.unpack("<4I", head))
c = Snow3G(SR, SQ)
c.initialize(iv)
z = [c.keystream_word() for _ in range(16)]
if [zi & 1 for zi in z[:8]] != bits: # ๊ฐ์ ์ด ์๊ธฐ๋ชจ์์ด๋ฉด ๋ฒ๋ฆฐ๋ค
continue
tail = b"".join(struct.pack("<H", invert(tw[8 + i], z[8 + i] & 1)) for i in range(8))
hits.append((guess, head + tail, z))
print("๊ฐ์ 256๊ฐ ์ค ์๊ธฐ๋ชจ์ ์์ด ์ด์๋จ์ ๊ฒ:", len(hits))
answer = None
for guess, cand, z in hits:
printable = all(32 <= b < 127 for b in cand)
print(" b0..b7=0x%02x printable=%-5s %s" % (
guess, printable, cand.decode() if printable else cand.hex()))
if not printable:
continue
# ์ ๋ฐฉํฅ์ผ๋ก ๋ค์ ๋๋ ค memcmp ๊ฐ ์ค์ ๋ก ํต๊ณผํ๋์ง ํ์ธ
buf = bytearray(cand)
c = Snow3G(SR, SQ)
c.initialize(list(struct.unpack("<4I", cand[:16])))
for i in range(16):
w = struct.unpack_from("<H", buf, 2 * i)[0] ^ XOR
if c.keystream_word() & 1:
w = (~w) & 0xFFFF
struct.pack_into("<H", buf, 2 * i, w)
ok = bytes(buf) == TARGET
print(" ์ ๋ฐฉํฅ ์ฌํ =", bytes(buf).hex(), "โ ์ผ์น" if ok else "โ ๋ถ์ผ์น")
if ok:
answer = cand.decode()
if answer:
print()
print("ANSWER:", answer)
if __name__ == "__main__":
main()python3 solve.py
์ด์๋จ์ ํ๋ณด๊ฐ 4๊ฐ๋ค. ๋๋จธ์ง ์ ์ ๋ฐ์ดํธ๊ฐ ๊นจ์ ธ ์์ด 32๊ธ์ ๋ฌธ์์ด๋ก ์ฑ๋ฆฝํ์ง ์๋๋ค.
์ฝํ๋ ํ๋๋ DH{F805042AC5E59FD07ABEF1E9B2EF}. ์ ๋ฐฉํฅ์ผ๋ก ๋ค์ ๋๋ ค ๋ด๋ .data ์ 32๋ฐ์ดํธ์ ์ ํํ ๊ฐ๋ค.
๐งช ๊ฒ์ฆ โ ๋ชจ๋ธ์ด ์ ๋ง ๊ทธ ๋ฐ์ด๋๋ฆฌ์ธ๊ฐ
ํ์ด์ฌ์ผ๋ก ์ฎ๊ธด ๊ฒ ์ง์ง ์ด ๋ฐ์ด๋๋ฆฌ์ ๊ฐ์์ง๋ ๋ฐ๋ก ํ์ธํด์ผ ํ๋ค. ์ ๋ต ํ๋๊ฐ ๋ง์๋ค๋ ๊ฒ๋ง์ผ๋ก๋ ์ฐ์ฐ์ผ ์ฌ์ง๊ฐ ๋จ๋๋ค.
PIE ๋ผ ์ฃผ์๋ฅผ ๊ด๋ก๋ก ๊ฐ์ ํ๋ฉด ์ ๋๋ค. gdb ๋ฐฐ์น ์คํฌ๋ฆฝํธ ์์์ info proc mappings ๋ก ๋ก๋ ๋ฒ ์ด์ค๋ฅผ ์ง์ ์ฝ์๋ค.
# verify.gdb
set confirm off
set pagination off
set args DH{F805042AC5E59FD07ABEF1E9B2EF}
starti
python
import gdb
base = None
for ln in gdb.execute("info proc mappings", to_string=True).splitlines():
f = ln.split()
if len(f) >= 5 and f[-1].endswith("/prob"):
base = int(f[0], 16)
break
gdb.execute("set $b = 0x%x" % base)
print("PIE base = 0x%x (๊ด๋ก ๊ฐ์ ๊ธ์ง โ info proc mappings ๋ก ์ฝ์๋ค)" % base)
end
break *($b + 0x1d22)
break *($b + 0x1d99)
continue
printf "\n[Initialize ์งํ] LFSR s0..s15\n"
x/16xw $b+0x4860
printf "[Initialize ์งํ] R1 R2 R3\n"
x/3xw $b+0x48a0
continue
printf "\n[memcmp ์ง์ ] ๋ณํ๋ ์
๋ ฅ ๋ฒํผ 32๋ฐ์ดํธ\n"
x/32xb $rbp-0x30
printf "[memcmp ์ง์ ] .data 0x4820 ์ ์ ๋ต 32๋ฐ์ดํธ\n"
x/32xb $b+0x4820
continuegdb -q -batch -x verify.gdb ./prob
๋ ์ง์ ์ ๋ดค๋ค. Initialize ๊ฐ ๋๋ ์งํ์ LFSR 16์๋์ FSM ์ธ ๋ ์ง์คํฐ, ๊ทธ๋ฆฌ๊ณ memcmp ์ง์ ์คํ ๋ฒํผ์ .data ์ ๋ชฉํ๊ฐ.
๋ฒํผ 32๋ฐ์ดํธ๊ฐ ๋ชฉํ์ ๋ฐ์ดํธ ๋จ์๋ก ๊ฐ๊ณ , ๋ง์ง๋ง์ Correct! ๊ฐ ์ฐํ๋ค.
์ด์ ํ์ด์ฌ ๋ชจ๋ธ์ด ๊ฐ์ ์ค๊ฐ ์ํ๋ฅผ ๋ง๋๋์ง ๋์กฐํ๋ค.
# check_state.py โ ์ค์ธก์น๋ฅผ ๋ฐ์ ๋๊ณ ํ์ด์ฌ ๋ชจ๋ธ๊ณผ ๋น๊ตํ๋ค
GDB_LFSR = [0xFF22D888, 0xA8777BA5, 0x6992F244, 0xCAF46272,
0xC38C6DE9, 0xA1D77FA2, 0x25B7F11C, 0xC3A147A3,
0xA402D8F2, 0xADFBD6E5, 0xAE331D05, 0x69DC8CA7,
0x747420EA, 0xF3530624, 0x12AD67EE, 0xC8007FAB]
GDB_FSM = [0xC0B7C911, 0xF64EB4D7, 0xFF628DC0]
SR, SQ, TARGET = load_tables("prob")
c = Snow3G(SR, SQ)
c.initialize(list(struct.unpack("<4I", FLAG.encode()[:16])))
print("ํ์ด์ฌ ๋ชจ๋ธ s0..s15")
for i in range(0, 16, 4):
print(" s%-2d " % i, " ".join("0x%08x" % v for v in c.s[i:i + 4]))
print("ํ์ด์ฌ ๋ชจ๋ธ R1 R2 R3")
print(" ", " ".join("0x%08x" % v for v in (c.R1, c.R2, c.R3)))
print()
print("gdb ์ค์ธก LFSR ๊ณผ ์ผ์น :", c.s == GDB_LFSR)
print("gdb ์ค์ธก FSM ๊ณผ ์ผ์น :", [c.R1, c.R2, c.R3] == GDB_FSM)python3 check_state.py
32๋ผ์ด๋ ์ด๊ธฐํ๋ฅผ ๊ฑฐ์น ๋ค์ ์ค๊ฐ ์ํ 19๊ฐ ์๋๊ฐ ์ ๋ถ ์ผ์นํ๋ค. ์ต์ข ๊ฒฐ๊ณผ ํ๋๊ฐ ์๋๋ผ ์ค๊ฐ์ด ๋ง์๋จ์ด์ง๋, ์ฐ์ฐํ ๋ต๋ง ๋ง์ ๊ฒ ์๋๋ผ๋ ๊ทผ๊ฑฐ๊ฐ ๋๋ค.
โ ์ง์ง ๋ฐ์ด๋๋ฆฌ๋ก ํ ๋ฒ ๋
๋ง์ง๋ง์ ๋ฐฐํฌ๋ ์คํ ํ์ผ์ ์ง์ ๋จน์ฌ ๋ณด๋ ๊ฒ์ด๋ค. ํ์ดํ ๋์ subprocess ๋ก ์ธ์๋ฅผ ๋๊ธด๋ค.
# verify.py
import subprocess
flag = "DH{F805042AC5E59FD07ABEF1E9B2EF}"
r = subprocess.run(["./prob", flag], capture_output=True, text=True)
print("Input :", flag, "(len=%d)" % len(flag))
print("Output :", r.stdout.strip())
wrong = "DH{F805042AC5E59FD07ABEF1E9B2Ef}" # ๋ง์ง๋ง ๊ธ์ ํ๋๋ง ๋ฐ๊พผ ๊ฒ
r2 = subprocess.run(["./prob", wrong], capture_output=True, text=True)
print("Input :", wrong, "(ํ ๊ธ์ ๋ณ์กฐ)")
print("Output :", r2.stdout.strip())python3 verify.py
ํ ๊ธ์๋ง ๋์๋ฌธ์๋ฅผ ๋ฐ๊ฟ๋ ๋ฐ๋ก Wrong... ์ด๋ค. ์ฐ์ฐํ ํต๊ณผํ ์ฌ์ง๊ฐ ์๋ค๋ ํ์ธ์ด๋ค.
๋ฐฑ์ ๋ณธ๋ง ๊ฐ์ง๊ณ ๋ ๋ค์ ํ๋๊ทธ๋ฅผ ๋ฝ์ ์ ์๊ฒ, ๋น๋๋ถํฐ ๋์กฐ๊น์ง ํ ๋ฐฉ์ ๋๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋จ๊ฒผ๋ค.
#!/usr/bin/env bash
# snow (Dreamhack, Silver 1 / reversing) โ ํ ๋ฐฉ ์ฌํ.
# ๋ฐฐํฌ ๋ฐ์ด๋๋ฆฌ prob ํ๋๋ง ์์ผ๋ฉด ๋๋ค. ํ
์ด๋ธยท์ ๋ต 32๋ฐ์ดํธ๋ฅผ .data ์์ ์ง์ ์ฝ์ผ๋ฏ๋ก
# ๋ณ๋ ์
๋ ฅ ํ์ผ์ด ์๋ค. ํ์ด์ฌ 3 ๋ง ์์ผ๋ฉด ์ด๋์๋ ๋๋ค(์ธ๋ถ ํจํค์ง ์์).
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('../๋ฌธ์ .json'))['flag'])" 2>/dev/null || echo '')
chmod +x prob 2>/dev/null || true
OUT=$(timeout 300 python3 solve.py 2>&1) || true
echo "$OUT" | tail -8
# ํ๋ณด ๋ชฉ๋ก์๋ DH{ ๋ก ์์ํ๋ ๊นจ์ง ๋ฌธ์์ด์ด ์์ด๋ฏ๋ก ๋ง์ง๋ง ANSWER ์ค์์๋ง ๋ฝ๋๋ค.
FLAG=$(printf '%s' "$OUT" | sed -n 's/^ANSWER: //p' | tail -1)
# ๋ฝ์ ๊ฐ์ ๋ฐฐํฌ ๋ฐ์ด๋๋ฆฌ์ ๊ทธ๋๋ก ๋จน์ฌ Correct! ๊ฐ ๋์ค๋์ง๊น์ง ๋ณธ๋ค.
if [ -n "$FLAG" ]; then
VERDICT=$(./prob "$FLAG" 2>&1 || true)
echo "./prob '$FLAG' โ $VERDICT"
else
VERDICT=""
fi
if [ -n "$FLAG" ] && [ "$VERDICT" = "Correct!" ] && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๋ฐ์ด๋๋ฆฌ ํ์ : '${VERDICT:-์์}' / ๊ธฐ๋: '$EXPECT')"; exit 1./reproduce.sh
๋ก์ปฌ ํ๊ฒฝ: Ubuntu 22.04 ยท x86-64 ยท Python 3.10(์ธ๋ถ ํจํค์ง ์์) ยท binutils 2.38 ์ objdump ยท GNU gdb 12.1. ๋ฌธ์ ๋ฐ์ด๋๋ฆฌ๋ for GNU/Linux 3.2.0 ๋ก ๋น๋๋ ๊ฒ์ด๋ผ ์์ฆ ๋ฐฐํฌํ์์๋ ๊ทธ๋๋ก ๋๋ค.
๐ ๊ฒฐ๋ก
ํ์ค ์๊ณ ๋ฆฌ์ฆ์ ์์๋ก ์ฐพ๋ ๊ฒ ๋น ๋ฅด๋ค
๋์ค์ด์
๋ธ๋ฆฌ๋ฅผ ์์์๋ถํฐ ๋ค ์ฝ์ ํ์๋ ์์๋ค. MULxPOW ์ ์ง์ 23, 245, 48, 239 ์ ์์ 0xa9 โ ์ด ์กฐํฉ ํ๋๊ฐ "SNOW 3G ๋ค"๋ฅผ ํ์ ํด ์คฌ๋ค.
AES S-box ๋ ์ฌ๋ฌ ์๊ณ ๋ฆฌ์ฆ์ด ๋น๋ ค ์ฐ๋ ๊ทธ๊ฒ๋ง์ผ๋ก๋ ๋ถ์กฑํ๋ค. ๋ฐ๋ฉด alpha ๊ณฑ์ ์ ์ง์ ๋ค ๊ฐ๋ ๊ทธ ์๊ณ ๋ฆฌ์ฆ์๋ง ์๋ค. ๋ฏ์ ์ํธ ๋ฃจํด์ ๋ง๋๋ฉด ๊ฐ์ฅ ํน์ดํ ์์๋ถํฐ ๊ฒ์ํ๋ ๊ฒ ํจ์ ์ด๋ฆ ์๋ ๋ฐ์ด๋๋ฆฌ์์ ์ ์ผ ๋น ๋ฅธ ๊ธธ์ด๋ค.
์ถ๋ ฅ์ ๋ฒ๋ฆฌ๋ ์ง์ ์ด ๊ณง ๊ณต๊ฒฉ๋ฉด์ด๋ค
์ด ๋ฌธ์ ์ ๊ธ์๋ SNOW 3G ์์ฒด๊ฐ ์๋๋ผ, 32๋นํธ๋ฅผ ๋ฝ์ 1๋นํธ๋ง ์ด ์ค๊ณ์๋ค. ์๋๋ง๋ค ๋ฏธ์ง์๊ฐ 1๋นํธ๋ก ์ค์ด๋๋ ์๊ธฐ์ฐธ์กฐ ๊ณ ๋ฆฌ๊ฐ 8๋นํธ๋ก ์ ํ๋ค.
์ํธ ์๊ณ ๋ฆฌ์ฆ์ด ์๋ฌด๋ฆฌ ํผํผํด๋ ๊ทธ๊ฑธ ์ฐ๋ ์ฝ๋๊ฐ ์ ๋ณด๋ฅผ ๋ฒ๋ฆฌ๋ฉด ๊ทธ ์๋ฆฌ๊ฐ ํ์ ๊ณต๊ฐ์ด ๋๋ค. ์ค๋ฌด์์๋ "ํด์์ ์ 4๋ฐ์ดํธ๋ง ์ด๋ค", "๋์์ ํ์ ๋นํธ๋ง ์ด๋ค" ๊ฐ์ ์ ์ฝ์ด ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋ฌด๋์ง๋ค.
ํ๋ณด๊ฐ ์ด์๋จ์๋ค๋ ๊ฒ ์์ฒด๋ ์ฆ๊ฑฐ๊ฐ ์๋๋ค
์๊ธฐ๋ฌด๋ชจ์ ๊ฒ์ฌ๋ง์ผ๋ก 256๊ฐ ์ค 4๊ฐ๊ฐ ๋จ์๋ค. ์๋ชป๋ ๋ชจ๋ธ(์ด๊ธฐํ ๊ผฌ๋ฆฌ ๋๋ฝ, ๋น ์๋์ธ IV)์์๋ 2~3๊ฐ๋ ๋จ์๋ค. "๋ช ๊ฐ ์ด์๋จ์๋ค"๋ ์ฌ์ค์ ๋ชจ๋ธ์ด ๋ง๋ค๋ ๋ณด์ฆ์ด ์ ํ ์ ๋๋ค.
ํ์ ์ ๋ฐ์์ ์จ ๋
๋ฆฝ ๊ธฐ์ค์ด์ด์ผ ํ๋ค. ์ฌ๊ธฐ์๋ printable ์ฌ๋ถ, ์ ๋ฐฉํฅ ์ฌํ, ๊ทธ๋ฆฌ๊ณ ์ค์ ๋ฐ์ด๋๋ฆฌ์ Correct! ์
์ด์๋ค.
IV ๋ฅผ ์ฌ์ฉ์ ์ ๋ ฅ์ผ๋ก ๋๋ฉด ์ฌํ์ฑ์ด ์ฌ๋ผ์ง๋ค
๋ฐฉ์ด ๊ด์ ์ผ๋ก ์ฎ๊ธฐ๋ฉด ์ด ๋ฌธ์ ์ฝ๋๋ IV ๋ฅผ ๊ฒ์ฆ ์์ด ์ฌ์ฉ์ ์ ๋ ฅ์์ ๊ฐ์ ธ์จ๋ค. ์ง์ง ์๋น์ค๋ผ๋ฉด IV ๋ ๋งค๋ฒ ์๋ก ๋ฝ์ ์ํธ๋ฌธ๊ณผ ํจ๊ป ์ ์ฅํ๊ฑฐ๋, ์ต์ํ ์นด์ดํฐ๋ก ๊ด๋ฆฌํด์ผ ํ๋ค.
๊ฐ์ IV ๋ฅผ ์ฌ์ฌ์ฉํ๋ฉด ํค์คํธ๋ฆผ์ด ๊ทธ๋๋ก ๋ฐ๋ณต๋๊ณ , ์ง๊ธ์ฒ๋ผ IV ๋ฅผ ๊ณต๊ฒฉ์๊ฐ ๊ณ ๋ฅผ ์ ์์ผ๋ฉด ํค์คํธ๋ฆผ์ ์ํ๋ ๋ฐฉํฅ์ผ๋ก ์ ๋ํ ์ ์๋ค. ์คํธ๋ฆผ ์ํธ์์ nonce ์ฌ์ฌ์ฉ์ด ์น๋ช ์ ์ธ ์ด์ ๊ฐ ์ด๊ฒ์ด๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ