Back to Intelligence

CVE-2026-15354: ACPT Premium WordPress Plugin Account Takeover (CVSS 9.8) — Detection, Hunting, and Remediation Guide

SA
Security Arsenal Team
September 4, 2026
12 min read

The NVD has published CVE-2026-15354, a CVSS 9.8 (Critical) vulnerability in the ACPT (Premium) plugin for WordPress, all versions up to and including 2.0.66. The flaw is a missing authorization check in the plugin's submit() function that allows unauthenticated, network-reachable attackers to overwrite any WordPress user's email address and password — including administrator accounts — and take over the site.

This is the class of vulnerability that keeps IR firms busy: a low-complexity, unauthenticated path to full administrative compromise of a content management system that underpins a massive share of the public internet. WordPress remains the most-attacked web application stack in the world, and plugin-level authorization bypasses are consistently among the fastest-converted vulnerabilities from disclosure to mass exploitation. If you operate or manage WordPress sites with ACPT Premium installed and publicly exposed user forms, treat this as an emergency patch event — not a routine maintenance item.

This post breaks down the vulnerability from a defender's perspective, provides production-ready detection content (Sigma, KQL, Velociraptor VQL), and gives you a verification and remediation script you can run today.

Technical Analysis

Affected Products and Versions

ItemDetail
ProductACPT (Premium) plugin for WordPress
Affected versionsAll versions up to and including 2.0.66
CVECVE-2026-15354
CVSS v3.19.8 — Critical (network-exploitable, low complexity, no privileges, no user interaction)
Vulnerability classMissing Authorization / Broken Access Control (CWE-862) leading to arbitrary account takeover
Referencehttps://nvd.nist.gov/vuln/detail/CVE-2026-15354

Root Cause and Exploitation Path

The defect lives in ACPT's form submission handler, the submit() function. The vulnerable code path:

  1. Accepts unauthenticated form submissions — an ACPT "user form" exposed to anonymous visitors (a common configuration for registration/profile forms built with the plugin).
  2. Fails to enforce authorization on the target user ID supplied with the submission. There is no check that the submitting party is authenticated, let alone that they own the account being modified.
  3. Passes attacker-controlled input directly into wp_update_user() — WordPress's core user update API. Because the attacker controls the user ID, they can point the update at any account, and because the form controls profile fields, they can overwrite the email address and password.

