Jellyfin Server 12.2 - عالية الخطورة - عن بُعد - IDOR - بقايا ItemIds في قوائم التشغيل العامة من GHSA-9x85 ثم بث بدون مصادقة
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · jellyfin-playlist-itemids
الفئة: IDOR الوصول: عن بُعد (أي مستخدم يستطيع رؤية قائمة تشغيل عامة)
Jellyfin Server v12.2 (ca0f16eb7c195f720a1493ed469a27c4657db0c6) - Jellyfin
GHSA-9x85 عالج GET /Playlists/{id}/Items باستخدام GetUserId بالإضافة إلى Where(i => i.Item2.IsVisible(user)). لم يُمنح GET /Playlists/{id} ذلك الفلتر. ItemIds هي GetManageableItems() بدون IsVisible على العناصر الفرعية. GetPlaylistForUser يتطلب فقط رؤية قائمة التشغيل.
CreatePlaylistDto.IsPublic افتراضيًا true. قوائم تشغيل OpenAccess تكون IsVisible لمستخدمين آخرين (HasParentalAllowedChild لا يزال يسمح لمستخدم ثانٍ برؤية قائمة التشغيل). GET /Videos/{id}/stream غير المصادق عليه لا يحتوي على [Authorize]. StreamingHelpers يحمّل GetItemById بدون مستخدم.
المستخدم الذي يستطيع رؤية قائمة تشغيل عامة يقرأ GUIDs العناصر الفرعية الخاصة من ItemIds، ثم يبث الملفات بدون رمز.
| المعرّف | لا يوجد CVE بعد |
| الفئة | IDOR (بقايا ItemIds في قائمة التشغيل من GHSA-9x85، ثم بث غير مصادق عليه) |
| الوصول | عن بُعد (أي مستخدم يستطيع رؤية قائمة تشغيل عامة) |
| CWE | CWE-639، CWE-862 |
| CVSS | عالٍ: 6.5 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N |
| المنتج | Jellyfin Server |
| المتأثر | v12.2 (ca0f16eb) صورة jellyfin/jellyfin:12.2 |
| المصادقة | مصادق عليه لطلب GET الخاص بقائمة التشغيل؛ لا شيء للبث |
| الترخيص | GNU Affero GPL v3.0 |
| المختبر | 127.0.0.1:18800 فقط |
البقايا #3 من Jellyfin 12.2 leftovers. البقايا #1 (الأهم): jellyfin-suggestions-idor. نفس التثبيت: jellyfin-svg-gzip · jellyfin-ffmpeg-colonslash.
الضحية والمهاجم مستخدمان على خادم واحد بمكتبات منفصلة (أطفال مقابل بالغين، أو مجلد مخفي). تضع الضحية فيلمًا خاصًا على قائمة تشغيل عامة. إنشاء الجسم IsPublic افتراضيًا true، لذا هذه هي الحالة المعتادة، وليست مشاركة. يسجّل المهاجم الدخول بحساب مقيّد، ويقرأ كائن قائمة التشغيل، ثم يجلب الملف بدون ترويسة Authorization.
هذه GUIDs لوسائط في مكتبات لم يُسنَد إليها المهاجم، ثم الملف الكامل عبر البث. ليست تعدادًا لعناوين قوائم التشغيل. الإضافة/الإنشاء لا تزال تُسقط العناصر غير المرئية.
| الطريقة | المسار | الصلاحية |
|---|---|---|
| GET | /Playlists/{playlistId} | GUIDs العناصر الفرعية لقائمة تشغيل مرئية (ItemIds من GetManageableItems()، بدون IsVisible على العناصر الفرعية). أيضًا Shares[].UserId / CanEdit (معرّفات المستخدمين المشاركين). المختبر: GET /Playlists/{id}/Items الخاص بـ B فارغ؛ نفس المعرّف GET /Playlists/{id} أدرج GUID الخاص a2bf95146e44897be6fb09020a82eb52. |
GetPlaylistForUser يتطلب فقط أن تكون قائمة التشغيل نفسها مرئية. OpenAccess بالإضافة إلى HasParentalAllowedChild (الذي يتخطى فحص الوسم المسموح) كافٍ. مسار Items المُعالَج هو الضابط: لا يزال يفلتر.
| الطريقة | المسار | الصلاحية |
|---|---|---|
| GET | /Videos/{itemId}/stream?static=true | بايتات وسائط خاصة. لا يوجد [Authorize]. يقوم StreamingHelpers بتنفيذ GetItemById<BaseItem>(id) بدون مستخدم. المختبر: 200 بدون مصادقة، 1142 بايت، الشاهد JELLYFIN-PLAYLIST-ITEMIDS-WITNESS في الجسم. |
| GET | /Audio/{itemId}/stream | نفس المصب للصوت. نفس المساعد، نفس البحث غير المقيّد. |
المصادَق GET /Videos/{itemId}/master.m3u8 عبر HLS هو نفس مسار GetItemById (DynamicHlsController لديه [Authorize] على مستوى الصنف). اختبر المختبر البث التدريجي بدون مصادقة، وليس HLS.
| الطريقة | المسار | الصلاحية |
|---|---|---|
| POST | /Playlists/{id}/Items وإنشاء Ids | مغلقة. يشغّل Playlist.GetPlaylistItems الدالة IsVisible. يتم إسقاط GUIDs الخاصة، لا ربطها. |
GetItemById(id, user) المقيّد بالمستخدم).EnableCollectionManagement افتراضيًا false؛ FilterLinkedChildrenPerUser).ValidateTranscodePath على مقاطع HLS القديمة داخل دليل التحويل البرمجي).userId في GHSA-9x85 على Items (تُرجع GetUserId رمز 403 لمعرّف غريب).GET /Playlists/{id}/Items للأبناء الخاصين (المسار المُرقّع؛ المختبر items-filtered=yes).لا كتابة لكلمة مرور/سياسة. لا تثبيت إضافة. لا shell.
ACL المجلد (EnabledFolders) وحده لم يُخفِ الابن على /Items في هذا المختبر. جعل AllowedTags=["lab-public"] الدالة IsVisible تُسقطه. لا يستشير ItemIds والبث بدون مصادقة ذلك الفلتر.
ثبّتُ jellyfin/jellyfin v12.2 (ca0f16eb). عشرون عملية بحث في المصدر على بقايا موجة GHSA 12.0. علّم GHSA-9x85 الدالة GetPlaylistItems تصفية الأبناء. سألتُ ما إذا كانت GetPlaylist قد حصلت على نفس Where. لم تحصل. لم تتلقَّ نقاط نهاية البث قط GetItemById مقيّدًا بالمستخدم.
منعطف خاطئ: معاملة EnabledFolders كإخفاء لـ Items. إن IsVisible للفيلم يتعلق بالأبوية/الوسوم، وليس بـ ACL المجلد. كان على المختبر تعيين AllowedTags حتى يصبح مسار Items المُرقّع فارغًا. هذا هو الضابط. البقايا لا تستخدمه.
git clone https://github.com/abraxas/jellyfin-playlist-itemids
cd jellyfin-playlist-itemids
python3 jellyfin-playlist-itemids-Abraxas-Labs.py
أو:
cd lab
./run.sh
استهدف فقط http://127.0.0.1:18800. الصورة jellyfin/jellyfin:12.2. يقوم run.sh بتهيئة ملفَي mp4 صغيرين (PrivateA كملف شاهد، PublicB كملف وهمي). ينشئ المعالج labadmin، والمجلدين كليهما، ثم labuser مع EnableAllFolders=false، وEnabledFolders=PublicB، وAllowedTags=["lab-public"]. يضع المسؤول الفيلم الخاص في قائمة تشغيل عامة. عبر loopback. للشاهد فقط. يتطلب Docker وffmpeg للتهيئة.
SUCCESS JELLYFIN-PLAYLIST-ITEMIDS items-filtered=yes itemids-leak=yes unauth-stream=200 openAccess=yes playlist=e4798134497195ff4733dde62428e8d8 private=a2bf95146e44897be6fb09020a82eb52 witness-in-stream=yes witness-in-playlist-json=yes JELLYFIN-PLAYLIST-ITEMIDS-WITNESS
إعادة التشغيل: python3 jellyfin-playlist-itemids-Abraxas-Labs.py.
قم بتصفية ItemIds باستخدام IsVisible(user)، بما يطابق GetPlaylistItems. قيّد نطاق البث GetItemById لمستخدم معين، أو ضع [Authorize] على VideosController.GetVideoStream (والنظير الصوتي).