Object.prototype pollution in Acrobat Reader DC parsing

Object.prototype pollution in Acrobat Reader DC parsing

Prototype pollution in a PDF parser is not just a tidy bug class name. In Acrobat, it can change how later JavaScript sees objects that were never meant to be attacker-controlled. That matters because parts of the application run with elevated trust, and they do not all re-check where a value came from before using it.

The affected builds included Adobe Reader 26.001.21367 and earlier for one issue, and Adobe Reader 26.001.21411 and earlier for the other two. Adobe later reported in-the-wild exploitation through malicious PDF files on Windows and macOS, with impact reaching arbitrary code execution and user information leakage.

How a polluted prototype reaches privileged Acrobat JavaScript

The dangerous part is the hand-off from parsing into privileged JavaScript. A polluted prototype can supply properties that trusted code expects to come from an explicit object, not the inheritance chain. If the code reads those properties without checking ownership, the attacker gets to shape control flow from a PDF file.

That becomes worse when the target code runs inside app.trustedFunction(...) or inside a privileged block. The trust boundary is already crossed at that point. If the object passed into that code is confused, polluted, or only partly normalised, the application starts treating inherited data as if it were valid document state.

Where Adobe’s trusted paths turn inherited properties into action

The patch work lined up with three different failure modes. One involved an unqualified global lookup, one involved string-to-code construction, and one involved trusted string-processing paths that accepted non-string values. They are different bugs, but they share the same shape: inherited properties survive long enough to reach a privileged sink.

The unqualified swConn lookup in SilentDocCenterLogin

In SilentDocCenterLogin, swConn was resolved without qualification. That sounds harmless until prototype pollution puts a fake swConn on Object.prototype and the lookup walks straight into it. From there, the fake object can redirect execution into SOAP.stringFromStream, which is not where a parser should end up because of a PDF-crafted property.

The important failure is not the missing variable name by itself. It is the assumption that a global-style lookup cannot be influenced by data that arrived through parsing. Once the object model is polluted, that assumption stops holding.

Inherited keys and eval-built handlers in ANFancyAlertImpl

ANFancyAlertImpl iterated over buttons with for (var i in buttons), so inherited keys were part of the walk. It then built handler source with eval('(function(dialog) { dialog.end('' + bid + ''); })'), which is exactly the sort of thing that turns a bad property name into executable behaviour.

The fix removed the source generation and switched to a closure parameter. That closes off the neat little trick where a polluted prototype feeds a crafted button identifier into eval and gets JavaScript execution for free. Acrobat did not need a clever parser exploit here. It only needed to trust a key that was never owned by the object it came from.

A third path involved object confusion in trusted string-processing code such as ANShareFile. Inputs such as doc.path, props.originalPath, props.savePath, decodedURL, and doc.documentFileName were used in trusted contexts where primitive string normalisation should have been the boundary. If a non-string object slips through there, inherited properties can surface in places that were meant to handle plain text.

Hardening the parsing boundary before malicious PDFs can pivot

The boundary needs to be boring. Parse untrusted PDF data into plain data structures, reject inherited properties, and normalise values before they reach any privileged JavaScript path. If a function expects a string, pass a string. If it expects an owned property, check ownership and do not walk the prototype chain.

Two controls matter here:

  • Use explicit own-property checks before iterating or reading parser-derived objects
  • Normalise parser outputs to primitive types before they reach trusted helpers

That is the part that stops prototype pollution from becoming code execution. Once a malicious PDF can influence a trusted lookup, the rest is just Acrobat following instructions it should never have accepted.

Patch checks that actually prove the fix held

A version bump alone proves very little. The fix needs to be exercised in the same paths that failed before. For the swConn issue, check that inherited Object.prototype.swConn values no longer influence SilentDocCenterLogin. For the alert handler issue, confirm that inherited button keys do not appear in handler construction and that no eval-built source remains.

For the trusted string paths, feed object values where strings are expected and check that the code rejects them before any privileged action. Adobe’s affected versions and patch steps make the comparison straightforward: 26.001.21367 to 26.001.21411, then 26.001.21411 to 26.001.21431. If a malicious PDF can still steer control flow through inherited keys, the boundary is still soft.

Related posts

OCI image trust for Linux host observability

Inspektor Gadget OCI images make deployment easy, but they also move trust to places people rarely examine. I would rather be awkward about signatures, TLS and build inputs now than discover later...

Object.prototype pollution in Acrobat Reader DC parsing

Adobe Acrobat PDF parsing is one of those areas where a small trust mistake becomes something much nastier. I spent time tracing how polluted prototypes slip into privileged JavaScript, and the path...

LiteLLM exploit chains at the auth boundary

LiteLLM exploit chains only work because the auth boundary is doing too much, and failing badly when the database wobbles. I trust a proxy less when a bad lookup can quietly turn into proxy-admin...