← Back to Feed CACHED · 2026-09-26 22:36:05 · CACHE_KEY CVE-2026-5076
CVE-2026-5076 · CWE-287 · Disclosed 2026-06-02

ARMember Premium ≤ 7.3.1 Insecure Plaintext Password Reset Key Storage Leading to Unauthenticated Admin Account Takeover

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Someone left a sticky note with every master key on the break-room fridge, and the break room is unlocked

CVE-2026-5076 targets the ARMember Premium plugin for WordPress (all versions through 7.3.1). When any user triggers a password reset, the plugin writes a plaintext copy of the 20-character reset key into the arm_reset_password_key field in wp_usermeta — right alongside the properly hashed key WordPress core stores in wp_users.user_activation_key. That plaintext key works with ARMember's custom armrp reset action to set a new password for any account, including administrators. Alone, the plaintext storage is a design flaw that needs another read primitive to weaponize. In practice, that primitive ships in the same plugin, same version range: CVE-2026-5073 provides unauthenticated SQL injection via the arm_directory_paging_action AJAX handler (registered on wp_ajax_nopriv_, no auth check). Chain them together and you get a zero-click, fully unauthenticated admin account takeover with two public PoCs automating every step.

Wordfence rated the chain CRITICAL 9.8 and the CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) is technically sound for the combined unauthenticated→admin-takeover path. However, 9.8 overweights the real-world footprint. ARMember Premium has roughly 30,000–50,000 active installations — a niche membership plugin, not a ubiquitous WordPress component like WooCommerce or Elementor. In a typical 10,000-host enterprise, the number of WordPress instances running ARMember is likely zero to low single digits. The blast radius is per-site (one WordPress database, one set of member PII), not domain-wide or fleet-scale. There is no confirmed in-the-wild exploitation, no KEV listing, and EPSS sits at 0.48% (39th percentile). The attack is dangerous where it applies, but the installed-base narrowness and per-site containment justify a downgrade to HIGH 8.5.

"Fully automated admin takeover chain, but niche plugin limits fleet-wide blast radius"
02 · The Attack Path

6 steps from start to impact.

STEP 01

Identify ARMember installation

The attacker scans for WordPress sites and fingerprints plugin presence via /wp-content/plugins/armember/ path probing, readme.txt version disclosure, or known front-end CSS/JS asset paths. Automated scanners like WPScan or Nuclei templates can enumerate this at scale.
Conditions required:
  • Target runs WordPress with ARMember Premium ≤ 7.3.1
  • Plugin assets or directory pages are publicly accessible
Where this breaks in practice:
  • ARMember Premium has only ~30K-50K installs globally — most WordPress sites do not run it
  • Version disclosure in readme.txt may be suppressed by hardened deployments
Detection/coverage: WPScan, Nuclei ARMember templates, or custom Shodan/FOFA dorks for ARMember CSS/JS paths
STEP 02

Extract nonce and template ID from directory page

ARMember's member directory feature outputs a form containing a WordPress nonce and a arm_template_id value. The attacker visits any public directory page and scrapes these values from the HTML source. These tokens are required to call the arm_directory_paging_action AJAX handler.
Conditions required:
  • At least one ARMember directory page exists and is publicly accessible
Where this breaks in practice:
  • If no directory page is configured or it is restricted to logged-in users, this step fails
  • Some WAFs strip or obfuscate nonce values in responses
Detection/coverage: Unusual scraping patterns on directory pages; rate-limiting on AJAX endpoints
STEP 03

Unauthenticated SQL injection via CVE-2026-5073

Using the extracted nonce and template ID, the attacker sends a POST to /wp-admin/admin-ajax.php with action=arm_directory_paging_action and a malicious order parameter. The arm_get_directory_members() function concatenates the unsanitized order value directly into the SQL query. The PoCs use an error-based boolean oracle technique (IF() with EXP(710) to trigger MySQL overflow) enabling blind extraction character by character. Tools: zycoder0day/CVE-2026-5076 and shootcannon/CVE-2026-5076 automate this with binary search.
Conditions required:
  • Valid nonce and template ID obtained in step 2
  • The arm_directory_paging_action AJAX endpoint is reachable (registered via wp_ajax_nopriv_)
