This is a booby-trapped draft in the newsroom, not a drive-by shot through the lobby
CVE-2025-22315 is a stored XSS in the WPDeveloper Typing Text WordPress plugin affecting all versions through 1.2.7. In practical terms, a user with Contributor-level access or higher can place a malicious payload into plugin-controlled content so that JavaScript runs later when someone else loads the affected page. Public sources show the plugin's latest listed version is still 1.2.7 and Wordfence still marks the issue unpatched, so there is no clean vendor-fixed target version to anchor on today.
The vendor-style 6.5/MEDIUM label is too generous for enterprise prioritization because the attack begins with authenticated low-privilege CMS access and ends only if a victim actually views the poisoned content. That means this is post-initial-access, narrow-population, and workflow-dependent. The danger is real on sites that let many semi-trusted users publish or submit content, but for most enterprise fleets this is not remotely wormable, not internet-sprayable, and not a Monday-morning fire drill.
4 steps from start to impact.
Acquire a Contributor account with a browser or credential theft kit
- WordPress site is reachable
- Attacker has valid credentials
- Account has Contributor+ privileges
- This is not unauthenticated remote; it presumes prior compromise or insider access
- MFA/SSO and tighter role assignment cut off a large share of real deployments
- Many enterprise WordPress estates do not grant Contributor broadly
Inject payload into Typing Text content using Burp Suite or the Gutenberg editor
- Plugin
typing-textis installed - Version is <= 1.2.7
- Contributor can create or edit content that uses the vulnerable block/plugin path
- The vulnerable plugin is niche, with roughly 600+ active installs in WordPress.org metadata
- Some editorial workflows require review before publication
- If the plugin is installed but not actually used in content creation, the reachable attack surface shrinks further
Wait for a privileged user to view the poisoned page
- Injected content is saved and viewable
- A victim browses the affected page or preview
- Victim session has meaningful privileges
- No victim view means no exploit
- Moderation queues and limited reviewer pools slow or prevent triggering
- If only anonymous visitors see the page, impact is usually reputational/phishing rather than admin takeover
Use same-origin JavaScript to perform admin actions or steal action tokens
- Victim has elevated permissions
- Browser executes injected JavaScript
- Relevant admin actions are reachable from the victim context
- HttpOnly may block direct cookie theft, forcing the attacker into action-for-action abuse instead
- Strong CSP, restricted admin plugins, and hardened browser settings can reduce blast radius
- This compromises one site/tenant at a time, not the broader network
The supporting signals.
| In-the-wild status | No evidence of active exploitation found in the sources reviewed; not in CISA KEV. |
|---|---|
| PoC availability | No public PoC located in reviewed sources. Exploitation does not require special tooling beyond a browser or Burp Suite once a Contributor account exists. |
| EPSS | User-supplied EPSS is 0.00139. Third-party mirrors show it staying very low, which matches the narrow attack preconditions. |
| KEV status | Not KEV-listed as of the reviewed CISA sources; no federal urgency signal. |
| CVSS vector reality check | Vendor/CNA vector: CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:L. The decisive terms are PR:L and UI:R: attacker already needs access, then still needs a victim view. |
| NVD enrichment delta | NVD later enriched the record to 5.4 MEDIUM with A:N, which is directionally closer to reality than the original 6.5 because availability impact from this bug is weak. |
| Affected versions | All versions through 1.2.7 are listed as affected. |
| Fixed version | No authoritative patched version found. WordPress.org still shows 1.2.7 as the listed version in reviewed metadata, and Wordfence marks the issue Unpatched. |
| Exposure population | WordPress.org metadata shows about 600+ active installations. That's a small exposure pool, and there is no reliable Shodan/Censys fingerprint for this specific block plugin, so internet-scale discoverability is limited. |
| Disclosure and researcher | Publicly disclosed 2025-01-07; researcher attribution in Wordfence is SavPhill (Savphill). |
noisgate verdict.
The single biggest downgrade factor is that exploitation requires authenticated Contributor access, which means this starts after the attacker already has a foothold or a trusted user account. From there, the chain still depends on a privileged victim loading poisoned content, so the reachable population and trigger rate are both low in real enterprise operations.
Why this verdict
- Starts post-compromise:
PR:Lis not a minor checkbox here; it means the attacker already has a WordPress account with content-authoring rights. - Needs the right victim: stored XSS only matters when an editor/admin actually views the page, preview, or moderation item.
- Small exposure pool: the plugin shows roughly 600+ active installs, so even successful internet discovery does not translate into mass enterprise risk.
Why not higher?
This is not an unauthenticated edge-service bug, not a one-packet exploit, and not broadly automatable across enterprise fleets. The chain compounds multiple narrowing prerequisites: niche plugin presence, authenticated Contributor access, vulnerable content path usage, and a privileged victim view.
Why not lower?
It is still stored XSS in a CMS, and those bugs can become account compromise or admin-side action abuse once triggered. The fact that sources indicate no known patch also keeps it above IGNORE for teams that actually run this plugin.
What to do — in priority order.
- Remove the plugin where it is not business-critical — Because no authoritative patched version is visible in reviewed sources, removal is the cleanest control. For a LOW verdict there is no formal mitigation SLA; treat this as backlog hygiene and remove it in the next normal change cycle, prioritizing any internet-facing sites with many contributor accounts.
- Restrict Contributor and Author roles — This bug lives or dies on low-privileged content-authoring access. Reduce the number of accounts that can create or edit content, enforce MFA/SSO, and review dormant contributor accounts; for LOW, there is no formal mitigation SLA, so fold this into routine IAM cleanup.
- Require editorial review before publication — A moderation queue adds real friction by preventing attacker-controlled content from going live or reaching admins automatically. There is no formal mitigation SLA for LOW, but this is worthwhile on shared or contractor-managed WordPress estates during the next policy update window.
- Harden browser-side script execution for admin users — A tight CSP, isolated admin workstations, and browser hardening reduce what a stored XSS can do after trigger. There is no formal mitigation SLA for LOW; apply where your CMS administrators already use hardened profiles.
- Monitor WordPress admin actions after content edits — If the XSS lands, the visible follow-on is usually odd admin-side behavior: new users, plugin changes, or content modifications. Add WordPress audit logging and alerting as backlog hygiene, especially on sites with external contributors.
- A perimeter WAF alone is unreliable here because the payload can be stored through legitimate editor workflows and rendered later.
- Network segmentation does little once the attacker is already inside the CMS with valid credentials; this is an application-layer trust problem.
- Cookie HttpOnly alone is not enough; same-origin JavaScript can still drive privileged actions even if it cannot directly read every cookie.
Crowdsourced verification payload.
Run this on the target WordPress host or in a mounted webroot on an auditor workstation. Invoke it as bash check_typing_text_cve_2025_22315.sh /var/www/html or point it straight at the plugin directory; it needs read access only and no root privileges unless your webroot is restricted.
#!/usr/bin/env bash
# check_typing_text_cve_2025_22315.sh
# Detects exposure to CVE-2025-22315 in the WPDeveloper Typing Text WordPress plugin.
# Exit codes:
# 0 = PATCHED (plugin absent/removed)
# 1 = VULNERABLE (version <= 1.2.7)
# 2 = UNKNOWN (cannot determine, or version > 1.2.7 with no trusted patch baseline)
set -u
TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
echo "UNKNOWN - usage: $0 <wordpress-root-or-plugin-dir>"
exit 2
fi
PLUGIN_DIR=""
if [[ -d "$TARGET/wp-content/plugins/typing-text" ]]; then
PLUGIN_DIR="$TARGET/wp-content/plugins/typing-text"
elif [[ -d "$TARGET/typing-text" ]]; then
PLUGIN_DIR="$TARGET/typing-text"
elif [[ -d "$TARGET" && "$(basename "$TARGET")" == "typing-text" ]]; then
PLUGIN_DIR="$TARGET"
else
echo "PATCHED - plugin directory not found (typing-text not installed or already removed)"
exit 0
fi
read_version() {
local file
for file in \
"$PLUGIN_DIR/typing-text.php" \
"$PLUGIN_DIR/plugin.php" \
"$PLUGIN_DIR/readme.txt"; do
if [[ -f "$file" ]]; then
local v
v=$(grep -Eim1 '^(Version|Stable tag):' "$file" | sed -E 's/^[^:]+:[[:space:]]*//I' | tr -d '\r')
if [[ -n "$v" ]]; then
echo "$v"
return 0
fi
fi
done
return 1
}
normalize_version() {
echo "$1" | grep -Eo '[0-9]+(\.[0-9]+){1,3}' | head -n1
}
RAW_VERSION="$(read_version 2>/dev/null || true)"
VERSION="$(normalize_version "$RAW_VERSION")"
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN - plugin found at $PLUGIN_DIR but version could not be parsed"
exit 2
fi
AFFECTED_MAX="1.2.7"
if [[ "$VERSION" == "$AFFECTED_MAX" ]]; then
echo "VULNERABLE - Typing Text version $VERSION detected at $PLUGIN_DIR"
exit 1
fi
# sort -V compares semantic-ish dotted versions available on GNU coreutils.
LOWEST=$(printf '%s\n%s\n' "$VERSION" "$AFFECTED_MAX" | sort -V | head -n1)
if [[ "$LOWEST" == "$VERSION" ]]; then
echo "VULNERABLE - Typing Text version $VERSION detected at $PLUGIN_DIR (<= $AFFECTED_MAX)"
exit 1
fi
# Sources reviewed did not provide a trusted patched version. If a newer version exists locally,
# report UNKNOWN rather than assuming it is fixed.
echo "UNKNOWN - Typing Text version $VERSION detected at $PLUGIN_DIR; no authoritative patched version confirmed in reviewed sources"
exit 2
If you remember one thing.
wp-content/plugins/typing-text, identify which sites actually use the plugin, and check whether those sites also allow Contributor/Author access for contractors, marketers, or community users. Because this is a LOW reassessment, there is no noisgate mitigation SLA and no noisgate remediation SLA beyond backlog hygiene; if the plugin is unnecessary, remove it in the next routine change window, and if it is necessary, tighten low-privilege publishing roles and review workflows while you watch for any vendor patch or repository update.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.