This is a bad note slipped into the newsroom CMS, not a crowbar through the front door
CVE-2025-22261 is a stored XSS in the WordPress WP FullCalendar plugin affecting versions through 1.5 and fixed in 1.6. The practical abuse case described by WordPress ecosystem sources is that an attacker with Contributor+ access can place JavaScript into content rendered by the plugin, and that script runs later in the browser of whoever views the affected page in the site's origin.
The vendor's MEDIUM 6.5 score is technically defensible in a vacuum, but it overstates enterprise patch urgency. The decisive friction is that exploitation is already post-auth: the attacker needs a valid low-privilege WordPress account and then still needs a victim to render the poisoned page. That makes this a workflow abuse problem inside a niche plugin population, not a broad unauthenticated internet takeover.
4 steps from start to impact.
Identify the plugin and version
wp-fullcalendar and whether it is on 1.5 or earlier. Typical tooling is WPScan or simple manual fingerprinting against plugin paths and metadata pages; this is commodity recon, not exploitation.- Target site is internet-reachable
- Plugin fingerprinting is possible from public content or predictable plugin paths
- Site actually runs WP FullCalendar <= 1.5
- Many WordPress sites do not run this plugin at all
- Plugin enumeration is often incomplete behind caching/CDNs or hardened WordPress configs
- WordPress.org shows only about 8,000+ active installs and WPScan lists roughly 10,000
Get a Contributor+ foothold
- Authenticated WordPress access
- Role of Contributor or higher
- Ability to create or modify content that the plugin renders
- This prerequisite implies prior compromise or authorized access, which is the biggest downward pressure on severity
- Many enterprise WordPress sites have tightly limited author/contributor populations
- MFA and SSO materially raise the cost of reaching this step
Store the payload in rendered calendar content
- Writable content path reaches the vulnerable rendering sink
- Server-side sanitization does not strip the payload
- The site uses the affected rendering path in production
- Exact sink may be narrow to shortcode/plugin-specific rendering rather than all editor content
- Modern WordPress deployments often add WAF, editor restrictions, or HTML filtering
- Payloads frequently break on theme/plugins/CDN minification or CSP
Wait for an admin or visitor to render it
- A victim visits the affected page
- Victim browser allows script execution in that context
- Desired follow-on action is achievable with victim privileges
- This is UI:R in the real world, not just on paper
- Impact is typically limited to the single WordPress site/tenant where the payload runs
- High-value outcomes depend on an admin/editor actually viewing the poisoned content
The supporting signals.
| In-the-wild status | No active exploitation evidence surfaced in the reviewed sources. OpenCVE exposes CISA ADP SSVC fields showing Exploitation: none and Automatable: no. |
|---|---|
| Public exploit / PoC | No public exploit signal worth prioritizing. Wiz marks Has Public Exploit: No; no mainstream PoC repo surfaced during source review. |
| EPSS | Low exploit probability. User-supplied EPSS is 0.00137; third-party snapshots vary by date/model (e.g. Wiz shows about 0.1% / 26th percentile, Tenable shows 0.00046), but all point to the same conclusion: near-floor exploitation likelihood. |
| KEV status | Not KEV-listed. The vulnerability does not appear in CISA's Known Exploited Vulnerabilities catalog, and Wiz also flags Has CISA KEV Exploit: No. |
| CVSS meaning | CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L means network reachable, low complexity, but requires low privileges and a victim page view. Those two fields are the real-world brakes. |
| Affected versions | WP FullCalendar <= 1.5 according to the CNA/OpenCVE record and Wordfence. |
| Fixed version | 1.6. WordPress.org's changelog for version 1.6 explicitly notes a fix for a reported security issue allowing potential XSS by authenticated users in shortcode handling. |
| Exposure / installed base | This is not directly enumerable like an exposed appliance. WordPress.org shows 8,000+ active installs and WPScan shows about 10,000 active installs, which is small enough that internet-wide blast radius is limited compared with mainstream CMS or edge software flaws. |
| Disclosure and researcher | Published 2025-01-07 in the CNA record; finder credited as LVT-tholv2k (Patchstack Alliance). |
| Scanner / detection coverage | Ecosystem coverage exists in WPScan, Wordfence, Rapid7, Tenable, and Wiz, but most detections are inventory/version based. They tell you *where to look*, not whether a site is actually weaponized. |
noisgate verdict.
The single most important reason this lands in LOW is the Contributor+ authentication requirement: the attacker is not breaking in from the internet; they are abusing a content-creation foothold they already have. That sharply narrows both exposure and urgency, especially with no KEV listing, no public exploit signal, and a small plugin population.
Why this verdict
- Post-auth prerequisite cuts hard against urgency: starting from the vendor's 6.5 baseline, this flaw needs Contributor+ access, which implies prior compromise, credential theft, or an intentionally open publishing workflow before the XSS even starts.
- A victim must still render the poisoned page: this is stored XSS with UI:R, so the attacker does not get instant server-side impact or unauthenticated one-shot exploitation.
- Reachable population is small: only sites running the niche WP FullCalendar plugin on <= 1.5 are in scope, and public install counts are in the 8k-10k range, not hundreds of thousands or millions.
- Threat signal is weak: there is no KEV listing, no strong public PoC signal, and EPSS is near the floor, so there is no evidence the market is weaponizing this at scale.
Why not higher?
This is not higher because there is no unauthenticated path, no server-side code execution, and no broad internet edge exposure. The chain assumes a prior low-privilege foothold plus a successful content-render event, which is exactly the kind of compounding friction that should drag severity down in enterprise patch triage.
Why not lower?
It is not IGNORE because stored XSS in a CMS can still cross privilege boundaries when an editor or admin views the payload. On the affected site, the bug can become a browser-side stepping stone to session abuse, nonce theft, or malicious content changes, so it deserves inventorying and cleanup rather than dismissal.
What to do — in priority order.
- Restrict Contributor publishing paths — Limit who can create or edit content that reaches WP FullCalendar rendering, especially shortcode-driven pages. For a LOW verdict there is no SLA (treat as backlog hygiene), but this is the fastest way to remove the exploitable path without waiting on a plugin update.
- Enforce MFA for WordPress authors and contributors — This bug is only useful after the attacker gets a valid low-privilege account, so shrinking credential abuse matters more than perimeter filtering. For LOW, there is no SLA (treat as backlog hygiene); roll into your normal identity hardening program.
- Disable or remove the plugin where unused — If the calendar feature is not business-critical, removing
wp-fullcalendarerases both the vulnerable code path and future plugin maintenance burden. For LOW, there is no SLA (treat as backlog hygiene); prune it during your next WordPress maintenance pass. - Review recent contributor-created content — Hunt for suspicious script fragments, odd shortcode attributes, unexpected HTML, and anomalous post revisions tied to low-privilege users. For LOW, there is no SLA (treat as backlog hygiene), but this is the best validation step if you suspect account abuse.
- A perimeter firewall does not solve this; the attacker uses normal authenticated CMS workflows after logging in legitimately.
- Network segmentation is mostly irrelevant for an internet-facing WordPress admin plane; the deciding control is identity and content governance, not east-west isolation.
- Server antivirus will not reliably catch JavaScript stored in WordPress content or database fields rendered back to browsers.
Crowdsourced verification payload.
Run this on the target WordPress host or a mounted backup of the site content. Invoke it as bash check-wp-fullcalendar-cve-2025-22261.sh /var/www/html and provide a WordPress root path; read-only access is enough, no root required unless filesystem permissions demand it.
#!/usr/bin/env bash
# check-wp-fullcalendar-cve-2025-22261.sh
# Purpose: Determine whether a local WordPress installation is vulnerable to CVE-2025-22261
# Affected: WP FullCalendar <= 1.5
# Fixed: WP FullCalendar >= 1.6
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
WP_ROOT="${1:-}"
if [[ -z "$WP_ROOT" ]]; then
echo "UNKNOWN - usage: $0 /path/to/wordpress"
exit 2
fi
PLUGIN_DIR="$WP_ROOT/wp-content/plugins/wp-fullcalendar"
README_FILE="$PLUGIN_DIR/readme.txt"
MAIN_FILE="$PLUGIN_DIR/wp-fullcalendar.php"
if [[ ! -d "$PLUGIN_DIR" ]]; then
echo "UNKNOWN - wp-fullcalendar plugin directory not found at: $PLUGIN_DIR"
exit 2
fi
version=""
# Try plugin readme first (Stable tag)
if [[ -f "$README_FILE" ]]; then
version=$(awk -F': *' 'BEGIN{IGNORECASE=1} /^Stable tag:/ {print $2; exit}' "$README_FILE" | tr -d '\r')
fi
# Fall back to plugin header version
if [[ -z "$version" && -f "$MAIN_FILE" ]]; then
version=$(awk -F': *' 'BEGIN{IGNORECASE=1} /^Version:/ {print $2; exit}' "$MAIN_FILE" | tr -d '\r')
fi
if [[ -z "$version" ]]; then
echo "UNKNOWN - could not determine wp-fullcalendar version from readme or plugin header"
exit 2
fi
normalize_version() {
echo "$1" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//'
}
verlte() {
# returns success if $1 <= $2
[[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" == "$1" ]]
}
vergte() {
# returns success if $1 >= $2
[[ "$(printf '%s\n%s\n' "$1" "$2" | sort -V | tail -n1)" == "$1" ]]
}
version=$(normalize_version "$version")
affected_max="1.5"
fixed_min="1.6"
if verlte "$version" "$affected_max"; then
echo "VULNERABLE - wp-fullcalendar version $version (affected <= $affected_max)"
exit 1
fi
if vergte "$version" "$fixed_min"; then
echo "PATCHED - wp-fullcalendar version $version (fixed >= $fixed_min)"
exit 0
fi
echo "UNKNOWN - parsed version '$version' but could not classify confidently"
exit 2
If you remember one thing.
wp-fullcalendar and confirm whether any untrusted Contributor+ users exist on them. Because this is LOW, there is no noisgate mitigation SLA and no noisgate remediation SLA beyond backlog hygiene; patch to 1.6+ in your normal WordPress/plugin maintenance cycle, and if a site has broad contributor access or signs of account abuse, temporarily restrict content creation on that site while you update.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.