ثغرة Comment2XSS: تعليق واحد قد يسلّم سيرفر ووردبريس

فقاعة تعليق مضيئة تتسلل من شق في جدار حصين ودرع أزرق يغلقه، رمزاً لثغرة ووردبريس CVE-2026-93485 وترقيعها

لو عندك موقع ووردبريس فيه تعليقات مفتوحة، سيب اللي في إيدك خمس دقايق. ثغرة جديدة في النواة نفسها، مش في إضافة، بتخلي زائر مجهول يكتب تعليق، وأول ما المدير يفتح المقالة ممكن الموضوع يوصل لتحكم كامل في السيرفر. الحل تحديث واحد، والتفاصيل تحت.

بطاقة الثغرة باختصار

البندالتفاصيل
المعرّفCVE-2026-93485
——
الاسمComment2XSS (أُعلنت أولاً باسم Comment2Shell)
الإصدارات المتأثرةمن 4.7 حتى 7.1 قبل الإصدار المرقّع لكل فرع
الإصلاح7.1.1، مع ترقيع خلفي إلى 7.0.5 و6.9.8 وصولاً إلى 4.7.36
الخطورة (CVSS 3.1)7.1 — عالية
النوعXSS مخزّن (CWE-79) قابل للتصعيد إلى تنفيذ أوامر
الصلاحية المطلوبةلا شيء؛ يكفي تعليق مجهول
المكتشفRafie Muhammad عبر برنامج مكافآت ووردبريس على HackerOne

البيانات أعلاه منقولة عن التحليل المنشور في المصدر. راجع صفحة الإصدار الرسمية في لوحة تحكم موقعك للتأكد من رقم الإصدار المرقّع لفرعك.

الفكرة في سطرين: فجوة بين التنقية والعرض

ووردبريس يتعامل مع التعليق على مرحلتين. عند الحفظ يمرّ النص على مُرشِّح KSES، الذي يحذف أي وسم أو خاصية ليست في قائمته المسموحة. وعند العرض تعيد سلسلة من الدوال المعلّقة على الفلتر comment_text تنسيق الـ HTML من جديد:

// wp-includes/default-filters.php:225-230
add_filter( 'comment_text', 'wptexturize' );
add_filter( 'comment_text', 'convert_chars' );
add_filter( 'comment_text', 'make_clickable', 9 );
add_filter( 'comment_text', 'force_balance_tags', 25 );
add_filter( 'comment_text', 'convert_smilies', 20 );
add_filter( 'comment_text', 'wpautop', 30 );

كل مرحلة سليمة وحدها. المشكلة أن دوال العرض تفهم الناتج المحفوظ بطريقة لم يتوقعها المُرشِّح، وفي هذه الفجوة يختبئ الهجوم. المتهمان الرئيسيان هما wpautop() و wptexturize().

كيف تتحول فجوة صغيرة إلى سكربت يعمل؟

لن ننشر هنا حمولة الهجوم الجاهزة، فهدفنا أن تفهم الآلية وتحمي موقعك. المهم أن تعرف السلسلة:

1. سطر جديد داخل خاصية cite

الوسم <blockquote> وخاصيته cite مسموحان في التعليقات. عند إعادة ترميز قيم الخصائص، لا يهرّب KSES إلا خمسة رموز:

// wp-includes/kses.php:1721-1727
$syntax_characters = array(
    '&' => '&amp;',
    '<' => '&lt;',
    '>' => '&gt;',
    "'" => '&apos;',
    '"' => '&quot;',
);

رمز السطر الجديد n ليس منها، فيُحفظ كما هو داخل قيمة cite. حتى هذه اللحظة لا يوجد خطر.

2. دالة الفقرات تضع تعليق HTML داخل الوسم

دالة الفقرات التلقائية (wpautop) تحتاج أن تميّز السطر الجديد داخل وسم عن السطر الجديد بين فقرتين، فتستبدل مؤقتاً كل سطر جديد داخل وسم بعلامة:

// wp-includes/formatting.php:502
// Find newlines in all elements and add placeholders.
$text = wp_replace_in_html_tags( $text, array( "n" => ' <!-- wpnl --> ' ) );

العلامة <!-- wpnl --> نفسها تحتوي على الرمز >، وهذا الرمز الشارد هو الطُعم.

3. التعبير النمطي يتوقف عند الرمز الخطأ (جذر المشكلة)

