← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2026-6895 · CWE-269 · Disclosed 2026-05-23

The WishList Member plugin for WordPress is vulnerable to Missing Authorization leading to Sensitive…

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

This is a spare key taped under the doormat, but only after the attacker gets onto the porch

CVE-2026-6895 affects the commercial wishlist-member-x WordPress plugin in versions up to and including 3.30.1 and is fixed in 3.31.0. A low-privileged authenticated user can call the vulnerable export_settings path, receive the WishList Member REST API key in an AJAX response, then use that key to create a membership level mapped to the WordPress administrator role and mint a new admin account.

The vendor's 8.8/HIGH score is technically fair in a lab because the end state is full site takeover, but it overstates broad enterprise urgency. The chain starts with PR:L, which in the real world means either open self-registration, cheap paid signup, or an already-compromised subscriber account; that is a real friction point, and the plugin's overall population is small enough that this is not a mass-internet emergency absent exploitation evidence.

"Serious if a stranger can self-register, but this is still an authenticated WordPress plugin privesc with narrow reach."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Fingerprint a live WishList Member target

An attacker first confirms the site is running WishList Member and is likely below 3.31.0, typically with normal web recon or a tool like WPScan. This is not an exploit step by itself, but it narrows the target set to a relatively small commercial-plugin population.
Conditions required:
  • The site runs WordPress with the wishlist-member-x plugin installed
  • The attacker can reach the public site over HTTPS
Where this breaks in practice:
  • WishList Member is not a broadly deployed commodity plugin compared with top free WordPress extensions
  • Precise version fingerprinting is often obscured unless plugin files or metadata are exposed
Detection/coverage: External scanners such as WPScan can usually identify the plugin, but they do not prove exploitability or whether public registration makes the bug reachable.
STEP 02

Get a subscriber foothold

The vulnerability is not unauthenticated; the attacker needs a low-privileged account first. In practice that means using open registration, buying the cheapest membership tier, abusing invite flows, or reusing already-stolen subscriber credentials.
Conditions required:
  • The attacker can register, buy, or otherwise obtain a low-privileged account
  • The account is allowed to log in and hit plugin AJAX functionality
Where this breaks in practice:
  • Many membership sites gate signups behind payment, approval, captcha, or email confirmation
  • If the site has no public registration path, this becomes post-initial-access only
Detection/coverage: Identity telemetry can catch bursts of fresh signups or suspicious low-tier purchases, but most vuln scanners miss this prerequisite entirely.
STEP 03

Call wlm3_export_settings and steal the API key

Using a browser, Burp, or curl, the attacker invokes the vulnerable AJAX action that lacks a proper capability check. The response leaks the WishList Member REST API key, which converts a normal subscriber session into API-level administrative reach over the plugin.
Conditions required:
  • The vulnerable code path is present in <= 3.30.1
  • The subscriber session is valid
  • The plugin's API key remains active
Where this breaks in practice:
  • Web application firewalls or custom admin-ajax.php hardening may block unusual request patterns
  • Some sites may rotate or disable API access during security hardening
Detection/coverage: Look for suspicious authenticated requests to wp-admin/admin-ajax.php invoking WishList actions, especially followed by API calls from the same user or IP.
STEP 04

Use the API to create an admin path and take over the site

Per the vendor API documentation, the leaked key can authenticate requests that POST new membership levels and set wordpress_role=administrator; the attacker can then register or promote an account into that level. At that point this is standard WordPress full-site compromise: install plugins, alter content, dump data, or create persistence.
Conditions required:
  • The leaked API key is accepted by the WishList Member API
  • API endpoints are reachable from the internet
  • The site accepts the created role mapping and new user workflow
Where this breaks in practice:
  • Attackers still need to understand the plugin's API workflow rather than pressing a one-click public exploit button
  • Operational mistakes are noisy because new levels, new users, and role changes leave artifacts
Detection/coverage: High-value detections are creation of new membership levels mapped to admin roles, unexpected API traffic to ?/wlmapi/2.0/, and any new administrator accounts shortly after suspicious subscriber activity.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo official active-exploitation signal found in the retrieved sources. CISA KEV has no record for CVE-2026-6895, and I found no public campaign reporting tied to this CVE.
PoC availabilityPublic write-up exists at Atomic Edge, but I did not find a widely circulated vendor-verified exploit repo in the retrieved sources.
EPSSUser-supplied EPSS is 0.00044 (0.044%), which is very low. Official FIRST sources retrieved here document the API/methodology, but the percentile for this specific CVE was not directly exposed in the fetched results.
KEV statusNot listed in the CISA KEV catalog as checked against retrieved search results.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — the critical nuance is PR:L: this is not an unauthenticated internet bug.
Affected versionsAuthoritative sources consistently place the vulnerable range at <= 3.30.1 / < 3.31.0 for the wishlist-member-x plugin.
Fixed version3.31.0 per WPScan and Patchstack. The vendor 3.31.0 changelog lists security fixes, though it does not name CVE-2026-6895 explicitly.
Exposure / populationThis is a commercial WordPress membership plugin, not a ubiquitous default component. Wordfence's plugin page shows about 4,245 active installs, while the vendor markets 120,310 cumulative site activations since 2008; either way, reachable enterprise population is far smaller than mainstream WordPress plugin emergencies.
Disclosure timelineNVD shows publication on 2026-05-23; WPScan shows public publication on 2026-05-22; Patchstack published its entry on 2026-05-25.
Researcher / sourceWPScan and Patchstack credit h0xilo; NVD lists Wordfence as the CNA/source for the CVE record.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (6.2/10)

