CVE-2026-8000 is an actively exploited remote code execution in ChromeDriver, patched in Chrome 148.0.7778.96 on May 6, 2026. A crafted HTML page can send a malicious WebDriver request to ChromeDriver's local control port (9515) and slip past insufficient input validation, dropping a VBScript payload on Windows runners. Google TAG attributed in-the-wild exploitation to an APT group targeting technology companies. CI pipelines that run Selenium Grid alongside headless PDF generation are directly in scope.
What CVE-2026-8000 actually is
CVE-2026-8000 is an insufficient input validation flaw in ChromeDriver that lets a malicious web page issue WebDriver protocol commands to the ChromeDriver process running on the same host. ChromeDriver is the HTTP server that implements the W3C WebDriver protocol for Chrome and Chromium. By default it listens on localhost:9515 and accepts JSON requests on the loopback interface. The Selenium client (in any language) speaks to that port to drive the browser.
The validation gap is in how ChromeDriver parses fields of an incoming WebDriver request. Several input fields, including paths used by the file upload endpoint and identifiers used by the script execution endpoint, were not validated against the expected character set or length. A request that combined an unchecked path with a crafted script payload could escape the intended sandbox boundary and execute attacker-controlled code with the privileges of the ChromeDriver process. Because ChromeDriver runs as the user that started the Selenium session, that is typically the CI runner user, which has read and write access to the workspace, the test artifacts, and any secrets the CI exposes through environment variables.
The exploit primitive is the same as classic cross-site request attacks on local services. A page that the headless Chrome instance navigates to can run JavaScript that issues a fetch to http://127.0.0.1:9515/session/{id}/file or /session/{id}/execute. CORS does not block the request because ChromeDriver does not enforce origin checks on its loopback endpoint. The Selenium session id is recoverable from the page context in several known ways, including by reading window properties the Selenium client sets on the page under test. Once the page has the session id and the validation gap, it has full WebDriver authority over the browser instance.
If your CI runs ChromeDriver and also navigates to attacker-influenced URLs (dependency previews, third-party documentation snapshots, customer-submitted test fixtures, HTML produced by a templating step that consumes user data), treat this as a paged incident. The exploit fires the moment the malicious page loads. The Selenium session is what the attacker hijacks; everything that session can do, the attacker can do.
The known in-the-wild attack
The documented exploit chain is short and quiet. Google TAG observed it against technology companies running Selenium-based test infrastructure on Windows hosts, and the same chain has been reproduced on instrumented honeypots and reported through Threat Radar and the CVE Threat Intelligence project. The community write-up on Windows Forum documents the patch guidance.
The chain in order:
| Step | What happens | Where it lands |
|---|---|---|
| 1 | Test job navigates to attacker-controlled HTML (malicious dependency docs page, polluted fixture, compromised CDN) | Inside the Selenium-driven Chrome instance |
| 2 | Embedded JavaScript reads the Selenium session id from page properties and recovers ChromeDriver's port | Same-origin scripting context |
| 3 | JavaScript issues a fetch to http://127.0.0.1:9515/session/{id}/... with crafted payloads that abuse the unchecked input fields | ChromeDriver process on the runner |
| 4 | ChromeDriver writes a .vbs file to a temp directory and executes it via Windows Script Host | Runner file system |
| 5 | VBScript pulls a second-stage payload, harvests credentials from the runner environment (env vars, mounted secrets, ssh keys), and installs a scheduled task for persistence | Runner OS + lateral movement |
Step 4 is the Windows-specific link. The dropper relies on wscript.exe being available and on Windows Script Host being enabled, which is the default on Windows runners. The earlier steps are operating-system agnostic; a Linux or macOS variant of the post-exploitation phase is plausible but not in the public reports as of June 2026.
The persistence stage uses a scheduled task named after a common Chromium internal service, which is the signal Google TAG used to attribute the cluster. The credentials harvested in step 5 give the attacker access to the CI's deploy keys, container registry pushes, and any cloud provider tokens stored as job-level secrets, which is the actual prize.
Who is exposed in CI and PDF pipelines
Three configurations show up repeatedly in incident reports. Audit your runners against each before assuming you are clear.
Selenium Grid bound on a routable address. The Grid hub and nodes default to 0.0.0.0 so they can accept connections from each other. A node that runs ChromeDriver internally and exposes the WebDriver port on the same routable interface lets any page the browser visits reach the port at 127.0.0.1, but it also lets a co-tenant on the same network reach it directly. The fix is to bind ChromeDriver to loopback inside each node and to keep Grid traffic on a separate internal port.
Shared CI runners with multi-tenant access. Self-hosted runners that serve multiple repositories or organizations share the runner OS between jobs. A malicious pull request from one tenant can land a crafted page on the runner, and the next job from another tenant inherits whatever state the exploit left behind (modified PATH, scheduled tasks, registry keys on Windows). GitHub-hosted runners are not in this bucket because each job gets a fresh VM, but the Drone, Jenkins, and self-hosted Actions worlds are.
Dev machines running Chrome with --remote-debugging-port open. Developer machines that start Chrome with --remote-debugging-port=9222 for debugging and forget to add --remote-allow-origins=null expose the DevTools Protocol to any page the browser visits. This is not CVE-2026-8000 directly (DevTools Protocol is a different control plane than WebDriver), but it is the same class of bug and the same mitigation pattern, and several teams discovered both during the same audit.
The PDF generation angle is the second-class affected stack. Many test pipelines run visual regression tests that render a PDF, screenshot it, and diff against a baseline. The PDF is produced by the same Chrome process Selenium is driving, often through print_to_pdf over the WebDriver session. If the Selenium session is hijacked, the attacker can also call print_to_pdf and exfiltrate every PDF the pipeline has rendered in the current session, which for invoice or certificate test suites means leaked customer data even before the second-stage payload runs.
Patch and detect
Upgrade to Chrome 148.0.7778.96 (or any later stable release) and the matching ChromeDriver build. Chrome and ChromeDriver share the same version number, so the validation goal is identical for both binaries on the runner.
# Check ChromeDriver version on every runner.
chromedriver --version
# On Windows runners, from PowerShell:
& "C:\Program Files\Google\Chrome\Application\chrome.exe" --version
& "C:\path\to\chromedriver.exe" --versionFor Docker-based Selenium Grid, rebuild the base images. The official selenium/standalone-chrome and selenium/node-chrome tags floating at latest should be re-pulled after May 6, 2026; pinned tags (selenium/standalone-chrome:4.20.0-20260301) must be bumped to a release that bundles the patched Chrome:
docker pull selenium/standalone-chrome:latest
docker inspect selenium/standalone-chrome:latest \
| grep -i chrome_versionFor Selenium 4 BiDi users, the bundled selenium-chrome images use the same Chrome roll, so the same image bump covers both the classic WebDriver path and the BiDi path. The Selenium release notes for 4.21 and later document the corresponding Chromium pin.
For GitHub Actions, check the runner image release notes for the Chrome version. As of June 2026 the ubuntu-22.04, ubuntu-24.04, and windows-2022 images all bundle a Chrome at or above the patched floor. If your workflow installs Chrome manually via apt or setup-chrome, pin the version explicitly:
- uses: browser-actions/setup-chrome@v1
with:
chrome-version: "148.0.7778.96"Detection in past logs uses two signals. The first is unexpected WebDriver commands issued from a page context: ChromeDriver writes one line per command to its log (enabled with --log-path=chromedriver.log), and any POST /session/{id}/execute that fired between page.goto and the next harness step is suspicious. The second, on Windows, is wscript.exe or cscript.exe processes whose parent is chromedriver.exe. The Sysmon detection is straightforward:
<RuleGroup name="ChromeDriver dropper" groupRelation="and">
<ProcessCreate onmatch="include">
<ParentImage condition="end with">chromedriver.exe</ParentImage>
<Image condition="end with">wscript.exe</Image>
</ProcessCreate>
</RuleGroup>Pair this with a baseline of normal ChromeDriver child processes (typically just chrome.exe); any other child binary is worth investigating.
Hardening Selenium and ChromeDriver beyond patching
The patch closes CVE-2026-8000. The hardening below makes the next ChromeDriver CVE cheap to absorb. Six defenses, ranked roughly by impact, with the configuration knob for each.
| Defense | Configuration | Stops |
|---|---|---|
| Bind ChromeDriver to loopback only | chromedriver --allowed-ips=127.0.0.1 --port=9515 | Cross-host requests to the control port; does not stop same-host page-to-port requests |
| Restrict allowed origins | --allowed-origins=http://localhost,http://127.0.0.1 (ChromeDriver 110+) | Cross-origin browser requests from the page under test to ChromeDriver |
| Isolate each test job in a network namespace | docker run --network=none, then bring up a private bridge for the Grid node only | Outbound exfiltration; co-tenant reachability |
| Drop the WebDriver port after the test session ends | Wrapper script that terminates chromedriver on pytest teardown rather than leaving it idle | Long-tail exposure between test runs |
Run Chromium with --remote-allow-origins=null | Launch flag on every browser the harness starts | Cross-origin DevTools Protocol abuse, the sibling class of bug to this CVE |
| Monitor outbound connections from CI runners | Egress policy via iptables, Cilium, or AWS security groups, plus DNS logging | Second-stage payload download and command-and-control |
The two most cost-effective defenses are the first and third. Binding to loopback removes the multi-tenant attack surface entirely. Network namespacing removes the post-exploitation lateral movement path, which is the part that turns a renderer compromise into a CI breach. Both are one-line configuration changes that pay back across every future ChromeDriver and Chromium CVE.
Two warnings on the configuration. --whitelisted-ips was renamed to --allowed-ips in ChromeDriver 116; older Selenium tutorials still use the legacy flag, which ChromeDriver 116 and later accept for backward compatibility but mark as deprecated. The --allowed-origins flag rejects requests whose Origin header is not in the list; a malicious page that omits the Origin header (some fetch shapes do) bypasses this defense, so use it alongside binding rather than instead of binding.
Why headless CI PDF generation is the second-class affected stack
Visual regression on PDF outputs is a common CI step. A test renders an invoice, certificate, or report template, generates the PDF via print_to_pdf over the WebDriver session, and pixel-diffs it against a baseline image stored in the repo. Teams running this pattern often have a Selenium Grid that handles all browser-driven tests, including the PDF rendering ones. The Grid runs ChromeDriver, which is exactly the attack surface CVE-2026-8000 targets.
The risk multiplier is what the CI runner has access to that the production renderer does not. A production HTML-to-PDF service is usually scoped tightly: it can talk to its template store and its output bucket, and that is it. A CI runner has the deploy key for the repository, the signing certificate for the artifact, the container registry push token, the npm publish token, and any cloud provider role attached to the runner. A renderer compromise in production leaks the current request's PDF. A renderer compromise in CI leaks the keys to push a backdoored release to production. The blast radius is an order of magnitude larger.
The detection signal is similar but the window is different. Production renderers churn through requests and recycle workers; an exploit gets seconds to do its work. CI runners idle between test runs and may persist state across jobs on self-hosted setups; an exploit can install a scheduled task or modify a global config and wait for the next job to inherit it. The persistence stage in the documented chain is targeting exactly this property.
How PDF4.dev handles this class
PDF4.dev uses Playwright in a server-side pool, not ChromeDriver, and the control plane between the Playwright process and each Chromium worker runs over a Unix pipe with no listening socket. There is no WebDriver port for a malicious page to reach. Each worker runs in its own network namespace, with egress blocked except to the asset CDN and the font host. Workers are recycled every N renders, so any successful exploit against a renderer has seconds to operate before the process is torn down.
PDF4.dev customers do not run ChromeDriver and are not exposed to CVE-2026-8000. The platform's Playwright-over-pipe model, network-namespaced workers, and per-N-renders recycle policy close this entire class of bug architecturally. If you want the renderer security model off your team's plate, that is what the service handles.
This is not a sales pitch, it is the architectural answer to the bundled-control-plane problem. ChromeDriver's HTTP control port is the convenient cross-language interface that makes Selenium work in Java, Python, C#, and Ruby; it is also the attack surface that CVE-2026-8000 exploited. Tools that ship a tighter control plane (Playwright, Puppeteer over CDP pipe) trade language portability for the absence of this attack surface.
Frequently asked questions
The FAQ block in the frontmatter answers the eight most common questions on this CVE. The short version: yes you are probably exposed, yes all three operating systems need patching but Windows is the documented post-exploitation target, the safe binding is --allowed-ips=127.0.0.1, Playwright is not affected by this specific bug, Google TAG confirmed APT exploitation without naming the actor, and GitHub-hosted runners need a fresh runner image but are not in the multi-tenant scenario.
Takeaway
CVE-2026-8000 is the third high-severity Chromium-ecosystem CVE in 2026 that hits PDF-rendering pipelines, after CVE-2026-5287 in March and CVE-2026-2441 in May. The patch path is fast: bump Chrome and ChromeDriver to 148.0.7778.96, rebuild Selenium Grid images, and re-pull pinned tags. The hardening path is the lasting fix: bind ChromeDriver to loopback, restrict origins, namespace the network, and recycle the control-plane process between sessions.
The single most useful change for any team running Selenium Grid in production CI is to add a startup assertion that ChromeDriver is bound to 127.0.0.1 only and refuse to take traffic if it is bound on any other address. The next CVE in this class is already being researched; the goal is to make the response routine rather than urgent.
Start generating PDFs
Build PDF templates with a visual editor. Render them via API from any language in ~300ms.



