← Back to Feed CACHED · 2026-10-26 05:51:26 · CACHE_KEY CVE-2026-60137
CVE-2026-60137 · CWE-89 · Disclosed 2026-07-17

WordPress Core WP_Query author__not_in facilitated SQL injection via REST API

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

A screen door on a submarine — the vendor locked the hatch but left a porthole that leads straight to the engine room

CVE-2026-60137 is a SQL injection in WordPress core's WP_Query class. The author__not_in parameter — meant to accept an array of integer user IDs — skips its absint() sanitisation guard when it receives a scalar string instead of an array. The REST API's author_exclude query parameter maps directly to author__not_in, so a crafted value passes raw SQL straight into the NOT IN (...) clause of the generated query. Affected versions are 6.8.0–6.8.5, 6.9.0–6.9.4, and 7.0.0–7.0.1; patches landed in 6.8.6, 6.9.5, and 7.0.2 on July 17 2026. On WordPress 6.8.x the flaw is reachable only when a plugin or theme forwards untrusted input to the parameter. On 6.9.x and 7.0.x a companion bug, CVE-2026-63030 (REST API batch-route confusion), removes that prerequisite entirely — the pair, dubbed wp2shell, yields unauthenticated remote code execution against default installations with a single POST to /wp-json/batch/v1.

The vendor rated this CVE a MEDIUM 5.9 (AC:H) because, in isolation, the SQLi needs a conduit. That framing is technically honest but operationally misleading to the point of negligence. CVE-2026-63030 ships in the same codebase, the same versions, disclosed the same day — every WordPress 6.9+ or 7.0.x instance carries both bugs by default. Attackers never exploit this CVE in isolation; the wp2shell chain is the observed attack. Within 48 hours of disclosure, 45 million exploit attempts from ~150,000 unique sources were logged, GreyNoise tracked a Chinese-speaking threat actor who compromised 49 organisations across 29 countries, and a western government lost 18,000+ sensitive records including law-enforcement PII. CISA added it to KEV on July 21 with a remediation deadline of August 4. Treating this as a 5.9 would be malpractice — the real-world severity is chain-aware CRITICAL.

"Vendor says MEDIUM; attackers say pre-auth RCE. 45M exploit attempts in week one."
02 · The Attack Path

6 steps from start to impact.

STEP 01

Target identification

The attacker fingerprints WordPress installations via /wp-login.php response headers, /wp-includes/js/ asset paths, or the REST API root at /wp-json/. Version detection is trivial via the <meta name="generator"> tag or the ?rest_route=/ response. Tools like WPScan, Nuclei templates, and custom Shodan/FOFA dorks automate this at scale.
Conditions required:
  • Target runs WordPress 6.9.x or 7.0.x (for full chain) or 6.8.x with a plugin exposing author__not_in
Where this breaks in practice:
  • WordPress auto-updates were force-pushed; sites with auto-update enabled patched within hours
  • Cloudflare deployed WAF rules across all plans blocking the batch-endpoint payload pattern
Detection/coverage: WPScan, Nuclei template wordpress-version-detect, Shodan query http.component:wordpress
STEP 02

REST API batch-route confusion (CVE-2026-63030)

The attacker sends a single POST to /wp-json/batch/v1 containing a nested batch envelope. WordPress's route dispatcher resolves the outer batch request without authentication, then processes inner sub-requests in an elevated context. This bypasses the authentication check that would normally gate the author_exclude parameter on the posts endpoint. No credentials, no cookies, no nonce required.
Conditions required:
  • WordPress ≥ 6.9.0 (batch endpoint introduced in 6.9)
  • REST API reachable (default — enabled out of the box)
Where this breaks in practice:
  • Sites behind a WAF that blocks nested batch payloads or SQL metacharacters in query parameters
  • Sites that disabled the REST API entirely via plugin (rare, <5%)
Detection/coverage: WAF logs showing POST to /wp-json/batch/v1 or /?rest_route=/batch/v1 with JSON body containing nested requests arrays; Cloudflare managed rule 100700
STEP 03

Blind SQL injection via author__not_in