Where this breaks in practice:
  • Web Application Firewalls (ModSecurity, Wordfence, Cloudflare WAF) may detect SQLi payloads in POST parameters — but error-based oracle payloads are subtler than UNION-based
  • Rate limiting on admin-ajax.php would slow binary-search extraction significantly
Detection/coverage: WAF SQLi rules, anomalous POST volume to admin-ajax.php, MySQL error log spikes from EXP(710) overflow
STEP 04

Trigger password reset for admin user

The attacker calls the ARMember forgot-password AJAX action (or WordPress core lostpassword) for the target admin username or email. This causes the plugin to generate a 20-character reset key and store it in plaintext in the arm_reset_password_key user meta field. The attacker does not need the email — they just need to trigger the write to the database.
Conditions required:
  • Admin username or email is known or guessable (often admin, or enumerable via ?author=1)
Where this breaks in practice:
  • WordPress username enumeration can be blocked with plugins or server config
  • Some hosting providers block the lostpassword action for admin accounts
Detection/coverage: Password reset email logs; spike in password-reset requests for privileged accounts
STEP 05

Extract plaintext reset key via SQLi

With the plaintext key now sitting in wp_usermeta, the attacker uses the same SQLi channel from step 3 to read arm_reset_password_key for the target user ID. The binary search technique extracts the 20-character key in approximately 100–140 HTTP requests. A critical persistence bug means get_password_reset_key() does not update arm_reset_password_key, so the plaintext key remains valid indefinitely until manually deleted.
Conditions required:
  • SQLi channel from step 3 is still usable
  • Plaintext key exists in wp_usermeta (step 4 succeeded)
Where this breaks in practice:
  • Same WAF/rate-limiting friction as step 3
  • If the database prefix is non-standard, the attacker needs an extra enumeration round
Detection/coverage: Same as step 3; additionally, monitoring wp_usermeta reads for arm_reset_password_key at the database audit level
STEP 06

Reset admin password and log in

The attacker issues a GET request to /?armrp=true&key=<EXTRACTED_KEY>&login=<admin_username> and submits a new password via the resulting form. They then log in to /wp-admin/ with full administrative privileges. From here, the attacker can install backdoor plugins, exfiltrate member data and payment information, modify site content, or pivot to the underlying server via the WordPress theme/plugin editor.
Conditions required:
  • Extracted reset key is valid (it persists until manually purged)
  • ARMember's armrp endpoint is functional
Where this breaks in practice:
  • Two-factor authentication on the WordPress admin account would block login even after password reset
  • IP-based admin login restrictions would limit access
Detection/coverage: WordPress login audit logs; new admin session from unfamiliar IP; file integrity monitoring for plugin/theme changes
03 · Compensating Control

