لو موقعك على ووردبريس ولسه ما حدّثتش لـ 7.1.2، وقّف اللي في إيدك وحدّث الأول، وبعدين كمّل القراية. الثغرة دي مش في إضافة ولا قالب، دي في النواة نفسها، ومحتاجة طلب واحد من غير أي تسجيل دخول. والمهاجمين بدأوا يجرّبوها في نفس يوم نزول التحديث.
الخلاصة السريعة قبل التفاصيل
إصدار ووردبريس (WordPress) رقم 7.1.2 تحديث أمني خالص يعالج ثغرة واحدة هي CVE-2026-87902. كل الإصدارات من 4.7.0 حتى 7.1.1 متأثرة، أي ما يقرب من عشر سنوات من الإصدارات.
| البند | التفاصيل |
|---|---|
| الرقم | CVE-2026-87902 |
| — | — |
| المكان | الدالة get_page_template() في wp-includes/template.php |
| النوع | اجتياز مسار (Path Traversal) ← تضمين ملفات محلية (السيرفر بمسار يتحكم فيه المهاجم، فيُنفَّذ كود لم يقصده المطوّر، وقد تنتهي بالسيطرة على الموقع إذا وُجد على السيرفر…">LFI) ← تنفيذ كود مشروط (RCE) |
| الخطورة | CVSS 4.0 بدرجة 9.2 (حرجة) |
| تسجيل الدخول | غير مطلوب |
| الإصدارات المتأثرة | من 4.7.0 حتى 7.1.1 |
| الإصدارات المصلَّحة | 7.1.2 و7.0.6 و6.9.9 و6.8.10، ونُقل الإصلاح حتى 4.7.37 |
الفكرة في جملة واحدة: اسم ملف القالب في Elementor تصميم محفوظ يُعاد استخدامه، مثل رأس الموقع أو التذييل أو صفحة المقالة أو صفحة المنتج. تصممه مرة وتحدد شروط ظهوره، فيُطبَّق على كل…">القالب الذي يُبنى من قيمة pagename القادمة في الطلب لم يمر أبدًا على فحص المسار.
كيف يختار ووردبريس قالب الصفحة؟
عند عرض أي صفحة، يحتاج ووردبريس إلى تحديد ملف القالب (Theme) الذي سيرسمها. الدالة get_page_template() تفعل ذلك على مرحلتين: تبني قائمة بأسماء ملفات مرشحة مرتبة حسب الأولوية، مثل page-about.php ثم page.php، ثم تسلّم القائمة لمحمّل القوالب الذي يضمّن أول ملف موجود منها.
النقطة الحساسة أن بعض هذه الأسماء يأتي من الطلب نفسه. فالقيمة pagename هي الاسم اللطيف (slug) للصفحة من الرابط، أي أن مدخلات المستخدم تدخل في مسار ملف. وهذا بالضبط الموضع الذي يصبح فيه التحقق إلزاميًا.
الكود المصاب سطرًا بسطر
هذا هو الجزء المعني من الدالة في الإصدار 7.1.1 وما قبله، كما نشرته Patchstack:
// wp-includes/template.php, get_page_template(), WordPress <= 7.1.1
if ( $template && 0 === validate_file( $template ) ) {
$templates[] = $template;
}
if ( $pagename ) {
$pagename_decoded = urldecode( $pagename );
if ( $pagename_decoded !== $pagename ) {
$templates[] = "page-{$pagename_decoded}.php";
}
$templates[] = "page-{$pagename}.php";
}
الجزء الأول: مرشّح محمي
المتغير $template هو القالب الذي اختاره المدير للصفحة، ولا يدخل القائمة إلا بعد المرور على validate_file(). هذه الدالة تعيد صفرًا عندما يكون المسار آمنًا، وتعيد قيمة أخرى إذا وجدت تسلسلات خطرة مثل ../.
إذًا ووردبريس كان يعرف أن هذا النوع من القيم خطر، وحمى الحالة الأولى فعلًا.
الجزء الثاني: مرشّح بلا حماية
في الجزء الثاني تُفك ترميز pagename بالدالة urldecode()، فتتحول تسلسلات مثل %2f إلى / حقيقية. وإذا اختلفت القيمة بعد فك الترميز، يُبنى منها اسم ملف ويُضاف مباشرة إلى القائمة.
هنا تكمن المشكلة: الفحص validate_file() الموجود قبلها بثلاثة أسطر غائب تمامًا. الحارس موجود، لكنه لم يُوضع على الباب الذي يحتاجه.
لماذا ينجو ../ من التنظيف؟
ووردبريس ينظّف الاسم اللطيف قبل وصوله إلى هذه الدالة، وهنا يصبح الأمر مثيرًا. فالمنظّف يحذف النقاط والشرطات المائلة الحرفية، لكنه يترك التسلسلات المرمّزة مثل %2f و%2e عمدًا.
النتيجة مسار من ثلاث خطوات:
- المنظّف يرى نصًا مرمّزًا يبدو بريئًا فيمرّره.
- الدالة
urldecode()تعيد../الحقيقية. - القيمة المستعادة تُستخدم في مسار ملف دون أي تحقق.
هذا نمط كلاسيكي اسمه الفجوة بين الفحص والاستخدام (Time-of-check to time-of-use)، أو فك الترميز بعد الفحص. وهو ما يفسر أن طلبات الهجوم الحقيقية تستخدم ترميزًا مزدوجًا مثل %252f: خط معالجة الطلب يفك الترميز مرة، ثم urldecode() تفكه مرة ثانية.
ما الذي يقيّد المهاجم؟
التحكم في جزء من المسار لا يعني قراءة أي ملف. الاسم الناتج دائمًا على هذا الشكل:
page- + (attacker value) + .php
وهذا يفرض قيدين:
- البادئة
page-: يجب أن تكمل القيمة اسم مجلد حقيقي يبدأ بـpage-في جذر القالب النشط، مثلpage-templates. بعض القوالب الافتراضية القديمة وعدد من القوالب التجارية المشهورة فيها مجلد كهذا. - اللاحقة
.php: تُضاف تلقائيًا، لذا يجب أن يكون الهدف ملف PHP قابلًا للقراءة.
وهناك شرط ثالث عملي: طلبات الاستغلال ترسل دائمًا page_id صالحًا مع pagename. فبدون صفحة حقيقية يعيد الاستعلام نتيجة فارغة، ويعرض ووردبريس صفحة 404، ولا يصل التنفيذ إلى الكود المصاب أصلًا.
من تضمين ملف إلى تنفيذ كود
تضمين الملفات المحلية (LFI) يعمل دائمًا، لكن تنفيذ الكود عن بُعد (RCE) يحتاج أن تتوافق ظروف السيرفر. فتضمين ملف محلي يشغّل ما يفعله هذا الملف أصلًا، لا كودًا كتبه المهاجم.
لذلك يبحث المهاجم عن ملف يفعل شيئًا مفيدًا له بمجرد تضمينه، والمرشح المعروف هو pearcmd.php من حزمة PEAR. عند تفعيل الإعداد register_argc_argv في PHP، تتحول سلسلة الاستعلام في الرابط إلى متغير $argv كأنها أوامر سطر أوامر. ولأن pearcmd يملك ميزة إنشاء ملفات، يمكن كتابة ملف PHP بمحتوى يختاره المهاجم.
وهذا الإعداد ليس نادرًا كما قد تظن: فهو مفعّل افتراضيًا في صور Docker الرسمية لـ PHP وفي بيئات cPanel مع إصدارات PHP أقدم من 8.5. وcPanel هو لوحة التحكم الأشهر عند أغلب شركات الاستضافة المشتركة (Shared Hosting) التي يستخدمها أصحاب المواقع في مصر والخليج.
| الشرط | المستوى | النتيجة |
|---|---|---|
مجلد يبدأ بـ page- في القالب | أساسي | يفتح باب الثغرة |
| — | — | — |
| ملف PHP قابل للقراءة يُضمَّن | الثغرة نفسها | تشغيل ملف نواة في رابط خاطئ (كافٍ لكشف أن الموقع مصاب) |
وجود pearcmd.php على السيرفر | مضاعِف للخطر | هدف مفيد للمهاجم |
تفعيل register_argc_argv | حرج | كتابة ملفات وتنفيذ كود كامل |
ماذا غيّر الإصلاح؟
أضاف فريق الأمان في ووردبريس تغييرين. الأول هو الفحص الناقص نفسه:
// wp-includes/template.php, WordPress 7.1.2
if ( $pagename_decoded !== $pagename && 0 === validate_file( $pagename_decoded ) ) {
$templates[] = "page-{$pagename_decoded}.php";
}
الآن تمر القيمة بعد فك ترميزها على validate_file()، فإذا ظهر فيها ../ لا تدخل القائمة أصلًا.
والتغيير الثاني دالة احتواء جديدة تُطبَّق على كل مسار قالب:
// wp-includes/template.php, new in WordPress 7.1.2
function _wp_is_template_path_allowed( $path ) {
global $wp_stylesheet_path, $wp_template_path;
// A file path that exists and does not contain `..` is allowed.
if ( 0 === preg_match( '#(?:^|/)..[. ]*(?:/|$)#', wp_normalize_path( $path ) ) ) {
return true;
}
// Otherwise resolve the real path and require it to sit inside an allowed theme directory.
$real_path = realpath( $path );
// ...
}
الدالة توحّد الفواصل أولًا، ثم تبحث بتعبير نمطي عن أي جزء .. في المسار، بما في ذلك الصيغ الملتوية التي تتبعها نقاط أو مسافات إضافية. المسارات العادية تمر فورًا، أما المشبوهة فيُحسب مسارها الحقيقي بـ realpath() ويجب أن يقع داخل مجلد القالب أو القالب الأب أو theme-compat.
الإصلاح الأول كان يكفي لسد الثغرة المبلَّغ عنها. لكن إضافة الثاني تعني أن فريق ووردبريس تعامل مع اختيار القوالب كفئة كاملة من المشكلات، لا كخطأ واحد: أيًا كان مصدر المسار، يجب أن ينتهي داخل مجلد مسموح.
كيف تحرك المهاجمون؟
الجدول الزمني بحسب Patchstack يوضح مدى السرعة:
| التوقيت | الحدث |
|---|---|
| 22 سبتمبر 2026 | صدور 7.1.2 ونشر التنبيه الأمني GHSA-7hp8-65ch-5whp |
| — | — |
| 22 سبتمبر، 11:49 UTC | أول محاولة استغلال، مبنية على مقارنة كود الإصلاح |
| 22 سبتمبر، 15:34 UTC | أول محاولة لكتابة ملف عبر pearcmd |
| 23 سبتمبر 2026 | ماسحات عامة وقالب Nuclei جاهز، والحركة تضاعفت أكثر من 10 مرات |
الهجوم يمر بثلاث مراحل: التأكد من أن التضمين يعمل بتضمين ملف نواة عادي مثل wp-links-opml.php، ثم التأكد من وجود pearcmd، ثم كتابة ملف PHP في /tmp أو /var/tmp. الملف في /tmp لا يُفتح عادة من المتصفح، لكنه دليل على أن التنفيذ نجح، ونفس الطريقة يمكن توجيهها إلى أماكن أخطر.
خطوات الحماية على موقعك
1. حدّث الآن
توجد نسخ مصلَّحة لكل فرع مدعوم: 7.1.2 و7.0.6 و6.9.9 و6.8.10، مع إصلاح منقول حتى 4.7.37. يعني حتى المواقع القديمة تستطيع أخذ الإصلاح دون قفزة كبيرة في الإصدار. من لوحة التحكم (Dashboard) ادخل على «التحديثات»، أو استخدم WP-CLI:
wp core version
wp core update --minor
wp core verify-checksums
الأمر الأخير يتأكد أن ملفات النواة مطابقة للنسخة الرسمية ولم يُعدَّل فيها شيء.
2. قدّر حجم الخطر
هذه الأسئلة تقيس احتمال تنفيذ الكود، لا وجود الثغرة نفسها؛ فوجود الثغرة يحدده رقم الإصدار فقط:
- هل في القالب النشط مجلد في الجذر يبدأ اسمه بـ
page-؟ - هل
register_argc_argvمفعّل؟ إذا كان معطلًا، يُغلق طريق pearcmd ويبقى خطر التضمين فقط. - هل على السيرفر
pearcmd.phpأو ملفات PHP أخرى تفعل شيئًا مفيدًا عند تضمينها؟
php -i | grep register_argc_argv
find / -name pearcmd.php 2>/dev/null
3. لو لا تستطيع التحديث فورًا
- ارفض أي قيمة
pagenameتحتوي على تسلسلات اجتياز. الاسم اللطيف الحقيقي لا يحتويها أبدًا، فالقاعدة آمنة على الزوار العاديين. يمكنك ضبطها في جدار الحماية عند Cloudflare أو في إضافة الحماية التي تستخدمها. - عطّل
register_argc_argvمن إعدادات PHP. هذا لا يصلح التضمين، لكنه يكسر سلسلة pearcmd ويعيد المشكلة إلى مجرد تضمين ملفات.
4. ابحث في السجلات
راجع سجلات الوصول (access logs) من عند الاستضافة عن هذه العلامات:
- قيمة
pagenameتحتوي على%2e%2eأو%252e%252e، في الرابط أو في جسم طلب POST. - قيمة
pagenameتبدأ بـtemplates%2fأو باسم مجلدpage-آخر. pagenameوpage_idمعًا على جذر الموقع أو على/index.php.- أي طلب يحتوي على
pearcmdأو+config-showأو+config-create. - وكلاء المستخدم
cve-2026-87902-poc/1.0وnuclei-cve-2026-87902/1.0، مع العلم أن أغلب الهجمات تنتحل متصفحات عادية. - محتوى OPML أو RSS يظهر في رابط صفحة عادية يعني أن مرحلة الاختبار الأولى نجحت.
- ملفات
.phpغير متوقعة في/tmpأو/var/tmpتعني أن الكتابة نجحت، وهنا اعتبر السيرفر مخترقًا وتعامل معه على هذا الأساس.
في Elementor: الثغرة في النواة وليست في Elementor أو JetEngine، لذا فكل موقع مبني بهما متأثر ما دام إصدار ووردبريس قديمًا. افتح مجلد القالب النشط عندك (سواء Hello أو قالب فرعي) وتأكد من عدم وجود مجلد يبدأ بـ
page-في جذره، لكن تذكّر أن هذا يقيس حجم الخطر فقط؛ التحديث هو الحل الوحيد المضمون.
اقرأ أيضًا
الأسئلة الشائعة
س: هل موقعي مصاب إذا كنت أستخدم إصدارًا أقدم من 4.7؟
ج: الثغرة تبدأ من 4.7.0، لكن أي إصدار أقدم من ذلك مليء بثغرات أخرى معروفة وغير مدعوم. الحل الترقية إلى فرع مدعوم يحتوي على الإصلاح.
س: هل التحديثات التلقائية لووردبريس تكفي؟
ج: غالبًا نعم، لأن التحديثات الأمنية الصغيرة تُثبَّت تلقائيًا افتراضيًا. تأكد من رقم الإصدار في لوحة التحكم، فبعض الاستضافات أو الإضافات تعطل التحديث التلقائي.
س: هل تكفي إضافة حماية أو جدار حماية بدل التحديث؟
ج: جدار الحماية يخفف الخطر مؤقتًا بحجب الأنماط المعروفة، لكن المهاجمين يطورون صيغ ترميز جديدة. التحديث هو الإصلاح الفعلي.
س: كيف أعرف أن موقعي اختُرق عبر هذه الثغرة؟
ج: ابحث في السجلات عن العلامات المذكورة أعلاه، وافحص /tmp و/var/tmp بحثًا عن ملفات PHP غريبة، وشغّل wp core verify-checksums. أي نتيجة مريبة تعني تغيير كلمات المرور ومفاتيح wp-config.php واستعادة نسخة احتياطية (Backup) نظيفة.
الخلاصة
ثغرة واحدة نسي فيها سطر تحقق واحد فتحت الباب لعشر سنين من إصدارات ووردبريس. حدّث موقعك النهارده، وبعدين راجع السجلات وإعداد register_argc_argv عند الاستضافة، ولو عندك مواقع لعملاء ابعت لهم تنبيه قبل ما حد تاني يوصل لهم الأول.
مرجع المقال (بالإنجليزية): CVE-2026-87902: Unauthenticated LFI (and Conditional RCE) in WordPress Core, Explained Line by Line
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.



