[譯] [2018-S&P] CollAFL: Path Sensitive Fuzzing

全文概述

覆蓋率引導模糊測試(CGF)是一種廣泛使用且有效的解決方案,可用於查找軟件漏洞。跟蹤代碼覆蓋率並利用它來指導模糊測試對覆蓋引導的模糊器。然而,由於高插樁開銷,在實踐中跟蹤完整且準確的路徑覆蓋是不可行的。流行的模糊器(例如,AFL)經常使用粗略的覆蓋信息,例如存儲在緊湊位圖中的邊緣命中計數,以實現高效的灰盒測試。覆蓋範圍的這種不準確性和不完整性給模糊器帶來了嚴重的限制。首先,它會導致路徑衝突,從而阻止模糊器發現導致新崩潰的潛在路徑。更重要的是,它可以防止模糊測試員對模糊測試策略作出明智的決定。在本文中,我們提出了一種覆蓋敏感的模糊解決方案CollAFL。它通過提供更準確的覆蓋信息來緩解路徑衝突,同時仍然保留低插樁開銷。它還利用覆蓋信息應用三種新的模糊測試策略,提高了發現新路徑和漏洞的速度。我們基於流行的模糊AFL實現了CollAFL的原型,並在24種流行的應用程序上進行了評估。結果表明,路徑碰撞是常見的,即在一些應用中高達75%的邊緣可能與其他邊緣碰撞,並且CollAFL可以將邊緣碰撞比降低到接近零。此外,憑藉三種模糊測試策略,CollAFL在代碼覆蓋率和漏洞發現方面均優於AFL。平均而言,CollAFL在200小時內覆蓋的程序路徑增加了20%,獨特崩潰次數增加了320%,錯誤次數比AFL多260%。 CollAFL總共發現了157個新的安全漏洞,分配了95個新的CVE。

貢獻:
1. 證明AFL的hash衝突限制了他的路徑和漏洞發現效果
2. 設計了一個算法解決hash衝突問題
3. 提出了3個新的覆蓋率敏感的種子選擇策略
4. 實現了CollAFL, 並在24個開源應用上進行實驗, 發現了100多個新的安全漏洞.


如果一個路徑有許多未探索(或未觸及)的相鄰分支,那麼該路徑的突變很可能會探索那些未探索的分支。       CollAFL-br
如果一個路徑有許多未被探索(或未被開發)的鄰居後代,那麼該路徑的突變很可能會探索那些未被探索的後代。 CollAFL-desc
如果一個路徑有許多內存訪問操作,它很可能觸發潛在的內存損壞漏洞,它的突變也是如此。                 CollAFL-mem

實現:
分別計算路徑中的所有BBL的權重.
CollAFL-br : BBL存在未覆蓋的鄰居,則BBL權重計爲1. 循環中的邊計算多次. 再對所有BBL的權重求和.
CollAFL-desc : BBL的權重計爲 未覆蓋的鄰居的子節點數之和. 循環中的邊計算多次. 再對所有BBL的權重求和
CollAFL-mem : 路徑中訪問內存指令的數量之和. 循環中的邊計算多次.
具體怎麼使用權重選擇種子沒說.

實驗:
8個fuzzer進程  200個小時
1. 衝突實驗. 
2. 覆蓋. 以發現的路徑數爲比較對象. 分別測試CollAFL CollAFL-br CollAFL-desc CollAFL-mem. 發現的路徑數平均提升9.9  20.78  17.23  15.7.  在附錄中補了1些數據,以bitmap作爲比較對象. 可理解爲邊覆蓋率提升3.31 7.77 6.74 4.36
3. 漏洞數.
和Evaluating Fuzz Testing一樣, 發現了AFLFast並不總是比AFL好. 總是在開始好,但是之後被AFL超越. 提出測試需要超出24小時. 應該是在Evaluating Fuzz Testing這篇文章之前提出這個觀點.
也提出了隨機化引起的測試問題.
路徑發現和崩潰發現的偏差:對於每個fuzzer和目標應用程序,我們首先每20分鐘採樣一次,每次試驗中迄今爲止發現的路徑數。然後我們計算了20個試驗中每個採樣點的相對標準偏差(RSD) 6。最後,計算了各採樣點RSDs的算術平均。表V顯示了20個應用於路徑發現的試驗中每個fuzzer的平均RSD。

在LAVA-M dataset中,AFL-laf的性能顯著優於AFL、AFL-pc和CollAFL,但在現實應用中表現較差,這表明LAVA-M基準測試有很多長字符串/整數比較,阻礙了傳統的模糊器,但在現實應用中並非如此。

一、引言

內存損壞漏洞是許多嚴重威脅程序的根本原因,包括控制流劫持攻擊[12、37、38]和信息泄漏攻擊[36]。攻擊者依靠漏洞破壞目標程序的執行並執行惡意操作。如果防禦者能夠提前發現漏洞,他們可以修補漏洞以擊敗潛在攻擊。

覆蓋引導模糊測試是最流行的漏洞發現解決方案之一,廣泛應用於工業領域。例如,Google的OSS Fuzz平臺[35]採用了多種最先進的覆蓋制導模糊測試,包括libfuzzer[34]、honggfuzz[40]和afl[45],來持續測試開源應用程序。它在5個月內發現了1000多個bug[3],其中有數千個虛擬機。

