← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-10928 · CWE-94 · Disclosed 2026-06-04

Script injection in Headless in Google Chrome prior to 149

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a landmine hidden in the print room, not a grenade rolling across every desktop

CVE-2026-10928 is a script injection flaw in Chrome Headless that affects versions before 149.0.7827.53 on Linux and before 149.0.7827.53/.54 on Windows and macOS. The attacker supplies crafted HTML, and the vulnerable code path is only reached when that content is rendered by Headless Chrome—the unattended browser mode used by automation stacks, PDF/screenshot services, CI jobs, crawlers, preview generators, and tools such as Puppeteer.

Google's HIGH 8.8 rating is technically defensible for the bug in isolation, but it overstates the population actually reachable in most enterprises. The real-world friction is the big one: the target must be running headless rendering of attacker-controlled content. That sharply narrows exposure versus normal Chrome desktop browsing, and the very low EPSS plus no KEV listing and no reviewed exploitation evidence push this down into a controlled, environment-dependent risk rather than an all-hands emergency.

"Serious bug, narrow target set: this is mostly a headless-renderer problem, not a fleet-wide desktop fire."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Get malicious HTML in front of a renderer

The attacker first needs a workflow that sends untrusted HTML or a URL into a Headless Chrome job. In practice that means a PDF export service, page previewer, screenshot worker, crawler, CI browser test, or an app embedding Chrome through Puppeteer or direct chrome --headless usage. The weaponized content is just a crafted HTML page; there is no need for the attacker to authenticate to Chrome itself.
Conditions required:
  • A service or host must run Chrome Headless
  • That workflow must render attacker-controlled HTML or visit attacker-controlled URLs
  • The vulnerable version must still be installed
Where this breaks in practice:
  • Most enterprise endpoints run normal interactive Chrome, not headless automation
  • Many headless jobs render only trusted internal templates, not arbitrary internet content
  • URL allowlists, job isolation, and queue mediation often block direct attacker reach
Detection/coverage: Vulnerability scanners can usually flag outdated Chrome/Chromium packages, but they often miss whether the vulnerable binary is actually used in headless paths.
STEP 02

Trigger the Headless-only vulnerable code path

When the job renders the crafted page, the bug is exercised inside the Headless implementation. Official docs show Headless is commonly used for --print-to-pdf, screenshots, and automation, which is exactly the kind of unattended rendering path an attacker wants. The practical toolchain here is Headless Chrome / Puppeteer / ChromeDriver.
Conditions required:
  • The render job must actually invoke Headless mode
  • The malicious HTML must hit the specific vulnerable parser/runtime path
  • The environment must not reject or sanitize the submitted content before render
Where this breaks in practice:
  • Some wrappers pre-render or sanitize content before browser execution
  • Job timeouts, CSP, or network egress controls can reduce exploit reliability
  • Many services run fresh Chrome-for-Testing bundles rather than stale system Chrome
Detection/coverage: EDR may show a browser subprocess spawned with --headless, but it usually will not distinguish safe rendering from exploit triggering without strong telemetry.
STEP 03

Gain code execution in the browser context

Vendor language says arbitrary code execution is possible via crafted HTML, so the attacker can achieve execution in the vulnerable browser process if exploitation succeeds. In real deployments, that usually lands in a worker/container/service-account context, not an end-user admin session. The blast radius depends on what that renderer can access: filesystem artifacts, tokens, cookies, internal web apps, or cloud metadata.
Conditions required:
  • The exploit must be reliable against the exact Chrome build and platform
  • The browser process must have access to useful secrets or network paths
  • The service account or container must not be heavily constrained
Where this breaks in practice:
  • Headless workers are often ephemeral and sandboxed
  • Many render nodes have low-privilege service accounts and minimal data at rest
  • There is no reviewed public PoC or active exploitation evidence reducing confidence in weaponization today
Detection/coverage: Post-exploitation behavior is where defenders have the best chance: unusual child processes, outbound callbacks from render nodes, file access spikes, or service-account token abuse.
STEP 04

Pivot from renderer to something that matters

The attacker only gets real enterprise impact if the compromised render host can reach internal apps, object storage, CI secrets, or metadata services. This is where segmentation and workload identity make or break the incident. The likely weaponized follow-on tools are commodity recon and exfil tooling run from the compromised worker.
Conditions required:
  • Renderer must have meaningful network or secret access
  • Segmentation or egress controls must be weak enough to allow pivoting
  • The workload must persist long enough for follow-on actions
Where this breaks in practice:
  • Good container isolation and short-lived jobs shrink dwell time
  • Cloud IAM scoping and secret brokers can sharply limit follow-on value
  • Many headless renderers sit behind queues and are not directly internet-addressable
