← Back to Feed CACHED · 2026-05-17 09:42:19 · cache_key CVE-2025-29912
CVE-2025-22261 · CWE-79 · Disclosed 2025-01-07

Improper Neutralization of Input During Web Page Generation

ASSESSED — NOISGATE V0.5
Vendor
Reassessed
Verdict:
01 · The Real Story

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.

"Real risk is limited by contributor-level auth and required page view; this is backlog-worthy, not a fire drill."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify the plugin and version

The attacker first confirms the site runs 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.
Conditions required:
  • Target site is internet-reachable
  • Plugin fingerprinting is possible from public content or predictable plugin paths
  • Site actually runs WP FullCalendar <= 1.5
Where this breaks in practice:
  • 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
Detection/coverage: External scanners can usually infer plugin presence/version only when enumeration is exposed; expect mostly version-based findings from WPScan/Wordfence/Tenable/Rapid7 rather than deep exploit validation.
STEP 02

Get a Contributor+ foothold

To weaponize the bug, the attacker needs a valid WordPress account with at least Contributor rights. In practice this means stolen credentials, weak password reuse, a separate auth bug, or a permissive publishing workflow that already grants untrusted users content creation rights.
Conditions required:
  • Authenticated WordPress access
  • Role of Contributor or higher
  • Ability to create or modify content that the plugin renders
Where this breaks in practice:
  • 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
Detection/coverage: Covered better by IAM telemetry than vuln scanning: look for new contributor logins, suspicious author activity, impossible travel, and unusual post creation from low-privilege users.
STEP 03

Store the payload in rendered calendar content

Using normal content submission paths and tools like Burp Suite for parameter shaping, the attacker injects JavaScript into content later rendered by the plugin. WordPress plugin ecosystem sources describe the bug as insufficient sanitization/output escaping leading to stored XSS tied to plugin rendering and shortcode use.
Conditions required:
  • 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
Where this breaks in practice:
  • 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
Detection/coverage: Weak to moderate. Generic WAF/XSS rules may catch noisy payloads, but stored XSS in authenticated content often slips through unless you inspect post revisions, shortcode parameters, or database diffs.
STEP 04

Wait for an admin or visitor to render it

The stored payload does nothing until someone loads the poisoned page. Once viewed, the script executes in the site's origin and can steal nonces, force actions, or pivot into browser-side admin abuse with tooling ranging from vanilla JavaScript to BeEF-style post-exploitation techniques.
Conditions required:
  • A victim visits the affected page
  • Victim browser allows script execution in that context
  • Desired follow-on action is achievable with victim privileges
Where this breaks in practice:
  • 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
Detection/coverage: Browser-side detection is poor; server-side clues include anomalous admin actions following low-privilege content changes, unexpected nonce use, or suspicious POST chains from legitimate admin sessions.
03 · Intelligence Metadata

The supporting signals.

In-the-wild statusNo active exploitation evidence surfaced in the reviewed sources. OpenCVE exposes CISA ADP SSVC fields showing Exploitation: none and Automatable: no.
Public exploit / PoCNo public exploit signal worth prioritizing. Wiz marks Has Public Exploit: No; no mainstream PoC repo surfaced during source review.
EPSSLow 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 statusNot KEV-listed. The vulnerability does not appear in CISA's Known Exploited Vulnerabilities catalog, and Wiz also flags Has CISA KEV Exploit: No.
CVSS meaningCVSS: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 versionsWP FullCalendar <= 1.5 according to the CNA/OpenCVE record and Wordfence.
Fixed version1.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 baseThis 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 researcherPublished 2025-01-07 in the CNA record; finder credited as LVT-tholv2k (Patchstack Alliance).
Scanner / detection coverageEcosystem 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.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to LOW (3.8/10)

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.

HIGH Affected/fixed version range
HIGH Attacker position requirement is post-auth Contributor+
MEDIUM Current exploitation prevalence and PoC availability

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.

05 · Compensating Control

What to do — in priority order.

  1. 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.
  2. 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.
  3. Disable or remove the plugin where unused — If the calendar feature is not business-critical, removing wp-fullcalendar erases 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.
  4. 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.
What doesn't work
  • 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.
06 · Verification

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.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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
07 · Bottom Line

If you remember one thing.

TL;DR
Monday morning: find the sites that actually run 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

  1. WordPress.org plugin page and changelog
  2. OpenCVE CNA record for CVE-2025-22261
  3. Wordfence vulnerability entry
  4. WPScan plugin page
  5. Rapid7 vulnerability database entry
  6. Wiz vulnerability database entry
  7. CISA Known Exploited Vulnerabilities catalog
  8. FIRST EPSS API documentation
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.