最初由 Michal Zalewski lcamt[email protected] 開發。
如果您沒有時間閱讀此文件,請參閱 QuickStartGuide.txt。
模糊測試是識別現實軟體中安全問題的最強大且經過驗證的策略之一;它是迄今為止在安全關鍵軟體中發現的絕大多數遠端程式碼執行和權限升級錯誤的罪魁禍首。
不幸的是,模糊測試也相對較淺。盲目的隨機突變使其不太可能到達測試程式碼中的某些程式碼路徑,從而使某些漏洞完全超出了該技術的範圍。
已經有許多嘗試來解決這個問題。由 Tavis Ormandy 首創的早期方法之一是語料庫蒸餾。該方法依靠覆蓋訊號從大量高品質的候選文件語料庫中選擇感興趣的種子子集,然後透過傳統方式對它們進行模糊測試。該方法效果非常好,但需要這樣的語料庫隨時可用。此外,區塊覆蓋率測量僅提供對程式狀態的非常簡單的理解,並且對於指導長期的模糊測試工作不太有用。
其他更複雜的研究則集中在程序流程分析(「concolic執行」)、符號執行或靜態分析等技術上。所有這些方法在實驗環境中都非常有前途,但在實際使用中往往會遇到可靠性和性能問題——並且目前還沒有提供「啞」模糊技術的可行替代方案。
American Fuzzy Lop 是一種強力模糊器,結合了極其簡單但堅如磐石的儀器引導遺傳演算法。它使用一種修改形式的邊緣覆蓋來輕鬆地獲取程序控制流的細微的、局部範圍的變化。
稍微簡化一下,整體演算法可以總結為:
將使用者提供的初始測試案例載入到佇列中,
從隊列中取出下一個輸入文件,
嘗試將測試案例修剪到不會改變程式的測量行為的最小尺寸,
使用平衡且經過充分研究的各種傳統模糊測試策略反覆變異文件,
如果任何產生的突變導致儀器記錄新的狀態轉換,請將突變輸出新增為佇列中的新條目。
轉到2。
發現的測試案例也會定期剔除,以消除那些被更新、覆蓋率更高的發現所淘汰的測試案例;並經歷其他幾個儀器驅動的工作最小化步驟。
作為模糊測試過程的副作用,該工具創建了一個小型的、獨立的有趣測試案例語料庫。這些對於播種其他勞動或資源密集型測試制度非常有用 - 例如,用於壓力測試瀏覽器、辦公室應用程式、圖形套件或閉源工具。
此模糊器經過徹底測試,其開箱即用的性能遠遠優於盲目模糊測試或僅覆蓋工具。
當原始程式碼可用時,可以透過配套工具注入檢測,該工具可以在第三方程式碼的任何標準建置過程中作為 gcc 或 clang 的直接替代品。
該儀器對性能的影響相當有限;與 afl-fuzz 實現的其他最佳化相結合,大多數程式都可以比傳統工具更快甚至更快地進行模糊測試。
重新編譯目標程式的正確方法可能會根據建置過程的具體情況而有所不同,但幾乎通用的方法是:
$ CC=/path/to/afl/afl-gcc ./configure
$ make clean all
對於 C++ 程序,您還需要設定CXX=/path/to/afl/afl-g++
。
clang 包裝器(afl-clang 和 afl-clang++)可以以相同的方式使用; clang 使用者也可以選擇利用更高效能的偵測模式,如 llvm_mode/README.llvm 所述。
測試庫時,您需要找到或編寫一個簡單的程序,從標準輸入或檔案中讀取資料並將其傳遞到測試的庫。在這種情況下,必須將此可執行檔案連結到偵測程式庫的靜態版本,或確保在執行時間載入正確的 .so 檔案(通常透過設定LD_LIBRARY_PATH
)。最簡單的選項是靜態構建,通常可以透過以下方式實現:
$ CC=/path/to/afl/afl-gcc ./configure --disable-shared
呼叫「make」時設定AFL_HARDEN=1
將導致 CC 包裝器自動啟用程式碼強化選項,從而更容易偵測簡單的記憶體錯誤。 Libdislocator,AFL 中包含的輔助函式庫(請參閱 libdislocator/README.dislocator)也可以協助發現堆損壞問題。
附言。建議 ASAN 使用者查看notes_for_asan.txt 檔案以了解重要注意事項。
當原始程式碼不可用時,模糊器會為黑盒二進位檔案的快速、動態偵測提供實驗支援。這是透過在鮮為人知的「用戶空間模擬」模式下運行的 QEMU 版本來完成的。
QEMU 是一個獨立於 AFL 的項目,但您可以透過執行以下操作來輕鬆建立該功能:
$ cd qemu_mode
$ ./build_qemu_support.sh
有關其他說明和注意事項,請參閱 qemu_mode/README.qemu。
該模式比編譯時檢測慢約 2-5 倍,不太利於並行化,並且可能有一些其他怪癖。
為了正確運行,模糊器需要一個或多個起始文件,其中包含目標應用程式通常期望的輸入資料的良好範例。有兩個基本規則:
保持文件小。儘管不是絕對必要的,但低於 1 kB 是理想的。有關大小為何重要的討論,請參閱 perf_tips.txt。
僅當多個測試案例在功能上彼此不同時才使用它們。使用五十張不同的度假照片來模糊圖像庫是沒有意義的。
您可以在該工具隨附的 testcases/ 子目錄中找到許多啟動檔案的好範例。
附言。如果有大量資料可供篩選,您可能需要使用 afl-cmin 公用程式來識別功能不同的檔案的子集,這些檔案在目標二進位檔案中執行不同的程式碼路徑。
模糊測試過程本身由 afl-fuzz 實用程式執行。該程式需要一個包含初始測試案例的唯讀目錄、一個儲存其結果的單獨位置以及要測試的二進位檔案的路徑。
對於直接從 stdin 接受輸入的目標二進位文件,通常的語法是:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
對於從檔案取得輸入的程序,請使用「@@」來標記目標命令列中應放置輸入檔案名稱的位置。模糊器將代替你:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
您也可以使用 -f 選項將變異資料寫入特定檔案。如果程式需要特定的檔案副檔名等,這很有用。
未偵測的二進位檔案可以在 QEMU 模式(在命令列中新增 -Q)或傳統的盲模糊器模式(指定 -n)中進行模糊測試。
您可以使用 -t 和 -m 覆蓋執行進程的預設逾時和記憶體限制;可能需要觸及這些設定的目標的罕見範例包括編譯器和視訊解碼器。
perf_tips.txt 中討論了優化模糊測試效能的技巧。
請注意,afl-fuzz 首先執行一系列確定性模糊測試步驟,這可能需要幾天時間,但往往會產生簡潔的測試案例。如果您想要立即獲得快速而骯髒的結果 - 類似於 zzuf 和其他傳統模糊器 - 將 -d 選項新增至命令列。
有關如何解釋顯示的統計資訊和監視進程運行狀況的信息,請參閱 status_screen.txt 檔案。請務必查閱此文件,尤其是當任何 UI 元素以紅色突出顯示時。
模糊測試過程將繼續,直到您按下 Ctrl-C。至少,您希望允許模糊器完成一個隊列週期,這可能需要幾個小時到一周左右的時間。
輸出目錄中創建了三個子目錄並即時更新:
queue/ - 每個獨特執行路徑的測試案例,以及使用者給予的所有起始檔案。這是第 2 節中提到的合成語料庫。該工具將找到提供同等邊緣覆蓋範圍的較小檔案子集。
crashes/ - 導致被測試程式接收致命訊號的獨特測試案例(例如,SIGSEGV、SIGILL、SIGABRT)。這些條目按接收到的訊號進行分組。
hangs/ - 導致測試程式逾時的獨特測試案例。某些內容被分類為掛起之前的預設時間限制是 1 秒和 -t 參數的值中的較大者。該值可以透過設定 AFL_HANG_TMOUT 進行微調,但這很少有必要。
如果關聯的執行路徑涉及先前記錄的故障中未出現的任何狀態轉換,則崩潰和掛起被認為是「唯一的」。如果可以透過多種方式到達單一錯誤,則在此過程的早期會出現一些計數膨脹,但這應該很快就會逐漸減少。
崩潰和掛起的檔案名稱與父級、無故障佇列條目相關。這應該有助於調試。
當您無法重現 afl-fuzz 發現的崩潰時,最可能的原因是您沒有設定與該工具使用的記憶體限制相同的記憶體限制。嘗試:
$ LIMIT_MB=50
$ ( ulimit -Sv $[LIMIT_MB << 10] ; /path/to/tested_binary ... )
更改 LIMIT_MB 以匹配傳遞給 afl-fuzz 的 -m 參數。在 OpenBSD 上,也將 -Sv 更改為 -Sd。
任何現有的輸出目錄也可用於恢復中止的作業;嘗試:
$ ./afl-fuzz -i- -o existing_output_dir [...etc...]
如果安裝了 gnuplot,您也可以使用 afl-plot 為任何活動的模糊測試任務產生一些漂亮的圖表。有關其外觀的範例,請參閱 http://lcamuf.coredump.cx/afl/plot/。
afl-fuzz 的每個實例大約佔用一個核心。這意味著在多核心系統上,需要並行化才能充分利用硬體。有關如何在多核心或多台連網機器上模糊公共目標的提示,請參閱parallel_fuzzing.txt。
平行模糊測試模式還提供了一種將 AFL 與其他模糊器、符號或 concolic 執行引擎等連接的簡單方法;再次,請參閱parallel_fuzzing.txt 的最後一部分以取得提示。
預設情況下,afl-fuzz 變異引擎針對緊湊資料格式進行了最佳化 - 例如影像、多媒體、壓縮資料、正規表示式語法或 shell 腳本。它不太適合那些特別冗長和多餘的語言——特別是 HTML、SQL 或 JavaScript。
為了避免建立語法感知工具的麻煩,afl-fuzz 提供了一種方法,透過可選的語言關鍵字字典、魔術頭或與目標資料類型相關的其他特殊標記來為模糊測試過程提供種子,並使用它來重建隨時隨地的底層語法:
http://lcamuf.blogspot.com/2015/01/afl-fuzz-making-up-grammar-with.html
要使用此功能,您首先需要以 dictionaries/README.dictionaries 中討論的兩種格式之一建立字典;然後透過命令列中的 -x 選項將模糊器指向它。
(此子目錄中也已經提供了幾個常用字典。)
無法提供底層語法的更結構化的描述,但模糊器可能會僅根據儀器回饋來找出其中的一些內容。這在實踐中確實有效,例如:
http://lcamuf.blogspot.com/2015/04/finding-bugs-in-sqlite-easy-way.html
附言。即使沒有給出明確的字典,afl-fuzz 也會在確定性位元組翻轉期間透過密切觀察檢測來嘗試提取輸入語料庫中現有的語法標記。這適用於某些類型的解析器和語法,但不如 -x 模式。
如果字典確實很難獲得,另一種選擇是讓 AFL 運行一段時間,然後使用 AFL 附帶的令牌捕獲庫。為此,請參閱 libtokencap/README.tokencap。
基於覆蓋範圍的崩潰分組通常會產生一個小資料集,可以手動或使用非常簡單的 GDB 或 Valgrind 腳本快速分類。每個崩潰還可以追溯到佇列中其父級非崩潰測試案例,從而更容易診斷故障。
話雖如此,重要的是要承認,如果不進行大量調試和程式碼分析工作,一些模糊測試崩潰可能很難快速評估其可利用性。為了協助完成此任務,afl-fuzz 支援使用 -C 標誌啟用的非常獨特的「崩潰探索」模式。
在這種模式下,模糊器將一個或多個崩潰測試案例作為輸入,並使用其反饋驅動的模糊策略非常快速地列舉程式中可以到達的所有程式碼路徑,同時保持程式處於崩潰狀態。
不會導致崩潰的突變會被拒絕;任何不影響執行路徑的變更也是如此。
輸出是一個小的文件語料庫,可以非常快速地檢查它,以了解攻擊者對錯誤地址的控製程度,或者是否有可能透過初始越界讀取,並查看背後的內容。
哦,還有一件事:為了最小化測試案例,請嘗試 afl-tmin。該工具的操作方式非常簡單:
$ ./afl-tmin -i test_case -o minimized_result -- /path/to/program [...]
該工具適用於崩潰和非崩潰測試案例。在崩潰模式下,它會很樂意接受已檢測和未檢測的二進位。在非崩潰模式下,最小化器依靠標準 AFL 工具來使檔案更簡單,而不改變執行路徑。
最小化器以與 afl-fuzz 相容的方式接受 -m、-t、-f 和 @@ 語法。
AFL 最近新增的另一個工具是 afl-analyze 工具。它需要一個輸入文件,嘗試順序翻轉字節,並觀察被測試程式的行為。然後,它根據哪些部分顯得重要、哪些部分不重要,對輸入進行顏色編碼;雖然不是萬無一失的,但它通常可以提供對複雜文件格式的快速洞察。有關其操作的更多資訊可以在 Technical_details.txt 末尾附近找到。
模糊測試也是一種奇妙但未被充分利用的技術,用於發現非崩潰的設計和實作錯誤。透過修改目標程式以在以下情況下呼叫 abort() ,發現了相當多有趣的錯誤:
當給定相同的模糊器產生的輸入時,兩個 bignum 庫會產生不同的輸出,
當要求影像庫連續多次解碼相同的輸入影像時,影像庫會產生不同的輸出,
當迭代序列化和反序列化模糊器提供的資料時,序列化/反序列化庫無法產生穩定的輸出,
當要求壓縮然後解壓縮特定 blob 時,壓縮庫會產生與輸入檔案不一致的輸出。
實施這些或類似的健全性檢查通常只需要很少的時間;如果您是特定套件的維護者,則可以使用#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
(也與 libfuzzer 共享的標誌)或#ifdef __AFL_COMPILER
(這僅適用於 AFL)將此程式碼設為條件。
請記住,與許多其他運算密集型任務類似,模糊測試可能會對您的硬體和作業系統造成壓力。尤其:
您的 CPU 運作時會很熱,需要足夠的冷卻。在大多數情況下,如果冷卻不足或停止正常工作,CPU 速度將自動降低。也就是說,特別是在不太合適的硬體(筆記型電腦、智慧型手機等)上進行模糊測試時,某些東西爆炸並非完全不可能。
目標程式最終可能會不穩定地佔用千兆位元組的記憶體或用垃圾檔案填充磁碟空間。 AFL 試圖強制實施基本的記憶體限制,但無法防止每一個可能的事故。最重要的是,您不應該對資料遺失風險不可接受的系統進行模糊測試。
模糊測試涉及對檔案系統進行數十億次讀取和寫入。在現代系統上,這通常會被大量緩存,從而導致相當適度的“物理”I/O - 但有許多因素可能會改變這個等式。您有責任監控潛在的問題;如果 I/O 非常繁重,許多 HDD 和 SSD 的使用壽命可能會縮短。
在 Linux 上監視磁碟 I/O 的一個好方法是「iostat」指令:
$ iostat -d 3 -x -k [...optional disk ID...]
以下是 AFL 的一些最重要的注意事項:
AFL 透過檢查第一個產生的進程是否因訊號(SIGSEGV、SIGABRT 等)而死亡來偵測故障。為這些訊號安裝自訂處理程序的程式可能需要註解掉相關程式碼。同樣,由模糊目標產生的子進程中的錯誤可能會逃避檢測,除非您手動添加一些程式碼來捕獲該錯誤。
與其他暴力工具一樣,如果使用加密、校驗和、加密簽名或壓縮來完全包裝要測試的實際資料格式,則模糊器提供的覆蓋範圍有限。
要解決此問題,您可以註解掉相關檢查(請參閱experimental/libpng_no_checksum/以獲取靈感);如果這是不可能的,您也可以編寫一個後處理器,如experimental/post_library/中所述。
ASAN 和 64 位元二進位檔案之間存在一些不幸的權衡。這並不是由於 afl-fuzz 的任何特定錯誤所致;而是由於 afl-fuzz 的任何特定錯誤所致。有關提示,請參閱notes_for_asan.txt。
不直接支援模糊網路服務、後台守護程式或需要 UI 互動才能運作的互動式應用程式。您可能需要進行簡單的程式碼更改,以使它們以更傳統的方式運行。 Preeny 也可能提供一個相對簡單的選項 - 請參閱:https://github.com/zardus/preeny
您也可以在以下位置找到修改基於網路的服務的一些有用提示:https://www.fastly.com/blog/how-to-fuzz-server-american-fuzzy-lop
AFL 不輸出人類可讀的報道資料。如果您想監控覆蓋範圍,請使用 Michael Rash 的 afl-cov:https://github.com/mrash/afl-cov
有時,有感知能力的機器會反抗它們的創造者。如果您遇到這種情況,請參閱http://lcamuf.coredump.cx/prep/。
除此之外,請參閱安裝以取得特定於平台的提示。
如果沒有以下機構的回饋、錯誤報告或補丁,afl-fuzz 的許多改進都是不可能實現的:
Jann Horn Hanno Boeck
Felix Groebert Jakub Wilk
Richard W. M. Jones Alexander Cherepanov
Tom Ritter Hovik Manucharyan
Sebastian Roschke Eberhard Mattes
Padraig Brady Ben Laurie
@dronesec Luca Barbato
Tobias Ospelt Thomas Jarosch
Martin Carpenter Mudge Zatko
Joe Zbiciak Ryan Govostes
Michael Rash William Robinet
Jonathan Gray Filipe Cabecinhas
Nico Weber Jodie Cunningham
Andrew Griffiths Parker Thompson
Jonathan Neuschfer Tyler Nighswander
Ben Nagy Samir Aguiar
Aidan Thornton Aleksandar Nikolich
Sam Hakim Laszlo Szekeres
David A. Wheeler Turo Lamminen
Andreas Stieger Richard Godbee
Louis Dassy teor2345
Alex Moneger Dmitry Vyukov
Keegan McAllister Kostya Serebryany
Richo Healey Martijn Bogaard
rc0r Jonathan Foote
Christian Holler Dominique Pelle
Jacek Wielemborek Leo Barnes
Jeremy Barnes Jeff Trull
Guillaume Endignoux ilovezfs
Daniel Godas-Lopez Franjo Ivancic
Austin Seipp Daniel Komaromy
Daniel Binderman Jonathan Metzman
Vegard Nossum Jan Kneschke
Kurt Roeckx Marcel Bohme
Van-Thuan Pham Abhik Roychoudhury
Joshua J. Drake Toby Hutton
Rene Freingruber Sergey Davidoff
Sami Liedes Craig Young
Andrzej Jackowski Daniel Hodson
謝謝你!
問題?擔憂?錯誤報告?請使用 GitHub。
該項目還有一個郵件清單;要加入,請發送郵件至 [email protected]。或者,如果您想先瀏覽檔案,請嘗試:https://groups.google.com/group/afl-users。