Back to Blog
Threat Intelligence

wp2shell: the WordPress core RCE, and what to do about it

Cypra Team
25 July 2026
11 min read

wp2shell: the WordPress core RCE, and what to do about it

On 17 July 2026, WordPress shipped emergency releases for two vulnerabilities that chain into pre-authentication remote code execution against a default installation. No plugins. No credentials. No user interaction.

The chain, named wp2shell by its finder, is the kind of bug that is supposed to be extinct in a codebase this heavily audited. It is not in some abandoned gallery plugin — it is in WordPress core, in a REST API feature that has shipped since 5.6.

Within days, public proof-of-concept exploits appeared, both CVEs were added to CISA's Known Exploited Vulnerabilities catalogue, and in-the-wild exploitation was reported. If you run WordPress and you have not confirmed your version since mid-July, treat that as an open question you need to close today.

The short version

What Two chained flaws in WordPress core: a REST API batch-route confusion (CVE-2026-63030) and a SQL injection in author__not_in (CVE-2026-60137)
Impact Unauthenticated remote code execution — full compromise of the site and its database
Preconditions None. A stock install with no plugins is exploitable
Patched 17 July 2026, in 6.8.6, 6.9.5, 7.0.2 and 7.1 Beta 2
Exploited Yes — public PoCs and confirmed in-the-wild exploitation

Are you affected?

Branch Status Update to
Below 6.8 Not affected
6.8.0 – 6.8.5 SQL injection only — not exploitable to RCE 6.8.6
6.9.0 – 6.9.4 Both flaws — full RCE chain 6.9.5
7.0.0 – 7.0.1 Both flaws — full RCE chain 7.0.2
7.1 beta Both flaws — full RCE chain 7.1 Beta 2

The nuance on the 6.8 branch matters and is widely reported wrong. The batch-route confusion was introduced in 6.9, so 6.8 sites cannot be driven to code execution through this chain. They are still exposed to the SQL injection on its own, which is quite enough to leak your password hashes. Update to 6.8.6 regardless — just do not let a "we are on 6.8, we are fine" reading of a headline stop you.

Note also that advisories disagree on severity scoring: the GitHub advisory rates it Critical while carrying a CVSS of 7.5, other trackers put it at 9.8, and some bulletins rate the SQL injection component Medium. Do not let that argument reach your change-approval meeting. Public exploit code plus KEV listing plus confirmed exploitation is the decision input. The number is a footnote.

How the chain works

The WordPress REST API exposes a batch endpoint that lets a client bundle several sub-requests into one call. Its validation logic walks the batch in two separate loops: the first validates and sanitises every sub-request and records the result, the second checks those results, runs permission callbacks, and executes.

The flaw is an indexing mistake between those two loops. When a sub-request path fails to parse, the error is appended to the validation array — but a continue skips the corresponding append to the matches array. The two arrays fall out of step, and from that point on every subsequent sub-request is validated as one endpoint and executed as another.

That desynchronisation is the whole game, because WordPress's parameter sanitising lives in validation. The author__not_in query parameter is safe when it arrives as an array, since every element is forced through absint(). Supply it as a scalar string and it is concatenated straight into SQL. Reached directly, the endpoint's type validation rejects that. Reached through the desync, validation belonged to a different route and never applied — yielding pre-authentication UNION-based SQL injection.

From there the finder built a genuinely inventive escalation: poison WordPress's in-memory post cache with fabricated post objects via the UNION injection, persist them into real database rows by abusing the oembed_cache behaviour, then use a forged customize_changeset — a post type that temporarily assumes a designated user_id when applied — to execute as administrator. A final trick replays the original batch request while holding that administrator context, at which point creating an admin account and uploading a plugin is trivial. Plugin upload means arbitrary PHP. That is your shell.

We are describing the mechanism rather than reproducing a working payload. The full chain, including copy-paste request bodies, is already public in Searchlight Cyber's writeup linked below; there is nothing defenders gain from us republishing it, and we would rather this page be useful to the people patching than to the people scanning.

The defensive lesson generalises well beyond WordPress. If your validation and your execution walk the same list separately, they can fall out of step, and every guarantee validation was supposed to provide evaporates. We see this pattern in custom API gateways, bulk endpoints, and job queues far more often than anyone expects. If you have written a batch endpoint, go and read it with this bug in mind.

What to do now

