- ProFuzzBench是网络协议状态模糊测试的基准测试。它包括一套用于流行协议(例如 TLS、SSH、SMTP、FTP、SIP)的代表性开源网络服务器,以及用于自动执行实验的工具。
- 详细参考:阅读笔记——《ProFuzzBench: A Benchmark for Stateful Protocol Fuzzing》-CSDN博客
- 环境:Ubuntu22.04(WSL)
1、设置环境变量
- 克隆项目,设置环境变量。
-
git clone https://github.com/profuzzbench/profuzzbench.git cd profuzzbench nano ~/.bashrc
-
- 添加下面内容。
-
# profuzzbench export PFBENCH=/home/zzs/GitProject/profuzzbench export PATH=$PATH:$PFBENCH/scripts/execution:$PFBENCH/scripts/analysis
-
- 重新加载配置文件以使更改生效。
-
source ~/.bashrc
-
2、构建docker镜像
- 安装docker,参考:Docker——简介、安装(Ubuntu22.04)-CSDN博客
- 构建docker镜像。
-
cd $PFBENCH cd subjects/FTP/LightFTP sudo docker build . -t lightftp # docker build .:在当前目录(LightFTP)下寻找 Dockerfile 并构建 Docker 镜像 # -t lightftp:将创建的 Docker 镜像标记为 lightftp
- 【注】这里创建了一个名为 lightFTP 的 Docker 镜像,该镜像包含了 LightFTP 及其所需的所有依赖项,使其可以在隔离的 Docker 容器中运行。这对于模糊测试和代码覆盖率收集特别有用,因为它提供了一个一致的、可重复的测试环境。
- 构建时间较长,耐心等待。
- 【注】可能遇到网络不好,多尝试。
-
- 查看构建的镜像。
-
docker images
-
3、运行模糊测试
- 运行‘profuzzbench_exec_common.sh’脚本以启动实验。该脚本接受以下 8 个参数:
- 第一个参数 (DOCIMAGE):Docker镜像的名称。
- 第二个参数 (RUNS):运行次数,每次运行都会启动一个独立的Docker容器。
- 第三个参数 (SAVETO):存储结果的文件夹路径。
- 第四个参数 (FUZZER):模糊测试工具的名称(例如,aflnet)——该名称必须与Docker容器内模糊测试工具文件夹的名称匹配(例如,/home/ubuntu/aflnet)。
- 第五个参数 (OUTDIR):在Docker容器内创建的输出文件夹的名称。
- 第六个参数 (OPTIONS):除目标特定的run.sh脚本中写入的标准选项外,所有模糊测试所需的选项。
- 第七个参数 (TIMEOUT):模糊测试的时间(以秒为单位)。
- 第八个参数 (SKIPCOUNT):用于计算随时间变化的覆盖率。例如,SKIPCOUNT=5表示我们在每5个测试用例后运行一次gcovr,因为gcovr需要时间,我们不希望在每个测试用例后都运行它。
- 以下命令运行一个AFLNet实例,对LightFTP进行60分钟模糊测试。
-
cd $PFBENCH mkdir results-lightftp profuzzbench_exec_common.sh lightftp 1 results-lightftp aflnet out-lightftp-aflnet "-P FTP -D 10000 -q 3 -s 3 -E -K -R" 3600 5
-
4、收集结果
- 所有结果(在 tar 文件中)都应存储在上个步骤(results-lightftp)创建的文件夹中。
- 具体来说,这些tar文件是所有模糊测试实例生成的输出文件夹的压缩版本。如果模糊测试器是基于AFL的(例如,AFLNet,AFLnwe),每个文件夹应包含诸如 crashes、hangs、queue 等子文件夹。
- 使用profuzzbench_generate_csv.sh脚本可以收集代码覆盖率随时间变化的结果。该脚本需要以下5个参数:
- 第一个参数 (PROG):目标程序的名称(例如,lightftp)。
- 第二个参数 (RUNS):运行次数。
- 第三个参数 (FUZZER):模糊测试器的名称(例如,aflnet)。
- 第四个参数 (COVFILE):用于保存结果的 CSV 格式的输出文件。
- 第五个参数 (APPEND):追加模式;对第一个模糊测试器设置为0,对后续的模糊测试器设置为1。
- 以下命令收集AFLNet生成的代码覆盖率结果,并将其保存到results.csv。
-
cd $PFBENCH/results-lightftp profuzzbench_generate_csv.sh lightftp 1 aflnet results.csv 0
-
- results.csv文件如下。该文件有六列,分别显示:
- 时间(time)
- 目标程序(subject)
- 模糊测试器名称(fuzzer)
- 运行索引(run)
- 覆盖类型(cov_type)
- 覆盖率(cov)
- 文件包含了随时间变化的行覆盖率(l_per、l_abs)和分支覆盖率(b_per、b_abs)信息。每种覆盖类型都有两个值,一个是百分比形式(_per),另一个是绝对数值(_abs)。
- 【注】这里时间为空,暂不知道什么原因。
5、分析结果
- results.csv中收集的结果可用于绘图。例如,使用示例python脚本绘制代码随时间变化的覆盖率。使用以下命令打印结果并将其保存到文件中。
-
cd $PFBENCH/results-lightftp profuzzbench_plot.py -i results.csv -p lightftp -r 4 -c 60 -s 1 -o cov_over_time.png
- 报错:IndexError: index 0 is out of bounds for axis 0 with size 0。
- 原因是因为文件中time为空,并且只有一个模糊测试器。
-
- 改使用aflnwe。删除原先的results-lightftp。
-
cd $PFBENCH rm -rf resluts-lightftp mkdir results-lightftp profuzzbench_exec_common.sh lightftp 1 results-lightftp aflnwe out-lightftp-aflnwe "-D 10000 -K" 3600 5 cd $PFBENCH/results-lightftp profuzzbench_generate_csv.sh lightftp 1 aflnwe results.csv 0 cat results.csv
-
- 修改profuzzbench_plot.py。
-
#!/usr/bin/env python import argparse from pandas import read_csv from pandas import DataFrame from pandas import Grouper from matplotlib import pyplot as plt import pandas as pd def main(csv_file, put, runs, cut_off, step, out_file): # Read the results df = read_csv(csv_file) # Calculate the mean of code coverage mean_list = [] for subject in [put]: fuzzer = 'aflnwe' # Only use aflnwe for cov_type in ['b_abs', 'b_per', 'l_abs', 'l_per']: # Get subject & fuzzer & cov_type-specific dataframe df1 = df[(df['subject'] == subject) & (df['fuzzer'] == fuzzer) & (df['cov_type'] == cov_type)] mean_list.append((subject, fuzzer, cov_type, 0, 0.0)) for time in range(1, cut_off + 1, step): cov_total = 0 run_count = 0 for run in range(1, runs + 1, 1): # Get run-specific data frame df2 = df1[df1['run'] == run] if df2.empty: continue # Get the starting time for this run start = df2.iloc[0, 0] # Get all rows given a cutoff time df3 = df2[df2['time'] <= start + time * 60] if df3.empty: continue # Update total coverage and #runs cov_total += df3.tail(1).iloc[0, 5] run_count += 1 # Add a new row if run_count > 0: mean_list.append((subject, fuzzer, cov_type, time, cov_total / run_count)) # Convert the list to a dataframe mean_df = pd.DataFrame(mean_list, columns=['subject', 'fuzzer', 'cov_type', 'time', 'cov']) fig, axes = plt.subplots(2, 2, figsize=(20, 10)) fig.suptitle("Code coverage analysis") for key, grp in mean_df.groupby(['fuzzer', 'cov_type']): if key[1] == 'b_abs': axes[0, 0].plot(grp['time'], grp['cov']) axes[0, 0].set_xlabel('Time (in min)') axes[0, 0].set_ylabel('#edges') if key[1] == 'b_per': axes[1, 0].plot(grp['time'], grp['cov']) axes[1, 0].set_ylim([0, 100]) axes[1, 0].set_xlabel('Time (in min)') axes[1, 0].set_ylabel('Edge coverage (%)') if key[1] == 'l_abs': axes[0, 1].plot(grp['time'], grp['cov']) axes[0, 1].set_xlabel('Time (in min)') axes[0, 1].set_ylabel('#lines') if key[1] == 'l_per': axes[1, 1].plot(grp['time'], grp['cov']) axes[1, 1].set_ylim([0, 100]) axes[1, 1].set_xlabel('Time (in min)') axes[1, 1].set_ylabel('Line coverage (%)') for i, ax in enumerate(fig.axes): ax.legend(('AFLNwe',), loc='upper left') # Only AFLNwe ax.grid() # Save to file plt.savefig(out_file) # Parse the input arguments if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-i', '--csv_file', type=str, required=True, help="Full path to results.csv") parser.add_argument('-p', '--put', type=str, required=True, help="Name of the subject program") parser.add_argument('-r', '--runs', type=int, required=True, help="Number of runs in the experiment") parser.add_argument('-c', '--cut_off', type=int, required=True, help="Cut-off time in minutes") parser.add_argument('-s', '--step', type=int, required=True, help="Time step in minutes") parser.add_argument('-o', '--out_file', type=str, required=True, help="Output file") args = parser.parse_args() main(args.csv_file, args.put, args.runs, args.cut_off, args.step, args.out_file)
-
- 重新执行:
-
cd $PFBENCH/results-lightftp profuzzbench_plot.py -i results.csv -p lightftp -r 4 -c 60 -s 1 -o cov_over_time.png
-