wp2shell (CVE-2026-63030): The Unauthenticated RCE Hitting the Heart of WordPress
wp2shell is a pre-auth RCE in WordPress Core (CVE-2026-63030 + CVE-2026-60137). How it works, affected versions, PoC, detection and remediation.

Introduction
On July 17, 2026, the WordPress ecosystem shifted overnight. A vulnerability nicknamed wp2shell lets an anonymous, completely unauthenticated attacker run arbitrary code on a default WordPress install, with no third-party plugins whatsoever. In other words, it is a pre-authentication RCE in WordPress Core, the single most critical class of flaw for a CMS that powers more than 40% of the web.
Within hours of the patch shipping, a working proof-of-concept appeared on GitHub. The clock is now ticking for hundreds of millions of sites. This article breaks down how wp2shell works, which versions are affected, how to detect exposure, and how to protect yourself, before mass scanners find your site before you do.
wp2shell at a glance
wp2shell is not a single flaw, but the chaining of two vulnerabilities in WordPress Core. The first, CVE-2026-60137, is a SQL injection that has existed since version 6.8. The second, CVE-2026-63030, is a route confusion in the REST API, introduced in 6.9. Taken separately, both flaws are hard to exploit. It is their combination that makes them devastating.
The result is an unauthenticated remote code execution on versions 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1, fixed in versions 6.9.5 and 7.0.2. The SQL injection on its own reaches back to 6.8 and is fixed with 6.8.6. No prerequisite is needed on the attacker's side, as a single anonymous HTTP request is enough. That is what explains a CVSS score of 9.1 for the SQL injection and 7.5 for the route confusion.
The diagram below sums up the full attack flow, from the anonymous request to the shell.

