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.
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.
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=??
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.
[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
+33 (0) 970 463 030
contact@hdwsec.fr
Notre clef PGP
178 Boulevard Haussmann
75008 Paris , FRANCE