Reading Backwards – Controlling an Integer Underflow in Adobe Reader

December 18, 2017 | Abdul-Aziz Hariri

This is the first in our series of Top 5 interesting cases from 2017. Each of these bugs has some element that sets them apart from the approximately 1,000 advisories released by the program this year. We begin with a former Pwn2Own winner submitting an Adobe Reader bug using a rarely seen vector.


At the ZDI, we receive a huge amount of vulnerability reports on a daily basis. It is not a secret that we receive a lot of vulnerabilities affecting Adobe Reader. The vulnerabilities submitted take on many forms – image parsing, JavaScript Engine, XFA etc. Today, I’m going to detail one of my favorite Adobe Reader bugs we nominated for one of the top 5 vulnerabilities for this year. The vulnerability, ZDI-17-260, was found by Sebastian Apelt of Siberas. For those who do not know Sebastian, he’s one of the Pwn2Own 2014 winners.

One of the reasons that we chose this vulnerability to be one of the top 5 is the fact that it’s an integer underflow leading to code execution, which is something that we don’t see that often. Besides that, this vulnerability is the result of a failed patch for ZDI-16-555/CVE-2016-6947 – a bug also reported by Sebastian.

The following is a snippet of the artistic XML Data Package (XDP) that Sebastian used to trigger and control the vulnerability.  As you can see, it contains a set of nested subform elementsThe following is a snippet of the artistic XML Data Package (XDP) that Sebastian used to trigger and control the vulnerability contains a set of nested subform elements:

To trigger the vulnerability, he used the following JavaScript that modifies the values of the baselineShift and posture attributes of the field element’s font:


The previously mentioned nested subform elements will be stored in an array of size 0x280. In order to control the crash, the heap must be accurately crafted in a way that the array buffer is preceded by a controlled buffer. Of course, this needs to be done before the out-of-bounds read condition is triggered. Crafting the heap cannot be done from normal JavaScript in this case since the array we are targeting deals with nested subform elements in the XDP. Thus, everything should be done from an XDP event.

The event handler in the XDP form will be hit before the nested subform elements are parsed and allocated. Sebastian leveraged the event handler to execute code that allowed him to create array buffers of size 0x280 and fill them with specific values that would result in the array being filled with 0xaaaaaaaabbbbbbbb.

When the handler finishes execution, the subform elements are parsed and the array buffer will be placed right after our controlled JS array.

The end result shows the control:

Later in the execution, it is possible for an attacker to control a vtable-pointer to achieve execution of arbitrary code within the sandboxed renderer process. Additional vulnerabilities would be needed to escape the sandbox and elevate privileges, but this bug is a nice piece of a Pwn2Own-worthy exploit puzzle.


The nature of this vulnerability, along with the fact that it was a failed patch, made it stand out as one of this year’s best. Sebastian’s technique for controlling the crashes in both the 2016 and 2017 vulnerability reports shows the power of fully understanding XDP event handling. Props to Sebastian for the great vulnerability report!

You can find me on Twitter at @AbdHariri, and follow the team for the latest in exploit techniques and security patches. Stay tuned for the next Top 5 bug blog, which will be released tomorrow.