1
HIGH 8.5→IGNORE 0.0
SEVERITY REDUCED
Update ARMember Premium to 7.3.2 immediately — The definitive fix. Version 7.3.2 eliminates plaintext key storage (CVE-2026-5076), patches the unauth SQLi in arm_directory_paging_action (CVE-2026-5073), and the auth SQLi in get_private_content_data (CVE-2026-5074). Per the noisgate mitigation SLA for HIGH, deploy within 30 days. Given public PoCs exist, treat this as the top priority among your WordPress patching queue.
2
HIGH 8.5→HIGH 7.5
Purge all plaintext reset keys from wp_usermeta — Run DELETE FROM wp_usermeta WHERE meta_key = 'arm_reset_password_key'; on every affected WordPress database. The plaintext keys persist indefinitely due to a logic bug where get_password_reset_key() does not update the ARMember meta field. Even after patching to 7.3.2, stale keys may remain exploitable if a new SQLi surfaces. Do this within 3 days alongside or before patching.
3
HIGH 8.5→MEDIUM 5.5
SEVERITY REDUCED
Enable WAF SQL injection rules on admin-ajax.php — Configure your WAF (Cloudflare, ModSecurity CRS paranoia level 2+, or Wordfence Pro) to inspect POST bodies to /wp-admin/admin-ajax.php for SQL injection patterns. This breaks Step 3 of the attack chain. Error-based oracle payloads using EXP(710) and IF() constructs should be blockable with tuned rules. Deploy within 30 days per the noisgate mitigation SLA.
4
HIGH 8.5→MEDIUM 5.0
SEVERITY REDUCED
Restrict or remove public ARMember directory pages — The SQLi requires a nonce and template ID harvested from a public directory page. If the directory feature is not business-critical, disable it or restrict to authenticated users only. This breaks Step 2 of the chain. Apply within 30 days.
5
HIGH 8.5→MEDIUM 6.0
SEVERITY REDUCED
Enforce two-factor authentication on WordPress admin accounts — Even if the attacker successfully resets the admin password, 2FA on the WordPress login (via plugin like Wordfence Login Security, WP 2FA, or Duo) prevents them from completing the takeover at Step 6. This does not prevent data exfiltration via SQLi but blocks admin panel access. Deploy within 30 days.
6
HIGH 8.5→HIGH 7.0
Force-rotate all WordPress admin and member passwords — If you cannot confirm whether plaintext reset keys were previously extracted, assume breach on affected sites. Rotate all admin credentials and invalidate all sessions (UPDATE wp_usermeta SET meta_value = '' WHERE meta_key = 'session_tokens';). Notify members if PII exposure is suspected. Execute within 3 days on any site confirmed to run vulnerable versions.
What doesn't work
  • IP allowlisting on wp-login.php alone — the attack chain does not go through wp-login.php until the final login step; the SQLi and password reset happen via admin-ajax.php and the armrp front-end endpoint, both of which bypass wp-login restrictions.
  • WordPress core auto-updates — ARMember Premium is sold via CodeCanyon, not the wordpress.org repository, so WordPress core auto-update mechanisms do not cover it. Manual update or CodeCanyon license-linked update is required.
  • Disabling XML-RPC — the attack uses AJAX endpoints (admin-ajax.php), not XML-RPC. Disabling XML-RPC provides no protection against this chain.
  • Database-level encryption at rest — the SQLi reads data through the application layer (MySQL queries via WordPress), so TDE or disk encryption does not prevent the attacker from extracting plaintext keys.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNo confirmed exploitation. Not listed on CISA KEV. No GreyNoise tags or Shadowserver scan reports observed as of 2026-09-27. Proofpoint's 2026 routinely-targeted list does not include this CVE.
Proof-of-concept availability2 public PoCs on GitHub — zycoder0day/CVE-2026-5076 (published 2026-06-04, credited XENON1337 & ENDANG) and shootcannon/CVE-2026-5076 (published 2026-06-05). Both automate the full 7-phase chain including SQLi extraction and password reset. Listed on pocindex.io with 2 entries from GitHub.
EPSS0.0048 (0.48%) — 39th percentile. Low predicted exploitation probability, though PoC availability often precedes EPSS catch-up by 30–90 days.
CISA KEV statusNot listed as of 2026-09-27.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H — Network-reachable, low complexity, no privileges, no interaction. The vector assumes the full chain with CVE-2026-5073 is available, which is accurate since both ship in the same plugin version range.
Affected versionsARMember Premium 5.x through 7.3.1 (all versions). The free/lite version on wordpress.org is not affected — this is the premium (CodeCanyon) edition only.
Fixed versionARMember Premium 7.3.2 — fixes CVE-2026-5076 (plaintext key storage), CVE-2026-5073 (unauth SQLi via order), and CVE-2026-5074 (auth SQLi via sSortDir_0).
Install base & exposure~30,000–50,000 active premium installations per vendor and PoC documentation. WordPress sites are internet-facing by nature; the vulnerable AJAX endpoint is reachable at /wp-admin/admin-ajax.php on every installation.
Disclosure timelineDiscovered by Wordfence Threat Intelligence, disclosed 2026-06-02, NVD published 2026-06-02, public PoCs appeared 2026-06-04/05. Patch (7.3.2) was available at disclosure.
Related CVEsCVE-2026-5073 (CVSS 7.5, unauth SQLi via order param) and CVE-2026-5074 (CVSS 6.5, auth SQLi via sSortDir_0, requires Subscriber + Private Content addon enabled). CVE-2026-5073 is the critical chain link enabling unauthenticated exploitation of CVE-2026-5076.