لاحقاً تنظّف الدالة حالة تغليف <p> لوسم <blockquote> بهذا التعبير:

// wp-includes/formatting.php:563 (vulnerable version)
$text = preg_replace( '|<p><blockquote([^>]*)>|i', '<blockquote$1><p>', $text );

النمط [^>]* يعني “أي حروف ليست >“. المفترض أن يمسك كل شيء حتى نهاية الوسم الحقيقية، لكنه يتوقف عند > المزروع داخل العلامة، فيُحقن <p> في منتصف قيمة cite.

4. دالة علامات التنصيص تكمل الخدعة

الدالة wptexturize() تحوّل علامات التنصيص المستقيمة إلى منحنية، وتعمل تلقائياً في القوالب الكتلية (Block Themes) منذ ووردبريس 6.0، وفي بعض القوالب الكلاسيكية. هي تقرأ الـ <p> المحقون كحدّ وسم حقيقي، فتحوّل علامة التنصيص التي تليه إلى رمز منحنٍ لا يُغلق الخاصية.

في المقابل، تتخطى الدالة محتوى وسوم مثل code:

// wp-includes/formatting.php:106
$default_no_texturize_tags = array( 'pre', 'code', 'kbd', 'style', 'script', 'tt' );

النتيجة أن علامة تنصيص مخبأة داخل <code> تصبح هي الإغلاق الفعلي، وما بعدها يقرؤه المتصفح كخصائص حقيقية على <blockquote>، منها معالج حدث يعمل لحظة تحميل الصفحة دون أي نقرة.

لماذا لا تحميك مراجعة التعليقات؟

ووردبريس يحجز افتراضياً أول تعليق لأي زائر جديد للمراجعة. لكن الباحث وثّق ثلاثة طرق للالتفاف:

  • التعليق التجريبي الافتراضي: أي تثبيت جديد يأتي بتعليق موافَق عليه من “A WordPress Commenter”. التحقق يعتمد على الاسم والبريد معاً، فمن يستخدمهما يُوافَق عليه تلقائياً.
  • إعداد الموافقة مطفأ: لو ألغيت خيار “يجب أن يكون لكاتب التعليق تعليق سابق موافَق عليه”، يُنشر أي تعليق جديد فوراً.
  • التعليق المعلّق يظهر لبعض الزوار: صاحب التعليق يستطيع رؤية تعليقه غير الموافَق عليه عبر رابط المراجعة الخاص به.

إذن عبارة “التعليقات عندنا بتتراجع” لا تغلق الثغرة.

من XSS إلى السيطرة على السيرفر

السكربت المحقون يعمل في متصفح من يفتح الصفحة. مع زائر عادي الضرر محدود. لكن لو فتحها مدير الموقع وهو مسجّل الدخول، يستطيع السكربت استغلال صلاحية رفع الإضافات التي يملكها المدير.

السيناريو الموثّق: السكربت يقرأ رمز الحماية (Nonce) من صفحة رفع الإضافات، ثم يرفع إضافة صغيرة تحتوي على ملف PHP خبيث. الملف لا يحتاج حتى إلى تفعيل؛ بمجرد وجوده في مجلد الإضافات يصبح قابلاً للاستدعاء مباشرة، فيحصل المهاجم على تنفيذ الأوامر عن بُعد (RCE) بصلاحيات مستخدم السيرفر.

بمعنى آخر: تعليق مجهول، ثم زيارة واحدة من المدير، ثم سيرفر مخترق.

الترقيع: تعبير نمطي يفهم علامات التنصيص

الإصلاح في 7.1.1 سطر واحد:

// Before — WordPress 7.1 and earlier
$text = preg_replace( '|<p><blockquote([^>]*)>|i', '<blockquote$1><p>', $text );

// After — WordPress 7.1.1
$text = preg_replace( '!<p><blockquote((?:[^>"']|"[^"]*"|'[^']*')*)>!i', '<blockquote$1><p>', $text );

النمط الجديد يكرر أحد ثلاثة بدائل:

  • [^>"'] — حرف عادي ليس علامة تنصيص ولا >.
  • "[^"]*" — قيمة خاصية كاملة بين علامتي تنصيص مزدوجتين، حتى لو بداخلها >.
  • '[^']*' — قيمة كاملة بين علامتي تنصيص مفردتين.

هكذا لا يُعتبر > داخل قيمة خاصية نهايةً للوسم، ويوضع <p> بعد الإغلاق الحقيقي فقط.

