PlaidCTF 2017 write-up
4/22-23に開催されたPlaidCTF 2017で解けた問題のwrite-upです。
チーム名 CTFinfinitとして参加し、226pts獲得して185位でした。
- sanity check (Misc/1pts)
- zipper (Misc/50pts)
- Down the Reversing Hole (Misc/50pts)
- no_mo_flo (Reversing/125pts)
sanity check (Misc/1pts)
The flag is PCTF{poop}
テスト問題なので、フラグ投稿するだけ。
zipper (Misc/50pts)
Something doesn't seem quite right with this zip file. Can you fix it and get the flag?
とりあえずzipinfoでは "filename too long" と出力される。
ubuntu@zipper$ zipinfo zipper.zip Archive: zipper.zip Zip file size: 236 bytes, number of entries: 1 warning: filename too long--truncating. -rw-rw-r-- 3.0 unx 246 tx defX 17-Apr-18 19:15
バイナリエディタで眺めてみると、0x2329 となっていて確かにファイル名長さの値が大きすぎる。また、ファイル名が入りそうな領域がすべて 0x00 になっている。
8文字分だったので、適当にファイル名を flag.txt として値を修正。
あとは、修正したファイルを展開して中のファイルを閲覧して終わり。
Huzzah, you have captured the flag: PCTF{f0rens1cs_yay}
Down the Reversing Hole (Misc/50pts)
Don't forget. This is a MISC challenge.
MISCチャレンジということで、実行ファイルに何か隠されているだろうとバイナリを眺めてみたらMZスタブが普通の実行ファイルに比べて大きい。あからさまに何かある。
seg000:0000 public start seg000:0000 start proc near seg000:0000 B8 03 00 mov ax, seg dseg seg000:0003 8E D8 mov ds, ax seg000:0005 assume ds:dseg seg000:0005 8D 16 00 00 lea dx, unk_30 seg000:0009 B4 09 mov ah, 9 ; Write string to STDOUT seg000:000B CD 21 int 21h ; DOS - PRINT STRING seg000:000B ; DS:DX -> string terminated by "$" seg000:000D B9 00 00 mov cx, 0 seg000:0010 seg000:0010 loc_10: seg000:0010 83 F9 39 cmp cx, 39h ; '9' ; 39文字分 seg000:0013 74 17 jz short loc_ seg000:0015 8D 1E 2B 00 lea bx, unk_5B ; 0x25B~ seg000:0019 03 D9 add bx, cx seg000:001B 8A 17 mov dl, [bx] seg000:001D 8D 1E 64 00 lea bx, unk_94 ; 0x294~ seg000:0021 03 D9 add bx, cx seg000:0023 32 17 xor dl, [bx] seg000:0025 B4 02 mov ah, 2 ; Write character to STDOUT seg000:0027 CD 21 int 21h ; DOS - DISPLAY OUTPUT seg000:0027 ; DL = character to send to standard output seg000:0029 41 inc cx seg000:002A EB E4 jmp short loc_10 seg000:002C seg000:002C loc_2C: seg000:002C B4 4C mov ah, 4Ch ; Exit program seg000:002E CD 21 int 21h ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT) seg000:002E start endp ; AL = exit code seg000:002E seg000:002E seg000 ends
フラグを得るプログラムは以下のようになった。
#!/usr/bin/python f = open("reversing-hole.exe", "rb") data = f.read() f.close() f = open("flag.txt", "wb") dl = data[0x25B:0x294] bx = data[0x294:0x2CD] for i in range(len(dl)): t = ord(dl[i]) ^ ord(bx[i]) f.write(chr(t)) f.write("\n") f.close()
ubuntu@misc$ ./solve.py; cat flag.txt But here's a flag PCTF{at_l3a5t_th3r3s_d00m_p0rts_0n_d0s}
no_mo_flo (Reversing/125pts)
Can you go with the flow?
とりあえず、逆アセンブル。
objdump -D -Mintel no_flo > no_flo.asm
mainのアセンブリ。
; main 40272e: 55 push rbp 40272f: 48 89 e5 mov rbp,rsp 402732: 48 81 ec b0 00 00 00 sub rsp,0xb0 402739: 48 8d 45 d0 lea rax,[rbp-0x30] 40273d: ba 20 00 00 00 mov edx,0x20 402742: 48 89 c6 mov rsi,rax 402745: bf 00 00 00 00 mov edi,0x0 40274a: e8 31 de ff ff call 400580 <read@plt> 40274f: 89 45 f8 mov DWORD PTR [rbp-0x8],eax 402752: 83 7d f8 20 cmp DWORD PTR [rbp-0x8],0x20 ; 32文字 402756: 74 14 je 40276c <strerror@plt+0x21ac> ; error; 402758: bf 58 2d 40 00 mov edi,0x402d58 40275d: e8 ee dd ff ff call 400550 <puts@plt> 402762: b8 ff ff ff ff mov eax,0xffffffff 402767: e9 9b 00 00 00 jmp 402807 <strerror@plt+0x2247> ; for 40276c: c7 45 fc 00 00 00 00 mov DWORD PTR [rbp-0x4],0x0 402773: eb 3a jmp 4027af <strerror@plt+0x21ef> 402775: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 402778: 01 c0 add eax,eax 40277a: 48 98 cdqe 40277c: 0f b6 44 05 d0 movzx eax,BYTE PTR [rbp+rax*1-0x30] 402781: 0f be d0 movsx edx,al 402784: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 402787: 48 98 cdqe 402789: 89 94 85 50 ff ff ff mov DWORD PTR [rbp+rax*4-0xb0],edx 402790: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 402793: 01 c0 add eax,eax 402795: 83 c0 01 add eax,0x1 402798: 48 98 cdqe 40279a: 0f b6 44 05 d0 movzx eax,BYTE PTR [rbp+rax*1-0x30] 40279f: 0f be d0 movsx edx,al 4027a2: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 4027a5: 48 98 cdqe 4027a7: 89 54 85 90 mov DWORD PTR [rbp+rax*4-0x70],edx 4027ab: 83 45 fc 01 add DWORD PTR [rbp-0x4],0x1 4027af: 83 7d fc 0f cmp DWORD PTR [rbp-0x4],0xf 4027b3: 7e c0 jle 402775 <strerror@plt+0x21b5> ; } 4027b5: b8 00 00 00 00 mov eax,0x0 4027ba: e8 b8 04 00 00 call 402c77 <strerror@plt+0x26b7> ; [rbp-0xb0]のチェック 4027bf: 48 8d 85 50 ff ff ff lea rax,[rbp-0xb0] 4027c6: 48 89 c7 mov rdi,rax 4027c9: e8 f8 de ff ff call 4006c6 <strerror@plt+0x106> 4027ce: 89 45 f4 mov DWORD PTR [rbp-0xc],eax ; [rbp-0x70]のチェック 4027d1: 48 8d 45 90 lea rax,[rbp-0x70] 4027d5: 48 89 c7 mov rdi,rax 4027d8: e8 3b e7 ff ff call 400f18 <strerror@plt+0x958> 4027dd: 89 45 f0 mov DWORD PTR [rbp-0x10],eax 4027e0: 83 7d f4 00 cmp DWORD PTR [rbp-0xc],0x0 4027e4: 74 12 je 4027f8 <strerror@plt+0x2238> 4027e6: 83 7d f0 00 cmp DWORD PTR [rbp-0x10],0x0 4027ea: 74 0c je 4027f8 <strerror@plt+0x2238> 4027ec: bf 65 2d 40 00 mov edi,0x402d65 ; "Good flow!!" 4027f1: e8 5a dd ff ff call 400550 <puts@plt> 4027f6: eb 0a jmp 402802 <strerror@plt+0x2242> 4027f8: bf 78 2d 40 00 mov edi,0x402d78 ; "You aint goin with the flow...." 4027fd: e8 4e dd ff ff call 400550 <puts@plt> 402802: b8 00 00 00 00 mov eax,0x0 402807: c9 leave 402808: c3 ret
main関数はなんとなくこんな感じ。
readで標準入力から読み込んだデータを2つのバッファへ交互に格納。
f_4006c6()、f_400f18()でそれぞれ入力されたデータのチェック。両関数の戻り値が0でなければ、Good flow!!が出力される流れ。なので、おそらく入力データがフラグになるだろうと予想。
int main( ) { int i; // rbp-0x4 int var_0xc, var_0x10; char buff_0x30[33], buff_0x70[16], buff_0xb0[16]; if (0x20 != read(0, buff_0x30, 0x20)) { return -1; } for (i=0; i<=0xf; i++) { buff_0xb0[i*4] = buff_0x30[2 * i]; buff_0x70[i*4] = buff_0x30[(2 * i) + 1]; } f_402c77(); var_0xc = f_4006c6(buff_0xb0); var_0x10 = f_400f18(buff_0x70); if (var_0xc == 0 || var_0x10 == 0) { puts("You aint goin with the flow...."); } else { puts("Good flow!!"); } return 0; }
f_4006c6の処理は、単純でcmpの結果 je されるルート(比較結果がイコール)になる入力を求める。
関数の返却値は、r8dレジスタに格納され、NGの文字が入力されていた場合、r8dに0が格納される。
: 4006f6: 8b 3a mov edi,DWORD PTR [rdx] ; データ 4006f8: 89 fa mov edx,edi 4006fa: 83 c2 05 add edx,0x5 ; データに5が加わる↓ 4006fd: 89 d7 mov edi,edx 4006ff: 83 ff 55 cmp edi,0x55 ; 0x50 = "P" ↑で5加わっているので、入力は減算 400702: 4c 8d 1c 25 30 07 40 lea r11,ds:0x400730 ; OKの場合の次のアドレス 400709: 00 40070a: 0f 84 00 08 00 00 je 400f10 <strerror@plt+0x950> ; OK 400710: 4c 8d 1c 25 1d 07 40 lea r11,ds:0x40071d ; ↓でjmpして戻ってくるアドレス 400717: 00 400718: e9 f3 07 00 00 jmp 400f10 <strerror@plt+0x950> ; 一旦jmpして戻ってくる 40071d: 41 b8 00 00 00 00 mov r8d,0x0 ; NG 400723: 4c 8d 1c 25 3d 07 40 lea r11,ds:0x40073d :
f_400f18の処理は、f_402c77でsigaction@pltで登録されたシグナル処理を利用してデータチェックをしている。
sigactionでは、SIGFPEのシグナル時の処理として、f_4005a0が登録されている。SIGFPEは浮動小数点例外(0除算処理)時のシグナル。
sigemptyset@plt に渡す前にsa_flagsにSA_SIGINFOを指定しているので、シグナルハンドラーが呼び出される際の引数として、最初の引き数としてシグナル番号を、二番目の引き数として siginfo_t へのポインターを、三番目の引き数として (void * にキャストした) ucontext_t へのポインターを受けとる。*2
402c77: 55 push rbp 402c78: 48 89 e5 mov rbp,rsp 402c7b: 48 81 ec a0 00 00 00 sub rsp,0xa0 402c82: c7 45 e8 04 00 00 00 mov DWORD PTR [rbp-0x18],0x4 ; SA_SIGINFO 402c89: 48 c7 85 60 ff ff ff mov QWORD PTR [rbp-0xa0],0x402b06 402c90: 06 2b 40 00 402c94: 48 8d 85 60 ff ff ff lea rax,[rbp-0xa0] 402c9b: 48 83 c0 08 add rax,0x8 402c9f: 48 89 c7 mov rdi,rax 402ca2: e8 f9 d8 ff ff call 4005a0 <sigemptyset@plt> ; 初期化 402ca7: 48 8d 85 60 ff ff ff lea rax,[rbp-0xa0] 402cae: ba 00 00 00 00 mov edx,0x0 ; struct sigaction *old 402cb3: 48 89 c6 mov rsi,rax ; struct sigaction *act 402cb6: bf 08 00 00 00 mov edi,0x8 ; SIGFPE 402cbb: e8 a0 d8 ff ff call 400560 <sigaction@plt> 402cc0: c9 leave 402cc1: c3 ret
チェック処理は、400f84にあるようにワザと0除算をして、SIGFPEに登録したシグナルハンドラを呼び出している。シグナルハンドラーの結果がOKだったら、0x603340にjmp先のアドレスが返却されてくる。
f_400f18関数の返却値は、esiレジスタに格納され、NGの文字が入力されていた場合、esiに0が格納される。
: 400f37: 8b 02 mov eax,DWORD PTR [rdx] ; データ 400f39: 89 c2 mov edx,eax 400f3b: 83 ea 03 sub edx,0x3 400f3e: 89 d0 mov eax,edx 400f40: 83 f8 40 cmp eax,0x40 ; 0x43 = "C" 400f43: 4c 8d 14 25 28 10 40 lea r10,ds:0x401028 400f4a: 00 400f4b: 49 c7 c3 05 00 00 00 mov r11,0x5 400f52: c7 04 25 28 33 60 00 mov DWORD PTR ds:0x603328,0x1 ; 比較に利用 400f59: 01 00 00 00 400f5d: 48 89 04 25 30 33 60 mov QWORD PTR ds:0x603330,rax ; "C" 400f64: 00 400f65: 48 c7 c0 00 00 00 00 mov rax,0x0 400f6c: 48 89 14 25 38 33 60 mov QWORD PTR ds:0x603338,rdx 400f73: 00 400f74: 48 8d 15 00 00 00 00 lea rdx,[rip+0x0] # 400f7b <strerror@plt+0x9bb> 400f7b: 48 89 14 25 48 33 60 mov QWORD PTR ds:0x603348,rdx 400f82: 00 400f83: 99 cdq 400f84: 48 f7 3c 25 20 33 60 idiv QWORD PTR ds:0x603320 ; 0除算でsigハンドラ呼び出し 400f8b: 00 400f8c: 48 c7 04 25 20 33 60 mov QWORD PTR ds:0x603320,0x0 400f93: 00 00 00 00 00 400f98: 48 8b 04 25 30 33 60 mov rax,QWORD PTR ds:0x603330 400f9f: 00 400fa0: 48 8b 14 25 38 33 60 mov rdx,QWORD PTR ds:0x603338 400fa7: 00 400fa8: 4c 8b 1c 25 40 33 60 mov r11,QWORD PTR ds:0x603340 ; 次のアドレス 400faf: 00 400fb0: 41 ff e3 jmp r11 ; OKなら次の検査 400fb3: be 00 00 00 00 mov esi,0x0 ; NG 400fb8: 4c 8d 14 25 28 10 40 lea r10,ds:0x401028 :
シグナルハンドラーでは、第3引数として受け取ったコンテキスト(ucontext_t へのポインター)のなかのEFLAGSを利用してデータの比較結果を確認していた。
40289a: 55 push rbp 40289b: 48 89 e5 mov rbp,rsp 40289e: 48 89 7d d8 mov QWORD PTR [rbp-0x28],rdi 4028a2: 48 89 75 d0 mov QWORD PTR [rbp-0x30],rsi 4028a6: 48 8b 45 d8 mov rax,QWORD PTR [rbp-0x28] 4028aa: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax 4028ae: 48 8b 05 93 0a 20 00 mov rax,QWORD PTR [rip+0x200a93] # 603348 <strerror@plt+0x202d88> 4028b5: 48 83 c0 38 add rax,0x38 4028b9: 48 89 45 f0 mov QWORD PTR [rbp-0x10],rax 4028bd: 48 8b 45 d0 mov rax,QWORD PTR [rbp-0x30] ; シグナルハンドラー呼び出し時のEFLAGS 4028c1: 83 e0 40 and eax,0x40 ; ZF 結果が0 4028c4: 48 85 c0 test rax,rax 4028c7: 0f 9f c0 setg al 4028ca: 0f b6 c0 movzx eax,al 4028cd: 89 45 ec mov DWORD PTR [rbp-0x14],eax 4028d0: 48 8b 45 d0 mov rax,QWORD PTR [rbp-0x30] 4028d4: 25 00 08 00 00 and eax,0x800 ; OF オーバーフロー 4028d9: 48 85 c0 test rax,rax 4028dc: 0f 9f c0 setg al 4028df: 0f b6 c0 movzx eax,al 4028e2: 89 45 e8 mov DWORD PTR [rbp-0x18],eax 4028e5: 48 8b 45 d0 mov rax,QWORD PTR [rbp-0x30] 4028e9: 25 80 00 00 00 and eax,0x80 ; SF 符号。負の場合。 4028ee: 48 85 c0 test rax,rax 4028f1: 0f 9f c0 setg al :
比較結果の確認のパターンとしては、イコールになるか X+1 > X > X-1 となるXか?の2種類だった(気がする)。例えば、入力文字Xが0x40="@" > X > 0x3e=">"を満たすかどうかをチェックしていた。
Flag: PCTF{n0_fl0?_m0_like_ah_h3ll_n0}