← Back to Feed CACHED · 2026-07-18 06:51:40 · CACHE_KEY CVE-2026-60137
CVE-2026-60137 · CWE-89 · Disclosed 2026-07-17

WordPress 6

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

A type-check the developers thought was defense-in-depth turned out to be the only guard between the REST API and raw SQL

The bug lives in WP_Query: when author__not_in is passed as a string instead of an array, the is_array() sanitisation branch is skipped, (array)"payload" wraps it as ["payload"], and implode(',', ...) drops the attacker's raw string straight into a NOT IN (...) clause. Affected branches: 6.8.x < 6.8.6, 6.9.x < 6.9.5, and 7.0.x < 7.0.2. In isolation the REST API's author_exclude schema (type: array, items: integer) is supposed to block string values before they ever reach the vulnerable path — which is why WordPress scored it MEDIUM and set AC:H.

That MEDIUM label is wrong for anyone running WordPress 6.9 or 7.0. Adam Kues (Searchlight Cyber / Assetnote) disclosed this alongside CVE-2026-63030, a REST batch-route confusion bug that dispatches a categories request through the posts controller and bypasses the schema check entirely. Chained, an unauthenticated attacker gets SQLi → object-injection → pre-auth RCE on a stock WordPress install. A public PoC (Senanfurkan/wordpress-cve-2026-63030) landed the same day, and Cloudflare shipped emergency WAF signatures within 24 hours. Treat this CVE ID as the SQLi half of WP2Shell — not a standalone medium.

"Vendor calls this MEDIUM standalone SQLi. Chained with CVE-2026-63030 it's a pre-auth RCE on every WP 6.9+ site. Patch now."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify a WP 6.9 / 7.0 target with REST API reachable

Attacker fingerprints WordPress version via /wp-json/, readme.html, generator meta, or /wp-includes/js/wp-embed.min.js hash. Mass scanning uses httpx + nuclei templates for WordPress version disclosure. Only 6.9.x < 6.9.5 and 7.0.x < 7.0.2 permit the unauthenticated RCE chain; 6.8.x is SQLi-only and requires a vulnerable plugin.
Conditions required:
  • Target running WordPress 6.9.0–6.9.4 or 7.0.0–7.0.1
  • /wp-json/ reachable from the attacker network (default on public installs)
Where this breaks in practice:
  • Sites behind Cloudflare with July-2026 WAF rules block the batch endpoint pattern
  • Version fingerprint hidden by hardening plugins (Wordfence, iThemes Security)
Detection/coverage: Nuclei templates published within 24h of disclosure; Wordfence and Sucuri scanners fingerprint by core version
STEP 02

Trigger batch route confusion (CVE-2026-63030)

Attacker POSTs to /wp-json/batch/v1 with a payload that references the categories endpoint but is dispatched by the posts controller. This is the schema-mismatch primitive that lets an unsanitised author_exclude value survive into WP_Query. Weaponised in Senanfurkan/wordpress-cve-2026-63030 PoC, which posts a crafted JSON body via curl.
Conditions required:
  • Anonymous access to /wp-json/batch/v1
  • WP core ≥ 6.9 (batch route did not exist in 6.8)
Where this breaks in practice:
  • Cloudflare, Sucuri, Wordfence all shipped rules blocking the specific batch-confusion pattern within 24h
  • Sites that disable REST API for anonymous users (jetpack/rest-api-toolbox) are immune
Detection/coverage: WAFs log batch POSTs with mixed controller identifiers; Sigma rule published by Beazley Security Labs
STEP 03

Inject via author__not_in into WP_Query SQL

The unsanitised string reaches implode(',', ...) and is interpolated raw into the posts query. Attackers use a time-based or UNION payload — the sqlmap tamper script for this CVE surfaced the day after disclosure. Data exfil (users table, wp_users.user_pass) is trivial from this primitive.
Conditions required:
  • Step 2 succeeded
  • MySQL/MariaDB backend (SQLite driver is not vulnerable to the tested UNION payload)
Where this breaks in practice:
  • DB_USER with restricted grants (rare in shared hosting)
  • SQL query logging + anomaly detection on prod DB
