Skip to content
KitploitKITPLOIT
أدواتالمدونة
Log in
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2026-19264 — CVE-2026-19264 - ثغرة حرجة لتجاوز المسار دون مصادقة تؤدي إلى السيطرة الكاملة على المثيل في Postiz (< 2.22.1). توثيق تقني: تجاوز ترتيب فك التشفير، تصعيد JWT_SECRET، وتحليل الإصلاح من المصدر العلوي. | Kitploit
أدوات/GitHubGitHub/darklycn1976/cve-2026-19264
تحليل الثغرات الأمنيةتحليل الكودالاستغلالاستغلال تطبيقات الويبأمن الويبالتعلم والتعليم
GitHubdarklycn1976/cve-2026-19264

CVE-2026-19264

CVE-2026-19264 - ثغرة حرجة لتجاوز المسار دون مصادقة تؤدي إلى السيطرة الكاملة على المثيل في Postiz (< 2.22.1). توثيق تقني: تجاوز ترتيب فك التشفير، تصعيد JWT_SECRET، وتحليل الإصلاح من المصدر العلوي.

عرض المستودع
8منذ شهر واحدلم تتم المراجعة بعد
الموقع الإلكتروني

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة

CVE-2026-19264 - اجتياز مسار غير مصادَق إلى السيطرة الكاملة على مثيل في Postiz

CVE-2026-19264 - اجتياز مسار غير مصادَق إلى السيطرة الكاملة على مثيل في Postiz

المؤلف: Krithik Babu P (@DarkLycn1976) نُشر: 2026-08-10 CVE: CVE-2026-19264 الخطورة: حرِج - CVSS 4.0 9.3 / CVSS 3.1 9.8 CWE: CWE-22 - تقييد غير سليم لاسم المسار إلى دليل مقيد متأثر: gitroomhq/postiz-app < 2.22.1 أُصلح في: v2.22.1


الخلاصة

يقدّم Postiz الوسائط المخزّنة محليًا عبر مسار يضمّ مقاطع المسار المورّدة في عنوان URL إلى دليل الرفع ثم يبث النتيجة - دون أي تطبيع للمسار، أو فحص احتواء، أو مصادقة.

يُرجع حمولة الاجتياز الواضحة 404، لأن Next.js يطوي مقاطع ../ قبل التوجيه. لكن فواصل الترميز بعنوان URL تنجو من مطابقة المسار وتُفك ترميزها مرة واحدة إضافية بالضبط في الطريق إلى استدعاء نظام الملفات، مما يعيد الاجتياز إلى الجانب الآخر من كل فحص.

يمكن لمهاجم غير مصادَق قراءة أي ملف يمكن لعملية التطبيق قراءته - بما في ذلك بيئته الخاصة، التي تحمل سر توقيع JWT. ولأن Postiz يوقّع رموز الجلسة بذلك السر ويصدرها بدون مطالبة انتهاء الصلاحية، فإن استرجاعه يحوّل بدائية قراءة الملفات إلى جلسة دائمة قابلة للتزوير بأي مستخدم، بما في ذلك المسؤول.

طلب GET واحد غير مصادَق يؤدي إلى السيطرة الكاملة على المثيل.


1. الخلفية

Postiz هو منصة جدولة وسائل تواصل اجتماعي مفتوحة المصدر - نحو 34,000 نجمة على GitHub وقت كتابة هذا التقرير - مبنيّة بواجهة أمامية Next.js وخلفية NestJS. تستضيفه ذاتيًا وكالات وفرق صغيرة على نطاق واسع لإدارة حسابات التواصل الاجتماعي المتصلة والمحتوى المجدول والفواتير.

يمكن للنشر الذاتي تخزين الوسائط المرفوعة محليًا بدلًا من التخزين الكائني. يتحكم في هذا السلوك متغير بيئة واحد:

STORAGE_PROVIDER=local

هذه هي القيمة المضمّنة في .env.example، لذا فهي ما يشغّله معظم المستضيفين ذاتيًا ما لم يكوّنوا S3 أو Cloudflare R2 عمدًا.

