转:http://www.tuicool.com/articles/j2eqym6
小结:找到感兴趣的函数,计算偏移,自动化fuzz.
这是一篇客座文章,作者是乌云二进制漏洞挖掘白帽子 k0shl 。其实上个月17号,本文就已经写完了,但是我们一直“捂”到了今天。算是给二进制方向的白帽子的七夕礼物吧 : )
0x01 什么是 winafl
其实说的 afl-fuzz 大家都不会很陌生, afl-fuzz 是 Linux 用于 fuzz 文件格式,协议等二进制漏洞的神器,而 winafl 则是 afl-fuzz 的 Windows 版,最近我对 winafl 进行了一些浅层研究,由于之前也进行过一段时间的二进制漏洞挖掘,但基本上都是停留在手工挖掘上,越发觉得自动化神器功能的强大,也为以后自己开发 fuzz 工具提供了很重要的指导依据。
Winafl 到底是什么?
Winafl 是 windows 下一种挖掘文件格式,协议漏洞的半自动化工具,为什么说半自动化呢,因为针对特定的软件, Winafl 并没有提供一个全面的样本库,虽然 winafl 确实提供了一些测试样本,但实际上真正 fuzz 的过程中,很多文件需要自己手工构造。
其次比起自动 fuzz , Winafl 需要自己手动定位需要 fuzz 的函数地址偏移,来确定具体要进行 fuzz 的函数位置。
那么相比较来说 winafl 到底优势在哪呢?这里我要提一下我的理解, winafl 的优势在于它采用的是代码扩展来确定输入输出,以此来判断漏洞是否存在,这么说可能大家比较晕乎。
这个原理有点像 PIN 插件, PIN 插件是微软提供的一种类似用于汇编指令扩展的插件,我用一张图来简单描述一下这个过程。
如何理解这个过程,可以想象钩子,插桩等过程,在函数进入和函数返回时,检查程序输入输出是否符合预期等等,通过插入一些“额外”的指令来进行检查,这样,对崩溃位置定位更加精准,误报率极低等等。
刚开始如何学习 fuzz ?
有很多刚开始接触二进制,或者学过一段时间二进制的小伙伴会问我如何去挖掘,或者刚开始如何学习挖掘二进制漏洞的方法,其实我想说二进制漏洞挖掘是一个很难的过程,随着现在一些类似于 strcpy_s ,或者说软件安全性越来越好, fuzz 的难度越来越高,想要挖掘高级漏洞,需要一些入门的知识,我的水平不高,在这里跟大家分享一些我做 fuzz 的心得,也是为之后利用 winafl 进行 fuzz 做一些铺垫。
在入门的漏洞挖掘中,最重要的是关注程序可能存在的脆弱点,其实和 web 很像,在二进制中,用户输入也应该是不可信的,而这里所谓的脆弱点,就是存在于用户输入和程序交互的过程中,比较有趣的函数有: strcpy , memcpy , sprintf , read 等函数,指针传递,指针赋值等操作中。
下面我来举一个简单的例子,通过 IDA 分析一个软件,发现有一处比较有趣的调用。
这里我们关注到调用了一处 strcpy 的调用,我们可以通过 windbg 附加调试,在 j_strcpy 位置下断点,这样,程序执行中,如果调用了这处函数,就会命中这处断点(当然,这里用 OllyDBG也是可以的)。
在敏感函数位置下断点,通过对样本的附加执行等等方法,直到命中断点,再对函数执行前后的输入输出进行判断,来确定样本是否可以造成程序崩溃,是否可控,是否是一处可造成拒绝服务或者代码执行的漏洞。
通过后续执行情况,判断栈空间的覆盖情况,来确定这里是否是一处可利用的漏洞,可以看到,此时栈回溯已经被畸形字符串冲垮,这个畸形字符串是构造样本中,用户可控的字符串部分。
这里只是简单的讲述了一下最简单的二进制漏洞挖掘过程,其实仔细回想我描述的这个过程,对函数进入推出时输入输出的检查,也就是增加一个类似于指令扩展的过程,那么其实就是自动化 fuzz 一个简单的模型。
0x02 Winafl fuzz 前的准备
这里,我们使用一个名为 VUPlayer 的软件来利用 Winafl fuzz 进行一次简单的漏洞挖掘,看过网上 afl-fuzz 教程的小伙伴可能会发现其实这个挖掘过程耗时很长,指令扩展一定意义上加大了代码冗余,增加了执行时间,这里我提供一个可以触发 VUPlayer 缓冲区溢出漏洞的 PoC ,只是为了讲解 Winafl fuzz 的使用和简单原理。
寻找一个可能存在的脆弱点
之前我提到了 Winafl fuzz 使用时需要提供一个函数偏移,而在上面的简单漏洞挖掘中,我提到了对敏感函数的寻找,那么我们就来看一下 VUPlayer 的函数结构,利用 IDA 分析一下 VUPlayer的函数部分。
发现函数调用了一个系统 dll 的函数 lstrcpyA ,这样回溯这个 lstrcpyA ,发现了一处函数。
那么我们就选择这个函数进行 fuzz ,函数入口偏移就是 0x532a0 ,接下来要开始准备 fuzz了。
DynamoRIO 是什么?
在 fuzz 前,不得不提到 winafl fuzz 必须要用到的 DynamoRIO ,这个软件我也是第一次听说,可能很多二进制的老司机对它都不陌生,其实粗略看过 winafl 的源码之后,我发现其实 winafl 很多的实现上,都借用了 DynamoRIO ,在这两者之间建立了通信管道从而实现两者之间的调用。
而 DynamoRIO 应该算是 winafl 的核心部分,它主要实现的是指令动态插桩,其实就是之前我提到的指令扩展,对函数输入输出进行一定的检查。关于 DynamoRIO 的原理以及介绍在网上有很多描述,这里不做过多介绍了。
用 DynamoRIO 测试过程
这里我们要用到的是 DynamoRIO 的 ddrun.exe 工具,代码如下
<code>
path\to\DynamoRIO\bin64\drrun.exe-c winafl.dll -debug -target_module [target exe or dll]VUPlyaer.exe -target_offset0x532a0 -fuzz_iterations 10 -- [target exe]VUPlayer.exe </code>
这里需要进行一些简单的解释,首先是 -D ,用于和 afl fuzz 进行链接,主要是调用 winafl.dll ,target_module 是测试目标模块, target_offset 是偏移,这样的话会打开目标程序。
接下来附加一个样本,发现程序崩溃了,其实这时候,在目标目录下会生成一个 log 文件。
这个 log 文件实际上记录了测试 VUPlayer 过程中,加载的模块,以及记录了偏移函数位置的变化情况,可以对这个崩溃场景进行一个简单的分析。
0x03 Winafl fuzz 与核心源码浅析
使用 Winafl 进行 fuzz
了解了 Dynamoafl fuzz 的基本工作流程之后,我们可以使用 winafl 进行漏洞挖掘,实际上, winafl 需要提供多个样本才能对目标程序进行挖掘。
这里为了介绍 winafl ,我们仍然使用能对目标程序造成崩溃的样本文件。
<code>
C:\ProgramFiles\VUPlayer>afl-fuzz.exe -i in -o out -D C:\Users\Administrator\De
sktop\DynamoRIO-Windows-6.1.1-3\DynamoRIO-Windows-6.1.1-3\bin32-t 20000 -- -fuz
z_iterations 5000-target_module VUPlayer.exe -target_offset 0x532a0 -nargs 2 -- VUPlayer.exe @@
</code>
这里仍然需要对参数进行一些简单说明,首先 afl-fuzz 需要和 winafl.dll 同时处于目标文件夹下, -i 参数是用于记录输入样本, -o 参数用于保存输出数据, -D 则是 DynamoRIO 的路径, -t是样本测试延时, -target_offset 是要测试函数的偏移。
当接触到崩溃的时候。
结果分析
当 winafl 碰到崩溃场景的时候,会在 -o 设定的文件夹下生成一系列文件,会详细记录指令扩展中产生的各种信息。
Crashes 文件记录了崩溃样本, queue 记录了指令扩展中的各种信息, cur_input 是当前输入信息。
只需要产生 crash 之后对指令进行分析就可以很清晰的分析到这个函数输入输出发生了什么。或者说,获取了可以崩溃的样本之后,直接附加 windbg 复现这个漏洞,也能很快的分析出漏洞的成因。
源代码中的关键点
之前提到了 DynamoRIO 在 winafl fuzz 中的重要性,其实在源码中有很多部分都有体现,下面从源码的角度来看一些在 fuzz 中的关键点。
这个位置会定义 dynamorio 的路径,以便后续会调用到 dynamorio 中的工具。
构造指令插桩的关键语句,可以看到这里调用了 ddrun ,是 DynamoRIO 动态插桩的核心工具。
目标进程崩溃的进程信息。其实还有很多,比如进程重启机制,指令扩展后记录输入输出,关键函数地址等信息的部分等等。