Inside the batch sub-request, the attacker supplies a crafted author_exclude value containing a UNION SELECT payload. Because WP_Query skips the absint() cast when the value arrives as a scalar string rather than an array, the raw SQL lands in the NOT IN (...) clause. The attacker uses UNION-based extraction with hex-encoded markers (e.g. 0x4f4b = 'OK') between || delimiters to exfiltrate data. The observed payload appends orderby=none&per_page=500 to maximise result visibility. Weaponised tools: wp2shell PoC by gichirimaku (564 stars), AbdullahMaqbool22 blind-SQLi harness, multiple Nuclei templates.
Conditions required:
  • Step 2 succeeded (unauthenticated context reached the posts endpoint)
  • WordPress database is MySQL/MariaDB (universal for WP)
Where this breaks in practice:
  • Persistent object cache (Redis/Memcached) may interfere with exploitation path per Tenable's FAQ
  • Parameterised query plugins or DB firewalls (extremely rare in WP ecosystem)
Detection/coverage: Database audit logs showing UNION SELECT in queries against wp_posts; IDS signatures for SQL metacharacters in REST API parameters; F5 Labs published payload signatures matching 735-byte exploit
STEP 04

Credential extraction and hash cracking

The UNION SELECT targets wp_users to extract user_login, user_pass (phpass bcrypt hashes), and user_email for administrator accounts. WordPress's default phpass hashing uses bcrypt with cost 10 — offline cracking with hashcat mode 400 on a modern GPU rig yields common passwords in minutes. The GreyNoise-tracked actor also extracted wp-config.php database credentials via the same SQLi channel.
Conditions required:
  • At least one admin account exists (always true)
  • Admin password is crackable or DB credentials are reusable
Where this breaks in practice:
  • Strong admin passwords resist offline cracking
  • MFA on wp-admin (via plugin) blocks credential reuse even if hash is cracked
Detection/coverage: Failed login spikes from unusual IPs; wp-config.php access patterns in application logs
STEP 05

Admin access and webshell deployment

With cracked admin credentials, the attacker logs into /wp-admin/ and uploads a malicious plugin containing a webshell. GreyNoise documented three webshell variants: a minimal one-liner, a medium PHP backdoor, and a sophisticated 150KB 'CMSmap' plugin with file management, database access, port scanning, and privilege escalation. The actor created backdoor accounts ('kapibala', 'kapibala2') with backdated timestamps to evade detection. Alternatively, the RCE can be achieved directly through the SQLi by writing to a file via SELECT ... INTO OUTFILE if MySQL file privileges permit.
Conditions required:
  • Admin credentials obtained or direct file-write via SQLi
Where this breaks in practice:
  • Hardened file permissions on wp-content/plugins may block plugin upload
  • Read-only filesystem on containerised deployments
  • Security plugins (Wordfence, Sucuri) may alert on plugin installation
Detection/coverage: File integrity monitoring on wp-content/; new user creation alerts; webshell detection (YARA rules for known wp2shell payloads); IoC: C2 domains *.981666.xyz, staging IP 74.48.66.73
STEP 06

Post-exploitation and lateral movement

From the webshell, the attacker conducts host reconnaissance, attempts AMSI bypass (17+ script variations observed), token impersonation, and credential spraying against internal SQL databases. The GreyNoise-tracked actor staged exfiltrated data in ZIP archives within web-accessible directories for bulk download. The compromised WordPress server becomes a pivot point into backend infrastructure — database servers, internal networks, and cloud metadata endpoints are all reachable.
Conditions required:
  • Webshell deployed and operational
  • Backend infrastructure reachable from web server
Where this breaks in practice:
  • Network segmentation isolating web tier from internal resources
  • EDR on the web server host
  • Container sandboxing limiting filesystem and network access
Detection/coverage: Outbound connections to C2 IPs (104.225.153.141, 172.245.247.21); ZIP file creation in web-accessible directories; anomalous process execution from PHP/web server context
03 · Compensating Control

