
Exploit de prueba de concepto para CVE-2026-5076 que demuestra la toma de control de una cuenta de administrador sin autenticación en ARMember Premium mediante inyección SQL y almacenamiento de la clave de restablecimiento de contraseña en texto plano.
Claves de restablecimiento de contraseña en texto plano almacenadas en la base de datos + Inyección SQL = Toma de control total del administrador
| Elemento | Detalle |
|---|---|
| CVE ID | CVE-2026-5076 |
| Plugin | ARMember – Membership Plugin & Content Restriction |
| Versión afectada | Premium <= 7.3.1 |
| Versión parcheada | 7.3.2 |
| Puntuación CVSS | 9.8 Crítico |
| CWE | CWE-640: Weak Password Recovery |
| Tipo | Mecanismo inseguro de restablecimiento de contraseña → Almacenamiento de clave en texto plano |
| Vector de ataque | Red / Remoto / No autenticado (mediante cadena de SQLi) |
| Instalaciones activas | 30,000+ (Premium) |
| Descubridor | Wordfence Threat Intelligence |
| Fecha de publicación | 3 de junio de 2026 |
| CVE | Tipo | Severidad |
|---|---|---|
| CVE-2026-5076 | Restablecimiento de contraseña inseguro — Almacenamiento de clave en texto plano | 9.8 Crítico |
| CVE-2026-5073 | Inyección SQL no autenticada (ORDER BY) | 9.8 Crítico |
| CVE-2026-5074 | Inyección SQL no autenticada (WHERE) | 9.8 Crítico |
Tres vulnerabilidades críticas en el plugin de WordPress ARMember Premium <= 7.3.1 que, al encadenarse, permiten la toma de control total de la cuenta de administrador sin autenticación:
wp_usermeta (arm_reset_password_key), en lugar de estar cifrada como en el estándar de WordPressorder del manejador AJAX arm_directory_paging_action()filter del manejador AJAX arm_directory_paging_action()Consecuencia directa de CVE-2026-5076: cualquier persona con acceso de lectura a la base de datos (mediante SQLi, exposición de copias de seguridad, etc.) puede leer la clave de restablecimiento de contraseña en texto plano y usarla directamente para restablecer la contraseña de cualquier cuenta — sin necesidad de descifrarla.
WordPress estándar almacena la clave de restablecimiento de contraseña en la columna user_activation_key en forma cifrada usando wp_hash(). ARMember almacena una copia de la misma clave en wp_usermeta con la meta_key arm_reset_password_key — pero en TEXTO PLANO:```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
**Problema crítico**: `$wp_key` aquí es la clave generada por `wp_generate_password(20, false)` — 20 caracteres alfanuméricos. WordPress aplica hash a esta clave antes de almacenarla en `user_activation_key`, pero ARMember la almacena **antes del hash** o guarda una copia separada que **no tiene hash**.
### Causa raíz #2: Error de persistencia de la clave
Cuando se llama a `get_password_reset_key()` (núcleo de WordPress), se genera y se aplica hash a una nueva clave. Sin embargo, esta función **NO actualiza** `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!
Como resultado, la clave antigua en texto plano se almacena permanentemente en arm_reset_password_key incluso después de que el usuario realice un restablecimiento de contraseña. Esta clave puede usarse repetidamente hasta que la metaclave se elimine explícitamente.
El manejador AJAX arm_directory_paging_action() tiene verificación de nonce mediante arm_check_user_cap(), pero los parámetros order y filter entran directamente en la consulta SQL sin sanitizar:```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! }
**Explotación de ORDER BY**: El parámetro `order` se inserta en la cláusula `ORDER BY` de SQL. Debido a que no hay saneamiento, un atacante puede inyectar una subconsulta:```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
Este Oracle es inmune a la latencia de red porque distingue TRUE/FALSE según error vs éxito, no el tiempo de respuesta.
╔══════════════════════════════════════════════════════════════════════════════════╗ ║ CVE-2026-5076 FULL CHAIN ATTACK ROADMAP ║ ║ ARMember Premium <= 7.3.1 → Unauthenticated Admin Takeover ║ ╚══════════════════════════════════════════════════════════════════════════════════╝