CVE-2026-93485

خطة الطوارئ لمواقع ووردبريس

نفّذها بالترتيب على كل موقع تديره

  1. اعرف إصدارك

    لوحة التحكم ← التحديثات. أي إصدار من 4.7 حتى 7.1 بدون الترقيع معرّض.

  2. حدّث النواة فوراً

    7.1.1 أو الإصدار المرقّع لفرعك: 7.0.5 أو 6.9.8 … حتى 4.7.36.

  3. لا تستطيع التحديث الآن؟

    أغلق التعليقات على الموقع كله مؤقتاً، أو فعّل قاعدة WAF تمنع سطراً جديداً داخل cite.

  4. احذف التعليق التجريبي

    تعليق A WordPress Commenter الافتراضي يفتح باب الموافقة التلقائية.

  5. راجع الإضافات والملفات

    ابحث عن إضافة لا تعرفها في wp-content/plugins حتى لو لم تكن مفعّلة.

ابن موقعك قطعة قطعةarabcms.com

ماذا تفعل الآن على مواقعك ومواقع عملائك؟

  1. حدّث النواة: إلى 7.1.1 لو كنت على 7.1، أو الإصدار المرقّع لفرعك (7.0.5، 6.9.8، وهكذا حتى 4.7.36). خذ نسخة احتياطية (Backup) قبلها إن لم تكن لديك نسخة حديثة.
  2. لو لا تستطيع التحديث فوراً: أغلق التعليقات على المقالات أو على الموقع كله مؤقتاً، فهذا يقطع مسار الهجوم بالكامل.
  3. أضف طبقة حماية مؤقتة: قاعدة في جدار حماية تطبيقات الويب (WAF)، مثل Cloudflare أو إضافة حماية، تمنع الطلبات التي تحتوي على سطر جديد داخل خاصية cite.
  4. احذف التعليق التجريبي الافتراضي إن كان ما زال موجوداً، فهو أحد طرق تخطي المراجعة.
  5. افحص المواقع التي ربما تعرّضت: التحديث يمنع الهجمات الجديدة لكنه لا يزيل ما زرعه مهاجم سابق. راجع مجلد wp-content/plugins بحثاً عن أي إضافة لا تعرفها، حتى غير المفعّلة.

لو بتدير مواقع عملاء كتير كفريلانسر، دي فرصة تفعّل التحديثات التلقائية لإصدارات الأمان في النواة لو كانت مقفولة، وتبعت لعملائك رسالة قصيرة إنك أمّنت مواقعهم. خدمة صيانة شهرية بتتباع بنفسها في أيام زي دي.

الأسئلة الشائعة

س: هل موقعي معرّض لو كانت التعليقات مغلقة؟
ج: مسار الهجوم الموثّق يعتمد على التعليقات، فإغلاقها يقطعه. لكن حدّث النواة على أي حال، فالإغلاق حل مؤقت فقط.

س: أنا على ووردبريس 6.9، هل أحتاج الترقية إلى 7.1.1؟
ج: لا يلزم تغيير الفرع. يكفي تحديث فرعك إلى الإصدار المرقّع، وهو 6.9.8 حسب المصدر.

س: هل تكفي إضافة حماية بدلاً من التحديث؟
ج: جدار الحماية يقلل الخطر مؤقتاً، لكنه لا يصلح الخلل في النواة. التحديث هو الحل الوحيد الكامل.

س: كيف أعرف إن كان موقعي اختُرق بالفعل؟
ج: ابحث عن إضافات أو ملفات PHP غريبة في wp-content/plugins، وعن حسابات مدير لا تعرفها، وراجع تعليقات تحتوي على blockquote بخاصية cite غريبة. عند الشك، استعن بمتخصص أمان.

الخلاصة

الثغرة دي مش في إضافة نسيتها، دي في قلب ووردبريس، وأخطر حاجة فيها إن المدير نفسه هو اللي بيكمّل الهجوم من غير ما يعرف. حدّث النهارده، امسح التعليق التجريبي، وراجع مجلد الإضافات. الأمان كمان بيتبني قطعة قطعة.

مرجع المقال (بالإنجليزية): CVE-2026-93485 Explained: How One WordPress Comment Can Hand Over the Server
المقال ده مش ترجمة حرفية: متكيّف للقارئ العربي ومضاف عليه سياق ووردبريس وElementor وJetEngine.

اترك تعليقاً