This is a second lock on the vault door, not the street-facing window
CVE-2026-10921 is an integer overflow in Dawn, Chromium's WebGPU implementation, affecting Google Chrome before 149.0.7827.53. The key qualifier in the vendor description matters more than the bug class: the attacker must already have compromised the renderer process and then use this flaw to push malformed WebGPU/Dawn inputs toward a more privileged process boundary.
Google's HIGH 8.3 label is technically defensible because a compromised renderer to GPU/browser-process memory corruption path is exactly the kind of bug that can become a sandbox-escape building block. But in real enterprise risk terms, this is not initial access and not a clean drive-by; it is a post-compromise chain component with very low EPSS and no KEV listing, so the vendor score overstates the urgency for defenders triaging across 10,000 endpoints.
4 steps from start to impact.
Land code execution in the renderer
- Victim runs a vulnerable Chrome build before 149.0.7827.53
- Attacker already has a renderer compromise from another bug or execution path
- A reachable WebGPU/Dawn code path exists in the victim's build/platform
- This CVE does not provide renderer compromise on its own
- Modern browser exploit chains need at least one additional bug before this CVE becomes useful
- Some enterprise fleets reduce reachable attack surface with hardware/feature policy controls
Abuse Dawn/WebGPU input handling
- WebGPU/Dawn path is enabled and reachable on the target platform
- Attacker can execute JavaScript and/or native logic inside the compromised renderer
- The vulnerable code path is exposed in the victim's exact Chrome build
- WebGPU paths are complex and exploit reliability can be highly platform-specific
- Graphics stack behavior varies by OS, GPU, driver, and software rendering path
- Integer-overflow bugs often need careful heap shaping to move from crash to controlled corruption
Cross the sandbox boundary
- Corruption lands in a process with privileges beyond the renderer sandbox
- The exploit can shape memory precisely enough for useful impact
- Platform mitigations do not terminate the process before weaponization
- A crash is far easier than reliable code execution
- Sandboxing, process isolation, CFG/CFI, and allocator hardening all raise exploit cost
- On many fleets, turning this into durable host compromise still needs another step
Chain to host-level objectives
- Attacker successfully weaponizes the bug into more than a denial-of-service crash
- The endpoint has valuable sessions, secrets, or reachable follow-on paths
- Endpoint controls do not contain the post-escape activity
- This CVE alone does not guarantee OS-level execution
- Credential protections, browser isolation, and EDR can still limit value after exploitation
- Enterprise blast radius is user-context centric unless the attacker adds further escalation
- A WAF does not help; this is a client-side browser bug, not a server-side request path problem.
- Perimeter scanning does not help; Shodan/Censys-style visibility cannot tell you which endpoints have a vulnerable browser build.
- MFA is good hygiene but irrelevant to preventing the vulnerability trigger itself.
The supporting signals.
| In-the-wild status | No public exploitation evidence located in primary sources reviewed, and not listed in CISA KEV at time of assessment. This materially lowers urgency versus Chrome flaws that arrive with zero-day reporting. |
|---|---|
| Proof-of-concept availability | No public PoC or exploit repo found for CVE-2026-10921 in primary-source review. Expect private exploitability to be plausible for advanced actors because Dawn/WebGPU has known research attention, but there is no public weaponization signal here. |
| EPSS | 0.00068 (~0.068%) from the supplied intel, which is very low. The exact percentile was not supplied; based on FIRST's 2026-06-04 EPSS distribution, that score sits roughly in the low-20th percentile *(inference, not a direct API lookup for this CVE)*. |
| KEV status | Not KEV-listed as of this review. If CISA later adds it, the severity and timeline should be revisited immediately because Chrome chain bugs move fast once public exploit chains stabilize. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H from the supplied vendor intel. The important real-world translation is: user interaction plus high complexity plus chained exploitation assumptions. |
| Affected versions | Affected: Google Chrome before 149.0.7827.53 per supplied advisory metadata. Chrome release notes show 149.0.7827.53/.54 as the early stable desktop build and 149.0.7827.53 on Beta for desktop. |
| Fixed versions | Fixed in 149.0.7827.53/.54 for Windows and Mac early stable and 149.0.7827.53 on Beta for Windows/Mac/Linux based on Chrome release pages. For fleet ops, treat 149.0.7827.53 or later as the minimum safe floor unless your channel-specific packaging says otherwise. |
| Scanning / exposure reality | Shodan/Censys/FOFA are not meaningful here because this is a client-side browser component, not an internet-listenable service. Exposure is determined by endpoint version inventory and policy state, not external attack-surface scans. |
| Disclosure date | 2026-06-04 from the supplied intel. Chrome 149.0.7827.53/.54 was already present in release channels on 2026-05-29, which suggests patch availability closely bracketed disclosure. |
| Researcher / reporting | No public credit found in the reviewed primary sources. Chrome often withholds details until users are broadly updated, so the absence of attribution here is not unusual. |
Sources.
- Chrome Releases: Early Stable Update for Desktop (149.0.7827.53/.54)
- Chrome Releases: Chrome Beta for Desktop Update (149.0.7827.53)
- Chromium Security Severity Guidelines
- Chromium Threat Model and Defenses Against Compromised Renderers
- Chromium WebGPU Technical Report
- FIRST EPSS API Documentation
- CISA Known Exploited Vulnerabilities Catalog
Why this verdict
- Requires prior compromise: this is not unauthenticated remote code execution from the open internet; it assumes the attacker already won code execution inside the renderer.
- Exposure population narrows hard: every added prerequisite compounds downward pressure — user interaction, a first-stage browser bug, then a reachable Dawn/WebGPU path, then reliable privilege-boundary crossing.
- Threat intel is quiet: no KEV entry, no public exploit kit/PoC located, and a very low EPSS score all argue against treating this like a break-glass Chrome zero-day.
Why not higher?
If this were exploitable from a malicious page without the renderer-compromise prerequisite, it would stay in HIGH or climb higher because Chrome is ubiquitous and browser compromise is high-value. But once you force the attacker to bring their own renderer RCE first, you have moved from initial-access risk to chain-completion risk.
Why not lower?
It still sits above LOW because the vulnerable component lives in a massively deployed browser and the likely outcome is not just a tab crash; it is a potential sandbox-boundary bypass primitive. Chromium's own security guidance treats compromised-renderer-to-higher-privilege memory corruption as strategically important, so ignoring it would be complacent.
Crowdsourced verification payload.
Run this on the target endpoint or via your software inventory/remote execution platform. Invoke it with python3 chrome_cve_2026_10921_check.py on macOS/Linux or py chrome_cve_2026_10921_check.py on Windows; it needs standard user privileges only because it reads local application metadata and common install paths.
#!/usr/bin/env python3
# CVE-2026-10921 Chrome version check
# Exit codes:
# 0 = PATCHED
# 1 = VULNERABLE
# 2 = UNKNOWN
import os
import platform
import re
import subprocess
import sys
from pathlib import Path
MIN_VERSION = (149, 0, 7827, 53)
def parse_version(s):
m = re.search(r'(\d+)\.(\d+)\.(\d+)\.(\d+)', s or '')
if not m:
return None
return tuple(int(x) for x in m.groups())
def version_str(v):
return '.'.join(str(x) for x in v)
def get_windows_versions():
candidates = [
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\Application\chrome.exe'),
]
found = []
try:
import winreg # type: ignore
reg_paths = [
(winreg.HKEY_CURRENT_USER, r'Software\Google\Chrome\BLBeacon', 'version'),
(winreg.HKEY_LOCAL_MACHINE, r'Software\Google\Chrome\BLBeacon', 'version'),
(winreg.HKEY_LOCAL_MACHINE, r'Software\WOW6432Node\Google\Chrome\BLBeacon', 'version'),
]
for hive, path, name in reg_paths:
try:
with winreg.OpenKey(hive, path) as k:
val, _ = winreg.QueryValueEx(k, name)
v = parse_version(str(val))
if v:
found.append(('registry:' + path, v))
except OSError:
pass
except Exception:
pass
for c in candidates:
if os.path.exists(c):
try:
out = subprocess.check_output([
'powershell', '-NoProfile', '-Command',
f"(Get-Item '{c}').VersionInfo.ProductVersion"
], stderr=subprocess.DEVNULL, text=True, timeout=10).strip()
v = parse_version(out)
if v:
found.append((c, v))
except Exception:
pass
return found
def get_macos_versions():
candidates = [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
str(Path.home() / 'Applications/Google Chrome.app/Contents/MacOS/Google Chrome'),
]
found = []
for c in candidates:
if os.path.exists(c):
try:
out = subprocess.check_output([c, '--version'], stderr=subprocess.DEVNULL, text=True, timeout=10).strip()
v = parse_version(out)
if v:
found.append((c, v))
except Exception:
pass
plist_candidates = [
'/Applications/Google Chrome.app/Contents/Info.plist',
str(Path.home() / 'Applications/Google Chrome.app/Contents/Info.plist'),
]
for p in plist_candidates:
if os.path.exists(p):
try:
out = subprocess.check_output(['/usr/libexec/PlistBuddy', '-c', 'Print :KSVersion', p], stderr=subprocess.DEVNULL, text=True, timeout=10).strip()
v = parse_version(out)
if v:
found.append((p, v))
except Exception:
pass
return found
def get_linux_versions():
commands = [
['google-chrome', '--version'],
['google-chrome-stable', '--version'],
['chromium-browser', '--version'],
['chromium', '--version'],
]
found = []
for cmd in commands:
try:
out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL, text=True, timeout=10).strip()
v = parse_version(out)
if v:
found.append((' '.join(cmd), v))
except Exception:
pass
for p in ['/opt/google/chrome/chrome', '/usr/bin/google-chrome', '/usr/bin/google-chrome-stable']:
if os.path.exists(p):
try:
out = subprocess.check_output([p, '--version'], stderr=subprocess.DEVNULL, text=True, timeout=10).strip()
v = parse_version(out)
if v:
found.append((p, v))
except Exception:
pass
return found
def dedupe(found):
seen = set()
result = []
for path, ver in found:
key = (path, ver)
if key not in seen:
seen.add(key)
result.append((path, ver))
return result
def main():
system = platform.system().lower()
if 'windows' in system:
found = get_windows_versions()
elif 'darwin' in system:
found = get_macos_versions()
else:
found = get_linux_versions()
found = dedupe(found)
if not found:
print('UNKNOWN: Google Chrome not found or version could not be determined')
sys.exit(2)
best = max((ver for _, ver in found))
best_paths = [path for path, ver in found if ver == best]
if best >= MIN_VERSION:
print(f'PATCHED: detected Chrome {version_str(best)} at {best_paths[0]} (minimum safe version {version_str(MIN_VERSION)})')
sys.exit(0)
else:
print(f'VULNERABLE: detected Chrome {version_str(best)} at {best_paths[0]} (requires >= {version_str(MIN_VERSION)})')
sys.exit(1)
if __name__ == '__main__':
main()