Fuzz-AFL入门
工具
AFL 则是fuzzing的一个很好用的工具,全称是American Fuzzy Lop,由Google安全工程师Michał Zalewski开发的一款开源fuzzing测试工具,可以高效地对二进制程序进行fuzzing,挖掘可能存在的内存安全漏洞,如栈溢出、堆溢出、UAF、double free等。原理是通过对源码进行重新编译时进行插桩(简称编译时插桩)的方式自动产生测试用例来探索二进制程序内部新的执行路径。AFL也支持直接对没有源码的二进制程序进行测试,但需要QEMU的支持。
安装使用
从 http://lcamtuf.coredump.cx/afl/ 下载源码,然后解压之后安装
make
sudo make install
安装成功
安装目录
afl-gcc 和afl-g++ 分别对应的是gcc 和g++ 的封装
afl-clang 和afl-clang++ 分别对应clang 的c 和c++ 编译器封装À。
afl-fuzz 是AFL 的主体,用于对目标程序进行fuzz。
afl-analyze 可以对用例进行分析,通过分析给定的用例,看能否发现用例中有意义的字段。
afl-qemu-trace 用于qemu-mode,默认不安装,需要手工执行qemu-mode 的编译脚本进行编译,后面会介绍。
afl-plot 生成测试任务的状态图
afl-tmin 和afl-cmin 对用例进行简化
afl-whatsup 用于查看fuzz 任务的状态
afl-gotcpu 用于查看当前CPU 状态
afl-showmap 用于对单个用例进行执行路径跟踪
图片为我们使用AFL进行测试时的运行截图
整体来说,运行状态主要包括8个部分
- process timing
这里展示了fuzz的运行时间、最近一次发现新执行路径的时间、最近一次崩溃的时间、最近一次超时的时间。
值得注意的是第2项,最近一次发现新路径的时间。如果由于目标二进制文件或者命令行参数出错,那么其执行路径应该是一直不变的,所以如果从fuzzing开始一直没有发现新的执行路径,那么就要考虑是否有二进制或者命令行参数错误的问题了。
- cycle progress
主要记录了当前正在处理的fuzz进程和由于超时放弃的路径数
- stage progress
这里记录了包括正在测试的fuzzing策略、进度、目标的执行总次数、目标的执行速度
执行速度可以直观地反映当前跑的快不快,如果速度过慢,比如低于500次每秒,那么测试时间就会变得非常漫长。如果发生了这种情况,那么我们需要进一步调整优化我们的fuzzing
- fuzzing strategy yields
- overrall results
这里包括运行的总周期数、总路径数、崩溃次数、超时次数。
其中,总周期数可以用来作为何时停止fuzzing的参考。随着不断地fuzzing,周期数会不断增大,其颜色也会由紫色,逐步变为黄色、蓝色、绿色。一般来说,当其变为绿色时,代表可执行的内容已经很少了,继续fuzzing下去也不会有什么新的发现了。此时,我们便可以通过Ctrl-C,中止当前的fuzzing
-
map coverage
-
findings in depth
-
path geometry
levels表示测试等级,pending表示还没有经过fuzzing的输入数量,pend fav表明fuzzer感兴趣的输入数量,own finds表示在fuzzing过程中新找到的,或者是并行测试从另一个实例导入的,imported中的n/a表明不可用,即没有导入。最后说明一下stability,表明相同输入是否产生了相同的行为,一般结果都是100%,如果低于100%并且变红,则需要查阅官方文档寻找解决步骤。
使用方法分为有源码和无源码有二进制应用程序
有源码-Fuzz
对于有源码的fuzz,使用AFL自带的编译器对源码进行插桩编译,然后利用Afl-fuzz对编译好的二进制应用进行fuzz
编译c程序使用afl-gcc,编译c++程序使用afl-g++
$ afl-gcc test.c -o test
$ afl-g++ test.cpp -o test
然后使用afl-fuzz进行测试,命令行如下,-i指明测试用例的目录,-o指明测试结果的存放目录。对于直接从终端获取输入的程序来说,我们需要在testcase_dir目录下新建一个文件,文件的内容就是程序的输入,文件命名不唯一,后面会给一个示例
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...]
对于从文件中获取输入的程序,请使用“@@”标记,AFL将会自动将其替换为测试用例中的文件名
$ ./afl-fuzz -i testcase_dir -o findings_dir /path/to/program @@
无源码二进制应用Fuzz
AFL对于缺少源码的二进制程序fuzz,利用qemu_mode
安装
cd qemu_mode
./ build_qemu_support.sh
中途会遇到报错,记录一下解决方案
报错1:
sudo apt-get install libtool
sudo apt-get install libtool-bin
报错2:
编译qemu的时候报错,这是因为memfd_create和glibc中的同名函数冲突了,所以需要给安装包打patche
cd ./qemu_mode/patches
vim memfd_create.diff
//添加以下patche 并保存
diff -ru qemu-2.10.0-clean/util/memfd.c qemu-2.10.0/util/memfd.c
--- qemu-2.10.0-clean/util/memfd.c 2018-11-20 18:11:00.170271506 +0100
+++ qemu-2.10.0/util/memfd.c 2018-11-20 18:11:13.398423613 +0100
@@ -37,7 +37,7 @@
#include <sys/syscall.h>
#include <asm/unistd.h>
-static int memfd_create(const char *name, unsigned int flags)
+int memfd_create(const char *name, unsigned int flags)
{
#ifdef __NR_memfd_create
return syscall(__NR_memfd_create, name, flags);
//修改 build_qemu_support.sh安装添加的patch
patch -p1 <../patches/elfload.diff || exit 1
patch -p1 <../patches/cpu-exec.diff || exit 1
patch -p1 <../patches/syscall.diff || exit 1
patch -p1 <../patches/memfd_create.diff || exit 1
报错3:
解决方案:
加上一个syscall1.diff文件,将其放在patches文件夹下,然后再改build_qemu_support.sh的patch就可以了
--- qemu-2.10.0-clean/linux-user/syscall.c 2020-03-12 18:47:47.898592169 +0100
+++ qemu-2.10.0/linux-user/syscall.c 2020-03-12 19:16:41.563074307 +0100
@@ -34,6 +34,7 @@
#include <sys/resource.h>
#include <sys/swap.h>
#include <linux/capability.h>
+#include <linux/sockios.h> // https://lkml.org/lkml/2019/6/3/988
#include <sched.h>
#include <sys/timex.h>
#ifdef __ia64__
@@ -116,6 +117,8 @@ int __clone2(int (*fn)(void *), void *ch
#include "qemu.h"
+extern unsigned int afl_forksrv_pid;
+
#ifndef CLONE_IO
#define CLONE_IO 0x80000000 /* Clone io context */
#endif
@@ -256,7 +259,9 @@ static type name (type1 arg1,type2 arg2,
#endif
#ifdef __NR_gettid
-_syscall0(int, gettid)
+// taken from https://patchwork.kernel.org/patch/10862231/
+#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. */
@@ -6219,7 +6224,8 @@ static void *clone_func(void *arg)
cpu = ENV_GET_CPU(env);
thread_cpu = cpu;
ts = (TaskState *)cpu->opaque;
- info->tid = gettid();
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ info->tid = sys_gettid();
task_settid(ts);
if (info->child_tidptr)
put_user_u32(info->tid, info->child_tidptr);
@@ -6363,9 +6369,11 @@ static int do_fork(CPUArchState *env, un
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);
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ put_user_u32(sys_gettid(), child_tidptr);
if (flags & CLONE_PARENT_SETTID)
- put_user_u32(gettid(), parent_tidptr);
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ put_user_u32(sys_gettid(), parent_tidptr);
ts = (TaskState *)cpu->opaque;
if (flags & CLONE_SETTLS)
cpu_set_tls (env, newtls);
@@ -11402,7 +11410,8 @@ abi_long do_syscall(void *cpu_env, int n
break;
#endif
case TARGET_NR_gettid:
- ret = get_errno(gettid());
+ // taken from https://patchwork.kernel.org/patch/10862231/
+ ret = get_errno(sys_gettid());
break;
#ifdef TARGET_NR_readahead
case TARGET_NR_readahead:
完成安装后:
使用Qemu模式来fuzz缺少源代码的二进制程序,使用-Q参数
如果提示出现Failed to locate afl-qemu-trace的问题
在qemu-2.10.0目录下执行
sudo make install
执行速度比起源码fuzz有显著下降
关于ARM这部分,看到Qemu的Readme里描述说在调用之前设置CPU_TARGET/build_qemu_support.sh,可以得到一个能够运行非本机二进制文件的构建(例如可以尝试CPU_TARGET=arm)
关于ARM架构的二进制FUZZ后面有待尝试,UU们有什么推荐的好工具嘛?
参考:
https://xz.aliyun.com/t/4314
https://blog.csdn.net/A951860555/article/details/119666269
https://blog.csdn.net/qq_42931917/article/details/112801073
https://blog.csdn.net/qysh123/article/details/114792891