1 模糊測試概述
1.1 模糊測試概念
模糊測試(Fuzzing),是一種通過向目标系統提供非預期的輸入并監視異常結果來發現軟體漏洞的方法。它是一種挖掘軟體安全漏洞、檢測軟體健壯性的黑盒測試,它通過向軟體輸入非法的字段,觀測被測試軟體是否異常而實作。
1.2 模糊測試優勢
進行軟體漏洞挖掘時,通常有靜态分析(Static Analysis)、動态分析(Dynamic Analysis)、符号執行(Symbolic Execution)、模糊測試(Fuzzing)這幾種技術手段。
靜态分析(Static Analysis)是指在不運作代碼的方式下,通過詞法分析、文法分析資料流分析等技術對程式代碼進行掃描,驗證代碼是否滿足規範性、安全性等名額的一種代碼分析技術。靜态分析的速度快,但是誤報率高。
動态分析(Dynamic Analysis)是指逐漸跟蹤程式運作進行的分析。它的準确率很高,但是需要調試人員豐富的知識儲備,而且這種調試方法很難進行大規模的程式漏洞挖掘。
符号執行(Symbolic Execution)簡單來說,就是試圖找到什麼輸入對應什麼樣的運作狀态,它要去覆寫所有的執行路徑。是以,當被分析的程式比較複雜,有很多執行路徑時,就會遇到路徑爆炸的問題。
模糊測試(Fuzzing)不需要過多的人為參與,也不像動态分析那樣要求分析人員有豐富的知識。在模糊測試中,用随機資料攻擊一個程式,然後等着觀察哪裡遭到了破壞。模糊測試的技巧在于,它是不符合正常邏輯的:自動模糊測試不去猜測哪個資料會導緻破壞,将盡可能多的雜亂資料投入程式中。進而發現哪些輸入能夠使程式發生異常,進而分析可能存在的漏洞。目前比較成功的Fuzzer(執行模糊測試的程式)有AFL、libFuzzer、OSS-Fuzz等。
2 AFL工具
2.1 AFL簡介
AFL(American Fuzzy Lop)是由安全研究員Michal Zalewski(@lcamtuf)開發的一款基于覆寫引導(Coverage-guided)的模糊測試工具,它通過記錄輸入樣本的代碼覆寫率,進而調整輸入樣本以提高覆寫率,增加發現漏洞的機率。
2.2 AFL工作流程
AFL工作流程大緻如下:
- 從源碼編譯程式時進行插樁,以記錄代碼覆寫率(Code Coverage);
- 選擇一些輸入檔案,作為初始測試集加入輸入隊列(queue);
- 将隊列中的檔案按一定的政策進行“突變”;
- 如果經過變異檔案更新了覆寫範圍,則将其保留添加到隊列中;
- 上述過程會一直循環進行,期間觸發了crash的檔案會被記錄下來。
3 模糊測試實驗
3.1 測試環境搭建
系統:Ubuntu 20.04
AFL:afl-2.52b
下載下傳AFL源碼(https://github.com/google/AFL/),解壓後,編譯安裝。
3.2 測試簡單例子
3.2.1 編寫測試程式
用c語言編寫一個測試用例,設定一些BUG和漏洞。
sudo vim easy_test.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
int AFLTest(char *str)
{
int len = strlen(str);
if(str[0] == 'A' && len == 6)
{
raise(SIGSEGV);
//如果輸入的字元串的首字元為A并且長度為6,則異常退出
}
else if(str[0] == 'F' && len == 16)
{
raise(SIGSEGV);
//如果輸入的字元串的首字元為F并且長度為16,則異常退出
}
else if(str[0] == 'L' && len == 66)
{
raise(SIGSEGV);
//如果輸入的字元串的首字元為F并且長度為66,則異常退出
}
else
{
printf("it is good!\n");
}
return 0;
}
int main(int argc, char *argv[])
{
char buf[100]={0};
gets(buf);
//存在棧溢出漏洞
printf(buf);
//存在格式化字元串漏洞
AFLTest(buf);
return 0;
}
3.2.2 插樁編譯
對該源檔案(easy_test.c)進行插樁編譯(編譯插樁是指在代碼編譯期間修改或新增代碼)。
afl-gcc easy_test.c -o easy_test
可以看到在編譯過程中,編譯器已經提示存在漏洞,不理會,用AFL去測試。
3.2.3 開始fuzz
從stdin讀取輸入的目标程式,文法如下:
從檔案讀取輸入的目标程式來說,文法如下:
./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
是以需要給程式設定testcase_dir和findings_dir兩個檔案夾,以及在testcase_dir中存放一些測試用例testcase。
mkdir fuzz_in
mkdir fuzz_out
cd fuzz_in
vim testcase
testcase中的輸入可以随意寫下(例如:aaa),接着就可以進行fuzz了。
afl-fuzz -i fuzz_in -o fuzz_out ./easy_test
3.2.4 結果分析
經過5分鐘的fuzz,共找到了7個crash,可以在fuzz_out中檢視詳情。
第1個crash:發現符合首字元為A且字元串長度為6的異常退出情況
第2個crash:發現符合棧溢出漏洞的crash情況
第3個crash:發現符合棧溢出漏洞的crash情況
第4個crash:發現符合棧溢出漏洞的crash情況
第5個crash:發現符合棧溢出漏洞的crash情況
第6個crash:發現符合首字元為L且字元串長度為66的異常退出情況
第7個crash:發現符合格式化字元串的%任意位址寫入的漏洞情況
3.3 測試開源程式w3m
3.3.1 w3m介紹
w3m是一個基于文本的網頁浏覽器,支援多種作業系統,在指令行終端可以很好的支援中文。即使在沒有滑鼠支援的情況下也可以檢查網頁的輸出。w3m可以處理表、Cookie、身份驗證以及除 JavaScript 以外的幾乎所有内容。下圖為用w3m通路www.qq.com。
該項目在GitHub上開源,https://github.com/tats/w3m,目前有300多個star,最近的一次代碼更新在幾天前,表明該項目一直在維護中。
3.3.2 fuzz前序工作
下載下傳源碼
git clone https://github.com/tats/w3m.git
cd w3m
./configure CC="afl-gcc" CXX="afl-g++"
配置生成Makefile,出現報錯configure: error: gc.h not found,提示缺少gc.h
查閱資料後發現,需要安裝gc,解決方案如下:
- 下載下傳gc-7.2.tar.gz後,解壓
- 配置生成Makefile
cd gc-7.2
./configure
- 安裝gc
make
sudo make install
重新對w3m進行配置後,能成功生成Makefile。
3.3.3 插樁編譯
檢視Makefile,可以看到編譯器為afl-gcc和afl-g++後,确認無誤後,執行編譯。
gedit Makefile
make
3.3.4 開始fuzz
設定in和out檔案夾,由于w3m是一款指令行浏覽器,是以我們可以收集一些html檔案放入in檔案夾中作為測試用例。
由于是輸入為檔案,是以fuzz的指令如下:
afl-fuzz -i in -o out ./w3m @@
進行了一個多小時的fuzz,沒有觸發crash。原因可能是軟體維護的比較好,bug較少,不容易找到。
3.4 總結
本文主要對模糊測試進行了熟悉運用,依靠AFL工具完成了對簡單例子的fuzz和對開源程式w3m的fuzz,并對簡單例子中發現的crash進行了分析,了解報錯的原因。經過實驗,了解了利用AFL進行模糊測試的大概過程:①插樁編譯;②設定種子測試用例;③開始fuzz。模糊測試對于軟體測試的幫助很大,其不受限于被測系統的内部實作細節和複雜程度,例如,使用模糊測試可以不用關心被測對象的實作語言等細節,是以即使是并不怎麼熟悉代碼内部結構的程式員也能完成測試。但是若模糊器是以窮舉方式産生各種可能的資料組合,則被測目标程式響應的檢測時間将急劇增長,這使得檢測費力且耗時。總之,模糊測試是一項用于驗證程式中真實錯誤的重要工具,也是所有意識到安全性問題且着力于程式健壯性的程式員們的工具箱中所必備的工具。