堆疊溢位的執行時探測(二)

2021-06-04 11:03:17 字數 2821 閱讀 5719

3. 技術

3.1 gnu lib c(glibc)中的堆管理

c語言沒有提供如動態記憶體管理、字串操作、輸入輸出等內建功能,而是把這些功能定義在乙個標準庫中,當使用者使用的時候會被編譯和鏈結。gnu c庫就是這一乙個庫,定義了iso c標準中的所有庫函式,以及posix(可移植作業系統介面)和gnu系統的特定函式。

c語言支援兩種記憶體分配機制:靜態和自動。當乙個變數被宣告為靜態或者全域性變數的時候採用靜態記憶體分配,每個靜態或者全域性變數定義了乙個固定大小的塊空間。這個空間從程式開始就被分配,在執行期間也不釋放。當變數是自動變數(如函式引數或者區域性變數)時進行自動記憶體分配,當遇到包含宣告的復合語句時會自動分配記憶體空間,當退出復合語句的時候記憶體就被釋放。

第三種記憶體分配方式是動態分配,這種方式不受c語言支援,但是通過glibc函式使用。動態記憶體分配是一種由程式決定哪些資訊需要被儲存的技術。當需要的記憶體數量或者記憶體使用週期未知的時候需要使用這種技術。使用到的兩個基本函式:malloc,動態分配記憶體;free,釋放記憶體。此外還有其他函式如calloc,realloc。

gnu lib c使用了doug lea的記憶體分配器dlmalloc來執行動態記憶體分配函式。dlmalloc利用了兩個核心特徵,邊界標籤(boundary tags)和分箱技術(binning),來代替使用者程式管理記憶體分配請求和釋放請求。

記憶體管理是基於「塊」的,即包含可用區域和記憶體管理資訊的記憶體塊。記憶體管理資訊也叫邊界標籤(boundary tags),儲存在每個塊的開始位置,保持當前塊和前面塊的大小。這樣便允許兩個相鄰的未使用塊合併為乙個大塊,使得未使用的小塊數量保持較小,減少碎片。

當前不使用的塊儲存在bin中,根據大小分組。小於512位元組的檔案只儲存乙個尺寸,大於等於512位元組的,其大小按幾何級數增長。搜尋可用塊的時候是按照最小優先的策略進行的,根據需要的記憶體大小在合適的bin中開始尋找。對於未分配的塊,管理資訊包含了兩個指標,用雙向鍊錶(自由鍊錶)儲存塊和對應的bin。這些鍊錶指標被稱為forward (fd) 和back (bk)。

對於32位的結構,管理資訊通常包含4位元組大小的資訊域(塊大小和前面塊的大小)。當塊未必分配時,也會包含兩個4位元組大小的指標用來操作bin中自由塊的雙鏈表。

3.2 堆溢位漏洞剖析

使用前後指標連線bin中的可用塊存在安全隱患:如果惡意使用者可以使分配的記憶體塊溢位,使用者可以重寫相鄰的下乙個記憶體塊頭指標。當溢位塊未被使用時儲存在bin的雙向鍊錶中,攻擊者就可用控制塊中前後指標的值。基於此,考慮到下面glibc使用的unlink巨集:

#define unlink(p, bk, fd)

如果試圖溢位bin中自由表的塊,unlink過程便會遭到惡意使用者的破壞,在任意記憶體位址中寫任意值。

在上面的unlink巨集中,第乙個引數p指向將要從雙向鍊錶中移除的塊。攻擊者必須在p→fd中儲存指標位址(-12位元組,下面會解釋),在p→bk中儲存需要的值。在第[1] 行和第[2] 行中,前指標(p →fd)和後指標 (p→bk) 的值分別被儲存在臨時變數fd和bk中。在第[3] 行中,fd被間接引用,fd中的位址增加了12位元組(邊界標籤中bk的偏移量)。這種技術可以用來改變程式的got (global offset table) 並且再直接訪問攻擊者**的函式指標。

上圖中的堆溢位漏洞變種,操作塊的大小域,而不是列表指標。攻擊者可以給相鄰塊的大小域提供任意值,類似於操作列表指標。當大小域可以操作的時候,例如合併兩個未使用塊,堆管理過程可能被誘使其將攻擊者控制下的記憶體區域作為下乙個塊。攻擊者可以在那個位置設定乙個偽塊來進行攻擊。如果攻擊者由於某種原因未能寫相鄰塊的列表指標但是能夠操作其大小域,那麼這次攻擊也不一定成功。

