A critical missing authentication check in Marimo's terminal WebSocket endpoint (/terminal/ws) allowed any attacker on the internet to obtain a full interactive shell — no credentials, no exploit code needed.
Marimo is used by data science, ML, and analytics teams — environments rich with cloud credentials, SSH keys, API tokens, and sensitive notebooks. One unauthenticated connection = full system access.
Any internet-reachable Marimo instance running version 0.22.5 or earlier should be treated as fully compromised. Upgrade to 0.23.0 immediately and audit for credential exfiltration.
Marimo is a modern open-source Python notebook framework — a reactive alternative to Jupyter used widely by data science, machine learning, and analytics teams. With nearly 20,000 GitHub stars, it has become a trusted tool for running and sharing Python-based analyses, often deployed in cloud containers where team members can collaborate over the network.
On April 8, 2026, Marimo's maintainers published a security advisory for CVE-2026-39987, a critical pre-authentication remote code execution vulnerability. The flaw exists in the application's integrated terminal feature: a WebSocket endpoint at /terminal/ws that provides a fully interactive command-line shell to connected users — except it never checks whether the connecting party is actually authorized.
Unlike every other WebSocket endpoint in Marimo, which correctly calls an authentication validation function before accepting connections, the terminal endpoint skipped this check entirely. Any attacker who could reach the server over the network could simply open a WebSocket connection and immediately receive a full PTY (pseudo-terminal) shell running as the Marimo process user.
The Authentication Asymmetry
Marimo uses Starlette's AuthenticationMiddleware, which marks unauthenticated users but does not actively block WebSocket connections — enforcement depends on @requires() decorators or explicit validate_auth() calls. The /terminal/ws endpoint had neither. The /ws endpoint on the same server correctly called validate_auth(). One line of missing code created a critical security gap.
/terminal/ws WebSocket on Sysdig's honeypot. Manual reconnaissance begins — the attacker explores the file system to understand the environment..env files, searches for SSH keys, and reads various configuration files. The operation is manual, deliberate, and focused purely on credential theft.The vulnerability originates in marimo/_server/api/endpoints/terminal.py at lines 340–356, where websocket.accept() is called without any prior authentication. The terminal endpoint only checks two conditions before granting shell access: whether the server is in the correct running mode, and whether the platform supports terminal functionality. No user identity check. No token validation.
async def terminal_ws(websocket: WebSocket):
# Only checks mode + platform — NO auth validation
if running_mode == "edit" and platform_supports_terminal:
await websocket.accept() # ← Full PTY shell granted immediately
# /ws endpoint — SECURE (for comparison)
async def ws_endpoint(websocket: WebSocket):
await WebSocketConnectionValidator.validate_auth(websocket) # ← Auth enforced
await websocket.accept()
The exploit is as simple as it gets: open a WebSocket handshake to the target's /terminal/ws endpoint. The advisory itself documented a complete working path. No specialized tooling required — any WebSocket client, including common browser developer tools, can execute the attack.
In default Docker deployments, the Marimo process typically runs as root, meaning exploitation yields unrestricted system-level access. The attacker runs as the most privileged user on the container or host from the moment of connection.
Why Data Science Environments Are High-Value Targets
Notebook servers like Marimo are deployed in environments that are intentionally rich in sensitive data: cloud provider API keys in .env files, AWS/GCP/Azure credentials, SSH keys for accessing production systems, database connection strings, ML model weights, and proprietary datasets. A single unauthenticated shell on a Marimo instance can yield credentials that provide lateral movement across an entire cloud infrastructure.
Scope of Exposure
Marimo's default server port is TCP 2718. Research indicates that while direct internet listeners are limited in large-scale scans, the real exposure is in cloud-hosted notebooks, internal platforms without network segmentation, and Kubernetes clusters where Marimo is exposed to shared networks. Any instance reachable by an attacker — even via a lateral movement path — is fully exploitable without credentials.
For leadership teams, the key framing is that developer and data science tooling has historically received less security scrutiny than customer-facing infrastructure — yet it sits at the intersection of source code, cloud access, and sensitive business data. This incident demonstrates that gap is actively exploited.
Full Credential Exfiltration
Cloud keys, database passwords, API tokens, and SSH keys in notebook environments should be assumed stolen if the instance was internet-reachable during the exposure window.
Cloud Infrastructure Access
Notebook hosts typically reach databases, object stores, internal HTTP services, and peer systems. Stolen credentials enable attackers to pivot from a notebook server to production cloud infrastructure.
Sensitive Notebooks & Models
Proprietary ML models, training datasets, business logic embedded in notebooks, and analytical outputs may be exfiltrated or tampered with silently.
Backdoor Potential
While this attacker chose not to install persistence mechanisms, the open shell access enables any form of follow-on activity — miners, backdoors, ransomware — at the attacker's discretion.
| Risk Area | Severity | Description |
|---|---|---|
| Pre-Auth RCE | Critical | Zero authentication required. Any attacker who can reach the port gets an immediate interactive shell. Attack complexity is effectively none. |
| Credential Theft | Critical | Cloud API keys, SSH keys, .env secrets, and database passwords in notebook environments should be treated as fully compromised. |
| Root-Level Access | Critical | Default Docker deployments run Marimo as root. Exploitation yields the highest possible privilege level on the system immediately. |
| Speed of Exploitation | Critical | Under 10 hours from public disclosure to confirmed in-the-wild exploitation, with no PoC code available — built directly from the advisory text. |
| Cloud Lateral Movement | Critical | Notebook environments have high connectivity to cloud services. Credential theft from one Marimo instance can cascade across entire cloud accounts. |
| Detection Difficulty | High | Manual terminal access over WebSocket produces minimal default logging. Without dedicated endpoint detection, the attack may go unnoticed. |
| Patch Adoption Lag | High | Data science infrastructure is often not centrally managed like front-line web apps — patches may be applied slowly or inconsistently across teams. |
/terminal/ws. Look for sessions originating from IP addresses outside your organization's known ranges, especially around April 8–10, 2026.