
استغلال لإثبات المفهوم لثغرة CVE-2026-5076 يوضح الاستيلاء غير المُصادَق عليه على حساب المسؤول في ARMember Premium عبر حقن SQL وتخزين مفتاح إعادة تعيين كلمة المرور كنصٍّ صريح.
مفاتيح إعادة تعيين كلمة المرور المخزنة كنص صريح في قاعدة البيانات + حقن SQL = استيلاء كامل على حساب المسؤول
| العنصر | التفاصيل |
|---|---|
| CVE ID | CVE-2026-5076 |
| الملحق | ARMember – Membership Plugin & Content Restriction |
| النسخة المتأثرة | Premium <= 7.3.1 |
| النسخة المصححة | 7.3.2 |
| درجة CVSS | 9.8 حرجة |
| CWE | CWE-640: Weak Password Recovery |
| النوع | آلية إعادة تعيين كلمة المرور غير آمنة → تخزين المفتاح كنص صريح |
| ناقل الهجوم | شبكة / عن بُعد / بدون مصادقة (عبر سلسلة SQLi) |
| التثبيتات النشطة | 30,000+ (Premium) |
| المكتشف | Wordfence Threat Intelligence |
| تاريخ النشر | 3 يونيو 2026 |
| CVE | النوع | الخطورة |
|---|---|---|
| CVE-2026-5076 | إعادة تعيين كلمة مرور غير آمنة — تخزين المفتاح كنص صريح | 9.8 حرجة |
| CVE-2026-5073 | حقن SQL بدون مصادقة (ORDER BY) | 9.8 حرجة |
| CVE-2026-5074 | حقن SQL بدون مصادقة (WHERE) | 9.8 حرجة |
ثلاث ثغرات حرجة في ملحق ووردبريس ARMember Premium <= 7.3.1 والتي عند ربطها معًا تتيح الاستيلاء الكامل على حساب المسؤول بدون مصادقة:
wp_usermeta (arm_reset_password_key)، وليس مجزأً كما في معيار ووردبريسorder في معالج AJAX arm_directory_paging_action()filter في معالج AJAX arm_directory_paging_action()النتيجة المباشرة لـ CVE-2026-5076: أي شخص لديه وصول للقراءة إلى قاعدة البيانات (عبر SQLi، تعرض النسخ الاحتياطية، وما إلى ذلك) يمكنه قراءة مفتاح إعادة تعيين كلمة المرور في شكل نص صريح واستخدامه مباشرة لإعادة تعيين كلمة مرور أي حساب — دون الحاجة إلى كسر التشفير.
يخزن ووردبريس القياسي مفتاح إعادة تعيين كلمة المرور في العمود user_activation_key في شكل مجزأ باستخدام wp_hash(). بينما يخزن ARMember نسخة من المفتاح نفسه في wp_usermeta مع meta_key باسم arm_reset_password_key — لكن في شكل نص صريح (PLAINTEXT):```php
// FILE: armember-membership/core/class.arm_member_forms.php
// Fungsi: arm_lost_password_action()
// WordPress menyimpan HASHED key (aman) $key = wp_generate_password(20, false); $wp_key = $wpdb->get_var( $wpdb->prepare("SELECT user_activation_key FROM $wpdb->users WHERE user_login=%s", $user_login) );
// ARMember menyimpan PLAINTEXT key (VULNERABLE!) update_user_meta($user_id, 'arm_reset_password_key', $wp_key); // ^^^^^^ // Ini adalah key ASLI yang bisa langsung dipakai
**مشكلة حرجة**: `$wp_key` هنا هو المفتاح الذي يولّده `wp_generate_password(20, false)` — 20 حرفًا أبجديًا رقميًا. WordPress يجزّئ هذا المفتاح قبل تخزينه في `user_activation_key`، لكن ARMember يخزّنه **قبل التجزئة** أو يخزّن نسخة منفصلة **غير مجزّأة**.
### السبب الجذري #2: خلل استمرار المفتاح
عندما يتم استدعاء `get_password_reset_key()` (نواة WordPress)، يتم توليد مفتاح جديد وتجزئته. لكن هذه الدالة **لا تقوم بتحديث** `arm_reset_password_key`:```php
// WordPress core: get_password_reset_key($user)
// - Generate key baru
// - Hash key → simpan di user_activation_key
// - Return key plaintext
// - TAPI: arm_reset_password_key TIDAK diupdate!
ونتيجة لذلك، يظل مفتاح النص العادي القديم مخزّنًا إلى الأبد في arm_reset_password_key حتى بعد قيام المستخدم بإعادة تعيين كلمة المرور. يمكن استخدام هذا المفتاح مرارًا وتكرارًا حتى يتم حذف المفتاح التعريفي (meta key) صراحةً.
معالج AJAX arm_directory_paging_action() يحتوي على فحص nonce عبر arm_check_user_cap()، لكن المعاملين order وfilter يدخلان مباشرةً إلى استعلام SQL دون تعقيم (sanitization):```php
// FILE: armember-membership/core/class.arm_member_forms.php
// Fungsi: arm_directory_paging_action()
// Nonce check (dibutuhkan nonce valid) $nonce_check = $this->arm_check_user_cap();
// ORDER BY injection — langsung ke SQL tanpa sanitasi! $orderby = "u.{$arm_member} {$order_dir}"; // $order_dir dari $_POST['order'] → LANGSUNG ke ORDER BY
// WHERE injection via filter if (!empty($filter)) { $where .= " AND " . $filter; // ← LANGSUNG concatenation! }
**استغلال ORDER BY**: يتم إدخال المعامل `order` في جملة `ORDER BY` في SQL. نظرًا لعدم وجود تنظيف للمدخلات، يمكن للمهاجم حقن استعلام فرعي:```sql
-- Payload SQLi via parameter order
ORDER BY u.ID ASC, IF(COND, 1, EXP(710))
-- COND = TRUE → IF returns 1 → ORDER BY 1 → response normal (besar)
-- COND = FALSE → IF returns EXP(710) → MySQL overflow ERROR → response 90B
Oracle هذا محصّن ضد تأخير الشبكة لأنه يميز TRUE/FALSE بناءً على الخطأ مقابل النجاح، وليس وقت الاستجابة.
╔══════════════════════════════════════════════════════════════════════════════════╗ ║ CVE-2026-5076 FULL CHAIN ATTACK ROADMAP ║ ║ ARMember Premium <= 7.3.1 → Unauthenticated Admin Takeover ║ ╚══════════════════════════════════════════════════════════════════════════════════╝