This is less a break-in and more a forged sticky note that only works if the site owner reads it while already at the desk
CVE-2025-22301 is a CSRF flaw in the WordPress MyBookTable Bookstore plugin affecting versions through 3.5.3 and fixed in 3.5.4. Wordfence attributes it to missing or incorrect nonce validation in mbt_add_admin_notices(), which lets an unauthenticated attacker trick a logged-in administrator into making an unwanted state-changing request that can add the plugin's pages.
The vendor's MEDIUM 5.4 score is technically defensible in a vacuum, but it overstates operational urgency for enterprise patch queues. This is not unauthenticated RCE, not auth bypass, not data theft, and not broadly wormable; it needs the plugin to be installed, a privileged WordPress admin session, and a successful lure. That combination sharply narrows both reachable population and reliable exploitation, so this lands as LOW in the real world.
4 steps from start to impact.
Identify a site running mybooktable
curl, WP enumeration, or a tool like WPScan against public assets and plugin paths. The exploit chain dies immediately if the plugin is absent or already updated past 3.5.3.- Target runs WordPress
- MyBookTable Bookstore plugin is installed
- Installed version is <= 3.5.3
- WordPress.org shows only a small install base for this plugin
- Plugin presence/version is not always externally fingerprintable
- A large enterprise fleet usually has very few author-marketing plugins at all
Build a forged request
mbt_add_admin_notices(), the payload targets a state-changing admin action without needing valid anti-CSRF material.- Attacker can determine the vulnerable endpoint/action
- Request shape matches what the plugin expects
- Impact is limited to the specific state change exposed by this function
- This is not a generalized admin takeover primitive
- No strong public exploit corpus was found, so actors still need to assemble their own trivial PoC
Social-engineer a logged-in administrator
/wp-admin/.- Victim is a WordPress administrator or similarly privileged user
- Victim is logged in at the time of click/visit
- Victim performs the required interaction
- This is user-interaction dependent
- It requires admin context, which implies a much smaller target set than ordinary site visitors
- Email security, browser isolation, secure web gateways, and basic user skepticism all cut success rates
Land the unwanted page-creation action
- The admin request is accepted by the vulnerable plugin logic
- WordPress permissions allow the resulting action
- Blast radius is narrow and feature-specific
- The outcome is visible and often reversible
- By itself this is poor material for mass exploitation campaigns
/wp-admin/ exposure — Put WordPress admin behind VPN, reverse-proxy ACLs, IP allowlists, or identity-aware access where feasible. This does not fix CSRF directly, but it reduces the number of admins who can be targeted from random internet browsing contexts. For a LOW verdict there is no SLA; align this with standard admin-surface reduction work.mybooktable and move any remaining installs to 3.5.4+. For a LOW verdict there is no SLA; bundle it into the next scheduled WordPress/plugin maintenance window.- MFA alone doesn't stop CSRF once the admin is already logged in; the forged request rides the existing session.
- Network IDS signatures are weak here because the malicious request can look like ordinary authenticated admin traffic.
- Generic WAF rules often miss feature-specific CSRF unless they understand the exact plugin endpoint and can enforce nonce or origin expectations.
The supporting signals.
| In-the-wild status | No public evidence of active exploitation found in the reviewed sources, and CISA KEV does not list CVE-2025-22301. |
|---|---|
| PoC availability | No notable public GitHub/Exploit-DB PoC surfaced in review; however, this is still easy to hand-craft as a basic HTML/link CSRF once the endpoint is known. |
| EPSS | 0.0009 from the supplied intel, which is very low. Percentile was not supplied in the prompt. |
| KEV status | Not listed in the CISA Known Exploited Vulnerabilities Catalog. |
| CVSS vector meaning | AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:L means network-reachable but click-dependent, with no confidentiality impact and only low integrity/availability impact. |
| Affected versions | MyBookTable Bookstore <= 3.5.3 according to NVD, Patchstack, and Wordfence. |
| Fixed version | 3.5.4 is the first fixed release. WordPress.org currently shows the plugin at 3.6.0, so supported current installs already have a patched line available. |
| Root cause / vulnerable function | Wordfence says the flaw is due to missing or incorrect nonce validation in mbt_add_admin_notices(). |
| Exposure population | This is a small-footprint plugin. WordPress.org shows 1,000+ active installations and WPScan reports 2,000. Also, internet-scale scanners cannot reliably fingerprint this plugin/version from the edge, so perimeter counts understate or miss true install base. |
| Disclosure / credit | Published 2025-01-07 in the CVE/NVD flow; Patchstack shows disclosure on 2025-01-06 and credits Dhabaleshwar Das. |
Sources.
Why this verdict
- Admin session required: despite
PR:Nin CVSS, the exploit only lands if a privileged authenticated WordPress user performs the request, which is strong real-world friction. - User interaction is mandatory: the chain needs a successful lure. That is a separate stage modern controls like SEG, browser isolation, and user awareness can disrupt.
- Blast radius is narrow: public descriptions point to adding plugin pages, not code execution, credential theft, database readout, or full site takeover.
- Small reachable population: this is a niche WordPress plugin with a modest install base, not a ubiquitous enterprise platform.
- No exploitation pressure: no KEV listing, no public active-campaign reporting, and an EPSS of 0.0009 all push urgency down.
Why not higher?
This is not an unauthenticated one-shot compromise. The attacker must first find a site with this plugin, then reach an authenticated administrator, then win user interaction, and even then the public record only supports limited page-creation impact. That is too much chain friction for MEDIUM/HIGH operational urgency in an enterprise patch queue.
Why not lower?
It still performs an unauthorized state-changing action in administrator context, so dismissing it entirely would be sloppy. In environments that host public author sites or where admins browse mail and the web from the same browser session, CSRF can still produce real changes and deserves cleanup during routine maintenance.
Crowdsourced verification payload.
Run this on the target WordPress host or inside the container/VM that serves the site. Invoke it as bash check-cve-2025-22301.sh /var/www/html where the argument is the WordPress root; it needs only read access to the plugin files, though wp-cli detection works best if the web user can read the install.
#!/usr/bin/env bash
# check-cve-2025-22301.sh
# Determine whether WordPress plugin mybooktable is vulnerable to CVE-2025-22301
# Affected: <= 3.5.3
# Fixed: >= 3.5.4
# Output: VULNERABLE / PATCHED / UNKNOWN
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
TARGET="${1:-}"
PLUGIN_SLUG="mybooktable"
FIXED_VERSION="3.5.4"
if [[ -z "$TARGET" ]]; then
echo "UNKNOWN - usage: $0 /path/to/wordpress/root"
exit 2
fi
if [[ ! -d "$TARGET" ]]; then
echo "UNKNOWN - target path does not exist: $TARGET"
exit 2
fi
PLUGIN_DIR="$TARGET/wp-content/plugins/$PLUGIN_SLUG"
MAIN_FILE="$PLUGIN_DIR/mybooktable.php"
README_FILE="$PLUGIN_DIR/readme.txt"
VERSION=""
verlte() {
# returns 0 if $1 <= $2
[[ "$1" == "$2" ]] && return 0
local first
first=$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)
[[ "$first" == "$1" ]]
}
# Prefer WP-CLI when available
if command -v wp >/dev/null 2>&1; then
if wp --path="$TARGET" plugin is-installed "$PLUGIN_SLUG" >/dev/null 2>&1; then
VERSION=$(wp --path="$TARGET" plugin get "$PLUGIN_SLUG" --field=version 2>/dev/null || true)
fi
fi
# Fallback: parse main plugin header
if [[ -z "$VERSION" && -f "$MAIN_FILE" ]]; then
VERSION=$(grep -iE '^\s*Version:\s*' "$MAIN_FILE" | head -n1 | sed -E 's/^\s*Version:\s*//I' | tr -d '\r' | xargs)
fi
# Fallback: parse readme stable tag
if [[ -z "$VERSION" && -f "$README_FILE" ]]; then
VERSION=$(grep -iE '^\s*Stable tag:\s*' "$README_FILE" | head -n1 | sed -E 's/^\s*Stable tag:\s*//I' | tr -d '\r' | xargs)
fi
if [[ ! -d "$PLUGIN_DIR" ]]; then
echo "UNKNOWN - plugin '$PLUGIN_SLUG' not installed under $TARGET"
exit 2
fi
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN - plugin installed but version could not be determined"
exit 2
fi
if verlte "$VERSION" "3.5.3"; then
echo "VULNERABLE - mybooktable version $VERSION is <= 3.5.3"
exit 1
fi
if verlte "$FIXED_VERSION" "$VERSION"; then
echo "PATCHED - mybooktable version $VERSION is >= 3.5.4"
exit 0
fi
# Defensive catch-all for odd version formats
echo "UNKNOWN - unhandled version string: $VERSION"
exit 2