← Back to Feed CACHED · 2026-10-26 05:46:00 · CACHE_KEY CVE-2026-63030
CVE-2026-63030 · CWE-436 · Disclosed 2026-07-17

WordPress Core REST API batch endpoint route confusion enabling unauthenticated RCE chain

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Someone left every front door in the neighborhood unlocked and published the master key on Twitter

CVE-2026-63030 is the route-confusion half of the wp2shell chain — a pre-authentication remote code execution path in WordPress Core. The REST API /wp-json/batch/v1 endpoint processes validation and execution in separate loops; when wp_parse_url() fails on a crafted sub-request path, the validation array and the matches array fall out of sync, causing every subsequent sub-request to dispatch under the wrong handler. This desync lets an unauthenticated attacker bypass route-level permission checks and funnel a malicious author__not_in parameter directly into WP_Query, where CVE-2026-60137's unsanitized SQL injection completes the chain. The result is UNION-based SQLi that extracts admin password hashes, followed by admin login, malicious plugin upload, and full code execution — all from a single anonymous HTTP POST. Affected versions are WordPress 6.9.0–6.9.4 and 7.0.0–7.0.1; the fix shipped in 6.9.5 and 7.0.2 on July 17, 2026. The companion SQLi (CVE-2026-60137) also affects 6.8.x before 6.8.6.

The vendor rated this CRITICAL at CVSS 9.8 and that score is *entirely accurate*. There is no inflation here. The chain requires zero authentication, zero user interaction, zero non-default configuration, and zero special plugins. It works against a stock WordPress install exposed to the internet — which describes hundreds of millions of sites. WordPress shipped forced auto-updates the same day, but Censys data showed only ~86% of public WordPress sites were on the latest security patch, leaving an estimated 90 million sites in the blast radius. CISA added it to KEV within four days with a three-day remediation deadline. GreyNoise, CrowdSec, and F5 Labs all confirmed mass automated exploitation within 72 hours of disclosure. The vendor severity is not only justified, it may understate the operational urgency given the installed base.

"Pre-auth WordPress RCE on default installs. Zero friction. Patch now or get owned."
02 · The Attack Path

6 steps from start to impact.

STEP 01

Locate target WordPress installation

The attacker scans the internet for WordPress sites using Shodan, FOFA, or custom banner-grabbing scripts. WordPress self-identifies via <meta name="generator"> tags, /wp-login.php, and /wp-json/ API responses. Automated tooling like fullhunt/wp2shell-scan and nuclei templates fingerprint the exact core version and confirm the /batch/v1 route is reachable — all without sending an exploit payload.
Conditions required:
  • Target runs WordPress 6.9.0–6.9.4 or 7.0.0–7.0.1
  • REST API is reachable (default on)
  • No persistent object cache (Redis/Memcached) is configured
Where this breaks in practice:
  • WordPress auto-updates may have already patched the site
  • A WAF blocking /batch/v1 would stop reconnaissance
Detection/coverage: Nuclei template wp2shell-exposure.yaml; Rapid7 InsightVM content release July 20, 2026; Qualys QID available; Tenable plugin available. Version fingerprinting is trivial.
STEP 02

Trigger batch route confusion