The practical attack chain is brutally simple:

  • Attacker identifies a WordPress site running ACPT Premium ≤ 2.0.66 with a public user form accepting anonymous submissions (easily fingerprinted via plugin paths such as /wp-content/plugins/acpt*/ and the form's AJAX endpoint behavior).
  • Attacker submits the form targeting user ID 1 (the default administrator account on most WordPress installs) with a new attacker-controlled email and password.
  • wp_update_user() executes the change. The attacker logs in at /wp-login.php with the new credentials and now owns the site: plugin/theme editor access, web shell upload, database access via wp-config.php credentials, and a pivot point into the hosting environment.

Exploitation Requirements

The one mitigating condition is that exploitation requires a public ACPT user form that permits anonymous submissions. Sites using ACPT only for backend custom post type management, or with forms restricted to authenticated users, are not directly exposed through this vector. However, defenders should not assume their exposure is low without verifying form configurations — the plugin is frequently deployed precisely to build front-end registration and profile forms, which is the vulnerable configuration by design.

Exploitation Status

At the time of publication, there is no confirmed entry in CISA's Known Exploited Vulnerabilities (KEV) catalog for CVE-2026-15354, and no confirmed in-the-wild mass exploitation campaign has been publicly attributed. However, the exploit primitive requires no special conditions, no race, and no memory corruption — it is a straightforward HTTP request to a form handler. Historically, unauthenticated WordPress plugin flaws with CVSS 9.8 scores see weaponization within days of public disclosure, and scanning for vulnerable plugin versions begins within hours. Operate on the assumption that exploitation is imminent or already occurring opportunistically.

Detection & Response

Because this is an HTTP-layer attack against a web application, your highest-fidelity telemetry is the web server access log (nginx/Apache), the WordPress application layer, and — after compromise — host-level process execution on the web server. Below is field-ready detection content.

Sigma Rules

The first rule targets the exploitation attempt itself: unauthenticated POSTs to the WordPress AJAX handler carrying ACPT form submission parameters along with user credential fields. Tune field names to your log pipeline (the example uses common webserver field mappings). The second rule catches the most common post-exploitation behavior on the host — the web server process spawning a shell after admin compromise and web shell upload.

YAML
---
title: ACPT Premium WordPress Plugin Unauthenticated Account Takeover Attempt (CVE-2026-15354)
id: 8b2e4a71-3c5d-4f6a-9b1e-7d8c2a4f5e01
status: experimental
description: Detects HTTP POST requests to the WordPress AJAX endpoint carrying ACPT form submission parameters combined with user credential fields, consistent with unauthenticated exploitation of CVE-2026-15354 to overwrite arbitrary user email/password via wp_update_user().
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-15354
author: Security Arsenal
date: 2026/02/09
tags:
  - attack.initial_access
  - attack.t1190
  - attack.persistence
  - attack.t1098
logsource:
  category: webserver
detection:
  selection_uri:
    cs-uri|contains:
      - '/wp-admin/admin-ajax.php'
      - '/wp-json/'
  selection_method:
    cs-method: 'POST'
  selection_body:
    cs-body|contains:
      - 'acpt'
  selection_creds:
    cs-body|contains:
      - 'user_pass'
      - 'user_email'
      - 'user_login'
      - 'user_id'
  condition: selection_uri and selection_method and selection_body and selection_creds
falsepositives:
  - Legitimate authenticated profile updates via ACPT forms (correlate against presence of wordpress_logged_in cookie)
level: high
---
title: Web Server Process Spawning Shell - Possible WordPress Post-Compromise Activity
id: 3f9c1d62-8a4b-4e7d-b2f1-6c9a3e5d7b02
status: experimental
description: Detects web server or PHP worker processes spawning interactive shells or system binaries, a common indicator after WordPress administrative compromise and web shell upload (e.g., following CVE-2026-15354 account takeover).
references:
  - https://nvd.nist.gov/vuln/detail/CVE-2026-15354
  - https://attack.mitre.org/techniques/T1505/003/
author: Security Arsenal
date: 2026/02/09
tags:
  - attack.persistence
  - attack.t1505.003
  - attack.execution
  - attack.t1059.004
logsource:
  category: process_creation
  product: linux
detection:
  selection_parent:
    ParentImage|endswith:
      - '/php-fpm'
      - '/php8.1-fpm'
      - '/php8.2-fpm'
      - '/php8.3-fpm'
      - '/apache2'
      - '/httpd'
      - '/nginx'
  selection_child:
    Image|endswith:
      - '/sh'
      - '/bash'
      - '/dash'
      - '/zsh'
      - '/curl'
      - '/wget'
      - '/nc'
      - '/ncat'
      - '/python'
      - '/python3'
      - '/perl'
  condition: selection_parent and selection_child
falsepositives:
  - Legitimate plugin or backup tooling invoking system commands (rare on production web servers; baseline and allowlist by command line)
level: high

Analyst note on tuning: the first rule will fire on legitimate ACPT profile-form usage if you deploy such forms. The critical discriminator is the absence of a wordpress_logged_in_* session cookie on the request. If your WAF/reverse proxy logging captures cookie presence (not values), add that as a filter: alert only when the POST carries credential fields and no logged-in cookie is present. That single filter converts this from a noisy rule into a high-fidelity one.

KQL (Microsoft Sentinel / Defender)

If your WordPress hosts forward nginx/Apache access logs via Syslog/CEF into Sentinel, hunt for the exploitation pattern across your fleet. This query also surfaces scanning behavior by counting requests per source IP — a single IP hitting many distinct hosts or repeatedly POSTing credential fields is a strong exploitation signal.

KQL — Microsoft Sentinel / Defender
let lookback = 7d;
CommonSecurityLog
| where TimeGenerated >= ago(lookback)
| where RequestMethod == "POST"
| where RequestURL has_any ("/wp-admin/admin-ajax.php", "/wp-json/")
| where AdditionalExtensions has "acpt" or RequestURL has "acpt"
| where AdditionalExtensions has_any ("user_pass", "user_email", "user_login", "user_id")
   or Message has_any ("user_pass", "user_email", "user_id", "acpt")
| summarize Requests = count(),
            DistinctTargets = dcount(DestinationHostName),
            FirstSeen = min(TimeGenerated),
            LastSeen = max(TimeGenerated),
            SampleURLs = make_set(RequestURL, 5)
      by SourceIP, DestinationHostName
| order by Requests desc
;
// Fallback for environments ingesting raw web logs via Syslog table
Syslog
| where TimeGenerated >= ago(lookback)
| where SyslogMessage has "POST" and SyslogMessage has "admin-ajax.php"
| where SyslogMessage has "acpt"
| where SyslogMessage has_any ("user_pass", "user_email", "user_login", "user_id")
| project TimeGenerated, Computer, HostIP, SyslogMessage
| order by TimeGenerated desc

For post-compromise hunting on hosts with Defender for Endpoint onboarded (Linux web servers supported):

KQL — Microsoft Sentinel / Defender
DeviceProcessEvents
| where TimeGenerated >= ago(7d)
| where InitiatingProcessFileName has_any ("php-fpm", "apache2", "httpd", "nginx", "php")
| where FileName in~ ("sh", "bash", "dash", "curl", "wget", "nc", "ncat", "python3", "perl")
| project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName, RemoteIP
| order by TimeGenerated desc

Velociraptor VQL

Two hunt artifacts are warranted here. The first verifies ACPT plugin exposure across your web server fleet by reading the plugin's version metadata (definitive for patch verification). The second hunts the access logs for the exploitation pattern for retroactive IR scoping.

VQL — Velociraptor
-- Artifact 1: Enumerate ACPT plugin version across WordPress installs
LET plugin_files = SELECT FullPath
FROM glob(globs='/var/www/**/wp-content/plugins/acpt*/**/*.php')
WHERE FullPath =~ 'acpt.php$'

SELECT FullPath,
       read_file(filename=FullPath, length=5000) AS Header,
       parse_string_with_regex(string=read_file(filename=FullPath, length=5000),
                               regex='Version:\\s*([0-9.]+)').g1 AS PluginVersion
FROM plugin_files
VQL — Velociraptor
-- Artifact 2: Hunt web access logs for CVE-2026-15354 exploitation attempts
LET logs = SELECT FullPath
FROM glob(globs=['/var/log/nginx/*.log', '/var/log/apache2/*.log', '/var/log/httpd/*.log'])

SELECT FullPath,
       Line,
       parse_string_with_regex(string=Line, regex='^([0-9.]+) ').g1 AS SourceIP
FROM foreach(row=logs,
query={
  SELECT FullPath, Line
  FROM parse_lines(filename=FullPath)
  WHERE Line =~ 'POST' AND Line =~ 'admin-ajax.php'
    AND Line =~ 'acpt'
    AND Line =~ 'user_pass|user_email|user_login|user_id'
})

On a confirmed compromise, also pull SELECT * FROM netstat() and review the WordPress wp_users table (via the site's database backup or wp-cli) for administrator accounts with recently changed emails or unexpected user_registered/user_activation_key timestamps — attackers frequently change the admin email first to intercept password reset and notification traffic.

Verification and Remediation Script

Run the following on each WordPress host (requires WP-CLI, standard on managed WordPress infrastructure). It inventories ACPT installs, flags vulnerable versions, checks for suspicious recent admin credential changes, and applies the update or deactivates the plugin as a hard stop.

Bash / Shell
#!/bin/bash
# CVE-2026-15354 - ACPT Premium WordPress Plugin verification and remediation
# Run as the web server user (e.g., sudo -u www-data) per site document root.

DOCROOT="${1:-/var/www/html}"
VULN_MAX="2.0.66"

echo "[*] Checking $DOCROOT for ACPT plugin..."

if ! wp --path="$DOCROOT" plugin is-installed acpt 2>/dev/null && \
   ! wp --path="$DOCROOT" plugin is-installed acpt-premium 2>/dev/null; then
  echo "[+] ACPT not installed in $DOCROOT. Not affected."
  exit 0
fi

for slug in acpt acpt-premium; do
  if wp --path="$DOCROOT" plugin is-installed "$slug" 2>/dev/null; then
    VER=$(wp --path="$DOCROOT" plugin get "$slug" --field=version 2>/dev/null)
    echo "[!] Found $slug version $VER"

    if [ "$(printf '%s\n' "$VULN_MAX" "$VER" | sort -V | head -n1)" = "$VER" ] || [ "$VER" = "$VULN_MAX" ]; then
      echo "[!!!] VULNERABLE to CVE-2026-15354 (<= $VULN_MAX). Attempting update..."
      wp --path="$DOCROOT" plugin update "$slug" 2>/dev/null
      NEWVER=$(wp --path="$DOCROOT" plugin get "$slug" --field=version 2>/dev/null)
      if [ "$(printf '%s\n' "$VULN_MAX" "$NEWVER" | sort -V | tail -n1)" = "$NEWVER" ] && [ "$NEWVER" != "$VULN_MAX" ]; then
        echo "[+] Updated to $NEWVER. Verify plugin changelog confirms the fix."
      else
        echo "[!!!] Update unavailable or failed. DEACTIVATING plugin as a hard stop."
        wp --path="$DOCROOT" plugin deactivate "$slug"
        echo "[!] Plugin deactivated. Re-enable only after a fixed version (> $VULN_MAX) is available."
      fi
    else
      echo "[+] Version $VER is above $VULN_MAX. Confirm against vendor advisory."
    fi
  fi
done

echo "[*] Auditing administrator accounts for recent credential/email changes..."
wp --path="$DOCROOT" user list --role=administrator \
  --fields=ID,user_login,user_email,user_registered --format=table

echo "[*] Review the admin emails above against expected values."
echo "[*] Check access logs for exploitation attempts:"
grep -E 'POST .*(admin-ajax\.php|wp-json)' /var/log/nginx/access.log /var/log/apache2/access.log 2>/dev/null \
  | grep -i 'acpt' | grep -Ei 'user_pass|user_email|user_login|user_id' | tail -n 50

echo "[*] Done. If compromise is suspected, force-reset ALL admin passwords:"
echo "    wp --path=$DOCROOT user reset-password <admin_id>"

Remediation

  1. Update the ACPT (Premium) plugin immediately to a version greater than 2.0.66 (confirm the exact fixed version against the vendor's changelog/advisory and the Wordfence/NVD record at https://nvd.nist.gov/vuln/detail/CVE-2026-15354). This is the only complete fix — the vulnerability is a missing authorization check in code, not a configuration weakness.
  2. If a patched version is not yet available in your update channel, deactivate the plugin or remove public anonymous form submissions. The exploitation precondition is a public ACPT user form permitting anonymous submissions. Restricting form access to authenticated users (or taking the form offline) eliminates the attack surface until the patch lands.
  3. Inventory exposure across your fleet. ACPT is a premium plugin and will not appear in free plugin directories — query your asset inventory and hosting panels for it explicitly. The Bash script above automates version verification per docroot.
  4. Hunt retroactively. Grep 30+ days of access logs for the POST pattern (admin-ajax.php + acpt + credential fields). Unauthenticated account takeover is silent from the application's perspective — wp_update_user() fires normal hooks, so there may be no anomalous application log entry at all. Web logs are your ground truth.
  5. On any confirmed exploitation, assume full site compromise: force-reset all administrator passwords (and any targeted accounts), rotate WordPress salts/keys in wp-config.php, rotate database credentials, audit wp_users for rogue admin accounts, scan for web shells in wp-content/uploads/ and theme/plugin directories, and review outbound connections from the host. A changed admin email address is a strong indicator the account was already hijacked.
  6. Harden persistently: enforce MFA on all WordPress administrator accounts (limits the value of credential overwrite attacks), place /wp-admin and xmlrpc.php behind IP allowlisting or authentication at the reverse proxy where operationally feasible, deploy a WAF rule blocking unauthenticated POSTs carrying user-credential fields to admin-ajax.php, and subscribe to plugin-specific vulnerability feeds so premium-plugin CVEs don't slip past automated patch management.

WordPress plugin vulnerabilities of this severity convert to exploitation faster than almost any other vulnerability class. If ACPT Premium is anywhere in your estate with public forms, patch today — and verify, don't assume.

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.