程式安全
Low addr |
---|
text |
data |
heap |
share library text |
share library data |
stack |
High addr |
malloc
/new
)malloc
或new
之前,process是不會有heap segment的glibc
來說使用ptmalloc
ptmalloc
winheap
jemalloc
zone allocator
ptmalloc
Workflow
mmap
的方向走
sys_brk
,修改mm_struct
中的brk
, brk
的值為heap的最後位址(s_brk
的值為heap的base address)主執行緒第一次呼叫malloc
或new
之後,系統會呼叫brk()
給程式分配132kb的heap空間,這132kb的空間叫做Arena
,因為由主執行緒分配的所以叫做main arena
Arena
,每個Arena
中有多個chunk
,這些chunk
用linked list串接起來,linked list的head稱為bin
,之後如果再申請空間會從這132kb中的chunk先分配,不夠再呼叫brk()
來增加空間,或是太多空閒記憶體時呼叫s_brk()
來減少空間Arena header
包含bins
的資訊,top chunk
,last_remainder
等
主執行緒呼叫free
之後,程式的heap空間並不會被釋放掉,而是由glibc
的malloc
函式庫管理,依chunk size將釋放的chunk
新增到main arena
中對應的bin
fast bin
: LIFO,用單向link list串接 - 因為free
或malloc
時都會把小區塊整合或是大區塊分割,但程式經常會malloc
或free
小區塊,如果每次都還要進行合併或分割會導致程式變慢,因此fastbin
用來收集小區塊的chunk,最多可以有8個free chunksmall bin
large bin
unsorted bin
tcache
malloc
時,會先從這些bin
中尋找是否有滿足需求的chunk
當程式呼叫malloc
後,glibc
會把一塊空間allocate給他,這個空間就叫做chunk
,每個chunk
包含兩部分
chunk header
: 給glibc
管理用,大小為0x10chunk data
: 真的要寫資料的地方Note: chunk必須0x10對齊,如果要求的空間不是0x10的倍數,會被padding到0x10對齊
ex. malloc(0x28)
會得到0x30 + 0x10(header)
大小的chunk
chunk種類有三種
inuse
: 正在使用中的chunk
Size
的最後3bit有特殊用途(反正size一定是0x10的倍數,後面的4bit不存也罷)
0xb0
,後面的1代表PREV_INUSE=1
freed
: 有被使用過但已被free掉的chunk
Size
的最後3bit跟inuse
一樣,但freed chunk
的PREV_INUSE
一定為0
ex. 下圖是一個freed chunk
,其中fd
指向同一個bin
中的下一個freed chunk
,bk
指向前一個
Note:
header(0x10) + fd(0x8) + bk(0x8)
fd_nextsize
跟bk_nextsize
在bk
下面top
: 沒有被使用過的chunk,會在heap的最頂端
top chunk
會盡可能地跟其他chunk合併,如果有個緊鄰top chunk
的chunk被free,就會合併到top chunk
中
top chunk
的PREV_INUSE
一定是1,因為如果前一個被free,一定會被合併進來demo
第9行之後,heap被分配空間,執行完兩個malloc
之後
0x290+0x420+0x20+0x20930=0x21000=132K
tcache
的空間0x421 = 0x420(size) + 0x1(PREV_INUSE)
執行完chunkA的free
後,可以看到chunkA的fd
跟bk
都指向0x7fffff78bbe0
,這個位址對應到main_arena
struct中的top
,代表top chunk
的位置
inuse
,PREV_INUSE
被設成0,header前面8byte被設成前一個chunk(chunkA)的size再free掉chunkB
chunkB的bk
被寫進0x8005010,指向tcache bin
因為chunkB小於0x400,所以被收進tcache bin
中,chunkA則在unsorted bin
中
tcache bin
最多可以放7個chunk,放滿之後就依size< 0x400byte
就放進fastbin
中> 400byte
就放進unsorted bin
malloc
8個0x20後,再free
掉glibc有幾種處理freed chunk的方式
依chunk大小,性質,何時free掉區分成
fd
一個ptr(single linked list)0x20-0x80
共7個bin,每個bin代表一個size0xb0
共10個binPREV_INUSE
不會被清掉fd
跟bk
(double linked list)0x20-0x3f0
的chunk,共62個bin有fd
跟bk
(double linked list)
處理大小 >=0x400
的chunk,共63個bin
0x400-0x430
)0x440-0x630
)0x640-0x1630
)0x1640-0x9630
)0x9640-0x49630
)所以一種大小的chunk還是只會對應到一種bin
每個bin中的chunk大小不一致,所以需要額外的entry(fd_nextsize
跟bk_nextsize
)來記錄下一個大小跟自己不一樣的chunk在哪裡
每個bin中的freed chunk由小到大排列
分配時使用Best fit
fd
跟bk
(double linked list)small bin
跟large bin
的cache
small bin
或large bin
的chunk都會先被丟進unsorted bin
中
malloc
時會先進unsorted bin
中traverse有無適合大小的chunksmall bin
或large bin
heap
上main_arena
中fd
(single linked list)bk
,用來存key
,
0x20-0x410
共64個bin
unsorted bin
等PREV_INUSE
不會被清掉為啥fastbin
跟small bin
處理的size會overlap?
unsorted bin
只有一個0x400
大小的chunk,此時malloc(0x398)
會要求一塊0x3a0
大小的chunk,因為Best fit
,因此會將這塊0x400
大小的chunk切出0x3a0
給他,剩下的0x400-0x3a0 = 0x60
雖然符合fastbin
可處理的範圍,但卻不會丟進fastbin
,而是繼續放在unsorted bin
中,最後可能流入small bin
中ex
執行完所有free
後,unsorted bin
中多了兩個freed chunk
接著第10行再malloc(0x398)
,traverse後發現沒有剛好大小的chunk,會將0x1010
的chunk放入large bin中,並將Best fit的0x420
大小的chunk切成適當大小,剩下的0x420-0x3a0 = 0x80
繼續放在unsorted bin
最後第11行再malloc(0x500)
,此時traverse unsorted bin後會把剛剛剩下0x80
的chunk放進small bin, 並把large bin中0x1010
的chunk切出0x510
,剩下0xb00
又被放回unsorted bin
Consolidate
malloc
了極多的小空間後又free掉,此時fastbin串接了非常多的小size的chunkmalloc
了一塊很大的空間,這些小freed chunk無法被使用,但top chunk也不夠大到能夠分配這塊大空間,此時只好向kernel要求更多空間,但其實目前heap中絕大多數的chunk都是freedconsolidate
這套機制來處理這樣的問題malloc
時,會先檢查fastbin中有沒有連續的chunk,將他們組合起來丟到unsorted bin中Note: fastbin跟tcache如果沒有遇到malloc大空間,其中的chunk就不會被合併
demos
執行結果(free
掉之後)
而free
掉之後看heap base,剛剛有提到heap最前面0x290是保留給tcache的chunk
tcache
中的chunk長這樣main_arena
中main_arena
的方法main_arena
一定會在malloc_hook
下面readelf -a libc-2.31.so | greop "malloc_hook"
free
掉之後
再次malloc
trigger_traverse
unsorted/small/large bin的ptr都放在main_arena
中
由上述可知每個chunk都有ptr涵蓋其中(稱為heap chunk meta data),這些ptr就是攻擊的主要目標
Common Tech
最終目標: 改變fd
fd
可控,可以把fd
改成可控的chunk的位置改變fd
的方法有UAF write, heap overflow, etc…,但通常題目沒有那麼簡單
double free
正常來說如果有個chunk被double free
double free or corruption (fasttop)
bypass Glibc的double free檢查機制
free
的時候會檢查fastbin的第一個chunk是不是要被free
的那個chunk
bypass方式: 不要讓要double free
的chunk在第一個就好
free(A)
free(B)
free(A)
free(A)
: fastbin的ptr指向A,A的fd
被設成NULLfree(B)
: fastbin的ptr指向B, B的fd
指向Afree(A)
:
free
的chunk(A) != B,通過檢查fd
指向Bfd
改寫成B的addr
double free之後的攻擊方式
malloc
一個同等大小的空間char *chunkC = malloc(0x18)
read(0, chunkC, 0x8 )
),把chunkA的fd
改成我們偽造的chunk