成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

淺談AFL++ fuzzing:如何進行有效且規整的fuzzing

安全 漏洞
大多數時候,我們fuzzing一個目標想要其達到我們預期的效果,都需要Patch。并且我們在后續fuzzing流程的持續改進中可能還會發現一些影響fuzzing效率的地方,我們又會倒回來patch,編譯重新啟動fuzzing。

input corpus

收集語料庫

對于模糊測試工具而言,我們需要為其準備一個或多個起始的輸入案例,這些案例通常能夠很好的測試目標程序的預期功能,這樣我們就可以盡可能多的覆蓋目標程序。
收集語料的來源多種多樣。通常目標程序會包含一些測試用例,我們可以將其做位我們初始語料的一部分,此外互聯網上也有些公開的語料庫你可以收集他們做為你的需要。
關于語料庫的主動性選擇,這個更多需要你對fuzzing 目標內部結構的了解。例如你當你要fuzzing的目標對隨著輸入的規模內存變化非常敏感,那么制作一批很大的文件與較小的文件可能是一個策略,具體是否是否有效取決于你經驗、以及對目標的理解。
此外,需要注意控制語料庫的規模,太過龐大的語料庫并不是好的選擇,太過潘達的語料庫會拖慢fuzzing的效率,盡可能用相對較小的語料覆蓋更多目標代碼的預期功能即可。

語料庫唯一化

我們在上一小節最后提到一點,太過龐大的語料庫會因為有太多的測試用例重復相同的路徑覆蓋,這會減慢fuzzing的效率。因此人們制作了一個工具,能夠使語料庫覆蓋的路徑唯一化,簡單的說就算去除重復的種子輸入,縮減語料庫的規模,同時保持相當的測試路徑效果。
在AFL++中可以使用工具afl-cmin從語料庫中去除不會產生新路徑和覆蓋氛圍的重復輸入,并且AFL++官方提示強烈建議我們對語料庫唯一化,這是一個幾乎不會產生壞處的友誼操作。
具體的使用如下:

  1. 將收集到的所有種子文件放入一個目錄中,例如 INPUTS
  2. 運行 afl-cmin:

字典

其實將字典放到這一個大節下面不是很合適,因為字典可以歸類為一種輔助技巧,不過因為字典影響輸入,所以我就將其劃到這里了。
關于是否使用字典,取決于fuzzing的目的與目標。例如fuzzing的目標是ftp服務器,我們fuzzing的目的是站在用戶的視角僅能輸入命令,因此我們的輸入其中很大一部分可以規范到ftp提供的命令,我們更多的是通過重復測試各種命令的組合來測試目標ftp服務器在各種場景都能正確運行。
又比如,當你fuzzing一個很復雜的目標時,它通常提供一個非常非常豐富的命令行參數,每一次運行時組合不同的參數可能會有更好的覆蓋效果,因此可以將你需要啟用的參數標記為字典添加進命令行參數列表中。
最后,目標程序可能經常有常量的比較和驗證,而這些環節通常會使得fuzzing停滯在此,因為模糊器的變異策略通常對應常量的猜測是非常低效的。我們可以收集目標程序中使用到的常量,定義為一個字典提供給模糊器。但目前對于AFL++來說有更好的方法解決這種需求,而無需定義字典,后面我們會介紹這些方法。

# 模糊器默認的變異策略通常難以命中if分支為true的情況,因為input做為64位,其值的空間太大了,根本難以猜測。
if (input = 0x1122336644587) {
	crash();
}
else {
	OK();
}

編譯前的準備

選擇最佳的編譯器

如我們上一節中談到收集程序常量定義字典時,事實上收集常量并生成字典這個事情,在編譯時完全可以順便將其解決。沒錯,功能強大的編譯器可以使我們在編譯期間獲得非常多有用的功能。對于AFL++的編譯器選擇,官方提供了一個簡單的選擇流程,如下

+--------------------------------+
| clang/clang++ 11+ is available | --> use LTO mode (afl-clang-lto/afl-clang-lto++)
+--------------------------------+     see [https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.lto.md](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.lto.md)
    |
    | if not, or if the target fails with LTO afl-clang-lto/++
    |
    v
