On publication to the NVD, CVE-2026-12949 landed with a CVSS v3.1 base score of 9.8 (CRITICAL), network-exploitable, no authentication required, no user interaction required. The affected component is the Wishlist Member plugin for WordPress, all versions up to and including 3.34.1 — a membership and access-control plugin deployed on commercial membership sites, course platforms, and subscription businesses where WordPress accounts are literally the revenue gate.
The vulnerability is an Account Takeover via Insufficient Verification of Data Authenticity (CWE-345 class). The plugin's wpm_register() function validates the registration cookie only against the reg GET parameter, while blindly accepting attacker-supplied mergewith and wpm_id POST parameters — without verifying that the mergewith user ID actually references a temporary, incomplete registrant bound to the current registration transaction. In plain terms: an unauthenticated attacker can start (or simulate) a registration flow and tell the plugin to "merge" that flow into any existing user account on the site — including administrators. That is a full account takeover primitive over the network with zero credentials.
If you run Wishlist Member anywhere in your WordPress fleet, treat this as an incident-response-adjacent patching event, not a routine plugin update. Membership plugins sit on the authentication boundary; a 9.8 on that boundary means your entire user base — and likely your wp-admin — is exposed.
Technical Analysis
Affected Products and Versions
| Component | Affected Versions | Exposure |
|---|---|---|
| Wishlist Member plugin for WordPress | ≤ 3.34.1 | Any WordPress site with the plugin active and the registration endpoint reachable |
- CVE: CVE-2026-12949
- CVSS v3.1: 9.8 CRITICAL — Vector: Network / Low complexity / No privileges / No user interaction (consistent with the NVD listing at https://nvd.nist.gov/vuln/detail/CVE-2026-12949)
- Weakness class: Insufficient Verification of Data Authenticity (CWE-345), manifesting as unauthenticated account takeover
How the Attack Works — Defender's View of the Chain
- Reconnaissance: The attacker identifies a target WordPress site running Wishlist Member (fingerprintable via plugin assets, registration page slugs, or
/?wpmstyle endpoints). They also need a target user ID — trivial, since WordPress user IDs are enumerable via/wp-json/wp/v2/users, author archives (/?author=1), or RSS feeds. The administrator account is almost always ID 1 or a low integer. - Trigger the registration flow: The attacker hits the plugin's registration handler — the code path that invokes
wpm_register(). The function checks the registration cookie against theregGET parameter. This is the only authenticity check in the flow. - Inject the takeover parameters: In the same (or follow-on) POST request, the attacker supplies
mergewith=<victim_user_id>andwpm_id. Becausewpm_register()never verifies thatmergewithpoints to a temporary/incomplete registrant tied to this registration transaction, the plugin merges the attacker's fresh registration into the victim's existing account record. - Account takeover: The attacker now controls the victim's credentials/session context. If the victim was an administrator, the attacker has wp-admin — which in WordPress means theme/plugin editor access, arbitrary PHP execution via plugin upload, and full site compromise.
The exploitation requirement set is brutally small: network reachability to the registration endpoint and a target user ID. No valid cookie, no nonce forgery beyond what the flawed reg check accepts, no race conditions.
Exploitation Status
At the time of writing, CVE-2026-12949 is freshly published on the NVD. There is no confirmed CISA KEV entry yet — check the KEV catalog daily, because unauthenticated WordPress account-takeover bugs with 9.8 scores historically move from disclosure to mass scanning within days (we have seen this cycle repeatedly with WordPress plugin auth-bypass and privilege-escalation flaws through 2025 and into 2026). Assume scanning for vulnerable endpoints will begin immediately, and assume opportunistic exploitation of high-value membership sites will follow. Do not wait for a KEV listing to patch a 9.8 unauthenticated ATO.
Detection & Response
The most reliable detection surface for this vulnerability is HTTP request telemetry: web server access logs (nginx/Apache), WAF logs, and WordPress/application-layer logs. The exploitation signature is distinctive — a POST request to the site (typically the registration or login-handling path) containing both mergewith and wpm_id parameters, frequently paired with a reg GET parameter. Legitimate users never submit mergewith; it is an internal parameter that should only ever reference server-generated temporary registrants. Any externally supplied mergewith value is suspicious by construction.
Endpoint-side, watch for post-takeover behavior: WordPress spawning shell commands (plugin/theme editor abuse, webshell drops), and unexpected writes into wp-content/uploads or theme directories from the web server process.
Sigma Rules
---
title: CVE-2026-12949 Wishlist Member Account Takeover Attempt
description: Detects HTTP POST requests containing the mergewith and wpm_id parameters indicative of CVE-2026-12949 exploitation against the WordPress Wishlist Member plugin registration flow.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-12949
author: Security Arsenal
date: 2026/02/13
status: experimental
tags:
- attack.initial_access
- attack.t1190
- attack.t1078
logsource:
category: webserver
product: apache
detection:
selection_method:
cs-method: 'POST'
selection_params:
c-uri-query|contains:
- 'mergewith='
- 'wpm_id='
condition: selection_method and selection_params
falsepositives:
- None expected; mergewith is an internal plugin parameter and should never appear in client-supplied requests
level: critical
---
title: CVE-2026-12949 Wishlist Member Takeover Attempt - Nginx
description: Detects exploitation of CVE-2026-12949 in nginx access logs via POST requests carrying mergewith/wpm_id parameters targeting the Wishlist Member registration handler.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-12949
author: Security Arsenal
date: 2026/02/13
status: experimental
tags:
- attack.initial_access
- attack.t1190
logsource:
category: webserver
product: nginx
detection:
selection:
cs-method: 'POST'
c-uri-query|contains: 'mergewith='
condition: selection
falsepositives:
- None expected in normal operation
level: critical
---
title: WordPress Web Server Spawning Shell After Suspected Plugin Compromise
description: Detects the web server process spawning command interpreters, consistent with post-account-takeover activity on a compromised WordPress site (e.g., after CVE-2026-12949 admin takeover and plugin/theme editor abuse).
references:
- https://attack.mitre.org/techniques/T1059/
author: Security Arsenal
date: 2026/02/13
status: experimental
tags:
- attack.execution
- attack.t1059.004
logsource:
category: process_creation
product: linux
detection:
selection_parent:
ParentImage|endswith:
- '/apache2'
- '/httpd'
- '/php-fpm'
- '/nginx'
selection_child:
Image|endswith:
- '/sh'
- '/bash'
- '/dash'
- '/curl'
- '/wget'
- '/python'
- '/python3'
- '/perl'
condition: selection_parent and selection_child
falsepositives:
- Rare legitimate plugin update routines invoking shell; baseline per host
level: high
Analyst note on tuning: If your pipeline doesn't split method and query string into discrete fields, match on the raw request line (c-uri|contains: 'mergewith=' plus sc-status filtering). Prioritize alerts where the same source IP also enumerated /?author=N or hit /wp-json/wp/v2/users — that's the full kill chain in one session.
KQL — Microsoft Sentinel / Defender
This query hunts IIS W3C logs, Azure Front Door/App Gateway logs, or any web proxy/firewall telemetry ingested into Sentinel (CEF/Syslog). It flags POST requests carrying the takeover parameters and correlates with prior user enumeration from the same source.
// CVE-2026-12949 - Wishlist Member account takeover attempts and enumeration
let takeover =
W3CIISLog
| where csMethod == "POST"
| where csUriQuery has_any ("mergewith=", "wpm_id=")
| project TimeGenerated, cIP, sIP, csUriStem, csUriQuery, scStatus, csUserAgent
| extend Indicator = "TakeoverAttempt";
let enumeration =
W3CIISLog
| where csUriStem has "wp-json/wp/v2/users" or csUriQuery has "author="
| project TimeGenerated, cIP, csUriStem, csUriQuery
| extend Indicator = "UserEnumeration";
union takeover, enumeration
| summarize FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated), Indicators=make_set(Indicator), Requests=make_set(strcat(csUriStem, "?", csUriQuery)) by cIP
| where array_length(Indicators) > 1 or Indicators has "TakeoverAttempt"
| sort by LastSeen desc
// Post-exploitation: web server spawning shells on the WordPress host (Linux via Syslog/CEF or MDE)
DeviceProcessEvents
| where InitiatingProcessFileName has_any ("apache2", "httpd", "php-fpm", "nginx")
| where FileName in~ ("sh", "bash", "dash", "curl", "wget", "python3", "perl")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName
| sort by TimeGenerated desc
Velociraptor VQL
Use this hunt artifact across your WordPress fleet to (a) identify hosts running a vulnerable Wishlist Member version and (b) find recently modified PHP files in web-writable paths — the classic post-admin-takeover webshell artifact.
-- CVE-2026-12949: Identify vulnerable Wishlist Member installs and suspicious web-root writes
-- 1) Locate plugin main file and extract version header
LET plugin_files = SELECT FullPath,
read_file(filename=FullPath, length=4096) AS Header
FROM glob(globs="/**/wp-content/plugins/wishlist-member*/**/*.php",
root="/var/www")
WHERE Header =~ "Version:"
SELECT FullPath,
parse_string_with_regex(string=Header,
regex="Version:\\s*([0-9.]+)").g1 AS PluginVersion,
CASE WHEN PluginVersion <= "3.34.1" THEN "VULNERABLE - CVE-2026-12949"
ELSE "Patched" END AS Status
FROM plugin_files
WHERE PluginVersion
-- 2) Recently modified PHP files under uploads/theme dirs (webshell triage)
SELECT FullPath, Mtime, Size
FROM glob(globs={
"/var/www/**/wp-content/uploads/**/*.php",
"/var/www/**/wp-content/themes/**/*.php"
})
WHERE Mtime > now() - 604800
ORDER BY Mtime DESC
Remediation & Verification Script (Bash + WP-CLI)
Run on each WordPress host (adjust paths). It reports the installed Wishlist Member version, attempts a WP-CLI update, applies a temporary nginx/Apache-level block on the dangerous parameters if you cannot patch immediately, and greps recent access logs for exploitation attempts.
#!/usr/bin/env bash
# CVE-2026-12949 - Wishlist Member ATO: verify, patch, mitigate, and hunt
set -euo pipefail
WP_PATH="${1:-/var/www/html}"
LOG_DIR="${2:-/var/log}"
echo "=== [1] Installed Wishlist Member version ==="
if command -v wp >/dev/null 2>&1; then
sudo -u www-data wp --path="$WP_PATH" plugin list 2>/dev/null | grep -i wishlist || echo "Wishlist Member not found via WP-CLI"
else
grep -rhoE "Version:[[:space:]]*[0-9.]+" "$WP_PATH"/wp-content/plugins/wishlist-member*/ 2>/dev/null | head -5 || echo "Plugin dir not found"
fi
echo "=== [2] Update plugin via WP-CLI (recommended) ==="
echo "Run: sudo -u www-data wp --path=$WP_PATH plugin update wishlist-member"
echo "If no fixed release is available yet, DEACTIVATE until patched:"
echo " sudo -u www-data wp --path=$WP_PATH plugin deactivate wishlist-member"
echo "=== [3] Emergency WAF/web-server mitigation (blocks takeover parameters) ==="
cat <<'EOF'
# nginx - add inside server{} block until patched:
if ($args ~* "mergewith=") { return 403; }
# Apache - .htaccess or vhost config:
RewriteCond %{QUERY_STRING} mergewith= [NC,OR]
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{QUERY_STRING} wpm_id= [NC]
RewriteRule .* - [F,L]
EOF
echo "=== [4] Hunt access logs for exploitation attempts (last 7 days) ==="
find "$LOG_DIR" -name '*access*.log*' -mtime -7 2>/dev/null | while read -r f; do
zgrep -aE 'mergewith=|POST.*wpm_id=' "$f" 2>/dev/null | tail -50 && echo "-- ^^ hits in $f"
done
echo "=== [5] Audit admin accounts & recent user changes ==="
sudo -u www-data wp --path="$WP_PATH" user list --role=administrator --fields=ID,user_login,user_email,user_registered 2>/dev/null || true
echo "Review: unexpected admin users, password resets, or email changes since disclosure."
Remediation
- Patch immediately. Update Wishlist Member to the fixed release above 3.34.1 via WP-CLI (
wp plugin update wishlist-member) or the WordPress admin dashboard. Verify the running version on every site — including staging, dev, and "forgotten" marketing microsites, which are the most common breach entry points. - If no patched release is available in your channel yet, deactivate the plugin. A broken membership paywall is cheaper than a hijacked admin account. Alternatively, apply the nginx/Apache/WAF parameter block above (deny any request containing
mergewith=) as a compensating control — this severs the exploitation primitive without touching plugin code. Managed WAF users (Cloudflare, Sucuri, Wordfence, AWS WAF) should push an equivalent custom rule fleet-wide. - Assume compromise and hunt back. Search at least 7–14 days of web logs for
mergewith=/wpm_id=POSTs. Any hit means: force password resets for all privileged accounts, rotate WordPress salts/keys (wp-config.php— this kills live sessions), auditwp_usersfor new/modified admins, check user email changes (attackers reroute password-reset flows), and sweepwp-content/uploadsand theme directories for PHP files. - Harden the blast radius long-term: disable the plugin/theme editor (
define('DISALLOW_FILE_EDIT', true);), restrict/wp-adminby IP or SSO where feasible, disable or authentication-gate/wp-json/wp/v2/usersuser enumeration, enforce MFA on all admin accounts (an ATO that lands on a password reset still has to clear MFA), and file-permission lockdown so the web user cannot write PHP into uploads. - Track authoritative sources: the NVD entry (https://nvd.nist.gov/vuln/detail/CVE-2026-12949), the plugin vendor's changelog/advisory, and the CISA KEV catalog. If KEV-listed, federal remediation deadlines apply and you should treat your own remediation SLA as 72 hours maximum.
Unauthenticated account takeover on an authentication-boundary plugin is as bad as WordPress vulnerabilities get short of RCE — and in practice, admin ATO becomes RCE within minutes via plugin upload. Patch today, block mergewith at the edge as a safety net, and hunt your logs like someone already found it.
Related Resources
Security Arsenal Penetration Testing Services AlertMonitor Platform Book a SOC Assessment vulnerability-management Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.