
تحليل موجز لثغرة RCE في React Server Components
في اليومين الماضيين، انتشرت أنباء عن ثغرة RCE (تنفيذ التعليمات البرمجية عن بُعد) في React عبر إلغاء التسلسل، وقد منحها التقييم الرسمي CVSS درجة كاملة 10.0، مماثلة لـ Log4j في ذلك الوقت. على الفور، انتشرت شائعات زعمت أنها "Log4j للواجهات الأمامية الحديثة"، مما تسبب في حالة من الذعر بين المطورين في العديد من الشركات، فاستيقظ الجميع وهم يبحثون عن الوثائق ويطبقون التصحيحات... وفي الوقت نفسه، ظهرت على الإنترنت أصوات تشكك عديدة، حيث اختبرها البعض ووجدوا أن الثغرة ليست كما تم الترويج لها، بل تتطلب شروطًا معينة للاستغلال. لذلك قررت أن أخصص وقتًا لدراسة هذه الثغرة بعمق.
react-server-dom-webpack < 19.2.0، react-server-dom-turbopack < 19.2.0سبب هذه الثغرة هو ما يلي، في [email protected]، الدالة الرئيسية التي يقوم الخادم من خلالها بتحليل إجراء الخادم هي requireModule (شفرة زائفة):
function requireModule(metadata) {
var moduleExports = __webpack_require__(metadata[0]);
// ...
return "*" === metadata[2]
? moduleExports
: "" === metadata[2]
? moduleExports.__esModule
? moduleExports.default
: moduleExports
: moduleExports[metadata[2]]; // ← نقطة الثغرة
}
المشكلة الأساسية للثغرة تكمن في الجزء moduleExports[metadata[2]]، حيث لا يتم التحقق من صحة الجزء metadata[2]، مما يسمح للمهاجم ليس فقط بالوصول إلى خصائص التصدير الخاصة بالوحدة نفسها، بل أيضًا إلى الخصائص الموجودة في سلسلة النموذج الأولي (مثل constructor، __proto__، إلخ). عندما يقوم المهاجم ببناء metadata[0] (على سبيل المثال، توجيهه إلى vm)، يمكنه بعد ذلك بناء metadata[2] لتحقيق تصدير طرق خطيرة من وحدة محددة، مثل vm.runInThisContext، مما يؤدي إلى استغلال الثغرة.
أثناء تحليلي، قمت بالرجوع إلى بيئة الاختبار واستغلال الثغرة التي قدمها ejpir، وتناولت أداة Code Execution gadget vm_runInThisContext كمثال للتحليل، وكانت العملية على النحو التالي (لاحظ أنه في البيئة الحقيقية، ستكون عملية استغلال الثغرة مختلفة إلى حد ما!):
أولاً، بعد إرسال الطلب مع الحمولة، نقطة التوقف للحصول على موقع الطلب تكون كما يلي:


بعد ذلك، يتم تنفيذ البرنامج حتى الموقع const formData = parseMultipart(buffer, boundaryMatch[1]);، ثم ندخل إلى parseMultipart

يقوم parseMultipart باستخراج بيانات جسم الطلب وإرجاعها إلى formData

ندخل إلى const actionFn = await decodeAction(formData, serverManifest); موقع حدوث الثغرة

ندخل إلى loadServerReference


الآن نصل إلى موقع الكود الأساسي للثغرة requireModule، ندخل إليه


يعيد قيمة id مع ما قبل وبعد # كطريقة للوحدة، ويعيد قيمة المعلمة bound كمعامل للطريقة




ندخل إلى actionFn، وننفذ الحمولة النهائية



وبهذا، انتهى استغلال الثغرة!
هذه الثغرة في جوهرها ناتجة عن عدم صرامة التحقق من الإدخال، وهذا يتطابق مع Log4j و fastjson. اختبرت أعلاه vm_runInThisContext، في الواقع هذه الثغرة بها أدوات متعددة قابلة للاستغلال، مثل
vm#runInThisContextvm#runInNewContextchild_process#execSyncchild_process#execFileSyncchild_process#spawnSyncfs#readFileSyncfs#writeFileSync#constructor#__proto__#prototypeيمكن للمهاجم باستخدام هذه الثغرة تحقيق:
vm#runInThisContext أو child_process#execSync لتنفيذ أي أمر نظامfs#readFileSync، fs#writeFileSync لقراءة/كتابة أي ملف.bashrc، استبدال ملفات التطبيق، إلخ.env، المفاتيح الخاصة، بيانات اعتماد قاعدة البيانات، إلخ)بناءً على ذلك، يمكن تقديم إجراءات دفاعية ذات صلة، على النحو التالي
بناءً على ذلك، يمكن أن ينطلق الدفاع المؤقت من هذه الزوايا، يمكن تكوين قواعد في جدار الحماية من تطبيقات الويب (WAF) لاعتراض هذه الحقول الخطيرة، وبالتالي اعتراض الهجمات الضارة في الوقت المناسب. بالإضافة إلى ذلك، يمكن أيضًا إجراء تطابق واعتراض في nginx، على النحو التالي:
# مثال تكوين Nginx
location /formaction {
# اعتراض الطلبات التي تحتوي على مراجع وحدات خطيرة
if ($request_body ~* "(vm#|child_process#|fs#|module#)") {
return 403;
}
# اعتراض محاولات تلوث سلسلة النموذج الأولي
if ($request_body ~* "(#constructor|#__proto__|#prototype)") {
return 403;
}
}
قامت الجهة الرسمية حاليًا بإصدار تحديث أمني، قم بالترقية فورًا إلى الإصدار الآمن!:
# ترقية react-server-dom-webpack
npm install react-server-dom-webpack@>=19.2.0
# ترقية react-server-dom-turbopack
npm install react-server-dom-turbopack@>=19.2.0
# مستخدمو Next.js
npm install next@>=15.0.5
الإصدارات التي تم إصلاحها:
react-server-dom-webpack: >= 19.2.0react-server-dom-turbopack: >= 19.2.0next.js: >= 15.0.5