[OSG]以福昕阅读器为例实现高性能Fuzz Applied high-speed in-process fuzzing: the case of Foxit Reader

首发看雪论坛:【OSG】以福昕阅读器为例实现高性能Fuzz Applied high-speed in-process fuzzing: the case of Foxit Reader

原文作者:Vitaly Nikolenko译者:Arming 校对:布兜儿

原文链接:http://www.hdwsec.fr/en/blog/inprocessfuzzing.html

Introduction(前言)

Fuzzing现在已经很普遍了,尤其是在AFL发布以来。众所周知,性能是非常关键的,因此为了在给定时间段内尽可能多的发现bug,我们希望优化我们的Fuzzing方法。本文以Foxit Reader的一个图片转为PDF的插件为例,描述了一种通用方法。更具体地说,我们用的Foxit版本是7.3.6.321,有漏洞的插件(ConvertToPDF_x86)版本是7.3.4.308. 这个版本的插件有几个有意思的crash。这些crash是由另一位研究人员独立发现的,并且已经提交给Foxit团队(已修复并发布新版本),因此我们决定公开它们。

Fuzzing Method

当通过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.dl

在我的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~300 份测试每秒的效果。重要的是,请注意,该Fuzzing必须在Foxit的进程中执行,手动调用该DLL插件将不会生效。

Fuzzing结果

即使是简单的位翻转,也会有很多crash快速产生,其中一些可以被利用。在进行了3天的Fuzzing后,发现了下面的这些crash。

eax=11ba0020 ebx=25000007 ecx=ec54a4e6 edx=11ba0020 esi=11544060 edi=25000007
eip=5cc76ff3 esp=003c8b10 ebp=003c8b18 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
ConvertToPDF_x86!CreateFXPDFConvertor+0x2ba793:
5cc76ff3 8a19            mov     bl,byte ptr [ecx]         ds:002b:ec54a4e6=??
 eax=1e7df002 ebx=1e7df000 ecx=1e7df002 edx=1e7df000 esi=1c7bdfc0 edi=1e7e0ff8
eip=6735d9a6 esp=00399750 ebp=00399750 iopl=0         nv up ei ng nz na pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010287
ConvertToPDF_x86!CreateFXPDFConvertor+0x192eb6:
6735d9a6 0fb601          movzx   eax,byte ptr [ecx]         ds:002b:1e7df002=??
 eax=7b2e0ee2 ebx=1c59cfc8 ecx=00000001 edx=52cf0002 esi=00000000 edi=1f20fff8
eip=67e399e8 esp=035f9854 ebp=035f985c iopl=0         nv up ei ng nz ac pe cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010297
ConvertToPDF_x86!CreateFXPDFConvertor+0x2deef8:
67e399e8 8808            mov     byte ptr [eax],cl         ds:002b:7b2e0ee2=??
eax=00000000 ebx=44436fb0 ecx=61f3dbf7 edx=431e1078 esi=00000000 edi=44438fb8
eip=620ba719 esp=00309efc ebp=00309f20 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
ConvertToPDF_x86!CreateFXPDFConvertor+0x2dfc29:
620ba719 837e0800        cmp     dword ptr [esi+8],0  ds:002b:00000008=????????
eax=00000000 ebx=56bc8fb8 ecx=67fae147 edx=42551078 esi=00000000 edi=00000000
eip=6812b468 esp=00219788 ebp=002197b8 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210246
ConvertToPDF_x86!CreateFXPDFConvertor+0x2e0978:
6812b468 397e08          cmp     dword ptr [esi+8],edi ds:002b:00000008=????????
eax=00000000 ebx=000002fa ecx=00000060 edx=44b57f7f esi=003f9d5c edi=44b57fbf
eip=69885b61 esp=003f9d08 ebp=003f9d1c iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210206
ConvertToPDF_x86!CreateFXPDFConvertor+0x1bb071:
69885b61 8a5c0302        mov     bl,byte ptr [ebx+eax+2]    ds:002b:000002fc=??

你可以在这里下载(http://www.hdwsec.fr/en/blog/foxit_crashes.zip)

Conclusion(结论)

这种方法可以用于任意软件,只要在调用目标函数(或一组函数)前能够轻松地再现软件的状态。在我看来这也是需要克服的最大障碍。

ConvertToPDF_x86的例子是非常简单的,但是在许多其他情况下,需要注意内存分配、释放和全局状态的改变(或初始化程序)。针对这个目的Pintool就变得非常有用,而且很可能是一个有趣的进一步研究的方向。

请注意,我们可以通过hook插件的读/写访问来优化该Fuzzing方法,从而避免执行过程中在磁盘上写入任何文件。

References:

[1] American Fuzzy Lop, american fuzzy lop

[2] Foxit Reader, Free PDF Reader | PDF Viewer Download | Foxit Reader

[3] Pintool, Pin - A Dynamic Binary Instrumentation Tool