前言
对于FUZZ的大名早有耳闻 今天终于开始正式学习这一知识
水平有限 本篇文章可能部分地方存在描述错误等问题
本篇文章使用AFL模糊器
什么是FUZZ
为了弄清楚FUZZ的概念 拜读了《Fuzzing: Art, Science, and Engineering》这篇经典的论文 如果有想看我阅读后总结的 可以去看另外一篇博客 这里就简单概述
FUZZ相比传统的软件测试 其包含的漏洞预测器用于决定测试过程中是否违反了安全策略
二者的目的性不一样 而FUZZ又分为三种 白盒 黑盒 灰盒 三者最大的差别在于模糊器对于PUT(待测程序)的了解程度有多少 是否知晓PUT的内部逻辑等
FUZZ采用模糊算法来生成随机的测试样例 部分复杂的模糊配置可以演变种子池来迭代测试样例 通常依靠代码覆盖率(即PUT执行路径)
配置FUZZ环境
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz |
上述是afl的安装 我个人建议是使用afl++ 不过本篇文章均采用afl
同时上述方式安装的afl版本是2.52 实际上最新的版本为2.57 需要去github上下载源代码后make编译 这里看各位的需求
第一次FUZZ
我们先来自己编写一个程序 逻辑很简单 输入对应的字符串就触发段错误
#include <stdio.h> |
随便创建两个空目录 一个用来存放测试样例 一个用来存放输出信息
本次模糊测试 测试样例由我们自己填写
随后使用-i指定前者 -o指定后者 开始fuzz
afl-fuzz -i ./fuzz/in -o ./fuzz/out ./test |
当然了 如果你使用的是afl 那么你就会发现 模糊器读取到第二个样例的时候就终止了
原因在于afl的测试样例貌似不能直接导致PUT触发crash
所以这里更改第二个样例 使其为aaa 让模糊器使其迭代 延伸成aaaa这个字符串
随后重新开始fuzz 发现成功找到了三个crash
看到官方文档说 如果要让模糊器运行完毕 需要几个小时到一周左右 所以这里直接ctrl+c终止了
官方文档提到
crashes/ - unique test cases that cause the tested program to receive a fatal signal (e.g., SIGSEGV, SIGILL, SIGABRT). The entries are grouped by the received signal. |
所以去crashes目录下找到了三个触发崩溃的样例
第一个不出所料 是我们原定的字符串aaaa 第二个和第三个有点意外 貌似也是一些无规则字节 打算动调来看看 利用hexdump获取一下16进制格式的ascii字节码
\x68\x68\x68\x68\x32\x00\x00\x68\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xCE\xC6\xC6\xC6\xC6\xC6\xC6\xC6\xC6\x68\x87\x68\xE8\x00\x10\x68\x7F\xFF\xF0\xF0\xF0\xF0\xF0\xCF\xF0\xF0\xF0\xE4\xF0\xF0\xF0\x70\xF0\x68\x68\x68\x68\x32\x68\x68\x68\x68\x68\x68\xE8\x68\x68\x68\x68\xF0\xF0\xF0\xF0\xF0\xF0\xCF\xF0\xF0\xF0\xF0\x68\x68\xF0\xE9\xF0\xF0\xFB\xF0\xCF\xF0\xF0\xF0\xF0\xF0\xF0\x07\x70\xF0\x68\x68\x68\x68\x68\x68\x68\x6E\x00\x00\x01\x00\x70\xF0\x68\x68\x68\x68\x68\x68\x68\x68\x68\x68\xDF\xD1 |
调试exp:
from pwn import* |
在程序执行到pthread_kill函数后 回溯一下执行流 发现了检查canary的函数
应该是由于栈溢出触发的crash
那么接下来来看第三个
看这个长度应该也是因为栈溢出导致的crash 那么这里就不进一步动调了
黑盒测试以及读取文件内容
本小节用来记录自己对于afl官方文档阅读后的理解和实操 没啥重要性
文档中粗略介绍了afl所采用的模糊算法
总结一下 可以得到下图
大体是和论文中描述的模糊器的五个功能大差不差
随后想要研究一下afl的黑盒测试功能 然后在配置环境的时候 不得不说是真的遇到一堆报错
首先进入下载的afl源码目录中的qemu-mode目录 运行build_qemu_support.sh
cd qemu-mode |
这里我首先遇到的是下载qemu的网址报错404 但是也不是虚拟机代理问题 我宿主机也访问不到
进入build文件中 找到对应代码的位置
将QEMU_URL更改为
https://download.qemu.org/qemu-${VERSION}.tar.xz |
随后就可以正常下载了 接下来遇到的问题是其默认使用的是python 需求是python2
所以还需要加一个软连接 使python可以指向python2
sudo ln -s /usr/bin/python2.7 /usr/bin/python |
随后虽然可以正常执行一段时间 最后还是遇到了一个报错
/home/chen/AFL/qemu_mode/qemu-2.10.0/linux-user/syscall.c:261:16: error: static declaration of ‘gettid’ follows non-static declaration
找到qemu-mode中的patches目录 更改syscall.diff文件内容为
--- qemu-2.10.0-clean/linux-user/syscall.c 2020-03-12 18:47:47.898592169 +0100 |
随后成功完成环境配置
更多报错可以参考该文章: https://blog.csdn.net/qysh123/article/details/114792891?utm_term=aflqemu%E6%A8%A1%E5%BC%8F&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduweb~default-0-114792891&spm=3001.4430
但是随后我们尝试使用-Q选项开始模糊测试 但是发现其找不到afl-qemu-trace
只需要添加环境变量AFL_PATH为afl目录的路径即可
这里还是使用上述的PUT进行测试 不过是用gcc将其编译
成功获取到两个crash
不出所料 一个应该是canary导致的栈溢出 一个是因为我们既定的字符串触发的crash
接下来试着研究了下PUT输入样例的两个方式
一种是直接从stdin输入 像本篇文章一直使用的PUT那样
还有一种是从文件中输入 那么接下来就重写一个PUT来实现第二种
不过我们先来搞清楚第一种
官方文档中 记录的指令是这样
./afl-fuzz -i testcase_dir -o findings_dir /path/to/program [...params...] |
[params]比较让我在意 这是否意味着不需要in目录? 可以直接从命令行输入测试样例
删除了in目录中的所有测试样例后 我想通过命令行向其传输aaa这个测试样例
结果还是失败了 那么为了验证 命令行输入的测试样例究竟是否起到了作用 接下来做一个小测试
第一组我们的测试样例中不包含aaa 同时不通过命令行输入测试样例
第二组我们的测试样例保持不变 通过命令行输入测试样例aaa
对比两组的测试结果 如果第二组的crash除了栈溢出之外 还变异出了aaa 而第一组没有 那么就可以证明测试样例成功输入进去
测试样例两个 内容分别如上图
第一组跑出的crash中不含有aaaa
第二组含有 那么可以证实测试样例确实是传入了
接下来研究一下从文件中写入的
#include <stdio.h> |
编译命令 这里考虑到了canary带来的crash太讨厌了 所以直接关掉了
afl-gcc -o test1 -no-pie -fno-stack-protector -g ./test1.c |
本次提供给模糊器的测试样例就一个aaa
然后由于是从文件读取输入 并且你可以看到上面的代码 我们是指定了所需的文件名 同时目录应该是位于当前PUT下
所以我们除了使用@@标识当前使用文件输入外 还需要使用-f指定对应路径下的文件名
不过我试了下 把@@删了也是可以的 可能是-f就自动默认了?
afl-fuzz -i ./fuzz/in -o ./fuzz/out -f ./flag ./test1 @@ |
fuzz实际利用
接下来准备尝试 使用fuzz来对一些开源的知名项目进行测试 以此来熟悉fuzz实际利用的操作
这里使用upx upx是一款可执行文件压缩工具 https://github.com/upx/upx#
upx使用的压缩算法涉及到了ucl 所以还要先安装ucl
mkdir fuzz-upx |
在开始对upx模糊测试之前 我们不能像之前一样随便给几个样本 样本收集对于fuzz来说至关重要