Fuzzing是指通过构造测试输入,对软件进行大量测试来发现软件中的漏洞的一种模糊测试方法。当前大多数远程代码执行和特权提升等比较严重的漏洞都是使用Fuzzing技术挖掘的,Fuzzing技术被证明是当前鉴别软件安全问题方面最强大测试技术。
然而Fuzzing技术仍然存在着覆盖率低的缺陷,而许多的代码漏洞需要更大的路径覆盖率才能触发,而不是通过纯粹的随机尝试。
AFL (American Fuzzy Lop)是一款采取遗传算法生成用例的Fuzzing工具,可以有效地解决这些问题。AFL有两种Fuzzing方法:
另外,AFL有基于gcc和llvm的两种实现方式,本文只讨论基于llvm的AFL对开源软件即有源码的程序的插桩和fuzz过程。
首先用afl-clang-fast编译源代码进行插桩,然后启动afl-fuzz程序,将testcase(输入的测试文件)作为程序的输入执行程序,AFL会在这个testcase的基础上进行自动变异输入,使得程序产生crash,产生的crash会被记录起来用于进一步分析。
首先使用以下命令编译源文件,编译过程中会进行插桩:
afl-clang-fast -g -o afl_test afl_test.c
如果是编译c++的源码,那就需要用afl-clang-fast++
。
接着建立两个目录:fuzz_in和fuzz_out,用来存放程序的输入文件和fuzz的输出文件。在fuzz_in中创建一个testcase文件,用于写入被测试程序的输入。
在编译项目时,通常有Makefile,这时就需要在Makefile中添加内容:
CC=/path/to/afl/afl-clang-fast ./configure
make
对那些可以直接从stdin读取输入的目标程序来说,语法如下:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program […params…]
对从文件读取输入的目标程序来说,要用“@@”,语法如下:
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
process timing:
这里展示了当前fuzz的运行时间、最近一次发现新执行路径的时间、最近一次崩溃的时间、最近一次超时的时间。
值得注意的是第2项,最近一次发现新路径的时间。如果从fuzzing开始一直没有发现新的执行路径,那么就要考虑是否有二进制或者命令行参数错误的问题了,对于此状况,AFL也会智能地进行提醒。
overall results:
这里包括运行的总周期数、总路径数、崩溃次数、超时次数。
其中,总周期数可以用来作为何时停止fuzzing的参考。随着不断地fuzzing,周期数会不断增大,其颜色也会由洋红色,逐步变为黄色、蓝色、绿色。一般来说,当其变为绿色时,代表可执行的内容已经很少了,继续fuzzing下去也不会有什么新的发现了。此时,我们便可以通过Ctrl-C,中止当前的fuzzing。
stage progress:
这里包括正在测试的fuzzing策略、进度、目标的执行总次数、目标的执行速度。
执行速度可以直观地反映当前跑的快不快,如果速度过慢,比如低于500次每秒,那么测试时间就会变得非常漫长。如果发生了这种情况,那么我们需要进一步调整优化我们的