On November 28, 2025, someone uploaded a PDF to VirusTotal. The filename was Invoice540.pdf. Thirteen of sixty-four antivirus engines flagged it. The other fifty-one saw a document.
The person who noticed was Haifei Li, founder of EXPMON. He pulled it apart and published a writeup. The PDF exploited a zero-day in Adobe Reader that let attacker JavaScript mutate Object.prototype, walk through the sandbox's trust checks, and invoke privileged Acrobat APIs. Confirmed on the latest Reader release. Exfiltration went to 169.40.2[.]68:45191.
Adobe did not ship a patch until April 12, 2026. That is 136 days. In the window between the VirusTotal upload and the CVE assignment, a second sample landed: same mechanism, same shape. Researcher Gi7w0rm noted the PDFs carried Russian-language lures referencing current events in Russia's oil and gas sector. The campaign was not a one-off.
This is not a story about prototype pollution being hard to fix. It is a story about how long prototype pollution ran in the wild before the official vendor advisory acknowledged it.
The gap between two evidence pipelines
The virus was not hidden. VirusTotal is a public hash registry. The sample sat there on November 28 with a 13-of-64 detection gradient for anyone to see. Li published the mechanism. The exfil IP was known. Four months later, another sample confirmed the campaign had not stopped.
On April 12, Adobe assigned CVE-2026-34621 (CVSS 8.6, or 9.6 per Adobe's own scoring), CWE-1321 Improperly Controlled Modification of Object Prototype Attributes. Two days later, APSB26-44 patched two more bugs in the same class: CVE-2026-34622 (arbitrary code execution) and CVE-2026-34626 (arbitrary file system read). The engineering team did not find one polluting call. They found a family of them.
Here is where the machinery of patch prioritization breaks. An ops team reading an advisory sees "no known exploits in the wild" and ranks the bug below things with active exploitation reports. That is how the system is supposed to work. The advisory text is the signal. For 136 days on CVE-2026-34621, the signal was wrong.
Adobe was not hiding the mechanism. The PSIRT process was just behind the evidence defenders had already paid for. VirusTotal had the hash. Li had the writeup. Gi7w0rm had the targeting. None of it reached the advisory text. Every defender who trusted the vendor's awareness apparatus as a threat-assessment input spent 136 days reading a document that did not reflect what was happening on the ground.
The two pipelines producing the signal operate on different evidence standards. Community telemetry reports what it sees: a hash, a detection score, a researcher's writeup, an exfil IP. PSIRT advisory text reports what the vendor can confirm through its own evidence chain: private incident reports, internal reverse-engineering, attribution it will stand behind. The first pipeline closed on November 28, 2025. The second did not close until April 12, 2026.
A defender reading PSIRT text is reading a document that says "no evidence the vendor will confirm." The defender thinks they are reading "no exploitation in the wild." Those are not the same sentence. On this bug, they were 136 days apart.
The mechanism, for those who want it
A PDF can carry JavaScript in an open-action handler. Adobe's EScript runtime sandboxes that JS. The sandbox boundary is enforced by JS checks that read from objects inheriting from Object.prototype.
Pollute Object.prototype, and every object in the runtime sees the attacker's value. The trust check reads a lie. The privileged API runs.
That is the class. The Invoice540.pdf campaign was the class in production: obfuscated JS on document open, prototype mutation, privilege escalation, exfiltration, secondary payload delivery.
A public proof-of-concept exists. NULL200OK/cve_2026_34621_advanced on GitHub is a cross-platform exploit generator that produces PDFs performing the prototype mutation and walking the trust-check chain on Windows and macOS. The code is public. Any defender wanting to understand which property descriptors Adobe failed to freeze can read it.
CISA added CVE-2026-34621 to the Known Exploited Vulnerabilities catalog on April 13 with a remediation deadline of April 27.
Why advisory text cannot be the detection layer
The pattern here extends beyond Acrobat. The advisory text a defender reads to decide if a bug is urgent. The "no known exploits" flag a procurement team reads to justify delaying patch deployment. The CVSS exploitation-status field feeding into a ticket's priority lane. All of these are run by the same vendor whose product is the thing being exploited.
A vendor has an institutional incentive to not confirm "exploitation in the wild" until they control the narrative around the fix. A defender assumes "no known exploits" means no known exploits. As the nefariousplan analysis put it: those are never the same sentence. For 136 days on CVE-2026-34621, the gap between them was the gap between "your oil and gas executives' laptops are already owned" and "read the bulletin on Tuesday."
The fix, once it came, was the right shape: freeze the prototypes the sandbox's trust checks read from, move security invariants into property descriptors user JS cannot overwrite, audit every check that reads from a mutable prototype chain. The fix is specific and mechanical. It did not require the patch to exist before it could be written down. The mechanism was available in public since the day the hash landed.
What made the signal wrong for 136 days was not that the vulnerability was hard to understand. It was that the detection architecture runs on legal-and-PR standards, not operational security standards. Treating the advisory text as the threat-assessment pipeline is a category error. The advisory tells you what the vendor will say. It does not tell you what is happening.
Sources
- Primary analysis: nefariousplan.com — CVE-2026-34621 Revisited: The 136-Day Detection Lie (April 23, 2026)
- NVD entry: CVE-2026-34621
- CISA KEV: Adobe Acrobat and Reader Prototype Pollution (added April 13)
- PoC: NULL200OK/cve_2026_34621_advanced (GitHub)
- Reddit discussion: r/netsec — CVE-2026-34621 post on /hot (April 25, 2026)
- Haifei Li / EXPMON — original zero-day analysis (November 28, 2025)
- Gi7w0rm — second-stage campaign analysis (March 23, 2026)