
Proof-of-concept and GLSL fuzzer for CVE-2026-9999 in Chrome's ANGLE/Metal WebGL backend, with build fingerprinting, curated shaders, and crash artifact capture.
| Field | Value |
|---|---|
| CVE | CVE-2026-9999 |
| Vendor / product | Google Chrome |
| Affected | Chrome < 148.0.7778.216, macOS only [1] |
| Fixed in | 148.0.7778.216 [1] |
| Impact | Arbitrary code execution inside a sandbox via a crafted HTML page [1] |
| Chromium severity | High [1] |
| CVSS 3.1 (CISA-ADP) | 8.8 HIGH — CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H [1] |
| NVD (NIST) CVSS assessment | Not yet provided [1] |
| CWE | CWE-269 — Improper Privilege Management (enrichment by CISA-ADP) [1] |
| CISA SSVC (2026-05-29) | exploitation: none, automatable: no, technicalImpact: total [1] |
| Published / last modified | 2026-05-28 / 2026-07-21 [1] |
| Root cause | Not public — Chromium issue 513364480 shows "Permissions Required" [1] |
Attack chain implication: code execution "inside a sandbox" [1] means an attacker gaining control of a sandboxed process (most plausibly the GPU process). A full browser takeover would additionally require a separate sandbox escape that is not part of this CVE.
The following is inference from the public record, not confirmed facts:
The harness therefore exercises exactly those patterns and then mutates GLSL at scale to hunt for a renderer-side crash (WebGL context lost = GPU process crash or watchdog kill).
| File | Purpose |
|---|---|
cve-2026-9999-poc.html | Self-contained PoC harness (no dependencies): build fingerprinting, 8 curated translator-stress shaders, GLSL fuzzer with crash artifact capture |
README.md | This file |
Google's Chrome for Testing archive provides version-pinned builds that do not auto-update. Find the newest vulnerable build (everything below 148.0.7778.216 is affected [1]):
import json, urllib.request
data = json.load(urllib.request.urlopen(
"https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json"))
key = lambda v: tuple(int(x) for x in v.split('.'))
fix = (148, 0, 7778, 216)
cands = sorted((v["version"] for v in data["versions"]
if "chrome" in v.get("downloads", {}) and key(v["version"]) < fix), key=key)
print(cands[-5:]) # pick the last (newest vulnerable) entry
Download for your architecture (uname -m):
# Apple Silicon
https://storage.googleapis.com/chrome-for-testing-public/<VERSION>/mac-arm64/chrome-mac-arm64.zip
# Intel
https://storage.googleapis.com/chrome-for-testing-public/<VERSION>/mac-x64/chrome-mac-x64.zip
Unzip into a lab folder (e.g. ~/lab/, not /Applications) and launch from there.
If a specific build is missing from Chrome for Testing, use the Chromium snapshot
archive (commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/ for Intel,
.../Mac_Arm/ for Apple Silicon), indexed by commit position — map the vulnerable
version to a branch position via chromium-history, then pull the nearest snapshot.
Snapshots also do not auto-update.
Kill the Keystone updater agents and block googleupdate.googleapis.com, or the test
build will silently patch itself overnight. Not needed for Chrome for Testing builds.
Without Metal, Chrome renders WebGL in software (SwiftShader) and the ANGLE Metal backend is never touched. In the PoC page's console, run:
const c = document.createElement('canvas'), g = c.getContext('webgl2');
const x = g.getExtension('WEBGL_debug_renderer_info');
console.log(g.getParameter(x.UNMASKED_RENDERER_WEBGL));
// Good: contains "ANGLE Metal Renderer"
// Bad: contains "SwiftShader" -> the vulnerable path is NOT being exercised
You can also confirm at chrome://gpu. Parallels/VMware Fusion guests with GPU
virtualization usually work; minimal QEMU/UTM setups often fall back to SwiftShader.
cd <repo dir>
python3 -m http.server 8000
# then browse to http://localhost:8000/cve-2026-9999-poc.html
1) Run curated triggers. Eight shaders stress the translator patterns
described in §2 (OOB constant indexing, integer-overflow array sizing, dynamic matrix
indexing, loop-unroll stress, arrays-of-arrays, discard/swizzle, preprocessor edges,
oversized UBOs). Watch for [!!] results and context-loss events.2) Fuzz. Mutated GLSL shaders are compiled, linked, drawn to, and
read back in a loop. Watch the log for CONTEXT LOST.localStorage. Retrieve it:
localStorage.getItem("cve20269999_poc_fs"); // crashing fragment shader
localStorage.getItem("cve20269999_crashes"); // crash event log
Live snapshot of a suspicious run is also visible at
chrome://gpuand in macOS Console (GPU process crash reports).