Detection/coverage: Network detection is strongest here: suspicious egress from CI/render subnets, unexpected DNS, and service-account activity outside normal automation patterns.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo reviewed evidence of active exploitation as of 2026-06-05; the CVE is not in CISA KEV.
Public PoCNo reviewed public PoC tied specifically to CVE-2026-10928. That matters because Headless-only bugs often need environment-specific grooming before they become reliably weaponized.
EPSS0.00084 (user-supplied intel), which is extremely low and consistent with a bug that is technically nasty but operationally niche.
KEV statusNot listed in the CISA Known Exploited Vulnerabilities Catalog.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H — remote, low complexity, no auth, but user interaction/render-trigger required. In this case the practical 'user' is often an automation pipeline or preview worker.
Affected versionsGoogle advisories place desktop fixes at 149.0.7827.53/.54 for Windows and Mac and 149.0.7827.53 for Linux; affected builds are earlier than those versions.
Fixed versionsPatch to 149.0.7827.53+ on Linux and 149.0.7827.53/.54+ on Windows/macOS. I found no distro-specific backport details in the reviewed sources.
Exposure realityThis is not directly internet-scannable like a server daemon. Exposure usually rides inside apps that call chrome --headless, use Puppeteer, or perform PDF/screenshot rendering.
Operational amplifiersRisk goes up fast if your environment has public preview endpoints, URL-to-PDF services, screenshot APIs, crawler farms, or CI jobs that open attacker-supplied pages.
DisclosureDisclosed 2026-06-04 (user-supplied intel); Chrome stable update appeared 2026-05-29 for Windows/Mac early stable, and the Canadian advisory references Google's 2026-06-02 publication.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (6.4/10)

The decisive downward pressure is exposure narrowing: the bug matters only where enterprises run Headless Chrome to render attacker-controlled content. That is a real attack surface, but it is a much smaller and more controllable population than the vendor CVSS suggests for a generic Chrome RCE.

HIGH Version and patch boundary
MEDIUM Real-world exposure assessment for typical enterprises
LOW Exploit maturity details, because no reviewed public PoC or exploitation reporting was found

Why this verdict

  • Starts at vendor HIGH 8.8 because unauthenticated remote content can trigger code execution with severe CIA impact.
  • Downward adjustment: requires Headless mode. That implies a narrower target set than normal user browsing; many enterprises have thousands of Chrome installs but only dozens of headless render nodes.
  • Downward adjustment: requires attacker-controlled render input. If your jobs only render trusted templates or internal pages, the attack chain never starts.
  • Downward adjustment: commonly post-app-friction, not raw internet reach. The attacker usually needs a preview/export/crawl feature, CI workflow, or embedded automation service that accepts untrusted content.
  • Further downward adjustment: no KEV, no reviewed exploitation evidence, very low EPSS. That weakens the case for emergency prioritization absent local exposure amplifiers.
  • Still not LOW because exposed render farms can be quietly valuable**: service tokens, internal reachability, CI artifacts, screenshots/PDFs, and cloud metadata can all turn a 'niche' browser bug into an infrastructure incident.

Why not higher?

This is not a broad, no-click, fleet-wide desktop event. The chain narrows hard on two prerequisites: Headless Chrome must be in use, and it must be fed attacker-controlled HTML or URLs. Without evidence of active exploitation or a public PoC, calling this HIGH for most enterprises would overweight theoretical impact and underweight deployment reality.

Why not lower?

A lot of modern infrastructure quietly depends on Headless Chrome for rendering and automation, and those workers often sit near secrets, CI tokens, internal apps, or cloud APIs. If you do run internet-facing preview/PDF/screenshot features, the attacker position is effectively unauthenticated remote, which is too strong to dismiss as backlog-only hygiene.

05 · Compensating Control

What to do — in priority order.

  1. Inventory headless usage — Identify every workload that launches Chrome with --headless or through Puppeteer/ChromeDriver and map which ones accept untrusted URLs or HTML. For a MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window, but do this discovery work now so the patch does not get lost behind desktop-only assumptions.
  2. Fence untrusted render paths — Restrict preview, screenshot, and PDF generation features to allowlisted domains or sanitized templates where possible. This is the best risk reducer because it removes the attacker-controlled content prerequisite entirely; deploy as part of the same remediation cycle even though there is no formal mitigation SLA for MEDIUM.
  3. Constrain renderer identities — Run headless workers with low-privilege service accounts, minimal filesystem access, no developer tokens, and tightly scoped cloud IAM. That keeps successful browser-process execution from turning into infrastructure compromise; complete during the 365-day remediation window if not already in place.
  4. Segment and egress-filter render nodes — Limit outbound network access from screenshot/PDF/CI renderer subnets to only the domains and APIs they genuinely need. This cuts off follow-on recon and exfiltration if exploitation occurs, and it is especially important for internet-facing render services.
  5. Update bundled browser runtimes — Patch not just visible desktop Chrome but also container images, CI images, and app-bundled Chrome-for-Testing or Chromium copies. Headless stacks frequently pin old browser versions; for MEDIUM, clear those through the noisgate remediation SLA of ≤365 days.