Sources.

  1. Wordfence Threat Intel — CVE-2026-5076 Advisory
  2. GitHub Advisory GHSA-53mc-7vx2-cm58
  3. NVD — CVE-2026-5076
  4. PoC: zycoder0day/CVE-2026-5076 (full chain exploit)
  5. PoC: shootcannon/CVE-2026-5076 (admin takeover)
  6. BitNinja — Critical Vulnerability Alert: ARMember Security Risk
  7. pocindex.io — CVE-2026-5076 PoC listing
  8. OpenCVE — CVE-2026-5073 (chained SQLi)
05 · The Call

Final Verdict
↓ DOWNGRADED to HIGH (8.5/10)

Why this verdict

  • Chain dependency is real but colocated: CVE-2026-5076 alone is a design flaw (plaintext key storage) that requires a separate read primitive. That primitive — CVE-2026-5073 (unauth SQLi) — ships in the *same plugin, same version range*, and both are fixed in 7.3.2. Still, the vendor's 9.8 scores the chain as a single monolithic attack; the conceptual dependency is a minor downward pressure (~0.3 points).
  • Niche install base: ARMember Premium has ~30K-50K installs versus millions for WooCommerce or Elementor. In a 10,000-host enterprise, the expected number of affected hosts is 0–3. This dramatically reduces fleet-level risk exposure (~0.5 points downward).
  • No confirmed exploitation and low EPSS: Despite PoCs being public for nearly 4 months, there is no KEV listing, no GreyNoise/Shadowserver scan data, and EPSS is at 0.48% (39th percentile). The exploitation window has been open but apparently underutilized (~0.2 points downward).
  • WAF friction on the SQLi step: The enabling SQLi (CVE-2026-5073) transits through admin-ajax.php POST parameters. Enterprise-grade WAFs (Cloudflare, ModSecurity CRS, Wordfence Pro) with SQL injection rulesets will catch many payloads, though error-based oracle techniques are subtler than UNION-based injections (~0.2 points downward).
  • Role multiplier: ARMember Premium is a WordPress membership/subscription plugin. (a) *Low-value role*: dev/staging WordPress instances — chain succeeds, blast radius = one dev site, negligible. (b) *Typical role*: SMB membership site with member PII and possibly payment data — chain succeeds, blast radius = one site's data + potential web shell, contained to that host. (c) *High-value role assessment*: WordPress membership sites are not in the canonical high-value-role catalog (not IdP, DC, hypervisor, CI/CD, backup, PKI, network edge, SIEM, or OT). A compromised WordPress site could theoretically serve as a pivot point, but it is not fleet-scale or identity-scale. The floor rule does not apply. Per-site blast radius caps the worst-case outcome at host-level compromise + data exfiltration for that site's member database.

Why not higher?

The attack chain is fully unauthenticated and automated with public PoCs, which would normally justify CRITICAL. However, the blast radius is contained to individual WordPress sites, the affected plugin is niche (~30K-50K installs, not a canonical high-value infrastructure component), and there is no evidence of active exploitation after nearly 4 months of PoC availability. The floor rule for CRITICAL requires the component to canonically occupy a high-value role (≥10% of installs), which ARMember does not.

Why not lower?

Despite the niche install base, this is a fully unauthenticated, zero-interaction, remotely exploitable admin takeover with working automation. Two independent PoC authors published full exploit chains within 72 hours of disclosure. Any WordPress site running ARMember Premium ≤ 7.3.1 with a public directory page is trivially compromisable. The plaintext key persists indefinitely due to a logic bug, meaning even a stale reset key works. Dropping below HIGH would understate the severity for the sites that *are* affected.

06 · Verification

Crowdsourced verification payload.

