初识AFL:工具安装与基本用法
字数 2018 2025-08-22 12:23:24
AFL模糊测试工具安装与使用指南
一、AFL简介
AFL (American Fuzzy Lop) 是一款基于覆盖引导(Coverage-guided)的模糊测试工具,通过记录输入样本的代码覆盖率来调整输入样本以提高覆盖率,增加发现漏洞的概率。其主要特点包括:
- 低性能消耗
- 高效的fuzzing策略
- 支持有源码和无源码两种测试模式
- 自动化的测试用例生成和优化
二、AFL安装
1. 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
2. 手动安装
基本安装
git clone https://github.com/google/AFL.git
make
sudo make install
QEMU模式安装(用于无源码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
3. 安装后生成的主要工具
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 | 对单个用例进行执行路径跟踪 |
三、AFL使用
1. 有源码测试
编写测试程序
// test.c
#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
2. 无源码测试
先把fuzz_out文件夹清空,然后执行以下指令(区别在于多了-Q):
afl-fuzz -i fuzz_in/ -o fuzz_out/ -Q ./afl_test
3. 运行结果分析
AFL运行后会显示如下信息:
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%]
主要信息包括:
- 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利用率
4. 测试结果分析
fuzz后会发现在fuzz_out目录下生成以下文件夹:
- crashes: 产生crash的测试用例
- hangs: 产生超时的测试用例
- queue: 每个不同执行路径的测试用例
可以查看造成crash的用例:
xxd id:000000,sig:06,src:000000,op:havoc,rep:64
xxd id:000001,sig:11,src:000002,op:havoc,rep:2
xxd id:000002,sig:06,src:000001,op:havoc,rep:16
xxd id:000003,sig:06,src:000002,op:havoc,rep:64
xxd id:000004,sig:11,src:000002,op:havoc,rep:4
xxd id:000005,sig:11,src:000001,op:havoc,rep:128
也可以测试crash用例:
cat id:000000,sig:06,src:000000,op:havoc,rep:64 | ./afl_test
cat id:000001,sig:11,src:000002,op:havoc,rep:2 | ./afl_test
cat id:000002,sig:06,src:000001,op:havoc,rep:16 | ./afl_test
cat id:000003,sig:06,src:000002,op:havoc,rep:64 | ./afl_test
cat id:000004,sig:11,src:000002,op:havoc,rep:4 | ./afl_test
cat id:000005,sig:11,src:000001,op:havoc,rep:128 | ./afl_test
四、常见问题解决
1. Pipe at the beginning of 'core_pattern'错误
执行fuzz的时候可能出现如下报错:
[-] PROGRAM ABORT : Pipe at the beginning of 'core_pattern'
解决方法:
sudo echo core > /proc/sys/kernel/core_pattern
五、AFL算法原理
模糊测试的基本思想是通过输入大量非法或意外的数据来测试程序的健壮性和安全性。AFL算法流程如下:
- 把用户提供的初始测试用例加载到队列(queue)中
- 从队列中获取下一个测试输入文件
- 在保持程序行为的前提下,尝试修剪(trim)测试用例(体积)到最小
- 使用传统的模糊测试策略中的各种已有的研究方法,重复变异文件
- 如果生成的变异结果能够驱动新的状态转换(通过插桩记录),则把这一测试用例加入到队列中
- 重复到第2步
AFL通过记录输入样本的代码覆盖率来调整输入样本以提高覆盖率,增加发现漏洞的概率。它使用高效的变异策略和启发式算法来自动确定文件格式结构,从而有效地发现程序中的潜在漏洞。