首先,跟蹤代碼覆蓋對於覆蓋引導的模糊器至關重要。準確的路徑覆蓋信息可以幫助模糊測試人員感知所有獨特路徑並探索它們以發現漏洞。然而,由於極高的儀表開銷,在實踐中跟蹤路徑覆蓋是不可行的。 Fuzzers將覆蓋精度與性能進行權衡。 LibFuzzer [34]和honggfuzz [40]利用Clang編譯器提供的SanitizerCoverage [4]工具來跟蹤塊覆蓋。 VUzzer [29]使用動態二進制檢測工具PIN [24]來跟蹤塊覆蓋。 AFL [45](在GCC和LLVM模式下)使用帶有緊湊位圖的靜態檢測來跟蹤邊緣覆蓋,提供比塊覆蓋更多的信息。即使對於AFL,也存在已知的哈希衝突問題,其中兩個不同的邊可以具有相同的哈希,因此在覆蓋位圖中共享相同的記錄。它會導致邊緣覆蓋精度的損失。我們的實驗表明,在某些應用中,高達75%的邊緣可能會與其他邊緣碰撞。

更重要的是,利用覆蓋信息來指導模糊測試對於覆蓋引導的模糊器至關重要。 AFL利用邊緣覆蓋信息來識別種子(即,有助於覆蓋的良好測試用例),並將它們添加到等待進一步突變和測試的種子池中。 AFLfast [11]進一步利用邊緣覆蓋信息來優先選擇和選擇種子(來自池)運行頻率較低的突變路徑,以提高路徑發現的效率。 VUzzer利用塊覆蓋信息來優先考慮行使錯誤處理塊的測試用例和執行頻繁路徑的測試用例。但是,鑑於代碼覆蓋率信息不準確,模糊器無法做出最佳決策。此外,很少有模糊測試器利用代碼覆蓋率信息直接驅動模糊到未探索的路徑。

據我們所知,覆蓋不準確的後果被模糊器的巨大成功所掩蓋,因此沒有得到系統的評估。在本文中,我們證明它實際上對模糊器的能力有着至關重要的影響。我們還證明,如果能夠以較低的開銷實現準確的邊緣覆蓋並且部署了適當的覆蓋引導的模糊測試策略,則模糊器可以顯着提高他們探索路徑和發現錯誤的能力。

A.覆蓋不準確性如何模糊錯誤發現?

首先,覆蓋不準確可能導致模糊器在某些情況下無法區分兩個不同的程序路徑。如果測試用例行使與先前探索過的路徑發生碰撞的新路徑,則模糊測試器可能會將其錯誤地歸類爲不感興趣,並錯過徹底測試此路徑或探索相關路徑的機會。因此,它將導致代碼覆蓋率的損失,甚至錯過隱藏在這些路徑中的潛在漏洞。同樣,模糊測試程序也可能錯誤地將新發現的漏洞分類爲不感興趣,因爲它的路徑與先前發現的漏洞相沖突。

其次,更重要的是,覆蓋不準確性模糊了模糊策略。例如,它可以防止模糊器在選擇種子進行變異和測試時做出最佳決策。例如,AFLfast [11]優先考慮運行頻率較低的路徑的種子,這可能是不準確的,因爲覆蓋範圍是由近似位圖捕獲的。 AFLgo [10]將種子優先於目標位置,也需要準確的路徑覆蓋信息。因此,覆蓋不準確性問題將使這些模糊測試器的種子選擇策略效率低下,從而降低了查找錯誤的速度。

B.如何提高覆蓋精度和引導模糊器?

如上所述,在實踐中跟蹤準確的路徑覆蓋是不可行的,但是跟蹤邊緣和塊覆蓋是可能的。 AFL提供的邊緣覆蓋提供了比塊覆蓋解決方案更多的信息。此外,AFL的覆蓋範圍跟蹤解決方案也比其他解決方案引入更低的運行時開銷。因此,我們可以將AFL的覆蓋跟蹤解決方案移植到其他模糊器,以提高其覆蓋精度。但是,由於哈希衝突問題,AFL的邊緣覆蓋本身並不完美。這個問題的直接解決方案是擴大AFL用於存儲覆蓋範圍的位圖的大小。正如我們的實驗所示,此解決方案無法消除已知邊緣的所有哈希衝突,但會引入顯着的開銷。

本文介紹了一種覆蓋敏感的模糊測試解決方案CollAFL,它可以解決AFL的哈希衝突問題並提高其覆蓋精度而不會降低性能。此外,CollAFL不僅利用準確的邊緣覆蓋信息,還使用三種新設計的模糊測試策略來驅動模糊器到非探索路徑,從而提高漏洞發現的效率。

CollAFL通過確保目標程序中的每個邊都具有唯一的散列來解決AFL中的散列衝突問題,以便AFL可以區分任何兩個邊。更具體地說,我們分析目標應用程序的控制流圖以獲得已知邊的列表。精心設計的散列方案用於爲所有邊緣分配ID到基本塊和計算散列,確保儀器成本低,並消除已知邊緣的衝突。

一旦解決了哈希衝突問題,模糊器就可以獲得準確的邊緣覆蓋信息,從而實現覆蓋敏感的模糊測試策略,例如種子選擇策略。 準確的邊緣覆蓋允許模糊器基於與種子的執行路徑相關聯的細粒度屬性來對種子進行優先級排序,例如,沿着每條路徑的存儲器訪問操作的數量,未觸摸的鄰居分支和未觸摸的鄰居後代。 相應地,本文提出了三種新的種子選擇策略:記憶訪問引導,未觸發分支引導和非引導後代引導,每種策略基於上述路徑屬性優先考慮種子選擇。 所有這些策略都顯示了模糊器的路徑和漏洞發現效率的改進。

C.覆蓋敏感模糊測試的表現如何?

