
إعادة إنتاج غير ضارة وغير متصلة بالإنترنت لـ CVE-2026-93355 — استيلاء على الحساب عبر JWT بالبريد الإلكتروني غير المُتحقَّق منه في LiteLLM (يشغّل الشيفرة الحقيقية لـ LiteLLM)
مختبر مكتفٍ ذاتيًا وقابل للتخلص منه يعيد إنتاج
CVE-2026-93355: مصادقة JWT في LiteLLM
تحدد هوية المستدعي بالرجوع إلى مطابقة البريد الإلكتروني — ولا تتحقق أبدًا من ادعاء email_verified. فالمهاجم الذي يستطيع جعل حساب مزوّد الهوية الخاص به يُصدر رمزًا موقّعًا بشكل صالح يحمل عنوان بريد إلكتروني للضحية (غير مُتحقَّق منه) يرث دور الضحية، وصولًا إلى proxy_admin، كما أن المطابقة تعيد كتابة ارتباط SSO المخزَّن للضحية بصمت لتحل محلّه هوية المهاجم، مما يجعل الاستيلاء دائمًا.
| التنبيه الأمني | VulnCheck · OX Security |
| CVE | CVE-2026-93355 |
| المشروع | litellm (الوكيل / مصادقة JWT) |
| المتأثر | ≤ 1.102.1 وفقًا للتنبيه الأمني — لا يزال دون إصلاح: الكود الثغرة لم يتغير في 1.104.0 (أحدث إصدار مستقر) وفي main حتى 2026-10-07 |
| CVSS | 8.1 HIGH (AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N) |
| الضعف | CWE-1390 — مصادقة ضعيفة |
| السبب الجذري | البحث عن المستخدم بالبريد الإلكتروني يُوثَق به دون التحقق من email_verified |
| الاكتشاف | Nir Zadok وMoshe Siman Tov Bustan (OX Security) |
حالة الإصلاح (2026-10-07): لا يوجد إصدار مُصلَح بعد. يُدرج التنبيه الأمني
1.102.1فقط لأنه كان أحدث إصدار وقت الإفصاح؛ وقد أصدر LiteLLM منذ ذلك الحين1.103.xو1.104.0، ولا تزال الدالة_get_fuzzy_user_objectفي كليهما — وعلىmain— بلا أي تحقق منemail_verified. يثبّت المختبر1.102.1لأغراض قابلية إعادة الإنتاج؛ وسينبّهscripts/verify_against_real_source.pyفي اليوم الذي يُطرح فيه إصلاح.
يعيد هذا إنتاج ثغرة مُفصَح عنها علنًا لأغراض تعليمية ودفاعية. يعمل محليًا بالكامل ولا يمس أي خدمة حقيقية:
[email protected]
داخل هذه العملية ويطبعه. لا يوجد حساب حقيقي، ولا مفتاح حقيقي، ولا شيء يغادر جهازك.يتيح وكيل LiteLLM المصادقة باستخدام JWT من مزوّد الهوية المُهيَّأ لديك. ولربط ذلك الرمز بمستخدم LiteLLM يستدعي get_user_object()،
التي تحاول أولًا مطابقة موضوع الرمز (sso_user_id)، وعند الفشل ترجع إلى
_get_fuzzy_user_object() — وهي مطابقة غير حساسة لحالة الأحرف على ادعاء email.
من يملك ذلك البريد الإلكتروني في جدول LiteLLM هو من تصبح هويته. ادعاء email_verified في الرمز
لا يُقرأ أبدًا — لا أثناء استخراج الادعاءات
(JWTHandler.get_user_email) ولا أثناء الحل. لذا فإن أي مزوّد هوية يُصدر رمزًا
لبريد إلكتروني غير مُتحقَّق منه (معظمهم يفعل ذلك بحكم التصميم — فالتحقق
هو مسؤولية الطرف المعتمد) يتيح للمهاجم تعيين بريد ملفه الشخصي إلى بريد الضحية،
وتقديم الرمز، ووراثة دور الضحية. والأسوأ من ذلك، عند مطابقة البريد الإلكتروني
يُطلق LiteLLM مهمة خلفية تستبدل sso_user_id الخاص بالضحية
بموضوع المهاجم — لذا فإن كل تسجيل دخول لاحق يُحل مباشرة إلى
حساب الضحية دون الحاجة إلى البريد الإلكتروني، ولا يفيد تغيير البريد الإلكتروني.

كل ما يخضع للاختبار هو كود LiteLLM نفسه، مستوردًا من حزمة
litellm==1.102.1 المثبَّتة. لا يوفّر المختبر سوى أمرين لا بد منهما: جدول
مستخدمين وهمي في الذاكرة وزوج مفاتيح مزوّد هوية محاكى. وهو لا يعيد تنفيذ
المنطق الثغرة.
inspect.getsource() على _get_fuzzy_user_object وJWTHandler.get_user_email المثبّتتين ونتحقق من وجود آلية الرجوع إلى البريد الإلكتروني، ومن وجود إعادة ربط sso_user_id، ومن أن أيًا من الدالتين لا تشير إلى email_verified. → scripts/verify_against_real_source.py_get_fuzzy_user_object الحقيقية؛ فيُحلّ إلى صف proxy_admin الخاص بالضحية. → poc/01_account_takeover.pysso_user_id الخاص بالضحية بمعرّف المهاجم (subject)؛ وعند تسجيل دخول لاحق دون أي بريد إلكتروني على الإطلاق يُحلّ مباشرةً إلى المسؤول، ولا يؤدي "إصلاح البريد الإلكتروني المكرر" إلى إبطال ذلك. → poc/02_persistent_rebind.pyemail_verified قبل الرجوع إلى البريد الإلكتروني يمنع الهجوم مع إبقاء عمليات تسجيل الدخول عبر SSO المُتحقَّق منها تعمل. → poc/03_patched_path.py./run.sh
run.sh يبني .venv مؤقتًا، ويثبّت litellm[proxy]==1.102.1، ويشغّل فحص
الحقيقة الأساسية، ثم جميع PoCs الثلاثة. أول تشغيل يقوم بتنزيل LiteLLM ويستغرق
بضع دقائق؛ احذف .venv للتنظيف. لاستخدام مفسّر موجود مسبقًا:
PYTHON=python3.12 ./run.sh.
شغّل PoC واحدًا مباشرةً (داخل الـ venv):
.venv/bin/python poc/01_account_takeover.py
cve-2026-93355-lab/
├── mock_idp.py # ephemeral RS256 OIDC IdP (real signatures)
├── fake_litellm_db.py # in-memory stand-in for the Prisma user table
├── harness.py # configures & drives LiteLLM's real objects
├── poc/
│ ├── 01_account_takeover.py # unverified email -> victim's proxy_admin
│ ├── 02_persistent_rebind.py # sso_user_id overwrite -> persistent access
│ └── 03_patched_path.py # the email_verified guard that closes it
├── scripts/
│ └── verify_against_real_source.py # proves the PoCs run the real vulnerable code
├── assets/attack-flow.svg|.png # the diagram above
├── blog-cve-2026-93355.md # the guard0 write-up
└── run.sh
email_verified: true عند حدود الثقة قبل أن يصل أي رمز
إلى عملية تحديد الهوية؛ ارفض أو تجاهل رسائل البريد الإلكتروني غير المُتحقَّق منها.user_id_jwt_field)،
وليس ببريد إلكتروني قابل للتغيير، ولا تدع الاحتياطي المعتمد على البريد الإلكتروني يعيد ربطها بصمت.proxy_admin المرتبط بالبريد الإلكتروني يُعدّ بدائية استيلاء دائمة.