原文首发看雪论坛
1前 言
Fuzzing
现在已经很普遍了,特别是在 AFL 发布之后。因为性能是关键,为了在给定时间内尽可能多的发现 bug,我们需要优化我们的 Fuzzing
方法。本文以 Foxit Reader 的一个图片转pdf的插件为例来描述一通用方法。确切地说,我们用的 Foxit 版本是
7.3.6.321,有漏洞的插件(ConvertToPDF_x86)版本是 7.3.4.308. 该版本的插件有几个有意思的
crash,是由另一位研究人员独立发现的,并且已经提交给 Foxit 团队(已修复并发布新版本),因为我们决定公开这些问题。
2FuzzingMethod
当通过Foxit Reader打开一个图片时,插件 ConvertToPDF_x86 就会被加载来转换图片为pdf并显示出来。该插件可以转换大部分常用的图片格式(JPEG, GIF,PNG...)。
ModLoad: 6cf90000 6cf96000 C:\Windows\SysWOW64\IconCodecService.dll
ModLoad: 50230000 50f61000 C:\Windows\SysWOW64\ieframe.dll
ModLoad: 53820000 53d45000 C:\Program Files (x86)\Foxit Software\Foxit Reader\Plugins\Creator\x86\ConvertToPDF_x86.dll
在我 Fuzzing 测试中,我专注于JPEG 文件格式,然后定位到 Foxit 调用插件的函数:
003b96f0 533c932e kernel32!CreateFileW
003b9720 533cb136 ConvertToPDF_x86!DestorFXPDFConvertor+0x4fe
003b975c 02598c3f ConvertToPDF_x86!CreateFXPDFConvertor+0x646
003bb644 0128e6fd FoxitReader_Lib_Full!CryptUIWizExport+0x8856ff
003beac8 0128637a FoxitReader_Lib_Full+0x18e6fd
003beb18 0128691c FoxitReader_Lib_Full+0x18637a
003beed0 013aefa8 FoxitReader_Lib_Full+0x18691c
通过检查栈上的参数,我们可以看出第一个是图片文件的路径,第二个是要写入的临时 PDF 文件。注意由于 ASLR,下面显示的地址可以与上面列出的模块不同。
0032e6f0 6a01 push 1
0032e6f2 6a00 push 0
0032e6f4 51 push ecx ; path to temp PDF file
0032e6f5 8bc8 mov ecx,eax
0032e6f7 8b4604 mov eax,dword ptr [esi+4]
0032e6fa 52 push edx ; path to JPEG image
0032e6fb ffd0 call eax
当 Foxit 调用该方法时通过动态地修改参数,我们可以打开其他任意地图片。因此在调用期间通过恢复上下文(尤其是x86寄存器),快速重复地调用该转换函数也就成为可能。幸运地是,每次调用不需要恢复堆。
该方法有几种实现自动化的方式。我们可以使用 Pintool,但是为了优化我决定写一个特定的工具。
最后,我写了一个小调试器,可以通过传一个图片参数来启动 Foxit Reader,并且断点在 ConvertToPDF_x86.dll 的图片转换函数的对应调用处,可以获取下一步进行回放的上下文。
CONTEXT ctx;
...
case EXCEPTION_DEBUG_EVENT:
{
EXCEPTION_DEBUG_INFO& exception = debug_event.u.Exception;
switch (exception.ExceptionRecord.ExceptionCode)
{
case STATUS_BREAKPOINT:
...
if (IsMyBp())
{
SaveContext(&ctx);
...
RunFuzzing(&ctx);
...
}
}
}
然后调试器注入一 DLL 到 Foxit 内存中(通过 RunFuzzing() 实现),并设置好要传给被调用函数的参数。
while(TRUE)
{
corrupt(image);
// call the fonction
_asm
{
push 1
push 0
lea edi, pdf_path
push edi;
lea edi, image_path
push edi;
mov edi, val_edi
mov esi, val_esi
mov ecx, val_ecx
mov ebx, val_ebx
mov eax, val_eax
mov edx, val_edx
call eax
}
}
在
Intel Xeon E3-1230v3 (3.3 GHz)机器上,基于该 Fuzzing 方法,我们达到了150~300tests/sec
的效果。很重要的一点是该 Fuzzing 必须在 Foxit 的进程中执行,手动调用该 DLL 插件不会直接生效。
3Fuzzing 结果
即使是简单的位翻转,就会产生很多的 crash,其中一些可利用。在进行了3天的Fuzzing 后,对这些 crash 有了如下发现。
请点击此处输入图片描述
4结 论
该方法可用于任意软件,只要在目标函数(或一组函数)被调用前能够轻松地再现软件的状态。在我看来这也是要克服的最大障碍。
ConvertToPDF_x86
的例子还是简单的,但是在其他大量的例子中,需要注意内存分配、回收和全局变量修改(或初始化程序)。针对这个目的 Pintool
就很有用了,并且可能是一个有趣的可进一步研究的方向。当然,我们可以通过 hook 插件的读/写访问来优化该 Fuzzing
方法,从而避免执行过程中写文件到硬盘。
本文由 看雪 iOS 安全小组 Arming 编译,小布校对,来源 hdwsec@Vitaly Nikolenko
请点击此处输入图片描述
往期热门内容推荐
等你来挑战!| 看雪 CTF 2017 攻击篇
【终于等到你!】看雪 CTF 2017
春风十里,我在等你
Windows 平台下的最优化 Shellcode 代码编写指引
使用最新的代码重用攻击绕过执行流保护(一)
新手逆向学习--Win7 下 64 位扫雷逆向以及辅助制作
......