我們基於流行的覆蓋引導模糊AFL實現了CollAFL的原型。我們在LAVA-M數據集[14]和24個開源應用程序上評估了CollAFL。評估結果表明:

  • 哈希衝突問題在現實世界的應用程序中很普遍,高達75%的邊緣可能與其他應用程序衝突;
  • 我們提出的碰撞緩解解決方案可以解決已知邊緣的所有哈希衝突,並可以幫助模糊器探索9.9%以上的代碼,發現250%的獨特崩潰和平均140%的安全漏洞。
  • 未受影響的分支引導種子選擇策略(連同碰撞緩解)可以進一步提高模糊器的路徑和錯誤發現的效率。平均而言,它可以幫助模糊器探索20%以上的代碼,發現320%的獨特崩潰和260%的安全漏洞。

總的來說,我們在這24個開源應用程序中發現了157個安全漏洞,並將它們報告給上游供應商。其他研究人員報告了其中23例,但沒有公開曝光。在剩餘的134個漏洞中,其中95個由CVE確認。總之,本文做出以下貢獻:

  • 我們研究了覆蓋引導模糊器中覆蓋不準確性的負面影響。 特別是,我們證明了AFL中的哈希衝突問題嚴重限制了其路徑和漏洞發現的效率。
  • 我們設計了一種算法來解決AFL中的哈希衝突問題,通過低開銷的檢測方案(在大多數情況下比AFL更快)提高其邊緣覆蓋精度。
  • 我們提出了三種新的覆蓋敏感種子選擇政策。 我們的實證結果證實,基於準確的邊緣覆蓋信息對種子進行優先級排序可以顯着提高模糊器的性能。
  • 我們實現了基於AFL的CollAFL原型,並在24個開源應用程序上進行了評估。 CollAFL的有效性部分得到了驗證,因爲它能夠在之前經過充分測試的應用程序中找到超過一百個新的安全漏洞。

II。背景和相關工作

A.模糊

Fuzzing是目前最划算最有效的漏洞發現解決方案。通常,模糊器將首先生成大量測試用例來測試目標應用程序,然後監視應用程序的運行時執行並在檢測到安全違規時報告錯誤。

模糊器通常易於設置,可以擴展到大型應用程序。因此,模糊測試已成爲業界主要的漏洞發現解決方案。 Fuzzers通常使用兩種類型的測試用例生成解決方案:基於語法和基於突變。基於語法的模糊器[15,17]基於已知的輸入語法生成測試用例。根據語法,fuzzers可以生成有效的測試用例並覆蓋程序路徑的主要部分。但是他們需要很多工程工作來翻譯輸入語法,並且無法處理沒有已知語法的應用程序。

另一方面,基於突變的模糊器[20,34,45]改變現有的測試用例以生成新的測試用例而不依賴於輸入語法,因此具有更好的可擴展性。由於簡單性和可擴展性,基於突變的解決方案在實踐中被廣泛採用。

然而,基於瑣碎突變的模糊器通常具有較差的代碼覆蓋率。 例如,它們可能會被輸入格式檢查阻止,並且無法觸發更深層次的漏洞。 因此,研究人員在改進這些模糊測試器的代碼覆蓋率和模糊測試效率方面做了大量工作。

B.覆蓋範圍引導的模糊測試

爲了改善代碼覆蓋率,最成功的解決方案之一是覆蓋引導模糊測試。 它採用不斷髮展的算法來驅動模糊器,以實現高代碼覆蓋率。 AFL [45],libFuzzer [34],honggfuzz [40]和VUzzer [29]是一些最先進的覆蓋引導模糊器。

圖1顯示了覆蓋引導模糊器的一般工作流程。它通常維護一個種子測試池並執行連續的模糊循環:(1)從具有特定策略的池中選擇種子,(2)變異種子以生成一批新的測試用例,(3)使用這些新測試用例高速測試目標應用程序,(4)用儀器監控程序執行情況,跟蹤代碼覆蓋率和安全違規情況;(5)報告檢測到安全違規時的漏洞,(6)過濾有助於代碼覆蓋的良好測試用例將它們放入池中,然後轉到步驟1.在這個連續循環之後,模糊器優先考慮種子覆蓋範圍,並向高代碼覆蓋範圍發展。

研究表明,對該循環的每個步驟的改進可以提高模糊器的效率和效率。模糊測試的成功有很多因素,包括測試速度,覆蓋準確性,種子選擇策略,種子變異策略以及對安全違規的敏感性等。

例如,在步驟3中,提出了幾種優化以提高模糊器的速度和吞吐量。 AFL利用Linux提供的fork機制加速,並進一步採用forkserver模式和persistent模式來減輕fork的負擔。此外,AFL還支持並行模式3,使多個模糊器實例能夠相互協作。文旭等。提出了幾個新的原語[44],使AFL加速6.1到28.9倍。

在步驟4中,模糊器可以使用不同的機制,例如靜態檢測,動態二進制檢測,調試或甚至系統仿真,來檢測目標應用並跟蹤有用信息。 AFL利用GCC和Clang編譯器執行靜態源檢測,並利用QEMU執行動態二進制檢測VUzzer利用工具PIN [24]執行動態二進制檢測。 Syzkaller [5]和kAFL [31]利用QEMU以及硬件功能(例如Intel PT)進行檢測。

在步驟5中,模糊器經常使用程序崩潰作爲漏洞的指示器,因爲即使沒有儀器也很容易檢測到它們。但是,當觸發漏洞時,例如,當覆蓋數組後面的填充字節時,程序並不總是崩潰。研究人員提出了幾種解決方案來檢測各種安全違規行爲。例如,廣泛使用的AddressSanitizer [32]可以檢測緩衝區溢出和釋放後使用漏洞。還有許多其他Sanitizer,包括UBSan [22],MemorySanitizer [39],LeakSanitizer [30],DataFlowsanitizer [2],ThreadSanitizer [33]和HexVASan [9]。

