This is the second lock on the door after the burglar is already inside the lobby
CVE-2026-10969 is an input-validation flaw in Chrome's Extensions component affecting Google Chrome versions prior to 149.0.7827.53 on Linux and prior to the 149.0.7827.53/.54 desktop stable build on Windows and macOS. The important caveat is in the description itself: the attacker must already have *compromised the renderer process* and then use a crafted HTML page to cross into more privileged browser territory.
Google marked it HIGH at 7.5, which is fair in a lab because privilege-boundary failures inside a browser exploit chain matter. In enterprise reality, though, this is not a clean unauthenticated remote compromise by itself; it is a *post-renderer-compromise* escalation step with no public exploitation evidence, no KEV listing, and a very low EPSS signal, so the vendor score overstates the Monday-morning urgency.
4 steps from start to impact.
Land the user on attacker-controlled web content
- Victim uses Google Chrome on Windows, macOS, or Linux
- Victim visits attacker-controlled or attacker-influenced web content
- Chrome version is older than the fixed
149.0.7827.53family
- Requires user interaction (
UI:R) - Enterprise web filtering, browser isolation, and DNS security can cut off delivery
- This step is common, but still not sufficient to exploit this CVE
Exploit a separate renderer bug first
- A separate working renderer exploit exists
- The attacker can make that exploit fire in the victim's browsing session
- The renderer compromise survives long enough to pivot
- Implies a prior compromise stage; the attacker is already mid-chain
- Modern Chrome hardening, site isolation, and exploit mitigations raise development cost
- EDR/browser exploit telemetry may catch crashes, heap grooming, or suspicious browser behavior
Abuse Extensions trust boundaries from the owned renderer
- Renderer is already compromised
- Chrome Extensions component is at a vulnerable version
- The attack path reaches the affected extensions-related interface
- Exploit path is component-specific and requires precise browser-internals knowledge
- Not every renderer foothold cleanly reaches the vulnerable extension pathway
- Extension policy restrictions and hardened enterprise configs can narrow reachable surfaces
Convert browser privilege gain into endpoint impact
- Successful extension-layer privilege escalation
- Valuable browser data, sessions, or follow-on targets exist on the host
- This CVE does not by itself promise full OS compromise
- Blast radius is typically one browsing session or one endpoint at a time
- Attackers still need post-exploitation tradecraft to turn browser privilege into enterprise-scale impact
MEDIUM verdict there is no mitigation SLA; use this as part of the normal remediation window rather than an emergency control.MEDIUM there is no mitigation SLA; do it within the standard remediation program if your fleet cannot update promptly.- Perimeter vuln scanning doesn't help much because this is a client-side browser flaw, not a remotely enumerable service.
- MFA doesn't stop exploitation inside the browser; it only limits reuse of stolen sessions or credentials afterward.
- A WAF is mostly irrelevant unless it blocks the delivery site entirely; it cannot protect a local browser from its own vulnerable code path.
The supporting signals.
| In-the-wild status | No public evidence found in reviewed authoritative sources. Google did not flag this as exploited in the stable-channel advisory, and CISA KEV does not list it. |
|---|---|
| Public PoC availability | No public exploit or PoC was surfaced in the reviewed primary sources. The GHSA page shows no known source code entry. |
| EPSS | 0.00066 (0.066%, 21st percentile on GHSA), which is weak signal for near-term broad exploitation. |
| KEV status | Not KEV-listed in CISA's Known Exploited Vulnerabilities Catalog. |
| CVSS vector meaning | CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H says remote delivery is possible, but the AC:H and UI:R already hint this is not a point-and-shoot bug. The more important real-world friction is *unstated in CVSS shorthand*: it requires a pre-compromised renderer. |
| Affected versions | Google Chrome prior to 149.0.7827.53; desktop stable announcement covers 149.0.7827.53 on Linux and 149.0.7827.53/.54 on Windows/macOS. |
| Fixed versions | Upgrade to Chrome 149.0.7827.53 or later; for Windows/macOS the stable desktop release was 149.0.7827.53/.54. Downstream Chromium backports are vendor-specific and were not spelled out in the Chrome advisory. |
| Scanning / exposure reality | This is not internet-enumerable in any useful way via Shodan/Censys/FOFA because it is a client browser flaw, not a network-listening service. Exposure is best measured by endpoint inventory and browser version telemetry, not external attack-surface scans. |
| Disclosure / reporter | Disclosed 2026-06-05 in GHSA/NVD records. Chrome release notes show it was reported by Google on 2026-05-10 under Chromium issue 511765713. |
Sources.
Why this verdict
- Start from Google's 7.5 baseline because Chrome is everywhere and privilege-boundary failures inside browsers can absolutely matter when chained
- Downgrade hard for attacker position: the attacker must already have compromised the renderer process, which implies a prior exploit stage and makes this a second-stage chain component, not initial access
- Downgrade again for operational signal: no KEV listing, no public exploitation note from Google, and a very low EPSS (
0.066%) all argue against fleet-emergency treatment
Why not higher?
If this were a standalone remote code execution or sandbox escape with no prerequisite beyond visiting a page, HIGH would be fair and CRITICAL would be on the table if exploitation showed up. But the renderer-compromise prerequisite is a huge real-world narrowing factor, and that matters more than abstract C/I/A impact.
Why not lower?
Chrome is ubiquitous, and browser exploit chains are still one of the most practical ways to land on endpoints. A privilege-escalation step that helps an attacker escape renderer restrictions can materially change the outcome of a targeted campaign, so this is not backlog junk.
Crowdsourced verification payload.
Run this on the target endpoint or through your EDR/remote-exec tooling, not from an auditor workstation. Invoke it with python3 check_chrome_cve_2026_10969.py on macOS/Linux or py check_chrome_cve_2026_10969.py on Windows; no admin rights are normally required, but access to standard install paths is needed.
#!/usr/bin/env python3
# Check Google Chrome version for CVE-2026-10969
# Outputs one of: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
import os
import platform
import re
import shutil
import subprocess
import sys
from typing import List, Optional, Tuple
THRESHOLD = (149, 0, 7827, 53)
VERSION_RE = re.compile(r'(\d+)\.(\d+)\.(\d+)\.(\d+)')
def parse_version(text: str) -> Optional[Tuple[int, int, int, int]]:
m = VERSION_RE.search(text or '')
if not m:
return None
return tuple(int(x) for x in m.groups())
def version_to_str(v: Tuple[int, int, int, int]) -> str:
return '.'.join(str(x) for x in v)
def run_and_get_version(cmd: List[str]) -> Optional[Tuple[int, int, int, int]]:
try:
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=5)
out = (proc.stdout or '') + ' ' + (proc.stderr or '')
return parse_version(out)
except Exception:
return None
def candidate_commands() -> List[List[str]]:
cmds = []
system = platform.system().lower()
if system == 'windows':
paths = [
os.path.join(os.environ.get('ProgramFiles', r'C:\\Program Files'), 'Google', 'Chrome', 'Application', 'chrome.exe'),
os.path.join(os.environ.get('ProgramFiles(x86)', r'C:\\Program Files (x86)'), 'Google', 'Chrome', 'Application', 'chrome.exe'),
os.path.join(os.environ.get('LocalAppData', ''), 'Google', 'Chrome', 'Application', 'chrome.exe'),
]
for p in paths:
if p and os.path.exists(p):
cmds.append([p, '--version'])
elif system == 'darwin':
mac_path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
if os.path.exists(mac_path):
cmds.append([mac_path, '--version'])
if shutil.which('google-chrome'):
cmds.append(['google-chrome', '--version'])
else:
for name in ['google-chrome', 'google-chrome-stable', '/usr/bin/google-chrome', '/usr/bin/google-chrome-stable']:
if name.startswith('/'):
if os.path.exists(name):
cmds.append([name, '--version'])
elif shutil.which(name):
cmds.append([name, '--version'])
return cmds
def main() -> int:
found_versions = []
for cmd in candidate_commands():
v = run_and_get_version(cmd)
if v:
found_versions.append((cmd[0], v))
if not found_versions:
print('UNKNOWN: Google Chrome not found or version unreadable')
return 2
vulnerable = []
patched = []
for path, ver in found_versions:
if ver < THRESHOLD:
vulnerable.append((path, ver))
else:
patched.append((path, ver))
if vulnerable:
details = '; '.join(f'{path}={version_to_str(ver)}' for path, ver in vulnerable)
print(f'VULNERABLE: {details} (fixed at {version_to_str(THRESHOLD)} or later)')
return 1
if patched:
details = '; '.join(f'{path}={version_to_str(ver)}' for path, ver in patched)
print(f'PATCHED: {details}')
return 0
print('UNKNOWN: unable to classify installed Chrome version')
return 2
if __name__ == '__main__':
sys.exit(main())