AFL--模糊測試使用淺析
一、AFL簡介
AFL(American Fuzzy Lop)是由安全研究員Micha? Zalewski(@lcamtuf)開發的一款基于覆蓋引導(Coverage-guided)的模糊測試工具,它通過記錄輸入樣本的代碼覆蓋率,從而調整輸入樣本以提高覆蓋率,增加發現漏洞的概率。
①從源碼編譯程序時進行插樁,以記錄代碼覆蓋率(Code Coverage);②選擇一些輸入文件,作為初始測試集加入輸入隊列(queue);③將隊列中的文件按一定的策略進行“突變”;④如果經過變異文件更新了覆蓋范圍,則將其保留添加到隊列中;
⑤上述過程會一直循環進行,期間觸發了crash的文件會被記錄下來。

二、AFL安裝、測試
*1.安裝AFL*
下載源碼

Make

llvm_mode安裝

之后輸入以下命令進行安裝

*2.AFL測試*
下載一個有缺陷的c文件

使用 afl-gcc/afl-clang 編譯

生成一些種子語料庫

開始fuzz

提示修改/proc/sys/kernel/core_pattern

再次運行之前的代碼可看到fuzz進度

現在就表示我們的ACL已經安裝成功了,注意出現(odd,check syntax!)是表示樣例根本沒有進入到測試中去,需要調整語料庫。
Ctrl+C打斷可以在out文件里看見我們的測試信息

*3.并行fuzz測試*
每個afl-fuzz進程占用CPU的一個核,實際上如果是多核的主機,AFL就可以并行工作
首先先看自己有多少內核

以上可以看出有四個內核意味著可以同時運行4個實例
首先指定主實例 -M 用于主實例,將 -S 添加到所有從屬實例。它們可以相互同步
主實例:afl-fuzz -M master -i in/ -o out/ -m none -- ./imgRead_afl @@
從實例:afl-fuzz -S slave1 -i in/ -o out/ -m none -- ./imgRead_afl @@

在之前的out文件夾會多出倆個不同的文件夾masterh和slave1
現在嘗試假如我們一次性運行5個實例會怎么樣
在運行第5個實例后報錯,其他實例不受影響,也可以確定4個核在運行中

三、AFL模糊測試libjpeg-turbo
libjpeg是專門處理Jpeg解碼、編碼、轉碼的自由軟件庫。libjpeg-turbo是其fork版本,還有一個基于libjpeg-turbo的fork的版本是MozJpeg。
*1.編譯libjpeg-turbo*
首先下載libjpeg-turbo
之后需要修改cmakelist.txt,進行插樁編譯
在cmakelist.txt中,在cmake_minimum_required命令下添加編譯器選項,在前面添加,免得被覆蓋,進行插樁編譯

之后在libjpeg-turbo文件夾下
mkdir build
cd build
cmake ..
make
sudo make install


安裝好之后build的內容如下

之后利用程序的示例對是否成功安裝libjpeg-turbo庫進行測試

該函數有倆個參數 一個輸入文件名,一個作為輸出文件名
具體作用就是調用了turbojpeg.h這個庫函數對輸入的jpg圖片進行壓縮
因為修改了cmake中的編譯器設置,應該庫函數里已經是被插過樁的,所以在編譯時是可以不用afl-gcc編譯也可以進行檢測

這樣是可以生成可執行文件,也可以實現壓縮圖片的功能,這里也對之前的樣例進行了修改,只接收一個變量,并且不對壓縮文件進行保存

但在進行模糊測試時出現以下問題

沒有插樁信息,無法進行測試
發現它是動態編譯的,雖然應該其動態鏈接庫是插過樁的。但最后已知沒有實現。這里最后考慮是想通過鏈接靜態庫實現。也是在網上查詢未果后,發現在根目錄下輸入 make test,可以調用他自己的樣例進行測試,這其中就包括了靜態鏈接的測試
在一個靜態鏈接測試的項目下,查看其ling.txt,得到靜態編譯的方式

最后對自己的編譯自己的樣例

之后開始模糊測試

總共測試次數超過1億次,開了4個并行




4個樣例的的最開始輸入都是不一樣的,可以從路徑速度和總量上看出明顯的區別,確實libjpeg-turbo在更新2.0之后,其安全性能得到了極大的提升,沒有收到一個報錯信息。
*2.內存錯誤檢查工具*
這里有很多的內存檢查工具,這里舉個大概,只大概研究ASAN (-fsanitize=address)的使用和與AFL測試的結合
這里測試了幾個漏洞文件以此來明晰ASAN的作用
編譯文件模板如下
g++ -fsanitize=address -fno-omit-frame-pointer -o t xxx.cpp
這里只對幾種漏洞進行展示
use-after-fee


可以看到漏洞的名稱和發生的內存地址
stack buffer overflow


還有很多其他類型的漏洞可以進行檢測
Address Sanitizer ?用法 - 簡書 (jianshu.com)
https://www.jianshu.com/p/3a2df9b7c353
在AFL中啟用ASAN的方式也比較簡單
在make時加上AFL_USE_ASAN=1

注意之后編譯文件時需要加上啟用asan的參數,不然會報錯