在以下部分中,我們將詳細介紹其他步驟。

C.覆蓋範圍跟蹤

覆蓋引導的模糊器利用覆蓋信息來驅動模糊測試(步驟6)。如前所述,覆蓋不準確性會使錯誤發現變得模糊。因此,模糊測試員必須跟蹤準確的覆蓋範圍。但讀者也應該意識到,覆蓋精度只是模糊器成功的一個因素。

不同的程序路徑表現出不同的程序行爲,因此可能具有不同的漏洞。準確的路徑覆蓋可以幫助模糊測試人員感知不同的路徑。但是,在運行時跟蹤所有路徑覆蓋(特別是邊緣的順序)是不可行的,因爲路徑的數量非常高並且每個路徑的存儲開銷很高。
在實踐中,覆蓋引導的模糊器跟蹤不同級別的代碼覆蓋。例如,LibFuzzer [34]和honggfuzz [40]利用Clang編譯器提供的SanitizerCoverage [4]檢測方法來跟蹤塊覆蓋。 VUzzer [29]使用PIN來跟蹤塊覆蓋。 AFL [45]使用靜態/動態儀器來跟蹤邊緣覆蓋。

給定邊緣覆蓋,我們當然可以推斷塊覆蓋。在某些情況下,我們甚至可以從塊覆蓋中推斷出邊緣覆蓋。 SanitizerCoverage進一步消除了關鍵邊緣以確保後者的推理,並聲稱支持邊緣覆蓋。但它只是塊覆蓋的增強版本。塊覆蓋提供的信息少於邊緣覆蓋。臨界邊緣只是妨礙從塊覆蓋中推斷邊緣覆蓋的一個因素。如圖2所示,函數foo中沒有關鍵邊。兩個程序路徑P1和P2共享其大部分邊緣,除了它們在函數foo中採用不同的子路徑。因此P1和P2的塊覆蓋範圍完全相同,但它們的邊緣覆蓋範圍不同。例如,邊緣B1-> C1僅存在於路徑P1中。

對於跟蹤塊覆蓋的模糊器,例如libFuzzer,honggfuzz和VUzzer,提高其覆蓋精度的解決方案是用邊緣覆蓋跟蹤(例如AFL使用的跟蹤覆蓋方案)替換它們的跟蹤方案。 然而,AFL提供的邊緣覆蓋是不完美的。

a)散列碰撞問題:AFL利用位圖(默認大小爲64KB)來跟蹤應用程序的邊緣覆蓋範圍。 位圖的每個字節表示特定邊緣的統計數據(例如,命中計數)。 爲每個邊計算哈希值,並將其用作位圖的關鍵字。 在該方案中存在哈希衝突問題,即,兩個邊可以具有相同的哈希。 因此,模糊器無法區分這些邊緣,導致覆蓋不準確。

更具體地說,AFL檢測目標應用程序並將隨機密鑰分配給其基本塊。 給定邊A-> B,然後AFL計算其散列如下:

cur\bigoplus (prev\gg 1)

其中prev和cur分別是基本塊A和B的鍵。 由於密鑰的隨機性,兩個不同的邊可以具有相同的散列。 此外,邊緣的數量很高(即,與位圖大小64K相當),考慮到生日攻擊[16],碰撞率會非常高。

據我們所知,覆蓋不準確的後果被模糊器的巨大成功所掩蓋,因此沒有得到系統的評估。 我們的實驗表明,由於哈希衝突問題,在實際應用中高達75%的邊緣對於AFL是不可見的,這極大地限制了AFL的能力。 本文討論了這個問題。

D.種子選擇政策

最近的研究[10,11]表明,種子選擇策略(即模糊測試循環中的步驟1)對於基於覆蓋的模糊器是至關重要的。一個好的種子選擇政策可以提高模糊測試的路徑探索和bug發現的速度。

AFL優先考慮較小且執行速度較快的種子,因此可能在給定時間內測試更多的測試用例。 Honggfuzz按順序選擇種子,Lib-Fuzzer優先考慮擊中更多新塊的種子。 VUzzer [29]優先考慮行使更深路徑的種子,並優先考慮行使錯誤處理塊和頻繁路徑的測試用戶,因此可能會測試難以到達的路徑並避免無用的錯誤處理路徑。 AFLfast [11]優先考慮種子行走頻率較低且選擇較少的種子,因此很可能可以徹底測試冷路徑,並且在熱路徑上浪費的能量更少。

種子選擇政策還可以加強模糊器在特定方向上的能力。例如,QTEP [42]優先考慮通過靜態分析識別的更多錯誤代碼的種子,增加了在測試期間觸發漏洞的可能性。 SlowFuzz [27]優先考慮使用更多資源(例如,CPU,內存和能量)的種子,從而增加了觸發算法複雜性漏洞的可能性。 AFLgo [10]優先考慮更接近預定目標位置的種子(例如,等待評論的新提交),從而實現有效的定向模糊測試。

但是,鑑於代碼覆蓋率信息不準確,模糊器無法對種子選擇做出最佳決策。例如,如果冷卻路徑發生碰撞,AFLfast可能會錯誤地將冷路分類爲熱路徑,從而導致此冷路測試不良並且漏掉了潛在的漏洞。此外,很少有模糊測試器利用代碼覆蓋率信息直接驅動模糊到未探索的路徑。本文提出了幾個解決此問題的新政策。

E.種子突變政策

