協議Fuzz工具整合
AFLNet
source:https://github.com/aflnet/aflnet
1、Description
AFLNet是一款灰盒協議fuzz工具,采用了代碼覆蓋率反饋、種子變異以及狀態反饋等技術。
與傳統的基于生成的協議fuzz工具,它采用了server和client之間的通信消息數據作為種子,無需任何的協議規范。本質上是模擬一個client來發送一系列消息到server,并保留可以觸發新的代碼執行路徑或者響應狀態的變異數據。
AFLNet使用server端的響應碼來識別消息序列觸發的不同狀態,根據這種反饋,AFLNet可以盡可能得向有效的狀態區域靠近。
2、Installation
(1)環境
system:Ubuntu 20.04 64-bit
dependencies:clang, graphviz-dev
(對于環境要求,只要支持AFL和graphviz工具即可,對版本沒有特殊要求。)
(2)AFLNet
下載源碼,并編譯,最后設置環境變量:
# First, clone this AFLNet repository to a folder named aflnetgit clone aflnet# Then move to the source code foldercd aflnetmake clean allcd llvm_mode# The following make command may not work if llvm-config cannot be found# To fix this issue, just set the LLVM_CONFIG env. variable to the specific llvm-config version on your machine# On Ubuntu 18.04, it could be llvm-config-6.0 if you have installed clang using apt-getmake# Move to AFLNet's parent foldercd ../..export AFLNET=$(pwd)/aflnetexport WORKDIR=$(pwd) export PATH=$AFLNET:$PATHexport AFL_PATH=$AFLNET
(3)Usage
樣例:
afl-fuzz -d -i in -o out -N <server info> -x <dictionary file> -P <protocol> -D 10000 -q 3 -s 3 -E -K -R <executable binary and its arguments (e.g., port number)>
選項說明:
- -N netinfo:server信息(例如 tcp://127.0.0.1/8554)
- -P protocol:待測試的應用協議(例如:RTSP, FTP, DTLS12, DNS, DICOM, SMTP, SSH, TLS, DAAP-HTTP, SIP)
- -D usec:可選,server完全初始化的等待時間,微秒為單位
- -K:可選,處理完所有請求消息后,向server發送SIGTERM信號以終止server
- -E:可選,開啟狀態感知模式
- -R:可選,開啟region-level變異
- -F:可選,啟用假陰性消除模式
- -c script:可選,server cleanup的腳本或完整路徑
- -q algo:可選,狀態選擇算法(1. RANDOM_SELECTION, 2. ROUND_ROBIN, 3. FAVOR)
- -s algo:可選,種子選擇算法(1. RANDOM_SELECTION, 2. ROUND_ROBIN, 3. FAVOR)
(4)Example
Fuzzing Live555流媒體服務器
Live555是一個為流媒體提供解決方案的跨平臺的C++開源項目,它實現了標準流媒體傳輸,是一個為流媒體提供解決方案的跨平臺的C++開源項目,實現了對標準流媒體傳輸協議如RTP/RTCP、RTSP、SIP等的支持。
Live555實現了對多種音視頻編碼格式的音視頻數據的流化、接收和處理等支持,包括MPEG、H.263+ 、DV、JPEG視頻和多種音頻編碼。同時由于良好的設計,Live555非常容易擴展對其他格式的支持。Live555已經被用于多款播放器的流媒體播放功能的實現,如VLC(VideoLan)、MPlayer。
Server和client的編譯和安裝
為了展示fuzz成果,這里使用的Live555為2018年的一個舊版本,在這個版本中,AFLNet發現了4個漏洞,其中2個是0day。Live555的編譯和安裝如下:
cd $WORKDIR# Clone live555 repositorygit clone https://github.com/rgaufman/live555.git# Move to the foldercd live555# Checkout the buggy version of Live555git checkout ceeb4f4# Apply a patch. See the detailed explanation for the patch belowpatch -p1 < $AFLNET/tutorials/live555/ceeb4f4.patch# Generate Makefile./genMakefiles linux# Compile the sourcemake clean all
在上述命令中,使用了一個patch來使得目標更容易被fuzz。patch文件的主要功能是設置編譯器為afl-clang-fast\fast++以實現覆蓋反饋:

禁用Live555中的隨機會話id生成:

這里的隨機會話id主要是在原生的Live555版本中,會為每個連接生成一個session id,該id會用在從以連接的client發送的后續的request中,如果沒有合法的id則會被server拒絕,這樣就會導致fuzz過程中即使是相同的消息序列也會產生不同的執行路徑,不同部分主要是session id部分的更改,所以這里就直接硬編碼session id來消除這種干擾變量。
Live555編譯成功后,在testProgs目錄下會看到server端testOnDemandRTSPServer和client端testRTSPClient。可以執行以下命令進行測試:
# Move to the folder keeping the RTSP server and clientcd $WORKDIR/live555/testProgs# Copy sample media source files to the server foldercp $AFLNET/tutorials/live555/sample_media_sources/*.* ./# Run the RTSP server on port 8554./testOnDemandRTSPServer 8554# Run the sample client on another screen/terminal./testRTSPClient rtsp://127.0.0.1:8554/wavAudioTest
client能成功發送reqeust并接收從server發來的數據即可。

① 準備種子
AFLNet使用發送的消息序列作為種子輸入,所以需要先獲取消息序列。基本方法就是首先捕獲一些testRTSPClient和server之間的交互數據作為種子輸入,這里以請求一個wav格式的音頻文件為例:
首先啟動server:
cd $WORKDIR/live555/testProgs./testOnDemandRTSPServer 8554
使用tcpdump捕獲8554端口的所有流量并保存到pcap文件:
tcpdump -w rtsp.pcap -i lo port 8554
運行client發起請求:
./testRTSPClient rtsp://127.0.0.1:8554/wavAudioTest
交互完成后,停下tcpdump,對抓到的流量進行分析。直接跟蹤TCP流查看所有發往server的數據:

這就是在整個的交互過程中,client發出的所有的request消息序列,將它作為種子即可(需要使用的是原始數據二進制格式)。種子路徑為$AFLNET/tutorials/live555/in-rtsp:

② 進行fuzz
cd $WORKDIR/live555/testProgsafl-fuzz -d -i $AFLNET/tutorials/live555/in-rtsp -o out-live555 -N tcp://127.0.0.1/8554 -x $AFLNET/tutorials/live555/rtsp.dict -P RTSP -D 10000 -q 3 -s 3 -E -K -R ./testOnDemandRTSPServer 8554
觸發crash的消息序列的測試用例會放在crashes或replayable-hangs目錄中。在fuzz過程中,AFLNet狀態機會不斷推斷server的已實現狀態,并相應地更新.dot文件,可以查看該文件來監控AFLNet在協議推理過程中的情況。


(備注:不做任何修改的話,這個fuzz速度慢到令人發指,具體原因暫時還未深入研究。)
③ crash重現
AFLNet自帶重現工具:afl-replay
./afl-replay tutorials/live555/CVE_2019_7314.poc RTSP 8554

(備注:官方并沒有說明命令執行完成后會怎么樣,反正我執行完沒有任何反應,目標程序也沒有發生crash)
獲取更多的crash信息可以使用GDB調試工具調試server,或者加上Address Sanitizer-Enabled patch。
StateAFL
1、Description
沿用AFL的基本思路,但是提升了代碼覆蓋率,并且尋求最大化協議狀態覆蓋。StateAFl會自動推測server的當前協議狀態。在編譯階段,會想目標server的內存分配和網絡I/O操作插入探針;在運行階段,會為每個協議迭代拍攝進程內存中長期駐留的數據的快照,并使用fuzzy hashing將內存狀態映射到唯一的協議狀態。

(感覺本質上還是AFLNet的思路,發送請求序列,只不過在狀態監控上做了另外一種方案。)
2、Installation
與AFLNet的安裝方式基本相同:
# Install clang (required by afl-clang-fast)sudo apt-get install clang# Install graphviz developmentsudo apt-get install graphviz-dev # First, clone this StateAFL repository to a folder named stateaflgit clone stateafl# Then move to the source code foldercd stateaflmake clean allcd llvm_mode# The following make command may not work if llvm-config cannot be found# To fix this issue, just set the LLVM_CONFIG env. variable to the specific llvm-config version on your machine# On Ubuntu 18.04, it could be llvm-config-6.0 if you have installed clang using apt-getmake# Move to StateAFL's parent foldercd ../..export STATEAFL=$(pwd)/stateafl
3、Usage
與AFLNet一模一樣。
4、Example
測試環境與AFLNet的一樣,這里直接執行fuzz,看看效果如何:


速度上比AFLNet會快一丟丟,在覆蓋率上也會高一點。
(自帶的語料庫沒法觸發漏洞,忽略這個吧。畢竟挖不到洞才是常態)
ProFuzzBench
這是一個對于網絡協議相關fuzz工具的測試集合,可以方便地對流行的幾款協議fuzz工具進行使用測試。
ProFuzzBench是一個有狀態的網絡協議測試的基準,包含了多個用于測試流行網絡協議(TLS, SSH, SMTP, FTP, SIP等)的server。
ProFuzzBench提供了自動化執行fuzz的腳本,截止目前包含的fuzz工具有:AFLnwe(基于網絡的AFL版本,使用TCP/IP sockets替代文件作為輸入)、AFLNet(一款針對有狀態網絡協議的fuzz工具),而StateAFL(另一款針對有狀態網絡協議的fuzz工具)尚未提供自動化執行腳本,可直接使用原生的StateAFL進行fuzz。
其項目結構如下:
protocol-fuzzing-benchmark├── subjects: this folder contains all protocols included in this benchmark and│ │ each protocol may have more than one target server│ └── RTSP│ └── FTP│ │ └── LightFTP│ │ └── Dockerfile: subject-specific Dockerfile│ │ └── run.sh: (subject-specific) main script to run experiment inside a container│ │ └── cov_script.sh: (subject-specific) script to do code coverage analysis│ │ └── other files (e.g., patches, other subject-specific scripts)│ └── ...└── scripts: this folder contains all scripts to run experiments, collect & analyze results│ └── execution│ │ └── profuzzbench_exec_common.sh: main script to spawn containers and run experiments on them│ │ └── ...│ └── analysis│ └── profuzzbench_generate_csv.sh: this script collect code coverage results from different runs│ └── profuzzbench_plot.py: sample script for plotting└── README.md
接下來以LightFTP為例進行fuzz。
Installation and Setup
首先,下載ProFuzzBench的源碼并設置環境變量:
git clone https://github.com/profuzzbench/profuzzbench.gitcd profuzzbenchexport PFBENCH=$(pwd)export PATH=$PATH:$PFBENCH/scripts/execution:$PFBENCH/scripts/analysis
然后,創建lightFTP server的docker image:
cd $PFBENCHcd subjects/FTP/LightFTPdocker build . -t lightftp # 為了提高build效率,可以使用以下命令:# docker build . -t lightftp --build-arg -j4 # 使用StateAFL時,使用的是額外定制Dockfile:Dockerfile-stateafl,所以使用的build命令需要指明使用的dockerfiledocker build . -f Dockerfile-stateafl -t lightftp-stateafl
1、Run Fuzzing
執行profuzzbench_exec_common.sh腳本開啟環境,這里涉及到8個參數:
- Docimage:指定docker image
- Runs:runs的實例數,每個container跑一個fuzz過程,類似于并行fuzz
- Saveto:結果的保存路徑
- Fuzzer:指定使用的fuzzer name
- Outdir:docker container中創建的output文件夾名稱
- Options:除了目標特定的 run.sh 腳本中編寫的標準選項之外,fuzz所需的所有選項
- Timeout:fuzz時長,秒為單位
- Skipcount:用于計算覆蓋率時間,例如Skipcount=5就是說每5個test case后執行一次gcov
以下指令就是在10分鐘內執行4個AFLNet實例和4個AFLnwe實例來對LightFTP進行fuzz:
cd $PFBENCHmkdir results-lightftp profuzzbench_exec_common.sh lightftp 4 results-lightftp aflnet out-lightftp-aflnet "-P FTP -D 10000 -q 3 -s 3 -E -K" 3600 5 &profuzzbench_exec_common.sh lightftp 4 results-lightftp aflnwe out-lightftp-aflnwe "-D 10000 -K" 3600 5
AFLNet的fuzz結果如下:

輸出結果保存在了`results-lightftp目錄中:

一個fuzzer實例產生一個.tar.gz文件,每個.tar.gz文件包含了這次fuzz過程產生的所有數據。
類似的,AFLnwe的輸出一樣:

這兩個工具對比起來,fuzz效果都一般般,但AFLNet的速度會快一些,相對而言,比AFLnwe效果也好一些。cov_over_time數據對比如下:

2、Collect the results
對于覆蓋率信息(也就是上面的表格)的處理,ProFuzzBench提供了處理覆蓋率的自動化工具profuzzbench_generate_cssv.sh,參數說明如下:
- prog:subject program的名字,例如lightftp
- runs:runs數量
- fuzzer:fuzzer的name,例如AFLNet
- covfile:CSV格式的輸出文件
- append:append模式
可以使用下面的命令來處理fuzzer跑出來的覆蓋率信息文件:
cd $PFBENCH/results-lightftp profuzzbench_generate_csv.sh lightftp 4 aflnet results.csv 0profuzzbench_generate_csv.sh lightftp 4 aflnwe results.csv 1
輸出結果為results.csv文件:

文件中的第一列為時間戳,第二列為目標程序,第三列為fuzzer,第四列為運行的index,第五列為coverage type,第六列為type的值。文件包含了隨時間變化的line coverage和branch coverage信息,每個coverage type都包含兩個值:百分比 (_per) 和絕對數量 (_abs)。
3、Analysis the results
前面生成的results.csv文件可以進一步進行繪圖顯示,使用腳本profuzzbench_plot.py。
cd $PFBENCH/results-lightftp profuzzbench_plot.py -i results.csv -p lightftp -r 4 -c 60 -s 1 -o cov_over_time.png
處理完成后,可以看到處理結果:

從這里也能看出AFLNet比AFLnwe的效果要好太多。其實這個腳本的實現也不難,python中有很多成熟的庫。
4、Conclusion
該工具支持添加自定義的fuzzer,按照stateafl的添加方式來進行添加即可,但是個人感覺以docker模式運行,速度上肯定要變慢。該工具值得肯定的是將常見的幾個網絡協議的fuzz環境能實現自動化搭建,可以節省一定的環境搭建時間成本。看個人需求吧,側重效率的話還是要自行根據需求搭建獨立環境。
該工具目前支持的協議環境有:

支持自定義添加協議,官方的開發文檔也算完善,還是比較值得嘗試的(畢竟也沒有其他更好的選擇了)。