1
CRITICAL 9.4→MEDIUM 5.9
SEVERITY REDUCED
Block REST API batch endpoint at WAF/reverse proxy — Add a WAF rule or nginx/Apache location block denying POST requests to /wp-json/batch/v1 and /?rest_route=/batch/v1. This breaks the CVE-2026-63030 chain partner, eliminating the unauthenticated path to the SQLi. Deploy within the noisgate CRITICAL mitigation SLA of 3 days. Cloudflare's managed rule 100700 already does this — enable it if you're on Cloudflare. For self-hosted WAFs, match on URI path containing batch/v1 with a POST method.
2
CRITICAL 9.4→IGNORE 0.0
SEVERITY REDUCED
Apply vendor patch (6.8.6 / 6.9.5 / 7.0.2) — The definitive fix. Verify auto-update succeeded — do not assume it did. Run wp core version across your fleet via WP-CLI or your configuration management tool. For hosts where auto-update failed (custom deployments, version-pinned Composer installs, Docker images with baked-in WP), apply manually. Target the noisgate CRITICAL remediation SLA of 90 days, but given active exploitation, prioritise within days not weeks.
3
CRITICAL 9.4→LOW 3.1
SEVERITY REDUCED
Disable REST API for unauthenticated users — Install and configure the 'Disable REST API' plugin or add a rest_authentication_errors filter returning WP_Error for non-logged-in users. This blocks the entire unauthenticated attack surface including both chain CVEs. Deploy within the 3-day mitigation window. Note: this breaks public-facing features that depend on the REST API (Gutenberg editor for logged-out previews, headless front-ends, some plugins).
4
CRITICAL 9.4→HIGH 7.5
SEVERITY REDUCED
Enforce MFA on all WordPress admin accounts — Even if the SQLi extracts password hashes and the attacker cracks them offline, MFA on wp-admin prevents credential-based login. This doesn't stop the SQLi data exfiltration itself but blocks the escalation path to webshell deployment via the admin panel. Use a plugin like WP 2FA, Wordfence Login Security, or hardware key support. Deploy within 3 days.
5
CRITICAL 9.4→CRITICAL 9.0
Deploy file integrity monitoring on wp-content/ — Monitor wp-content/plugins/, wp-content/uploads/, and wp-content/themes/ for new or modified PHP files. Alert on any .php file creation that doesn't correlate with a legitimate admin action. This doesn't prevent exploitation but dramatically shortens dwell time from the webshell deployment stage. OSSEC, Wazuh, Tripwire, or Wordfence's built-in scanner all work.
What doesn't work
  • WordPress security plugins alone (Wordfence free tier, Sucuri free) — the free tiers of these plugins did not have WAF rules for wp2shell at disclosure; premium/paid tiers received rules faster but still required the plugin to be installed and active before the attack. They cannot protect against the SQLi at the database layer.
  • Changing the REST API URL prefix (/wp-json/ → custom) — security through obscurity; the ?rest_route= fallback path is always active and bypasses any custom prefix. Attackers use both paths.
  • PHP version upgrade — this is a WordPress application-layer flaw, not a PHP runtime vulnerability. Running PHP 8.4 vs 8.1 makes no difference.
  • Database user privilege restriction — while removing FILE privilege from the WordPress MySQL user blocks INTO OUTFILE, it does not prevent UNION-based data extraction of user credentials and site data, which is the primary attack path.
04 · Intelligence Metadata

The supporting signals.