變異種子(即模糊環中的步驟2)對於覆蓋引導的模糊器是必不可少的。 AFL和libFuzzer等基本上使用一組確定性和隨機算法來變異種子並生成新的測試用例。種子突變政策與幾個核心問題有關:(1)種子來源,(2)變異的地方,以及(3)突變使用的價值。

一組好的種子可以幫助產生良好的突變。 IMF [19]從正常的應用程序執行中學習系統調用之間的順序和值依賴關係,然後相應地生成測試用例,從而能夠找到許多深層內核錯誤。 Skyfire [41]從豐富的輸入中學習概率上下文敏感語法,以指導測試用例的生成。 DIFUZE [13]利用靜態分析在用戶空間中組合有效輸入來測試內核驅動程序。最近,研究人員利用AI技術來幫助模糊測試。 Patrice Godefroid et.al.提出了一種RNN(遞歸神經網絡)解決方案[18]來生成有效的種子文件,並且可以幫助生成輸入以通過格式檢查,從而改善代碼覆蓋率。 Nicole Nichols等。提出了一個GAN(生成對抗網絡)解決方案[26],用額外的種子來爭論種子庫,展示了另一個有希望的解決方案。但是,需要更多的研究來進一步提高種子投入的質量。

突變的另一個核心問題是在哪裏發生變異。 VUzzer [29]使用控制流和數據流特徵來推斷要變異的字節(例如,魔術字節),這對某些類型的數據字段很有用。志強等提出了一種解決方案[23],用於識別使用靜態數據沿襲分析進行變異的敏感字節。 Mohit Rajpal等。提出了一個DNN(深度神經網絡)解決方案[28]來預測要變異的字節,顯示出有希望的改進。 TaintScope [43]使用污點分析識別校驗和字節並在測試期間修復它們。

突變的另一個核心問題是用於突變的價值。 VUzzer [29]使用動態分析來推斷用於變異的有趣值(例如,幻數)。 Honggfuzz [40]採用類似的策略來在運行時識別有趣的值(即cmp指令的操作數)並大大改善其路徑覆蓋。 Laf-intel [1]轉換目標應用程序,將長字符串或常量比較分成幾個小的比較語句,使模糊器能夠找到匹配的變異並更快地運行新的路徑。

F.本文的重點

爲了提高模糊測試器的查找效率,我們提出了CollAFL,一種覆蓋敏感的模糊測試解決方案。 圖1中的黃色組件展示了我們解決方案的重點。 簡而言之,它首先提高了代碼覆蓋率跟蹤的準確性,然後通過替換種子選擇策略,利用準確的覆蓋率信息來指導模糊器。 更多細節將在以下部分中討論。

對種子突變政策,測試性能優化和儀器方案以及細粒度安全sanitizers,的研究與我們提出的工作是正交的。 我們的解決方案也可以從這些工作中受益

III.提高覆蓋準確度

如前所述,覆蓋不準確性模糊了模糊測試者發現錯誤的能力,導致模糊器無法看到某些路徑。 CollAFL優於現有覆蓋引導模糊器的第一個改進是覆蓋精度。 它可以幫助模糊測試人員探索更多路徑並找到更多漏洞。

我們已經研究了不同類型的覆蓋粒度,並且找出邊緣覆蓋是最佳選擇,其在儀器開銷和覆蓋精度之間達到了良好的平衡。 我們進一步指出當前邊緣覆蓋實現中的不準確性問題,並提出解決方案。

A.覆蓋粒度

有三種常見類型的覆蓋粒度,即塊覆蓋,邊緣覆蓋和路徑覆蓋。他們每個人都有其優點和缺點。

典型的塊覆蓋解決方案將在測試期間跟蹤每個塊的命中數。它被模糊器廣泛採用,例如VUzzer,libFuzzer和honggfuzz。但是,它不跟蹤塊的順序,導致覆蓋信息丟失。圖2顯示了兩個不同的路徑共享完全相同數量的塊命中,因此其中一個塊對於塊覆蓋模糊器是不可見的。

典型的邊緣覆蓋解決方案將跟蹤每個邊緣的命中計數。代表性實施是AFL使用的實施。儀表開銷類似於塊覆蓋解決方案。但是,它不會跟蹤邊緣的順序,也會丟失一些信息。

路徑覆蓋解決方案將跟蹤邊緣的順序,提供最完整的代碼覆蓋率信息。但是,路徑的長度非常長,並且應用程序中的路徑數量很大,因此跟蹤路徑覆蓋的運行時開銷和內存開銷非常高。實際上,跟蹤路徑覆蓋是不可行的。

因此,邊緣覆蓋解決方案在效率和覆蓋範圍信息之間達到某種平然而,即使對於代表性邊緣覆蓋解決方案AFL,也存在導致不準確的哈希衝突問題。 CollAFL採用邊緣覆蓋跟蹤方案並修復了碰撞問題。其他模糊器(例如VUzzer)也可以從該方案中受益。

B.哈希碰撞的平凡解決方案

這個問題的直接解決方案是擴大散列的空間,即AFL實現中的位圖大小。但是,正如AFL本身所解釋的那樣,當前的默認位圖大小(即64KB)是性能的折衷。

選擇地圖的大小,使得幾乎所有預定目標的碰撞都是零星的,這些目標通常在2k到10k之間發現可發現的分支點。同時,它的大小足夠小,可以在接收端以微秒爲單位分析地圖,並毫不費力地適應L2緩存。

我們已經評估了該解決方案的效率,並確認如果我們擴大位圖大小,模糊器的性能會迅速下降。如第V-A節所示,爲了將哈希衝突率降低到5%,我們必須將位圖大小從64KB增加到4MB,從而導致60%的執行速度下降。更糟糕的是,由於隨機性,我們不能保證僅通過放大位圖來消除衝突。因此,這不是哈希衝突問題的正確解決方案。

