Table of Contents

CVE-2026-39987 is a missing authentication flaw in Marimo’s /terminal/ws WebSocket endpoint that grants any u (see also LangChain load_prompt file read)nauthenticated client a root-privileged PTY shell with a single handshake. Disclosed on April 8, 2026 with a CVSS 9.3 score, it affects all versions prior to 0.23.0 and was first exploited in the wild 9 hours and 41 minutes later — before a patch was widely deployed. (Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass · GHSA-2679-6mx9-h9xc, Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig)

What the Vulnerability Is: A Single WebSocket Handshake to Root

The flaw is CWE-306: Missing Authentication for Critical Function. Marimo’s /terminal/ws endpoint accepts WebSocket upgrade requests without verifying identity, spawning a PTY with the privileges of the running process. (Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass · GHSA-2679-6mx9-h9xc) Endor Labs tested 186 internet-reachable Marimo instances and found 30 — approximately 16% — accepted unauthenticated /terminal/ws handshakes. The testing stopped at the upgrade attempt, meaning the true exposure rate is a lower bound. (Root in One Request: Marimo’s Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labs)

Why the Auth Gap Existed: Starlette Middleware Is Not a WebSocket Security Strategy

Marimo relies on Starlette’s AuthenticationMiddleware, which marks failed WebSocket upgrades as UnauthenticatedUser but does not close the connection. Actual enforcement requires either a @requires(‘edit’) decorator or an explicit WebSocketConnectionValidator.validate_auth() call. (Root in One Request: Marimo’s Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labs)

The /ws endpoint implements both checks. The /terminal/ws endpoint implements neither. The fix, shipped in version 0.23.0 via PR #9098, adds validate_auth() to the terminal route, bringing it into alignment with the main WebSocket handler. (Release 0.23.0 · marimo-team/marimo — GitHub)

9h 41m: The Attack Timeline Reconstructed

The GitHub security advisory went live on April 8, 2026 at 21

UTC. Sysdig Threat Research observed the first exploitation on April 9 at 07
UTC from IP address 49.207.56.74 — a gap of 9 hours and 41 minutes, with no public proof-of-concept code available. (Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig)

The attacker opened with delimited PoC markers (---POC-START--- and ---POC-END---), ran id to confirm root access, and returned at 08

UTC to inspect the filesystem. Credential theft — extraction of an .env file containing AWS credentials and probing for SSH keys — completed in under three minutes of shell access. (Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig)

BleepingComputer reported that within 12 hours of disclosure, 125 IP addresses had begun reconnaissance. The initial campaign focused exclusively on credential exfiltration; no persistence mechanisms or cryptominers were deployed. (Critical Marimo pre-auth RCE flaw now under active exploitation — BleepingComputer)

The CVE Treadmill Widens: Marimo Joins vLLM, SGLang, and Langflow

Marimo is not an isolated case. Langflow CVE-2026-33017, a prior AI framework RCE, was exploited within 20 hours of disclosure. Marimo’s 9 hour 41 minute window is roughly 50% faster, despite Marimo having approximately 20,000 GitHub stars versus Langflow’s roughly 145,000. The speed implies attacker scanning is not purely proportional to deployment base size. (Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig)

The concurrent landscape for inference servers is equally active. SGLang CVE-2026-5760, disclosed in April 2026, carries a CVSS 9.8 score and enables RCE via malicious GGUF model files at /v1/rerank. SGLang also faces CVE-2026-3059 and CVE-2026-3060, both CVSS 9.8, where a ZMQ broker binds to all interfaces with zero authentication and executes pickle.loads() on any payload. vLLM carries CVE-2025-30165, a deserialization RCE with a CVSS 8.0 score. (SGLang CVE-2026-5760 (CVSS 9.8) Enables RCE via Malicious GGUF Model Files — The Hacker News)

The precedent for this pattern is the ShadowMQ vulnerability family identified by Oligo Security. A single ZMQ/pickle implementation flaw propagated via copy-paste across Meta Llama, NVIDIA TensorRT-LLM, vLLM, and Modular Max Server, yielding CVE-2024-50050, CVE-2025-30165, CVE-2025-23254, and CVE-2025-60455. Marimo has now joined the same advisory-to-exploit timeline that inference servers have occupied for more than a year. (ShadowMQ: How Code Reuse Spread Critical Vulnerabilities Across the AI Ecosystem — Oligo Security)

Why Docker + Root PTY Turns RCE Into Full Infrastructure Compromise

The credential theft speed is a function of container defaults. Docker containers run as root unless explicitly configured otherwise, and a PTY spawned inside such a container inherits that UID. The attacker in the Sysdig reconstruction needed only shell access to locate .env files and SSH keys. (Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig, Root in One Request: Marimo’s Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labs)

Teams running Marimo in Docker therefore face a compounding risk: the vulnerability itself is a missing auth check, but the environment turns that check into immediate cloud-credential exposure. Network segmentation and non-root UIDs are not optional hardening for notebook platforms; they are prerequisites for any internet-adjacent deployment. (Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig)

The Multi-Transport Obligation: Every Alternate Endpoint Is Now a Regression Test

