One POST request now owns a default WordPress site. Patch to 7.0.2 today.

One POST request now owns a default WordPress site. Patch to 7.0.2 today.

On July 17, WordPress shipped 7.0.2, and it was not a normal point release. It closes a chain that researchers named wp2shell: two bugs that, stitched together, let an anonymous attacker run code on a default WordPress install. No login. No vulnerable plugin. No weird theme. One crafted POST to the REST API, and the server can end up with a new admin account and a shell answering to commands.

The chain is two CVEs. CVE-2026-63030 is a route/handler confusion in the REST API batch endpoint, rated critical, introduced back in 6.9. CVE-2026-60137 is a SQL injection in WP_Query's handling of author__not_in, rated high, and it reaches all the way back to 6.8. On their own, each is a headache. Chained, the batch-endpoint confusion unbinds validation from execution and feeds unvalidated input straight into the SQL injection, and you get pre-authentication remote code execution. WordPress enabled forced auto-updates for affected sites, which tells you how the core team read the risk. Patchstack says it is already seeing exploitation attempts in its logs.

Here is the version map. The full RCE chain hits 6.9.0 through 7.0.1. The 6.8 branch only carries the SQL injection, not the route confusion that makes it reachable without a login. Fixes landed in 6.8.6, 6.9.5, and 7.0.2, plus a 7.1 beta2 for anyone testing the next branch. The route confusion was reported by Adam Kues at Assetnote, the research arm of Searchlight Cyber, through WordPress's HackerOne program. The SQL injection was reported separately by TF1T, dtro, and haongo.

Why this one is worse than the usual WordPress CVE

Most WordPress vulnerabilities live in plugins. You can audit those, pin them, or rip them out. This one is in core, on a default install, reachable before authentication. That combination is rare, and it is exactly the profile attackers automate first. A pre-auth core bug in the platform that runs a big chunk of the web is a mass-scanning event, not a targeted one. Public proof-of-concept scripts appeared within a day or two of disclosure, and forced updates only reach sites that have auto-update turned on and actually phone home. Plenty do not.

There is an operational detail worth calling out. Reporting around the bug notes the RCE path is easiest to reach when a site is not running a persistent object cache, which is the default for a lot of small and mid-size installs. So the sites least likely to have a hardened setup are the ones most exposed. That tracks with what we see during pentesting engagements for gaming studios: the real risk usually sits in the boring default configuration, not the exotic edge case everyone spends their time worrying about.

What to do this week

First, patch. If you run WordPress 6.8 through 7.0.1 anywhere, move to 7.0.2 or the matching 6.9.5 or 6.8.6 backport now, not at the next maintenance window. Then confirm the update actually applied. Forced auto-updates can fail quietly behind a reverse proxy or on a locked-down filesystem, so check the reported version rather than trusting that the mechanism did its job.

Second, assume the time between 6.9's release and July 17 was an open window and look for signs someone walked through it. Check for admin users you do not recognize, unexpected files under wp-content/plugins/, and outbound connections from the web host you cannot explain. A clean patch does not undo a compromise that already happened, and this bug leaves an attacker with exactly the kind of persistent access that survives an update.

Third, put a control in front of the application, not just inside it. Cloudflare shipped an emergency WAF rule the day of disclosure. When we configure Cloudflare for clients who need DDoS protection and a WAF, a managed rule for a fresh core CVE like this buys hours or days of cover while a patch rolls across a fleet of sites. From the travel-platform work we've done, that virtual-patch window is often the difference between reacting in minutes and reacting after the fact.

For teams building on PHP more broadly, the real lesson is about trust boundaries. This chain worked because a validation layer and an execution layer disagreed about what the request contained. We see the same class of bug in custom apps all the time: input gets checked in one place and consumed in another, and the two drift apart until an attacker slips something between them. In our PHP and Docker work, the habit that catches it is treating every request as hostile until the exact layer that runs the query has re-validated it. Validation that happens three functions away from the database is not validation you can count on.

We run this kind of pre-auth review for enterprise clients with real compliance stakes, and the team's background is in finding the request that should not work but does. If "we patched, but we're not certain nothing got in first" sounds familiar, let's talk.

expert-analysisphpsecuritytech-newsvulnerabilitywordpress