C. CollAFL解決哈希衝突的方法

如等式1所示,AFL使用固定公式來計算每條邊的散列,這很快但很容易發生碰撞。 我們通過仔細地爲不同的邊緣應用不同的哈希公式來細化它,以消除哈希衝突,同時保持哈希計算和覆蓋跟蹤的速度。

通常,給定兩個具有鍵prev和cur的塊A和B,我們計算邊A-> B的哈希,如下所示:

Fmul(cur,prev)=(cur \gg x)\oplus (prev \gg y)+z

其中<x,y,z>是要確定的參數,對於不同的邊可能是不同的。 AFL使用的等式1是該算法的特定形式,即,對於所有邊/塊,<x = 0,y = 1,z = 0>。 Fmul的計算過程與AFL相同,具有相同的開銷。

如圖3所示,我們可以爲每個結束塊而不是每個邊選擇一組參數來計算邊緣哈希值。 爲簡單起見,將在塊之間共享相同的參數y,並將值(prev \gg y)緩存在全局變量_prev中。 每個塊可以具有不同的參數集<x,z>。

因此,給定一個應用程序,我們可以嘗試找到每個基本塊的參數解決方案,確保通過Fmul計算的所有邊緣哈希值都不同。我們使用貪婪算法逐個搜索每個塊的參數。一旦找到所有塊的解,我們就可以使用它們的哈希來區分任何兩個邊,從而解決哈希衝突問題。

但是,我們無法保證爲給定的應用程序找到解決方案,因爲應用程序中的基本塊太多而我們無法遍歷所有可能的參數。 即使我們可以這樣做,我們也無法保證存在解決方案,因爲基本塊的密鑰是隨機分配的。 因此,我們進一步細化所提出的散列計算算法如下。

1)具有單個先例的塊的散列算法:如果一個塊只有一個先例,如圖3(3)所示,我們可以在結束塊中直接爲該邊分配散列,而不是使用等式2來計算一個 ,只要這個哈希不與任何其他邊緣碰撞'。

因此,對於僅具有一個先前塊A的塊B,我們不需要找到參數<x,y,z>的組合,而只需要其唯一的入口邊緣a \rightarrow b的唯一散列。 因此,我們爲它引入了一個不同的哈希算法如下:

Fsingle(cur,prev):c

其中prev和cur是分配給塊A和B的鍵,參數c是要確定的唯一常量。

這個散列值c可以離線解析,然後在結束塊B中硬編碼。因此,CollAFL比AFL快得多,以獲得這樣的邊緣散列。 正如我們的實驗所示,在大多數應用中,超過60%的基本塊只有一個先例塊。 因此,它可以節省大量的運行時開銷,從而提高了模糊器的吞吐量。

此外,這些哈希值可以隨時解決。 因此,爲了避免衝突,我們可以等到確定所有其他邊的散列,然後選擇未使用的散列並將它們分配給只有一個先前塊的塊。

2)具有多個先例的塊的散列算法:如果塊B具有多個先前塊,即B具有多個入射邊緣,則我們必須動態計算塊B中的散列,因爲被擊中的入射邊緣僅在運行時已知。 通常,我們將使用上述等式2來計算散列。

如前所述,我們無法保證找到這個等式的解決方案以避免衝突,即使在僅使用一個先例刪除塊之後也是如此。 我們使用貪心算法來解析這些塊的參數。 我們將可以解析的塊表示爲可解塊,並表示我們無法解析爲無法解析塊的塊。

對於不可解析的塊B,我們爲其入口邊A-> B引入另一個散列算法,如下所示:

Fhash(cur,prev):hash_table_lookup(cur,prev)

其中prev和cur是塊A和B的鍵。它離線構建一個哈希表,所有邊的唯一哈希以不可解析的塊結尾,不同於所有其他邊的哈希。 在運行時,它會查找此預先計算的哈希表,以使用它們的開始和結束塊作爲鍵來獲取此類邊緣的哈希值。

在運行時,哈希表查找操作比以前的算法Fmul和Fsingle慢得多。 因此,我們應該將不可解析的塊集限制爲儘可能小。 根據我們的實驗,這套通常是空的。

3)整體緩解解決方案:首先,我們應該確保位圖大小(即散列值空間的大小)大於邊數,否則無法避免散列衝突。 然後,使用三個提議的哈希公式,即Fmul,Fsingle和Fhash,我們可以通過根據它們的類型對它們應用不同的公式來解決所有邊的哈希衝突,如下所示:

F=\left\{\begin{matrix} fmul &Solvable blocks with multi pred \\ fhash & Unsolvable blocks with ...\\ fsingle & Blocks with single precedent \end{matrix}\right.

 
  1. Algorithm 1 The collision mitigation algorithm.

  2. Input: Original program

  3. Output: Instrumented program

  4. (BBS, SingleBBS, MultiBBS, Preds) = GetCFG()

  5. Keys = AssignUniqRandomKeysToBBs(BBS)

  6. // Fixate algorithms. Preds and Keys are common arguments

  7. (Hashes, Params, Solv, Unsolv) = CalcFmul(MultiBBS)

  8. (HashMap, FreeHashes) = CalcFhash(Hashes;Unsolv)

  9. // Instrument program with coverage tracking.

  10. InstrumentFmul(Solv, Params)

  11. InstrumentFhash(Unsolv, HashMap)

  12. InstrumentFsingle(SingleBBS, FreeHashes)

a)預處理應用程序:我們首先檢索由任何靜態分析工具或編譯器提供的目標應用程序的基本塊和先前信息。如第1行所示,我們可以在程序中獲得一組基本塊BBS,並將其拆分爲兩個子集SingleBBS和MultiBBS,具體取決於塊是單個還是多個先例。每個基本塊的先前信息存儲在地圖Preds中。在第2行中,它爲程序中的每個基本塊分配唯一的隨機密鑰。此分配信息存儲在映射鍵中。