The decisive downward pressure is the attacker-position requirement: this bug starts at authenticated subscriber, not unauthenticated remote. That means many enterprises only care if they run public-facing membership sites with self-registration or if an attacker already has a foothold in the WordPress tenant.

HIGH Technical exploit chain and impact once a subscriber account exists
MEDIUM Real-world exploitation likelihood across enterprise fleets

Why this verdict

  • Start from 8.8, then cut for attacker position: vendor scoring assumes full-value internet reach, but PR:L means the attacker needs a subscriber account before anything interesting happens.
  • Exposure population is narrow: WishList Member is a niche commercial membership plugin, not a mass-installed core dependency. Even the higher vendor marketing number reflects cumulative activations, not current exposed fleet.
  • The prerequisite often implies prior compromise or business-process abuse: if registration is closed, paid, approved, or invite-only, exploitation becomes post-initial-access rather than first-hop internet exploitation.
  • No exploitation evidence is doing more work than the CVSS does: no KEV, no public campaign reporting, and a very low user-supplied EPSS all argue against emergency fleet-wide treatment.
  • Impact is still real: once a subscriber can hit the vulnerable action, the chain leads to admin creation and full WordPress takeover, so this is not backlog trash.

Why not higher?

This is not a clean unauthenticated remote takeover. The exploit chain depends on a low-privileged account and a plugin that is comparatively narrow in deployment, which compounds the reachable-population problem. In enterprise triage terms, that is a substantial downgrade from the vendor's lab score.

Why not lower?

The post-auth impact is complete site compromise, not just data leakage or a cosmetic role bug. On any affected site with open or easy self-registration, the friction collapses fast and a low-skill attacker can plausibly buy or create their way to admin.

05 · Compensating Control

What to do — in priority order.

  1. Disable public self-registration on affected sites — If the site does not strictly need anonymous signup, close the cheapest path into PR:L. For a MEDIUM verdict there is no mitigation SLA — go straight to the 365-day remediation window, but do this early on any internet-facing membership site because it materially changes exploitability.
  2. Regenerate the WishList Member API key — The exploit chain turns on disclosure of the plugin's API credential, so rotate it anywhere exposure is suspected and invalidate any copied key. There is no mitigation SLA — go straight to the 365-day remediation window, but key rotation should be bundled with patch validation on exposed sites.
  3. Monitor and, if needed, block suspicious WishList AJAX and API paths — Add temporary WAF or reverse-proxy controls around suspicious authenticated requests to wp-admin/admin-ajax.php for WishList actions and unusual traffic to ?/wlmapi/2.0/. There is no mitigation SLA — go straight to the 365-day remediation window, so use this as selective hardening where patching is delayed.
  4. Audit low-tier accounts and recent admin creation — Review subscriber signups, low-cost membership purchases, new membership levels, and any recently created administrator accounts for compromise artifacts. There is no mitigation SLA — go straight to the 365-day remediation window, but this is the fastest way to tell whether the bug was already abused.
What doesn't work
  • Resetting only administrator passwords does not fix the issue; the attacker path is to create a fresh admin via plugin/API logic.
  • Generic perimeter blocking of wp-login.php is incomplete; a valid subscriber session can come from normal site registration and then exploit admin-ajax.php and plugin API endpoints.
  • Assuming MFA on existing admins fully solves it is risky; it helps only if MFA is enforced on any newly created admin account and across all WordPress admin entry paths.
06 · Verification

Crowdsourced verification payload.

Run this on the target WordPress host or inside the application container, pointing it at the WordPress document root. Example: bash verify-cve-2026-6895.sh /var/www/html; it needs only read access to the plugin files, so root is usually unnecessary unless your web root is locked down.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# verify-cve-2026-6895.sh
# Check whether a local WordPress install appears vulnerable to CVE-2026-6895
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 1=vulnerable, 0=patched, 2=unknown/error

set -u

TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
  echo "UNKNOWN - usage: $0 /path/to/wordpress-root"
  exit 2
fi

if [[ ! -d "$TARGET" ]]; then
  echo "UNKNOWN - path does not exist: $TARGET"
  exit 2