How does wp2shell work?
wp2shell's power, and its danger, comes from this chaining: two moderately severe bugs on their own become catastrophic once combined. Let's break it down.
Part 1: the SQL injection in WP_Query (CVE-2026-60137)
At the core is a classic PHP type juggling bug. The WP_Query class, which builds nearly every WordPress query, accepts an author__not_in parameter that is meant to be an array of IDs. The code assumes that type and concatenates the values straight into the SQL query, relying on the fact that an array of integers is inherently safe.
The problem shows up when you supply a string instead of an array: the sanitization intended for the array is bypassed and the raw value lands directly in the WHERE clause.
// Expected: author__not_in = [1, 2, 3] -> "AND wp_posts.post_author NOT IN (1,2,3)"
// Supplied: author__not_in = "1) AND 1=0 UNION ALL SELECT ... -- -"
// the string is concatenated as-is into the SQL
$where .= " AND {$wpdb->posts}.post_author NOT IN ({$author__not_in})";In practice, the payload looks like a classic UNION injection, with per_page=-1 to disable query splitting and ensure the full injection is executed:
author__not_in=1) AND 1=0 UNION ALL SELECT <23 columns> -- -
per_page=-1This injection has existed since WordPress 6.8, but on its own it stays bounded: the routes that expose author__not_in require authentication. A missing key was needed to reach it anonymously, and that key is Part 2.
Part 2: the REST route confusion in /wp-json/batch/v1 (CVE-2026-63030)
Since 5.6, WordPress has exposed a batch endpoint: /wp-json/batch/v1. It bundles several REST sub-requests into a single HTTP call, then executes them in series. Internally, WordPress tracks these sub-requests in two parallel arrays: one for validation (permission checks and endpoint allow-list), one for execution.
The flaw, introduced in 6.9, is a desync of those two arrays. When one sub-request triggers an error, the two arrays shift by one position. The result is that sub-request number N runs under the handler, and crucially under the permission context, of sub-request number N-1.
The chain: from bounded injection to anonymous RCE
Combined, the two parts yield the full primitive:
- The
/wp-json/batch/v1endpoint receives a nested, malformed batch request. - The desync (CVE-2026-63030) slips past the permission check: a public route acts as cover for a sensitive one.
- Attacker input reaches
author__not_inand triggers the SQL injection (CVE-2026-60137), now unauthenticated. - The injection doesn't just read the database: it forges rows to obtain write primitives.
From SQL injection to code execution
This is the most elegant step of the chain. The SQL injection is turned into RCE through a series of abuses of legitimate WordPress features:
- Forging
wp_postsandwp_optionsrows: by injecting rows containing markup like[embed]<site-url>[/embed], WordPress creates genuine oEmbed cache entries and transients, with predictable identifiers (post_name = md5(url + attributes)). - Escalation via
customize_changeset: a forgedcustomize_changesetrow carrying theuser_idof a real administrator, together with apost_type=requestrow, triggers aparse_requestexecuted in admin context. - Admin creation and webshell: the same batch request embeds a
POST /wp/v2/usersthat creates a new administrator account, then a self-destructing plugin runs system commands.
The end result is a shell, which is where the name wp2shell comes from. All from a single unauthenticated HTTP request.
Affected WordPress versions and patches
| WordPress version | Status | Patch |
|---|---|---|
| < 6.8 | Not affected | None |
| 6.8.0 → 6.8.5 | SQL injection only (no RCE) | 6.8.6 |
| 6.9.0 → 6.9.4 | Full RCE chain | 6.9.5 |
| 7.0.0 → 7.0.1 | Full RCE chain | 7.0.2 |
| 7.1 beta | Affected | 7.1 beta2 |
The batch endpoint has existed since 5.6, but it is the route confusion introduced in 6.9 that turns a bounded injection into anonymous RCE. WordPress pushed the patches through its forced auto-update system. Not every install has auto-updates enabled, though, and sites managed manually or frozen on a version remain at risk.
How do you know if you're vulnerable to wp2shell?
Three approaches, from simplest to most rigorous.
1. Check your version
The fastest way is to look at your version number. If your site runs 6.9.0 through 6.9.4 or 7.0.0 through 7.0.1, treat it as vulnerable until proven otherwise. Be careful, though, as the displayed version can be hidden or misleading (themes, reverse proxies, WAF).
2. Test the batch endpoint's exposure
The endpoint is reachable in two ways, and both must be blocked:
# "Clean" route
curl -s -o /dev/null -w "%{http_code}\n" https://your-site/wp-json/batch/v1
# Query-string variant (often missed by WAFs)
curl -s -o /dev/null -w "%{http_code}\n" "https://your-site/?rest_route=/batch/v1"Any status other than 404 or 403 on either route indicates an exposed attack surface.
3. Active detection (blind SQL injection, non-destructive)
Reliable detection relies on a time-based blind SQL injection. You measure the response time with and without a SLEEP payload, wrapped in a derived table to defeat engine optimizations:
(SELECT 1 FROM (SELECT SLEEP(5))x)If the request with SLEEP(5) consistently responds 5 seconds slower than the control request, the injection sink is reachable. The public wp2shell-lab project even provides a Docker environment (WordPress 6.9.4 and MySQL) and a non-destructive detection script built on this principle.
Emergency protection steps
In order of priority:
- Update immediately to 6.9.5, 7.0.2 or 6.8.6. This is the only complete remediation.
- Block both forms of the batch route at the WAF or web-server level, namely
/wp-json/batch/v1and?rest_route=/batch/v1. Don't filter only/wp-json, or the query-string variant stays exploitable. - Restrict the REST API to authenticated users via a plugin, or a must-use plugin that rejects anonymous batch requests at the
rest_pre_dispatchhook. - Check indicators of compromise: new administrator accounts, unknown plugins, suspicious rows in
wp_postsorwp_options, requests to/batch/v1in your logs.
Why classic scanners miss it
wp2shell perfectly illustrates the blind spots of traditional auditing.
The annual pentest snapshots your security at a single point in time. A flaw published on July 17 doesn't exist in a report dated March, and between two audits you stay blind. Version scanners just read a version number, trivially spoofable, without ever testing the actual behavior of the batch endpoint. Finally, incomplete scope is almost always to blame: it is the forgotten subdomain (an old WordPress blog, an indexed staging environment) that falls first, and you first have to know it exists.
Detecting wp2shell doesn't require a destructive exploit. It requires mapping your entire perimeter, identifying WordPress instances, then actively but safely testing the batch route and the injection sink, continuously rather than once a year.
Flawfence detects wp2shell natively
At Flawfence, the class of vulnerabilities wp2shell belongs to (REST API abuse, route confusion, unauthenticated SQL injection) has been part of the scan engine from day one. It is not a module bolted on in a panic after disclosure, but the very foundation of our approach.
Concretely, against wp2shell, Flawfence:
- Automatically maps your perimeter. Every subdomain and every exposed WordPress instance is discovered, including the Shadow IT you had forgotten. See our guide on how to map your company's information system.
- Actively tests the
/wp-json/batch/v1endpoint (both clean and query-string routes) and detects the SQL injection sink via a non-destructive time-based method, without ever creating an admin account or writing to the database. - Validates every finding with AI, for zero false positives and only genuinely exploitable flaws, with context and CVSS scoring.
- Monitors continuously. The moment a new vulnerable version is deployed, or a subdomain appears, the alert fires, without waiting for the next pentest.
Response timeline
wp2shell shows just how short the window is between a flaw being disclosed and its mass exploitation: hours, not weeks. Here is how the events unfolded, and where Flawfence stepped in.

