
ثغرة RCE قبل المصادقة في React Server Components الإصدارات 19.0.0 و19.1.0 و19.1.1 و19.2.0.
يحتوي هذا المستودع على نسخة PoC توضح CVE-2025-55182، وهي ثغرة أمنية حرجة في React Server Components (RSC) تتيح تنفيذ كود عشوائي دون مصادقة.
تكمن الثغرة في كيفية قيام React Server Components بإلغاء تسلسل "Server Actions" من طلبات العملاء. على وجه التحديد، فشلت دالة requireModule في التحقق من أن اسم التصدير المطلوب هو خاصية مباشرة من الوحدة. وقد سمح ذلك للمهاجمين بالوصول إلى خاصية constructor للدوال المُصدَّرة، والحصول على مرجع إلى مُنشئ Function العام، والذي يمكن استخدامه لتنفيذ كود عشوائي.
يستخدم هذا الـ PoC بيئة Node.js بسيطة لعزل الثغرة في مكتبة react-server-dom-webpack، فقط للتأكد من أن الاستغلال يُظهر الخلل في المكتبة نفسها، وليس سوء إعداد في إطار عمل.
npmnpm install
[!NOTE] ملف
package.jsonمثبّت على الإصدار الضعيف19.0.0.
يقوم هذا السكربت بإعداد خادم HTTP خام يستخدم بيئة تشغيل React الضعيفة لفك تشفير الطلبات.
# tty1
node --conditions react-server server.js
في طرفية منفصلة، قم بتشغيل الاستغلال. يرسل هذا حمولة Flight خبيثة إلى الخادم.
# tty2
node exploit.js id
يجب أن ترى مخرجات الأمر معروضة في الاستجابة:
المخرجات المتوقعة:
Response: uid=0(root) gid=0(root) groups=0(root)
لماذا حدثت الثغرة؟
دالة requireModule في ReactFlightDOMServerNode.js كانت تثق أساسًا في أي name يرسله العميل. كانت تنفذ moduleExports[metadata[NAME]] دون التحقق مما إذا كانت تلك الخاصية معرّضة بالفعل. فإذا قال العميل "أعطني هذه الخاصية"، كان الخادم يرد "بالطبع! هاك يا صديقي".
لماذا يُعد السماح للأشخاص بالوصول إلى أي خاصية فكرة سيئة؟
لأنه يتيح لأي شخص الوصول إلى سلسلة النماذج الأولية (prototype chain)، حتى constructor، وهو أمر خطير للغاية. إذا صادف أن الوحدة تصدّر دالة (مثل module.exports = () => {})، فإن constructor الخاص بها هو حرفيًا مُنشئ Function العام.
لماذا يعني الحصول على مُنشئ Function تنفيذ كود عشوائي (RCE)؟
بمجرد أن يحصل المهاجم على مُنشئ Function، يمكنه إساءة استخدام ميزة "Bound Server Action". يربط سلسلة تحتوي على JavaScript خبيث به (مما يحوّلها فعليًا إلى new Function("evil code")). وبمجرد تنفيذها، ينفذ الخادم أي كود يضعه المهاجم.
لماذا شغّل React تلك الدالة الخبيثة فعليًا؟
لأن Server Actions يمكن تشغيلها بواسطة المعرّف (ID). إذا صمم المهاجم حمولة تحتوي على Action ID يشير إلى مرجع module#constructor الخاص به، فإن React يحلها كإجراء عادي وينفذها. وهذا "الإجراء" هو في الواقع الدالة الخبيثة الخاصة به.
لماذا لم يتم التحقق من أي من ذلك؟
افترض النظام فقط أن id و name من بيانات تعريف Server Reference سيشيران دائمًا إلى تصديرات صالحة يعرّفها المطور. لم يكن هناك فحص أمان مثل hasOwnProperty للتأكد من أن الخاصية المطلوبة هي تصدير حقيقي وليست موروثة من سلسلة النماذج الأولية.
server.js بدلاً من Next.js؟أستخدم server.js خام (وملف مساعد webpack-runtime.js) لتكوين بيئة تشغيل React Server Components يدويًا. يتيح لنا ذلك:
module.exports = fn). قد يغيّر أي bundler حقيقي طريقة تغليف التصديرات اعتمادًا على إعداداته.react-server-dom-webpack وليس في Next.js.react-server-dom-webpack أنه يعمل داخل حزمة Webpack. ملف webpack-runtime.js يوفّر له المتغيرات العامة التي يتوقعها (__webpack_require__، __webpack_chunk_load__).
هذا ليس محاكاة للثغرة، بل مجرد توفير الحد الأدنى من بيئة التشغيل التي تحتاجها المكتبة لتعمل فعليًا.كانت هناك نقاشات حول "PoCs غير صالحة" تعمل فقط إذا قام المطور بتعريض أشياء خطيرة عن قصد مثل child_process.exec.
هذا الـ PoC ليس واحدًا منها. إنه يعمل على إعداد طبيعي وآمن.
الدالة المكشوفة غير ضارة
يعرّض التطبيق دالة updateProfile بسيطة تُرجع سلسلة نصية فقط، ولا شيء مريب، ولا أوامر شل.
الاستغلال يتجاوز تلك الدالة بالكامل
تتيح الثغرة للمهاجم تجاهل التصدير الآمن والانتقال مباشرة إلى updateProfile.constructor، وهو مُنشئ Function العام.
المشكلة الأساسية هي الوصول إلى الخاصية
كان يجب ألّا يسمح React بالوصول إلى .constructor. لم ينوِ المطور تعريض مُنشئ Function، بل قامت عملية إلغاء التسلسل غير الآمنة بذلك نيابةً عنه.
الشرط الحقيقي الوحيد هو أن تصدّر الوحدة دالة مباشرة (module.exports = fn)، وهو أمر شائع جدًا في CommonJS والعديد من إعدادات bundler.
تصوغ الحمولة في exploit.js رسالة React Flight بثلاثة أجزاء:
id: "user-profile-action#constructor"، أي "أعطني الـ constructor".bound: يشير إلى الجزء 2، الذي يحتوي على الوسائط.["console.log('nice try, diddy!')"]: السلسلة النصية للكود الخبيث.عندما يقوم React بإلغاء تسلسل هذا:
user-profile-action..constructor => يحصل على Function العام.new Function("console.log('nice try, diddy!')")وهذا هو الـ RCE!
قم بالترقية فورًا إلى الإصدارات المصححة:
react-server-dom-webpack >= 19.0.1react-server-dom-parcel >= 19.0.1react-server-dom-turbopack >= 19.0.1يضيف التصحيح فحوصات hasOwnProperty لمنع الوصول إلى الخصائص الموروثة ويقيد تحميل ملفات base64.
إذا قمت بتشغيل هذا الـ PoC ضد إصدار مصحح، فسوف يتعطل الخادم أو يظهر خطأ:
$ node --conditions react-server server.js
Listening on http://localhost:3000
/path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726
resolvedValue = resolvedValue.bind.apply(
^
TypeError: Cannot read properties of undefined (reading 'bind')
at /path/to/CVE-2025-55182/node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js:2726:43
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.19.3
يؤكد هذا أن الاستغلال فشل في الوصول إلى خاصية constructor (أرجعت undefined بدلاً من Function)، وبالتالي فشلت استدعاء .bind اللاحق.
هذا الكود مخصص للأغراض التعليمية والاختبارية فقط. لا تستخدم هذا الاستغلال ضد أنظمة لا تملكها أو ليس لديك إذن صريح لاختبارها.
صدر بموجب DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE.