
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).
WEBGL_context_lost ≠ vulnerability. Chrome's GPU watchdog kills long-running
shaders (the loop-unroll-dos trigger and fuzz iterations with huge loop counts are
designed to be slow). Silence the noise: reduce SH_ITER, or confirm any candidate
crash reproduces quickly (< 1 s per iteration).EXC_BAD_ACCESS) rather
than a watchdog termination.Build a vulnerable revision of Chromium with AddressSanitizer:
fetch chromium
cd src
git checkout tags/148.0.7778.215 # last tag before the fix
gn gen out/asan --args='is_asan=true is_debug=false symbol_level=1 dcheck_always_on=true'
autoninja -C out/asan chrome
Then run the minimized crashing shader with Metal API validation enabled:
MTL_DEBUG_LAYER=1 out/asan/Chromium.app/Contents/MacOS/Chromium poc.html
The ASAN trace will name the exact ANGLE/Metal function and confirm whether the crash
matches the CVE's component [1].
(Downstream: once the fix commit for 148.0.7778.216 is visible, diff
third_party/angle/src/libANGLE/renderer/metal/ to pinpoint the true root cause and
replace the fuzzing approach with a deterministic trigger.)
exploitation: none [1].Test only on machines you own or are explicitly authorized to test. The harness intentionally compiles adversarial shaders to crash a graphics process — expect GPU process kills. Keep the vulnerable browser build off production networks.
[1] NVD — CVE-2026-9999: https://nvd.nist.gov/vuln/detail/CVE-2026-9999 * Vendor advisory (Chrome Releases blog, Stable Channel update): https://chromereleases.googleblog.com/2026/05/stable-channel-update-for-desktop_0877304591.html * Chromium issue 513364480 (access-restricted): https://issues.chromium.org/issues/513364480