EXCLUSIVE WEBINAR: Microsoft Outlook Chaos Unleashed — Live Technical Analysis of new Vulnerabilities
arrow-white arrow-white Secure your spot

[CRITICAL ALERT] CVE-2018-4990 Acrobat Reader DC Double-Free Vulnerability

Posted by Michael Gorelik on June 18, 2018
Find me on:

 Acrobat Reader vulnerability

After more than four years with no weaponized exploits for Adobe Acrobat Reader, researchers at ESET identified a weaponized PDF that allows attackers to execute arbitrary code on the targeted machine and eventually assume full system control. The PDF exploits two previously unknown vulnerabilities, Acrobat Reader vulnerability CVE-2018-4990 and a privilege escalation vulnerability in Microsoft Windows, CVE-2018-8120.

Adobe Reader has a built-in sandbox feature that usually makes exploitation difficult. By combining vulnerabilities, this attack achieves code execution and then bypasses the sandbox protection to fully compromise the targeted system.

Who is Affected?

As PDF (Portable Document Format) is one of the most popular file formats to share documents, and Acrobat Reader the most common PDF viewer, nearly any unpatched Windows system is vulnerable (Morphisec protected systems were never in danger).

Patches from Adobe and Microsoft can be found here:

Technical Details

A good technical description of the vulnerability trigger can be found on the 360 Threat Intelligence Center (we recommend using Google translate).

Our analysis describes the steps taken after the trigger of the vulnerability, focusing on the ROP (Return oriented programming) chain and shellcode demonstrated by the PoC that was uploaded to VirusTotal.

While the object and ROP offsets have been calculated for Acrobat Reader DC 2017.009.20044, we are including a link to a PoC that also covers the latest vulnerable version: Acrobat Reader DC 2018.011.20038, see https://github.com/smgorelik/Windows-RCE-exploits/tree/master/Documents/Acrobat.

Acrobat reader vulnerability Overview

Following the vulnerability trigger, the attacker gains read and write primitives within the Escript.api library (the ability to write and read from memory within the JavaScript interpreter).

The attacker then must find a way to redirect the flow of the application. Usually this is done by overriding a virtual table function of a specific object and then triggering the execution of the overridden function.

Upon successful flow redirection, the attacker’s next step is to execute malicious shellcode within the memory. This requires either good egg hunting (magic number that indicates the shellcode and is unique within the memory) or a successful heap spraying in order to know where the shellcode is located. It is not possible to directly execute the shellcode as heap sprayed strings are not executable. Trying to execute string within the memory will fail due to the DEP mechanism (Data Execution Prevention), which does not allow the execution of directly written data (either write or execute).

Due to DEP, the attacker needs to execute a ROP chain of executable gadgets (parts of the application that are already executable). The goal of this ROP chain is to eventually make the shellcode executable and then redirect the flow to the shellcode.

Following a successful redirection to the shellcode, the shellcode needs to locate the relevant resources from within the process memory in order to independently execute. This can be achieved in multiple ways, while the given shellcode uses the PEB (Process Environment Block) to obtain the basic functionality it needs to further map its next stage exploit dll.

Below we cover each step, starting from flow redirection and ending with shellcode execution.

Flow redirection

One of the first things the attacker needs is to find an object which he can manipulate to redirect the flow of the application. The best object is pointed from within the leaked library (In this case it’s Escript.api).

The attacker chooses one of the derived objects of “root”. The object is initialized during load of the main AcroRd32.dll library and is pointed directly from within the AcroRd32 library data section.

AcroRd32 library

Later this object is passed as a parameter to the “AcroView” initialization routines, both in AcroForm.api and Escript.api which have this object pointed from within their data section.


 cve 2018 4990

The pointer to the object can be automatically located by looking for the “AcroView” string and then looking for the referencing opcodes (simple jmp opcode arithmetic). The attackers choose to use exact offsets in the Escript library that works for an exact version of Acrobat (probably to limit the footprint of the exploit and hide the Acrobat version used during development).

