
تحليل دفاعي، تفصيل التصحيح، وماسح كشف لـ CVE-2026-14378 (إضافة WordPress DevKit Pro <= 2.3.0).
CVE-2026-14378 هي ثغرة تجاوز مصادقة غير مُصادَق عليه حرجة (CVSS 9.8) في إضافة DevKit Pro لـ WordPress، تؤثر على جميع الإصدارات حتى الإصدار 2.3.0 بما في ذلك.
تتضمن الإضافة آلية تبديل المستخدم للمطورين. عندما "يبدّل" المسؤول إلى حساب مستخدم آخر، تخزّن الإضافة معرّف المسؤول في ملف تعريف ارتباط يُسمى original_user_id. العيب: تُصيّر الإضافة نموذج HTML "للتبديل مرة أخرى" على أي صفحة كلما وُجد ملف تعريف الارتباط هذا — بما في ذلك للزوار غير المُصادَق عليهم الذين يضبطون ملف تعريف الارتباط يدويًا. والأسوأ من ذلك، أن خطوة التحقق من الـ nonce تتحقق من صلاحيات المسؤول الخاصة بـ مستخدم ملف تعريف الارتباط، وليس من جلسة المُتصل — لذا يمنح الخادم بكل سرور ملف تعريف ارتباط جلسة مسؤول مُصادَق عليه لأي شخص يرسل طلب POST الصحيح.
النتيجة الصافية: لا حاجة إلى أي بيانات اعتماد للسيطرة الكاملة على مسؤول WordPress.
| السمة | التفاصيل |
|---|---|
| معرّف CVE | CVE-2026-14378 |
| فئة الثغرة | مصادقة غير سليمة (CWE-287) |
| درجة CVSS v3.1 | 9.8 (حرجة) |
| متجه CVSS | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| البرنامج المتأثر | إضافة DevKit Pro (dplugins) لـ WordPress |
| الإصدارات المُصابة | <= 2.3.0 |
| الإصدار المُرقّع | 2.3.1 |
| تاريخ الإفصاح | 02 أكتوبر 2026 |
تتضمن DevKit Pro أداة مساعدة للمطورين تتيح لمسؤولي الموقع "التبديل" إلى حسابات مستخدمين آخرين لاختبار الصلاحيات. عندما يستخدم المسؤول ميزة التبديل:
Set-Cookie: original_user_id=1isset($_COOKIE['original_user_id'])wp_footer() مع نموذج POST مخفي يحتوي على nonce جديدadmin-post.php?action=revert_switchwp_set_auth_cookie($user_id) لاستعادة الجلسة الأصليةمعالج خطاف wp_footer:
// DevKit Pro <= 2.3.0 — render_switch_back_bar()
public function render_switch_back_bar() {
// FLAW: Only checks if cookie exists — no session validation!
if ( isset( $_COOKIE['original_user_id'] ) ) {
$user_id = (int) $_COOKIE['original_user_id'];
$nonce = wp_create_nonce( 'devkit_revert_switch_' . $user_id );
echo '<div id="devkit-pro-switch-back" class="devkit-switch-bar" style="display:none;">';
echo ' <form id="devkit-revert-form" action="' . admin_url('admin-post.php') . '" method="POST">';
echo ' <input type="hidden" name="action" value="revert_switch" />';
echo ' <input type="hidden" name="_wpnonce" value="' . $nonce . '" />';
echo ' <input type="hidden" name="target_user_id" value="' . $user_id . '" />';
echo ' </form>';
echo '</div>';
echo '<!-- DevKit Pro 2.3.0 Switch Component Active -->';
}
}
معالج POST الذي يعالج إرسال النموذج:
// DevKit Pro <= 2.3.0 — handle_revert_switch()
public function handle_revert_switch() {
$user_id = (int) $_POST['target_user_id'];
$nonce = sanitize_text_field( $_POST['_wpnonce'] );
if ( ! $this->verify_nonce_and_capability( $user_id, $nonce ) ) {
wp_die( 'Unauthorized' );
}
wp_set_current_user( $user_id );
wp_set_auth_cookie( $user_id ); // <-- Grants authenticated session to caller
wp_redirect( admin_url() );
exit;
}
private function verify_nonce_and_capability( $user_id, $nonce ) {
if ( ! wp_verify_nonce( $nonce, 'devkit_revert_switch_' . $user_id ) ) {
return false;
}
// CRITICAL FLAW: Checks the cookie user's capability, not the caller's!
return user_can( $user_id, 'manage_options' );
}
user_can( $user_id, 'manage_options' ) يجيب على السؤال: "هل يمتلك المستخدم #1 القدرة manage_options؟"
الإجابة بالنسبة للمستخدم #1 (أول مسؤول WordPress تم إنشاؤه) هي دائمًا true.
يجب أن يسأل بدلاً من ذلك: "هل يمتلك الشخص الذي يقوم بطلب HTTP هذا القدرة manage_options؟"
الفحص الصحيح هو current_user_can('manage_options') والذي سيعيد false لزائر غير مُصادَق عليه.
تم تنفيذ هذا الشرح بالكامل والتحقق منه ضد نسخة WordPress حية تعمل في حاوية Podman محلية (http://localhost:8080) مع تفعيل DevKit Pro 2.3.0.
لإعادة إنتاج هذا محليًا، تحتاج إلى:
إعداد مختبر Podman السريع:
# Start MariaDB
podman run -d --name wp-db \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
mariadb:10.6
# Start WordPress
podman run -d --name wp-app \
-p 8080:80 \
--link wp-db:mysql \
-e WORDPRESS_DB_HOST=mysql \
-e WORDPRESS_DB_NAME=wordpress \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=wppass \
wordpress:latest
بعد تهيئة WordPress (http://localhost:8080/wp-admin/install.php)، قم بتثبيت وتفعيل DevKit Pro 2.3.0 عبر قائمة الإضافات.
قبل إرسال أي حمولة هجومية، تأكد من وجود الإضافة وحدد إصدارها من خلال جلب ملف readme الخاص بالإضافة:
الطلب:
GET /wp-content/plugins/devkit-pro/readme.txt HTTP/1.1
Host: localhost:8080
الاستجابة (HTTP 200):
=== DevKit Pro ===
Requires at least: 5.0
Tested up to: 6.8
Requires PHP: 7.4
Stable tag: 2.3.0
License: GPLv2 or later
الإصدار 2.3.0 يقع ضمن النطاق المتأثر <= 2.3.0. تابع.
إذا أعاد الخادم HTTP 404، فإن الإضافة إما غير مثبتة أو تم تغيير المسار — هذا الهدف غير قابل للاستغلال عبر هذا المتجه.
أرسل طلب GET إلى أي صفحة WordPress مع تعيين الكوكي original_user_id إلى 1 (معرّف المستخدم الخاص بالمسؤول الأول):