Detection/coverage: MySQL general log will show NOT IN (SLEEP(... or UNION SELECT patterns against wp_posts; EDR unlikely to catch
STEP 04

Escalate SQLi to RCE via PHP object injection

Payload writes a serialised PHP object into wp_options, and a follow-up request triggers unserialize() in an autoloaded option. This reaches known WordPress core POP gadgets, yielding code execution as the PHP-FPM/Apache user. This is what makes the WP2Shell label appropriate.
Conditions required:
  • Attacker can write to wp_options (implied by SQLi with INSERT/UPDATE grants — WordPress default)
  • Autoloaded option is fetched on a subsequent request
Where this breaks in practice:
  • DISALLOW_FILE_MODS and read-only filesystems block webshell drop but not code exec
  • Some managed hosts (WP Engine, Kinsta) run PHP under a jailed user with no shell
Detection/coverage: EDR on the host may catch php-fpm spawning sh/bash; File-integrity monitoring catches webshell writes to wp-content
STEP 05

Pivot from the WordPress host

The PHP user typically has access to wp-config.php (DB creds), wp-content/uploads (webshell staging), and outbound egress. Mass campaigns will drop coin miners, SEO-poisoning shells, or use the box as a phishing infra pivot. Enterprise-hosted WordPress often shares AD-integrated auth or lives inside the corporate DMZ.
Conditions required:
  • Egress allowed from the web server (default)
  • No egress filtering or micro-segmentation
Where this breaks in practice:
  • Segmented DMZ with no lateral routes limits blast radius
  • Immutable-infra WordPress (containers rebuilt on deploy) survives worse than long-lived VMs
Detection/coverage: Outbound C2 to newly-registered domains; NetFlow anomalies from web tier
03 · Intelligence Metadata

The supporting signals.

In-the-wildActive mass scanning observed for /wp-json/batch/v1 within 48h of disclosure per GreyNoise tag WordPress WP2Shell RCE Attempt. No confirmed KEV listing as of 2026-07-18, but expected imminently.
Public PoCSenanfurkan/wordpress-cve-2026-63030 — end-to-end pre-auth RCE chain published 2026-07-17. Nuclei + sqlmap tamper scripts followed.
EPSSScore not yet published (disclosed 2026-07-17). Similar WordPress core pre-auth chains historically hit >0.90 within 7 days.
KEV statusNot listed as of 2026-07-18. Given PoC + mass-scan activity, add-date within 7 days is likely.
CVSS vectorVendor: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N (5.9). noisgate reassess: AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H when chained → 9.6.
Affected versionsWordPress 6.8.0–6.8.5, 6.9.0–6.9.4, 7.0.0–7.0.1. 6.7.x and earlier not affected (author__not_in path predates the missing guard).
Fixed versions6.8.6, 6.9.5, 7.0.2. Debian/Ubuntu backports pending as of 2026-07-18; auto-updates rolling to sites with background updates enabled.
Exposure dataShodan facet http.component:WordPress returns ~14.2M hosts; Censys estimates ~35–45% are on 6.9.x or 7.0.x branches — roughly 5–6M internet-exposed vulnerable instances.
DisclosureCoordinated disclosure 2026-07-17 alongside WP 7.0.2 release. Cloudflare shipped emergency WAF rules same day.
ResearcherAdam Kues, Searchlight Cyber / Assetnote research team.
04 · The Call

noisgate verdict.

Final Verdict
UPGRADED to CRITICAL (9.4/10)

Chained with the same-day-disclosed CVE-2026-63030 batch-route bug, this SQLi becomes an unauthenticated pre-auth RCE on WordPress 6.9+ — the single most decisive factor is a public end-to-end PoC hitting a component with ~14M internet-exposed installs. Vendor's MEDIUM label reflects only the standalone SQLi in isolation, which is not how this CVE will be exploited in practice.

HIGH chain viability and public PoC quality
HIGH installed-base exposure via Shodan/Censys
MEDIUM EPSS trajectory (score not yet published)

Why this verdict

  • Chain flips the CVSS: standalone AC:H/C:H/I:N/A:N is fair, but chained with CVE-2026-63030 the attack complexity collapses to AC:L and impact expands to C:H/I:H/A:H. A CVE ID is not a threat model.
  • Role multiplier — public-facing web tier: WordPress is canonically the internet-facing marketing/CMS layer for a majority of enterprises. Chain succeeds → PHP-user shell → webshell → DMZ pivot. Blast radius: host → tenant → potential AD pivot where WP shares SSO. Deployment share in this role: >90% of installs are public-facing by definition — floor is CRITICAL.
  • Weaponisation velocity: Public PoC same-day, WAF signatures same-day, GreyNoise tag within 48h. Mass exploitation window has already opened.
  • Population is enormous: 5–6M vulnerable internet-exposed instances per Shodan/Censys facets. Even at 0.1% conversion, that is a five-figure victim count.

Why not higher?

There is no higher bucket. Score capped at 9.4 (not 10.0) because a small fraction of managed WordPress hosts (WP Engine, Kinsta, Automattic-hosted) auto-patched within hours of the 7.0.2 release, and Cloudflare WAF customers with default managed-rules had virtual patching within 24h — so real-world victim count will be materially lower than the raw exposed-population number.

Why not lower?

Downgrading below CRITICAL requires either no public exploit (there is one), narrow install base (14M+ exposed hosts), or high attacker prerequisites (chain is unauthenticated). None of the friction factors — WAF, hardening plugins, jailed PHP — apply to enough of the population to break the CRITICAL floor. Vendor MEDIUM is only defensible if you ignore the sibling CVE, which no attacker will.

05 · Compensating Control

What to do — in priority order.

  1. Block /wp-json/batch/v1 at the WAF or reverse proxy — The batch endpoint is the vector for the RCE chain and is rarely used by legitimate frontend traffic. Add a deny rule for POST to that path (or return 403 for anonymous requests) at Cloudflare/Akamai/F5/nginx. Deploy within 3 days per noisgate mitigation SLA — hours if you are internet-facing.
  2. Force WordPress auto-updates to minor releases — Set WP_AUTO_UPDATE_CORE = 'minor' (or true) in wp-config.php on every managed WP host. 6.8.6 / 6.9.5 / 7.0.2 will flow automatically. Combine with a scheduled wp cron event run --due-now if cron is disabled. Roll out within 3 days.
  3. Enable Cloudflare / Sucuri / Wordfence emergency rules — All three vendors shipped CVE-2026-63030 / 60137 signatures on 2026-07-17. If you already pay for them, verify the ruleset version is post-disclosure and set to block, not log. Confirm rule ID coverage within 24 hours.
  4. Restrict database grants on wp_users and wp_options — Even a successful SQLi is materially less useful if the DB_USER in wp-config.php cannot write to wp_options (blocks the unserialize gadget) or SELECT from wp_users (blocks credential dump). Rotate creds and constrain grants within 30 days as a systemic hardening step.
What doesn't work
  • Disabling XML-RPC — different endpoint, does not touch the REST batch route or WP_Query path.
  • Two-factor authentication on wp-admin — the chain is pre-auth against the REST API; no login is involved.
  • IP-allowlisting /wp-admin — same reason; the exploit hits /wp-json/, which is often left open for headless-CMS integrations.
  • Hiding the WordPress version — attackers spray the payload blind; version fingerprinting is a nice-to-have, not a control.
06 · Verification

Crowdsourced verification payload.

Run this from an auditor workstation with network reach to the target site. Usage: ./check-cve-2026-60137.sh https://target.example.com. No credentials required — it inspects the WordPress REST API version banner and the batch endpoint. Non-zero exit indicates VULNERABLE.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# noisgate: CVE-2026-60137 / CVE-2026-63030 (WP2Shell) detector
# Usage: ./check-cve-2026-60137.sh https://target.example.com
set -u

TARGET="${1:-}"
if [[ -z "$TARGET" ]]; then
  echo "Usage: $0 <https://site>" >&2
  exit 2
fi

# Strip trailing slash
TARGET="${TARGET%/}"

# 1. Fetch WordPress version from /wp-json/ (public endpoint)
BODY="$(curl -sS --max-time 10 -A 'noisgate-verifier/1.0' "$TARGET/wp-json/" || true)"
VER="$(echo "$BODY" | grep -oE '"gmt_offset"|"home"' >/dev/null && \
       curl -sS --max-time 10 -A 'noisgate-verifier/1.0' "$TARGET/?rest_route=/" | \
       grep -oE 'wp-json/wp/v2[^"]*' | head -n1)"

HDR_VER="$(curl -sSI --max-time 10 "$TARGET/wp-json/" | grep -i '^x-wp-' || true)"
RAW_VER="$(curl -sS --max-time 10 "$TARGET/readme.html" | grep -oE 'Version [0-9]+\.[0-9]+(\.[0-9]+)?' | head -n1 | awk '{print $2}')"

if [[ -z "$RAW_VER" ]]; then
  echo "UNKNOWN: could not determine WordPress version at $TARGET"
  exit 3
fi

echo "Detected WordPress version: $RAW_VER"

# 2. Compare against fixed versions per branch
vuln=1  # 1 = patched, 0 = vulnerable
IFS='.' read -r MAJ MIN PATCH <<<"$RAW_VER"
PATCH="${PATCH:-0}"

case "${MAJ}.${MIN}" in
  6.8) [[ "$PATCH" -lt 6 ]] && vuln=0 ;;
  6.9) [[ "$PATCH" -lt 5 ]] && vuln=0 ;;
  7.0) [[ "$PATCH" -lt 2 ]] && vuln=0 ;;
  *)   # older branches: SQLi path predates guard; RCE chain N/A
       [[ "$MAJ" -eq 6 && "$MIN" -lt 8 ]] && { echo "PATCHED: $RAW_VER predates vulnerable code path"; exit 0; } ;;