fi

PLUGINS_DIR="$TARGET/wp-content/plugins"
if [[ ! -d "$PLUGINS_DIR" ]]; then
  echo "UNKNOWN - wp-content/plugins not found under: $TARGET"
  exit 2
fi

# Compare versions using sort -V
ver_lt() {
  [[ "$1" != "$2" ]] && [[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" == "$1" ]]
}

ver_le() {
  [[ "$1" == "$2" ]] || ver_lt "$1" "$2"
}

extract_version_from_file() {
  local f="$1"
  local v
  v=$(head -n 80 "$f" 2>/dev/null | sed -nE 's/^[[:space:]]*\*?[[:space:]]*Version:[[:space:]]*([^[:space:]]+).*/\1/p' | head -n1)
  if [[ -n "$v" ]]; then
    echo "$v"
    return 0
  fi
  return 1
}

extract_stable_tag() {
  local f="$1"
  local v
  v=$(sed -nE 's/^[[:space:]]*Stable tag:[[:space:]]*([^[:space:]]+).*/\1/p' "$f" 2>/dev/null | head -n1)
  if [[ -n "$v" ]]; then
    echo "$v"
    return 0
  fi
  return 1
}

find_plugin_dir() {
  local d
  for d in \
    "$PLUGINS_DIR/wishlist-member-x" \
    "$PLUGINS_DIR/wishlist-member" \
    "$PLUGINS_DIR/WishListMember"; do
    if [[ -d "$d" ]]; then
      echo "$d"
      return 0
    fi
  done

  d=$(find "$PLUGINS_DIR" -maxdepth 1 -mindepth 1 -type d \( -iname 'wishlist-member*' -o -iname 'wishlistmember*' \) 2>/dev/null | head -n1)
  if [[ -n "$d" ]]; then
    echo "$d"
    return 0
  fi
  return 1
}

PLUGIN_DIR=$(find_plugin_dir) || {
  echo "UNKNOWN - WishList Member plugin directory not found"
  exit 2
}

VERSION=""

# Preferred candidate files
for f in \
  "$PLUGIN_DIR/wishlist-member.php" \
  "$PLUGIN_DIR/wishlist-member-x.php" \
  "$PLUGIN_DIR/plugin.php" \
  "$PLUGIN_DIR/wpm.php" \
  "$PLUGIN_DIR/index.php"; do
  if [[ -f "$f" ]]; then
    VERSION=$(extract_version_from_file "$f" || true)
    [[ -n "$VERSION" ]] && break
  fi
done

# Fallback: first PHP file with a Version header near the top
if [[ -z "$VERSION" ]]; then
  while IFS= read -r -d '' f; do
    VERSION=$(extract_version_from_file "$f" || true)
    [[ -n "$VERSION" ]] && break
  done < <(find "$PLUGIN_DIR" -maxdepth 2 -type f -name '*.php' -print0 2>/dev/null)
fi

# Final fallback: readme stable tag
if [[ -z "$VERSION" ]]; then
  for f in "$PLUGIN_DIR/readme.txt" "$PLUGIN_DIR/readme.md"; do
    if [[ -f "$f" ]]; then
      VERSION=$(extract_stable_tag "$f" || true)
      [[ -n "$VERSION" ]] && break
    fi
  done
fi

if [[ -z "$VERSION" ]]; then
  echo "UNKNOWN - could not determine WishList Member version in $PLUGIN_DIR"
  exit 2
fi

if ver_le "$VERSION" "3.30.1"; then
  echo "VULNERABLE - WishList Member version $VERSION detected in $PLUGIN_DIR"
  exit 1
fi

if ! ver_lt "$VERSION" "3.31.0"; then
  echo "PATCHED - WishList Member version $VERSION detected in $PLUGIN_DIR"
  exit 0
fi

# This should be rare, but keeps ambiguous versions explicit.
echo "UNKNOWN - WishList Member version $VERSION detected; unable to map confidently"
exit 2
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning, inventory every WordPress site using wishlist-member-x, then split them into two buckets: public membership sites with self-registration and everything else. For this MEDIUM reassessment there is noisgate mitigation SLAno mitigation SLA — go straight to the 365-day remediation window — but exposed membership sites should still get first attention for registration hardening, API-key rotation, and log review while you validate upgrades. Apply the vendor fix to 3.31.0+ within the noisgate remediation SLA of <= 365 days, with internet-facing sites that allow easy subscriber creation patched first.

Sources

  1. NVD CVE-2026-6895
  2. Patchstack advisory for CVE-2026-6895
  3. WPScan vulnerability entry
  4. WishList Member 3.31.0 release notes
  5. WishList Member API 2.0 documentation
  6. Wordfence plugin record with active installs context
  7. CISA Known Exploited Vulnerabilities catalog
  8. FIRST EPSS API documentation
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.