简介
AFL(American Fuzzy Lop)是一款基于覆盖引导(Coverage-guided)的模糊测试工具,通过记录输入样本的代码覆盖率,从而调整输入样本以提高覆盖率,增加发现漏洞的概率,具有较低的性能消耗和高效的 fuzzing 策略。
安装
release版安装
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
tar xvf afl-latest.tgz
cd afl-2.52b
sudo make && sudo make install
cd qemu_mode
./build_qemu_support.sh
手动安装
基本安装
git clone https://github.com/google/AFL.git
make
sudo make install
qemu_mode安装
用于无源码 fuzz,先安装必要环境
sudo apt-get install libtool-bin libtool wget python automake autoconf sha384sum bison iconv
进入 qemu_mode 文件夹修改 build_qemu_support.sh 中的 QEMU_URL 为https://download.qemu.org/qemu-${VERSION}.tar.xz
进入 patches 文件夹在 syscall.diff 文件的第三行开始插入以下内容
@@ -34,6 +34,7@@
#include <sys/resource.h>
#include <sys/swap.h>
#include <linux/capability.h>
+#include <linux/sockios.h>
#include <sched.h>
#include <sys/timex.h>
#include <sys/socket.h>
在 patches 文件夹下创建 gettid.diff 文件,内容如下
--- qemu-2.10.0-rc3-clean/linux-user/syscall.c 2020-11-06 22:14:34.218924847 -0500
+++ qemu-2.10.0-rc3/linux-user/syscall.c 2020-11-06 22:17:09.722926317 -0500
@@ -258,7 +258,8 @@
#endif
#ifdef __NR_gettid
-_syscall0(int, gettid)
+#define __NR_sys_gettid __NR_gettid
+_syscall0(int, sys_gettid)
#else
/* This is a replacement for the host gettid() and must return a host
errno. */
@@ -6221,7 +6222,7 @@
cpu = ENV_GET_CPU(env);
thread_cpu = cpu;
ts = (TaskState *)cpu->opaque;
- info->tid = gettid();
+ info->tid = sys_gettid();
task_settid(ts);
if (info->child_tidptr)
put_user_u32(info->tid, info->child_tidptr);
@@ -6365,9 +6366,9 @@
mapping. We can't repeat the spinlock hack used above because
the child process gets its own copy of the lock. */
if (flags & CLONE_CHILD_SETTID)
- put_user_u32(gettid(), child_tidptr);
+ put_user_u32(sys_gettid(), child_tidptr);
if (flags & CLONE_PARENT_SETTID)
- put_user_u32(gettid(), parent_tidptr);
+ put_user_u32(sys_gettid(), parent_tidptr);
ts = (TaskState *)cpu->opaque;
if (flags & CLONE_SETTLS)
cpu_set_tls (env, newtls);
@@ -11404,7 +11405,7 @@
break;
#endif
case TARGET_NR_gettid:
- ret = get_errno(gettid());
+ ret = get_errno(sys_gettid());
break;
#ifdef TARGET_NR_readahead
case TARGET_NR_readahead:
qemu_mode 目录下运行 ./build_qemu_support.sh
回到 AFL 目录执行:
sudo make install
sudo cp ./afl-qemu-trace /usr/local/bin
说明
make 之后生成的程序会被保存在/usr/local/bin
中可以直接调用,用途如下:
程序 | 功能 |
---|---|
afl-gcc / afl-g++ | gcc / g++ 的封装,可以编译文件并在源文件中添加特殊指令编译插桩 |
afl-clang / afl-clang++ | clang / clang++ 的封装 |
afl-fuzz | 对目标程序进行 fuzz |
afl-analyze | 对用例进行分析,寻找有意义的字段 |
afl-qemu-trace | 用于 qemu-mode 在无源码的条件下进行 fuzz |
afl-plot | 生成测试任务的状态图 |
afl-tmin afl-cmin | 对用例进行简化 |
afl-whatsup | 查看 fuzz 任务状态 |
afl-gotcpu | 查看 cpu 状态 |
afl-showmap | 对单个用例进行执行路径跟踪 |
使用
有源码
编写测试程序
// 编写测试程序test.c,这里头文件都用"",主要是防止博客识别markdown时将其清空。实际编写时请替换回<>
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "string.h"
#include "signal.h"
int vuln(char *str)
{
int len = strlen(str);
if(str[0] == 'A' && len == 66)
{
// 如果输入的字符串的首字符为A并且长度为66,则异常退出
raise(SIGSEGV);
}
else if(str[0] == 'F' && len == 6)
{
// 如果输入的字符串的首字符为F并且长度为6,则异常退出
raise(SIGSEGV);
}
else
{
printf("it is good!\n");
}
return 0;
}
int main(int argc, char *argv[])
{
char buf[100]={0};
gets(buf); // 存在栈溢出漏洞
printf(buf); // 存在格式化字符串漏洞
vuln(buf);
return 0;
}
使用 afl-gcc 编译插桩
$afl-gcc test.c -o afl_test
创建 fuzz_in 和 fuzz_out 文件夹,在 fuzz_in 中创建 testcase 输入样例,内容 abc
初始输入数据(也叫种子文件)作为Fuzzing的起点,这些输入甚至可以是毫无意义的数据,AFL可以通过启发式算法自动确定文件格式结构,输入数据需要是有效输入且体积尽量小
执行 fuzz
$afl-fuzz -i fuzz_in -o fuzz_out ./afl_test
无源码
先把 fuzz_out 文件夹清空然后执行以下指令(区别在于多了-Q
)
$ afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q ./afl_test
运行结果
american fuzzy lop 2.57b (afl_test)
┌─ process timing ─────────────────────────────────────┬─ overall results ─────┐
│ run time : 0 days, 0 hrs, 10 min, 51 sec │ cycles done : 377 │
│ last new path : 0 days, 0 hrs, 10 min, 51 sec │ total paths : 3 │
│ last uniq crash : 0 days, 0 hrs, 0 min, 51 sec │ uniq crashes : 6 │
│ last uniq hang : none seen yet │ uniq hangs : 0 │
├─ cycle progress ────────────────────┬─ map coverage ─┴───────────────────────┤
│ now processing : 1 (33.33%) │ map density : 0.05% / 0.06% │
│ paths timed out : 0 (0.00%) │ count coverage : 1.00 bits/tuple │
├─ stage progress ────────────────────┼─ findings in depth ────────────────────┤
│ now trying : havoc │ favored paths : 2 (66.67%) │
│ stage execs : 160/256 (62.50%) │ new edges on : 3 (100.00%) │
│ total execs : 269k │ total crashes : 3140 (6 unique) │
│ exec speed : 403.6/sec │ total tmouts : 0 (0 unique) │
├─ fuzzing strategy yields ───────────┴───────────────┬─ path geometry ────────┤
│ bit flips : 1/96, 0/93, 0/87 │ levels : 2 │
│ byte flips : 0/12, 0/9, 0/3 │ pending : 0 │
│ arithmetics : 1/672, 0/75, 0/0 │ pend fav : 0 │
│ known ints : 0/58, 0/252, 0/132 │ own finds : 2 │
│ dictionary : 0/0, 0/0, 0/0 │ imported : n/a │
│ havoc : 6/267k, 0/0 │ stability : 100.00% │
│ trim : n/a, 0.00% ├────────────────────────┘
────────────────────────────────────────────────────┘ [cpu000: 8%]
- 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利用率
中文翻译版(对齐不上看着难受,去掉了一些竖线
american fuzzy lop 2.57b (afl_test)
┌─────────────────── 处 理 时 间 ──────────────────┬────── 总体结果─────┐
│ 运行时间 : 0 days, 0 hrs, 10 min, 51 sec 完成循环 : 377
│ 最后一条新路径 : 0 days, 0 hrs, 10 min, 51 sec 总路径数 : 3
│ 最后一次崩溃 : 0 days, 0 hrs, 0 min, 51 sec 崩溃 : 6
│ 最后一次挂起 : none seen yet 挂起 : 0
├──────── 循 环 进 度 ────────┬─────────── 覆 盖 率 ──┴───────────────────┤
│ 当前处理 : 1 (33.33%) │ 密度 : 0.05% / 0.06%
│ 路径超时 : 0 (0.00%) │ 计数覆盖率 : 1.00 bits/tuple
├───────── 阶 段 进 度 ─────────┼─── 深 入 发 现 ───────────────────────────┤
│ 当前尝试 : havoc │ 首选路径 : 2 (66.67%)
│ 阶段执行 : 160/256 (62.50%) │ 新边 : 3 (100.00%)
│ 全部执行 : 269k │ 总崩溃数 : 3140 (6 unique)
│ 执行速度 : 403.6/sec │ 总 tmouts : 0 (0 unique)
├─────── 模糊测试策略结果 ───────┴───────────────┬──── 执行路径信息 ──────────┤
│ 位翻转 : 1/96, 0/93, 0/87 级别 : 2
│ 字节翻转 : 0/12, 0/9, 0/3 待处理 : 0
│ 算术 : 1/672, 0/75, 0/0 待处理收藏 : 0
│ 已知整数 : 0/58, 0/252, 0/132 自己的发现 : 2
│ 字典 : 0/0, 0/0, 0/0 导入 : n/a
│ 破坏 : 6/267k, 0/0 稳定性 : 100.00%
│ 修剪 : n/a, 0.00% ├──────────────────────────┘
─────────────────────────────────────────────┘ [cpu000: 8%]
fuzz 后发现了6个crash,进入 fuzz_out 目录下查看文件夹:
- crashes:产生crash的测试用例
- hangs:产生超时的测试用例
- queue:每个不同执行路径的测试用例
进入 crashes 文件夹查看造成 crash 的用例
$crashes xxd id:000000,sig:06,src:000000,op:havoc,rep:64
00000000: 7fcb d5d5 b3d5 7575 ffff ff7f b3c1 7575 ......uu......uu
00000010: 7fff 2b00 af7a 350b 7d88 7a35 0b7d 8875 ..+..z5.}.z5.}.u
00000020: 751b 017f abba d57f 0000 00ff ff00 0058 u..............X
00000030: 7a00 0100 008b bb88 3575 1b01 7f2b ba58 z.......5u...+.X
00000040: 7a35 0b7d 98ef ff80 bad5 7f00 0000 ffff z5.}............
00000050: d5d5 b3c1 7575 7fff 2323 af7a 350b 7d88 ....uu..##.z5.}.
00000060: 7575 1b01 7fab bad5 7f00 0000 ffff 0000 uu..............
00000070: 587a 350b 8888 8bbb Xz5.....
$xxd id:000001,sig:11,src:000002,op:havoc,rep:2
00000000: 4646 6262 6303 FFbbc.
$xxd id:000002,sig:06,src:000001,op:havoc,rep:16
00000000: 41fd fdfd fdfd fdfd fdfd fdfd 00fe 11fd A...............
00000010: fdfd fdfd fdfd fdfd fdfd fd00 04fd 0441 ...............A
00000020: fdfd fefd fdfd 00fd 06fe 0000 fd04 00fd ................
00000030: fdfd fdff ff00 00fd fdfd fdfd fdfd fd00 ................
00000040: fe11 fdfd 051e 0400 fdfd fdfd fdfd fdfd ................
00000050: fdfd fdfd fdfd fdfd 00fe 11fd fdfd fdfd ................
00000060: fdfd fdfd fdfd fdfd fde5 fd01 0010 ..............
$xxd id:000003,sig:06,src:000002,op:havoc,rep:64
00000000: 4696 9a9a 9a9a 9aba b19a 9a9a 9a8e 019a F...............
00000010: 9a9a 9a9a bab1 9a9a 9a9a 8e01 9a9a 9a8d ................
00000020: 9a8e ff9a 9a9f 5a5a 5a5a 5a5a 5a09 8d9a ......ZZZZZZZ...
00000030: 8eff 9a9a 9f5a 5a5a 5a5a 5a5a 095a 5a5a .....ZZZZZZZ.ZZZ
00000040: 5a5a 5a5a 5a5a 5a5a 5a5a 5a5a 9a9a 9a9a ZZZZZZZZZZZZ....
00000050: 9a20 0000 00ff e757 109a 9a9a 9a9a 0080 . .....W........
00000060: 9f5a 5a5a 5a5a 5a5a 095a 5a5a 5a5a 5a5a .ZZZZZZZ.ZZZZZZZ
00000070: 5a5a 5a5a 5a5a 5a5a 9a80 f701 0000 ff ZZZZZZZZ.......
$xxd id:000004,sig:11,src:000002,op:havoc,rep:4
00000000: 2573 630a %sc.
$xxd id:000005,sig:11,src:000001,op:havoc,rep:128
00000000: 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b {{{{{{{{{{{{{{{{
00000010: 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b {{{{{{{{{{{{{{{{
00000020: 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b {{{{{{{{{{{{{{{{
...
000015b0: 7b7b 7b7b 7b7b 7b7b 7b7b 7b7b 7b93 8280 {{{{{{{{{{{{{...
也可以测试 crash 用例
$cat id:000000,sig:06,src:000000,op:havoc,rep:64 | ../../afl_test
��ճ�uu�����uu�+it is good!
*** stack smashing detected ***: terminated
[1] 312817 done cat id:000000,sig:06,src:000000,op:havoc,rep:64 |
312818 abort ../../afl_test
$cat id:000001,sig:11,src:000002,op:havoc,rep:2 | ../../afl_test
[1] 312920 done cat id:000001,sig:11,src:000002,op:havoc,rep:2 |
312921 segmentation fault ../../afl_test
$cat id:000002,sig:06,src:000001,op:havoc,rep:16 | ../../afl_test
A�����������it is good!
*** stack smashing detected ***: terminated
[1] 313025 done cat id:000002,sig:06,src:000001,op:havoc,rep:16 |
313026 abort ../../afl_test
$cat id:000003,sig:06,src:000002,op:havoc,rep:64 | ../../afl_test
F�����������������������������������ZZZZZZZ �������ZZZZZZZ ZZZZZZZZZZZZZZZ����� it is good!
*** stack smashing detected ***: terminated
[1] 313141 done cat id:000003,sig:06,src:000002,op:havoc,rep:64 |
313142 abort ../../afl_test
$cat id:000004,sig:11,src:000002,op:havoc,rep:4 | ../../afl_test
[1] 313244 done cat id:000004,sig:11,src:000002,op:havoc,rep:4 |
313245 segmentation fault ../../afl_test
$cat id:000005,sig:11,src:000001,op:havoc,rep:128 | ../../afl_test
{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{�{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{i{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{it is good!
*** stack smashing detected ***: terminated
[1] 313356 done cat id:000005,sig:11,src:000001,op:havoc,rep:128 |
313357 abort ../../afl_test
报错解决
执行 fuzz 的时候可能出现如下报错:
[-] PROGRAM ABORT : Pipe at the beginning of 'core_pattern'
解决方法:
sudo echo core > /proc/sys/kernel/core_pattern
AFL算法
模糊测试的基本思想是通过输入大量非法或意外的数据来测试程序的健壮性和安全性,通常模糊测试工具会根据目标程序的输入格式生成大量变体数据,一旦发现导致程序崩溃或异常行为的输入数据,模糊测试工具就会对这些数据进行分析和记录,并将其作为种子数据进行进一步变异,以触发更多潜在漏洞。afl 算法流程如下:
-
把用户提供的初始测试用例加载到队列(queue)中
-
从队列中获取下一个测试输入文件
-
在保持程序行为的前提下,尝试修剪(trim)测试用例(体积)到最小
-
使用传统的模糊测试策略中的各种已有的研究方法,重复变异文件
-
如果生成的变异结果能够驱动新的状态转换(通过插桩记录),则把这一测试用例加入到队列中
-
重复到第2步
参考文章
https://cata1oc.github.io/2021/12/22/AFL%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA
https://0xc4m3l-jiang.github.io/2021/03/09/afl%E4%BD%BF%E7%94%A8%E5%AD%A6%E4%B9%A0/