๋ฌธ์ : DreamHack ์๊ฒ์ โ bitvm ๋ถ๋ฅ: reversing ๋์ด๋: ๐ฅ Gold 4 FLAG:
DH{bc42e3abb217fa4fd8b8991bc5a378d7dd4421fd887feb797eb759ca2d0d0e95}
๋ฌธ์ ์ค๋ช ์ด ํ ์ค์ด๋ค.
๊ฐ๋จํ VM์ ๋๋ค! ์ ํ, ๋น๊ต์ฐ์ฐ์, ์ฌ์ง์ด๋ ๋ง์ ๋บ์ ๋ ์กด์ฌํ์ง ์์ต๋๋ค!
๋ณดํต ์ด๋ฐ ๋ฌธ์ฅ์ ๊ฒ์ ์ฃผ๋ ค๊ณ ์ด๋ค. ์ฌ๊ธฐ์๋ ๋ฐ๋๋ค. ์๋ ๊ฒ๋ค์ ํ๋์ฉ ์ง์ ๋๊ฐ๋ฉด ๋จ๋ ๊ฒ ๋๋ฌด ์ ์ด์, ์คํ๋ ค ๋ต์ด ํ ์ค ์์์ผ๋ก ๋จ์ด์ง๋ค.
๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | bitvm |
| ๋์ด๋ | ๐ฅ Gold 4 |
| ๋ถ๋ฅ | reversing |
| ์ถ์ฒ | Dreamhack CTF Season 4 Round #10 (Div2) |
| ์ ๊ณต ํ์ผ | chall (ELF 64-bit PIE, stripped) ยท command (2,472๋ฐ์ดํธ ๋ฐ์ดํธ์ฝ๋) |
| ์๋ฒ | ์์ โ ์คํ๋ผ์ธ ๋ฌธ์ |
| ํต์ฌ ์ทจ์ฝ์ / ๊ธฐ๋ฒ | ๋นํธ์ฐ์ฐ๋ง ๊ฐ์ง VM ์ ๊ฒ์ฌ์์ ๋นํธ ๋จ์๋ก ๋ถํดํด ์ญ์ฐ |
ํ์ด ํ๋ฆ์ ์ ์ผ๋ก ๋๋๋ค.
chall์ ๋์คํจ์น ๋ฃจํ๋ฅผ ์ฝ์ด ๋ช ๋ น ์งํฉ ์ด ๊ฐ๋ฅผ ๋ณต์ํ๋ค.- ๊ทธ ๋ช
๋ น ์งํฉ์ผ๋ก
command๋ฅผ ๋์ค์ด์ ๋ธํด ๊ฒ์ฌ ๋ธ๋ก 68๊ฐ๋ฅผ ์ฐพ๋๋ค. - ๋ธ๋ก์ด ๊ฑฐ๋ ์ ์ฝ ๋ ์ค์ ๋นํธ๋ณ๋ก ํ์ด ๊ธ์๋ฅผ ํ๋์ฉ ๊ฒฐ์ ํ๋ค.
๐งฉ ๋ฐฐ๊ฒฝ โ ๋ฐ์ดํธ์ฝ๋ VM ๋ฌธ์ ๊ฐ ์๊ตฌํ๋ ๊ฒ
๋ฆฌ๋ฒ์ฑ ๋ฌธ์ ์์ VM ์ด ๋์ค๋ฉด ๋ถ์ ๋์์ด ๋ ๊ฒน์ด ๋๋ค.
๋ฐ๊นฅ์ ๋ค์ดํฐ๋ธ x86-64 ๋ฐ์ด๋๋ฆฌ๋ค. ์ด ์์๋ "๋ช ๋ น์ ํ๋ ์ฝ์ด์ ์ข ๋ฅ์ ๋ฐ๋ผ ๊ฐ๋ผ์ง๋ค"๋ ๋ฃจํ ํ๋๋ง ๋ค์ด ์๋ค. ์ง์ง ๋ก์ง์ ๊ทธ ๋ฃจํ๊ฐ ํด์ํ๋ ๋ฐ์ดํฐ ํ์ผ ์ชฝ์ ์๋ค.
๊ทธ๋์ ์์๊ฐ ์ ํด์ ธ ์๋ค. ๋ฐ๊นฅ ๋ฃจํ๋ฅผ ์ฝ์ด ๋ช ๋ น ์งํฉ(ISA)์ ํ์ ํ์ง ๋ชปํ๋ฉด ์์ชฝ ๋ฐ์ดํฐ๋ ๊ทธ๋ฅ ๋ฌด์๋ฏธํ ๋ฐ์ดํธ ๋ฉ์ด๋ฆฌ๋ค. ๋ฐ๋๋ก ISA ๋ง ํ์ ๋๋ฉด ์์ชฝ์ ๋์ค์ด์ ๋ธ๋ฌ 30์ค๋ก ์ฝํ๋ค.
์ด ๋ฌธ์ ๊ฐ ์ฌ๋ฏธ์๋ ์ง์ ์ ISA ๊ฐ ์๋์ ์ผ๋ก ๋น์ฝํ๋ค๋ ๊ฒ์ด๋ค. ๋ฌธ์ ์ค๋ช ์ด ์๋ค๊ณ ์๋ํ ๊ฒ๋ค โ ๋ถ๊ธฐยท๋น๊ตยท๋ง์ โ ์ ๋ณดํต "๊ฒ์ฌ"๋ฅผ ์งค ๋ ์ ์ผ ๋จผ์ ์ฐ๋ ๋๊ตฌ๋ค์ด๋ค. ๊ทธ๊ฑธ ๋ค ๋นผ๊ณ ๋ 68๊ธ์๋ฅผ ๊ฒ์ฌํ๋ ค๋ฉด ๋ฐฉ๋ฒ์ด ํ๋๋ก ์ขํ์ง๋ค. ๊ทธ ์ขํ์ง ๋ฐฉ๋ฒ์ด ๊ณง ์ญ์ฐ ๊ณต์์ด ๋๋ค.
๐ฌ ์ ์ฐฐ โ ํ์ผ ๋ ๊ฐ, ๊ทธ๋ฆฌ๊ณ ์ธ์ฌ๋ง์ด ๋ฐ์ดํฐ์ ๋ฐํ ์๋ค
๋ฐฐํฌ๋ณธ์ ํ๋ฉด ํ์ผ์ด ๋์ด๋ค. ์คํ ๊ฐ๋ฅํ chall ๊ณผ, file ์ด ๊ทธ๋ฅ data ๋ผ๊ณ ๋ตํ๋ command.
ls -l extracted/ && file extracted/* && checksec --file=extracted/chall
๋ณดํธ ๊ธฐ๋ฒ์ Full RELRO ยท Canary ยท NX ยท PIE ๋ก ๋ค ์ผ์ ธ ์๋ค. ํ์ง๋ง ์ด๊ฑด pwn ๋ฌธ์ ๊ฐ ์๋๋ผ
๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊นจ๋จ๋ฆด ์ผ์ด ์๋ค. ์ฌ๊ธฐ์ ์๋ฏธ ์๋ ๊ฑด stripped ํ๋๋ค. ์ฌ๋ณผ์ด ์์ผ๋ main ๋
run ๋ ์ด๋ฆ์ผ๋ก๋ ๋ชป ์ฐพ๋๋ค.
๋จผ์ ์์๋ ๊ฒ ํ๋. chall ์ ์ธ์ ์์ด ์คํํ๋ฉด ์๋ฌด๊ฒ๋ ์ ํ๋ค.
// 0x1671 โ main
if (argc != 2) { puts("Usage: ./chall command_file"); return 0; }๋ฐ์ดํธ์ฝ๋ ํ์ผ ๊ฒฝ๋ก๋ฅผ ๋ฐ๋์ ๋๊ฒจ์ผ ํ๋ค. ๊ทธ๋ฅ ./chall ๋ง ์น๊ณ "์คํ์ด ์ ๋๋ค" ํ๊ณ
๋์ด๊ฐ๊ธฐ ๋ฑ ์ข์ ์ง์ ์ด๋ผ, ์คํ์ ๋ ์ด ๊ผด์ด์ด์ผ ํ๋ค.
cd extracted && chmod +x chall && ./chall commandcommand ๋ฅผ ๋จผ์ ๋ค์ฌ๋ค๋ดค๋ค.
xxd extracted/command | sed -n "1,6p;10,18p"
์ฌ๊ธฐ์ ์ด๋ฏธ ์ ๋ฐ์ ๋ณด์ธ๋ค.
์๋จธ๋ฆฌ ASCII ์ด์ด !T!h!i!s! !i!s! ๋ค. ์๋ณธ ๋ฌธ์์ด์ This is a mini VM to check the flag! ์ธ๋ฐ
๊ธ์๋ง๋ค ์์ 0x21(!)์ด ํ๋์ฉ ๋ผ์ด ์๋ค. ์ฆ 0x21 ์ "๋ค์ ๋ฐ์ดํธ๋ฅผ ์ถ๋ ฅํ๋ผ"๋
1๋ฐ์ดํธ ์คํผ๋๋ ๋ช
๋ น์ด๋ค. ์ธ์ฌ๋ง์ด ์ฝ๋๊ฐ ์๋๋ผ ๋ช
๋ น ์ํ์ค๋ก ๋ฐํ ์๋ ๊ฒ์ด๋ค.
๊ทธ ๋ค์ Input: ๋ค๋ก๋ 0x22(")๊ฐ ๊ธธ๊ฒ ์ด์ด์ง๋ค. ์ถ๋ ฅ ๋ค์์ ์ค๋ ๊ฒ์ด๋ ์
๋ ฅ์ ๋ฐ๋
๋ช
๋ น์ผ ๊ฐ๋ฅ์ฑ์ด ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ค๋ถํฐ ํ์ผ ๋๊น์ง๋ 0x23 0x25 0x26 0x27 0x28 ์ด
์ผ์ ํ ๋ฆฌ๋ฌ์ผ๋ก ๋ฐ๋ณต๋๋ค. ๊ฒ์ฌ ๋ฃจํด์ด ์ฌ๊ธฐ ์๋ค.
์ถ์ธก์ ์ฌ๊ธฐ๊น์ง๋ค. ๋๋จธ์ง๋ ๋ฐ๊นฅ ๋ฐ์ด๋๋ฆฌ๊ฐ ํ์ ํด ์ค๋ค.
๐ง ๋ช ๋ น ์งํฉ ๋ณต์ โ ๋์คํจ์น ์ฒด์ธ์ ๊ทธ๋๋ก ์ฝ๋๋ค
objdump -d ๋ก ์ ์ฒด๋ฅผ ๋ ๋์ผ๋ฉด 490์ค์ฏค ๋์จ๋ค. stripped ๋ผ ํจ์ ์ด๋ฆ์ด ์์ง๋ง PLT ํธ์ถ์
๊ทธ๋๋ก ๋ณด์ด๋, ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํจ์๋ฅผ ๋ถ๋ฅด๋์ง๋ก ์ญํ ์ ์ง์ํ ์ ์๋ค.
.text ์์์ ์๋ฏธ ์๋ ๋ฉ์ด๋ฆฌ๋ ๋ค์ฏ์ด๋ค. ์์ชฝ ์
์ __cxa_finalize ๋ฅผ ๋ง์ง๋ ์ปดํ์ผ๋ฌ
์์ฑ ์ฝ๋(frame_dummy ๋ฅ)๋ผ ๊ฑด๋๋ด๋ค. ๋จ์ ๊ฒ ์ค fopen / getc / feof ๋ฅผ ๋ถ๋ฅด๋ฉฐ
๋ฐ์ดํธ๋ฅผ .bss ๋ก ํผ ๋๋ฅด๋ ๊ฒ ํ์ผ ๋ก๋๊ณ , putchar / getchar ๋ฅผ ๋ถ๋ฅด๋ฉฐ ๋๋์์ค๋
์ ํ๊ฐ ์๋ ๊ฒ VM ๋ณธ์ฒด๋ค. ๋ง์ง๋ง ํ๋๋ puts ๋ ๋ฒ์ผ๋ก ํ์ ์ ์ฐ๋ main ์ด๋ค.
๋ก๋๋ถํฐ ๋ณด์.
// 0x1229 โ command ํ์ผ์ .bss 0x4040 ์ผ๋ก ํต์งธ ๋ณต์ฌ
FILE *f = fopen(path, "rb");
if (!f) { printf("No file named %s\n", path); return 1; }
for (int i = 0; !feof(f); i++) {
if (i > 0x1387) { puts("Command too large"); return 1; } // ์ต๋ 5000๋ฐ์ดํธ
code[i] = getc(f);
}feof ๋ฅผ getc ์์์ ๊ฒ์ฌํ๋ ๊ตฌ์กฐ๋ผ, ๋ง์ง๋ง ํ ๋ฒ์ EOF(-1)๋ฅผ ๊ทธ๋๋ก 0xff ๋ก
์ ์ฅํ๊ณ ๋๋๋ค. command ๋ 2,472๋ฐ์ดํธ์ด๊ณ ๋ง์ง๋ง ๋ฐ์ดํธ๊ฐ ๋ง์นจ HALT ๋ผ ์๋ฌด ์ํฅ์ด ์๋ค.
์ฝ๋ค๊ฐ ๊ฐธ์ํ์ง๋ง ํจ์ ์ ์๋์๋ค.
๊ทธ ๋ค์ ํจ์๊ฐ ๋ณธ์ฒด๋ค. 0x1341 ์์ ์์ํด 0x1364 ๋ก ๋๋์์ค๋ ๋ฃจํ ํ๋๊ฐ ์ ๋ถ๋ค.
// 0x1341 โ ์ด๊ฒ VM ์ด๋ค
unsigned char regs[8] = {0}; // [rbp-0x10]
for (;;) {
unsigned char op = code[pc]; // pc = .bss 0x6768
pc++;
if (op == 0x20) return regs[0];
else if (op == 0x21) { putchar(code[pc]); pc++; }
...
}๋ถ๊ธฐ ๊ตฌ์กฐ๊ฐ if-else 10๋จ์ด๋ผ cmp BYTE PTR [rbp-0x11], <์์> ๋ง ๋ฝ์ผ๋ฉด ์คํผ์ฝ๋ ๋ชฉ๋ก์ด
๊ทธ๋๋ก ๋์จ๋ค. ๊ฐ ์ค์ ๋ฌด์จ ์ผ์ ํ๋์ง ์ฃผ์์ ์น์ด ์ฐ์๋ค. ์ฃผ์์ sed ๋ก ๋ด๊ฐ ๋ถ์ธ ๊ฒ์ด๊ณ
objdump ์๋ ์ถ๋ ฅ์๋ ์๋ค.
# annot_dispatch.sed โ ์ค์ ์ถ๋ ฅ์ ์ค๋ช
๋ง ์น๋๋ค
s/cmp BYTE PTR \[rbp-0x11\],0x20/& <-- 0x20 HALT : r0 ์ ๋ฐํ๊ฐ์ผ๋ก/
s/cmp BYTE PTR \[rbp-0x11\],0x21/& <-- 0x21 PUTC : ๋ค์ ๋ฐ์ดํธ๋ฅผ putchar/
s/cmp BYTE PTR \[rbp-0x11\],0x22/& <-- 0x22 READ : getchar ๊ฒฐ๊ณผ๋ฅผ ์คํ์ push/
s/cmp BYTE PTR \[rbp-0x11\],0x23/& <-- 0x23 SETI : r[a] = imm/
s/cmp BYTE PTR \[rbp-0x11\],0x24/& <-- 0x24 MOV : r[a] = r[b]/
s/cmp BYTE PTR \[rbp-0x11\],0x25/& <-- 0x25 AND/
s/cmp BYTE PTR \[rbp-0x11\],0x26/& <-- 0x26 OR/
s/cmp BYTE PTR \[rbp-0x11\],0x27/& <-- 0x27 XOR/
s/cmp BYTE PTR \[rbp-0x11\],0x28/& <-- 0x28 POP : r0 = pop()/
s/cmp BYTE PTR \[rbp-0x11\],0x29/& <-- 0x29 PUSH : push(r[a])/objdump -d -M intel --start-address=0x1364 --stop-address=0x1670 extracted/chall | grep -E "cmp +BYTE PTR .rbp-0x11." | sed -f annot_dispatch.sed
์ด ๊ฐ๋ค. 0x20 ๋ถํฐ 0x29 ๊น์ง ๋นํ์์ด ์ด์ด์ง๊ณ ๊ทธ ๋ฐ์ ๊ฐ์ ์๋ฌด ๋ถ๊ธฐ์๋ ์ ๊ฑธ๋ ค
์กฐ์ฉํ ๋ฌด์๋๋ค(๋ฃจํ ๋งจ ๋์ผ๋ก ๋จ์ด์ ธ ๋ค์ fetch ๋ก ๊ฐ๋ค).
ALU ์ชฝ์ ๋ฐ๋ก ๋ด์ผ ํ๋ค. 0x25 0x26 0x27 ์ธ ํธ๋ค๋ฌ๋ ์ฝ๋๊ฐ ๊ฑฐ์ ๋๊ฐ๊ณ , ๋ฑ ํ ๋ช
๋ น๋ง
๋ค๋ฅด๋ค. ๋ ํผ์ฐ์ฐ์๋ฅผ esi ์ ecx ์ ๊ฐ๊ฐ ์ค์ ๋ค์ ๊ทธ ๋์ ํฉ์น๋ ์๋ฆฌ๋ค.
# annot_alu.sed โ ๋ ์ง์คํฐ ํ์ผ ์ ๊ทผ๊ณผ ์ฐ์ฐ ์๋ฆฌ์ ์ค๋ช
์ ์น๋๋ค
s/and esi,ecx/& <-- 0x25 AND : r[a] \& r[b]/
s/or esi,ecx/& <-- 0x26 OR : r[a] | r[b]/
s/xor esi,ecx/& <-- 0x27 XOR : r[a] ^ r[b]/
s/add esi,ecx/& <-- (์์)/
s/sub esi,ecx/& <-- (์์)/
s/movzx esi,BYTE PTR \[rbp+rax\*1-0x10\]/& <-- ์ฒซ์งธ ํผ์ฐ์ฐ์ r[a] ์ฝ๊ธฐ/
s/movzx ecx,BYTE PTR \[rbp+rax\*1-0x10\]/& <-- ๋์งธ ํผ์ฐ์ฐ์ r[b] ์ฝ๊ธฐ/
s/mov BYTE PTR \[rbp+rax\*1-0x10\],dl/& <-- ๊ฒฐ๊ณผ๋ฅผ r[d] ์ ์ฐ๊ธฐ/objdump -d -M intel --start-address=0x149f --stop-address=0x1610 extracted/chall | grep -E "(and|or|xor|add|sub) +esi,ecx|rbp.rax.1-0x10" | sed -f annot_alu.sed
andยทorยทxor ์
๋ง ๊ฑธ๋ฆฐ๋ค. ๊ฐ์ grep ์ addยทsub ๋ฅผ ๊ฐ์ด ๋ฃ์๋๋ฐ ๋งค์น๊ฐ ์๋ค.
๋ฌธ์ ์ค๋ช
์ด ์๋ํ ๊ทธ๋๋ก๋ค.
์ ๋ฆฌํ๋ฉด ์ด๋ ๋ค.
| op | ์ด๋ฆ | ์คํผ๋๋ | ๋์ |
|---|---|---|---|
0x20 | HALT | โ | r0 ์ ๋ฐํ๊ฐ์ผ๋ก ์ข
๋ฃ |
0x21 | PUTC | imm | putchar(imm) |
0x22 | READ | โ | push(getchar()) |
0x23 | SETI | d, imm | r[d] = imm |
0x24 | MOV | d, s | r[d] = r[s] |
0x25 | AND | d, a, b | r[d] = r[a] & r[b] |
0x26 | OR | d, a, b | r[d] = r[a] | r[b] |
0x27 | XOR | d, a, b | r[d] = r[a] ^ r[b] |
0x28 | POP | โ | r0 = pop() |
0x29 | PUSH | s | push(r[s]) |
์ํ๋ ์ ๋ถ ์ ์ญ์ด๋ค. ์ฝ๋ ๋ฒํผ๊ฐ .bss 0x4040, pc ๊ฐ 0x6768, ๋ฐ์ดํฐ ์คํ์ด 0x53e0,
์คํ ํฌ์ธํฐ๊ฐ 0x676c. ๋ ์ง์คํฐ๋ง ์์ธ๋ก run() ์ ์คํ ์ง์ญ๋ณ์ [rbp-0x10] 8๋ฐ์ดํธ๋ค.

๋ ์ง์คํฐ ์ธ๋ฑ์ค๋ ๋ฐ์ดํธ์ฝ๋์์ ๊ทธ๋๋ก ์ค๋๋ฐ ๊ฒฝ๊ณ ๊ฒ์ฌ๊ฐ ์๋ค. 8 ์ด์์ ์ฃผ๋ฉด ๋ฐ๋ก
๋ค ์นด๋๋ฆฌ๋ฅผ ๋ฐ๋๋ค. ์ค์ command ๊ฐ ์ฐ๋ ์ต๋ ์ธ๋ฑ์ค๋ 7 ์ด๋ผ ์ด๋ฒ์ ์๋ฌด ์ผ๋ ์ ์ผ์ด๋๋ค.
์ฌ๊ธฐ์ ์ค์ํ ๊ฑด ์๋ ์ชฝ์ด๋ค. pc ๋ฅผ ๋๋๋ฆฌ๋ ๋ช
๋ น์ด ์์ผ๋ ๋ฃจํ๋ ์กฐ๊ฑด ๋ถ๊ธฐ๋ ๋ชป ๋ง๋ ๋ค.
ํ๋ก๊ทธ๋จ์ ์์์ ๋ค๋ก ํ ๋ฒ ํ๋ฅด๊ณ ๋๋๋ค. 68๊ธ์๋ฅผ ๊ฒ์ฌํ๋ ค๋ฉด ๊ฒ์ฌ ์ฝ๋๋ฅผ 68๋ฒ ํผ์ณ ๋๋
์๋ฐ์ ์๊ณ , ์ค์ ๋ก ๊ทธ๋ ๊ฒ ๋ผ ์๋ค.
๐ ๋์ค์ด์ ๋ธ๋ฌ โ 30์ค์ด๋ฉด ์์ชฝ์ด ๋ค ๋ณด์ธ๋ค
ISA ๊ฐ ํ์ ๋์ผ๋ command ๋ฅผ ์ฝ๋ ๊ฑด ๊ธฐ๊ณ์ ์ด๋ค. ๊ฐ ๋ช
๋ น์ ์คํผ๋๋ ๊ธธ์ด๋ง ์๋ฉด ๋๋๊น.
#!/usr/bin/env python3
"""bitvm - command ํ์ผ(VM ๋ฐ์ดํธ์ฝ๋) ๋์ค์ด์
๋ธ๋ฌ."""
import sys
code = open(sys.argv[1] if len(sys.argv) > 1 else "extracted/command", "rb").read()
pc = 0
out = []
while pc < len(code):
at = pc
op = code[pc]
pc += 1
if op == 0x20:
out.append((at, "HALT", ()))
break
elif op == 0x21:
out.append((at, "PUTC", (code[pc],)))
pc += 1
elif op == 0x22:
out.append((at, "READ", ()))
elif op == 0x23:
out.append((at, "SETI", (code[pc], code[pc + 1])))
pc += 2
elif op == 0x24:
out.append((at, "MOV", (code[pc], code[pc + 1])))
pc += 2
elif op in (0x25, 0x26, 0x27):
name = {0x25: "AND", 0x26: "OR", 0x27: "XOR"}[op]
out.append((at, name, (code[pc], code[pc + 1], code[pc + 2])))
pc += 3
elif op == 0x28:
out.append((at, "POP", ()))
elif op == 0x29:
out.append((at, "PUSH", (code[pc],)))
pc += 1
else:
out.append((at, "??? 0x%02x" % op, ()))
for at, name, args in out:
if name == "PUTC":
print("%04x PUTC 0x%02x ; %r" % (at, args[0], chr(args[0])))
elif name == "SETI":
print("%04x SETI r%d, 0x%02x" % (at, args[0], args[1]))
elif name == "MOV":
print("%04x MOV r%d, r%d" % (at, args[0], args[1]))
elif name in ("AND", "OR", "XOR"):
sym = {"AND": "&", "OR": "|", "XOR": "^"}[name]
print("%04x %-5s r%d, r%d, r%d ; r%d = r%d %s r%d"
% (at, name, args[0], args[1], args[2], args[0], args[1], sym, args[2]))
elif name == "PUSH":
print("%04x PUSH r%d" % (at, args[0]))
else:
print("%04x %s" % (at, name))
print("\n์ด %d ๋ช
๋ น, READ %d ๊ฐ, POP %d ๊ฐ"
% (len(out),
sum(1 for _, n, _ in out if n == "READ"),
sum(1 for _, n, _ in out if n == "POP")))๋๋ ค์ ์๋ค๋ฅผ ์๋ผ ๋ดค๋ค.
python3 disasm.py extracted/command | sed -n "41,50p;110,112p" && python3 disasm.py extracted/command | tail -4
์ด 794๋ช
๋ น. READ 68๊ฐ, POP 68๊ฐ. ์
๋ ฅ์ 68๊ธ์ ๋ฐ์์ 68๋ฒ ๊บผ๋ด ์ด๋ค๋ ๋ป์ด๋ค.
DH{ ์ } ๋ฅผ ๋นผ๋ฉด 64์ โ ๋ฑ SHA-256 ํฅ์ฌ ๊ธธ์ด๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง ๋ ์ค์ด ๊ฒฐ๋ก ์ ๋ฏธ๋ฆฌ ์๋ ค ์ค๋ค.
09a4 MOV r0, r7
09a7 HALTr7 ์ r0 ์ ์ฎ๊ธฐ๊ณ ๋๋ธ๋ค. main ์ ์ด ๋ฐํ๊ฐ์ ์ด๋ ๊ฒ ์ด๋ค.
// 0x16bf โ run() ์ ๋ฐํ๊ฐ์ด 0 ์ด์ด์ผ Correct
if (run() == 0) puts("Correct!!!");
else puts("Wrong.");์ฆ r7 ์ 0 ์ผ๋ก ๋ง๋๋ ์
๋ ฅ์ด flag ๋ค. r7 ์ด ๋ฌด์์ ๋ชจ์ผ๋์ง๋ง ๋ณด๋ฉด ๋๋ค.
๊ทธ ์ ์ ๋ช ๋ น์ด ๊ฐ๊ฐ ๋ช ๋ฒ ์ฐ์ด๋์ง ์ธ์ด ๋ดค๋ค. ๋ฐ๋ณต ๊ตฌ์กฐ๋ผ๋ฉด ํ์๊ฐ 68์ ๋ฐฐ์๋ก ๋ฑ ๋จ์ด์ง ๊ฒ์ด๊ณ , ์ ๋จ์ด์ง๋ฉด ๋ด๊ฐ ์คํผ๋๋ ๊ธธ์ด๋ฅผ ์๋ชป ์ฝ์๋ค๋ ๋ป์ด๋ค. ๊ฐ์ผ ์๊ฐ ์ง๋จ์ด๋ค.
#!/usr/bin/env python3
"""command ๋ฐ์ดํธ์ฝ๋๊ฐ ๊ฐ ๋ช
๋ น์ ๋ช ๋ฒ ์ฐ๋์ง ์ผ๋ค."""
from collections import Counter
NAMES = {0x20: "HALT", 0x21: "PUTC", 0x22: "READ", 0x23: "SETI", 0x24: "MOV",
0x25: "AND", 0x26: "OR", 0x27: "XOR", 0x28: "POP", 0x29: "PUSH"}
WIDTH = {0x20: 0, 0x21: 1, 0x22: 0, 0x23: 2, 0x24: 2,
0x25: 3, 0x26: 3, 0x27: 3, 0x28: 0, 0x29: 1}
code = open("extracted/command", "rb").read()
cnt = Counter()
pc = 0
while pc < len(code):
op = code[pc]
cnt[op] += 1
pc += 1 + WIDTH[op]
if op == 0x20:
break
WHY = {
"HALT": "ํ๋ก๊ทธ๋จ ๋ ํ ๋ฒ",
"PUTC": "์ธ์ฌ๋ง 44๊ธ์",
"READ": "์
๋ ฅ 68๊ธ์",
"SETI": "๋ธ๋ก๋น A,B,C = 68 x 3",
"MOV": "๋ง์ง๋ง r0 = r7 ํ ๋ฒ",
"AND": "๋ธ๋ก๋น c & A = 68 x 1",
"OR": "c | A ์ r7 ๋์ 2ํ = 68 x 3",
"XOR": "B/C ์ ๋น๊ต 2ํ = 68 x 2",
"POP": "๋ธ๋ก๋น ๊ธ์ ํ๋ = 68 x 1",
"PUSH": "์ด ๋ฐ์ดํธ์ฝ๋๋ ์ ์ด๋ค",
}
print("op ์ด๋ฆ ํ์ ์ค๋ช
")
for op in sorted(NAMES):
n = NAMES[op]
print("0x%02x %-5s %4d %s" % (op, n, cnt[op], WHY[n]))
print("\nํฉ๊ณ %d ๋ช
๋ น / ํ์ผ %d ๋ฐ์ดํธ" % (sum(cnt.values()), len(code)))์ค๋ฅธ์ชฝ ์ค๋ช ์ด์ ๋ด๊ฐ ๋ถ์ธ ๊ฒ์ด๋ค. ์ซ์๋ง ์คํฌ๋ฆฝํธ๊ฐ ์ผ๋ค.
python3 opcount.py
์ ๋ถ 68์ ๋ฐฐ์๋ค. SETI 204 = 68 ร 3, XOR 136 = 68 ร 2, OR 204 = 68 ร 3.
ํ์ฑ์ด ์ด๊ธ๋ฌ๋ค๋ฉด ์ด ์ซ์๋ค์ด ์ง์ ๋ถํ๊ฒ ๋์์ ํ
๋ฐ ๊นจ๋ํ๋ค.
๋ ๊ฐ์ง๊ฐ ๋์ ๊ฑธ๋ฆฐ๋ค.
MOV ๊ฐ ๋ฑ ํ ๋ฒ๋ฟ์ด๋ค. ๋ง์ง๋ง MOV r0, r7 ์ด ๊ทธ ํ๋๋ค. ๋ ์ง์คํฐ ์ฌ์ด ๊ฐ์ ์ฎ๊ธธ ์ผ์ด
์๋ค๋ ๊ฑด ๋ธ๋ก๋ค์ด ์๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ง ์๋๋ค๋ ๋ป โ ๊ธ์๋ง๋ค ์์ ํ ๋
๋ฆฝ์ด๋ค.
PUSH ๋ ์์ 0 ์ด๋ค. ISA ์๋ ์๋๋ฐ ์ด ๋ฐ์ดํธ์ฝ๋๋ ํ ๋ฒ๋ ์ ์ด๋ค. ์คํ์ ๊ฐ์ด ์ฌ๋ผ๊ฐ๋
๊ฒฝ๋ก๊ฐ READ ํ๋๋ฟ์ด๋ผ๋ ๋ป์ด๊ณ , ๊ทธ๋์ ์คํ ๋ด์ฉ์ ์ฒ์๋ถํฐ ๋๊น์ง "์
๋ ฅ 68๊ธ์"
๊ทธ๋๋ก๋ค. ์ด ์ฌ์ค์ด ๋ค์์ ์์๋ฅผ ํ์ ํ ๋ ์ฐ์ธ๋ค.
PUTC 44 ๋ ๋ง๋ค. This is a mini VM to check the flag! 36์ + ๊ฐํ + Input: 7์.
๊ฒ์ฌ ๋ธ๋ก์ ๋ ๊ฐ ๋๋ํ ์ฐ์๋ค.
python3 disasm.py extracted/command | sed -n "113,132p"
๋ธ๋ก ํ๋๊ฐ ์ด ์ค์ด๊ณ , ์์ ์ ๋ง ๋ฐ๋๋ฉด์ 68๋ฒ ๋ํ์ด๋๋ค. ํ ๋ฒ์ ์ฎ๊ฒจ ์ ์ผ๋ฉด ์ด๋ ๋ค.
SETI r1, A ; ๋ธ๋ก๋ง๋ค ๋ค๋ฅธ ์์ ์
SETI r2, B
SETI r3, C
POP ; r0 = ์
๋ ฅ ํ ๊ธ์ c
AND r4, r0, r1 ; r4 = c & A
XOR r5, r2, r4 ; r5 = B ^ (c & A)
OR r7, r5, r7 ; r7 |= r5
OR r4, r0, r1 ; r4 = c | A
XOR r5, r3, r4 ; r5 = C ^ (c | A)
OR r7, r5, r7 ; r7 |= r5XOR ๋ก ์ฐจ์ด๋ฅผ ๋ง๋ค๊ณ OR ๋ก ๋ชจ์๋ค. ๋น๊ต ๋ช
๋ น์ด ์์ผ๋ ์ด๊ฒ "๊ฐ๋ค"๋ฅผ ํํํ๋ ์ ์ผํ ๋ฐฉ๋ฒ์ด๋ค.
์ฐจ์ด๊ฐ 0 ์ด๋ฉด XOR ๊ฒฐ๊ณผ๊ฐ 0 ์ด๊ณ , ํ๋๋ผ๋ ๋ค๋ฅด๋ฉด ๊ทธ ๋นํธ๊ฐ ์ผ์ ธ์ r7 ์ ๋์ด๋ถ๋๋ค.
ํ ๋ฒ ์ผ์ง ๋นํธ๋ OR ๋ก๋ง ์์ด๋ ์ ๋ ์ ๊บผ์ง๋ค.
๊ทธ๋ฌ๋ r7 == 0 ์ 68๊ฐ ๋ธ๋ก์ด ์ ๋ถ ์๋ ๋์ ๋ง์กฑํ๋ค๋ ๋ป์ด๋ค.
(c & A) == B ๊ทธ๋ฆฌ๊ณ (c | A) == C๐ ์ฝ์ง
โถ๐ ์ฝ์ง 1 โ AND ์ ์ฝ๋ง ๋ณด๊ณ ๊ธ์๋ฅผ ์ ํ๋ ค ํ๋ค
๋ธ๋ก์ ์ฒ์ ์ฝ์์ ๋ (c & A) == B ๊ฐ ๋์ ๋จผ์ ๋ค์ด์๋ค. AND ๋ง์คํฌ์ ์์๋ฅผ ๋ง์ถ๋
์ ํ์ ์ธ ๊ผด์ด๋ผ ์ฌ๊ธฐ์ ๋ต์ด ๋์ฌ ์ค ์์๋ค.
์ ๋์จ๋ค. A ๊ฐ 0 ์ธ ๋นํธ์์๋ (c & A) ๊ฐ c ์ ๋ฌด๊ดํ๊ฒ 0 ์ด๋ผ, ๊ทธ ์๋ฆฌ c ๋
์๋ฌด ๊ฐ์ด์ด๋ ์ ์ฝ์ ํต๊ณผํ๋ค. ์ฒซ ๋ธ๋ก๋ง ๋ด๋ A = 0xf3 ์ด๋ 0 ์ธ ๋นํธ๊ฐ ๋, ํ๋ณด๊ฐ ๋ท์ด๋ค.
์ผ๋ง๋ ํผ์ง๋์ง ์ค์ ๋ก ์ธ์ด ๋ดค๋ค.
#!/usr/bin/env python3
"""์ฝ์ง ์ฌํ โ AND ์ ์ฝ ํ๋๋ง ๋ณด๋ฉด ๊ธ์๊ฐ ์ ์ ํด์ง๋ค."""
from solve import consts
total = 1
for i, (A, B, C) in enumerate(consts):
free = bin((~A) & 0xFF).count("1") # A ๊ฐ 0 ์ธ ๋นํธ = ๋ฏธ์ ๋นํธ
total *= 1 << free
if i < 6:
print("๋ธ๋ก %2d: A=0x%02x B=0x%02x C=0x%02x ๋ฏธ์ ๋นํธ %d๊ฐ -> ํ๋ณด %3d ๊ฐ"
% (i, A, B, C, free, 1 << free))
print("...")
print("AND ์ ์ฝ๋ง ์ฐ๋ฉด ์ ์ฒด ํ๋ณด ์ = 2^%d" % (total.bit_length() - 1))
print("OR ์ ์ฝ๊น์ง ๊ฑธ์ด์ผ c = (A & B) | (~A & C) ๋ก ์ ์ผํด์ง๋ค")python3 naive_and_only.py
2^272. ๋ธ๋ฃจํธํฌ์ค๋ก ๊ฐ ๊ธธ์ด ์๋๋ค.
์ฌ๊ธฐ์ ๋ฐฉํฅ์ ์๋ชป ์ก์ผ๋ฉด SMT ์๋ฒ๋ฅผ ๊บผ๋ด๊ฒ ๋๋ค. ์ค์ ๋ก z3 ์ ๋ ์ ์ฝ์ ๋ค ๋ฃ์ผ๋ฉด ํ๋ฆฌ๊ธฐ๋ ํ๋ค. ํ์ง๋ง ๊ทธ๊ฑด ํ์ ์๋ ๋๊ตฌ๋ค. ๋ค์ OR ์ ์ฝ์ด ์ ํํ ์ด ๋น์นธ๋ง ๋ฉ์ฐ๋๋ก ์ง์ฌ ์๋ค๋ ๊ฑธ ์์์ฑ๋ฉด ์ฐ์ ๋ ๋ฒ์ผ๋ก ๋๋๋ค.
โถ๐ ์ฝ์ง 2 โ ๋ธ๋ก ์์๋๋ก ์ด์ด๋ถ์๋๋ flag ๊ฐ ๋ค์งํ ๋์๋ค
0x28 POP ์ "์
๋ ฅ ํ ๊ธ์๋ฅผ ๊ฐ์ ธ์จ๋ค" ์ ๋๋ก ์ฝ๊ณ ๋์ด๊ฐ๋ฉด ๊ฑธ๋ฆฐ๋ค. ํธ๋ค๋ฌ๋ฅผ ๋ค์ ๋ณด๋ฉด
๋ถ๋ช
ํ ์คํ์ด๋ค.
// 0x1315 โ pop()
sp--;
return stack[sp];0x22 READ ๊ฐ 68๊ธ์๋ฅผ ์์๋๋ก push ํ๊ณ , 0x28 POP ์ ๊ทธ ๋ฐ๋์ชฝ ๋๋ถํฐ ๊บผ๋ธ๋ค.
์ฒซ ๋ฒ์งธ ๊ฒ์ฌ ๋ธ๋ก์ด ์๋ํ๋ ๊ฑด ์
๋ ฅ์ ๋ง์ง๋ง ๊ธ์๋ค.
๋ธ๋ก ์์ ๊ทธ๋๋ก ์ด์ด๋ถ์ด๋ฉด ์ด๋ป๊ฒ ๋๋์ง ์ฐ์ด ๋ดค๋ค.
#!/usr/bin/env python3
"""์ฝ์ง ์ฌํ โ POP ์ด ์คํ์ด๋ผ๋ ๊ฑธ ๋์น๋ฉด ์ด๋ป๊ฒ ๋๋."""
from solve import consts # solve.py ์ ํ์ฑ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฌ์ฉ
chars = [(A & B) | (~A & C) & 0xFF for A, B, C in consts]
print("๋ธ๋ก ์์ ๊ทธ๋๋ก :", bytes(chars).decode())
print("๋ค์ง์ ๊ฒ :", bytes(reversed(chars)).decode())python3 naive_order.py
}59e0d0d2...cb{HD. ๋คํํ ์ด๊ฑด ํฐ๊ฐ ํ ๋๋ค. flag ๊ฐ DH{ ๋ก ์์ํ๋ค๋ ๊ฑธ ์๊ณ ์์ผ๋
๊ฑฐ์ธ์์ด ๋์ค๋ฉด ๋ฐ๋ก ์์์ฑ๋ค. ์ฒซ ๋ธ๋ก์ ์์ ์
(A=0xf3 B=0x71 C=0xff)์ ์์ผ๋ก ํ์ด
0x7d = } ๊ฐ ๋์ค๋ ๊ฑธ ํ์ธํ๋ ๊ฒ ๋ ๋น ๋ฅธ ํ์ ์ด์๋ค.
flag ํฌ๋งท์ ๋ชจ๋ฅด๋ ๋ฌธ์ ์๋ค๋ฉด ์ฌ๊ธฐ์ ํ์ฐธ ํค๋งธ์ ๊ฒ์ด๋ค.
๐ฃ ํต์ฌ โ ๋ ์ ์ฝ์ด ์๋ก์ ์ฌ๊ฐ์ง๋๋ฅผ ์ ํํ ๋ฉ์ด๋ค
(c & A) == B ์ (c | A) == C ๋ฅผ ๋นํธ ๋จ์๋ก ์ชผ๊ฐ๋ฉด ๊ฒฝ์ฐ๊ฐ ๋๋ฟ์ด๋ค.
A ์ ๊ทธ ๋นํธ๊ฐ 1 ์ธ ๊ฒฝ์ฐ. AND ๋ c ๋ฅผ ๊ทธ๋๋ก ํต๊ณผ์ํจ๋ค. (c & A) ์ ๊ทธ ์๋ฆฌ๋ c ์ด๋ฏ๋ก
c ๋นํธ = B ๋นํธ๋ก ๋ชป ๋ฐํ๋ค. ๋ฐ๋ฉด OR ์ ๋ฌด์กฐ๊ฑด 1 ์ ๋ง๋ค์ด c ์ ๋ํด ์๋ฌด๊ฒ๋ ์ ์๋ ค ์ค๋ค.
(๊ทธ๋์ A ๊ฐ 1 ์ธ ์๋ฆฌ์์ C ๊ฐ 0 ์ด๋ฉด ๊ทธ ๋ธ๋ก์ ์ ์ด์ ๋ง์กฑ ๋ถ๊ฐ๋ฅ์ด๋ค.)
A ์ ๊ทธ ๋นํธ๊ฐ 0 ์ธ ๊ฒฝ์ฐ. ์ด๋ฒ์ ๋ฐ๋๋ค. AND ๋ ๋ฌด์กฐ๊ฑด 0 ์ด๋ผ c ์ ๋ฌด๊ดํ๊ณ , OR ์ด
c ๋ฅผ ๊ทธ๋๋ก ํต๊ณผ์์ผ c ๋นํธ = C ๋นํธ๊ฐ ๋๋ค.
(๋ง์ฐฌ๊ฐ์ง๋ก A ๊ฐ 0 ์ธ ์๋ฆฌ์์ B ๊ฐ 1 ์ด๋ฉด ๋ง์กฑ ๋ถ๊ฐ๋ฅ์ด๋ค.)
ํ๋ก ์ ๋ฆฌํ๋ฉด ์ฌ๊ฐ์ง๋๊ฐ ์ด๋ป๊ฒ ์๊ฐ๋ฆฌ๋์ง๊ฐ ํ๋์ ๋ณด์ธ๋ค.
A ๋นํธ | (c & A) ๊ฐ ์๋ ค ์ฃผ๋ ๊ฒ | (c | A) ๊ฐ ์๋ ค ์ฃผ๋ ๊ฒ | c ๋นํธ ๊ฒฐ์ |
|---|---|---|---|
| 1 | c ๊ทธ๋๋ก โ B ๋นํธ์ ๊ฐ์์ผ ํจ | ํญ์ 1 (์ ๋ณด ์์) | B ์์ |
| 0 | ํญ์ 0 (์ ๋ณด ์์) | c ๊ทธ๋๋ก โ C ๋นํธ์ ๊ฐ์์ผ ํจ | C ์์ |
์ด๋ ๋นํธ๋ฅผ ์ก์๋ ๋ ์ค ์ ํํ ํ๋๊ฐ ๊ฐ์ ํ์ ํ๋ค. ๊ฒน์น์ง๋ ์๊ณ ๋น์ง๋ ์๋๋ค.
c = (A & B) | (~A & C)์ฒซ ๋ธ๋ก์ ์์๋ก ๊ทธ๋ ค ๋ณด๋ฉด ์ด๋ ๊ฒ ๋๋ค.

A = 0xf3 ์ 1111 0011. 1 ์ธ ์ฌ์ฏ ์๋ฆฌ๋ B = 0x71 ์์, 0 ์ธ ๋ ์๋ฆฌ๋ C = 0xff ์์
๊ฐ์ ธ์จ๋ค. ์กฐ๋ฆฝํ๋ฉด 0111 1101 = 0x7d = }. ์ฒซ ๋ธ๋ก์ด ๋ง์ง๋ง ๊ธ์๋ฅผ ๊ฒ์ฌํ๋ค๋ ๊ฒ๊ณผ
์ ํํ ๋ง์๋จ์ด์ง๋ค.
์ถ์ ์ ์ ์ฅ์์ ๋ณด๋ฉด ์ด ๊ตฌ์กฐ๋ ์์ฐ์ค๋ฝ๋ค. ๋น๊ต ๋ช ๋ น์ด ์๋ VM ์์ "์ด ๊ธ์๊ฐ ์ ํํ ์ด ๊ฐ์ธ๊ฐ"๋ฅผ ๋ฌผ์ผ๋ ค๋ฉด, ํ ๋ฐฉํฅ ๋ง์คํฌ ํ๋๋ก๋ ๋ถ์กฑํ๊ณ ์ ๋ณด๋ฅผ ์๋ ๋ฐฉํฅ์ด ์๋ก ๋ฐ๋์ธ ์ฐ์ฐ ๋ ๊ฐ๋ฅผ ๊ฒน์ณ์ผ ํ๋ค. AND ์ OR ์ด ๋ฑ ๊ทธ ์ง์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ง์ ์ญ์ฐ๋ ๋์นญ์ผ๋ก ์ด๋ฆฐ๋ค. ๋ง์ผ๋ ค๊ณ ๋ฃ์ ๊ตฌ์กฐ๊ฐ ๊ทธ๋๋ก ์ฌ๋ ์ด์ ๊ฐ ๋๋ค.
์ด๋์ ๋ธ๋ฃจํธํฌ์ค๋ ์๋ฒ๋ ํ์ ์๋ค. 68๊ธ์ ร ์ฐ์ ๋ ๋ฒ, ๋์ด๋ค.
๐ฏ ๋ฐํ์ ๊ฒ์ฆ โ gdb ๋ก r7 ์ ์ง์ ๋ณธ๋ค
์ ์ ๋ถ์๋ง์ผ๋ก "r7 ์ด ๋์ ๊ธฐ๊ณ 0 ์ด์ด์ผ ํ๋ค"๊น์ง ์๋ค. ์ค์ ๋ก ๊ทธ๋ฐ์ง๋ ์คํ ์ค์
๊ทธ ๋ฐ์ดํธ๋ฅผ ๋์ผ๋ก ๋ด์ผ ํ์คํ๋ค.
๋ฐ์ด๋๋ฆฌ๊ฐ stripped PIE ๋ผ ๋ธ๋ ์ดํฌํฌ์ธํธ ์ฃผ์๋ฅผ ์์ผ๋ก ์ธ ์ ์๋ค. ๋ก๋ ๋ฒ ์ด์ค๋ฅผ ๊ด๋ก๋ก
๊ฐ์ ํ๋ ๊ฒ๋ ์ํํด์(ํ๊ฒฝ์ ๋ฐ๋ผ ์ด๊ธ๋๋ค), starti ๋ก ํ ๋ฒ ๋์ด ๋ค info proc mappings
์์ ์ค์ ์ฃผ์๋ฅผ ์ฝ์ด ๊ณ์ฐํ๋ค.
set confirm off
set pagination off
set disable-randomization on
# stripped + PIE ๋ผ ๋ก๋ ๋ฒ ์ด์ค๋ฅผ ๊ด๋ก๋ก ๊ฐ์ ํ์ง ์๋๋ค.
# starti ๋ก ํ ๋ฒ ๋์ด ๋ค info proc mappings ์์ ์ค์ ์ฃผ์๋ฅผ ์ฝ์ด ๋ธ๋ ์ดํฌํฌ์ธํธ๋ฅผ ๊ฑด๋ค.
starti extracted/command
python
import gdb
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("/chall"))
print("PIE base = 0x%x" % base)
print("run() ๋์คํจ์น ๋ฃจํ = 0x%x" % (base + 0x1364))
# 0x138f = 0x20(HALT) ๋ถ๊ธฐ ์, r0 ๋ฅผ ๋ฐํ๊ฐ์ผ๋ก ์ฝ์ด๋ค์ด๋ ์๋ฆฌ
gdb.execute("break *0x%x" % (base + 0x138f))
end
printf "\n===== [1] ๋ณต์ํ flag ๋ฅผ ๋ฃ์์ ๋ =====\n"
run extracted/command < flag.txt
x/8xb $rbp-0x10
printf "r7(๋์ ๋ถ์ผ์น) = 0x%02x -> r0(๋ฐํ๊ฐ) = 0x%02x\n", *(unsigned char*)($rbp-0x9), *(unsigned char*)($rbp-0x10)
continue
printf "\n===== [2] ๋์กฐ๊ตฐ: DH{aaa...a} ๋ฅผ ๋ฃ์์ ๋ =====\n"
run extracted/command < wrong.txt
x/8xb $rbp-0x10
printf "r7(๋์ ๋ถ์ผ์น) = 0x%02x -> r0(๋ฐํ๊ฐ) = 0x%02x\n", *(unsigned char*)($rbp-0x9), *(unsigned char*)($rbp-0x10)
printf "main ์ ์ด ๋ฐํ๊ฐ์ด 0 ์ผ ๋๋ง Correct!!! ๋ฅผ ์ถ๋ ฅํ๋ค\n"flag.txt ๋ ๋ณต์ํ flag, wrong.txt ๋ ๊ฐ์ ๊ธธ์ด์ DH{aaa...a} ๋ฅผ ๊ฐํ ์์ด ๋ด์ ํ์ผ์ด๋ค.
gdb -q -batch -x verify.gdb ./extracted/chall 2>&1 | grep -v "Thread debugging\|libthread_db"
๋ ์ง์คํฐ ํ์ผ 8๋ฐ์ดํธ๊ฐ ๊ทธ๋๋ก ์ฐํ๋ค. ์ ๋ต ์
๋ ฅ์์๋ r0 ๋ถํฐ r7 ๊น์ง
00 14 04 54 54 00 00 00 โ ๋งจ ๋ r7 ์ด 0x00 ์ด๊ณ , ๊ทธ๊ฒ r0 ์ ๋ณต์ฌ๋ผ ๋ฐํ๋๋ค.
๋์กฐ๊ตฐ์์๋ 5f 14 04 54 54 00 00 5f ๋ก r7 ์ด 0x5f ๋ค.
0x5f = 0101 1111. 68๊ธ์๋ฅผ ํ๋ ๋์ ์ฌ์ฏ ๊ฐ ๋นํธ ์๋ฆฌ์์ ๋ถ์ผ์น๊ฐ ๋ฌ๋ค๋ ๋ป์ด๋ค.
๋์ ๊ธฐ๊ฐ ์ง์ง OR ๋ก๋ง ์์ธ๋ค๋ ๊ฒ๋ ์ด ๊ฐ์ผ๋ก ํ์ธ๋๋ค โ ๊ฐ๋ณ ๋ธ๋ก์ ์ฐจ์ด๊ฐ ๊ฒน์ณ ๋์ด๋ถ์
ํฉ์งํฉ์ด๋ค.
์ค๊ฐ์ r1~r4 ์ ๋จ์ ๊ฐ(14 04 54 54)์ ๋ง์ง๋ง ๋ธ๋ก์ ์์ฌ๋ฌผ์ด๋ค. ๋์ค์ด์
๋ธ ๊ฒฐ๊ณผ
๋งจ ๋ ๋ธ๋ก์ด SETI r1, 0x14 / SETI r2, 0x04 / SETI r3, 0x54 ์์ผ๋ ์ ํํ ์ผ์นํ๋ค.
์ ์ ์ผ๋ก ์ฝ์ ๊ฒ๊ณผ ์ค์ ์คํ์ด ์ด๊ธ๋์ง ์์๋ค๋ ๊ต์ฐจ ํ์ธ์ด ํ๋ ๋ ๋ถ์ ์
์ด๋ค.
๐ Full Exploit
๋์ค์ด์
๋ธ๋ฌ์ ๊ฐ์ ๋ฐฉ์์ผ๋ก command ๋ฅผ ํ๋, ์ด๋ฒ์ SETI r1/r2/r3 ์ธ ๊ฐ๊ฐ ๋ชจ์ผ ๋๋ง๋ค
์์ ๋ฌถ์์ ํ๋์ฉ ๋ฝ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ณต์์ ์ ์ฉํ ๋ค ๋ค์ง๋๋ค.
#!/usr/bin/env python3
"""bitvm (Dreamhack, Gold 4 / reversing) - flag ์ญ์ฐ.
command ๋ฐ์ดํธ์ฝ๋๋ ๊ฒ์ฌ ๋ธ๋ก ํ๋๋ฅผ ์ด๋ ๊ฒ ๋ง๋ ๋ค.
SETI r1, A ; SETI r2, B ; SETI r3, C
POP ; r0 = ์
๋ ฅ ํ ๊ธ์ c (์คํ์ด๋ผ LIFO)
AND r4,r0,r1 ; XOR r5,r2,r4 ; OR r7,r5,r7 ; (c & A) ^ B ๋ฅผ r7 ์ ๋์
OR r4,r0,r1 ; XOR r5,r3,r4 ; OR r7,r5,r7 ; (c | A) ^ C ๋ฅผ r7 ์ ๋์
๋ง์ง๋ง์ MOV r0, r7 / HALT ์ด๊ณ main ์ r0 == 0 ์ผ ๋๋ง "Correct!!!" ๋ฅผ ์ถ๋ ฅํ๋ค.
์ฆ ๋ชจ๋ ๋ธ๋ก์ด ์๋ ๋์ ๋์์ ๋ง์กฑํด์ผ ํ๋ค.
(c & A) == B and (c | A) == C
๋นํธ๋ณ๋ก ๋ณด๋ฉด
A_i = 1 -> (c&A)_i = c_i = B_i, (c|A)_i = 1 = C_i
A_i = 0 -> (c&A)_i = 0 = B_i, (c|A)_i = c_i = C_i
๋ฐ๋ผ์ c = (A & B) | (~A & C) ๋ก ์ ์ผํ๊ฒ ๊ฒฐ์ ๋๋ค.
"""
import sys
PATH = sys.argv[1] if len(sys.argv) > 1 else "extracted/command"
code = open(PATH, "rb").read()
# --- 1) ๋ฐ์ดํธ์ฝ๋์์ (A, B, C) 3๊ฐ์ง๋ฆฌ SETI ๋ฌถ์์ ์์๋๋ก ๋ฝ๋๋ค ---
consts = []
pc = 0
pending = {}
while pc < len(code):
op = code[pc]
if op == 0x20: # HALT
break
elif op == 0x21: # PUTC imm
pc += 2
elif op == 0x22 or op == 0x28: # READ / POP
pc += 1
elif op == 0x23: # SETI r, imm
pending[code[pc + 1]] = code[pc + 2]
pc += 3
elif op == 0x24: # MOV d, s
pc += 3
elif op in (0x25, 0x26, 0x27): # AND / OR / XOR d, a, b
pc += 4
elif op == 0x29: # PUSH r
pc += 2
else:
raise SystemExit("unknown opcode 0x%02x at 0x%x" % (op, pc))
# r1,r2,r3 ์ด ๋ค ์ฑ์์ง๋ฉด ํ ๋ธ๋ก์ ์์ ์ธํธ๊ฐ ์์ฑ๋ ๊ฒ
if {1, 2, 3} <= pending.keys():
consts.append((pending.pop(1), pending.pop(2), pending.pop(3)))
# --- 2) ๋นํธ ์ ์ฝ์ ํ์ด ๊ธ์๋ฅผ ๋ณต์ํ๋ค ---
chars = []
for i, (A, B, C) in enumerate(consts):
if B & ~A & 0xFF: # A=0 ์ธ ๋นํธ์์ B ๋ ๋ฐ๋์ 0
raise SystemExit("๋ธ๋ก %d ๋ชจ์: B ์ A=0 ๋นํธ๊ฐ ์ผ์ ธ ์์" % i)
if (~C & A) & 0xFF: # A=1 ์ธ ๋นํธ์์ C ๋ ๋ฐ๋์ 1
raise SystemExit("๋ธ๋ก %d ๋ชจ์: C ์ A=1 ๋นํธ๊ฐ ๊บผ์ ธ ์์" % i)
c = (A & B) | (~A & C) & 0xFF
assert (c & A) == B and (c | A) == C, i
chars.append(c)
# POP ์ LIFO โ ์ฒซ ๋ธ๋ก์ด ๋ง์ง๋ง ์
๋ ฅ ๊ธ์๋ฅผ ๊ฒ์ฌํ๋ค
flag = bytes(reversed(chars))
if __name__ == "__main__":
print("๊ฒ์ฌ ๋ธ๋ก %d ๊ฐ" % len(consts))
print("FLAG:", flag.decode())๋ ๊ตฐ๋ฐ๋ฅผ ์ผ๋ถ๋ฌ ๋ฐฉ์ด์ ์ผ๋ก ์งฐ๋ค.
๋ชจ์ ๊ฒ์ฌ ๋ ์ค์ "์ด ๊ตฌ์กฐ๊ฐ ์ ๋ง ๋ด๊ฐ ์ฝ์ ๊ทธ ๊ตฌ์กฐ์ธ๊ฐ"๋ฅผ ์ค์ค๋ก ํ์ธํ๋ ์ฅ์น๋ค.
A ๊ฐ 0 ์ธ ์๋ฆฌ์ B ๋นํธ๊ฐ ์ผ์ ธ ์๊ฑฐ๋ A ๊ฐ 1 ์ธ ์๋ฆฌ์ C ๋นํธ๊ฐ ๊บผ์ ธ ์์ผ๋ฉด
๊ทธ ๋ธ๋ก์ ์ด๋ค c ๋ก๋ ๋ง์กฑ๋ ์ ์๋ค. 68๋ธ๋ก ์ ๋ถ ์ ๊ฑธ๋ ธ์ผ๋ ํด์์ด ๋ง์ ๊ฒ์ด๋ค.
assert ๋ ๋ณต์ํ ๊ธ์๋ฅผ ์๋ ์ ์ฝ์ ๋๋ก ๋ฃ์ด ๋ณธ๋ค. ๊ณต์์ ์ฎ๊ฒจ ์ ๋ค ํ๋ฆฌ๋ฉด ์ฌ๊ธฐ์ ์ฃฝ๋๋ค.
python3 solve.py extracted/command
DH{bc42e3abb217fa4fd8b8991bc5a378d7dd4421fd887feb797eb759ca2d0d0e95}.
๋ง์ง๋ง์ ๋ฐฐํฌ๋ ๋ฐ์ด๋๋ฆฌ์ ์ง์ ๋จน์ฌ ํ์ ์ ๋ฐ๋๋ค. ์ค๋ต ๋์กฐ๊ตฐ์ ๊ฐ์ด ๋๋ ค์ผ "๋ญ ๋ฃ์ด๋ Correct ๊ฐ ๋์ค๋ ๊ฒ ์๋๊ฐ"๋ผ๋ ์์ฌ์ด ์ ๋ฆฌ๋๋ค.
#!/usr/bin/env python3
"""๋ณต์ํ flag ๋ฅผ ์ค์ chall + command ์ ๋จน์ฌ ํ์ ์ ํ์ธํ๋ค."""
import subprocess
FLAG = "DH{bc42e3abb217fa4fd8b8991bc5a378d7dd4421fd887feb797eb759ca2d0d0e95}"
WRONG = "DH{" + "a" * 64 + "}"
for label, s in (("๋ณต์ํ flag", FLAG), ("๋์กฐ๊ตฐ(์ ๋ถ a)", WRONG)):
r = subprocess.run(["./chall", "command"], input=s,
capture_output=True, text=True, cwd="extracted")
print("[%s] %s" % (label, s))
print(" -> %s" % r.stdout.strip().splitlines()[-1])python3 verify.py
๐ ์ฌํ
๋ฌธ์ ํด๋์ ๋ฃ์ด ๋ ํ ๋ฐฉ ์คํฌ๋ฆฝํธ๋ค. ์ญ์ฐ โ flag ์ถ๋ ฅ โ ๋ฐ์ด๋๋ฆฌ ํ์ ๊น์ง ์ค์ค๋ก ๋์กฐํ๊ณ
โ
PASS / โ FAIL ๋ก ๋๋๋ค. ์คํ๋ผ์ธ ๋ฌธ์ ๋ผ ์๋ฒ๊ฐ ํ์ ์๊ณ , ๊ฒฐ์ ๋ก ์ ์ด๋ผ ํ ๋ฒ์ด๋ฉด ๋๋ค.
#!/usr/bin/env bash
# bitvm (DreamHack Gold 4 / reversing) โ ํ ๋ฐฉ ์ฌํ.
# command ๋ฐ์ดํธ์ฝ๋ ์ญ์ฐ -> flag ์ถ๋ ฅ -> ์ค์ chall ๋ฐ์ด๋๋ฆฌ๋ก ํ์ ๊น์ง ํ์ธ.
# ์คํ๋ผ์ธ ๋ฌธ์ ๋ผ ๋ผ์ด๋ธ ์๋ฒ๊ฐ ํ์ ์๋ค. ๊ฒฐ์ ๋ก ์ ์ด๋ผ 1ํ ์คํ์ด๋ฉด ๋๋๋ค.
set -eu
cd "$(dirname "$(readlink -f "$0")")"
EXPECT=$(python3 -c "import json;print(json.load(open('๋ฌธ์ .json'))['flag'])" 2>/dev/null || cat flag.txt 2>/dev/null || echo '')
chmod +x extracted/chall
OUT=$(timeout 120 python3 solve.py extracted/command 2>&1) || true
echo "$OUT"
FLAG=$(printf '%s' "$OUT" | grep -aoE 'DH\{[^}]+\}' | head -1)
if [ -z "$FLAG" ]; then
echo; echo "โ FAIL (solve.py ๊ฐ flag ๋ฅผ ๋ฝ์ง ๋ชปํ๋ค)"; exit 1
fi
# ๋ฐ์ด๋๋ฆฌ์ ์ง์ ๋จน์ฌ Correct!!! ๊ฐ ๋์ค๋์ง ๋
๋ฆฝ ๊ฒ์ฆ
JUDGE=$(printf '%s' "$FLAG" | (cd extracted && ./chall command) | tail -1)
echo "ํ์ : $JUDGE"
if echo "$JUDGE" | grep -q 'Correct!!!' && { [ -z "$EXPECT" ] || [ "$FLAG" = "$EXPECT" ]; }; then
echo; echo "โ
PASS $FLAG"; exit 0
fi
echo; echo "โ FAIL (์ป์ ๊ฐ: '${FLAG:-์์}' / ๊ธฐ๋: '$EXPECT' / ํ์ : '$JUDGE')"; exit 1๋ก์ปฌ ํ๊ฒฝ์ ํน๋ณํ ๊ฒ ์๋ค. python3 ์ objdump(binutils), ๋ฐํ์ ๊ฒ์ฆ์ฉ gdb ๋ฉด ๋๋ค.
chall ์ ํ๋ฒํ ๋์ ๋งํฌ x86-64 ELF ๋ผ ์์ฆ ๋ฐฐํฌํ์์ ๊ทธ๋๋ก ๋๋ค.
๐ ๊ฒฐ๋ก
์๋ ๋ช ๋ น์ด ๊ณง ์ค๊ณ๋๋ค.
VM ๋ฌธ์ ๋ฅผ ๋ง๋๋ฉด ๋ณดํต "๋ฌด์จ ๋ช ๋ น์ด ์๋"๋ถํฐ ์ผ๋ค. ์ด๋ฒ์ ๋ฐ๋์ชฝ์ด ํจ์ฌ ๋ง์ ๊ฑธ ์๋ ค ์คฌ๋ค. ๋ถ๊ธฐ๊ฐ ์๋ค๋ ๊ฑด ์ฝ๋๊ฐ ์ง์ ์ด๋ผ๋ ๋ป์ด๊ณ , ์ง์ ์ด๋ฉด 68๊ธ์ ๊ฒ์ฌ๋ 68๋ฒ ํผ์ณ์ง ๋ฐ๋ณต ํจํด์ผ ์๋ฐ์ ์๋ค. ๋น๊ต๊ฐ ์๋ค๋ ๊ฑด "๊ฐ๋ค"๋ฅผ XOR ๋ก ํํํ๋ค๋ ๋ป์ด๊ณ , XOR ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์ผ๋ ค๋ฉด OR ๋์ ๊ธฐ ๋ง๊ณ ์ธ ๊ฒ ์๋ค. ๋ช ๋ น ํ๋ฅผ ๋ค ๋ฝ๊ธฐ๋ ์ ์ ์์ชฝ ๊ตฌ์กฐ๊ฐ ์ ๋ฐ์ ์์ธก๋๋ค.
์ ๋ณด๋ฅผ ์๋ ์ฐ์ฐ๋ ์ง์ ์ง์ผ๋ฉด ์ ๋ณด๋ฅผ ์์ง ์๋๋ค.
c & A ๋ A ๊ฐ 0 ์ธ ๋นํธ๋ฅผ ๋ฒ๋ฆฌ๊ณ , c | A ๋ A ๊ฐ 1 ์ธ ๋นํธ๋ฅผ ๋ฒ๋ฆฐ๋ค. ๊ฐ๊ฐ์ ๋๋๋ฆด ์
์์ง๋ง ๋ฒ๋ฆฌ๋ ์๋ฆฌ๊ฐ ์ ํํ ๋ฐ๋๋ผ์, ๋์ ๊ฒน์น๋ฉด ์๋ณธ์ด ๊ทธ๋๋ก ๋ณต์๋๋ค.
๊ฒ์ฌ๋ฅผ ํผํผํ๊ฒ ๋ง๋ค๋ ค๊ณ ์ ์ฝ์ ํ๋ ๋ ์น์ ๊ฒ์ด ์คํ๋ ค ๋ต์ ์ ์ผํ๊ฒ ๋ง๋ค์ด ๋ฒ๋ ธ๋ค.
๋๋
ํ์์ ์์ฃผ ๋ณด๋ ์ญ์ค์ด๋ค.
๋ง๋ค ๋๋ ์ด๋ฐ ๊ฒ์ฌ๋ฅผ ์ฐ์ง ๋ง ๊ฒ.
๋ฐฉ์ด ๊ด์ ์์ ๋ณด๋ฉด ์ด๊ฑด ํด๋ผ์ด์ธํธ์ ์ ๋ต์ ๊ทธ๋๋ก ๋ค๊ณ ์๋ ๊ตฌ์กฐ๋ค. ๊ฒ์ฌ์ ์์๊ฐ
command ํ์ผ์ ํ๋ฌธ์ผ๋ก 68๋ฒ ๋์ด๋ผ ์๊ณ , ๊ฒ์ฌ๊ฐ ๊ธ์ ๋จ์๋ก ๋
๋ฆฝ์ ์ด๋ผ ์๋ก ์์ด์ง๋ ์๋๋ค.
์ค๋ฌด์์ ์
๋ ฅ ๊ฒ์ฆ์ ์ด๋ ๊ฒ ์ง๋ฉด ์ ๋๋ค. ์ต์ํ ๊ฒ์ฌ๊ฐ์ ์๋ณธ์ด ์๋๋ผ ํด์๋ก ๋๊ณ ,
๊ธ์๋ณ๋ก ์ชผ๊ฐ์ง ๋ง๊ณ ์ ์ฒด๋ฅผ ํ ๋ฉ์ด๋ฆฌ๋ก ๋ฌถ์ด์ผ ํ ๊ธ์์ฉ ๋ผ์ด๋ด๋ ๊ณต๊ฒฉ์ด ๋งํ๋ค.
stripped PIE ๋ฅผ gdb ๋ก ๋ถ์ผ ๋๋ ๋ฒ ์ด์ค๋ฅผ ์ฝ์ด์ ์ด๋ค.
0x555555554000 ์ ๊ด๋ก์ผ ๋ฟ ๋ณด์ฅ์ด ์๋๋ค. starti ๋ก ๋์ด ๋ค์ info proc mappings ์์
์ค์ ๊ฐ์ ๋ฝ์ ์คํ์
์ ๋ํ๋ ๋ช ์ค์ด๋ฉด, ํ๊ฒฝ์ด ๋ฐ๋์ด๋ ๊ฐ์ ์คํฌ๋ฆฝํธ๊ฐ ๊ทธ๋๋ก ๋๋ค.
์ด๋ฒ ๊ฒ์ฆ ์คํฌ๋ฆฝํธ๊ฐ ๊ทธ๋ ๊ฒ ๋ผ ์๊ณ , ๋๋ถ์ ๋ธ๋ ์ดํฌํฌ์ธํธ๋ฅผ ์์ผ๋ก ๊ณ ์น ์ผ์ด ์์๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