This is a side door into the plugin’s control panel, not a master key to the whole building
CVE-2025-22299 is a missing authorization flaw in the WordPress plugin ai-for-seo affecting versions through 1.2.9 and fixed in 1.2.10. Public advisories describe a missing capability check on a plugin function, which means a request can reach a privileged plugin action without the authorization gate WordPress admins would expect.
The vendor's MEDIUM call is broadly right on business impact, but the published scoring looks a little too calm on exploitability. Multiple public databases describe the issue as unauthenticated even though the supplied CVSS vector says PR:L; that mismatch pushes the real-world risk slightly upward, but not into HIGH because there is still no evidence of RCE, data theft, tenant breakout, or broad platform compromise.
4 steps from start to impact.
Enumerate WordPress and plugin presence
WPScan or httpx to identify a WordPress site and then fingerprints the ai-for-seo plugin by passive asset discovery, plugin paths, or content enumeration. This is commodity recon and easy to automate across large website sets.- Target runs WordPress
- The
ai-for-seoplugin is installed - The site is reachable from the internet
- The plugin is relatively niche rather than ubiquitous
- Some sites suppress plugin enumeration or hide static paths
- Enterprise estates often do not expose most WordPress instances publicly
Reach the vulnerable function directly
curl, Burp Suite, or a simple bot, the attacker sends a crafted request to the vulnerable plugin endpoint or action. Public intelligence says the problem is a missing capability check, so the request succeeds where WordPress should have rejected it.- Vulnerable version is present (
<= 1.2.9) - The vulnerable route is exposed through normal WordPress request handling
- The exact function is not publicly documented in the advisories we found
- Some WAFs or hardening plugins may block noisy probing
- If the route is admin-only in practice, reverse proxies or extra auth can reduce reachability
Execute the unauthorized plugin action
curl or a small Python requests script. Based on the plugin's feature set, plausible outcomes are SEO metadata manipulation, image/alt-text generation actions, settings abuse, or unwanted credit consumption, not host takeover.- The vulnerable function maps to a state-changing action
- The plugin accepts the request without a capability check
- Published advisories do not show that the action grants admin access or arbitrary code execution
- Impact may be limited to plugin-specific data or workflow effects
- Some actions may require valid object IDs or site-specific context
Land limited integrity impact
- The site relies on this plugin for production metadata or media workflows
- No public evidence of chaining to code execution
- No stated confidentiality or availability impact
- Damage is typically visible and operationally reversible
The supporting signals.
| In-the-wild status | No public evidence of active exploitation found. I found no KEV entry and no vendor/advisory claim of observed campaigns. |
|---|---|
| Proof-of-concept availability | No public PoC located in the sources reviewed. That lowers opportunistic abuse slightly, though missing-auth WordPress flaws are usually trivial to reproduce once the route is known. |
| EPSS | Very low. Your provided EPSS was 0.00114; public aggregators currently show similarly near-zero values, which is consistent with low broad attacker interest. |
| KEV status | Not listed in CISA KEV as reviewed. No federal exploitation signal. |
| CVSS and interpretation | Vendor baseline is 4.3 MEDIUM with CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N, which says network-reachable, low complexity, integrity-only, and requires low privileges. |
| Privilege mismatch | Important caveat: Patchstack labels required privilege as Unauthenticated, and Wordfence says the flaw lets unauthenticated attackers perform an unauthorized action. If that is correct, the official vector understates exploitability. |
| Affected versions | <= 1.2.9 according to NVD, Patchstack, WPScan, and Wordfence. |
| Fixed version | 1.2.10 is the first patched release called out by Patchstack, WPScan, and Wordfence. Current WordPress.org listing is much newer. |
| Exposure footprint | WordPress.org currently shows 2,000+ active installs for the plugin, and Wordfence lists about 42,729 downloads. That is real internet exposure, but nowhere near a core-platform problem. |
| Scanning and detection reality | Shodan/Censys/GreyNoise are not very helpful here. This is a CMS plugin, not a standalone service with a clean banner, so exposure is usually found via WordPress/plugin enumeration rather than internet-wide banner search. |
noisgate verdict.
The decisive factor is limited downstream impact: everything public points to a plugin-scoped authorization failure, not server compromise or tenant takeover. I nudged the score up because the public writeups suggest unauthenticated reachability, but the population is narrow and the blast radius still looks contained.
Why this verdict
- Vendor baseline starts at MEDIUM because the stated impact is integrity-only with no confidentiality or availability loss.
- Exploitability likely deserves a small upward adjustment because Patchstack and Wordfence both describe the issue as reachable by unauthenticated attackers, which is easier to weaponize than the supplied
PR:Lvector implies. - Population pressure pushes back down because this is a niche WordPress plugin with roughly
2,000+active installs, not a default enterprise platform or ubiquitous edge service. - Blast radius stays bounded because there is no public sign of RCE, auth takeover, privilege escalation to WordPress admin, or a clean path to full site compromise from this bug alone.
Why not higher?
I do not see the ingredients for HIGH: no KEV, no exploitation evidence, no public PoC, and no proof that the vulnerable action crosses from plugin abuse into system compromise. Even if internet-reachable, a plugin-scoped integrity issue on a niche install base is not the same class of problem as unauthenticated RCE on an edge appliance.
Why not lower?
I also would not bury this as LOW or IGNORE because public sources strongly suggest the flaw may be unauthenticated, and WordPress sites are routinely probed at scale. If the plugin is internet-facing on a production website, unauthorized state change is still meaningful operational risk.
What to do — in priority order.
- Update to
1.2.10+— This is the clean fix and removes the missing authorization path entirely. For a MEDIUM verdict there is no noisgate mitigation SLA; go straight to the normal patch cycle and complete remediation within the 365-day window, sooner for internet-facing marketing sites. - Restrict access to WordPress admin and AJAX surfaces — If patching is delayed, put
wp-admin, login flows, and high-riskadmin-ajax.phppatterns behind IP allowlists, reverse-proxy auth, or equivalent origin controls where business permits. There is no mitigation SLA for MEDIUM, so use this when exposure is public and patch timing is messy. - Turn on plugin auto-updates — WordPress plugin flaws age badly because bot traffic keeps retrying old bugs. Enable controlled auto-updates for this plugin or for low-risk content plugins generally so the estate stops carrying stale versions into the next quarter.
- Monitor for plugin-specific POST activity — Add temporary logging and alerting for unusual unauthenticated requests touching plugin routes,
admin-ajax.php, or settings-changing actions tied toai-for-seo. This does not fix the bug, but it gives you evidence if someone is already poking at it while you work through remediation.
- A generic EDR story does not help much here; this is a web application authorization flaw, not a process-execution problem on the host.
- Relying on MFA for WordPress admins is not sufficient if the vulnerable action is genuinely unauthenticated, because the exploit path bypasses the normal login gate.
- A broad network perimeter blocklist is weak protection against commodity web probing; attackers will blend into normal HTTP traffic unless you target the WordPress/plugin paths specifically.
Crowdsourced verification payload.
Run this on the target WordPress host or inside the container/VM that holds the site files. Invoke it as bash check-ai-for-seo-cve-2025-22299.sh /var/www/html (or pass the WordPress docroot); no root is required if you can read the plugin files.
#!/usr/bin/env bash
# check-ai-for-seo-cve-2025-22299.sh
# Detects whether WordPress plugin ai-for-seo is vulnerable to CVE-2025-22299
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
TARGET="${1:-.}"
PLUGIN_DIR="$TARGET/wp-content/plugins/ai-for-seo"
README="$PLUGIN_DIR/readme.txt"
MAIN_PHP=""
VERSION=""
find_main_php() {
local f
for f in "$PLUGIN_DIR"/*.php; do
[ -f "$f" ] || continue
if grep -qiE '^\s*Plugin Name:\s*' "$f" 2>/dev/null; then
echo "$f"
return 0
fi
done
return 1
}
get_version_from_readme() {
[ -f "$README" ] || return 1
awk -F': *' 'tolower($1)=="stable tag" {print $2; exit}' "$README" | tr -d '\r'
}
get_version_from_php() {
local phpfile="$1"
[ -f "$phpfile" ] || return 1
awk -F': *' 'tolower($1)=="version" {print $2; exit}' "$phpfile" | tr -d '\r'
}
verlte() {
[ "$1" = "$2" ] && return 0
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" = "$1" ]
}
vergte() {
[ "$1" = "$2" ] && return 0
[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1)" = "$1" ]
}
if [ ! -d "$PLUGIN_DIR" ]; then
echo "UNKNOWN - plugin directory not found: $PLUGIN_DIR"
exit 2
fi
VERSION="$(get_version_from_readme)"
if [ -z "$VERSION" ]; then
MAIN_PHP="$(find_main_php || true)"
if [ -n "$MAIN_PHP" ]; then
VERSION="$(get_version_from_php "$MAIN_PHP")"
fi
fi
if [ -z "$VERSION" ]; then
echo "UNKNOWN - could not determine installed ai-for-seo version"
exit 2
fi
if verlte "$VERSION" "1.2.9"; then
echo "VULNERABLE - ai-for-seo version $VERSION is <= 1.2.9"
exit 1
fi
if vergte "$VERSION" "1.2.10"; then
echo "PATCHED - ai-for-seo version $VERSION is >= 1.2.10"
exit 0
fi
echo "UNKNOWN - parsed version $VERSION but could not classify reliably"
exit 2
If you remember one thing.
ai-for-seo, confirm version with the verification script, and move anything on <= 1.2.9 into your normal web-app patch queue. For this MEDIUM verdict there is no noisgate mitigation SLA — go straight to the 365-day remediation window under the noisgate remediation SLA, but do not treat public-facing sites casually: if an exposed marketing site cannot be patched in the next routine cycle, apply temporary access restrictions to WordPress admin/AJAX paths while you schedule the upgrade.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.