AFL(American Fuzzy Lop)是一款用于测试程序安全性的模糊测试工具,其主要针对命令输入类型的程序进行FUZZ,例如文档类型的FUZZ(通过构造畸形文档作为文档解析程序的输入)。而对网络协议类型的程序并不能很好的支持,例如http server程序。所以本文将以Apache Server为例来讲解如何使用AFL来FUZZ该类程序。
01 环境准备
系统环境:Ubuntu 14 linux 64位(其它linux都行debian/kali)
编译器环境:Clang+LLVM (kali自带,也可以自行下载最新版,下载路径http://releases.llvm.org/download.html)
实验目录(不分目录也行,只是为了实验过程方便说明):
Victims :fuzz的目标程序存放目录
Fuzzers :fuzz程序安装包目录
Sessions: 存放fuzz中间过程文件
Testcases: 存放测试样本
Compiler: 编译器存放目录
首先配置好编译器环境,这里我直接下载的Clang+LLVM最新版放在compiler下,并配置好环境变量,修改/etc/profile如下:
然后再把clang建立两个软链接(由于afl的afl-clang.c和afl-clang-fast.c编译需要/bin/clang或/usr/bin/clang两路径):
然后安装AFL,很简单直接去
http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz
下载,然后解压安装即可:
最后下载apache源码包,编译并安装。之所以需要源码包安装,是因为后续需要修改源码编译才能让AFL正常FUZZ apache http server。编译之前需要准备好APR(Apache Portable Runtime)、APR Utils、nghttp2(用于支持http2)、Pcre,相关版本如下:
编译过程比较繁琐(中途可能需要手动安装libtool和g++),这里提供一个自动化编译shell
编译过程有问题可自行修改,然后执行以下命令来编译apache:
若能成功编译完成,即实验环境准备就绪。
02 Apache修改编译
由于AFL 不支持针对网络服务程序的Fuzz,即无法实现与apache建立连接、发送畸形报文,所以接下来我们需要修改下apache,让其能够接收来自文本文件的输入。这里我们提供一个patch,它针对server/main.c文件进行修改,修改完成后编译即可实现上述功能,并能被AFL Fuzz。
该Patch主要在apache内起了一个线程,该线程跟web server自身建立连接,并能接收AFL Fuzz的输入并发送给web server进程。
修改关键代码如下:
通过-F 参数接受FUZZ文件的输入:
新启的线程会调用GETDATA函数,会对文件内容读取,然后对服务进程建立连接发送数据:
Path的使用方法(下载至源码包下,然后执行如下命令):
03 开始Fuzzing
打好补丁后,我们就可以开始编译修改后的apache,然后开始Fuzz。编译过程依然可以使用上述的自动化脚本:
然后创建两个简单的测试模板,当然这里仅仅是实验验证可以用AFL来FUZZ apache,若要想通过FUZZ来发现安全问题,还需根据分析的实际情况来构造更多模板:
然后可以通过如下命令来进行Fuzz了,其中-i 是模板路径,-o 是fuzz中间过程路径, -m 是子进程内存大小限制,–t 是每次启动程序允许的超时时间。
然后AFL会载入测试模板开始测试:
但是这里我们发现exec speed的速度太慢了,只有6.35/sec,正常的速度至少在1000/sec以上,所以我们需要开启persistent模式,即AFL的高性能模式。
04 开启高性能模式(persistent模式)
先进入llvm_mode目录,make之后再返回上级目录,再make install安装:
目的是为了安装afl-clang-fast这个编译器。
然后我们同样也需要修改下原始main.c,Patch的链接如下:
打补丁的目的是开启persistent模式之后,针对apache这种server Fuzz需要做出修改才能提高效率,因为如果每fuzz的一个测试用例就新开一个进程会大大降低效率。
官方的做法是使用包含一个while循环和一个特殊的AFL函数__AFL_LOOP(),该函数的作用是使得AFL能在一个进程中进行多个testcase的fuzzing。修改如下:
AFL在一个进程中fuzz 10000个test cases,然后再新开一个进程执行相同的工作。
和上节打补丁的方法一样。打完补丁之后,再用afl-clang-fast和afl-clang-fast++编译一下:
CC="afl-clang-fast" CXX="afl-clang-fast++" PREFIX="/usr/local/apache_afl_per/" ./compile_httpd_with_flags.sh
完成之后make install apache就可以开始FUZZ了,执行如下命令可以看到效果:
afl-fuzz -i testcases/ -o sess/ -m none -t 2000 -- /usr/local/apache_afl_per/bin/httpd –X
至此,AFL FUZZ apache的环境已准备完毕,接下来我们用afl来测试fuzz出cve-2017-7659漏洞,做一个简短的验证。
把最终POC稍微做了下修改保存成testcase,让其不会引起httpd崩溃,看看AFL能否通过变形测试并输出最终POC:
很快,几秒钟AFL便FUZZ出一个crash,通过查看crash文件发现正是我们原来的POC文件:
把fuzz出的结果发送给httpd,果然崩溃:
通过GDB加载core dump结果调试可以发现崩溃的位置,如下正是该漏洞的触发点: