Applied high-speed in-process fuzzing: the case of Foxit Reader

Introduction

Fuzzing has now become commonplace, especially since the release of AFL [1]. As performance is key, we’d like to optimize our fuzzing methods to maximize the number of bugs found during a given period of time. This article describes a general method applied to a plugin of Foxit Reader [2] which is responsible for converting an image to PDF.

More specifically, we are focusing on version 7.3.6.321 of Foxit and version 7.3.4.308 of the vulnerable plugin (ConvertToPDF_x86). Several interesting crashes were found for this version of the plugin. As they were independently discovered by another researcher who disclosed them to the Foxit team (leading to a new, fixed version) we have decided to release them now.

Fuzzing method

When an image is opened with Foxit Reader, the plugin ConvertToPDF_x86 is loaded in order to convert the image to PDF and display it. This plugin takes care of the most common image file formats (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

In my fuzzing tests, I decided to concentrate on the JPEG file format. I then identified the function of Foxit (core) that calls the plugin:

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

By examining the arguments pushed on the stack, we can see that the first one points to the image file path, and the second to the temporary PDF file to be written. Note that due to ASLR the addresses shown below may differ from the list of modules above.

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

By dynamically altering the arguments when Foxit calls the function, we were able to open any other arbitrary image. Then by restoring the context (notably x86 registers) between calls, it becomes possible to quickly call this conversion function repeatedly. Fortunately, the heap does not need to be restored each time.

This approach can be automated in a number of ways. We could have used Pintool [3], but instead I decided to write a specific tool for optimization purposes. To this end I wrote a mini debugger that starts Foxit Reader by passing an image as parameter and breaks on the relevant call to the image conversion function in ConvertToPDF_x86.dll in order to get the context to be replayed in the next step.

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);
                ...
            }
    }
}

The debugger then injects a DLL (this is done by RunFuzzing()) in Foxit’s memory and sets the appropriate values to be passed to the called function.

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
    }
}

On an Intel Xeon E3-1230v3 (3.3 GHz) we managed to run between 150 and 300 tests/sec depending on the fuzzing method used. It is important to note that the fuzzing has to take place within the Foxit process: manually calling the DLL plugin will not work directly.

Fuzzing results

Even with simple bit flipping, a number of crashes were quickly produced, some of them potentially exploitable. A total of three days of fuzzing led to the discovery of the crashes below.

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=??

You can download some of them here

Conclusion

This method could potentially be applied to any piece of software on the condition that one is able to easily recreate the state of the software right before the targeted function (or group of functions) is to be called. This is in my opinion the biggest hurdle to overcome. The case of ConvertToPDF_x86 is straightforward, but in a number of other cases, memory allocations, deallocations and global state changes (or initialization routines) must be taken care of. Towards this aim Pintool [3] might become very useful and may be an interesting direction for further research.

Note that we could optimize this fuzzing method by hooking the plugin’s read/write access, thus avoiding writing any files on disk during the job.

References

[1] American Fuzzy Lop, https://lcamtuf.coredump.cx/afl/

[2] Foxit Reader, https://www.foxitsoftware.com/products/pdf-reader/

[3] Pintool, https://software.intel.com/en-us/articles/pin-a-dynamic-binary-instrumentation-tool

Call us

+33 (0) 970 463 030

Email us

contact@hdwsec.fr
Our PGP key

Our address

178 Boulevard Haussmann
75008 Paris , FRANCE