This is a side door left unlocked on a storefront that only exists if you installed this exact plugin
CVE-2021-24931 is an unauthenticated SQL injection in the *Secure Copy Content Protection and Content Locking* WordPress plugin. Affected versions are all releases before 2.8.2; the bug sits in the ays_sccp_results_export_file AJAX action, where the sccp_id parameter is fed into a SQL query without proper sanitization. Because the action is reachable through /wp-admin/admin-ajax.php for unauthenticated users, an external attacker can hit it directly over HTTP and pull or tamper with WordPress database content.
The raw 9.8 score is directionally understandable on a lab bench: no auth, no user click, database compromise potential. In real fleets, though, this is not a universal WordPress bug; it only matters on sites running this specific plugin, and even then the practical target set is public-facing WordPress estates rather than your whole enterprise. That trims it down from *internet-on-fire critical* to a very solid HIGH—still patch-worthy fast because public PoC, scanner support, high EPSS, and current attack telemetry all keep the risk elevated.
4 steps from start to impact.
Identify a site running the plugin
secure-copy-content-protection is installed. Common tooling is WPScan, passive plugin enumeration, or a nuclei template reference set that specifically targets this CVE. Once the plugin is confirmed, the attacker knows a public AJAX route may be reachable without login.- Target runs WordPress
- The vulnerable plugin is installed in a version older than
2.8.2 - The site is reachable over HTTP/HTTPS
- This is a plugin-specific flaw, not a WordPress core issue
- Many enterprises do not run this plugin at all
- Passive fingerprinting can fail if plugin paths are hidden or aggressively cached
Reach the unauthenticated AJAX handler
/wp-admin/admin-ajax.php?action=ays_sccp_results_export_file. Because this action is exposed to unauthenticated users, the attacker does not need a WordPress account, session, or CSRF setup. Weaponization is simple with curl, browser requests, or sqlmap pointed at the vulnerable parameter.- The site's
admin-ajax.phpendpoint is externally reachable - No upstream control blocks the request shape
- Some WAFs and managed WordPress front ends will flag obvious SQLi syntax
- Rate limits or bot controls can slow bulk exploitation
/wp-admin/admin-ajax.php with action=ays_sccp_results_export_file and malformed sccp_id values.Exploit sccp_id with SQL injection
sccp_id parameter is concatenated into a database query without safe preparation. Public references include a WPScan proof of concept and a ProjectDiscovery nuclei template that uses time-based SQLi for validation; operators can move from that to sqlmap for enumeration and extraction. In the common WordPress deployment model, the app's DB user has broad rights over the WordPress schema, so the attacker can often read user hashes, emails, and site configuration.- Backend database executes the crafted payload
- WordPress DB credentials used by the app have normal schema access
- Blind/time-based extraction is slower than a clean UNION path
- Custom DB hardening or reduced grants can limit write impact
- WAF normalization may break noisy payloads before they land
Turn database access into tenant compromise
wp_users, steal password hashes, enumerate salts and options, or alter application data. Depending on DB permissions and local hardening, they may pivot into admin takeover by changing credentials or planting data that supports later code execution through the CMS. The practical impact is typically full compromise of that WordPress site and its content store, not broad domain-wide enterprise takeover.- Useful data exists in the WordPress database
- The attacker can successfully extract or modify rows
- Impact is mostly confined to the affected WordPress tenant
- EDR does not help much until the attacker moves beyond SQLi into post-exploitation
- Strong credential hygiene can blunt the value of dumped password hashes
The supporting signals.
| In-the-wild status | Not in CISA KEV, but this is not dormant. CrowdSec currently reports 636 exploiting IPs and steady automated scanning against the vulnerable AJAX route. |
|---|---|
| Proof-of-concept availability | Public PoC exists in the WPScan advisory. NVD also links a Packet Storm exploit reference, and ProjectDiscovery ships a nuclei template. |
| EPSS | 0.7216 with roughly 98.8th percentile exposure pressure, which is very high for a WordPress plugin CVE. |
| KEV status | Absent from the CISA Known Exploited Vulnerabilities catalog at time of review. |
| CVSS vector reality check | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H correctly captures the *technical* ease and impact, but it ignores the biggest real-world filter: only sites running this specific plugin are in play. |
| Affected versions | All plugin versions before 2.8.2 are affected; NVD models this as a CPE range ending before 2.8.2. |
| Fixed versions | The flaw is fixed in 2.8.2. The public plugin has since moved far beyond that; WordPress.org currently lists 5.1.6. |
| Exposure population | WordPress.org currently shows 20,000+ active installations. That is meaningful internet exposure, but still far smaller than a mainstream plugin or WordPress core issue. |
| Disclosure timeline | WPScan lists public publication on 2021-11-08; NVD published the CVE on 2021-12-06. |
| Researcher / submitter | Credited to Krzysztof Zając, per WPScan/OpenCVE record data. |
noisgate verdict.
The decisive factor is that this is unauthenticated remote SQLi on a public endpoint, which keeps exploitation friction low once a target is found. It stays out of CRITICAL because the reachable population is materially narrowed by the requirement that the victim run this specific WordPress plugin, so this is a dangerous internet-facing application flaw rather than an all-fleet emergency.
Why this verdict
- Unauthenticated remote path: no login, no click, no prior foothold; the attacker can hit
admin-ajax.phpdirectly from the internet, which preserves a high baseline. - Public weaponization exists: WPScan published a concrete PoC, NVD links exploit references, and ProjectDiscovery maintains a ready-made
nucleicheck. This is easy to operationalize withcurl,sqlmap, or scanner automation. - Observed attack pressure is real: despite no KEV listing, CrowdSec reports active exploitation telemetry and hundreds of exploiting IPs, so this is not theoretical shelfware.
- Downward pressure from exposure population: this is a plugin-specific bug. The attacker needs a site that actually runs *Secure Copy Content Protection and Content Locking* below
2.8.2, which sharply narrows the target fraction versus a core WordPress or mass-market plugin issue. - Blast radius is usually tenant-level: compromise is often severe for the affected WordPress site, but it does not inherently imply domain-wide enterprise compromise unless that site is already a privileged hub.
Why not higher?
I am not calling this CRITICAL because the main friction is population reachability, not exploit syntax. A 9.8 score assumes every network-reachable instance matters equally; in production, only a subset of public WordPress sites with this exact plugin installed are exposed, and the likely blast radius is usually one web tenant at a time rather than immediate estate-wide compromise.
Why not lower?
I am not dropping this to MEDIUM because the attack does not require credentials, internal access, user interaction, or chaining with another bug. Add the public PoC, scanner support, and active opportunistic exploitation telemetry, and this is well above backlog hygiene.
What to do — in priority order.
- Block the vulnerable AJAX action — At the reverse proxy or WAF, block requests to
/wp-admin/admin-ajax.phpwhereaction=ays_sccp_results_export_file, or restrict that action to trusted admin source ranges if the workflow is genuinely needed. Because there is active exploitation evidence, deploy this within hours, not on a normal 30-day HIGH cycle. - Pull external exposure on low-value sites — If the affected site does not need to be public, put it behind VPN, identity-aware proxy, or temporary maintenance controls. This sharply cuts attacker reach and should be done within hours for any internet-facing instance that cannot be patched immediately.
- Hunt for exploitation telemetry — Search WAF, reverse-proxy, and web logs for
/wp-admin/admin-ajax.phprequests withaction=ays_sccp_results_export_file, array-stylesccp_id[],UNION,SLEEP(, or abnormally repeated export attempts. Start the hunt immediately and keep it active until patch coverage is complete. - Constrain database privileges where feasible — If your WordPress DB accounts are over-permissioned, reduce them to the minimum schema rights needed by the application. This will not prevent exploitation, but it can limit post-injection write impact; treat it as a supporting control and complete it within the normal HIGH remediation window if urgent containment is already in place.
- Patch the plugin everywhere it exists — Upgrade all instances to
2.8.2or later and verify no stragglers remain in staging, DR, or forgotten marketing sites. Because current exploitation exists, treat patching as accelerated and begin within hours, not at the edge of the usual HIGH timeline.
- EDR on the web server does not reliably stop SQLi at the HTTP layer; it only helps if the intrusion turns into post-exploitation on the host.
- Hiding
wp-content/plugins/paths is not a control. It may slow passive fingerprinting, but the vulnerable endpoint can still be probed directly. - MFA for WordPress admins is good hygiene but irrelevant to the initial exploit path, because this bug is reachable without authentication.
Crowdsourced verification payload.
Run this on the target WordPress host or in a build/CI artifact where the plugin files are present. Invoke it as bash check-cve-2021-24931.sh /var/www/html and use a path to the WordPress root; read access only to the plugin directory is sufficient.
#!/usr/bin/env bash
# check-cve-2021-24931.sh
# Detects whether the Secure Copy Content Protection and Content Locking plugin
# is vulnerable to CVE-2021-24931 based on installed version.
#
# Usage: bash check-cve-2021-24931.sh /path/to/wordpress
# Exit codes:
# 0 = PATCHED
# 1 = VULNERABLE
# 2 = UNKNOWN (not installed / unreadable / cannot determine)
set -u
TARGET_VERSION="2.8.2"
WP_ROOT="${1:-}"
PLUGIN_DIR_REL="wp-content/plugins/secure-copy-content-protection"
if [[ -z "$WP_ROOT" ]]; then
echo "UNKNOWN - usage: $0 /path/to/wordpress"
exit 2
fi
PLUGIN_DIR="$WP_ROOT/$PLUGIN_DIR_REL"
if [[ ! -d "$PLUGIN_DIR" ]]; then
echo "UNKNOWN - plugin directory not found: $PLUGIN_DIR"
exit 2
fi
# Find the plugin header file by looking for the plugin name or a Version header.
PLUGIN_FILE=""
while IFS= read -r -d '' file; do
if grep -qi "Plugin Name:.*Secure Copy Content Protection" "$file" 2>/dev/null; then
PLUGIN_FILE="$file"
break
fi
done < <(find "$PLUGIN_DIR" -maxdepth 2 -type f -name '*.php' -print0 2>/dev/null)
if [[ -z "$PLUGIN_FILE" ]]; then
# Fallback: grab the first PHP file with a Version header.
while IFS= read -r -d '' file; do
if grep -qi '^\s*Version:' "$file" 2>/dev/null; then
PLUGIN_FILE="$file"
break
fi
done < <(find "$PLUGIN_DIR" -maxdepth 2 -type f -name '*.php' -print0 2>/dev/null)
fi
if [[ -z "$PLUGIN_FILE" || ! -r "$PLUGIN_FILE" ]]; then
echo "UNKNOWN - could not locate readable plugin header file"
exit 2
fi
INSTALLED_VERSION=$(awk -F': *' 'BEGIN{IGNORECASE=1} /^\s*Version:/ {print $2; exit}' "$PLUGIN_FILE" | tr -d '\r')
if [[ -z "$INSTALLED_VERSION" ]]; then
echo "UNKNOWN - could not parse version from $PLUGIN_FILE"
exit 2
fi
version_lt() {
# returns 0 if $1 < $2
[[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" != "$2" && "$1" != "$2" ]]
}
if version_lt "$INSTALLED_VERSION" "$TARGET_VERSION"; then
echo "VULNERABLE - installed version $INSTALLED_VERSION is older than fixed version $TARGET_VERSION"
exit 1
fi
if [[ "$INSTALLED_VERSION" == "$TARGET_VERSION" || "$(printf '%s\n%s\n' "$INSTALLED_VERSION" "$TARGET_VERSION" | sort -V | tail -n1)" == "$INSTALLED_VERSION" ]]; then
echo "PATCHED - installed version $INSTALLED_VERSION is at or above fixed version $TARGET_VERSION"
exit 0
fi
echo "UNKNOWN - installed version $INSTALLED_VERSION could not be compared reliably"
exit 2
If you remember one thing.
secure-copy-content-protection plugin, assume any internet-facing host below 2.8.2 is exposed, and put a temporary block on the vulnerable AJAX action immediately, within hours because there is live exploitation telemetry. Even though the reassessed bucket is HIGH, active exploitation overrides the normal timetable: for this case, treat the noisgate mitigation SLA as patch / mitigate immediately, within hours, then complete the actual plugin upgrade under the noisgate remediation SLA well before 180 days—realistically this should be finished in the current patch cycle, not left to long-tail backlog.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.