b)確定塊的算法:如第4行所示,我們首先嚐試使用CalcFmul爲具有多個先例的塊找到適當的參數,並獲得可解塊的集合Solv和不可解析的塊Unsol,以及可解塊的參數Params到目前爲止,邊緣採用的哈希解決了。在第5行中,我們使用CalcFhash爲不可解析的塊Unsol構建哈希映射HashMap,並獲取到目前爲止未解析的任何邊緣未使用的未使用的哈希FreeHashes集合。

算法2演示了CalcFmul的工作流程,即如何搜索具有多個先例的塊的參數。它首先選擇參數y然後迭代每個塊BB,並遍歷<x,z>的所有組合以找到組合,使得以該塊結束的所有邊的哈希值與其他塊不同。如果找不到任何組合,則該塊將被歸類爲無法解析並放入Unsol。否則,該塊將被放入Solv,解決方案將被放入Params。一旦處理了具有多個先例的所有基本塊,並且Unsol集足夠小,我們就找到了問題的解決方案。否則,我們將選擇另一個參數y並繼續前一個過程。

算法3(附錄A)演示了CalcFhash,即如何爲不可解析的塊Unsol構建哈希表。 簡而言之,它爲每個以無法解析的塊結尾的邊選擇隨機未使用的哈希值,並將其存儲在哈希映射HashMap中。 它還返回一組未使用的哈希FreeHashes。

c)工具塊:我們然後檢測應用程序以跟蹤邊緣覆蓋,如圖3所示。對於可解塊,我們使用Fmul對它們進行測量,即與AFL相同,但具有不同的參數<x,y,z >在Params。 對於不可解析的塊Unsolv,我們使用Fhash對每個塊進行檢測,以在HashMap中搜索在運行時以這些塊結尾的邊的哈希值。 對於具有單個先例的塊,我們在FreeHashes中爲每個塊硬編碼未使用的哈希。 通過這種方式,我們可以消除所有已知邊緣的哈希衝突。

D.業績分析

如前所述,這三種提議的哈希算法的性能開銷如下,

cost(Fhash)> cost(Fmul)>cost(Fsingle)\approx 0(6)

另一方面,根據實驗,大多數基本塊只有一個先例塊,無法解析的塊數非常少。

num(Fsingle)> num(Fmul)\gg num(Fhash)\approx 0(7)

總的來說,CollAFL使用的哈希計算引入的整體性能成本很小。如評估表II所示,對於大多數應用,我們的解決方案比AFL引入的指令更少,性能成本更低。


E.實施細節

CollAFL基於邊緣覆蓋引導的模糊AFL構建。我們擴展了AFL的llvm_mode,並編寫了一個Clang鏈接時間優化傳遞給(1)檢索所需的基本塊和邊緣信息,(2)爲每個基本塊分配唯一的密鑰,(3)解決每個基本的哈希計算算法阻止取決於其類型,以及(4)使用散列計算和覆蓋跟蹤代碼對每個塊進行檢測。通過遵循第III-C節和第3節中的算法,可以輕鬆實現最後三個步驟。

對於第一步,我們使用Clang的默認實現來獲取後繼和先前信息,例如,通過API llvm :: TerminatorInst :: getSuccessor。然而,離線控制間接控制轉移的目標是一個開放的挑戰,影響了我們所需的先前信息的精確性。例如,它可能錯誤地將一些基本塊分類爲單個(或沒有)先前塊。

因此,我們採取兩個額外步驟來改進結果。首先,我們將未被任何人直接調用的函數的入口塊標記爲多先前塊。此外,我們將間接調用指令展開到一組直接調用和間接調用指令,類似於去虛擬化技術[25]。因此,它將一些基本塊連接在一起,減少了單個先前塊的數量。因此,我們將使用Fmul而不是Fsingle來計算這些塊的哈希值,從而降低運行時發生衝突的可能性。

邊緣信息的準確性會影響我們所知道的邊緣數量。由於我們的碰撞緩解解決方案僅確保消除已知邊緣的碰撞,因此即使使用CollAFL,也可能在運行時存在一些邊緣碰撞。此外,CollAFL目前僅適用於具有源代碼的應用程序。但它也應該用於二進制文件,除了邊緣信息不太準確。我們將在未來的工作中評估其在二進制文件上的表現。

IV。優先種子選擇

現有的種子選擇策略主要關注執行速度,路徑頻率和路徑深度,但沒有一個專注於直接驅動模糊器到非探索路徑。爲實現這一目標,我們有兩個可以提供幫助的直覺:

  • 如果一條路徑有許多未探索(或未觸及)的鄰居分支,那麼這條路徑的突變很可能會探索那些未探索的分支。
  • 如果一條路徑有許多未探索(或未觸及)的鄰居後代,那麼這條路徑的突變很可能會探索那些未探索的後代。

最終目標是提高漏洞發現的有效性。爲實現這一目標,我們有另一種直覺:

  • 如果路徑具有許多內存訪問操作,則可能會觸發潛在的內存損壞漏洞,因此其突變也是如此。

遵循這些直覺的突變可以指導模糊測試人員探索更多路徑並發現更多漏洞。因此,我們基於這些直覺提出了三種新的種子選擇政策。