In-the-wild statusActively exploited at scale. 45M exploit attempts from ~150K unique IPs in the first week (CyberSecurityNews). GreyNoise tracked a Chinese-speaking actor (UTC+8) who compromised 49 orgs across 29 countries. A government org lost 18,000+ records including law-enforcement PII. Wiz reported 60% of WP orgs had a vulnerable instance at disclosure; 25% were internet-exposed.
CISA KEVListed 2026-07-21, remediation due 2026-08-04 per BOD 26-04. Added alongside CVE-2026-63030 (the chain partner).
EPSS0.05323 (5.3%) — top ~5th percentile. High for a 'MEDIUM' CVE, reflecting the active exploitation signal and massive attack surface.
PoC availability20+ public repositories on GitHub. Top repos: gichirimaku/wp2shell-PoC (564★, full RCE chain), AbdullahMaqbool22/CVE-2026-60137-WordPress-Core-SQL-Injection-PoC (blind SQLi harness), Bhanunamikaze/WP2Shell-CVE-2026-63030-POC (safe detector). Multiple Nuclei templates available. Many PoCs reportedly AI-generated within hours of disclosure.
CVSS vectorVendor: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N = 5.9. The AC:H reflects the standalone requirement for a plugin conduit. Chained with CVE-2026-63030: effective AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8 (pre-auth RCE on default installs).
Affected versionsWordPress 6.8.0–6.8.5 (SQLi only, requires plugin/theme conduit), 6.9.0–6.9.4 (full wp2shell RCE chain), 7.0.0–7.0.1 (full wp2shell RCE chain), 7.1 beta (pre-beta2).
Fixed versions6.8.6, 6.9.5, 7.0.2, 7.1 beta2. WordPress.org force-pushed auto-updates for supported installations. Distro backports: Debian wordpress package updated; Ubuntu USN issued.
Scanning & exposureShodan indexes ~80M+ WordPress instances globally. Wiz: 25% of WP orgs had internet-exposed vulnerable instances at disclosure (dropping to ~10% within 24h due to auto-updates). Cloudflare deployed managed WAF rule 100700 across all plans. F5 Labs published 735-byte payload signature.
Disclosure timeline2026-07-17: Patches released + CVEs published. 2026-07-19: First public PoCs on GitHub (AI-assisted, <48h). 2026-07-20: Horizon3 detection tooling released; mass exploitation begins. 2026-07-21: CISA KEV listing. Researchers noted 'no security researcher could have found and completed this exploit chain in 10 hours without AI.'
DiscoverersTF1T, dtro, haongo (CVE-2026-60137). Adam Kues / Searchlight Cyber (CVE-2026-63030, the chain partner). Kues published research on disclosure day while withholding full technical details; AI-assisted PoC development filled the gap within hours.

Sources.

  1. GitHub Advisory (GHSA-fpp7-x2x2-2mjf)
  2. Horizon3 — CVE-2026-60137 & CVE-2026-63030 Technical Analysis
  3. Tenable — wp2shell FAQ
  4. F5 Labs — wp2shell Captured Exploit Payload
  5. GreyNoise — Kapibala Actor Campaign
  6. Wiz — wp2shell Exploitation in the Wild
  7. CISA KEV Entry
  8. CyberSecurityNews — 45M Exploit Attempts Analysis
05 · The Call

Final Verdict
↑ UPGRADED to CRITICAL (9.4/10)

Why this verdict

  • Chain collapses AC:H to AC:L. The vendor's MEDIUM rating assumes the SQLi needs a plugin to deliver untrusted input. On WordPress 6.9+ and 7.0.x, CVE-2026-63030 — same product, same versions, same disclosure day — provides that delivery mechanism on *default installations* with zero authentication. Every unpatched 6.9+/7.0.x instance carries both bugs. Scoring this CVE in isolation is like rating a detonator as 'low explosive yield' while ignoring the bomb it ships with.
  • Active mass exploitation destroys any theoretical friction. 45 million exploit attempts in week one, 150K unique source IPs, 49 confirmed compromises across 29 countries, a government data breach of 18,000+ records. This is not a lab curiosity — the exploit is commoditised, AI-generated PoCs proliferated within 48 hours, and the chain is a single HTTP POST.
  • WordPress is internet-facing by definition with a colossal installed base. WordPress powers roughly 40% of the web. The affected component is core, not a plugin. There is no 'internal-only deployment' friction to apply — if WordPress is running, it is reachable. Wiz documented 25% of WP organisations exposing a vulnerable server to the internet at disclosure.
  • Role multiplier: WordPress in high-value roles. (a) *Low-value*: personal blogs — chain succeeds, blast radius limited to that site's data. (b) *Typical*: small business marketing sites — chain succeeds, credential theft + customer data exposure. (c) *High-value*: government portals (confirmed compromised), e-commerce platforms (PCI cardholder data in wp_postmeta/WooCommerce tables), healthcare patient portals (PHI), corporate intranets on WordPress — chain succeeds with blast radius extending to regulated data exfiltration, lateral movement into backend databases, and supply-chain pivot via compromised update mechanisms. The GreyNoise-tracked actor demonstrated credential spraying from compromised WP hosts into internal SQL databases. WordPress in government/e-commerce roles represents well over 10% of the installed base → verdict floor is CRITICAL.
  • KEV listing with 14-day remediation deadline confirms federal-level urgency. CISA added both chain CVEs on July 21 with an August 4 due date — the fastest remediation window CISA assigns, signalling assessed criticality far above the vendor's MEDIUM.

