
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 shell PTY interattiva completa 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