一、安装afl++
参考afl++的官方文档https://github.com/AFLplusplus/AFLplusplus#building-and-installing-afl
和他们官方github库的一个issue https://github.com/AFLplusplus/AFLplusplus/issues/211
前景知识:需要安装docker并对docker有一点点的了解,一点点就够了
第一步:
docker pull aflplusplus/aflplusplus
拉取afl++docker镜像
或者自己build一个镜像
$ git clone https://github.com/vanhauser-thc/AFLplusplus
$ cd AFLplusplus
$ sudo docker build -t aflplusplus .
注意:两种方法不要同时使用,你用build
构建本地文件时候会经过一段时间的构建生成一个镜像aflplusplus
可以看到我有两个aflplusplus镜像,直接从docker hub 拉取的镜像是下面那个1.85GB的,自己通过docker build命令生成的名字只有一个aflplusplus。两者可能因为名字重了,执行第二步的时候aflplusplus镜像不能用,会报错。可能删除掉一个就好了(我没实践过,可以试一下)。
第二步:
看下面的命令
$ docker run -it --name afl -v /Users/inf/Projects/FuzzTest:/AFLplusplus/my_test --rm aflplusplus/aflplusplus
执行docker run之前,请设置共享文件夹,因为mac默认情况下只有/Users, /Volume, /private, /tmp 这四个文件夹是共享文件夹。点开docker dashboard界面,
点击小齿轮的设置 -> Resources -> FILE SHARING -> 点击加号,添加你的路径,我的添加后是这样
如果你是 win10且没有FILE SHARING,删掉
-v /Users/inf/Projects/FuzzTest:/AFLplusplus/my_test
然后执行docker run 命令
这一步将创建一个aflplusplus/aflplusplus容器并运行
先来解释一下各个参数
-it
应该是 -i
-t
的结合
-i
: 以交互模式运行容器,通常与 -t
同时使用;
-t
: 为容器重新分配一个伪输入终端,通常与 -i
同时使用;
所以-it
就是以交互模式运行aflplusplus/aflplusplus容器
--volume , -v
: 绑定一个卷到容器中,整个命令 -v 本地目录:容器目录
,其实就是将本地文件和容器的虚拟机共享,具体到上面的命令就是将我我本地文件/Users/inf/Projects/FuzzTest映射到容器内的AFLplusplus/my_test
:/
这个就是映射的意思
注意:
两个路径一定是绝对路径,相对路径的问题具体见
https://www.cnblogs.com/conserdao/p/9061774.html
为什么绑定本地文件,测试的时候有妙用,后面揭晓
--name afl
是给我的容器取个名字
--rm
容器退出后立即删除该容器使用
这是个可选择的,不加也可以
执行这条命令后就进入
alfplusplus/aflplusplus容器内部了,这里面其实就是一个linux虚拟环境了
输入
cd .. && ls
[afl++]root@423d3c7f80f2:/AFLplusplus# cd .. && ls
AFLplusplus bin dev home lib32 libx32 mnt proc run srv tmp var
afl-cov boot etc lib lib64 media opt root sbin sys usr
和linux系统其实是一样的,重新进入AFLplusplus文件夹,输入 ls
命令,可以看到对比原来的aflplusplus源代码,新增了一个my_test文件夹
以后就把我们自己的测试文件放这里吧。
二、测试AFL
参考https://blog.csdn.net/qq_42745625/article/details/109315155
我们前面在本地建立了一个FuzzTest文件夹,要清楚它和docker容器里的my_test文件夹是共享的,你在本地对这个文件夹内容的改变会反映到容器里面。
那我们就在这个文件夹创建测试用例。当然你也可以手动在命令行进行下面的操作。
如果是win10系统而且没有FILE SHARING这个选项,就只能命令行操作了。全程在容器内手动写吧。
1. 准备测试文件
#include <stdio.h>
int main(int argc, char *argv[])
{
char buf[100] = {0};
gets(buf); //存在栈溢出漏洞
printf(buf); //存在格式化字符串漏洞
return 0;
}
手动的话就是 ,在容器内
$ mkdir my_test && cd my_test
$ vi fuzzTest.c
按 i 进入编辑模式,复制粘贴,按冒号(shift + ;),输入wq保存
2. 新建两个文件夹fuzz_in,fuzz_out
或者
mkdir fuzz_in && mkdir fuzz_out
fuzz_in 下新建一个文本文件testcase,随便输入一些字符串
cd fuzz_in && vi testcase
3. alf-gcc编译fuzzTest.c
进入容器my_test文件夹,执行
$ afl-gcc -g -o fuzzTest fuzzTest.c
此时本地文件夹也能看到变化
4. Fuzzing
容器内执行
$ afl-fuzz -i fuzz_in -o fuzz_out ./fuzzTest
稍等片刻出现
afl不会自动结束,我们用 ctr+c 结束它
可以看到右边第三栏的 finding in depth 下面有个total crashes,这个就是我们按下ctr +c之前找到的漏洞,这个界面的各个部分具体是什么意思我们以后来分析。
下面这个链接也列出来了全部的标签
https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/status_screen.md
打开本地文件夹FuzzTest,可以看到fuzz_out 多了很多文件
queue:存放所有具有独特执行路径的测试用例。
crashes:导致目标接收致命signal而崩溃的独特测试用例。
crashes/README.txt:保存了目标执行这些crash文件的命令行参数。
afl-fuzz -i fuzz_in -o fuzz_out ./fuzzTest
hangs:导致目标超时的独特测试用例。
fuzzer_stats:afl-fuzz的运行状态。
plot_data:用于afl-plot绘图。
三、何时结束测试?
原作者提到
This is a difficult question. Basically if no new path is found for a long time (e.g. for a day or a week) then you can expect that your fuzzing won't be fruitful anymore. However often this just means that you should switch out secondaries for others, e.g. custom mutator modules, sync to very different fuzzers, etc.
Keep the queue/ directory (for future fuzzings of the same or similar targets) and use them to seed other good fuzzers like libfuzzer with the -entropic switch or honggfuzz.
但一般来讲,也是有几个原则,检查afl-fuzz工作状态的目的是为何时停止测试提供依据,通常来说符合下面几种情况时就可以停掉了。
(1)状态窗口中 "cycles done" 字段颜色变为绿色该字段的颜色可以作为何时停止测试的参考,随着周期数不断增大,其颜色也会由洋红色,逐步变为黄色、蓝色、绿色。当其变为绿色时,继续Fuzzing下去也很难有新的发现了,这时便可以通过Ctrl-C停止afl-fuzz。
(我这里很快就被我结束了,所以还是黄色)
(2)距上一次发现新路径(或者崩溃)已经过去很长时间了,至于具体多少时间还是需要自己把握,看测试人员的耐心了。
(3)目标程序的代码几乎被测试用例完全覆盖,这种情况可能很少见,但是对于某些小型程序应该还是可能的。
应该还有一些简单原则,后续再补充。
四、AFL状态窗口
① Process timing:Fuzzer运行时长、以及距离最近发现的路径、崩溃和挂起经过了多长时间。
② Overall results:Fuzzer当前状态的概述。
③ Cycle progress:我们输入队列的距离。
④ Map coverage:目标二进制文件中的插桩代码所观察到覆盖范围的细节。
⑤ Stage progress:Fuzzer现在正在执行的文件变异策略、执行次数和执行速度。
⑥ Findings in depth:有关我们找到的执行路径,异常和挂起数量的信息。
⑦ Fuzzing strategy yields:关于突变策略产生的最新行为和结果的详细信息。
⑧ Path geometry:有关Fuzzer找到的执行路径的信息。
⑨ CPU load:CPU利用率
网友评论