The attacker sends a single POST /wp-json/batch/v1 request with a crafted JSON body containing nested sub-requests. The first sub-request uses a malformed path (e.g. ///) that causes wp_parse_url() to fail. This failure populates the validation array but not the route-matches array, creating an off-by-one desynchronization. Every subsequent sub-request in the batch now dispatches under the *wrong* handler, bypassing the intended route's permission callback entirely.
Conditions required:
  • Network access to the WordPress REST API endpoint
  • No authentication required
Where this breaks in practice:
  • A WAF with a rule blocking nested batch requests or malformed paths would intercept this
  • Rate limiting on the REST API endpoint could slow but not prevent exploitation
Detection/coverage: WAF rules matching POST to /wp-json/batch/v1 or ?rest_route=/batch/v1 with nested JSON arrays. F5 Labs captured the canonical payload at 735 bytes. CrowdSec published detection signatures July 20, 2026.
STEP 03

Inject SQL via author__not_in

With the route confusion active, the attacker's second sub-request targets /wp/v2/posts with a crafted author_exclude parameter. Because the desync caused this request to dispatch under the wrong handler, WP_Query receives the attacker-controlled string without sanitization. The attacker closes the original NOT IN (...) clause with 0) and appends a UNION SELECT statement targeting wp_users and wp_usermeta tables, extracting admin usernames and password hashes in a single response.
Conditions required:
  • Route confusion from step 2 is active
  • WordPress database contains at least one admin user (always true on any install)
Where this breaks in practice:
  • A database firewall or SQL-aware WAF could detect UNION-based injection patterns
  • Prepared statements in a hypothetical code fix would neutralize the injection
Detection/coverage: SQL injection signatures in WAF logs; anomalous author_exclude parameter values in access logs; database query logs showing UNION SELECT against wp_users.
STEP 04

Crack admin password hash or create admin account

The attacker receives WordPress phpass-hashed passwords in the HTTP response. For weak passwords, offline cracking with hashcat (-m 400) succeeds in minutes. Alternatively, some PoC variants use the SQLi to directly insert a new admin row into wp_users and wp_usermeta, bypassing the need to crack anything. The attacker now has full WordPress admin access.
Conditions required:
  • Successful SQLi from step 3
  • For hash cracking: admin password is not extremely strong (most are not)
  • For direct INSERT: MySQL user has INSERT privileges (default for WordPress DB user)
Where this breaks in practice:
  • MFA on wp-admin login would block hash-cracking path (but not direct DB INSERT path)
  • Strong, unique admin passwords slow the cracking path
Detection/coverage: Failed login attempts from unusual IPs; new admin user appearing in wp_users without corresponding audit trail; wp-admin login from previously unseen geolocation.
STEP 05

Upload malicious plugin for code execution

The authenticated attacker uploads a PHP webshell disguised as a WordPress plugin via the admin plugin installer (/wp-admin/plugin-install.php). The webshell executes arbitrary system commands as the web server user (typically www-data or apache). GreyNoise documented attackers uploading custom reconnaissance plugins, executing AMSI bypass scripts, and performing credential harvesting from readable config files including wp-config.php database credentials.
Conditions required:
  • WordPress admin access from step 4
  • Plugin upload functionality is enabled (default)
Where this breaks in practice:
  • Read-only filesystem or restricted wp-content/plugins permissions would block upload
  • File integrity monitoring would alert on new PHP files
Detection/coverage: New plugin appearing in /wp-content/plugins/ without admin action; PHP files with eval(), system(), exec(), or base64_decode() patterns; outbound connections from the web server to C2 infrastructure.
STEP 06

Post-exploitation: data exfiltration and lateral movement

With code execution on the web server, the attacker reads wp-config.php for database credentials, dumps the full database (all user PII, e-commerce transactions, session tokens), and pivots to other services reachable from the web server's network position. GreyNoise documented one campaign exfiltrating 18,000+ sensitive government records including plaintext passwords and law enforcement PII from a single compromised WordPress instance. The attacker also attempted privilege escalation on the underlying OS.
Conditions required:
  • Code execution from step 5
  • Web server has network access to database and potentially internal services
Where this breaks in practice:
  • Network segmentation limiting web server egress would constrain lateral movement
  • Database access controls beyond wp-config.php credentials would limit data theft
Detection/coverage: Anomalous database queries (full table dumps); large outbound data transfers from the web server; new cron jobs or persistence mechanisms; unexpected processes spawned by the web server user.
03 · Compensating Control

1
CRITICAL 9.8→LOW 2.0
SEVERITY REDUCED
Block /wp-json/batch/v1 and ?rest_route=/batch/v1 at the WAF or reverse proxy immediately — This kills the route confusion entry point without touching WordPress itself. Add a WAF rule (Cloudflare, AWS WAF, ModSecurity, nginx location block) that returns 403 for any request matching /wp-json/batch/v1 or containing rest_route=/batch/v1. This is the highest-leverage mitigation — deploy within 3 days per the noisgate mitigation SLA for CRITICAL. The batch API endpoint is rarely used by legitimate front-end traffic; verify with your development team before deploying, but most sites can block this with zero functional impact.
2
CRITICAL 9.8→LOW 2.5
SEVERITY REDUCED
Disable unauthenticated REST API access — Install and activate a plugin like Disable REST API or add a rest_authentication_errors filter that requires authentication for all REST API endpoints. This blocks the entire unauthenticated attack surface. Deploy within 3 days. Residual risk: authenticated users with Subscriber or higher roles could still theoretically reach the batch endpoint, but the route confusion payload in known PoCs targets the unauthenticated path.
3
CRITICAL 9.8→IGNORE 0.0
SEVERITY REDUCED
Apply WordPress 6.9.5 / 7.0.2 patch — The definitive fix. WordPress forced auto-updates on July 17, 2026 — if your sites received it, verify the running version. For managed fleets, push the update via WP-CLI (wp core update) or your configuration management tool. Per the noisgate remediation SLA for CRITICAL, complete patching within 90 days, though given KEV status and active exploitation, the real deadline is immediately. CISA's BOD 26-04 required federal agencies to patch by July 24, 2026.
4
CRITICAL 9.8→HIGH 8.5
SEVERITY REDUCED
Enforce MFA on all wp-admin accounts — MFA on WordPress admin login blocks the credential-reuse path (where the attacker cracks extracted password hashes and logs in). It does NOT block the direct database INSERT path some PoC variants use to create a new admin account. This is defense-in-depth, not a standalone mitigation. Deploy as standard hygiene.
5
CRITICAL 9.8→CRITICAL 9.8
Conduct forensic triage on all unpatched WordPress instances — CISA KEV entry flags forensic triage as required. For any site that was running a vulnerable version between July 17–present: review access logs for POST requests to /wp-json/batch/v1; check wp_users for accounts created after July 17; scan wp-content/plugins/ for unknown PHP files; review wp-config.php access timestamps; check for cron jobs or persistence mechanisms added by the web server user. GreyNoise documented attackers backdating malicious admin accounts to 2025 to evade detection.
What doesn't work
  • IP-based blocking — Attackers use thousands of distributed IPs across multiple ASNs and geographies (US, Iran, Singapore, Germany, France, Netherlands). Blocking individual IPs is whack-a-mole.
  • Rate limiting alone — The exploit requires a single HTTP request. Rate limiting cannot stop a one-shot attack; it only slows enumeration of additional targets.
  • WordPress application-level security plugins (Wordfence, Sucuri, iThemes) — While some updated their WAF rules post-disclosure, the vulnerability is in WordPress Core *before* plugin code executes. A plugin-level WAF that loads after WordPress Core initialization may not intercept the batch endpoint processing in time. Prefer network-level WAF/reverse proxy rules.
  • Disabling XML-RPC — This vulnerability is in the REST API (/wp-json/), not XML-RPC (/xmlrpc.php). Disabling XML-RPC provides zero protection against wp2shell.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild StatusActively exploited at mass scale. CISA KEV-listed July 21, 2026 (due date July 24). CrowdSec classified exploitation as "Rapid Escalation" by July 28. GreyNoise tracked a campaign (suspected Red Heron / Chinese-speaking actor in UTC+8) that compromised 49 organizations across 29 countries, including a western government entity with 18,000+ sensitive records stolen. F5 Labs captured canonical exploit payloads from automated scanning infrastructure. CrowdSec observed thousands of unique attacking IPs worldwide.
PoC AvailabilityExtensively weaponized. Multiple full-chain RCE PoCs on GitHub: mcipekci/wp2shell, Icex0/wp2shell-poc, 4minx/CVE-2026-63030, GhostInExile/CVE-2026-63030-Wp2Shell, c0gnit00/Wp2Shell, 0xsha/wp2shell. Scanners: fullhunt/wp2shell-scan, ZephrFish/wp2shell-scanner, zi3lak/wp2shell_scanner. Nuclei template available (projectdiscovery/nuclei-templates#16602). First PoC appeared within hours of disclosure.
EPSS Score0.10649 (10.6% probability of exploitation in 30 days) per user-provided data. Some aggregators report higher values post-KEV listing. Regardless of the exact EPSS percentile, the real-world exploitation data makes probability scoring academic — this is *already* being exploited at scale.
CISA KEVYes. Added July 21, 2026 with a July 24, 2026 remediation deadline (3 calendar days). Forensic triage flagged as required. Ransomware association: Unknown. Bound by BOD 26-04.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8). Every base metric is at maximum severity except Scope (Unchanged). Network-attackable, no complexity, no privileges, no interaction, full CIA impact. This is the platonic ideal of a 9.8.
Affected VersionsCVE-2026-63030 (route confusion): WordPress 6.9.0–6.9.4, 7.0.0–7.0.1. CVE-2026-60137 (SQLi component): WordPress 6.8.0–6.8.5, 6.9.0–6.9.4, 7.0.0–7.0.1. Both must be present for the RCE chain. Note: the route confusion bug was introduced in 6.9.0, so WordPress ≤6.8.x has the SQLi but NOT the batch desync — no RCE chain on 6.8.x.
Fixed VersionsWordPress 6.8.6 (SQLi only), 6.9.5, 7.0.2 (both bugs). WordPress 7.1 Beta 2+ also contains the fix. Forced auto-updates were triggered globally on July 17, 2026.
Scanning / Exposure DataGreyNoise: AS211736 carried ~50% of scanning volume; ports 80 and 8080 targeted. CrowdSec: thousands of unique IPs, led by US and Iran. F5 Labs: canonical payload is 735 bytes, identical across targets (automated). Censys (pre-disclosure): only ~14% of public WordPress sites on latest patch. Post-auto-update estimates: ~15% still vulnerable = ~90 million sites globally.
Disclosure & ResearcherDiscovered by Adam Kues of Searchlight Cyber. Disclosed July 17, 2026 with coordinated WordPress security release. Searchlight Cyber withheld full technical details initially, then published the complete attack chain on July 20, 2026. First exploitation observed July 20, 2026 (same day as full writeup).
Related VulnerabilitiesCVE-2026-60137 (WP_Query author__not_in SQLi, CVSS 5.9 MEDIUM) is the mandatory chain partner. Neither CVE alone achieves unauthenticated RCE — the route confusion bypasses auth, and the SQLi provides the injection surface. The combined chain is branded wp2shell.

Sources.

  1. CISA KEV Catalog Entry
  2. Rapid7 ETR: wp2shell Critical RCE in WordPress Core
  3. Horizon3.ai: CVE-2026-60137 & CVE-2026-63030 Attack Research
  4. GreyNoise: Government Records Stolen via WordPress Exploitation
  5. F5 Labs: wp2shell Captured Exploit Payload Analysis
  6. CrowdSec: WordPress wp2shell SQLi-to-RCE Tracking
  7. WordPress 7.0.2 Security Release
  8. Tenable: wp2shell Pre-Auth RCE FAQ
05 · The Call

Final Verdict
= UNCHANGED to CRITICAL (9.8/10)

Why this verdict

  • Zero authentication friction: The entire chain — from route confusion through SQLi to admin credential extraction — executes via a single unauthenticated HTTP POST to a default-enabled endpoint. No session, no cookie, no API key. This is the lowest possible attacker effort.
  • Zero configuration friction: The vulnerability affects *stock WordPress installations* with no plugins, no custom configuration, and no unusual setup. The only precondition that might limit exploitation (persistent object cache via Redis/Memcached) is absent from the vast majority of WordPress deployments — most hosting providers ship WordPress without it.
  • Mass weaponization and active exploitation: Multiple full-chain PoCs appeared within hours. Automated scanning tools, nuclei templates, and turnkey exploit scripts are publicly available. CISA KEV-listed within 4 days. GreyNoise confirmed campaigns compromising government entities. CrowdSec classified this as "Rapid Escalation" within 11 days. This is not theoretical — it is happening right now at industrial scale.
  • Role multiplier — internet-facing web application tier: WordPress is the world's most popular CMS, powering ~40% of all websites. Every WordPress installation is, by definition, an internet-facing application handling HTTP traffic. Deployment roles include: (a) Low-value: personal blogs, dev sandboxes; (b) Typical: business marketing sites, corporate blogs; (c) High-value: e-commerce platforms (WooCommerce with PII/payment card data), government portals (confirmed compromised — 18,000+ law enforcement records stolen), enterprise customer portals, news organizations, healthcare patient portals. The chain succeeds identically across all roles because no role-specific configuration blocks it. The blast radius per compromised site is: full database contents (all user credentials, PII, payment data, session tokens) + code execution on the web server + lateral movement potential to backend databases and internal networks. For e-commerce and government deployments, this is mass data breach / regulatory catastrophe territory. Given that WordPress is *canonically* an internet-facing, data-bearing application — and the chain works on default installs — the verdict floor is CRITICAL.
  • Installed base magnitude: ~600 million WordPress sites worldwide. Even with forced auto-updates, an estimated 90 million remained vulnerable weeks after disclosure. The attack surface is *orders of magnitude* larger than typical enterprise CVEs. A single automated scanning campaign can compromise thousands of sites per day with no human operator involvement.

Why not higher?

The CVSS 9.8 and CRITICAL rating are already at the ceiling of the scoring system. The only way this could be worse is if it were a wormable vulnerability with Scope: Changed (affecting the underlying OS or adjacent systems beyond the WordPress application), but the initial impact is constrained to the WordPress application context and its database, requiring post-exploitation steps for lateral movement.

Why not lower?

Every downgrade argument fails the reality test. Authentication? None needed. Complexity? Single HTTP request. Configuration dependency? Default installs are vulnerable. Exploitation maturity? Fully weaponized with mass scanning. Installed base? Largest CMS on the planet. Real-world impact? Government data breaches confirmed. There is literally no friction point in this chain that would justify any reduction from 9.8 CRITICAL. Even the persistent-object-cache exclusion works *in favor* of exploitation, since the vast majority of WordPress sites don't use one.

06 · Verification

Crowdsourced verification payload.

Run this script on a remote auditor workstation (not the target) with curl installed. Pass the target WordPress site URL as the first argument: bash wp2shell_check.sh https://example.com. No special privileges required — it only performs safe, non-exploitative HTTP GET requests to fingerprint the WordPress version and check batch endpoint reachability.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# wp2shell_check.sh — CVE-2026-63030 exposure checker (non-exploitative)
# Usage: bash wp2shell_check.sh https://target-wordpress-site.com
# Exit codes: 1=VULNERABLE, 0=PATCHED, 2=UNKNOWN

set -euo pipefail

TARGET="${1:?Usage: $0 https://target-site.com}"
TARGET="${TARGET%/}"  # strip trailing slash

echo "[*] Checking CVE-2026-63030 (wp2shell) exposure for: $TARGET"
echo ""

# Step 1: Fingerprint WordPress version
echo "[*] Step 1: Fingerprinting WordPress version..."
VERSION=""

# Try generator meta tag
GEN=$(curl -sS -L --max-time 10 "$TARGET/" 2>/dev/null | grep -ioP 'content="WordPress \K[0-9]+\.[0-9]+\.?[0-9]*' | head -1)
if [ -n "$GEN" ]; then
  VERSION="$GEN"
  echo "    Found version via meta tag: $VERSION"
fi

# Try feed generator if meta tag failed
if [ -z "$VERSION" ]; then
  FEED=$(curl -sS -L --max-time 10 "$TARGET/feed/" 2>/dev/null | grep -ioP 'wordpress.org/\?v=\K[0-9]+\.[0-9]+\.?[0-9]*' | head -1)
  if [ -n "$FEED" ]; then
    VERSION="$FEED"
    echo "    Found version via feed: $VERSION"
  fi
fi

# Try REST API root
if [ -z "$VERSION" ]; then
  API=$(curl -sS -L --max-time 10 "$TARGET/wp-json/" 2>/dev/null | grep -oP '"version"\s*:\s*"\K[0-9]+\.[0-9]+\.?[0-9]*' | head -1)
  if [ -n "$API" ]; then
    VERSION="$API"
    echo "    Found version via REST API: $VERSION"
  fi
fi

if [ -z "$VERSION" ]; then
  echo "    [!] Could not determine WordPress version."
  echo ""
  echo "RESULT: UNKNOWN — version fingerprinting failed. Verify manually."
  exit 2
fi

# Step 2: Check if version is in the vulnerable range
echo "[*] Step 2: Checking version against vulnerable ranges..."

# Parse major.minor.patch
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
PATCH=${PATCH:-0}

VULN_VERSION=false

# Vulnerable: 6.9.0-6.9.4 and 7.0.0-7.0.1
if [ "$MAJOR" -eq 6 ] && [ "$MINOR" -eq 9 ] && [ "$PATCH" -le 4 ]; then
  VULN_VERSION=true
  echo "    Version $VERSION is in vulnerable range (6.9.0-6.9.4)"
elif [ "$MAJOR" -eq 7 ] && [ "$MINOR" -eq 0 ] && [ "$PATCH" -le 1 ]; then
  VULN_VERSION=true
  echo "    Version $VERSION is in vulnerable range (7.0.0-7.0.1)"
else
  echo "    Version $VERSION is NOT in a vulnerable range."
fi

# Step 3: Check batch endpoint reachability
echo "[*] Step 3: Checking batch endpoint reachability..."
BATCH_REACHABLE=false

HTTP_CODE=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 "$TARGET/wp-json/batch/v1" 2>/dev/null || echo "000")
echo "    /wp-json/batch/v1 returned HTTP $HTTP_CODE"