Run this script on the WordPress host (the server where wp-content lives) as any user with read access to the WordPress installation directory. Invoke with the path to the WordPress root: bash check_cve_2026_5076.sh /var/www/html. No root privileges required — just filesystem read access to the plugin directory and wp-config.php.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_5076.sh — Detect ARMember Premium CVE-2026-5076
# Usage: bash check_cve_2026_5076.sh /path/to/wordpress
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN

set -euo pipefail

WP_ROOT="${1:?Usage: $0 /path/to/wordpress}"
PLUGIN_DIR="$WP_ROOT/wp-content/plugins/armember"
CHANGELOG="$PLUGIN_DIR/changelog.txt"
CORE_FILE="$PLUGIN_DIR/armember.php"

# Check if plugin exists
if [ ! -d "$PLUGIN_DIR" ]; then
  echo "UNKNOWN — ARMember Premium plugin not found at $PLUGIN_DIR"
  exit 2
fi

# Try to extract version from main plugin file
VERSION=""
if [ -f "$CORE_FILE" ]; then
  VERSION=$(grep -i 'Version:' "$CORE_FILE" | head -1 | sed 's/.*Version:[[:space:]]*//' | tr -d '[:space:]')
fi

# Fallback: check readme.txt
if [ -z "$VERSION" ] && [ -f "$PLUGIN_DIR/readme.txt" ]; then
  VERSION=$(grep -i 'Stable tag:' "$PLUGIN_DIR/readme.txt" | head -1 | sed 's/.*Stable tag:[[:space:]]*//' | tr -d '[:space:]')
fi

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — Could not determine ARMember Premium version"
  exit 2
fi

echo "Detected ARMember Premium version: $VERSION"

# Compare versions: vulnerable if <= 7.3.1
# Using sort -V for version comparison
MIN_SAFE="7.3.2"
SORTED_FIRST=$(printf '%s\n%s' "$VERSION" "$MIN_SAFE" | sort -V | head -1)

if [ "$SORTED_FIRST" = "$MIN_SAFE" ] || [ "$VERSION" = "$MIN_SAFE" ]; then
  # VERSION >= MIN_SAFE
  echo "PATCHED — Version $VERSION is >= $MIN_SAFE (CVE-2026-5076 fixed)"
  exit 0
else
  # VERSION < MIN_SAFE
  echo "VULNERABLE — Version $VERSION is below $MIN_SAFE (CVE-2026-5076 applies)"
  # Bonus: check for stale plaintext reset keys in the database
  if [ -f "$WP_ROOT/wp-config.php" ]; then
    DB_NAME=$(grep "DB_NAME" "$WP_ROOT/wp-config.php" | cut -d "'" -f 4 2>/dev/null || true)
    DB_USER=$(grep "DB_USER" "$WP_ROOT/wp-config.php" | cut -d "'" -f 4 2>/dev/null || true)
    DB_PASS=$(grep "DB_PASSWORD" "$WP_ROOT/wp-config.php" | cut -d "'" -f 4 2>/dev/null || true)
    DB_HOST=$(grep "DB_HOST" "$WP_ROOT/wp-config.php" | cut -d "'" -f 4 2>/dev/null || true)
    PREFIX=$(grep "table_prefix" "$WP_ROOT/wp-config.php" | cut -d "'" -f 2 2>/dev/null || true)
    PREFIX=${PREFIX:-wp_}
    if [ -n "$DB_NAME" ] && command -v mysql &>/dev/null; then
      KEY_COUNT=$(mysql -h"${DB_HOST:-localhost}" -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" -sNe \
        "SELECT COUNT(*) FROM ${PREFIX}usermeta WHERE meta_key='arm_reset_password_key' AND meta_value != '';" 2>/dev/null || echo "?")
      if [ "$KEY_COUNT" != "?" ] && [ "$KEY_COUNT" -gt 0 ] 2>/dev/null; then
        echo "WARNING — $KEY_COUNT plaintext password reset key(s) found in ${PREFIX}usermeta"
      fi
    fi
  fi
  exit 1
fi