2. سطح الهجوم

عندما يكون التخزين المحلي مفعّلًا، يعيد next.config.js كتابة المسار العام /uploads/:path* إلى مسار API داخلي:

apps/frontend/src/app/(app)/api/uploads/[[...path]]/route.ts

خاصيتان تجعلان هذا المسار مثيرًا للاهتمام قبل أي ثغرة:

  1. إنه غير مصادَق. لا يحرسه أي برمجية وسيطة أمامية. خدمة الوسائط العامة هي القصد، لذا لا يُشترط أي جلسة.
  2. إنه مسار التقاط-الكل (catch-all). مقطع الالتقاط الاختياري [[...path]] يعني أن كل مقطع مسار متبقٍ يصل كمصفوفة يمكن للمعالج تفسيرها بحرية.

عندما يكون STORAGE_PROVIDER أي قيمة غير local، تشير إعادة الكتابة إلى /404 ويصبح المعالج غير قابل للوصول. بوابة التكوين هذه هي الشيء الوحيد الذي يفصل بين النشر وهذه الثغرة.

3. الكود المعرض للثغرة

المعالج، قبل الإصدار v2.22.1:

export const GET = async (request: NextRequest, context) => {
  const { path } = await context.params;
  const filePath =
    process.env.UPLOAD_DIRECTORY + '/' + (path ?? []).join('/');

  const response  = createReadStream(filePath);
  const fileStats = statSync(filePath);
  // ... stream the file back to the caller
};

ثلاثة عيوب في أربعة أسطر:

  • لا تطبيع. path.normalize()، path.resolve() - لا يُستدعى أي منهما. المقاطع الواصلة تُضم حرفيًا كما وصلت.
  • لا فحص احتواء. لا شيء يتحقق من أن filePath الناتج ما يزال داخل UPLOAD_DIRECTORY.
  • ضم سلاسل نصية، لا ضم مسارات. + '/' + يعامل المكونات كنص، لا كمسار له دلالات.

النتيجة تذهب مباشرة إلى createReadStream() وتُبث البايتات إلى المتصل بنوع MIME يُستنتج من اسم الملف. لا توجد قائمة سماح بالامتدادات ولا مرشح محتوى.

4. لماذا تفشل الحمولة الواضحة

الهجوم النموذجي هو:

GET /uploads/../../../etc/passwd

يُرجع هذا 404 على Postiz، وهذا الـ 404 هو السبب الكامل وراء بقاء هذه الثغرة حتى اكتُشفت.

يطبّع Next.js مسار الطلب أثناء التوجيه. تُطوى مقاطع ../ الخام قبل أن يقرر الموجّه أي معالج يستدعي. بحلول وصول الطلب إلى مسار الالتقاط، يكون الاجتياز قد أُزيل بالفعل - إما أن المسار يُحل إلى مكان لا يوجد فيه مسار مطابق، أو يُحل إلى داخل /uploads مع اختفاء المقاطع المنقوطة.

بالنسبة لمن يختبر بسرعة، يقرأ ذلك الـ 404 كأنه "الإطار يعالج هذا". إنه دفاع حقيقي وفعّال. المشكلة ليست في غيابه - بل في مكان تنفيذه في خط الأنابيب.

5. التجاوز - عدم تطابق ترتيب فك الترميز

لا تؤدي مطابقة المسار ومعالج الطلب نفس عدد مرات فك ترميز النسبة المئوية.

إذا كانت الفواصل مشفّرة بالنسبة المئوية، فإن التسلسل ليس فاصل مسار أثناء مطابقة المسار. %2e%2e%2f مجرد سلسلة معتمة - نص خامل ليس لدى المطبّع سبب للمسه. يمر عبر التوجيه سليمًا، يطابقه مسار الالتقاط، ويُفك ترميزه في طريقه إلى params الخاصة بالمعالج، حيث يصبح ../ من جديد.

عند تلك النقطة يُضم إلى UPLOAD_DIRECTORY ويُسلَّم إلى createReadStream() - متجاوزًا التوجيه والتطبيع وكل ضابط كان سيمنعه.