On the very day of disclosure, wp2shell detection was already live in our scan engine, and the first vulnerable instances surfaced an hour later. That is the whole difference between an audit that runs continuously and a pentest scheduled months in advance.
FAQ: wp2shell
What is wp2shell?
wp2shell is the nickname for an unauthenticated remote code execution in WordPress Core, resulting from chaining two vulnerabilities: CVE-2026-63030 (REST /wp-json/batch/v1 route confusion) and CVE-2026-60137 (SQL injection in the author__not_in parameter of WP_Query). An anonymous attacker can obtain a shell on a default installation.
Which WordPress versions are vulnerable?
The full RCE chain affects WordPress 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1. The SQL injection alone reaches back to 6.8. The patches are 6.9.5, 7.0.2 and 6.8.6.
Is wp2shell being actively exploited?
A working proof-of-concept has been public on GitHub since July 18, 2026, and a reproduction lab is available. Given the number of WordPress sites and the automation of attacks, mass exploitation should be considered imminent. Patch without delay.
How can I detect wp2shell without compromising my site?
Through time-based blind detection. You compare the response time of a normal request to one carrying a SLEEP payload, via the batch route. This method is non-destructive, as it creates no account and no content. It is the approach used by Flawfence's scan.
Is blocking /batch/v1 with a WAF enough?
No. It's a useful temporary mitigation, but it can be bypassed, and it doesn't address the underlying SQL injection. Be sure to block both forms of the route (/wp-json/batch/v1 and ?rest_route=/batch/v1). The only complete remediation remains updating.
Does Flawfence detect wp2shell?
Yes. Detecting REST API abuse and unauthenticated SQL injection has been part of the Flawfence engine from the start. The scan identifies your exposed WordPress instances and actively, safely tests their exposure to wp2shell, continuously.
Conclusion
wp2shell is a textbook case: two harmless-looking bugs on their own, a devastating chain, a massive attack surface, and a public PoC within hours. For security teams, the lesson is clear: WordPress security is not a once-a-year exercise, but a daily one, across every exposed asset, known or forgotten.
The good news is that detecting this class of flaw requires neither a destructive exploit nor a $50k pentest. It takes an exhaustive inventory of your perimeter and continuous monitoring that tests the real behavior of your endpoints. That is exactly what Flawfence does, for wp2shell as for the next flaw that doesn't yet have a name.
Check your exposure to wp2shell right now, it will only take 5 minutes.
Let’s discuss your external exposure
Request a personalized Flawfence demo and discover your real exposure level.