The architectural lesson is not specific to Marimo. Starlette’s middleware marks the connection but does not police it; every WebSocket route, IPC channel, and debug endpoint must re-implement enforcement independently. (Root in One Request: Marimo’s Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labs)

For AI dev tools shipping multiple transports — a primary /ws for notebook sync, a /terminal/ws for shell access, perhaps a metrics or profiling port — the Marimo case establishes that auth inheritance cannot be assumed. Each alternate endpoint is an independently audited attack surface. Omitting validate_auth() from one route is the kind of oversight that regression testing should catch, but only if the test suite treats every transport as a first-class security boundary rather than an inherited afterthought. (Root in One Request: Marimo’s Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labs)

Mitigation Checklist: Upgrade, Segment, and Harden Container UIDs

  1. Upgrade to 0.23.0 or later. The patch adds validate_auth() to /terminal/ws, closing the gap. (Release 0.23.0 · marimo-team/marimo — GitHub)
  2. Verify deployment mode. Run mode and WASM deployments do not expose the endpoint at all. (Release 0.23.0 · marimo-team/marimo — GitHub)
  3. Run containers with non-root UIDs. A non-privileged PTY limits post-exploitation damage even if a similar flaw appears elsewhere. (Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdig)
  4. Segment network access. Internet-adjacent notebook instances should sit behind a proxy or within a VPC that restricts inbound connections to known sources. (Root in One Request: Marimo’s Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labs)

The 9 hour 41 minute window is a timestamp that will fade as patching progresses. The underlying pattern — a multi-transport tool with auth enforced on one route and silently skipped on another — is a design flaw that recurs wherever server-side AI tools add convenience endpoints without independent security review.

Frequently Asked Questions

Does CVE-2026-39987 affect Marimo notebooks running in read-only or WASM mode?

No. The /terminal/ws route is only registered in edit mode. Run mode serves a static snapshot with no PTY, and WASM notebooks execute client-side with no server-side terminal at all. If an instance sits behind an external authentication proxy, the proxy blocks unauthenticated traffic before it reaches Marimo, regardless of mode.

Why didn’t Starlette’s AuthenticationMiddleware block unauthenticated connections to /terminal/ws?

Starlette’s middleware only sets the identity on the request scope — closing the connection is the application’s job. Marimo enforced auth on /ws via both validate_auth() and @requires(‘edit’), but /terminal/ws had neither guard, so connections marked as UnauthenticatedUser stayed open. The middleware cannot close what it only labels.

How does Marimo’s exploitation speed compare to other AI framework CVEs?

At ~9.7 hours post-disclosure, Marimo was exploited roughly 50% faster than Langflow CVE-2026-33017 (~20 hours), despite having ~7x fewer GitHub stars. On the inference-server side, SGLang alone accumulated three CVSS 9.x RCEs in the same period, and the ShadowMQ family shows how shared implementation patterns propagate similar gaps across tools from different vendors — making exploit speed an ecosystem property, not a project-specific one.

What specific changes do teams running Marimo in Docker need to make?

Upgrade to 0.23.0+ to close the auth gap, then address the compounding container risk: Docker defaults to root, so any future PTY flaw immediately yields full container compromise. Running as a non-root UID limits blast radius, and placing the instance behind a proxy or within a VPC reduces the chance that an unauthenticated handshake reaches the server at all.

Is the multi-transport auth gap a Marimo-specific problem or a broader AI tooling risk?

Ecosystem-wide. The ShadowMQ research traced a single ZMQ/pickle flaw across Meta Llama, NVIDIA TensorRT-LLM, vLLM, and Modular Max Server — four vendors, four CVEs. Marimo’s gap differs in mechanism (Starlette WebSocket auth vs. ZMQ binding) but follows the same pattern: a convenience endpoint added without independent security review. Any team evaluating AI tooling should audit every transport route, not just the primary one.

Sources

  1. Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass · GHSA-2679-6mx9-h9xcprimaryaccessed 2026-04-23
  2. Marimo OSS Python Notebook RCE: From Disclosure to Exploitation in Under 10 Hours — Sysdigprimaryaccessed 2026-04-23
  3. Root in One Request: Marimo's Critical Pre-Auth RCE (CVE-2026-39987) — Endor Labsanalysisaccessed 2026-04-23
  4. Release 0.23.0 · marimo-team/marimo — GitHubvendoraccessed 2026-04-23
  5. Critical Marimo pre-auth RCE flaw now under active exploitation — BleepingComputercommunityaccessed 2026-04-23
  6. Weaponized CVE-2026-39987 Pushes Blockchain Backdoor Through Hugging Face — GBHackerscommunityaccessed 2026-04-23
  7. SGLang CVE-2026-5760 (CVSS 9.8) Enables RCE via Malicious GGUF Model Files — The Hacker Newsanalysisaccessed 2026-04-23
  8. ShadowMQ: How Code Reuse Spread Critical Vulnerabilities Across the AI Ecosystem — Oligo Securityanalysisaccessed 2026-04-23

Enjoyed this article?

Stay updated with our latest insights on AI and technology.