1. Patch, and then verify the patch landed. WordPress enabled forced updates through the auto-update system for affected versions, which covers a large share of the internet automatically. It does not cover you if auto-updates are disabled, if your version is pinned in a container image or a Composer manifest, if the site is deployed from a build pipeline, or if a managed host has its own release train. Check the running version on each internet-facing site rather than assuming.

2. If you cannot patch within the hour, block the endpoint — carefully. The batch API is reachable two ways, and blocking only the pretty permalink form leaves the door open:

  • /wp-json/batch/v1
  • /?rest_route=/batch/v1

Block both at your WAF. Blocking anonymous REST API access, or dropping unauthenticated batch requests via the rest_pre_dispatch filter, are stronger versions of the same idea. All of these are stopgaps to buy hours, not a substitute for the update.

3. Assume the patch is not remediation. This is the part organisations get wrong. Exploitation was under way while sites were still unpatched. Updating core evicts nobody who already installed a plugin backdoor, created an administrator, or dropped a web shell — those survive the update perfectly happily. If your site was internet-facing and unpatched during the exposure window, patching is step one of an investigation, not the end of one.

Checking whether you were already hit

Work through this on any site that was exposed:

  • Access logs. Look for POST requests to /wp-json/batch/v1 or ?rest_route=/batch/v1, especially with deeply nested requests arrays or malformed paths. The chain requires recursive batch calls and a deliberately unparseable path to trigger the desync, so the traffic looks distinctly odd next to legitimate batch usage.
  • Administrator accounts. Enumerate every account with admin capability and confirm each one against a person. Check registration dates against your exposure window.
  • Plugins and files. Look for plugins nobody recognises or installed, and for new or modified PHP files under wp-content — particularly in uploads directories, where PHP has no business existing.
  • Suspicious post rows. This chain leaves unusual oembed_cache and customize_changeset rows behind. Unexpected entries of those post types, correlated in time with odd batch traffic, are a strong signal.
  • Persistence beyond the webroot. Scheduled tasks, cron entries, modified core files, and database-level changes all outlive a core update and a plugin removal.

Absence of evidence here is weak evidence of absence — logs rotate, and a competent operator cleans up. If the site handles personal data or payments, the standard of proof you need is higher than a quick grep.

The part that should worry you more than the bug

wp2shell was not found by a team grinding through a code audit. It was found by pointing a large language model at WordPress core in a multi-agent setup, running it for a few hours at a total cost of around 25 dollars. The researcher deliberately withheld changelogs and git history to force first-principles analysis of the code rather than diffing for known-weak spots.

It is worth being precise about what that does and does not mean. This was a skilled vulnerability researcher directing the model, with an adversarial verification harness and the judgement to recognise a promising desync and drive it seven stages further into a working chain. The model did not wander off and produce an exploit unattended, and one impressive result is not a trend.

But the economics have moved, and they have moved in a direction that matters. Finding a pre-authentication RCE in one of the most-reviewed codebases on the internet no longer necessarily requires a specialist team and months of effort. It can require an afternoon, a good harness, and pocket change. Whatever your threat model assumed about the cost of discovering novel bugs in your own stack, that assumption now needs revisiting — and so does the comfortable belief that the mature dependency you inherited has already had all its bugs found by someone else.

The defensive side of that coin is that the same tooling is available to you. This is exactly the work we do in our AI security practice: using these techniques to review your own code before someone else does, and assessing the risk of the AI systems you are deploying.

Key takeaways

  • Update now to 6.8.6, 6.9.5 or 7.0.2, and verify the running version on every internet-facing site rather than trusting auto-update
  • 6.8 is not exempt — it dodges the RCE chain but not the SQL injection
  • Block both forms of the batch endpoint if you need hours before you can patch, and treat that as a stopgap
  • Patching is not remediation — if you were exposed and unpatched, investigate for admin accounts, rogue plugins, web shells and odd oembed_cache / customize_changeset rows
  • Audit your own batch endpoints for the same validate-then-execute desynchronisation pattern
  • Reprice your assumptions about bug discovery — this chain cost about 25 dollars of compute to find

If you are running WordPress and are not certain whether you were compromised during the exposure window, that uncertainty is worth resolving properly. Our digital forensics team handles compromise assessments and incident response, and our penetration testing team can tell you what else on your perimeter looks like this. If you believe you are dealing with an active incident, mark it as such on our contact form and we will prioritise it.

Sources

wordpressrcecve-2026-63030incident-responseai-security