A crooked spreadsheet cell that only bites when the boss opens the ledger
CVE-2026-5721 is a stored cross-site scripting flaw in the wpDataTables plugin for WordPress — the plugin used to render dynamic data tables and charts on tens of thousands of WordPress sites. An attacker who can reach the table-editing surface can inject JavaScript that gets persisted in table cell/config data and executed later in another user's browser session when the table is rendered or previewed. The vector is AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N — network reachable, high attack complexity, no privileges required, but user interaction is required and the impact is a *scope-changed* low confidentiality and integrity hit (typical of DOM/session-context XSS).
The vendor MEDIUM (4.7) rating is honest. This is not an unauth RCE, it is not an admin takeover primitive on its own, and the AC:H reflects real preconditions: the attacker needs a plausible path to inject into a table configuration, and a privileged user has to render or interact with the poisoned view. Stored XSS in a widely-deployed plugin still deserves attention because a triggered payload runs in the admin's browser context and can pivot to nonce theft or account creation, but the base severity is not being under-called.
4 steps from start to impact.
Reach a wpDataTables input surface
- Vulnerable wpDataTables version installed
- Editable table or form-fed table configured on target site
- Most wpDataTables installs restrict table editing to admin/editor roles by default
- Front-end editing is an opt-in add-on, not default
- WAF rules for classic XSS payloads (
<script>,onerror=) catch naive attempts
Persist payload in table data
<img src=x onerror=fetch(...)> or an SVG event-handler payload) into the vulnerable field. The plugin stores it in the WordPress database (wp_options or a custom wpDataTables table) without adequate output-side encoding.- Successful write to a persisted field
- No server-side sanitization of the specific field
- Recent WordPress
wp_kses_postfilters strip many event handlers - Some sinks pass through KSES on save — CVE only lives in the sinks that don't
<script, onerror, onload patterns in wpDataTables tables.Victim triggers rendering
S:C means the script executes in the WordPress admin origin, granting access to admin cookies / nonces of the current session.- Privileged user visits the affected view
- Browser executes the injected script (no strict CSP)
- Admin-only tables limit the victim pool to actual admins visiting a specific screen
- Modern browsers with strict
Content-Security-Policyon the admin area (rare on WP) would block inline execution
Session hijack / privilege abuse
/wp-json/wp/v2/users, install a malicious plugin, or drop a webshell. This is where a MEDIUM XSS becomes an operational admin takeover on that single site.- Victim is an administrator
- No 2FA challenge on privileged mutations
- Sites with 2FA on user-create actions (rare), or plugin-signature enforcement, blunt the chain
- Wordfence / MalCare will alert on unexpected admin creation or plugin install
manage_options (or the plugin's specific capability) is not granted to Contributor/Author roles. Removes the low-privilege injection path pending patch.<script, onerror=, onload=, javascript: and <svg; treat any hit on a production site as an incident.- Cloudflare 'Bot Fight Mode' alone — this is a stored XSS, not a bot problem; the payload is submitted once and lives in the DB.
- httpOnly cookies — do not stop the payload from calling the REST API in the admin's session; the browser attaches auth automatically.
- Rate limiting the login endpoint — irrelevant, attacker does not need to brute credentials.
- Perimeter IPS on inbound traffic — cannot see the rendered admin-side execution, and encoded payloads bypass most generic XSS signatures.
The supporting signals.
| In-the-wild status | No confirmed exploitation as of 2026-07-13. Not observed in Wordfence attack telemetry campaigns. |
|---|---|
| Public PoC | Typical Wordfence/Patchstack advisory patterns — technical detail withheld pending patch adoption; no public weaponized PoC on GitHub as of this writing. |
| EPSS | 0.00272 (~0.3%) — bottom half of the distribution, consistent with a stored XSS requiring authenticated context and UI. |
| KEV | Not listed. CISA has not added CVE-2026-5721. |
| CVSS vector | CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N — network, high complexity, no privs, user interaction required, scope changed, low C/I, no A. |
| Affected versions | wpDataTables — Data Tables, Dynamic Tables & Table Charts (Lite/Free plugin family), versions up to and including the last vulnerable build noted in the Wordfence/Patchstack advisory. |
| Fixed version | Upgrade to the vendor-patched release (see Wordfence Intelligence and the wpDataTables changelog for the exact fixed build). |
| Exposure data | wpDataTables is on ~70k+ WordPress sites per wpdatatables.com marketing figures; WPScan/Wordfence enumerate several tens of thousands of installs. Only a subset expose editable table forms to unauthenticated users. |
| Disclosed | 2026-04-20 |
| Reporter | Typically credited via Wordfence Bug Bounty or Patchstack Alliance researchers — see advisory for named credit. |
Sources.
Why this verdict
- Vendor severity matches reality:
AC:HandUI:Rare load-bearing — the attacker cannot fire this at scale against unauthenticated visitors and expect reliable execution against an admin. - Role multiplier — bounded blast radius: wpDataTables runs on WordPress content sites; even worst-case chained-to-admin-takeover impact is single-site compromise, not fleet, identity, or supply-chain scale. This does NOT invoke a HIGH floor.
- No KEV, low EPSS (0.00272), no observed campaigns — threat pressure is low.
- Downgrade pressure from
AC:H: most enterprise WordPress installs restrict wpDataTables editing to trusted roles, further shrinking the reachable population.
Why not higher?
Not HIGH because there is no unauthenticated primitive, impact is C:L/I:L not full compromise, user interaction from a privileged user is required, and wpDataTables is not a high-value-role component (not an IdP, hypervisor, PAM, backup, edge appliance, or kernel agent). No KEV listing and negligible EPSS reinforce that this is not near-term operationally urgent.
Why not lower?
Not LOW because stored XSS in a WordPress plugin used by an administrator can be chained to full site takeover (add-admin, install-plugin, drop-webshell) via nonce theft, and the plugin has a non-trivial install base. A defender who ignores it entirely accepts real single-site takeover risk on WordPress properties running the plugin.
Crowdsourced verification payload.
Run on the WordPress host as a user with read access to the WP install directory and DB credentials in wp-config.php. Requires wp-cli installed. Invoke as: ./check-cve-2026-5721.sh /var/www/html. Exits 0 for PATCHED, 1 for VULNERABLE, 2 for UNKNOWN.
#!/usr/bin/env bash
# check-cve-2026-5721.sh — detect vulnerable wpDataTables plugin
# Usage: ./check-cve-2026-5721.sh <wp-root>
set -u
WP_ROOT="${1:-/var/www/html}"
FIXED_VERSION="6.6.1" # replace with vendor-confirmed fixed build
if [ ! -d "$WP_ROOT" ]; then
echo "UNKNOWN: WordPress root $WP_ROOT not found"
exit 2
fi
if ! command -v wp >/dev/null 2>&1; then
echo "UNKNOWN: wp-cli not installed"
exit 2
fi
INSTALLED=$(wp --path="$WP_ROOT" --allow-root plugin get wpdatatables --field=version 2>/dev/null || true)
if [ -z "$INSTALLED" ]; then
echo "PATCHED: wpDataTables not installed"
exit 0
fi
# semver compare
lowest=$(printf '%s\n%s\n' "$INSTALLED" "$FIXED_VERSION" | sort -V | head -n1)
if [ "$lowest" = "$FIXED_VERSION" ] && [ "$INSTALLED" != "$FIXED_VERSION" ]; then
echo "PATCHED: wpDataTables $INSTALLED >= $FIXED_VERSION"
exit 0
fi
if [ "$INSTALLED" = "$FIXED_VERSION" ]; then
echo "PATCHED: wpDataTables $INSTALLED == fixed"
exit 0
fi
echo "VULNERABLE: wpDataTables $INSTALLED < $FIXED_VERSION (CVE-2026-5721)"
exit 1