The attackers are using the read primitive to read the address of the object in memory (objescript), and later override its execute function pointer from within the virtual table of the object (located at 0x598 offset). This way, as soon as the object.execute function is triggered, the first ROP gadget is executed instead:



The first gadget to execute is a simple stack pivoting gadget.

adobe object.execute


Since eax points to the object address, the stack pointer (esp) now gets the object address. As the first DWORD pointed by the object was changed to point to a new gadget (shown in the previous image), the next gadget executed after return is 0x64187e76-0x640c000+escript.api.

The second gadget as pointed by the object is yet again a second stack pivoting redirection:

second stack pivoting redirection

Stack pointer (esp) will now get the second DWORD pointed from the bookmarkRoot object, as shown in the last image in the Flow Redirection section. This DWORD was overridden to point to “myarraybase” (an array of ROP gadgets which is now the new stack).

second DWORD

The ROP was intentionally written starting from the 4 DWORD to support the identified stack pivoting gadget that will pop the first 3 element out.

4 DWORD to support

Below we describe the full gadgets stack step by step as pointed now by the stack pointer:

full acrobat gadgets stack

  • The first gadget (1) in this ROP is irrelevant since it was jumped above due to previous gadget “ret 4” (pops 4 bytes – DWORD immediately after return). You may garbage this gadget or reuse it in the case that a different stack pivoting is located; next gadget (2) is a simple ret that jumps to the next command in the stack.
  • Gadget (3) assigns VirtualAlloc Import offset (gadget 4) into ecx register. Gadget (5) will get the VirtualAlloc address pointed by the import into eax register.
  • Gadget (6) moves the VirtualAlloc address into the esi register. Gadget (7) moves the address of the “jmp esp” (gadget 8) into ebp register.
  • Gadget (9) assigns 0x10201 (gadget 10) into ebx register. Gadget (11) assigns 0x40 into ecx register. Gadget (13) assigns the “retn” address into edi register. Gadget(15) assigns 0x1000 into edx register. Gadget (17) assigns 0x90909090 (nop slide) into eax register.
  • The last gadget (19) pushes the assigned registers onto the stack and returns into “retn” gadget (14) that was pointed by the edi register and was pushed last by

edi register

The ROP will ret directly into the VirtualAlloc function (0x7621c43a) with all parameters already assigned.


As can be seen from the function prototype, the memory is allocated with memory protection 0x40 (PAGE_EXECUTE_READWRITE). The interesting point is that the lpAddress points to the continuation of the array with the shellcode written. The VirtualAlloc succeeds because the memory was double freed due to the vulnerability trigger. The result is that the shellcode becomes executable.

The VirtualAlloc returns into the nop slide - gadget (18), which will slide directly into the first shellcode command.


The first part of the shellcode locates the dll within the array by looking for a cookie that is implanted just before the MZ magic number that represents the dll header.


dll load

The next part of the shellcode reflectively loads the dll. The reflective loading requires basic functionality of mapping all the relevant import functions.

mapping functions

As can be seen from the image above, the shellcode gets the kernel32 module based on order of the PEB loaded module list. Next it locates the GetProcAddress function by regular traversal over the export, and then it uses the function to map the rest of the Basic functions:

LoadLibrayA, VirtualAlloc, VirtualProtect, VirtualFree, GetModuleHandle

Using these functions, the shellcode reflectively maps the dll into memory.

map dll into memory


This year we have seen a marked rise in use-after-free exploits, and this double free vulnerability is a sub-type of the UAF category. These types of vulnerabilities are devastating since they can result in easily achievable read and write primitives. Thankfully such vulnerabilities are also hard to find.

We haven’t seen weaponized exploits for Acrobat Reader for over four years; we classify this exploit as severely critical and highly recommend patching Acrobat Reader or deploying Morphisec immediately. Morphisec bridges the patch gap by dynamically shifting the memory and making it unpredictable, preventing attacks as soon as they emerge. Morphisec’s Moving Target Defense stops the attack at its earliest stages, before any malicious activity can occur.