WordPress shipped security updates on July 17, 2026, to address wp2shell, a pair of vulnerabilities reported to allow remote code execution without a login on affected default installations. The WordPress.org team also enabled forced background updates. By the morning of July 18, a working proof-of-concept exploit was publicly available on GitHub.

The full attack chain affects WordPress 6.9.0 through 6.9.4 and 7.0.0 through 7.0.1. The fixes are in 6.9.5 and 7.0.2. WordPress 6.8.x needs a separate update to 6.8.6 for the underlying SQL injection, although that branch isn't exposed to the full code execution chain.

The reported attack requires no plugin, account, or special configuration. A single crafted HTTP request can reach the vulnerable code in WordPress core. Sites using a persistent object cache reportedly have some protection against the full chain, but that isn't a sound reason to delay patching.

How the two vulnerabilities connect

CVE-2026-60137 is a SQL injection in WP_Query. Its author__not_in parameter is supposed to accept an array of integer user IDs. When given a string instead, the validation logic coerces the value rather than rejecting it. The raw value then reaches a database query without proper sanitization.

This flaw affects WordPress 6.8 and later. It explains why the 6.8 branch needs a security update even though it isn't vulnerable to the complete wp2shell chain.

CVE-2026-63030 supplies the route-confusion flaw that allows the SQL injection to become remote code execution. According to Hadrian's account of the discovery, it involves the REST API batch endpoint. That endpoint was introduced in WordPress 6.6, but the combination with this injection surface became reachable when WordPress 6.9 shipped on December 2, 2025.

The batch endpoint lets a caller bundle several REST sub-requests into one HTTP round-trip. It is available at /wp-json/batch/v1 and through /?rest_route=/batch/v1. The performance benefit is straightforward: fewer separate requests between a client and the server. The security problem is in how the handler tracks each sub-request and its permission check.

A parsing failure puts permission checks out of alignment

The batch handler maintains three parallel arrays:

  • $requests holds the raw requests.
  • $validation holds the results of permission checks.
  • $matches holds the route matched to each request.

These arrays need to stay aligned. The permission result at a given position must describe the same request and route as the entries at that position in the other arrays.

A sub-request that fails early parsing breaks that assumption. For example, a malformed URL such as the literal string http:: causes the handler to record a failure in $validation without adding a corresponding entry to $matches. The arrays are then off by one.

In the reported exploit, subsequent sub-requests are dispatched against the next route's handler while using the previous route's permission callback. An attacker can arrange the requests so that a privileged endpoint capable of writing data runs under the permission check for an endpoint that accepts anonymous callers.

The Hacker News describes the result as code execution from an anonymous HTTP request with no preconditions. CVE-2026-63030 has a reported CVSS score of 9.8, Critical. The stated exposure of a fresh WordPress 6.9 installation, without plugins or configuration changes, supports treating this as an urgent patch.

What the patch changes

Hadrian describes the core alignment fix as two lines of code. When a sub-request fails early parsing, the batch handler now inserts a placeholder into $matches. That keeps its positions aligned with the other arrays, so a failed request doesn't shift the permission checks for later requests.

A second change adds a re-entrancy guard through an is_dispatching() method. Its purpose is to prevent other batch-processing edge cases from producing the same class of misalignment.

The small fix addresses a long exposure window. As of July 18, WordPress 6.9 had been available for 228 days. Affected 6.9.x and 7.0.x installations could therefore have been exposed for months. The reported scale is tens of millions of sites, though an installed-site count doesn't establish how many meet every condition needed for exploitation.

Affected versions and the cache caveat

WordPress versionReported exposurePatched version
6.8.x before 6.8.6SQL injection, CVE-2026-60137; not the full RCE chain6.8.6
6.9.0 through 6.9.4Full wp2shell RCE chain6.9.5
7.0.0 through 7.0.1Full wp2shell RCE chain7.0.2

Sites running a persistent object cache, such as Memcached or Redis, reportedly aren't exploitable through the full RCE chain even on affected versions. This cache keeps application objects available across requests, and the reported explanation is that it changes the code path through which the injection fires. That protection is a consequence of the execution path, not a replacement for fixing the vulnerabilities.

The batch API is part of core's REST infrastructure and has been enabled by default since 6.6. Site owners don't have to opt into it. Blocking only /wp-json/batch/v1 also leaves the alternate /?rest_route=/batch/v1 form to consider. Any temporary filtering needs to account for both.

Background updates and the public probe

The WordPress.org team's decision to enable forced background updates was an unusually urgent response. Minor security releases normally use the background-update mechanism on sites where it hasn't been disabled. Even with this push, administrators shouldn't assume an installation received the patch. Sites with automatic updates disabled need a manual version check and update.

By the morning of July 18, the public GitHub proof of concept was described as working and non-destructive. Its probe sends a batch request with a malformed first entry, followed by a DELETE against /wp/v2/categories/0 and a POST to the block-renderer route. It is intended to confirm vulnerability without causing damage.

A diagnostic probe doesn't establish that malicious exploitation is already happening. It does make the vulnerable behavior publicly reproducible. Weaponized variants could follow quickly, which leaves little room for a routine, slow update cycle.

Checks for site operators

  1. Confirm the installed version. Update affected 6.9.x sites to 6.9.5 and affected 7.0.x sites to 7.0.2. Check the running version after the update rather than relying on an update notification.
  2. Include 6.8.x installations. They need 6.8.6 for the SQL injection fix, even without exposure to the full RCE chain.
  3. Check the update workflow. Agencies and developers may disable automatic updates to test releases first. That process still needs a way to deploy critical security patches within hours rather than weeks.
  4. Review web application firewall coverage. Cloudflare published WAF rules for both CVEs shortly after the patches were released. Sites behind Cloudflare should verify that those rules are active. Customers of other WAF vendors should check whether equivalent coverage is available.
  5. Investigate unusual batch traffic. The reported pattern is a POST to either batch endpoint form with a malformed first sub-request URL. Review anomalous activity from the preceding 24 hours. Detecting the malformed entry requires visibility into the request body, not just the URL.

The review problem behind the bug

The batch endpoint's purpose is reasonable. The failure comes from an assumption that several arrays remain synchronized through every parsing and dispatch path. A review limited to input sanitization inside individual functions can miss that relationship.

Finding this class of flaw requires following a request through the whole batch lifecycle, including early failures. The key invariant, a condition that must remain true throughout processing, is that every handler runs with the permission result belonging to its own request. Static analysis alone shouldn't be assumed to establish that relationship.

For platform operators, the relevant exposure includes REST endpoints, batch processors, and permission dispatchers supplied by dependencies, not just application code written locally. Operational coverage needs to match that scope: prompt critical updates, active application-layer filtering where available, and monitoring that includes REST API anomalies rather than only familiar SQL injection patterns in form fields.

Technical teams should verify that each affected installation is running a patched release. They can then use the proof of concept to understand the failed permission boundary and assess whether their monitoring would have noticed it.