[afl-training] quickstart
afl-trainning 是之前在安全客上发现的关于 AFL 实战的学习资料,之前一直想通过 AFL 来进行实战,苦于网上的都是一些基础的使用教程,发现这个资料的时候欣喜万分。
在这个 AFL workshop 中,主要包含了以下的一些内容:
- quickstart:一个简单的例子,通过 afl 编译程序然后使用 afl-fuzz 来 fuzz,新手入门必看,也就是这一篇文章
- harness:
- challenges:几个使用 fuzz 可以挖掘出来的经典漏洞
- libxml2:CVE-2015-8317
- heartbleed:openssl 的心脏滴血漏洞 CVE-2014-0160
- sendmail:CVE-1999-0206, CVE-2003-0161
- ntpq:CVE-2009-0159
- date:CVE-2017-7476
- cyber-grand-challenge
- sendmail/1305
教程是可以使用 docker 的形式创建学习环境的,我这个地方就没有使用了,直接在 git 目录中进行学习。
编译
trainning 中使用的是 AFLplusplus,我此处使用的就是 AFL,因为之前在看 AFL 的源码。
首先进入 quickstart 目录,然后使用 afl-clang 对程序源码进行编译
1 | cd quickstart |
编译出来的程序是读取 STDIN 标准输入进行处理,可直接运行程序,如果敲下回车不输入数据会显示程序帮助信息,也可以直接从 inputs 提供的种子文件进行运行。
Fuzzing
使用如下的命令直接进行 fuzz,下图是 fuzz 出来的结果,运行了 44 分钟之后跑出了 9 个 crash
1 | afl-fuzz -i inputs -o out ./vulnerable |
问题总结
AFL 是如何使用 afl-clang 进行插桩的?
首先需要知道正常使用 gcc 进行编译和使用 alf-clang 进行编译,产生的可执行文件在二进制上的差别。安全相关,因此先使用 checksec 查看 afl-clang 编译出来的可执行文件开启了哪些防御措施,然后使用 gcc 开启对应的防御参数重新进行编译
那么使用 gcc 进行编译的命令如下,FORTIFY 选项没有编译出来
1 | gcc -no-pie -fstack-protector-all -z noexecstack -O2 -D_FORTIFY_SOURCE=1 -o vulnerable_gcc vulnerable.c |
然后使用 bindiff 工具进行查看,可以看到 afl-clang 编译出来的可执行文件中,在每一个基本块中,都加入了 afl_maybe_log 函数,通过在 AFL 源码目录中搜索该函数,可以定位到是在 afl-as.h 文件中,在源码中是以一串静态字符串形式存储的汇编代码,此处以 64 位为例,源码部分如下。
1 | static const u8* trampoline_fmt_64 = |
将源代码编译成二进制文件的基本流程是:源代码 -> 汇编代码 -> 二进制代码,将汇编代码编译成二进制的工具就是汇编器 assembler。Linux 常用的汇编器是 as,当完成了 AFL 的编译后,在目录下也会存在一个 as 文件,并且作为符号链接指向 afl-as。因此,此处的代码插桩实现应该是使用的 afl-as,在将源代码编译成汇编代码的过程中,将如上的 afl_maybe_log 函数插入到分支处,也就是在基本块中进行插桩。如上的插桩代码就是 x64 下正常调用一个函数的流程:开辟栈空间,调用 afl_maybe_log 函数,执行完毕函数之后恢复栈平衡。afl_maybe_log 函数也就是插桩具体要执行的内容。此处不多分析 afl_maybe_log 函数的源码,函数位于 afl-as.c 中,就简单说一下函数的实现功能:通过共享内存对基本块的执行情况进行保存。
对于插桩分析得比较不错的可以参考看雪的这篇文章:[原创]AFL编译插桩部分源码分析