This is a razor blade hidden in the PDF inbox, not a battering ram at your perimeter
CVE-2026-34621 is a prototype-pollution bug in the Acrobat/Reader JavaScript handling path that can end in arbitrary code execution as the logged-in user after the victim opens a crafted PDF. The affected lines are Acrobat DC and Acrobat Reader DC Continuous 26.001.21367 and earlier on Windows/macOS, plus Acrobat 2024 Classic 24.001.30356 and earlier, with Adobe fixing DC/Reader DC in 26.001.21411, Acrobat 2024 Windows in 24.001.30362, and Acrobat 2024 macOS in 24.001.30360.
Adobe's raw score is fair but incomplete. The big downward pressure is real: this is AV:L with UI:R, so the attacker still needs delivery and a user open event; the big upward pressure is bigger: Adobe says it is exploited in the wild and CISA put it in KEV on 2026-04-13, which means the bug has already crossed from lab curiosity into operational tradecraft. Net: keep it HIGH, but treat it operationally like an emergency because the reachable population is huge even if the trigger requires user action.
4 steps from start to impact.
Deliver a weaponized PDF
- Target uses Adobe Acrobat or Reader in an affected version
- Attacker can reach the user with a file or link
- The file is not stripped, blocked, or detonated before delivery
- Mail security can quarantine suspicious PDFs
- Browser-based viewers and safe-view features can break the chain before Reader opens the file
- Targeted delivery still requires initial contact with a user
Trigger Acrobat's JavaScript engine
- Victim opens the PDF in vulnerable Acrobat/Reader
- JavaScript execution or relevant document actions are allowed by product policy
- The exploit matches the victim's product track and version
- User interaction is mandatory
- Protected modes, hardening, or disabled JavaScript may interrupt portions of the chain
- Version-specific exploit reliability is usually worse across mixed fleets than headlines imply
Execute code as the current user
- Exploit succeeds against the installed build
- User context has enough rights to launch follow-on payloads
- EDR or application control does not stop child-process or script behavior
- Current-user execution limits blast radius versus a privileged service RCE
- Application control, EDR, and ASR rules often stop commodity post-exploitation
- Sandboxing and containerized document handling reduce impact on hardened endpoints
AcroRd32.exe or Acrobat spawning cmd.exe, powershell.exe, script hosts, unsigned children, or unusual network beacons.Monetize the foothold
- Compromised user has access to sensitive apps, files, or tokens
- Network egress and identity protections allow payload staging or token abuse
- No rapid containment from SOC or EDR
- Least privilege and MFA narrow what the compromised user can actually do
- EDR containment and identity detections often catch the post-exploitation phase
- Single-host compromise is not the same as unauthenticated server-side takeover
The supporting signals.
| In-the-wild status | Confirmed exploited. Adobe says it is being exploited in the wild, and CISA added it to KEV. That is the strongest real-world amplifier in this case. |
|---|---|
| KEV status | KEV-listed on 2026-04-13; due date 2026-04-27. That moves this out of theoretical risk management and into active-incident-prevention territory. |
| Proof-of-concept availability | Public PoC/exploit material appears available. Feedly and CVE aggregation services index GitHub exploit content for this CVE; I would assume copycat weaponization is already underway even if the public code quality varies. |
| EPSS | 0.09811 (~9.8%). That's not sky-high, but EPSS tends to understate targeted client-side document exploits compared with internet-facing server bugs. With KEV present, EPSS loses the argument. |
| CVSS vector read | CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H means no auth required, low technical complexity, but the victim must open the file. The AV:L here is really 'local execution through a document-open event,' not 'needs shell access to the box.' |
| Affected versions | Acrobat DC and Acrobat Reader DC Continuous 26.001.21367 and earlier on Windows/macOS, plus Acrobat 2024 Classic 24.001.30356 and earlier. |
| Fixed versions | Acrobat DC/Reader DC Continuous 26.001.21411; Acrobat 2024 Windows 24.001.30362; Acrobat 2024 macOS 24.001.30360. |
| Scanning / exposure reality | Shodan/Censys/GreyNoise are mostly the wrong lens. This is endpoint client software and the CNA corrected the attack vector from AV:N to AV:L on 2026-04-12. Exposure is driven by install base and user PDF workflows, not internet-open ports. That means your EDR/software inventory is a better source of truth than external attack-surface scanners. |
| Disclosure and reporting | Published 2026-04-11. Adobe credits Haifei Li (EXPMON) for reporting it. |
| Vendor severity nuance | There is a label mismatch worth noting: Adobe's bulletin text calls the bug *critical*, while the CNA CVSS base severity for 8.6 is HIGH after Adobe corrected AV:N to AV:L on 2026-04-12. |
noisgate verdict.
The decisive downward pressure is that exploitation still requires a user to open a malicious PDF in a vulnerable client, so this is not an unauthenticated perimeter break or a wormable service flaw. The decisive upward pressure is stronger than usual: Adobe confirmed active exploitation and CISA KEV listing means attackers are already operationalizing it against a massive installed base.
Why this verdict
- KEV overrides complacency: confirmed real-world exploitation means this is already a practiced attack path, not a hypothetical parser bug.
- Huge endpoint population: Acrobat/Reader is common across enterprise fleets, so even a client-side bug can produce large incident volume once lures land.
- Friction audit keeps it out of CRITICAL:
AV:L+UI:Rmeans the attacker needs delivery plus an open event; that is materially narrower than pre-auth remote exploitation of an exposed service.
Why not higher?
It is not a no-click, no-auth network service exploit. The attacker still needs a victim to open a crafted PDF in a vulnerable local application, and the resulting execution is in the current-user context, which lowers immediate blast radius compared with privileged server-side RCE.
Why not lower?
Dropping this to MEDIUM would ignore the one fact that matters most operationally: it is already being exploited and is KEV-listed. Also, the population at risk is broad because Acrobat/Reader is common, and delivery by PDF is one of the oldest, most reliable social-engineering channels in enterprise environments.
What to do — in priority order.
- Block risky PDF delivery paths — Immediately tighten email and web controls for inbound PDFs from untrusted senders, newly registered domains, and external file-sharing links. Because this is KEV-listed, deploy this within hours as a temporary guardrail while patch rollout catches up.
- Disable Acrobat JavaScript where business allows — If your workflows do not require PDF JavaScript, turn it off through enterprise policy for Reader/Acrobat. That directly attacks the exploit path and should be pushed within hours for high-risk groups such as finance, executives, HR, and externally facing users.
- Force safer PDF handling — Route internet-origin PDFs to browser-native viewers, remote browser isolation, or detonation workflows instead of launching local Acrobat by default. For KEV cases like this, make the policy change within hours for unmanaged or higher-risk user populations.
- Harden child-process behavior from Acrobat — Use EDR/ASR/application-control rules to block or alert on Acrobat/Reader spawning shells, script hosts, LOLBins, or unsigned child processes. This does not fix the bug, but it cuts off the most common post-exploitation paths and should be enforced within hours.
- Prioritize exposed user groups first — If you cannot patch the whole fleet immediately, front-load users who routinely open externally sourced PDFs: accounts payable, legal, recruiting, support, sales, and executives. For a KEV client exploit, risk is driven by document-handling behavior, so target those cohorts within hours.
- A WAF does nothing here because the vulnerable component is a desktop document parser, not a web application endpoint.
- External attack-surface scanning is low value because Acrobat/Reader is not an internet-listening service; your inventory and EDR are the authoritative sources.
- Relying on MFA alone does not prevent the initial endpoint compromise; it only helps constrain some post-exploitation outcomes.
Crowdsourced verification payload.
Run this on the target endpoint or via your software-distribution/EDR remote execution framework. Invoke it with python3 check_cve_2026_34621.py on macOS or py check_cve_2026_34621.py on Windows; standard user rights are usually enough, but local admin helps if you want complete registry visibility across machine-wide installs.
#!/usr/bin/env python3
# check_cve_2026_34621.py
# Detects vulnerable Adobe Acrobat/Reader versions for CVE-2026-34621.
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 1=vulnerable, 0=patched, 2=unknown/error
import os
import re
import sys
import platform
import subprocess
from pathlib import Path
RESULTS = []
THRESHOLDS = {
'dc_continuous': ('26.001.21411',),
'acrobat_2024_windows': ('24.001.30362',),
'acrobat_2024_macos': ('24.001.30360',),
}
def norm(v):
nums = [int(x) for x in re.findall(r'\d+', str(v))]
return tuple(nums)
def cmp_ver(a, b):
aa = list(norm(a))
bb = list(norm(b))
n = max(len(aa), len(bb))
aa += [0] * (n - len(aa))
bb += [0] * (n - len(bb))
return (aa > bb) - (aa < bb)
def add_result(product, version, status, note):
RESULTS.append({
'product': product,
'version': version or 'UNKNOWN',
'status': status,
'note': note
})
def assess_product(name, version, os_name):
lname = name.lower()
if not version:
add_result(name, version, 'UNKNOWN', 'No version found')
return
# Continuous DC / Reader DC
if 'reader' in lname and 'dc' in lname:
if cmp_ver(version, THRESHOLDS['dc_continuous'][0]) < 0:
add_result(name, version, 'VULNERABLE', f'Patch level required: >= {THRESHOLDS["dc_continuous"][0]}')
else:
add_result(name, version, 'PATCHED', f'Patched threshold: >= {THRESHOLDS["dc_continuous"][0]}')
return
if 'acrobat' in lname and 'dc' in lname:
if cmp_ver(version, THRESHOLDS['dc_continuous'][0]) < 0:
add_result(name, version, 'VULNERABLE', f'Patch level required: >= {THRESHOLDS["dc_continuous"][0]}')
else:
add_result(name, version, 'PATCHED', f'Patched threshold: >= {THRESHOLDS["dc_continuous"][0]}')
return
# Acrobat 2024 Classic
if '2024' in lname and 'acrobat' in lname:
if os_name == 'Windows':
target = THRESHOLDS['acrobat_2024_windows'][0]
else:
target = THRESHOLDS['acrobat_2024_macos'][0]
if cmp_ver(version, target) < 0:
add_result(name, version, 'VULNERABLE', f'Patch level required: >= {target}')
else:
add_result(name, version, 'PATCHED', f'Patched threshold: >= {target}')
return
# Generic Adobe Reader/Acrobat install found but track unclear
add_result(name, version, 'UNKNOWN', 'Adobe product found, but track could not be mapped confidently to CVE thresholds')
def scan_windows():
try:
import winreg
except ImportError:
add_result('Windows registry', None, 'UNKNOWN', 'winreg unavailable')
return
uninstall_roots = [
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'),
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall'),
(winreg.HKEY_CURRENT_USER, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'),
]
seen = set()
def reg_get(key, value):
try:
return winreg.QueryValueEx(key, value)[0]
except OSError:
return None
for hive, root in uninstall_roots:
try:
with winreg.OpenKey(hive, root) as kroot:
for i in range(winreg.QueryInfoKey(kroot)[0]):
try:
subname = winreg.EnumKey(kroot, i)
with winreg.OpenKey(kroot, subname) as sk:
dn = reg_get(sk, 'DisplayName')
dv = reg_get(sk, 'DisplayVersion')
if not dn:
continue
ldn = str(dn).lower()
if 'adobe' in ldn and ('acrobat' in ldn or 'reader' in ldn):
ident = (dn, dv)
if ident in seen:
continue
seen.add(ident)
assess_product(str(dn), str(dv) if dv else None, 'Windows')
except OSError:
continue
except OSError:
continue
# Fallback known registry paths
known_paths = [
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Adobe\Acrobat Reader\DC\Installer', 'Adobe Acrobat Reader DC'),
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\Adobe\Adobe Acrobat\DC\Installer', 'Adobe Acrobat DC'),
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\WOW6432Node\Adobe\Acrobat Reader\DC\Installer', 'Adobe Acrobat Reader DC'),
(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\WOW6432Node\Adobe\Adobe Acrobat\DC\Installer', 'Adobe Acrobat DC'),
]
for hive, path, name in known_paths:
try:
with winreg.OpenKey(hive, path) as k:
for val in ('ProductVersion', 'Version', 'AcroVersion'):
v = reg_get(k, val)
if v:
ident = (name, v)
if ident not in seen:
seen.add(ident)
assess_product(name, str(v), 'Windows')
break
except OSError:
continue
def mac_read_plist_value(app_path):
plist = Path(app_path) / 'Contents' / 'Info.plist'
if not plist.exists():
return None
try:
import plistlib
with open(plist, 'rb') as f:
data = plistlib.load(f)
return data.get('CFBundleShortVersionString') or data.get('CFBundleVersion')
except Exception:
return None
def scan_macos():
app_candidates = [
'/Applications/Adobe Acrobat Reader.app',
'/Applications/Adobe Acrobat.app',
'/Applications/Adobe Acrobat DC/Adobe Acrobat.app',
'/Applications/Adobe Acrobat Reader DC.app',
]
found_any = False
for app in app_candidates:
if Path(app).exists():
found_any = True
version = mac_read_plist_value(app)
name = Path(app).stem
assess_product(name, version, 'macOS')
# Try Spotlight metadata as a fallback if available
if not found_any:
try:
cmd = ['mdfind', 'kMDItemCFBundleIdentifier == "com.adobe.Reader" || kMDItemCFBundleIdentifier == "com.adobe.Acrobat.Pro"']
out = subprocess.check_output(cmd, text=True, stderr=subprocess.DEVNULL)
for line in [x.strip() for x in out.splitlines() if x.strip()]:
version = mac_read_plist_value(line)
name = Path(line).stem
assess_product(name, version, 'macOS')
found_any = True
except Exception:
pass
if not found_any:
add_result('Adobe Acrobat/Reader', None, 'UNKNOWN', 'No known Acrobat/Reader app bundle found')
def main():
os_name = platform.system()
if os_name == 'Windows':
scan_windows()
elif os_name == 'Darwin':
scan_macos()
else:
print('UNKNOWN - Unsupported OS for this check')
sys.exit(2)
if not RESULTS:
print('UNKNOWN - No Adobe Acrobat/Reader installation data found')
sys.exit(2)
for r in RESULTS:
print(f"{r['status']}: {r['product']} {r['version']} ({r['note']})")
statuses = {r['status'] for r in RESULTS}
if 'VULNERABLE' in statuses:
print('VULNERABLE')
sys.exit(1)
if 'PATCHED' in statuses and statuses.issubset({'PATCHED'}):
print('PATCHED')
sys.exit(0)
print('UNKNOWN')
sys.exit(2)
if __name__ == '__main__':
main()
If you remember one thing.
Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.