值得注意的是,這些政策不僅限於任何模糊器。只要提供邊緣覆蓋信息,我們就可以將這些策略應用於模糊器並提高其在漏洞發現方面的效率。

A.未觸及的鄰居分支指導政策
A.未觸及的鄰居分支引導策略在此策略中,具有更多未觸及的鄰居分支的種子將優先化爲模糊。 我們相信基於這些種子的突變有更高的概率來探索那些未觸及的鄰居分支。 爲簡單起見,我們將此政策表示爲CollAFL-br。
更具體地說,我們使用未觸摸的鄰居分支的數量作爲測試用例T的權重,如下所示:


在此策略中,具有更多未觸及的鄰居分支的種子將優先於模糊。 我們相信基於這些種子的突變有更高的概率來探索那些未觸及的鄰居分支。 爲簡單起見,我們將此政策表示爲CollAFL-br。


更具體地說,我們使用未觸摸的鄰居分支的數量作爲測試用例T的權重,如下所示:

WeightBr(T)=\sum_{\begin{matrix} bb\in Path(T)\\<bb,bb_{i}> \in EDGES \end{matrix}}IsUntouched(<bb,bb_{i}>)

當且僅當邊<bb,bb_{i}>未被任何先前的測試用例覆蓋時,函數IsUntouched返回1,否則返回0。

在本策略中,將優先考慮權重較高的種子進行模糊測試。 值得注意的是,先前運行的測試用例集將隨着測試的進行而改變,因此函數IsUntouched的返回值也將改變。 結果,測試用例的權重是動態的。

值得注意的是,如果測試用例被多次擊中,我們將多次迭代一個基本塊。 因此,循環中的塊將對總體權重貢獻更多。

B.未觸及的鄰居後裔指導政策

在這項政策中,具有更多未觸及的鄰居後代的種子將優先考慮模糊。 來自這些種子的突變有更高的概率來探索那些未觸及的鄰居後代。 我們將此政策表示爲CollAFL-desc。

更具體地說,我們將使用未觸及的鄰居後代的數量作爲測試用例T的權重,如下所示:

WeightBr(T)=\sum_{\begin{matrix} bb\in Path(T)\\IsUntouched<bb,bb_{i}>\inEDGES \end{matrix}}NumDesc(bb_{i})

函數IsUntouched與CollAFL-br策略中使用的函數相同,函數NumDesc返回從參數基本塊開始的後代路徑數。 其正式定義如下:

NumDesc(bb)=\sum_{<bb,bb_{i}> \in EDGES}NumDesc(bb_{i})

這裏的權重不是確定性的,因爲函數IsUntouched是動態的。 但是,後代子路徑的數量對於每個基本塊是確定的。 我們可以使用靜態分析來計算此值,而無需運行時開銷。 類似地,如果測試用例多次觸發基本塊,我們將多次迭代它。

C.記憶訪問指導政策

在此策略中,表示爲CollAFL-mem,具有更多內存訪問操作的種子將優先於模糊。

更具體地說,我們使用內存訪問操作的數量作爲測試用例T的權重,如下所示:

WeightMem(T)=\sum_{bb \in Path(T)}NumMemInstr(bb)

其中函數NumMemInstr返回參數基本塊中的內存訪問操作數,可以靜態計算。因此,與前兩個策略不同,以這種方式計算的權重是確定性的。類似地,如果測試用例多次觸發基本塊,我們將多次迭代它。

D.實施細節

值得注意的是,只要可以提供邊緣覆蓋和塊信息,這些策略就可以應用於任何覆蓋引導的模糊器。

我們通過替換其默認的種子選擇策略,在AFL中實施這三個策略。如前所述,我們可以在編譯時獲取每個基本塊的內存訪問操作數和後代子路徑數。

在運行時,在測試種子測試用例T之後,我們將計算其未觸及的鄰居分支和後代子路徑,以及代表性地沿着路徑的存儲器訪問操作。更具體地說,我們將首先檢查測試用例的覆蓋位圖,並獲得此測試用例覆蓋的所有邊緣以及命中計數。由於每個邊都有不同的散列,我們可以從散列中解碼每個邊的起始​​和結束塊。然後,對於每個塊,我們將根據整體覆蓋位圖獲取其未觸及的鄰居分支列表。連同我們已經收集的後代子路徑和內存訪問操作的數量,我們可以相應地計算所有三個策略的權重。

我們採取兩個額外的步驟來細化結果。首先,我們將不被任何人直接調用的函數的入口塊標記爲多先例塊。此外,我們將間接調用指令展開爲一組直接調用和一條間接調用指令,類似於去虛擬化技術[25]。因此,它將一些基本塊連接在一起,減少了單先例塊的數量。因此,我們將使用Fmul而不是Fsingle來計算這些塊的哈希值,從而降低運行時發生衝突的可能性。

VI。結論

在本文中,我們研究了覆蓋引導模糊器中覆蓋不準確性的負面影響。我們提出了一種覆蓋敏感的模糊測試解決方案CollAFL,它解決了最先進的模糊AFL中的哈希衝突問題,能夠在保持低儀器開銷的同時實現更準確的邊緣覆蓋信息。基於準確的覆蓋信息,我們提出了三種新的種子選擇策略,以便將模糊器直接驅動到非探索路徑。實驗表明,就路徑發現,崩潰發現和漏洞發現而言,該解決方案既有效又高效。我們在24個真實世界的應用程序中發現了157個新的安全漏洞,其中95個由CVE確認。

 

可參考如下兩篇文章:

https://blog.csdn.net/cn_lyxc/article/details/93866676

https://blog.csdn.net/zhang14916/article/details/90601317

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章