*3.構造自己的字典*
AFL自帶自己的一個字典庫,主要用于各種變異操作的
如下是AFL的jpeg的字典

為了符合jpeg圖片的實際,需要分析在jpeg中出現次數多且固定的字符
這里挑選一些頻率較高的字符加入字典

這里挑選的字符主要來源自各種jpeg的開頭部分
之后如果要使用字典需要使用-x參數進行指定字典文件

https://paper.seebug.org/496/#dictionary
*4.語料庫蒸餾*
afl-cmin的核心思想是:嘗試找到與語料庫全集具有相同覆蓋范圍的最小子集。舉個例子,假設有多個文件,都覆蓋了相同的代碼,那么就丟掉多余的文件。

最后只留下一個文件
afl-tmin(減小單個輸入文件的大小)
afl-tmin有兩種工作模式,
instrumented mode和crash mode。默認的工作方式是instrumented mode

后面查資料得到tmin只能處理文件,文件夾需要修改腳本

精簡到0bytes,后面在網上看到了相似的例子,這和tmin的精簡策略有關,確實存在這種情況。
如果加上了參數-x,就會調用crash mode模式,會把導致程序非正常退出的文件直接剔除。這里測試的樣例并沒有crash例子,所以不進行測試。
*5.持久模式*
在持久模式下,AFL 僅模糊部分程序,而不是整個程序。當只想模糊復雜軟件中的特定功能時,這很有用。與分叉服務器模式相比,這提供了許多速度改進。
具體例子如下:

對想要進行的部分進行修改

此時修改的文件是turbojpeg.c
再修改cmakelist.txt如下

之后對庫進行重新編譯

編譯方式

再進行afl-fuzz(與之前一致)

速度上確實比之前的速度要快,最快時比之前要快上倆倍多
*6.Afl-cov使用*
可以快速幫助我們調用lcov和gcov處理來自afl-fuzz測試用例的代碼覆蓋率結果
安裝
GCOV,它隨gcc一起發布,所以不需要再單獨安裝,和afl-gcc插樁編譯的原理一樣,gcc編譯時生成插樁的程序,用于在執行時生成代碼覆蓋率信息
LCOV,它是GCOV的圖形前端,可以收集多個源文件的gcov數據,并創建包含使用覆蓋率信息注釋的源代碼HTML頁面。

這里也可以使用apt-install afl-cov來安裝,不過看網上建議這個版本實際使用上會有問題,所以這里還是直接下載源碼

為了實現檢查覆蓋率需要修改cmakelist.txt如下

再次編譯庫
編譯文件

這里的afl-cov選擇實時監控 也就是添加--live,先啟動afl-cov,后啟動afl-fuzz,當afl-fuzz退出時,afl-cov就會跟著退出
啟動afl-cov的命令
/home/user/Desktop/afl-cov/afl-cov -d afl-cc --live --enable-branch-coverage -c . -e "cat AFL_FILE | ./ttt AFL_FILE" --overwrite

-d是之后afl-fuzz的輸出文件,-c是直向源碼文件的,在編譯.c文件后,會生成一個.gno文件,-c 后面跟該文件的目錄
啟動afl-fuzz(與之前一致)

Afl-fuzz退出后,afl-cov需要等一會才能正常退出,此時就可以看見生成分析的網頁了

也可以針對已經生成的數據直接開啟afl-cov,但要求編譯已經加上了-fprofile-arcs -ftest-coverage
網頁首頁

也可以進入到文件里,查看具體語句的執行次數


*7.afl_postprocess使用*
它最主要的作用就是可以規定生成種子的格式

作者在github上的樣例的作用是讓每個測試用例開頭的標頭都是
GIF89ahttps://github.com/mirrorer/afl/blob/master/experimental/post_library/post_library.so.c
編譯方法
gcc -shared -wall -O3 post_library.so.c -o post_library.so
可以看看afl-fuzz.c對該方法的支持

獲取AFL_POST_LIBRARY環境變量的值,自動加載afl_postprocess函數
這里推薦使用export設定環境變量,需要說明的是export的環境變量只在當前的shell(BASH)或其子shell(BASH)下是有效的,shell關閉了,變量也就失效了,再打開新shell時就沒有這個變量,需要使用的話還需要重新定義。如果需要一直使用,需要修改配置文件,方法推薦
https://blog.csdn.net/wx_it/article/details/118450790

加載后處理器庫成功

也可以看到我們的測試樣例變成了GIF格式,后處理庫有效。
測試其他的例子


這部分需要注意的是對源碼的處理,確保樣例格式的滿足輸入的要求
參考資料
Fuzzing open source software?s with AFL
AFL 漏洞挖掘技術漫談(一):用 AFL 開始你的第一次 Fuzzing
AFL 漏洞挖掘技術漫談(二):Fuzz 結果分析和代碼覆蓋率
Fuzzing software: common challenges and potential solutions (Part 1)
Fuzzing software: advanced tricks (Part 2)
AFL 源碼分析
實操推薦:Fuzz之AFL
測試用例來探索二進制程序內部新的執行路徑。通過該實驗了解AFL的使用方法,能夠通過AFL模糊測試一些簡單的軟件,明白fuzz的基本方法和思想