# Also check alternate path
HTTP_CODE_ALT=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 "$TARGET/?rest_route=/batch/v1" 2>/dev/null || echo "000")
echo "    /?rest_route=/batch/v1 returned HTTP $HTTP_CODE_ALT"

if [ "$HTTP_CODE" != "403" ] && [ "$HTTP_CODE" != "404" ] && [ "$HTTP_CODE" != "000" ] && [ "$HTTP_CODE" != "401" ]; then
  BATCH_REACHABLE=true
fi
if [ "$HTTP_CODE_ALT" != "403" ] && [ "$HTTP_CODE_ALT" != "404" ] && [ "$HTTP_CODE_ALT" != "000" ] && [ "$HTTP_CODE_ALT" != "401" ]; then
  BATCH_REACHABLE=true
fi

# Step 4: Verdict
echo ""
echo "========================================"
if $VULN_VERSION && $BATCH_REACHABLE; then
  echo "RESULT: VULNERABLE"
  echo "WordPress $VERSION is in the affected range AND the batch endpoint is reachable."
  echo "ACTION: Update to WordPress 6.9.5 / 7.0.2 immediately, or block /batch/v1 at WAF."
  exit 1
elif $VULN_VERSION && ! $BATCH_REACHABLE; then
  echo "RESULT: VULNERABLE (mitigated)"
  echo "WordPress $VERSION is in the affected range but the batch endpoint appears blocked."
  echo "ACTION: Still patch to 6.9.5 / 7.0.2 — the WAF rule is a compensating control, not a fix."
  exit 1
elif ! $VULN_VERSION; then
  echo "RESULT: PATCHED"
  echo "WordPress $VERSION is not in the vulnerable range for CVE-2026-63030."
  exit 0
else
  echo "RESULT: UNKNOWN"
  exit 2
fi