
Erkennungs- und Exploitation-Toolkit für CVE-2026-39987, eine Pre-Auth-RCE in Marimo-Notebooks. Enthält einen Python-Scanner und ein Nmap-NSE-Skript zur Identifizierung verwundbarer Instanzen über WebSocket-Endpunktprüfungen.
Eine Remote-Code-Execution-Schwachstelle ohne Authentifizierung in Marimo, einem Open-Source-Python-Notebook für Data Science und KI/ML. Der Terminal-WebSocket-Endpunkt (/terminal/ws) überspringt die Authentifizierungsprüfung vollständig, während der benachbarte Notebook-Endpunkt (/ws) diese korrekt durchsetzt. Ein nicht authentifizierter Angreifer kann sich mit /terminal/ws verbinden und ohne jegliche Anmeldedaten eine auf dem Hostsystem erhalten.
Innerhalb von 10 Stunden nach der Offenlegung wurde die Schwachstelle bereits in freier Wildbahn ausgenutzt. Angreifer stahlen AWS-Anmeldedaten in weniger als 3 Minuten.
Betrifft Marimo <= 0.20.4. Behoben in Marimo 0.23.0.
| Feld | Detail |
|---|---|
| CVE-ID | CVE-2026-39987 |
| Anbieter | Marimo Project |
| Produkt | Marimo (Python-Notebook) |
| Betroffene Versionen | <= 0.20.4 |
| CVSS v3.1 | 9.3 (Kritisch) |
| CWE | CWE-306 — Fehlende Authentifizierung für kritische Funktion |
| Angriffsvektor | Netzwerk |
| Authentifizierung | Keine erforderlich |
| Benutzerinteraktion | Keine |
| Reifegrad des Exploits | Aktiv in freier Wildbahn ausgenutzt |
| Zeit bis zur Ausnutzung | ~10 Stunden nach Offenlegung |
| Behoben in | Marimo 0.23.0 |
Marimo ist ein Open-Source-reaktives Python-Notebook, das als moderne Alternative zu Jupyter entwickelt wurde. Es ist für Data Science, KI/ML-Experimente und interaktive Datenanalyse konzipiert. Zu den wichtigsten Funktionen gehören automatische Abhängigkeitsverfolgung, reproduzierbare Ausführung und eine sauberere Entwicklererfahrung im Vergleich zu herkömmlichen Notebooks.
Marimo gewinnt in der Python- und KI/ML-Community schnell an Bedeutung, insbesondere bei Praktikern, die strukturiertere Notebook-Workflows wünschen, als Jupyter sie bietet.
Wie alle Notebook-Umgebungen haben Marimo-Instanzen typischerweise Zugriff auf sensible Ressourcen: Cloud-Anmeldedaten (AWS, GCP, Azure), Datenbank-Verbindungsstrings, API-Keys für KI-Dienste (OpenAI, Anthropic usw.) sowie internen Netzwerkzugriff. Im Gegensatz zu herkömmlichen Webanwendungen sind Notebooks darauf ausgelegt, beliebigen Code auszuführen. Das ist ihr Kernzweck.
Diese Kombination macht jede Authentifizierungsumgehung in einer Notebook-Umgebung besonders verheerend.``` Typical Marimo Deployment:
┌──────────────┐ ┌────────────────────────────────┐ │ │ HTTP │ Marimo Server │ │ Browser │────────>│ │ │ (User) │ │ ┌──────────────────────────┐ │ │ │<────────│ │ /ws (Notebook) │ │ └──────────────┘ WS │ │ ✅ validate_auth() │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ /terminal/ws │ │ │ │ ❌ NO AUTH CHECK │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ Python Environment │ │ │ │ .env files │ │ │ │ AWS credentials │ │ │ │ API keys │ │ │ └──────────────────────────┘ │ └────────────────────────────────┘
---
## Schwachstellen-Tiefenanalyse
### Die beiden WebSocket-Endpunkte
Der Marimo-Server implementiert mehrere WebSocket-Endpunkte für verschiedene Funktionen. Der entscheidende Unterschied zwischen den beiden Hauptendpunkten ist das Vorhandensein (oder Fehlen) einer Authentifizierungsprüfung:```
Authentication Flow Comparison:
/ws (Notebook WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ validate_auth │───>│ Accept │───>│ Notebook │
└─────────┘ └───────┬───────┘ └──────────┘ └───────────┘
│
❌ Reject if
not authenticated
/terminal/ws (Terminal WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ Check mode & │───>│ Accept │───>│ PTY Shell │
└─────────┘ │ platform only │ └──────────┘ └───────────┘
└───────────────┘
⚠️ No auth check!
Anyone gets a shell!
Der Notebook-Endpunkt (/ws) ruft korrekt validate_auth() auf, um die Identität des Benutzers zu überprüfen, bevor WebSocket-Verbindungen akzeptiert werden. Dies ist das erwartete Sicherheitsverhalten.
Der Terminal-Endpunkt (/terminal/ws) prüft lediglich, ob sich der Server im laufenden Modus befindet und ob die Plattform Terminalfunktionen unterstützt. Er ruft niemals validate_auth() auf. Nachdem diese grundlegenden Prüfungen bestanden sind, akzeptiert er die Verbindung und erstellt eine vollständige PTY-Sitzung (Pseudo-Terminal).```python
async def websocket_connect(self, message): await self.validate_auth() # ✅ Checks authentication await self.accept() # ... notebook communication
async def websocket_connect(self, message): if not self.is_running_mode(): # Only checks mode await self.close() return if not self.is_platform_supported(): # Only checks platform await self.close() return await self.accept() # ❌ No auth! Anyone gets a shell # ... PTY shell creation
Dies ist **CWE-306: Fehlende Authentifizierung für kritische Funktionen**. Der gefährlichste Endpunkt auf dem Server (derjenige, der eine interaktive Shell bereitstellt) hat keinerlei Authentifizierung.
### Der Angriff: Von der Beratung zu AWS-Keys in 3 Minuten
Die Ausnutzungs-Zeitleiste zeigt, wie schnell moderne Bedrohungsakteure agieren:```
┌──────────────────────────────────────────────────────────────┐
│ CVE-2026-39987 Timeline │
├──────────────────────────────────────────────────────────────┤
│ │
│ T+0h Advisory published │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ T+9h First exploit built from advisory │
│ T+10h Exploitation in the wild confirmed │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ T+10h 0m Attacker connects to /terminal/ws │
│ T+10h 1m Full PTY shell obtained │
│ T+10h 2m .env file located and read │
│ T+10h 3m AWS keys exfiltrated │
│ Total attack time: ~3 minutes │
└──────────────────────────────────────────────────────────────┘
Der Angriff selbst ist trivial einfach:``` Step 1: Attacker opens WebSocket connection to /terminal/ws (No authentication needed, no special tools required)
Step 2: Server creates a PTY (pseudo-terminal) session Attacker now has an interactive shell
Step 3: Attacker runs commands: $ cat .env AWS_ACCESS_KEY_ID=AKIA... AWS_SECRET_ACCESS_KEY=... DATABASE_URL=postgres://... OPENAI_API_KEY=sk-...
Step 4: Credentials exfiltrated Attacker now has cloud access, database access, and API keys for AI services
Total time: under 3 minutes Authentication required: none Tools required: any WebSocket client
Keine Exploit-Entwicklung nötig. Kein Shellcode. Keine Speicherkorruption. Nur ein WebSocket-Client und eine fehlende Authentifizierungsprüfung.
---
## Auswirkungsanalyse
**Unmittelbare Auswirkung auf den Marimo-Host:**
- Vollständige interaktive Shell mit den Rechten des Marimo-Prozesses
- Zugriff auf alle Dateien, die für den Prozess lesbar sind (Quellcode, Daten, Anmeldedaten)
- Zugriff auf Umgebungsvariablen mit API-Schlüsseln und Geheimnissen
- Möglichkeit, beliebige Befehle auf dem Hostsystem auszuführen
**Offenlegung von Anmeldedaten (das primäre Angriffsziel):**
- AWS-Zugriffsschlüssel und geheime Schlüssel aus `.env`-Dateien oder Umgebungsvariablen
- GCP/Azure-Dienstkontenanmeldedaten
- Datenbank-Verbindungsstrings mit Passwörtern
- OpenAI-, Anthropic- und andere KI-Dienst-API-Schlüssel
- SSH-Schlüssel und anderes Authentifizierungsmaterial
**Nachgelagerte Auswirkung (über gestohlene Anmeldedaten):**
- Unbefugter Zugriff auf Cloud-Infrastruktur (EC2, S3, Lambda usw.)
- Datendiebstahl aus Cloud-Speicher und Datenbanken
- Ressourcenmissbrauch (Cryptomining, Diebstahl von KI-API-Guthaben)
- Laterale Bewegung in Cloud- und On-Premise-Netzwerke
**Risikoverstärkende Faktoren:**
- Notebook-Umgebungen sind dafür ausgelegt, beliebigen Code auszuführen (das ist ihr Zweck)
- Data-Science-Umgebungen haben typischerweise breiten Cloud-Zugriff für Trainingsaufträge
- Viele Marimo-Instanzen sind für Zusammenarbeit und Remote-Arbeit im Internet exponiert
- Sicherheitshärtung ist in Forschungs-/Experimentierumgebungen oft ein nachträglicher Gedanke
---
## Betroffene Versionen
| Version | Status |
|---------|--------|
| Marimo 0.23.0+ | **Behoben** |
| Marimo 0.20.5 bis 0.22.x | **Wahrscheinlich verwundbar** (zwischen Advisory-Bereich und Fix) |
| Marimo <= 0.20.4 | **Verwundbar** (bestätigter Bereich) |
---
## Das große Ganze: KI/ML-Toolchain unter Beschuss
CVE-2026-39987 ist kein Einzelfall. Es ist Teil eines klaren Musters, das im April 2026 auftrat:
| CVE | Produkt | Typ | Status |
|:---|:---|:---|:---|
| **CVE-2026-39987** | Marimo | Pre-Auth-RCE (WebSocket) | In 10 Stunden ausgenutzt |
| **CVE-2026-33017** | Langflow | RCE | CISA KEV (26. März) |
| **CVE-2026-5059** | aws-mcp-server | Command-Injection-RCE | Öffentliches Advisory |
| **TorchGeo** | TorchGeo | eval()-RCE | Öffentliches Advisory |
Vier KI/ML-Entwicklungstools wurden in einem einzigen Monat mit kritischen RCE-Schwachstellen getroffen. Die KI/ML-Entwicklungspipeline wird zur neuen Schatten-IT: Tools, die mit breitem Zugriff, minimaler Sicherheitsaufsicht und umfangreichen Anmeldedatenspeichern bereitgestellt werden.```
┌─────────────────────────────────────────────────┐
│ Why AI/ML Tools Are Prime Targets │
├─────────────────────────────────────────────────┤
│ │
│ 1. DESIGNED to execute arbitrary code │
│ (that's literally what notebooks do) │
│ │
│ 2. Run with elevated privileges │
│ (GPU access, cloud SDKs, network access) │
│ │
│ 3. Contain high-value credentials │
│ (AWS keys, API tokens, DB connections) │
│ │
│ 4. Often exposed to the network │
│ (for collaboration and remote access) │
│ │
│ 5. Security hardening is an afterthought │
│ (focus on features and UX, not security) │
│ │
│ 6. Users are researchers, not security experts │
│ (default configs, weak passwords, no VPN) │
└─────────────────────────────────────────────────┘
Das Python-Skript erkennt verwundbare Marimo-Instanzen durch eine mehrstufige Analyse.
So funktioniert es:
/api/status, /api/health und / nach Marimo-Indikatoren in Antwortinhalten und Headern ab/terminal/ws (keine Daten werden über die Verbindung übertragen)/terminal/ws (sollte Auth erfordern) mit /ws (bekannt dafür, Auth zu erfordern), um die Inkonsistenz zu bestätigenAuf Zielsystemen werden keine Befehle ausgeführt. Der WebSocket-Handshake wird getestet, aber es werden keine Daten über die Verbindung gesendet. Die Prüfung ist vollständig passiv und für die Produktion sicher.
Verwendung:```bash
pip install -r requirements.txt
python CVE-2026-39987_Marimo_RCE_detector.py -t http://marimo-host:2718
python CVE-2026-39987_Marimo_RCE_detector.py -t https://marimo-host:443
python CVE-2026-39987_Marimo_RCE_detector.py -f targets.txt -o results.json -v
python CVE-2026-39987_Marimo_RCE_detector.py -t http://10.0.0.5:2718 --timeout 15
**Optionen:**
| Flag | Beschreibung | Standard |
|------|-------------|---------|
| `-t`, `--target` | Einzelne Ziel-URL (z. B. `http://host:2718`) | — |
| `-f`, `--file` | Datei mit Ziel-URLs, eine pro Zeile (`#`-Kommentare unterstützt) | — |
| `-o`, `--output` | Ergebnisse in JSON-Datei speichern | — |
| `--timeout` | Verbindungs-Timeout in Sekunden | `10` |
| `--verify-ssl` | SSL-Zertifikatsprüfung aktivieren | Deaktiviert |
| `-v`, `--verbose` | Ausführliche Ausgabe mit vollständigen Details | Aus |
**Beispielausgabe:**```
[*] CVE-2026-39987 Marimo Pre-Auth RCE Scanner
[*] Scanning 1 target(s)...
[*] NOTE: This scanner only checks for endpoint exposure.
[*] No commands are executed on target systems.
======================================================================
Target: http://10.0.0.5:2718
Scan Time: 2026-04-14T16:00:00Z
Risk Level: CRITICAL
======================================================================
Is Marimo: YES
Marimo Version: 0.19.2
/terminal/ws Open: YES — UNAUTHENTICATED
Vulnerable: YES
*** CRITICAL: Pre-authenticated RCE is possible! ***
*** An attacker can get a full PTY shell without any auth ***
Details:
- Marimo instance detected via /api/status
- Marimo version: 0.19.2
- WebSocket upgrade accepted — /terminal/ws accessible WITHOUT auth
- CONFIRMED: /terminal/ws accepts unauthenticated connections while
/ws requires auth — classic CVE-2026-39987 signature
- Version 0.19.2 <= 0.20.4 — VULNERABLE to pre-auth RCE
======================================================================
[*] Scan Complete: 1 targets scanned
[*] Marimo Instances: 1 | Vulnerable: 1 | Critical: 1
======================================================================
sudo cp CVE-2026-39987_Marimo_RCE.nse /usr/share/nmap/scripts/ sudo nmap --script-updatedb
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE
nmap -p 2718,8080,8443,443 --script CVE-2026-39987_Marimo_RCE
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE 10.0.0.0/24
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE -iL targets.txt
nmap -sV -p 2718 --script CVE-2026-39987_Marimo_RCE
**Beispiel für die Nmap-Ausgabe:**```
PORT STATE SERVICE
2718/tcp open http
| CVE-2026-39987_Marimo_RCE:
| VULNERABLE:
| Marimo Pre-Auth RCE (CVE-2026-39987)
| State: VULNERABLE
| Risk level: CRITICAL
| Marimo Version: 0.19.2
| /terminal/ws: accessible without authentication
| Description:
| The Marimo /terminal/ws WebSocket endpoint accepts connections
| without authentication, enabling pre-authenticated RCE.
| An attacker can obtain a full PTY shell without any credentials.
| References:
|_ https://nvd.nist.gov/vuln/detail/CVE-2026-39987
Wenn Sie Zugriff auf die Marimo-Instanz haben:```bash
curl -s http://:2718/api/status | python3 -m json.tool
curl -s -o /dev/null -w "%{http_code}"
-H "Upgrade: websocket"
-H "Connection: Upgrade"
-H "Sec-WebSocket-Key: dGVzdC1rZXktMTIzNDU2Nzg="
-H "Sec-WebSocket-Version: 13"
http://:2718/terminal/ws
curl -s -o /dev/null -w "%{http_code}"
-H "Upgrade: websocket"
-H "Connection: Upgrade"
-H "Sec-WebSocket-Key: dGVzdC1rZXktMTIzNDU2Nzg="
-H "Sec-WebSocket-Version: 13"
http://:2718/ws
If `/terminal/ws` 101 zurückgibt, während `/ws` 401/403 zurückgibt, ist dies die klassische CVE-2026-39987-Signatur.
---
## Indikatoren für eine Kompromittierung
Achten Sie in Ihrer Umgebung auf diese Anzeichen:
| Indikator | Wo prüfen | Worauf achten |
|:---|:---|:---|
| Nicht autorisierte WebSocket-Verbindungen | Server-/Proxy-Logs | Verbindungen zu `/terminal/ws` von unerwarteten IPs |
| Erstellung von PTY-Sitzungen | Prozessüberwachung | Unerwartete Shell-Prozesse, die vom Marimo-Server gestartet wurden |
| Dateizugriff | Datei-Audit-Logs | Lesezugriffe auf `.env`, Anmeldedatendateien oder SSH-Schlüssel |
| Nutzung von Anmeldedaten | Audit-Logs des Cloud-Anbieters | API-Aufrufe mit Schlüsseln, die in der Marimo-Umgebung gespeichert waren |
| Abgehender Datentransfer | Netzwerküberwachung | Ungewöhnlicher ausgehender Datenverkehr vom Marimo-Host |
**Befehle zur Untersuchung:**```bash
# Check for active WebSocket connections
ss -tnp | grep <MARIMO_PORT>
# Review process tree for unexpected shells
ps aux --forest | grep -A5 marimo
# Check if .env or credential files were recently accessed
stat .env
stat ~/.aws/credentials
# Review cloud provider activity logs for unauthorized access
aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=<KEY_ID>
# Check for unauthorized outbound connections
netstat -tnp | grep ESTABLISHED | grep -v 127.0.0.1
Sofortmaßnahmen (jetzt durchführen):
pip install --upgrade marimo)Kurzfristig (diese Woche):
.env-Dateien und Umgebungsvariablen überprüfen auf sensible Daten, die möglicherweise offengelegt wurdenLangfristig:
Kerem Oruç — Cybersecurity-Ingenieur