+---------------------------------+
| clang/clang++ 3.8+ is available | --> use LLVM mode (afl-clang-fast/afl-clang-fast++)
+---------------------------------+     see [https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.llvm.md](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.llvm.md)
    |
    | if not, or if the target fails with LLVM afl-clang-fast/++
    |
    v
 +--------------------------------+
 | gcc 5+ is available            | -> use GCC_PLUGIN mode (afl-gcc-fast/afl-g++-fast)
 +--------------------------------+    see [https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.gcc_plugin.md](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.gcc_plugin.md) and
                                       [https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.instrument_list.md](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.instrument_list.md)
    |
    | if not, or if you do not have a gcc with plugin support
    |
    v
   use GCC mode (afl-gcc/afl-g++) (or afl-clang/afl-clang++ for clang)

若你的LLVM和clang版本大于等于11,那么你可以啟用LLVM LTO模式,使用afl-clang-lto/afl-clang-lto++,該模式通常是最佳的。隨后依次是afl-clang-fast/afl-clang-fast++和afl-gcc-fast/afl-g++-fast。
關于為什么LTO模式通常是最佳的,其中一個原因是它解決了原版AFL中邊碰撞的情況,提供了無碰撞的邊(edge)檢測。在原本AFL中,因為其對邊(edge)的標識是隨機的,對于AFL默認2^16容量來說,一旦程序足夠大,邊的標識會重復,這種現象就算邊碰撞,它會降低模糊測試的效率。此外LTO模式會自動收集目標代碼中的常量制作成為一個字典并自動啟用,并且社區提供的一些有用的插件和功能很多時候是要求LLVM模式(clang-fast)甚至是LTO模式(clang-lto)。