3.3 堆完整性探測

為了保護堆,我們的系統對glibc的對管理做了很多改進,包括每個塊的結構和各自的管理方式。如下圖:

保護塊管理資訊的第乙個元素是預先在塊結構中插入乙個識別符號,還加了乙個填充區域_pad0。識別符號包含了塊頭種子和隨機值的檢校和,下面會具體描述。

第二個元素是引入種子值的全域性檢校和,保留在乙個靜態變數中(_heap_magic),這個變數在程式啟動時被賦予乙個隨機值,然後通過乙個函式設定記憶體映像保護來防止被被改寫。這與依賴重複函式設定記憶體映像保護的堆保護方案相反。我們只需要程式啟動時一次呼叫,不用遭受其他方案在執行時產生的一些損失。

最後乙個元素是增大堆管理方案,用**管理和檢查每個塊的識別符號。新分配的塊的識別符號被初始化為保護記憶體位置和塊大小域的檢校和,有全域性變數_heap_magic的種子值。注意檢校和不包括分配塊的列表指標,因為這些區域屬於塊中使用者資料部分。新塊才能使用這個方法。

當乙個塊通過釋放記憶體函式返回到堆管理,其識別符號要受檢查看是否和分配時的檢校和一致。如果儲存的值不匹配,就認為堆管理資訊被破壞了。 這時會引發警報,程式會中斷。否則,程式照常進行,塊被插入到bin中,必要時與其他塊進行合併。任何鍊錶操作都在檢查塊的識別符號之前進行。當塊被插入到bin中後,其識別符號會被更新。

上面提到的元素通過修改塊的頭資訊有效預防任意位置的記憶體寫操作,無論通過溢位還是直接操作塊的頭資訊。每個分配的塊都通過隨機種子的檢校和進行保護,每個鍊錶指標都要通過檢查完整性進行保護。

這種方法還有其他用處,比如可以探測出無意的堆溢位或者重複呼叫釋放函式。此方法缺點就是不能定位指標破壞攻擊,比如破壞應用函式的指標。也不能保證 使用者資料的完整性,只能保證塊的頭資訊的有效性。

需要注意的是glibc的堆管理已經包含了保證堆管理完整性的功能。但是使用除錯方案會增加資源耗費。這種方案對堆的自由鍊錶和全域性狀態進行全面的檢查,包括與對製作漏洞無關的檢查。此外,不能保證所有的攻擊都被探測到。不是所有的鍊錶操作都會被檢查,惡意值可能通過不是針對堆溢位的完整性檢查。這樣,我們只能說這種方法不適合保護我們提到的那些情況。

上面的系統已經被應用到glibc2.3和glibc2.2.9x中,以及redhat 8.0使用的glibc預發布版本。這項技術可以被靈活運用到其他堆設計中,下一步需要將此技術應用到除了glibc之外的其他開放式系統。

iOS runtime 執行時 二 深談

attention!逆天的來了 我們在程式設計過程中,如果使用到了runtime 執行時 我們幾乎都是想動態的改變這個類的資訊,包括方法,屬性,balabala的,並且獲得這個類的一些資訊,等等,下面我們就來看看怎麼通過runtime提供的方法,來達到這些目的 1 獲取 這個類的所有屬性 h 檔案 ...

java執行時異常和非執行時異常的區別

建議使用執行時異常和編譯時異常叫法,便於區分和理解 非執行時異常是什麼異常?很懵逼 編譯時異常 程式沒有通過編譯器的編譯,必須處理掉這個異常程式才能正常執行,比如檔案路徑找不到異常,類找不到異常,io異常,必須用try catch或是throwable處理掉才能編譯通過 可以理解為一種特殊的語法錯誤...

php程式執行時間 php計算程式的執行時間

首先我們分析一下原理,要想得到程式執行時間,那麼可以在程式最開始執行的時候定義乙個變數記下當前時間,然後等我們程式執行完之後再記錄一下當前的時間,兩者相差就是該程式執行花費的時間了。這裡介紹一下 microtime 這個函式,microtime 用的不多,但是不能不知道這個函式,它是返回當前 uni...