Why not higher?

A 9.4 rather than 10.0 because WordPress.org force-pushed auto-updates, which materially reduced the vulnerable population within 24 hours (Wiz: from 25% to ~10% internet-exposed). Sites with functioning auto-update received the patch without operator action. The persistent-object-cache requirement noted by Tenable introduces a minor exploitation constraint on some configurations. These factors prevent a perfect 10 but do not meaningfully lower the severity for the remaining unpatched population.

Why not lower?

Downgrading below CRITICAL would require ignoring confirmed mass exploitation, KEV listing, the chain context that eliminates the AC:H prerequisite, and the blast radius against high-value deployment roles (government, e-commerce, healthcare). The 6.8.x branch alone — where a plugin is genuinely required — might warrant HIGH, but assessing the CVE across all affected versions (including 6.9+/7.0+ where the chain is default-exploitable), CRITICAL is the floor. The GreyNoise campaign with 49 compromised organisations is dispositive.

06 · Verification

Crowdsourced verification payload.

Run this script on each WordPress host (or from a management server with SSH/WP-CLI access) as any user who can read the WordPress installation directory. Example: bash check_cve_2026_60137.sh /var/www/html where the argument is the WordPress root. No root privileges required — only read access to wp-includes/version.php.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_60137.sh — Detect CVE-2026-60137 (WordPress author__not_in SQLi)
# Usage: bash check_cve_2026_60137.sh /path/to/wordpress
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

WP_ROOT="${1:-/var/www/html}"
VERSION_FILE="${WP_ROOT}/wp-includes/version.php"

if [[ ! -f "$VERSION_FILE" ]]; then
  echo "UNKNOWN — Cannot find ${VERSION_FILE}. Is this a WordPress installation?"
  exit 2
fi

# Extract $wp_version from version.php
WP_VERSION=$(grep -oP "\\\$wp_version\s*=\s*'\K[^']+" "$VERSION_FILE" 2>/dev/null || true)

if [[ -z "$WP_VERSION" ]]; then
  echo "UNKNOWN — Could not parse WordPress version from ${VERSION_FILE}"
  exit 2
fi

echo "Detected WordPress version: ${WP_VERSION}"

# Compare versions using sort -V
version_gte() {
  # Returns 0 if $1 >= $2
  printf '%s\n%s' "$2" "$1" | sort -V -C
}

version_lt() {
  ! version_gte "$1" "$2"
}

# Vulnerable ranges:
#   6.8.0 <= v < 6.8.6
#   6.9.0 <= v < 6.9.5
#   7.0.0 <= v < 7.0.2

VULNERABLE=false
CHAIN_RCE=false

if version_gte "$WP_VERSION" "6.8.0" && version_lt "$WP_VERSION" "6.8.6"; then
  VULNERABLE=true
  echo "STATUS: Version ${WP_VERSION} is in vulnerable range 6.8.0–6.8.5 (SQLi only, requires plugin conduit)"
elif version_gte "$WP_VERSION" "6.9.0" && version_lt "$WP_VERSION" "6.9.5"; then
  VULNERABLE=true
  CHAIN_RCE=true
  echo "STATUS: Version ${WP_VERSION} is in vulnerable range 6.9.0–6.9.4 (wp2shell RCE chain exploitable)"
elif version_gte "$WP_VERSION" "7.0.0" && version_lt "$WP_VERSION" "7.0.2"; then
  VULNERABLE=true
  CHAIN_RCE=true
  echo "STATUS: Version ${WP_VERSION} is in vulnerable range 7.0.0–7.0.1 (wp2shell RCE chain exploitable)"
fi

if [[ "$VULNERABLE" == "true" ]]; then
  if [[ "$CHAIN_RCE" == "true" ]]; then
    echo "VULNERABLE — CVE-2026-60137 + CVE-2026-63030 (wp2shell pre-auth RCE). PATCH IMMEDIATELY."
  else
    echo "VULNERABLE — CVE-2026-60137 (SQLi, requires plugin/theme conduit). Patch to 6.8.6."
  fi
  exit 1
else
  echo "PATCHED — WordPress ${WP_VERSION} is not in a vulnerable range for CVE-2026-60137."
  exit 0
fi