> ## Documentation Index
> Fetch the complete documentation index at: https://blog.onetsolutions.net/llms.txt
> Use this file to discover all available pages before exploring further.

# wp2shell (CVE-2026-63030): Unauthenticated Remote Code Execution in WordPress Core

> A batch-API routing flaw chained with an SQL injection gives full site takeover without credentials. Patched in 6.9.5 and 7.0.2, and exploited in the wild.

*Published on August 8, 2026*

<Warning>
  **CVE-2026-63030 — "wp2shell", CVSS 9.8.** No account, no password, no user interaction: a single request chain takes over the site. Exploitation is confirmed in the wild and the flaw is in CISA's KEV catalog. If you run WordPress 6.9.x or 7.0.x, update before you read the rest.
</Warning>

## What is affected

This is a core vulnerability, not a plugin one. No amount of plugin hygiene protects you.

| Branch        | Vulnerable                             | Patched   |
| ------------- | -------------------------------------- | --------- |
| WordPress 6.9 | 6.9.0 → 6.9.4                          | **6.9.5** |
| WordPress 7.0 | 7.0.0 → 7.0.1                          | **7.0.2** |
| WordPress 7.1 | Beta 1 and earlier                     | Beta 2    |
| WordPress 6.8 | 6.8.0 → 6.8.5, for CVE-2026-60137 only | 6.8.6     |

Sites on 6.8.x are not exposed to the full chain — the batch-API flaw was introduced in 6.9 — but they still need 6.8.6 for the SQL injection half.

## How the chain works

Two flaws, neither of which is enough on its own.

**CVE-2026-63030** is a logic bug in the REST API batch processor, at `/wp-json/batch/v1`. Validation and execution run in two separate loops. When `wp_parse_url()` fails on a sub-request path, the error is appended to the validation array but not to the matches array. The two arrays fall out of alignment, and from that point on every subsequent sub-request is dispatched under the wrong handler.

That misalignment is the whole trick: a request gets executed by a handler that never validated its parameters.

**CVE-2026-60137** is an SQL injection in the `author__not_in` parameter of `WP_Query`, scored 5.9 on its own — the kind of finding that would normally sit in a backlog.

Chained, the routing confusion delivers unvalidated parameters into the injectable query. The published path goes from there to creating an administrator account, and from an administrator account to code execution through the plugin installer. Full site takeover, no credentials at any step.

<Info>
  This is a good illustration of why a medium-severity finding is not a low-priority one. CVE-2026-60137 alone is a 5.9. Given a routing bug to pair with, it becomes half of a 9.8.
</Info>

## Timeline, and why it matters

* **17 July 2026** — public disclosure via a GitHub Security Advisory. Public proof-of-concept exploits appeared on GitHub within hours.
* **21 July 2026** — CISA adds both CVEs to the Known Exploited Vulnerabilities catalog.
* **22 July 2026** — full technical write-up of the chain published.

A KEV listing means exploitation has been observed against real systems, not merely predicted. The gap between disclosure and working public exploit was measured in hours, which leaves no room for a "we will patch next sprint" posture.

## Updating

WordPress.org pushed forced automatic updates to affected installations that had auto-updates enabled. Do not assume this covers you — sites with auto-updates disabled, sites under version control, and sites where the core files are not writable were not updated.

Check what you are actually running:

```bash theme={null}
# From the site root, with WP-CLI
wp core version

# Without WP-CLI, the version lives in this file
grep "wp_version =" wp-includes/version.php
```

Then update:

```bash theme={null}
# Update core to the latest release of your branch
wp core update

# Database schema migration, required after a core update
wp core update-db

# Confirm
wp core version
```

If you manage your site through cPanel, WordPress Toolkit and [Softaculous](https://help.onetsolutions.net/web-hosting/softaculous) both expose a one-click core update and a per-site version list — the fastest way to audit several sites at once.

<Note>
  Take a backup before updating, not after. If the update goes badly you want a rollback point; if you turn out to have been compromised, you want a copy of the evidence.
</Note>

## Checking whether you were hit

Patching closes the door. It does nothing about anyone already inside. Three checks, in order of usefulness:

**Unexpected administrator accounts.** The published chain creates one. Look at every account with elevated rights and the date it was created.

```bash theme={null}
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered
```

**Requests to the batch endpoint.** The endpoint has legitimate uses, so volume and source matter more than presence.

```bash theme={null}
# Adjust the path to your access log
grep "batch/v1" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
```

**Files that changed recently.** A web shell dropped through the plugin installer lands in `wp-content`.

```bash theme={null}
# PHP files modified in the last 30 days, most recent first
find wp-content -name "*.php" -mtime -30 -printf "%TY-%Tm-%Td %p\n" | sort -r | head -40
```

Compare that list against your own deployments. A PHP file in `wp-content/uploads` is essentially never legitimate.

If any of these turn something up, treat the site as compromised: restore from a backup predating the first suspicious event, then update, then rotate every password and API key. Cleaning a live compromised site file by file rarely ends well — see our [checklist for hardening WordPress after this incident](/en/secure-wordpress-after-wp2shell).

## The wider point

WordPress core vulnerabilities of this severity are rare — the attack surface that usually gets exploited is plugins and themes. That rarity cuts both ways: because core is trusted, core updates are more often postponed on "stable" production sites, and a site that has not been touched in a year is exactly the one still sitting on 6.9.2.

Automatic updates for minor core releases are on by default for a reason. If you disabled them, this is the incident that should make you reconsider.

Questions about a site hosted with us? [Our team is available](https://help.onetsolutions.net/console/support) from your client area.
