A REST API traffic cop who reads the destination sign but never checks the passenger's ticket
CVE-2026-63030 is a route-confusion flaw in the WordPress REST API /wp/v2/batch/v1 endpoint. The batch handler resolves sub-request routes correctly but re-uses the *outer* request's permission context when dispatching them, so authorization checks that should fire on each nested route silently pass. The result is unauthenticated read access to REST endpoints that were supposed to require edit_posts, manage_options, or higher — including draft/private content, user metadata, and internal option leakage. Affected versions are WordPress 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1; fixed in 6.9.5, 7.0.2, and backported to 6.8.6 and 7.1 Beta 2.
The vendor's HIGH (7.5, C:H only) rating is technically defensible for the isolated bug — no integrity or availability impact in a vacuum. But it understates real-world blast radius. WordPress's own advisory ties this route-confusion to CVE-2026-60137 (the batch-endpoint SQLi dubbed *wp2shell*), and when persistent object cache is absent the pair yields unauthenticated RCE. Given WordPress's ~43% share of all websites and the fact that Automattic is force-pushing auto-updates, the operational severity is CRITICAL, not HIGH.
5 steps from start to impact.
Reach /wp-json/wp/v2/batch/v1
POST to the batch endpoint over HTTPS. No credentials, no nonce, no user interaction. Tooling: curl, Burp Repeater, or the public wp2shell PoC (referenced in TheHackerNews coverage).- Site reachable on 80/443
- REST API not blocked at WAF or
.htaccess - Site running WP 6.9.0–6.9.4 or 7.0.0–7.0.1
- Some managed hosts (WP Engine, Kinsta) proxy-block
/batch/v1for non-admin traffic - Cloudflare Managed Ruleset shipped emergency signatures on 2026-07-17
POST /wp-json/wp/v2/batch/v1 from non-admin IPsCraft nested sub-requests to privileged routes
requests[] targeting endpoints like /wp/v2/users/1?context=edit or /wp/v2/settings. Because permission callbacks are evaluated against the outer batch context (unauthenticated) but the inner dispatcher trusts them as pre-authorized, protected fields are returned. Public PoC: wp2shell demonstrates the pivot.- Vulnerable core version
- Endpoint responds with 200 rather than 401/403
- Some plugin-registered routes wrap
permission_callbackwith their own re-checks and are unaffected
user_email, roles, or siteurl from an unauthenticated caller are trivially alertable in a SIEMChain with CVE-2026-60137 (SQLi in the same batch handler)
search parameter that concatenates user input into a wp_posts query. Attacker exfiltrates wp_users.user_pass bcrypt hashes and secret keys stored in wp_options.- Site has NOT deployed a persistent object cache (Redis/Memcached) — the flawed code path is bypassed when object caching is active
- MySQL user has typical WP privileges
- WP.com, WP Engine, Pantheon all run Redis/Memcached by default — meaningfully reduces the chain on managed hosts
- Self-hosted / shared-hosting installs almost never run object cache
POST bodies with SQL metacharacters; Wordfence Premium signature WORDFENCE-BATCH-SQLI-2026 covers thisEscalate to RCE via theme/plugin editor or wp-cron
wp-cron event that eval()s remote code. Web shells like WSO and b374k are the historical follow-on.- File-write to
wp-content/from the PHP-FPM user DISALLOW_FILE_EDITNOT set inwp-config.php
DISALLOW_FILE_EDIT=trueblocks the editor path- Read-only container filesystems (increasingly common) block plugin upload
.php files under wp-content/uploads/ or wp-content/mu-plugins/ — trivial FIM catchFleet-scale mass exploitation
- Public-facing WP site not yet patched
- WordPress core auto-update is FORCED for this CVE class — sites with
WP_AUTO_UPDATE_CORE=true(default) will patch themselves
WordPress Batch Endpoint Scanner and Shodan queries for vulnerable versionsThe supporting signals.
| In-the-wild status | Active exploitation confirmed as of 2026-07-17 per Cloudflare blog; mass-scan traffic tagged by GreyNoise within 12h of disclosure |
|---|---|
| Public PoC | wp2shell (referenced in TheHackerNews); Cloudflare published detection logic; expect Metasploit module within days |
| KEV status | Not yet listed as of 2026-07-18 — expect listing within 72h given active exploitation |
| EPSS | Too new for stable EPSS; comparable unauth WP core bugs historically score >0.90 within a week |
| CVSS vector | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N — network, no auth, no UI. Vendor scored C:H only; chain with CVE-2026-60137 pushes I and A to H |
| Affected versions | WordPress 6.9.0 – 6.9.4 and 7.0.0 – 7.0.1 |
| Fixed versions | 6.9.5, 7.0.2, 6.8.6 backport, 7.1 Beta 2; distros (Debian, Ubuntu) shipping backports |
| Exposure | ~43% of all websites run WordPress (W3Techs); BuiltWith counts ~810M live WP installs |
| Disclosure | 2026-07-17, coordinated with Automattic; auto-update push initiated same day |
| Chain partner | CVE-2026-60137 (SQLi in same batch endpoint) — together yield unauth RCE when persistent object cache is absent |
noisgate verdict.
The single most decisive factor is role multiplier + active exploitation: WordPress is the public-facing web tier for ~43% of the internet, the flaw is unauthenticated and network-reachable, and it chains with CVE-2026-60137 for RCE. Vendor's C:H-only score describes the isolated primitive; the operational reality is fleet-scale unauth RCE against a canonically high-value role (public web + often the CMS backing e-commerce PII).
Why this verdict
- Unauthenticated + network + AC:L — no friction on initial access; a single
POSTreaches the primitive. - Role multiplier: WordPress is canonically the public web edge and frequently the WooCommerce data plane. High-value-role share is effectively 100% of the affected base — every install IS the role. Blast radius on successful chain: site defacement, credential theft, checkout skimmer injection, pivot to hosting account.
- Chain partner is disclosed and PoC'd. CVE-2026-60137 (SQLi) uses the same permission bypass. wp2shell PoC weaponizes both.
- Active exploitation the day of disclosure (Cloudflare blog, TheHackerNews). Auto-update is being force-pushed BY THE VENDOR — that's a HIGH-severity-class signal from Automattic themselves.
- Friction credit: managed hosts with Redis/Memcached blunt the RCE chain to information-disclosure only. That's still C:H unauth on private posts / user emails / secret keys — not a downgrade.
Why not higher?
Already at CRITICAL 9.3. Score is not 10.0 because managed hosts running persistent object cache (WP.com, WP Engine, Pantheon — a meaningful minority of installs) break the RCE chain, leaving only the confidentiality primitive. Also, WordPress auto-update mechanism will remediate a large fraction of the install base automatically within days.
Why not lower?
Downgrading to HIGH would require the vendor's C:H-only framing to hold in the wild, but it does not: the chain partner (CVE-2026-60137) is public, the PoC (wp2shell) is public, and active exploitation is confirmed. On a ~43%-of-web install base, HIGH understates the operational threat. The Deployment-Role Blast Radius floor is CRITICAL because 100% of installs occupy the canonical public-web role.
What to do — in priority order.
- Force WordPress core auto-update NOW — Set
WP_AUTO_UPDATE_CORE=trueinwp-config.phpon every host; runwp core updatevia WP-CLI for fleets. Deploy within 3 days per noisgate mitigation SLA — but given active exploitation, treat as hours, not days. - Block
/wp-json/wp/v2/batch/v1at the WAF/edge — Cloudflare, Akamai, and AWS WAF shipped managed rules on 2026-07-17. If you can't patch immediately, dropPOSTto that path from non-authenticated sessions. Deploy within hours. - Deploy a persistent object cache — Install Redis or Memcached with a WP object-cache drop-in. This breaks the RCE chain (CVE-2026-60137 requires the non-cached code path). Doesn't fix the info-disclosure but removes the worst outcome. Deploy within 3 days.
- Set
DISALLOW_FILE_EDIT=trueinwp-config.php— Kills the theme/plugin editor RCE path even if an attacker recovers admin session. Zero-cost, deploy fleet-wide within 24h. - Rotate WordPress secret keys and force password reset for all admins — Assume
wp_optionsandwp_usersdisclosure occurred if the site was exposed and unpatched between 2025-12-02 (6.9.0 release) and your patch time. Regenerate keys viahttps://api.wordpress.org/secret-key/1.1/salt/within 7 days.
- Requiring login for
/wp-admin/— the vuln is in the public REST API, not/wp-admin/. Login walls don't touch it. - IP allow-listing xmlrpc.php — wrong endpoint entirely.
- MFA on admin accounts — the primitive is unauthenticated; no admin session is involved.
- Web ACL rate-limiting — a single request is enough to exfiltrate data; rate limits don't stop exploitation, only mass scanning.
Crowdsourced verification payload.
Run on the WordPress host as any user with read access to wp-includes/version.php, or against a remote target as an unauthenticated auditor. Example: ./check-cve-2026-63030.sh /var/www/html for local check, or ./check-cve-2026-63030.sh https://site.example.com for remote probe. No privileges needed for remote mode; read-only file access for local.
#!/usr/bin/env bash
# noisgate verifier — CVE-2026-63030 (WordPress REST batch route confusion)
# Exit codes: 0=PATCHED, 1=VULNERABLE, 2=UNKNOWN
set -u
TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
echo "UNKNOWN: usage: $0 <wp-root-path | https://site>" >&2
exit 2
fi
vercmp() {
# returns 0 if $1 >= $2
[[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" == "$2" ]]
}
check_version() {
local v="$1"
# Fixed lines: 6.8.6+, 6.9.5+, 7.0.2+, 7.1+
local major_minor="${v%.*}"
case "$major_minor" in
6.8) vercmp "$v" "6.8.6" && return 0 || return 1 ;;
6.9) vercmp "$v" "6.9.5" && return 0 || return 1 ;;
7.0) vercmp "$v" "7.0.2" && return 0 || return 1 ;;
*) vercmp "$v" "7.1" && return 0 || return 1 ;;
esac
}
if [[ "$TARGET" =~ ^https?:// ]]; then
# Remote probe
BODY=$(curl -sk --max-time 10 "${TARGET%/}/?rest_route=/" || true)
VER=$(curl -sk --max-time 10 "${TARGET%/}/wp-login.php" | grep -oiE 'ver=[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1 | cut -d= -f2)
if [[ -z "$VER" ]]; then
echo "UNKNOWN: could not fingerprint WordPress version at $TARGET"
exit 2
fi
else
# Local file check
VF="$TARGET/wp-includes/version.php"
if [[ ! -r "$VF" ]]; then
echo "UNKNOWN: $VF not readable"
exit 2
fi
VER=$(grep -E "^\\\$wp_version" "$VF" | sed -E "s/.*=[[:space:]]*'([^']+)'.*/\\1/")
fi
echo "Detected WordPress version: $VER"
if check_version "$VER"; then
echo "PATCHED"
exit 0
else
echo "VULNERABLE — upgrade to 6.8.6, 6.9.5, 7.0.2, or 7.1+"
exit 1
fi
If you remember one thing.
wp core update fleet-wide, verify version ≥6.9.5 / 7.0.2 / 6.8.6, and deploy Cloudflare/Akamai/AWS WAF rules blocking unauthenticated POST /wp-json/wp/v2/batch/v1. Per the noisgate remediation SLA for CRITICAL (≤90 days), complete the audit: confirm 100% of your WordPress fleet is on a fixed version, rotate wp-config.php secret keys, force admin password reset on any site that was internet-exposed and unpatched since 2025-12-02, and set DISALLOW_FILE_EDIT=true as permanent hardening. Any WP site still on 6.9.0–6.9.4 or 7.0.0–7.0.1 Wednesday morning should be treated as presumed-compromised and IR-triaged.Sources
- Cloudflare blog — WAF protects WordPress from two high-severity vulnerabilities
- TheHackerNews — wp2shell WordPress core flaw
- WordPress CVEs — cvedetails
- Wordfence Intelligence — WP vulnerability report
- OpenCVE — WordPress vulnerabilities
- Feedly CVE feed — WordPress
- W3Techs — CMS usage statistics
- WordPress developer — REST API batch endpoint
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.