All posts by Bryan Alexander

CVE-2015-5090 is an Adobe Reader/Acrobat Pro bug discovered and reported on by ZDI a handful of months ago.  They used this bug during their fantastic Abusing Adobe Reader’s JavaScript APIs talk at Defcon 23 to demonstrate one of many sandbox escapes discovered in Adobe Reader’s Javascript API.  Reader is and has been a huge target for spear phishing and privilege escalation bugs; not only is the attack surface massive, but patching often poses problematic from a usability and enterprise perspective.  Red teams and real-world adversaries often can harness months old bugs during phishing campaigns to compromise targets, as opposed to potentially burning 0days.  FusionX often relies on these sorts of bugs or flaws when simulating advanced adversaries; there is no need burn an 0day when a public vulnerability can be weaponized and deployed just as easily.  Though ZDI never released a proof of concept (PoC) for this bug, we thought it was a great, reliable bug that still has plenty of utility today.  This post details the bug, a variation in its exploitation, and a working proof of concept.

The AdobeARMService is Adobe’s updating software, and is installed on a system as a service upon installation of Adobe Reader or Acrobat Pro.  This service creates and registers a service control handler on start-up, which allows the service to process external control requests.  The control dispatcher processes incoming service request codes and routes them to appropriate methods; the updating binary, armsvc, has eight or so of these functions, all of which can be triggered from unprivileged processes (intended).  You can read more about service control handlers here.

Our bug begins with service code 0x000000AB, or SHAREDMEMOTY_CREATE (misspelling intentional).  Once the service receives this control request, the request is routed into the CreateSharedMemory function, which creates a named shared memory file with weak permissions and a predictable name.  The security descriptor is as follows:


The shared memory handle name is Global\{E8F34725-3471-4506-B28B-47145817B1AE}_, followed by the C drive’s serial number and the hard-coded string thsnYaViMRAeBoda.  Very simple to generate on the fly.  Once we’ve triggered the generation of the shared memory object, any process running on the system has full read/write to it.  What exactly does this mean?

As it just so happens, another exposed service control 0x000000B4, or ELEVATE_ARM, reads from this shared memory file.  ELEVATE_ARM actually parses out the data pulled from the SM and passes it as command-line arguments to AdobeARMHelper.exe via a ShellExecuteExW call.  This allows us to pass semi-arbitrary flags to the AdobeARMHelper.exe binary, as launched by the armsvc.  I say semi-arbitrary because there is some parsing logic here, though it’s about as trivial as a wcsstr scan of the literal string and copying data out.

AdobeARMHelper.exe is a pretty simple binary that essentially bootstraps some functionality for the updater, including preference setting, PDF ownership patching, and registry changes.  If the binary receives the /Svc or /ArmService flags, it’ll call into the ProcessRequestFromArmService function, which in turn, checks for the /ArmUpdate flag.

If this flag is found, we dive into the ProcessARMUpdateRequest function, a behemoth of self-updating mechanisms and failed install failsafes.  The real meat of this function is its auto-updating mechanisms.  Given a flag on the command line, it will find the first file in the folder, check its digital signature, and then copy it to C:\Program Files (x86)\Common Files\Adobe\ARM\1.0.

As an example, let’s say we write the following into the shared memory handle:

FOLDER:"C:\Windows\Temp\AdobeTEST\" /ArmUpdate

armsvc will parse this out into

FOLDER:"C:\Windows\Temp\AdobeTEST\" /Svc /USER:/ArmUpdate /SESSIONID:0

Our helper binary will then attempt to copy the first file in the C:\Windows\Temp\AdobeTEST\ directory signed by Adobe into the directory in which AdobeARM and AdobeARMHelper reside.  This routine makes no attempt to validate the filename nor its extension.  Thusly, we can get an arbitrary binary signed by Adobe, rename it to AdobeARM.exe, and force armsvc to overwrite the AdobeARM.exe executable.  We would then retrigger the ELEVATE_ARM service handler to have it execute our newly copied binary with whatever command-line flags we’d like.

This is the point in which ZDI demonstrated its JavaScript sandbox bypasses.  By overwriting AdobeARM.exe with the Adobe Reader binary, they forced it to load a malicious PDF that then dropped a DLL into the local directory using the Javascript API and a sandbox escape in Reader.  It’s a great example of a reliable logic-based bug.

We found a second way of exploiting this without the PDF dropper, however ran into a rather interesting hitch.  adl.exe, or the Adobe AIR Debug Launcher, is a tool packaged with Reader that can be used to run and debug AIR applications.  It’s an Adobe signed binary that allows us to specify a remote runtime directory, which we can use to load in a malicious DLL.  Something like this: \adl.exe -run c:\windows\temp\airtmp.

