A critical pre-authentication remote code execution (RCE) vulnerability, CVE-2026-39987, has been discovered in Marimo, a widely used reactive Python notebook framework with over 19,600 GitHub stars.
Allowing unauthenticated attackers to seize full root-level shell access via a single WebSocket connection, with active exploitation confirmed in the wild less than 10 hours after public disclosure.
Marimo Re-Authentication RCE Vulnerability
Tracked as CVE-2026-39987 and assigned a CVSS score of 9.3 (Critical), this vulnerability resides in Marimo’s integrated terminal WebSocket endpoint. /terminal/ws.
The flaw is classified under CWE-306: Missing Authentication for Critical Function, a deceptively simple but catastrophically impactful design oversight. Every version of Marimo up to and including 0.20.4 is affected, with the patch delivered in the 0.23.0 release.
Security researchers at Sysdig Threat Research Team (TRT) and independent reporter q1uf3ng are credited with uncovering and responsibly disclosing the vulnerability.
Root Cause: WebSocket Authentication Bypass
The core issue is a missing authentication check on the /terminal/ws WebSocket endpoint in marimo/_server/api/endpoints/terminal.py.
Marimo uses Starlette’s AuthenticationMiddleware, which flags unauthenticated users but critically does not reject WebSocket connections outright.
Actual authentication enforcement must be implemented at the endpoint level, either via a @requires() decorator or an explicit validate_auth() call.
While other WebSocket endpoints /ws correctly invoke WebSocketConnectionValidator.validate_auth() Before proceeding, the terminal endpoint performs only two checks: whether the server is running in edit mode and whether the platform supports terminal operations.
After those checks pass, it immediately calls websocket.accept() and invokes pty.fork() to spawn a full pseudo-terminal (PTY) shell, all without verifying the identity of the connecting client.
This means even when Marimo is configured with a randomly generated access token (authentication enabled), the /terminal/ws endpoint bypasses that protection entirely, granting immediate shell access to any network-reachable attacker.
Attack Chain and Real-World Impact
The exploit chain is alarmingly straightforward, requiring no credentials, no token, and no user interaction:
- Attacker connects to
ws://TARGET:2718/terminal/wswithout any authentication headers - Server unconditionally accepts the WebSocket handshake
pty.fork()spawns an interactive PTY child process- Attacker receives a fully interactive shell
- In default Docker deployments, commands execute as root (
uid=0(root))
A proof-of-concept (PoC) exploit requires fewer than 20 lines of Python using the standard websocket library. Within a single connection, an attacker can execute arbitrary system commands, exfiltrate sensitive data, pivot laterally across infrastructure, or deploy persistent backdoors.
Real-world deployment patterns compound the severity. Researchers at Endorlabs performed internet-wide reconnaissance against 186 exposed Marimo instances and found approximately 30 (~16%) actively accepting unauthenticated /terminal/ws Handshakes.
Provide a conservative lower bound on the attack surface, as many instances sit behind corporate firewalls but may still be reachable via misconfigured cloud security groups or VPN access.
Exploitation Timeline: Zero to Shell in 10 Hours
Sysdig documented active exploitation in the wild within approximately 10 hours of the vulnerability’s public disclosure on April 8, 2026. Threat actors leveraged the short window between disclosure and patching to target exposed Marimo edit servers.
Observed post-exploitation activity included credential theft and lateral movement within cloud environments. This rapid weaponization underscores the critical importance of immediate patching for internet-facing Marimo deployments.
Affected Versions and Patch Details
| Detail | Information |
|---|---|
| CVE ID | CVE-2026-39987 |
| CVSS Score | 9.3 (Critical) |
| Affected Versions | All Marimo versions < 0.23.0 (advisory text: ≤ 0.20.4) |
| Patched Version | 0.23.0 |
| CWE | CWE-306: Missing Authentication for Critical Function |
| Attack Vector | Network (unauthenticated WebSocket) |
All versions before 0.23.0 should be considered vulnerable, as the advisory’s structured version range consumed by tools like Dependabot, pip-audit, and OSV reflects < 0.23.0.
Remediation and Mitigation
Organizations running Marimo must act immediately. The primary remediation is upgrading to version 0.23.0 or later, which aligns /terminal/ws authentication with the validated pattern used by other WebSocket endpoints.
pip install --upgrade marimo
Beyond patching, defenders should enforce the following hardening measures:
- Do not expose Marimo edit mode to untrusted or public networks that are bound to
127.0.0.1instead of0.0.0.0 - Deploy behind authenticated reverse proxies, VPNs, or private subnets
- Disable terminal functionality unless explicitly required for your workflow
- Audit all WebSocket endpoints in custom or forked deployments for missing
validate_auth()calls - Monitor network traffic for unexpected WebSocket connections to port 2718
FAQs
Q1: What is CVE-2026-39987?
It is a critical (CVSS 9.3) pre-authentication RCE vulnerability in Marimo’s /terminal/ws WebSocket endpoint that grants full root shell access without any credentials.
Q2: Which versions of Marimo are affected by CVE-2026-39987?
All Marimo versions before 0.23.0 are vulnerable, with the advisory specifically noting versions up to and including 0.20.4.
Q3: Has CVE-2026-39987 been exploited in the wild?
Yes, Sysdig TRT confirmed active exploitation within approximately 10 hours of public disclosure on April 8, 2026, with observed credential theft in cloud environments.
Q4: How do I fix the CVE-2026-39987 vulnerability in Marimo?
Immediately upgrade to Marimo 0.23.0 via pip install --upgrade marimo and avoid exposing the Marimo edit server to public or untrusted networks.
Site: http://thecybrdef.com