Prototype pollution in Adobe Acrobat PDF code paths

Prototype pollution in Adobe Acrobat PDF code paths

Prototype pollution in Acrobat matters because the PDF JavaScript environment is not a toy sandbox bolted on for decoration. It sits close enough to privileged collaboration and dialog logic that a polluted property can change which object gets used, which branch runs, or which source string gets built. Once attacker input lands in that space, the code stops behaving like a parser and starts behaving like a confused assistant.

The three CVEs share that pattern, but each one fails in a different way. One follows an undeclared variable into a fake collaboration object. One turns dialog button keys into code. One trusts object values where plain strings should have been enforced.

How attacker-controlled PDF data reaches privileged JavaScript

The exploit path starts with malicious PDF content feeding JavaScript-backed workflows inside Acrobat or Reader. That content does not need to look dramatic. A document can carry values that end up in collaboration helpers, dialog handlers, or trusted file-sharing routines, and the dangerous part happens later when those values are interpreted as code, object properties, or file paths.

The relevant failure is object confusion. Code that assumes a local variable, a primitive string, or a clean object can be steered into inherited properties on Object.prototype. In practice, that gives attacker input a way to shape privileged behaviour without passing through the checks the code meant to rely on.

SilentDocCenterLogin and the swConn object leak

One route involved SilentDocCenterLogin, which called Collab.swConnect(connectParams) and then used swConn without declaring it as a proper local variable. That sounds small because it is small. The damage comes from scope resolution: once swConn is not anchored locally, the runtime can fall back to global or inherited properties, which is exactly where polluted prototype state becomes useful to an attacker.

The patched behaviour turned swConn into a true local variable. That closes off the easy inheritance path and stops the code from accepting a fake collaboration object through Object.prototype. In the exploit chain, that fake object could redirect execution into stream-handling paths such as SOAP.stringFromStream, which is the sort of detour no sensible document should be taking.

Where the three CVEs diverge in practice

The three CVEs are linked by prototype pollution, but they do not fail in the same place. That matters for defence because a generic “block malicious PDFs” answer misses the real boundary. One bug is about name resolution, one is about source generation, and one is about trusting object values in a privileged string-processing path.

That split is why a partial fix can still leave room for abuse. A patch that closes one route does not automatically stop another code path from accepting polluted objects or attacker-controlled identifiers.

Dialog handlers built from button keys

One of the bugs sat in ANFancyAlertImpl, where dialog handlers were built from button keys and then passed to eval. That is ugly in the classic, boring way. If an attacker can control the identifier that lands in the handler source, the code generator stops being a convenience and becomes an execution path.

The fix replaced the source-string construction with a function that uses a closure parameter instead of eval. That removes the direct code-generation step and cuts off attacker JavaScript execution from malicious dialog button identifiers. The important part is not that eval is bad in the abstract. It is that dynamic handler generation with untrusted keys gives an attacker a place to smuggle syntax into a privileged path.

Trusted string sinks that stop trusting strings

The third bug sat in trusted collaboration and file-sharing workflows, including ANShareFile, which was defined with app.trustedFunction(function(props) ...). Here the problem was not code generation. It was type confusion. Fields such as doc.path, props.originalPath, props.savePath, decodedURL, and doc.documentFileName were treated as if they were plain strings, then reused in ways that assumed clean primitive values.

That allowed object values to reach sinks that expected strings, which is a neat way to get a trusted function to behave unpredictably. The fix forced doc.path to be a primitive string and reused a normalised local value. That is the kind of change that looks dull in a diff and saves someone from a bad afternoon, or worse.

The practical boundary is simple: trusted functions should not accept object-shaped surprises where they expect a string. Once a string sink starts accepting object values, prototype pollution turns into a control-flow problem rather than a data-cleanliness problem, and that is a much nastier place to be.

Adobe Acrobat PDF prototype pollution is a good reminder that privileged document logic is only as safe as the assumptions wrapped round it. Undeclared variables, eval, and loose string handling are three different ways to hand an attacker a spare key.

Related posts

Weekly Tech Digest | 06 Jul 2026

Stay updated with the latest in tech! This digest covers AI ethics, auto industry shifts, and the impact of politics on technology, exploring today's pressing issues.

wolfCOSE zero-allocation parsing in embedded C

wolfCOSE looks sensible only if you care about what your firmware actually has to carry. I like that, because on small targets the wrong crypto feature can cost more than the message itself, and there...

restic | v0.19.1

restic v0 19 1: safer FUSE mounts and mountpoint checks, robust backup source and exclude handling, clearer CLI JSON output, Windows SFTP deletion fixes