import asyncio import argparse import json import logging from datetime import datetime from pathlib import Path from pyppeteer import launch logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s' ) FATAL_MARKERS = ( "GPU DEVICE LOST", "CRASH DETECTED", "VULNERABILITY CONFIRMED", ) def ensure_parent_dir(file_path: str): p = Path(file_path) p.parent.mkdir(parents=True, exist_ok=True) def classify_console_line(text: str): upper = text.upper() if any(marker in upper for marker in FATAL_MARKERS): return "fatal" if "UNCAUGHT GPU ERROR" in upper and any(x in upper for x in ("DEVICE LOST", "GPU HANG", "CONTEXT LOST", "OUT OF MEMORY", "INTERNAL ERROR", "REMOVED")): return "fatal" if "MAX ATTEMPTS REACHED WITHOUT CRASH" in upper: return "non_fatal_terminal" return "info" async def test_cve_2026_5281_headless(url: str, timeout_seconds: int, browser_path: str): logging.info("Starting Headless Automated Testing for CVE-2026-5281 UAF...") run_data = { "started_at": datetime.utcnow().isoformat() + "Z", "url": url, "timeout_seconds": timeout_seconds, "browser_path": browser_path, "console": [], "status": "running", "signals": { "fatal": False, "max_attempts_without_crash": False, }, } def on_console_message(msg): text = msg.text classification = classify_console_line(text) run_data["console"].append({ "ts": datetime.utcnow().isoformat() + "Z", "classification": classification, "text": text, }) logging.info(f"BROWSER CONSOLE: {text}") if classification == "fatal": run_data["signals"]["fatal"] = True if classification == "non_fatal_terminal": run_data["signals"]["max_attempts_without_crash"] = True # Launch browser with parameters allowing WebGPU natively browser = await launch( headless=True, executablePath=browser_path, args=[ '--enable-unsafe-webgpu', '--disable-gpu-sandbox', # Disabling sandbox to ensure direct hardware testing '--enable-features=Vulkan', '--use-angle=vulkan' # Depending on platform, you could also configure to 'd3d11' or 'default' ] ) page = await browser.newPage() # We must listen to console logs generated by our exploit HTML page.on('console', on_console_message) try: logging.info(f"Navigating to exploit payload on {url}") await page.goto(url, {'waitUntil': 'domcontentloaded'}) logging.info(f"Waiting for exploit to execute. The automated headless script will run for up to {timeout_seconds} seconds.") await asyncio.sleep(timeout_seconds) run_data["status"] = "completed" except Exception as e: # A successful DoS or context crash might actually break the Pyppeteer protocol connection logging.warning("Exception caught while connected. This could mean the GPU rendering engine cleanly crashed the headless tab!") logging.error(f"Error trace: {e}") run_data["status"] = "exception" run_data["exception"] = str(e) finally: logging.info("Attempting to close the headless browser.") try: await browser.close() logging.info("Browser closed gracefully.") except Exception as e: logging.warning("Browser was completely hung or killed! (Expected during DoS)") run_data["status"] = "browser_hung" run_data["ended_at"] = datetime.utcnow().isoformat() + "Z" return run_data def summarize_run(run_data): if run_data["signals"]["fatal"]: verdict = "fatal_signal_detected" elif run_data["signals"]["max_attempts_without_crash"]: verdict = "no_crash_within_attempt_budget" else: verdict = "inconclusive" return { "verdict": verdict, "status": run_data.get("status", "unknown"), "signals": run_data.get("signals", {}), "console_lines": len(run_data.get("console", [])), } def parse_args(): parser = argparse.ArgumentParser(description="CVE-2026-5281 automated browser behavior runner") parser.add_argument("--url", default="http://localhost:8080/exploit.html", help="Target URL to open") parser.add_argument("--timeout", type=int, default=15, help="How long to observe behavior") parser.add_argument("--browser-path", default=r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe", help="Chromium-based browser executable path") parser.add_argument("--out-json", default="automated_test_run.json", help="Path for structured JSON output") return parser.parse_args() if __name__ == '__main__': args = parse_args() logging.info("=== AUTOMATED WEBGPU CRASH DETECTOR ===") data = asyncio.get_event_loop().run_until_complete( test_cve_2026_5281_headless(args.url, args.timeout, args.browser_path) ) summary = summarize_run(data) data["summary"] = summary ensure_parent_dir(args.out_json) Path(args.out_json).write_text(json.dumps(data, indent=2), encoding="utf-8") logging.info(f"Saved run JSON: {args.out_json}") logging.info(f"Run verdict: {summary['verdict']}") logging.info("=== END OF TEST ===")