经典 Fuzzer 工具 AFL 模糊测试指南
AFL(American Fuzzy Lop)是由安全研究员Michał Zalewski 开发的一款基于覆盖引导(Coverage-guided)的模糊测试工具。通过记录输入样本的代码覆盖率,不断对输入进行变异,从而达到更高的代码覆盖率。AFL 采用新型的编译时插桩和遗传算法自动发现新的测试用例,这些用例会触发目标二进制文件中的新内部状态。这大大改善了模糊测试的代码覆盖范围。1
- 从源码编译程序时进行插桩,以记录代码覆盖率(Code Coverage);
- 选择一些输入文件,作为初始测试集加入输入队列(queue);
- 将队列中的文件按一定的策略进行变异”;
- 如果经过变异文件更新了覆盖范围,则将其保留添加到队列中;
- 上述过程会一直循环进行,期间触发了crash的文件会被记录下来。
0x10 安装 afl
以下三个版本任选其一(推荐第二个,笔者三个都试过,在普通 afl 模式下,三个都没有问题。但是涉及到更深层次的用法,多多少少都有些 bug;第二个是相对较好的一个版本)
- 原版 AFL
- afl-unicorn
- AFLplusplus
AFLplusplus,引进了更强的编译算法,如果是源码插桩的方式,更适合使用 AFLplusplus。进入到已经下载好的项目,安装 afl,命令如下
make # 编译 afl
sudo make install
这是编译成功的截图
0x20 构建被测程序(白盒测试)
用 AFL 编译目标源码,其目的在于插桩,让编译得到的程序,反馈路径覆盖。AFL 自带定制版本的 gcc 和 clang 编译器,建议选择 LLVM 的 clang 编译器,可以加快 fuzz 的速度。
0x21 使用 afl-gcc 进行源码插桩
对于单个文件,直接使用 afl-gcc 代替 gcc 即可
afl-gcc test.c -o test
对于完整的项目,需要将编译器指定为 afl-gcc,然后再进行编译
./configure CC="afl-gcc" CXX="afl-g++" # 或者直接修改 Makefie 文件,将编译器改位 afl-gcc
make
如果需要 fuzz 共享库,可以通过设置 LD_LIBRARY_PATH
让程序加载经过 AFL 插桩的 .so 文件,不过最简单的方法是静态构建,通过以下方式实现
./configure --disable-shared CC="afl-gcc" CXX="afl-g++"
0x22 使用 LLVM 模式进行源码插桩
AFL 还支持使用 LLVM 模式,可以获得更快的Fuzzing速度,而且具有更多的选项,LLVM 的前端表现形式是 clang 编译器,因此需要自行安装 clang,之后就可以编译了
afl-clang test.c -o test
0x23 出现 error while loading shared libraries 错误 2
构建完成之后,可能出现如下错误。这是因为编译器默认只会使用 /lib 和 /usr/lib 这两个目录下的库文件,通过源码编译的方式安装程序,如果不指定--prefix
,会将库安装在/usr/local/lib
目录下;当运行程序需要链接动态库时,提示找不到相关的.so库,会报错。也就是说,/usr/local/lib目录不在系统默认的库搜索目录中,需要将目录加进去。
解决方法
- 打开
/etc/ld.so.conf
文件 - 加入动态库文件所在的目录:执行
vi /etc/ld.so.conf
,在"include ld.so.conf.d/*.conf"
下方增加"/usr/local/lib"
- 保存后,在命令行终端执行:
/sbin/ldconfig -v
;其作用是将文件/etc/ld.so.conf
列出的路径下的库文件缓存到/etc/ld.so.cache
以供使用,因此当安装完一些库文件,或者修改etc/ld.so.conf
增 加了库的新搜索路径,需要运行一下 ldconfig,使所有的库文件都被缓存到文件/etc/ld.so.cache
中,如果没做,可能会找不到刚安装的库。
0x30 选择语料库
其实就是给被测程序喂入合适的测试用例,afl 会根据这些原始种子,变异生成大量的用例。理想条件下,提供的语料库(即原始的测试用例)能够让程序执行不同的路径,这样才能达到代码覆盖最大化。
0x31 选择用例
这里,借用 Freebuf 提供的资料,给出一些开源的语料库
- afl generated image test sets
- fuzzer-test-suite
- libav samples
- ffmpeg samples
- fuzzdata
- moonshine
事实上很多程序也会自带一些案例,也可以作为测试用例。
0x32 精简语料库
找到语料库之后,最好能够进行修剪,合并重复用例,裁剪体积。afl 推荐的每个用例体积小于 1KB,不然会影响 fuzz 的效率。
去重
afl-cmin
是 afl 提供的一个十分有用的工具,可以精简语料库,去掉可能重复的测试用例,针对一些复杂的语料库十分有用,可大大减少无用的 fuzz 用例。
afl-cmin -i input_dir -o output_dir -- /path/to/tested/program [params]
更多的时候,我们是从文件中获取输入,因此,往往使用 @@ 替代 params(参数),即
afl-cmin -i input_dir -o output_dir -- /path/to/tested/program @@
缩小体积
afl-tmin
可以缩短文件体积,因为 afl 要求测试用例的大小最好小于 1KB,因此最好将精简后的用例进一步缩小体积。afl-tmin 有两种工作模式,instrumented mode
和crash mode
。默认的工作方式是instrumented mode
afl-tmin -i input_file -o output_file -- /path/to/tested/program [params] @@
由于 afl-cmin 一次性只能精简单个文件,如果用例特别多,需要手动花费很长时间,其实一条简单的 shell 脚本即可完成
for i in *; do afl-tmin -i $i -o tmin-$i -- ~/path/to/tested/program [params] @@; done;
0x40 fuzzing
在对程序正式进行 fuzz 之前,可以使用 afl-map
跟踪单个用例的执行路径,它会打印出程序的输出和 tuples
afl-showmap -m none -o /dev/null
正式执行 fuzz 测试的命令如下
afl-fuzz -m none -i in -o out target_binary @@
0x50 黑盒 fuzz
以上 fuzz 过程,依赖于我们有程序的源码,并且在编译过程中进行了插桩,但很多时候,我们并没有源码,这时候就要靠 afl 提供的 qemu_mode 模式了。原版本的 afl qemu 模式由于版本过老,已不能正常运行,推荐使用 github 上的 AFLplusplus 或者 afl-unicorn。AFLplusplus
更容易安装,而 afl-unicorn
针对 qemu 模式更加友好。
无论是下载的哪个版本的 afl,根目录下都会有 qemu_mode 文件夹,进入此目录,运行以下脚本,如果没有出错,就代表 qemu_mode 成功了
cd qemu_mode
sudo ./build_qemu_support.sh
如果出错,请访问:深入分析 afl / qemu-mode(qemu模式) / afl-unicorn 编译及安装存在的问题以及相应的解决方案
如果要对不同架构的二进制文件进行黑盒 fuzz,需要在编译 qemu 脚本前,指定相应的架构,举个例子,要在 x86 架构下,fuzz arm 架构的程序,需要运行如下命令
CPU_TARGET=arm ./build_qemu_support.sh
编译成功后,即可进行黑盒 fuzz
afl-fuzz -Q -m none -i in -o out target_binary @@
0x60 实战分析
0x61 源码分析
编写个 demo,举例来说。这里为了体现 afl 的插桩功能,特意多写了几个 if 分支,在分支深处,会发生栈溢出
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>int main(int argc, char const *argv[])
{if(argc != 2){printf("null args!\n");return -1;}/* Get file state */struct stat fstat;if(stat(argv[1], &fstat)){printf("Failed ^_^\n");return -1;}/* Open file */FILE * fd = NULL;fd = open(argv[1], O_RDONLY);if(fd == -1){printf("open file failed!\n");return -1;}/* Select */char buf[15];if(read(fd, buf, 2) == -1){printf("read failed!");return -1;}if(buf[0] == 'a' && buf[1] == 'b'){if(read(fd, buf, 4) != -1){if(buf[2] == 's'){read(fd, buf, fstat.st_size - 6);printf("%s\n", buf);}}}return 0;
}
0x62 源码插桩
使用 afl 的 LLVM 模式编译,即对源代码进行插桩
afl-clang afl_demo.c -o afl_demo
每一个 if 语句代表每一个条件分支,在 IDA 的反汇编窗口现实的就是一个基本块。一个 if 语句就是一个新分支,如下所示,我们用 afl-clang 编译后的反汇编代码,可以看到,在每个 if 语句中,都已经自动插桩,_afl_maybe_log
是插桩函数,用来反馈路径
0x63 生成语料库
新建 in 目录,在目录下新建文件,写入种子,变异算法会根据此种子变异生成各种测试用例,喂给程序,比如写入以下信息(根据代码,当第一个字符为 a,第二个字符为 b,第五个字符 为 s,可能会发生溢出)
abttdcccccccccccccccccccccccccaaaaaaaaaaaaaaaaaaaaaaadddddddddddddddddddddddddddd
0x64 开始 fuzzing
root@lys-virtual-machine:~/Documents/test# afl-fuzz -m none -i in -o out ./afl_demo @@
afl-fuzz++2.60d based on afl by Michal Zalewski and a big online community
[+] afl++ is maintained by Marc "van Hauser" Heuse, Heiko "hexcoder" Eißfeldt and Andrea Fioraldi
[+] afl++ is open source, get it at https://github.com/vanhauser-thc/AFLplusplus
[+] Power schedules from github.com/mboehme/aflfast
[+] Python Mutator and llvm_mode whitelisting from github.com/choller/afl
[+] afl-tmin fork server patch from github.com/nccgroup/TriforceAFL
[+] MOpt Mutator from github.com/puppet-meteor/MOpt-AFL
[*] Getting to work...
[+] Using exploration-based constant power schedule (EXPLORE)
[+] You have 8 CPU cores and 2 runnable tasks (utilization: 25%).
[+] Try parallel jobs - see /usr/local/share/doc/afl/parallel_fuzzing.md.
[*] Checking CPU core loadout...
[+] Found a free CPU core, try binding to #0.
[*] Checking core_pattern...
[!] WARNING: Could not check CPU scaling governor
[*] Setting up output directories...
[+] Output directory exists but deemed OK to reuse.
[*] Deleting old session data...
[+] Output dir cleanup successful.
[*] Scanning 'in'...
[+] No auto-generated dictionary tokens to reuse.
[*] Creating hard links for all input files...
[*] Validating target binary...
[*] Attempting dry run with 'id:000000,time:0,orig:testcases.txt'...
[*] Spinning up the fork server...
[+] All right - fork server is up.len = 81, map size = 9, exec speed = 199 us
[+] All test cases processed.[+] Here are some useful stats:Test case count : 1 favored, 0 variable, 1 totalBitmap range : 9 to 9 bits (average: 9.00 bits)Exec timing : 199 to 199 us (average: 199 us)[*] No -t option specified, so I'll use exec timeout of 20 ms.
[+] All set and ready to roll!
fuzz 窗口
可以看到,已经有 crash 出现,当 cycles done 为绿色时,表示可以停止 fuzz 了。
0x65 结果分析
我们分析一下结果,根据 fuzz 时输入的命令,我们的结果是输出在 out 目录下
root@lys-virtual-machine:~/Documents/test/out# tree
.
├── cmdline
├── crashes
│ ├── id:000000,sig:11,src:000001,time:600493+000003,op:splice,rep:64
│ ├── id:000001,sig:11,src:000003,time:600898,op:havoc,rep:64
│ └── README.txt
├── fuzz_bitmap
├── fuzzer_stats
├── hangs
├── plot_data
└── queue├── id:000000,time:0,orig:testcases.txt├── id:000001,src:000000,time:3,op:flip1,pos:0,+cov├── id:000002,src:000000,time:6,op:flip1,pos:1,+cov└── id:000003,src:000000,time:3406,op:havoc,rep:2,+cov3 directories, 11 files
- crashes:导致目标接收致命 signal 而崩溃的独特测试用例
- fuzzer_stats:afl-fuzz 的运行状态
- hangs:导致目标超时的独特测试用例
- plot_data:用于 afl-plot 绘图
- queue:存放所有具有独特执行路径的测试用例
afl-plot 可以绘制更加直观的结果,利用的就是 fuzzer 生成的 plot_data 文件。当然,要使用 afl-plot ,需要先安装 apt-get install gnuplot
root@lys-virtual-machine:~/Documents/test# afl-plot out result/
progress plotting utility for afl-fuzz by Michal Zalewski[*] Generating plots...
[*] Generating index.html...
[+] All done - enjoy your charts!
这样就可以在 result 目录下,生成 html 和图片文件,如下所示
- 第一幅图:路径覆盖变化,pending fav 数量变为零并且 total paths 数量基本上没有再增长时,说明 fuzzer 有新发现的可能性就很小了
- 崩溃和超时的变化
- 执行速度的变化
要想重新找到 bug,直接输入 crash 目录下的测试用例即可。
0x70 总结
AFL 作为一款优秀的 fuzz 工具,通过源码插桩的方式,计算代码覆盖率,再以此为基础,对语料库(种子文件)不断进行变异,从而达到增大代码覆盖率的效果。本文主要讲解其一般用法,其效率最高的也是 LLVM 模式下的源码插桩,结合具体实例,进行 fuzz 测试。对 C/C++ 代码编译成的二进制文件,虽然 AFL 也提供基于 qemu 的无源码黑盒 fuzz,但是效率低,发现漏洞的可能性小。所以最好采用 AFL 的一般模式,即基于源码插桩的 fuzz 测试。
在没有源码的情况下,如果想直接使用二进制插桩,需要安装 qemu-mode 或者 unicorn 模式,安装可能会出现很多问题,欢迎访问另外一篇博客:深入分析 afl / qemu-mode(qemu模式) / afl-unicorn 编译及安装存在的问题以及相应的解决方案。
AFL 作为一个具有跨时代意义的 fuzz 工具,后续其他的 fuzzer 如雨后春笋,不断涌现,如果你想了解更多的 AFL 改进型工具,或者你想了解更多的 fuzzing 技术,欢迎访问:https://github.com/liyansong2018/fuzzing-tutorial
https://www.freebuf.com/articles/system/191543.html ↩︎
https://www.cnblogs.com/codingmengmeng/p/7456539.html ↩︎
经典 Fuzzer 工具 AFL 模糊测试指南相关推荐
- 【软件与系统安全】AFL模糊测试实验
软件与系统安全 - AFL 模糊测试实验 软件与系统安全的作业,写得不尽详尽,仍有问题未解决,欢迎反馈 对 Coreutils 软件集合使用 AFL 进行模糊测试,撰写测试报告 在报告中详述: (1) ...
- 2021年软件测试工具总结——模糊测试工具
~ 什么是模糊测试?~ 模糊测试(Fuzz Testing)是一种自动化的软件测试技术,最初是由威斯康辛大学的巴顿·米勒于1989年开发的,通常用于识别程序中的潜在漏洞.模糊测试的核心是自动或半自动的 ...
- 利用AFL进行模糊测试
1 模糊测试概述 1.1 模糊测试概念 模糊测试(Fuzzing),是一种通过向目标系统提供非预期的输入并监视异常结果来发现软件漏洞的方法.它是一种挖掘软件安全漏洞.检测软件健壮性的黑盒测试,它通过向 ...
- linux 符号执行,[原创]符号执行Symcc与模糊测试AFL结合实践
上个月末无聊的划水时间段内,在推上看到有人发了一篇关于如何结合去年新发布的符号执行Symcc与模糊测试引擎AFL,以提升Fuzz效率的视频贴.打开这个链接后才发现是个卖课的,emmm.... , 看价 ...
- 模糊测试+符号执行等漏洞挖掘工具安装使用
漏洞挖掘工具 符号执行 KLEE QSYM driller+AFL symcc+AFL 模糊测试 AFL AFLGO AFL++ honggfuzz 隔离了一周,解封了随便写点东西吧 符号执行 KLE ...
- 模糊测试入门案例,利用AFL和Honggfuzz模糊测试Tiff
1.AFL模糊测试tiff AFL的安装已经在前文记录过American Fuzzy Lop(AFL)的安装与简单使用,不再赘述.这里主要记录一下在使用AFL时的可以注意的点. 最好选择由C或者C++ ...
- 网站模糊测试爆破工具Wfuzz
网站模糊测试爆破工具Wfuzz 模糊测试爆破使用模糊测试的方式对HTTP请求中的各个参数同时进行猜测爆破.例如,渗透测试人员可以采用不同的HTTP请求方式来访问由字典生成的网页路径,以判断网页目录或者 ...
- 知识普及:关于Fuzzing模糊测试入门原理及实践的讨论
狩猎者网络安全旗下--知柯信息安全团队(知柯信安) 漏洞挖掘是否是真正的安全呢? "The best alternative to defense mechanisms is to find ...
- 模糊测试(fuzz testing)介绍(一)
模糊测试(fuzz testing)是一类安全性测试的方法.说起安全性测试,大部分人头脑中浮现出的可能是一个标准的"黑客"场景:某个不修边幅.脸色苍白的年轻人,坐在黑暗的房间中,正 ...
最新文章
- 幸运数字Ⅱ(树型结构构造答案,打表)难度⭐⭐
- 正确配置nginx和php
- cmd窗口设置charset
- 训练集、验证集、测试集区分
- CSS 背景尺寸 background-size属性
- Java知识点详解 4 泛型
- 如何为报表服务器设置SQL Server数据库复制
- php4.0中文手册,服务 — CodeIgniter 4.0.0 中文手册|用户手册|用户指南|中文文档
- 如何将道具传递给{this.props.children}
- Linux acpi off学习的必要
- redis 正则匹配value_Java-19-Redis
- 以半桥驱动芯片FAN73933为例说明自举原理
- Powerbi简体中文版修改数据显示单位为英文方法
- 聊聊flink TaskManager的managed memory
- 【渝粤题库】广东开放大学 金融经济学 形成性考核
- Python爬取京东商品评论数据
- 程序员创业:高智商的程序员为什么创业却屡屡失败?
- 表示学习的挑战:三场机器学习竞赛报告Challenges in Representation Learning: A report on three machine learning contests
- TCPMP编译方法(转载)
- 写给婆婆的话,太经典了