أشكال مجدية:

GET /uploads/%2e%2e%2fsecretdir%2fsecret.txt        → 200, ملف خارج دليل الرفع
GET /uploads/..%2f..%2f..%2fetc%2fpasswd            → 200
GET /uploads/%2e%2e%2f%2e%2e%2f...%2fetc%2fpasswd   → 200, أعاد ملف /etc/passwd الفعلي

الترميز المضاعف لا ينجح - يبقى %252e حرفيًا عبر مرحلة فك الترميز الوحيدة ولا يصبح نقطة أبدًا. طبقة واحدة بالضبط من الترميز هي النقطة المثلى، وهو تذكير مفيد بأن "شدد الترميز أكثر" ليس استراتيجية.

الثابت الذي يجب التمسك به:

الضابط الذي يعمل قبل اكتمال فك الترميز لا يحمي نقطة الوصول (sink).

6. التصعيد - من القراءة العشوائية إلى السيطرة على المثيل

بدائية قراءة الملفات هي ثغرة عالية بحد ذاتها. ما يجعلها حرجة هو ما تصل إليه.

الخطوة 1 - قراءة البيئة. إعدادات عملية Node نفسها موجودة على القرص في جذر النشر. يوفّر .env، من بين أمور أخرى:

  • JWT_SECRET - مفتاح توقيع رموز الجلسة
  • DATABASE_URL - بيانات اعتماد Postgres كاملة
  • أسرار OAuth للمزود المتصل ومفاتيح الفوترة

الخطوة 2 - تزوير جلسة. يوقّع Postiz رموز الجلسة بـ JWT_SECRET باستخدام HS256 عبر jsonwebtoken. والأهم أن الرموز تُصدر بدون expiresIn، لذا يكون الرمز المزوّر صالحًا إلى أجل غير مسمى.

الخطوة 3 - كن أي شخص. يعيد برمجية المصادقة الوسيطة حل المستخدم من قاعدة البيانات باستخدام مطالبة id. وهي عمدًا لا تثق بمطالبة مثل isSuperAdmin من الرمز - تصميم جيد - لكن هذا التحصين غير ذي صلة بمجرد أن تتمكن من توقيع id عشوائي. توقيع { id: <معرف المستخدم الضحية> } ينتج جلسة لا يمكن تمييزها عن تسجيل دخول مشروع:

read .env  →  JWT_SECRET  →  sign({ id: victim })  →  authenticated as victim, forever

تحققت من هذا مقابل منطق التحقق الحقيقي للمشروع مع اعتماد jsonwebtoken الفعلي: قُبل رمز موقّع بالسر المسترجع، ورُفض رمز موقّع بسر خاطئ. حالة التحكم مهمة - بدونها لديك افتراض، لا اكتشاف.

الخطوة 4 - المسار الموازي. DATABASE_URL وحده كافٍ للوصول المباشر إلى Postgres: اقرأ كل حساب متصل، أو اقلب علامة المسؤول مباشرة.

لا كلمة مرور. لا وصول سابق. لا تفاعل مستخدم. طلب HTTP واحد غير مصادَق.

7. الأثر والتقييم

CVSS 4.0  9.3  AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
CVSS 3.1  9.8  AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

PR:N وUI:N هما المقياسان اللذان يقومان بالعمل. المسار لا يتطلب جلسة ولا تفاعل ضحية - المهاجم يعمل وحده، عبر الشبكة، ضد تكوين افتراضي.

الحد الوحيد الصادق هو بوابة التكوين: النشر على S3 أو R2 غير معرض، لأن المسار يُعاد كتابته إلى /404. هذا يقلل عدد المتأثرين لكنه لا يقلل الخطورة لأي شخص داخلهم - وlocal هي الافتراضي المُسلَّم.

8. الإصلاح

تصحيح المطوّرين (7936062) ثمانية أسطر ويستحق القراءة، لأنه صحيح بطريقة لا تكون عليها هذه الإصلاحات غالبًا:

تنزيل الأداة