
Kit di rilevamento e sfruttamento per CVE-2026-39987, una RCE pre-autenticazione nei notebook Marimo. Include uno scanner Python e uno script Nmap NSE per identificare istanze vulnerabili tramite controlli sugli endpoint WebSocket.
Una vulnerabilità di esecuzione remota di codice pre-autenticata in Marimo, un notebook Python open-source per la scienza dei dati e AI/ML. L'endpoint WebSocket del terminale (/terminal/ws) salta completamente la validazione dell'autenticazione, mentre l'endpoint notebook adiacente (/ws) la applica correttamente. Un attaccante non autenticato può connettersi a /terminal/ws e ottenere una sul sistema host senza alcuna credenziale.
Sfruttata in natura entro 10 ore dalla divulgazione. Gli attaccanti hanno rubato le credenziali AWS in meno di 3 minuti.
Colpisce Marimo <= 0.20.4. Corretto in Marimo 0.23.0.
| Campo | Dettaglio |
|---|---|
| ID CVE | CVE-2026-39987 |
| Vendor | Marimo Project |
| Prodotto | Marimo (Notebook Python) |
| Versioni Interessate | <= 0.20.4 |
| CVSS v3.1 | 9.3 (Critico) |
| CWE | CWE-306 — Autenticazione Mancante per Funzione Critica |
| Vettore di Attacco | Rete |
| Autenticazione | Nessuna richiesta |
| Interazione Utente | Nessuna |
| Maturità dell'Exploit | Attivamente sfruttato in natura |
| Tempo per lo Sfruttamento | ~10 ore dopo la divulgazione |
| Corretto In | Marimo 0.23.0 |
Marimo è un notebook Python reattivo open-source progettato come alternativa moderna a Jupyter. È costruito per la scienza dei dati, la sperimentazione AI/ML e l'analisi interattiva dei dati. Le sue caratteristiche principali includono il tracciamento automatico delle dipendenze, l'esecuzione riproducibile e un'esperienza di sviluppo più pulita rispetto ai notebook tradizionali.
Marimo sta guadagnando rapidamente popolarità nella community Python e AI/ML, soprattutto tra i professionisti che desiderano flussi di lavoro notebook più strutturati rispetto a quelli offerti da Jupyter.
Come tutti gli ambienti notebook, le istanze Marimo hanno tipicamente accesso a risorse sensibili: credenziali cloud (AWS, GCP, Azure), stringhe di connessione a database, chiavi API per servizi AI (OpenAI, Anthropic, ecc.) e accesso alla rete interna. A differenza delle applicazioni web tradizionali, i notebook sono progettati per eseguire codice arbitrario. Questo è il loro scopo principale.
Questa combinazione rende qualsiasi bypass dell'autenticazione in un ambiente notebook particolarmente devastante.``` 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 │ │ │ └──────────────────────────┘ │ └────────────────────────────────┘
---
## Analisi Approfondita della Vulnerabilità
### I Due Endpoint WebSocket
Il server di Marimo implementa più endpoint WebSocket per diverse funzionalità. La differenza critica tra i due endpoint principali è la presenza (o l'assenza) di un controllo di autenticazione:```
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!
L'endpoint del notebook (/ws) chiama correttamente validate_auth() per verificare l'identità dell'utente prima di accettare connessioni WebSocket. Questo è il comportamento di sicurezza previsto.
L'endpoint del terminale (/terminal/ws) controlla solo se il server è in modalità di esecuzione e se la piattaforma supporta la funzionalità del terminale. Non chiama mai validate_auth(). Dopo aver superato questi controlli di base, accetta la connessione e crea una sessione PTY (pseudo-terminale) completa.```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
Questo è **CWE-306: Autenticazione Mancante per Funzioni Critiche**. L'endpoint più pericoloso sul server (quello che fornisce una shell interattiva) non ha alcuna autenticazione.
### L'Attacco: Dalle Advisory alle Chiavi AWS in 3 Minuti
La timeline dello sfruttamento mostra quanto velocemente operano gli attori delle minacce moderni:```
┌──────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────────────────────────────────────────────────┘
L'attacco in sé è banalmente semplice:``` 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
Nessuno sviluppo di exploit necessario. Nessuno shellcode. Nessuna corruzione di memoria. Solo un client WebSocket e un controllo di autenticazione mancante.
---
## Analisi dell'Impatto
**Impatto immediato sull'host Marimo:**
- Shell interattiva completa con i privilegi del processo Marimo
- Accesso a tutti i file leggibili dal processo (codice sorgente, dati, credenziali)
- Accesso alle variabili d'ambiente contenenti chiavi API e segreti
- Capacità di eseguire comandi arbitrari sul sistema host
**Esposizione delle credenziali (l'obiettivo primario dell'attacco):**
- Chiavi di accesso AWS e chiavi segrete da file `.env` o variabili d'ambiente
- Credenziali degli account di servizio GCP/Azure
- Stringhe di connessione al database con password
- Chiavi API di OpenAI, Anthropic e altri servizi AI
- Chiavi SSH e altro materiale di autenticazione
**Impatto a valle (tramite credenziali rubate):**
- Accesso non autorizzato all'infrastruttura cloud (EC2, S3, Lambda, ecc.)
- Esfiltrazione di dati da storage cloud e database
- Abuso di risorse (cryptomining, furto di crediti API AI)
- Movimento laterale in reti cloud e on-premise
**Fattori di amplificazione del rischio:**
- Gli ambienti notebook sono progettati per eseguire codice arbitrario (è il loro scopo)
- Gli ambienti di data science hanno tipicamente un ampio accesso cloud per i job di training
- Molte istanze Marimo sono esposte a internet per collaborazione e lavoro remoto
- L'hardening della sicurezza è spesso un ripensamento negli ambienti di ricerca/sperimentazione
---
## Versioni Interessate
| Versione | Stato |
|---------|--------|
| Marimo 0.23.0+ | **Corretta** |
| Marimo 0.20.5 a 0.22.x | **Probabilmente vulnerabile** (tra l'intervallo dell'advisory e la correzione) |
| Marimo <= 0.20.4 | **Vulnerabile** (intervallo confermato) |
---
## Il Quadro Più Ampio: La Toolchain AI/ML Sotto Attacco
CVE-2026-39987 non è un incidente isolato. Fa parte di un chiaro schema emerso nell'aprile 2026:
| CVE | Prodotto | Tipo | Stato |
|:---|:---|:---|:---|
| **CVE-2026-39987** | Marimo | RCE Pre-Auth (WebSocket) | Sfruttato in 10 ore |
| **CVE-2026-33017** | Langflow | RCE | CISA KEV (26 marzo) |
| **CVE-2026-5059** | aws-mcp-server | Command Injection RCE | Advisory pubblico |
| **TorchGeo** | TorchGeo | eval() RCE | Advisory pubblico |
Quattro strumenti di sviluppo AI/ML colpiti da vulnerabilità RCE critiche in un solo mese. La pipeline di sviluppo AI/ML sta diventando il nuovo shadow IT: strumenti distribuiti con ampio accesso, supervisione di sicurezza minima e ricchi depositi di credenziali.```
┌─────────────────────────────────────────────────┐
│ 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) │
└─────────────────────────────────────────────────┘
Lo script Python rileva istanze Marimo vulnerabili attraverso un'analisi multi-step.
Come funziona:
/api/status, /api/health e / per indicatori Marimo nel contenuto delle risposte e negli header/terminal/ws (nessun dato trasmesso attraverso la connessione)/terminal/ws (dovrebbe richiedere autenticazione) vs /ws (noto per richiedere autenticazione) per confermare l'incoerenzaNessun comando viene eseguito sui sistemi target. L'handshake WebSocket viene testato ma nessun dato viene inviato attraverso la connessione. Il controllo è completamente passivo e sicuro per la produzione.
Utilizzo:```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
**Opzioni:**
| Flag | Descrizione | Predefinito |
|------|-------------|---------|
| `-t`, `--target` | URL di destinazione singola (es. `http://host:2718`) | — |
| `-f`, `--file` | File con URL di destinazione, uno per riga (commenti `#` supportati) | — |
| `-o`, `--output` | Salva i risultati in un file JSON | — |
| `--timeout` | Timeout di connessione in secondi | `10` |
| `--verify-ssl` | Abilita la verifica del certificato SSL | Disabilitato |
| `-v`, `--verbose` | Output dettagliato con informazioni complete | Off |
**Esempio di output:**```
[*] 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
**Output di esempio di Nmap:**```
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
Se hai accesso all'istanza Marimo:```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
Se `/terminal/ws` restituisce 101 mentre `/ws` restituisce 401/403, questa è la classica firma di CVE-2026-39987.
---
## Indicatori di Compromissione
Cerca questi segnali nel tuo ambiente:
| Indicatore | Dove Controllare | Cosa Cercare |
|:---|:---|:---|
| Connessioni WebSocket non autorizzate | Log del server/proxy | Connessioni a `/terminal/ws` da IP inattesi |
| Creazione di sessioni PTY | Monitoraggio dei processi | Processi shell inattesi generati dal server Marimo |
| Accesso ai file | Log di audit dei file | Letture di `.env`, file di credenziali o chiavi SSH |
| Utilizzo delle credenziali | Log di audit del provider cloud | Chiamate API che utilizzano chiavi memorizzate nell'ambiente Marimo |
| Trasferimento dati in uscita | Monitoraggio della rete | Traffico egress insolito dall'host Marimo |
**Comandi di indagine:**```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
Azioni immediate (esegui subito):
pip install --upgrade marimo)A breve termine (questa settimana):
.env e le variabili d'ambiente per eventuali dati sensibili che potrebbero essere stati espostiA lungo termine:
Kerem Oruç — Ingegnere della Cybersecurity