esac

# 3. Probe batch endpoint reachability (RCE chain requires it)
BATCH_CODE="$(curl -sS -o /dev/null -w '%{http_code}' --max-time 10 \
  -X POST -H 'Content-Type: application/json' \
  --data '{"validation":"require-all-validate","requests":[]}' \
  "$TARGET/wp-json/batch/v1")"

echo "Batch endpoint returned: HTTP $BATCH_CODE"

if [[ "$vuln" -eq 0 ]]; then
  if [[ "$BATCH_CODE" =~ ^(200|400|401|403)$ && "${MAJ}.${MIN}" != "6.8" ]]; then
    echo "VULNERABLE: $RAW_VER, batch endpoint reachable — pre-auth RCE chain viable"
    exit 1
  else
    echo "VULNERABLE: $RAW_VER SQLi path present (batch route not confirmed reachable)"
    exit 1
  fi
fi

echo "PATCHED: $RAW_VER is at or above fixed release"
exit 0
07 · Bottom Line

If you remember one thing.

TL;DR
This is not a MEDIUM — it is the SQLi half of a public, unauthenticated, pre-auth RCE chain against every internet-exposed WordPress 6.9+ install. Monday morning: (1) push 6.8.6 / 6.9.5 / 7.0.2 to every WP host you own, starting with internet-facing marketing and customer sites; (2) add a WAF deny rule for POST /wp-json/batch/v1 and confirm your Cloudflare/Sucuri/Wordfence emergency ruleset is loaded; (3) run the verification script across your inventory and treat any VULNERABLE result on an externally reachable host as an incident-response candidate — assume attempted exploitation. noisgate mitigation SLA: ≤ 3 days (WAF block + emergency ruleset). noisgate remediation SLA: ≤ 90 days for full 7.0.2 rollout, but for public-facing hosts you should be done inside a week — mass exploitation is already underway per GreyNoise telemetry. If KEV listing lands mid-week, federal defenders get a hard 21-day BOD 22-01 clock; the rest of us should already be finished.

Sources

  1. Cloudflare Blog — WordPress WAF protection for CVE-2026-60137 / 63030
  2. Beazley Security Labs — WP2Shell advisory (BSL-A1193)
  3. SecurityOnline — WordPress Pre-Auth RCE CVE-2026-63030 PoC public
  4. GitHub PoC — Senanfurkan/wordpress-cve-2026-63030
  5. Cyber Kendra — WP2Shell critical RCE writeup
  6. THREATINT — CVE-2026-60137 record
  7. Feedly CVE — CVE-2026-63030 sibling record
  8. WordPress security release announcement (7.0.2 / 6.9.5 / 6.8.6)
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.