Glibc堆漏洞利用基礎-深入理解ptmalloc2 part2
本篇文章是該系列的第二篇,第一篇是glibc漏洞利用基礎知識。
在上一篇文章中我們提到過,堆管理將保留關於空閒塊的元資料,以便這些空閒塊可以被重新分配。為了補充我在上一篇文章中的說法,我在這篇文章中會提到針對不同大小的空閒塊,會有不同型別的連結串列來管理,即如下這些連結串列:
· Unsorted Bin -這個連結串列用來臨時儲存那些不屬於Fast,Large或者Small bin分類的塊。在這裡引用某大牛論文中關於此的說法:當釋放不在fastbin範圍內的塊時,這些塊就被劃到了未分類的bin中,而不會劃分到small bins或者large bins中。
論文下載地址: https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf
· Small bins -正如其他連結串列一樣,這只是另一個連結串列或者是一組連結串列,用來儲存特定大小的空閒堆塊。而這個大小的閾值可能因架構和glibc實現或者是build不同而有所變化。不過,最基本的一點是他們比Fast Bins大,比Large Bins小。我會在以後的文章中再來講述關於這個的更多細節。
· Large Bins -比最大值還大的塊,我現在搞得還不是很明白,所以也留在以後的文章中再講。
· Fast Bins -本文的重點,儲存的是所有小於特定最大塊的空閒塊。
這裡之所以選擇講解Fastbin中的塊,是因為它們是作為malloc_chunk基本格式的擴充套件,而malloc_chunk格式用於普通的未分類的或正常大小的堆塊,並且它們也有一些很不錯的技巧我們可以嘗試一下。所以,本文不會講解large chunks,只是目前不講,不過以後等我有了足夠的資料,我還是會分析一波。
好了,不叨叨了,開始講fastbins吧。
Fast Bin格式
Fastbins其實也是記憶體中的一個堆塊。
Fastbins是為小的記憶體物件保留的(小的結構和字串)。fastbin的思想就是,這些塊大小是能夠從常規的計算開銷中獲得一些優勢的,如果不使用的話,那麼你使用的僅僅是符合所請求大小的小記憶體區域的簡單鏈表。根據目前關於堆的研究,Fastbins是不同大小的fast bins的集合,(所以不只是簡單的單一連結串列,對於每種大小的組,可能會有很多fastbins在操作),這裡再引用一篇論文中的觀點:
“Fastbin是一種針對性能和區域性快取進行了優化的特殊設計。它是一個單獨連結串列,類似於Windows的look aside表格,其中相同大小的空閒塊以LIFO方式連結。不同fastbins的塊大小各不相同。在arena中有共有10個fastbins,但預設情況下使用前7個,在32位系統上為16到64個位元組,在64位系統上為32到128個位元組 “
論文地址:
https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf
還有一點我要提醒的是,如果fastbins被釋放了,它們是不會合並的。在glibc- [版本] /malloc/malloc.c的文件中有更多關於fastbins的精彩內容。我建議大家可以去通讀一遍 – 我這裡就不進行復制了。
好了,關於fastbins的介紹部分就扯這麼多了,現在我們來看看它們到底是什麼東西以及它們的工作原理。
關於fastbins大小的閾值在 malloc/malloc.c 中進行了定義。就像glibc-2.23中一樣,它的定義是這樣的:MAX_FAST_SIZE = (SIZE_SZ * 80)/ 4,最大值為80個位元組。因此,任何低於80位元組的資料都會被分配到fastbins中。
下圖是記憶體中fastbins的一個連結串列,可以很好的展示fastbins的格式:
在這個示例圖片中,它也比較了一個非fastbins的塊,只是一個普通的大塊,大小為0xb0 (記憶體指標分配的第一個塊,位置是0x602010) – 這是為了更加清楚的顯示格式之間的區別。
在圖片左側,你可以看到“live”的fastbin塊(從0x6020b0開始)。除了它們的大小之外,在這種狀態下,不能把它們像fastbin一樣分配出去。在圖片右側,你可以看到被釋放的空閒fastbins塊; 這裡需要注意的一個關鍵事項是它們有一個後向指標(形成一個fastbin塊的連結串列),指示下一個空閒fastbins塊的位置。
還有一點你可能也會注意到,這個例子中:在記憶體中彼此相鄰的fastbin'd塊是空閒的; 但是大小欄位都沒有重疊,也沒有塊合併在一起。如前所述,他們不會合並。
OK,現在我們知道它們在記憶體中的格式,我們再來談談另一個重要機制,也就是fastbin first fit。
Fastbin First Fit
fastbins的重新分配規則略有不同。當他們進行重新分配時,他們位於後進先出的佇列中。這意味著,如果我們一個接一個的將它們釋放出來,那麼第一個返回重新分配的塊將會是系列中最後一個釋放的。
在下面的記憶體分佈圖片中,圖片左邊顯示了在呼叫malloc之前和所有fastbin塊被釋放之後的堆狀態。右邊是第二個malloc(或重新分配)被呼叫後的堆。所以我們基本上能夠看到哪些fastbin塊被返回和使用,以及它們的順序(主要是因為我使用程式將一些資訊寫入堆塊 – 每個分配寫入0xAA,0xBB按照它們分配的順序進入堆中):
你應該能夠很容易地找出最先返回的塊。另請注意顯示在釋放列表中的指標; 這些0x602yyy 在堆塊中的值是malloc_chunk-> bk指標。
毫無疑問,你的下一個問題是我們如何讓它返回一個我們想要的指標,或者我們如何影響空閒塊,比方說,強制返回某個指定的空閒塊?當我們“在使用中”時覆蓋這些資訊然後檢視返回哪個fastbin時會發生什麼?這裡我們提供了下面這個測試方案:
1.釋放一些塊
2.重新指向fastbins 的malloc_chunk-> bk指標
3.看看哪些可用的塊被寫入0x4242 (這個0x4242 / 0x4141純粹是因為我讓這個程式對我有所幫助)
我們來看看這些塊在記憶體中的檢視:
解釋一下。
首先,我用set {size_t} 0x602100 = 0x0000000000602000命令覆蓋bk指標。這基本上告訴堆在返回第一個fast bin後(LIFO中的第一個fastbin在0x6020f0位置處),指標應該根據連結串列指向下一個空閒的fastbin,它位於0x0602000。
下一個截圖顯示瞭如何重定向分配; 它不是在最底層的上方分配下一個塊,而是一直跳到頂部:
可以清楚地看到,我們只是重定向堆重新分配!連結串列權現在屬於我們!!
你也可以將堆重定向到記憶體中可寫可讀部分中的其他位置的偽塊。為此,需要做到如下幾點:
1.釋放所有的fastbins
2.就在第一次重新分配之前; 重新指向首先適合你的偽fastbin的fastbin
例如:set {size_t} 0x602100 = 0x601050 (這會將塊的bk指標設定為0x6020f0 )
3.將fast bin的大小設定為可接受的值,這裡我只是回收與被替換的fast bin相同的大小,如下所示:set {size_t} 0x601058 = 0x0000000000000051
下面的截圖展示了這一點。
我們可以在0x6010yy地址區域中看到一個奇怪的堆塊,而其餘的塊在0x6020yy範圍內:
這並不是一個完整的安全漏洞,但它確實讓我們更加接近一個漏洞。它還讓我們學到了一個重要的小技巧,就是讓堆去做一些奇怪的事情。
好了,本文就到這裡,我將在下一篇文章中介紹更多關於堆元資料的內容。敬請關注!