NOTE:此處涉及一點AFL度量覆蓋率的工作原理,可以參考我注意的另一篇文章《基于覆蓋率的Fuzzer和AFL》,寫的很一般(逃

關于編譯器的選擇,如果可能直接選LTO模式即可。但你需要注意,LTO模式編譯代碼非常的吃內存,編譯時間也會很久,尤其是啟用某些Sanitizer的時候。

NOTE:你的計算機配置最好至少由8核心,內存最好不低于16G。請注意8核心,16G仍然不是很夠用,最好32G,16核或以上,核心越多越好。因為到時候你會編譯很多不同版本的程序,不同的插件、不同的sanitizer、不同策略等等,這些不同的選項往往不能兼并到一個程序上,往往需要編譯多分不同配置的程序,并你會經常patch程序再編譯測試patch的效果。簡言之,你會編譯很多次程序,你需要足夠大的內存和核心來編譯目標,使得你不必經常阻塞等待編譯隊列和結果。

編譯的選項

AFL++是一個非常活躍的社區,AFL++會集成社區中、互聯網上一些強大的第三方插件,這些集成的插件有一些我們可以通過設置對應的編譯選項啟用。
對于LTO模式(afl-clang-fast/afl-clang-lto)進行編譯插樁時,可以啟用下面兩項比較通用的特性,主要用于優化一些固定值的比較和校驗。

  • Laf-Intel:能夠拆分程序中整數、字符串、浮點數等固定常量的比較和檢測。考慮下面一個情況assert x == 0x11223344,Laf-Intel會拆分為assert (x & 0xff) == 0x44 && ((x >> 8) & 0xff) == 0x33 ....這樣形式,每一次只會進行單字節的比較,這樣AFL就可以逐個字節的猜測,每當確定一個字節時,就會發現一個新的路徑,進而繼續在第一個字節的基礎上猜測第二個字節,如此使得模糊器可以快速猜出0x11223344。如果你沒有自己制作好的字典、豐富的語料庫,這個功能會非常有用,通常建議至少有一個AFL++實例運行Laf-Intel插件。在編譯前設置如下環境使用:export AFL_LLVM_LAF_ALL=1
  • CmpLog:這個插件會提取程序中的比較的固定值,這些值會被用于變異算法中。功能與Laf-Intel類似,但效果通常比Laf-Intel。使用該插件需要單獨編譯一份cmplog版本的程序,在fuzzing時指定該cmplog版本加入到fuzzing中。具體的用法如下:
# 編譯一份常規常規版本
cd /target/path
CC=afl-clang-lto  make -j4
cp ./program/path/target ./target/target.afl

# 編譯cmplog版本
make clean
export AFL_LLVM_CMPLOG=1
CC=afl-clang-lto  make -j4
cp ./program/path/target ./target/target.cmplog
unset AFL_LLVM_CMPLOG

# 使用cmplog, 用-c參數指定cmplog版本目標,因為cmplog回申請很多內存做映射因此我們設置
# -m none,表示不限制afl-fuzz的內存使用。你也可以指定一個值例如 -m 1024,即1GB。
afl-fuzz -i input -o output -c ./target.cmplog -m none -- ./target.afl @@

NOTE:需要注意,兩個插件并不是說誰替代誰,往往在實際fuzzing中兩者都會用至少一個afl實例啟用。

考慮下面兩種場景。
有時候你想要fuzzing的目標中,他自動的集成了很多第三方的庫代碼,他們會在編譯中一并編譯,而你并不想fuzzing這些第三方庫來,你只想高效、快速的fuzzing目標的代碼,額外的fuzzing第三方代碼只會拖慢你fuzzing的效率。
有時候你的目標會非常龐大和復雜,他們的構建往往是模塊化的,有時候你只想fuzzing某幾個模塊。
這上面兩種情況都是我們fuzzing中很常遇見的,所幸AFL++提供了部分插樁編譯的功能,即"partial instrumentation",它允許我們指定應該檢測那些內容以及不應該檢測那些內容,這個檢測的顆粒是代碼源文件、函數級兩級。具體用法如下:

  • 檢測指定部分。創建一個文件(allowlist.txt,文件名沒有要求),需要在其中指定應包含檢測的源代碼文件或者函數。
  • 1.在文件中每行輸入一個文件名或函數
foo.cpp # 將會匹配所有命名為foo.cpp的文件,注意是所有命名為foo.cpp的文件
path/foo.cpp # 將會只確定的包含該路徑的foo.cpp文件,不會造成意外的包含
fun:foo_fun # 將會包含所有foo_fun函數
  1. 設置export AFL_LLVM_ALLOWLIST=allowlist.txt 啟用選擇性檢測
  • 排除某些部分。與指定某些部分類似,編寫一個文件然后設置環境變量export AFL_LLVM_DENYLIST=denylist.txt以啟用,這會跳過我們文件中指定的內容。

Note:有些小函數可能在編譯期間被優化,內聯到上級調用者,即類似于宏函數展開。這時將會導致指定失效!如果不想受此影響,禁用內聯函數優化即可。
此外,對于C++由于函數命名粉碎機制,你需要特別的提取粉碎后的函數名。例如函數名為test的函數可能會被粉碎重命名為_Z4testv。可以用nm提取函數名,創建一個腳本篩選出來。

添加Sanitizer檢測更多BUG

Sanitizer最初是Google的一個開源項目,它們是一組檢測工具。例如AddressSanitizer是一個內存錯誤檢測器,可以檢測諸如OOB、UAF、Double-free等到內存錯誤的場景。現在該項目以及成為LLVM的一部分,相對較高的gcc和clang都默認包含Sanitizer功能。
由于AFL++基本只會檢測到導致Crash的BUG,因此啟用一些Sanitizer可以使得我們檢測一些并不會導致Crash的錯誤,例如內存泄露。
AFL++內置支持下面幾種Sanitizer:

  • ASAN:AddressSanitizer,用于發現內存錯誤的bug,如use-after-free空指針解引用(NULL pointer dereference)緩沖區溢出(buffer overruns)Stack And Heap OverflowDouble Free/ Wild FreeStack use outside scope等。若要使用請在編譯前設置環境變量export AFL_USE_MSAN=1。更多關于ASAN的信息參與LLVM官網對ASAN:AddressSanitizer的描述(https://clang.llvm.org/docs/AddressSanitizer.html)。
  • MSAN:MemorySanitizer,用于檢測對未初始化內存的訪問。若要啟用,在編譯前設置export AFL_USE_MSAN=1以啟用。
  • UBSAN:UndefinedBehavior Sanitizer,如其名字一般用于檢測和查找C和C++語言標的未定義行為。未定義行為是語言標準沒有定義的行為,編譯器在編譯時可能不會報錯,然而這些行為導致的結果是不可預測的,對于程序而言是一個極大的隱患。請在編譯前,設置export AFL_USE_UBSAN=1環境變量以啟用。
  • CFISAN:Control Flow Integrity Sanitizer,CFI的實現有多種,它們是為了在程序出現未知的危險行為時終止程序,這些危險行為可能導致控制流劫持或破壞,用于預防ROP。在Fuzzing中,CFISAN主要用于檢測類型混淆。請在編譯前,設置export AFL_USE_CFISAN=1環境變量以啟用。
  • TSAN:Thread Sanitizer, 用于多線程環境下數據競爭檢測。在目前,計算機通常都是多核,一個進程中通常包含多個進程,常常導致一個問題,即數據競爭。此類錯誤通常很難通過調試發現,出現也不穩定。當至少兩個線程訪問同一個變量,并且同時存在讀取和寫入的行為時,即發送了數據競爭,若讀取在寫入之后,線程可能讀取到非預期的數據,可能導致嚴重的錯誤。請在編譯前,設置export AFL_USE_TSAN=1環境變量以啟用。
  • LSAN,Leak Sanitizer,用于檢測程序中的內存泄露。內存泄露通常并不會導致程序crash,但它是一個不穩定的因素,可能會被利用、也可能沒辦法被利用,這不是一個嚴格意義上的漏洞。與其他Sanitizer的使用不同,需要將__AFL_LEAK_CHECK();添加到你想要進行內存泄露檢查的目標源代碼的所有區域。在編譯之前啟用 ,export AFL_USE_LSAN=1。要忽略某些分配的內存泄漏檢查,__AFL_LSAN_OFF(); 可以在分配內存之前和__AFL_LSAN_ON();之后使用,LSAN不會檢查這兩個宏之間區域。

Note:

  1. 一些Sanitizer不能混用,而即使有些可以同時允許的Santizier也可能導致意想不到的行為影響fuzzing,這需要結合你fuzzing的目標情況而定。如果你不熟悉Sanitizer的原理,最好一個編譯實例中只啟用一個Sanitizer,這樣通常不會出問題,而且組合Sanitizer不見得會有好效果,基于對目標的了解正確的使用Sanitizer才是最佳的實踐。
  2. 有些Sanitizer提供了參數設置的環境變量,如ASAN_OPTIONS,如果你有很明確的需求可以設置該變量進一步限制Sanitizer的檢測行為,這可能會提高你fuzzing的效率。如果你不熟悉、也沒有明確的需求,那么保持默認即可,這通常是最實用的。
  3. 啟用CFISAN的實例,可能會檢測出很多crash(成百上千),這是正常的,但大多數是無用的,甚至全是無用的,你需要注意甄別。
  4. 如果你對目標內部結構足夠熟悉,你確定那些區域是線程并發的高發區域,那么你可以結合TSAN與partial instrumentation功能提高TSAN的檢測效率,因為啟用TSAN的實例通常fuzzing速度會大幅減慢。
  5. 通常啟Sanitizer后,會大幅減慢fuzzing的速度,CPU每秒執行次數會減少,內存也會被大量消耗(AddressSanitizer會大量消耗內存,甚至可能導致計算機內存耗盡)。如果你的計算機配置不行,請斟酌一個合理的搭配。
  6. 一種Sanitizer只應該允許一個實例 。在兩個實例上允許兩個同樣的Sanitizer是一種浪費,因為AFL++會同步所有實例的testcase,其他實例的testcase無論如何都會被該實例上的Sanitizer檢測一遍,不應該啟用兩個相同的Sanitizer檢測兩遍,這會減慢效率。

暫時只想到這些,以后想到了再補充。

LLVM Persistent Mode

In-process fuzzing是一個強大功能,通常比默認常規編譯fuzzing的速度快得多,大概快10-20倍,并且基本沒有任何缺點。如果可以,請毫不猶豫的使用Persistent mode。
眾所周知,AFL使用ForkServer來進行每次fuzzing,然而即便不用execve這種巨大的開銷,但fork仍然是一筆不小的開。而Persistent fuzzing即一次fork進程種進行多次fuzzing,而無需每次都fork。
Persistent mode提供一組AFL++的函數和宏,我們使用下面的形式,用一個while包含我們要進行Persistent fuzzing的區域。請注意,該區域的代碼必須要是無狀態的,要么是可以手動可靠的重置為初始狀態!這樣我們才能再每次fuzzing時重置進而再次fuzzing。
afl-clang-fast/lto編譯的情況下,只需要使用下面的形式即可,但若不是,則復雜一些。
AFL++官方的倉庫對Persistent Mode花了不小的篇幅講訴,講的也比較全面,請在此處Persistent Mode中查閱,我就不做過多描述了。

#include "what_you_need_for_your_target.h"

__AFL_FUZZ_INIT();

int main() {

	// anything else here, e.g. command line arguments, initialization, etc.

	#ifdef __AFL_HAVE_MANUAL_CONTROL
	__AFL_INIT();
	#endif

	unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;  // must be after __AFL_INIT
	// and before __AFL_LOOP!

	while (__AFL_LOOP(10000)) {

		int len = __AFL_FUZZ_TESTCASE_LEN;  // don't use the macro directly in a
		// call!

		if (len < 8) continue;  // check for a required/useful minimum input length

		/* Setup function call, e.g. struct target *tmp = libtarget_init() */
		/* Call function to be fuzzed, e.g.: */
		target_function(buf, len);
		/* Reset state. e.g. libtarget_free(tmp) */

	}

	return 0;
}

Patch

大多數時候,我們fuzzing一個目標想要其達到我們預期的效果,都需要Patch。并且我們在后續fuzzing流程的持續改進中可能還會發現一些影響fuzzing效率的地方,我們又會倒回來patch,編譯重新啟動fuzzing。
此外,有時候一些校驗、檢查,它們往往對于fuzzing的結果沒有什么影響,但是卻嚴重影響fuzzing的效率。此時我們通常會審查目標內部代碼,將這些嚴重性fuzzing效率的地方Patch,或者是刪除。
我們都知道Persistent Mode收益十分巨大,但卻要求Persistent循環區域內的代碼是無狀態的,有時候區域會有一些有狀態的函數,但他們卻并不重要,這時你可以Patch它們,使它們返回諸如硬編碼之類可以,這樣就變成無狀態的,我們就可以使用Persistent Mode了。
例如一個區域的輸入可能依賴于socket IO讀入,而處理socket IO是很麻煩的,因此我們可以考慮將socket fd替換為文件 fd,并patch那些受socket fd受影響區域,以便我們fuzzing正確運行。
簡言之,Patch最好有明確的理由,隨意的Patch對模糊測試來說可能會導致很糟糕的現象,要么你對此處的Patch是基于改進fuzzing效率,要么是為了啟用某些有益的fuzzing功能....總之,最好清楚自己的Patch是為了什么。
但請注意,對于一次模糊測試來說Patch只是可選的,如果你對自己的工具、目標不甚了解,那么Patch對你而言可能不重要。如果你清楚目標的內部結構,并且明確知道要改進fuzzing的流程和目的,那么Patch可能是你定制化自己fuzzing的一個重要手段。

后續

目前就先寫到著,后面的內容,包括build、fuzzing、評估、流程改進等等就放到下篇,最近的工作可能要忙一些其他的。

本文作者:Cheney辰星, 轉載請注明來自FreeBuf.COM

責任編輯:武曉燕 來源: FreeBuf.COM?
相關推薦

2020-10-16 09:42:22

漏洞

2021-06-08 11:19:43

GoBeta測試Fuzzing

2016-03-30 11:20:10

2009-12-16 17:58:18

2014-07-17 11:33:47

2020-10-20 10:12:00

Windows

2017-05-02 10:13:46

2024-04-12 11:38:20

數據中心運營商

2011-07-29 12:18:30

2020-09-29 10:44:51

漏洞

2009-07-22 13:04:49

網絡管理網絡設備

2009-12-01 14:38:28

路由器上網配置

2009-11-16 14:06:31

2009-12-01 17:44:44

2021-03-29 10:39:07

evSecOps信息安全系統安全

2022-08-09 18:26:04

KubernetesLinux

2009-11-30 10:19:50

VPN連接ADSL路由器

2010-03-29 14:26:57

無線網絡故障修復

2017-05-08 07:37:56

2009-11-30 14:59:01

路由器設置
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 荷兰欧美一级毛片 | 日本久久久久久久久 | 国产亚洲精品精品国产亚洲综合 | 久久久久久久国产 | 黄色高清视频 | 91精品亚洲 | 欧美一级做a爰片免费视频 国产美女特级嫩嫩嫩bbb片 | 三级黄色片在线 | 欧美寡妇偷汉性猛交 | 午夜精品影院 | 四虎永久免费地址 | 久久久久久久久久久一区二区 | 国内自拍视频在线观看 | 91在线视频免费观看 | 日本在线网站 | 色吊丝在线 | 中文av在线播放 | 日韩一区二区三区在线视频 | 九九热这里 | 99精品视频在线 | 亚洲午夜视频在线观看 | 一级毛片免费 | 精品欧美乱码久久久久久1区2区 | 欧洲免费毛片 | 色姑娘av| 欧美一区二区三区四区在线 | 国产在线精品一区二区三区 | 99精品视频免费观看 | 国产精品毛片在线 | 青草久久免费视频 | 欧美日韩在线免费 | 国产精品视频不卡 | 久久久久久久一区 | 日本三级全黄三级a | 99re6在线视频精品免费 | 亚洲第1页 | 一级黄色短片 | 亚洲欧洲精品在线 | 欧美精品一区三区 | 国产精品福利在线 | 国精产品一区二区三区 |