What doesn't work
  • A WAF on your front-end does not reliably stop this if the app legitimately forwards attacker-supplied URLs/HTML to a backend renderer.
  • Patching only end-user desktop Chrome misses the actual risk if your vulnerable copy lives inside containers, build agents, or app bundles.
  • Browser hardening meant for interactive users—homepage locks, extension policy, Safe Browsing prompts—does little for unattended Headless jobs.
06 · Verification

Crowdsourced verification payload.

Run this on the target host or container image that may execute Chrome Headless. Invoke it with python3 check_cve_2026_10928.py or python3 check_cve_2026_10928.py --version "Google Chrome 149.0.7827.52"; no admin rights are required, but local read/execute access to Chrome binaries helps automatic discovery.

noisgate-verify.py
PYTHONREAD-ONLYSAFE
#!/usr/bin/env python3
# check_cve_2026_10928.py
# Determine likely exposure to CVE-2026-10928 by checking installed Chrome/Chromium version.
# Outputs exactly one of: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

import os
import platform
import re
import shutil
import subprocess
import sys

PATCH_WIN_MAC = (149, 0, 7827, 54)   # conservative threshold for Windows/macOS
PATCH_LINUX   = (149, 0, 7827, 53)

COMMON_COMMANDS = [
    "google-chrome",
    "google-chrome-stable",
    "chromium",
    "chromium-browser",
    "chrome",
    "msedge",
]

COMMON_PATHS = [
    "/usr/bin/google-chrome",
    "/usr/bin/google-chrome-stable",
    "/usr/bin/chromium",
    "/usr/bin/chromium-browser",
    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
    r"C:\Program Files\Google\Chrome\Application\chrome.exe",
    r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
]


def parse_version(text):
    m = re.search(r"(\d+)\.(\d+)\.(\d+)\.(\d+)", text)
    if not m:
        return None
    return tuple(int(x) for x in m.groups())


def version_to_str(v):
    return ".".join(str(x) for x in v) if v else "unknown"


def cmp_version(a, b):
    return (a > b) - (a < b)


def run_cmd(cmd):
    try:
        p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=8)
        out = (p.stdout or "") + "\n" + (p.stderr or "")
        return out.strip()
    except Exception:
        return ""


def find_version_from_commands():
    checked = []
    for c in COMMON_COMMANDS:
        path = shutil.which(c)
        if not path:
            continue
        checked.append(path)
        out = run_cmd([path, "--version"])
        v = parse_version(out)
        if v:
            return v, path, checked
    return None, None, checked


def find_version_from_paths():
    checked = []
    for p in COMMON_PATHS:
        if os.path.exists(p):
            checked.append(p)
            out = run_cmd([p, "--version"])
            v = parse_version(out)
            if v:
                return v, p, checked
    return None, None, checked


def threshold_for_platform():
    system = platform.system().lower()
    if system == "linux":
        return PATCH_LINUX, "Linux"
    if system == "darwin":
        return PATCH_WIN_MAC, "macOS"
    if system == "windows":
        return PATCH_WIN_MAC, "Windows"
    return PATCH_WIN_MAC, platform.system()


def main():
    explicit = None
    args = sys.argv[1:]
    if "--version" in args:
        try:
            idx = args.index("--version")
            explicit = args[idx + 1]
        except Exception:
            print("UNKNOWN")
            sys.exit(2)

    threshold, os_name = threshold_for_platform()

    version = None
    source = None
    checked = []

    if explicit:
        version = parse_version(explicit)
        source = "--version argument"
    else:
        version, source, checked = find_version_from_commands()
        if not version:
            version, source, checked2 = find_version_from_paths()
            checked.extend(checked2)

    if not version:
        print("UNKNOWN")
        sys.exit(2)

    # CVE-2026-10928 affects Chrome prior to 149.0.7827.53 on Linux
    # and prior to 149.0.7827.53/.54 on Windows/macOS.
    # We use 149.0.7827.54 as the conservative patched threshold on Windows/macOS.
    if cmp_version(version, threshold) < 0:
        print("VULNERABLE")
        sys.exit(1)
    else:
        print("PATCHED")
        sys.exit(0)


if __name__ == "__main__":
    main()
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: separate desktop Chrome from headless Chrome in your asset view, then find every PDF/screenshot/preview/crawler/CI workload that renders untrusted content and verify whether it is still below 149.0.7827.53/54. For this MEDIUM reassessment there is no noisgate mitigation SLA — go straight to the 365-day remediation window; use that noisgate remediation SLA to patch all affected headless workers, containers, and bundled runtimes within 365 days, sooner if those services are internet-facing or process attacker-submitted URLs/HTML.

Sources

  1. Chrome Releases - Early Stable Update for Desktop
  2. Chrome Headless mode documentation
  3. Puppeteer headless mode guide
  4. Canadian Centre for Cyber Security advisory AV26-544
  5. CISA Known Exploited Vulnerabilities Catalog
  6. FIRST EPSS overview
  7. Quanteta CVE entry showing CVSS/vendor metadata
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.