数据库中以明文存储的密码重置密钥 + SQL注入 = 完全管理员接管
| 项目 | 详情 |
|---|---|
| CVE ID | CVE-2026-5076 |
| 插件 | ARMember – Membership Plugin & Content Restriction |
| 受影响版本 | Premium <= 7.3.1 |
| 已修补版本 | 7.3.2 |
| CVSS 评分 | 9.8 严重 |
| CWE | CWE-640: 弱密码恢复 |
| 类型 | 不安全的密码重置机制 → 明文密钥存储 |
| 攻击向量 | 网络 / 远程 / 未认证(通过 SQLi 链) |
| 活跃安装量 | 30,000+(Premium) |
| 发现者 | Wordfence Threat Intelligence |
| 发布日期 | 2026 年 6 月 3 日 |
| CVE | 类型 | 严重性 |
|---|---|---|
| CVE-2026-5076 | 不安全的密码重置 — 明文密钥存储 | 9.8 严重 |
| CVE-2026-5073 | 未认证 SQL 注入(ORDER BY) | 9.8 严重 |
| CVE-2026-5074 | 未认证 SQL 注入(WHERE) | 9.8 严重 |
WordPress 插件 ARMember Premium <= 7.3.1 中存在三个严重漏洞,若串联利用,可导致 无需认证即可完全接管管理员账户:
wp_usermeta(arm_reset_password_key)中,而非像 WordPress 标准那样进行哈希处理arm_directory_paging_action() 的 order 参数中存在 SQL 注入arm_directory_paging_action() 的 filter 参数中存在 SQL 注入CVE-2026-5076 的直接后果:任何能够读取数据库(通过 SQLi、备份泄露等)的人都可以直接读取明文形式的密码重置密钥,并立即将其用于重置任意账户的密码 —— 无需暴力破解。
WordPress 标准将密码重置密钥以 哈希 形式存储在 user_activation_key 列中,使用 wp_hash()。ARMember 将相同的密钥副本存储在 wp_usermeta 中,元键为 arm_reset_password_key —— 但却是以 明文 形式:```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!
Akibatnya, key plaintext lama tetap tersimpan selamanya di arm_reset_password_key bahkan setelah user melakukan password reset. Key ini bisa digunakan berulang kali sampai meta key secara eksplisit dihapus.
AJAX handler arm_directory_paging_action() memiliki nonce check via arm_check_user_cap(), tapi parameter order dan filter langsung masuk ke SQL query tanpa sanitasi:```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` 被插入到 SQL 的 `ORDER BY` 子句中。由于缺乏过滤,攻击者可以注入子查询:```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 ║ ╚══════════════════════════════════════════════════════════════════════════════════╝