On launch, this will attempt to load C:\Windows\Temp\airtmp\Adobe AIR\Versions\1.0\Adobe AIR.dll.  Pretty simple.  Unfortunately, the digital certificate check in AdobeARMHelper is pretty specific, and oddly irritating.  adl.exe is signed with the following:

Thumbprint Subject
---------- -------
A2D24C4708488FE490310CEF7AC667A71921D635 CN=Adobe Systems Incorporated, OU=Universal Client, OU=Digital ID Class 3 ...

And AcroRdr32.exe is signed as follows:

Thumbprint Subject
---------- -------
45548B92B80CB79A7C628B83D9DBA37B9C86971D CN="Adobe Systems, Incorporated", OU=Acrobat DC, O="Adobe Systems, Incorpo...

It would seem Adobe has incorrectly spelled its own name.  This check is obvious in the AdobeARMHelper code, in which they strcmp the certificate names:


Quite odd, and rather frustrating.  After digging through quite a few of Adobe’s binaries, it would appear that the quoted and comma CN is used primarily within the Reader/Acrobat group, so we limited our search there.

While binary hunting, we thought of a third way of exploiting this without the need for a separate binary.  Using Forshaw’s NTLM reflection PoC, we can force AdobeARM to open a pdf, much like the ZDI folks did, but pass it a UNC path instead to a local attacker-controlled system.  We were able to use this to compromise hashes via Responder, but using Forshaw’s technique, we can actually use the Adobe updater to trigger the privileged access instead of, say, Windows Defender.  This would allow us to redirect the privileged access to a local WebDAV service and proxy an NTLM session.

Finally, we found yet another way of exploiting this, without the need for a separate bug or Windows-specific vector.  Adobe distributes an Enterprise Toolkit (ETK) containing a variety of different enterprise and administrative specific tools related to the management and deployment of Adobe Reader.  Acrobat Pro DC actually uses one of these ETK tools for updating and patching, called a bootstrap deployment.  This tool, setup.exe, simply providers a wrapper around msiexec.exe, used for executing MSI files.  A snippet from Adobe’s documentation on this:

The Acrobat-Reader bootstrapper is provided as part of the Reader bundle on the
CD and the web download. It is also provided for some releases on the FTP
download site. It provides a streamlined way to chain installs without the need
for administrative install points. The bootstrapper provides the following
benefits (...)

We found a copy of this binary on a test system with Acrobat Pro installed, under the following directory: C:\ProgramData\Adobe\Setup\{AC76BA86-7AD7-1033-7B44-AC0F074E4100}.  This application takes a single ini file (setup.ini), parses it out (as described in the link above), and executes an MSI file.  Thankfully, setup.exe allows us to specify alternative ini files, doesn’t choke on extraneous command line flags, and is signed with the correct Adobe digital certificate that we require.

Getting the correct ini file is pretty simple and is just a few lines:


The key entry here being the msi value, which points setup.exe to our malicious MSI to execute.  msfvenom is capable of generating MSI files to obtain code execution, but the template file used is missing a key property that setup.exe requires.  The property UpgradeCode is required to be set in the MSI, and this can set using Microsoft’s Orca tool, as shown:


It doesn’t matter what this value is, we just want setup.exe to query the value and compare it to the current.  With these values in place, we simply trigger the overwrite, then retrigger with /sAll /ini “C:\path\to\ini”; as mentioned, the extraneous commands are thrown out.  This culminates in a safe and reliable privilege escalation bug:


Obviously you don’t want to launch interactive services, but you get the picture.  I’ve attached the code at the bottom of this post.  In order to use this, you’ll just need to generate your own MSI and drop it into the resources folder, named asdf.msi and compile.  setup.exe, asdf.ini, and asdf.msi are compiled into the binary as resources, and dropped to hard-coded folders (in %tmp%) on the system.  Adjust to preference.

The latest version, 11.0.14 with armsvc 1.824.16.6751, doesn’t actually fix the crux of the bug, our exposed shared memory handle.  Instead, they’ve removed the 7Z updating mechanism and now instead enforce an MSI update.  Adequate checks are in place to ensure the binary in question is both a real MSI file and signed by Adobe.  This is done by first inspecting the digital signature of the given file, then ensuring that it’s an MSI:


It then continues to poll for MSI product information using the msi!Msi* suite of win32 functions, eventually using MsiReinstallProductW or MsiInstallProduct to install the MSI.

The proof of concept has been tested with Adobe Reader 11.0.10 with version 1.801.10.4720 of armsvc and can be found here.