
توثّق هذه الوثيقة عملية بحثي الشخصية في CVE-2025-55182، بما في ذلك النتائج المؤكدة والتجارب التي أجريتها. وهي تمثّل سردًا صادقًا لتحقيقي، بما في ذلك الافتراضات الخاطئة الأولية والاختراق النهائي.
بعد تحقيق موسّع في CVE-2025-55182 (درجة خطورة CVSS 10.0)، كان استنتاجي الأولي أن تنفيذ الأكواد البرمجية عن بُعد (RCE) التلقائي لم يُثبت علنًا وأن الاستغلال يتطلب أدوات مساعدة (gadgets) خاصة بالتطبيق. كان هذا الاستنتاج خاطئًا.
في 5 ديسمبر 2025، بعد تلقي رؤى إضافية من باحث مستقل آخر على منصة X (@maple3142)، نجحت في إعادة إنتاج تنفيذ أكواد برمجية عن بُعد (RCE) كامل غير مصادَق عليه على Next.js القياسي دون الحاجة إلى أي ثغرات برمجية خاصة بالتطبيق.
تركز اهتمامي الأولي على التغيير الأول في تصحيح React 19.0.1:
النسخة الضعيفة (19.0.0):
return fn.bind.apply(fn, [null].concat(_ref));
النسخة المصححة (19.0.1):
if (Array.isArray(promiseValue)) {
promiseValue = promiseValue.slice(0);
} else {
promiseValue = [];
}
افترضت أن مسار الهجوم يتم عبر fn.bind.apply() مع كائنات خبيثة بدلاً من المصفوفات. تمكنت من إثبات حقن الوسائط في Server Actions باستخدام $ACTION_REF_ مع bound الذي يتحكم فيه المهاجم:
curl -X POST http://localhost:9000/ \
-F '$ACTION_REF_0=' \
-F '$ACTION_0:0={"id":"<ACTION_ID>","bound":["; id #","/etc/passwd"]}'
النتيجة: تم حقن الوسائط بنجاح في Server Action. ومع ذلك، فإن هذا يؤدي فقط إلى RCE إذا كانت الدالة الهدف تستخدم تلك الوسائط بطريقة غير آمنة.
كشف نظرة أعمق في التصحيح عن تغيير مهم آخر في getOutlinedModel():
النسخة الضعيفة:
for (key = 1; key < reference.length; key++)
parentObject = parentObject[reference[key]];
النسخة المصححة:
if (hasOwnProperty.call(value, name)) {
value = value[name];
}
هذا السلوك الضعيف سمح بالتنقل عبر سلسلة النماذج الأولية (prototype chain) باستخدام مراجع مثل:
$1:__proto__:constructor:constructor
أثناء البحث، اختبرت كائنًا قابلاً للـ then (thenable) يحتوي على خاصية .then:
{"then": "$1:__proto__:constructor:constructor"}
عندما يعالج JavaScript هذا عبر await:
.then ويعامل الكائن على أنه Promiseobj.then(resolve, reject)then تشير إلى Function.constructor، يحاول JavaScript تنفيذ Function(resolve, reject)النتيجة الملاحظة:
SyntaxError: Unexpected token 'function'
at Object.Function [as then] (<anonymous>)
عند استدعاء Function.constructor بالشكل:
Function(resolve, reject)
// resolve.toString() = "function () { [native code] }"
// Function attempts to parse this as code → SyntaxError
الوسيطان resolve وreject هما دائمًا دالتا Promise الأصليتان. يحاول Function تفسير الوسيط الأول على أنه كود مصدري، وهو JavaScript غير صالح.
هنا توقف بحثي. خلصت إلى أن التحكم في وسائط Function.constructor كان مستحيلاً دون وجود أداة مساعدة (gadget) خاصة بالتطبيق.
بعد نشر النتائج الأولية، أشارني باحث مستقل آخر إلى قطعة حاسمة فاتتني: نقطة إغراق (sink) إزالة التسلسل $B (Blob).
في كود React Flight منفذة الخادم المُجمَّع (غير الظاهر في مصادر TypeScript)، يوجد:
case "B":
return response._formData.get(response._prefix + id);
الموقع:
[email protected]cjs/react-server-dom-webpack-server.node.unbundled.development.js[email protected]/dist/compiled/react-server-dom-webpack/يسمح هذا الكود لـ React باستدعاء response._formData.get() بقيم مشتقة من مدخلات يتحكم فيها المهاجم، بدون تحقق.
| النهج | وسائط Function.constructor | النتيجة |
|---|---|---|
| كائن قابل للـ then (المرحلة 3) | resolve, reject (دوال أصلية) | ❌ SyntaxError |
| Blob + استجابة مسمومة | _prefix (سلسلة يتحكم فيها المهاجم) | ✅ RCE |
من خلال الجمع بين:
$1:__proto__:then → Chunk.prototype.then)_response مسموم يحتوي على:
_formData.get مضبوطًا على Function.constructor_prefix مضبوطًا على كود JavaScript عشوائي$Bينفّذ معالج case "B": التالي:
Function.constructor("<attacker code>" + id)
هذا يتجاوز قيد ربط الوسائط بالكامل.
تستخدم PoCs الشهيرة على GitHub التي تدّعي RCE معرّفات Actions مثل:
"child_process#execSync""vm#runInThisContext"هذه مزيفة. يقبل Next.js فقط معرّفات Actions المعرّفة من قبل التطبيق. المعرّفات غير الصالحة تُنتج:
TypeError: Cannot read properties of undefined (reading 'workers')
ومع ذلك، الاستغلال الحقيقي لا يتطلب معرّفات Actions مزيفة. أي معرّف Server Action صالح يعمل.
اختبرت سلسلة الاستغلال هذه على تطبيق Next.js 15.0.3 + React 19.0.0 الأدنى الذي يحتوي فقط على:
async function myAction(data) {
"use server";
console.log("Server Action called with:", data);
return { success: true, received: data };
}
النتيجة: تم تأكيد RCE كامل. لم يحتوِ التطبيق على أي كود غير آمن، ولا eval، ولا execSync، ولا أدوات مساعدة.
اعتبارًا من 5 ديسمبر 2025، لا تزال نقطة إغراق $B موجودة في Next.js 15.0.5 (النسخة المزعوم تصحيحها).
التحقق:
$ npm pack [email protected]
$ tar -xzf next-15.0.5.tgz
$ grep -A5 'case "B":' package/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-server.node.development.js
النتيجة:
case "B":
return response._formData.get(response._prefix + obj);
الكود مطابق تمامًا للنسخة الضعيفة.
نظرًا لاكتشاف أن الثغرة قد لا تكون مصححة بالكامل في الإصدارات "المصححة" المزعومة، سأحجب حمولة إثبات المفهوم (proof-of-concept) الكاملة لحين التحقق مع فرق الأمان في Vercel وMeta.
التفاصيل التقنية المقدمة في هذه الوثيقة كافية لفهم آلية الثغرة ولكنها ناقصة عمدًا لمنع الاستغلال الفوري.
| التقنية | الحالة |
|---|---|
حقن الوسائط عبر bound | ✅ مؤكد (تأثير محدود) |
| التنقل عبر النماذج الأولية | ✅ مؤكد |
| المسألة | الأثر |
|---|---|
نقطة إغراق إزالة التسلسل $B (Blob) | ❌ حرجة - تتيح التحكم في الوسائط |
| فحص الكود المُجمَّع مقابل الكود المصدري | ❌ النقطة موجودة فقط في المخرجات المُجمَّعة |
| آلية تسميم كائن الاستجابة | ❌ تتيح تجاوز جميع الحمايات |
يمكن استغلال CVE-2025-55182 لتنفيذ أكواد برمجية عن بُعد (RCE) كامل غير مصادَق عليه على تطبيقات Next.js القياسية.
$B من باحث مستقل$B لا تزال تقبل كائنات _response عشوائيةNext-Action غير معتادة$@ و__proto__ و$B$B قدمها باحث على منصة X (@maple3142)آخر تحديث: 5 ديسمبر 2025
| الجانب | النتيجة |
|---|
| مطلوب مصادقة؟ | ❌ لا |
| مطلوب أداة مساعدة خاصة بالتطبيق؟ | ❌ لا |
| يعمل على Next.js القياسي؟ | ✅ نعم |
| عدد الطلبات المطلوبة | 1 طلب POST |
| الإصدارات المتأثرة | Next.js ≤15.0.4 + React 19.0.0 |
| درجة CVSS | 10.0 (مبررة) |
الوصول إلى Function.constructor عبر كائن قابل للـ then | ✅ مؤكد (لكنه غير قابل للاستغلال بمفرده) |
| اكتشاف الإصدارات الضعيفة | ✅ مؤكد |