wp2shell: Critical Unauthenticated RCE Chain in WordPress Core
By Anna Balabushko, Mia Skibinski, Aileen Ward
wp2shell is a critical pre-authentication remote code execution chain in WordPress Core that requires no plugins and affects default installations. In-the-wild exploitation has been confirmed, and administrators must immediately update to WordPress 6.9.5, 7.0.2, or 7.1 beta2.
The vulnerability was publicly disclosed on July 17, 2026, and patched the same day in WordPress 7.0.2, 6.9.5, and 7.1 beta2. Due to its severity, WordPress.org also enabled forced auto-updates across affected sites (WordPress, 2026).
The flaw chains together two bugs: a REST API batch-route confusion vulnerability in the default /wp-json/batch/v1 endpoint, tracked as CVE-2026-63030, which smuggles attacker-controlled input into an SQL injection in WP_Query's author__not_in parameter, tracked as CVE-2026-60137 (The Hacker News, 2026). When chained, the vulnerabilities allow an anonymous HTTP request to result in the creation of a rogue administrator account and ultimately lead to code execution.
The batch-route confusion bug was discovered and responsibly disclosed by Adam Kues of Assetnote/Searchlight Cyber, while the SQL injection was reported separately by TF1T, dtro, and haongo (WordPress, 2026). Unlike most WordPress security issues, which originate in plugins or themes, wp2shell exists in WordPress Core, affects bare installations, and is reachable through the batch endpoint, which is enabled by default (Eye Security, 2026).
Vulnerability Type (CWE)
The official WordPress GHSA advisories assigned no CWEs. The CWEs below come from the WPScan CNA and CISA-ADP CVE records (WPScan, 2026; CISA-ADP, 2026).
CWE-89: Improper Neutralization of Special Elements used in an SQL Command (SQL Injection) Assigned to CVE-2026-60137 by both WPScan (CNA) and CISA-ADP. The author__not_in query var accepts a scalar string that is interpolated directly into raw SQL with no escaping when the input is not an array.
CWE-436: Interpretation Conflict Assigned to CVE-2026-63030 by both WPScan (CNA) and CISA-ADP. The batch endpoint's parallel $matches / $validation array desync lets a validated request execute under a different request's handler, bypassing parameter sanitization and the method allow-list.
Impacted Versions
The two vulnerabilities affect different WordPress versions, which determines the level of exposure. The SQL injection affects versions dating back to 6.8, while the route-confusion flaw—and therefore the full RCE chain—only affects versions 6.9 and later.
| Version Range | Exposure | Fixed In |
|---|---|---|
| < 6.8.0 | Not affected | — |
| 6.8.0 – 6.8.5 | SQL injection only (no RCE chain) | 6.8.6 |
| 6.9.0 – 6.9.4 | Full unauthenticated RCE chain | 6.9.5 |
| 7.0.0 – 7.0.1 | Full unauthenticated RCE chain | 7.0.2 |
| 7.1 beta | Full unauthenticated RCE chain | 7.1 beta2 |
Narrowing condition
The code-execution path depends on the site not using a persistent external object cache, such as Redis or Memcached, because those caches change how the underlying query is constructed. According to Cloudflare, this may reduce the blast radius for some larger deployments, but it provides no protection for standard WordPress installations, which do not use an external object cache by default. This is an incidental side effect, not a fix, and it does not prevent exploitation of the SQL injection. A default installation running an affected version of WordPress Core is sufficient exposure.
Mitigation Steps
Check if your instance is vulnerable at wp2shell.com/. If it is, update immediately—this is the only effective fix. Do not assume the forced auto-update reached your site; confirm the installed WordPress version directly in your dashboard.
| WordPress Branch | Mitigation |
|---|---|
| 6.8.x | Update to 6.8.6 (patches SQLi only) |
| 6.9.x | Update to 6.9.5 |
| 7.0.x | Update to 7.0.2 |
| 7.1 beta | Update to 7.1 beta2 |
If you cannot update immediately, all temporary mitigations should focus on preventing unauthenticated requests from reaching the batch endpoint. These measures may disrupt legitimate REST integrations and should be treated only as short-term stopgaps (Searchlight Cyber, 2026):
- Block both
/wp-json/batch/v1and requests using the query parameterrest_route=/batch/v1at the WAF. Cloudflare's managed WAF blocks the exploit chain for sites behind its network. However, blocking the endpoint can break the WordPress Block Editor (Gutenberg) and several modern plugin dashboards that rely heavily on the batch API to save data. Use this only as an absolute last resort if patching is delayed. - Install the Disable WP REST API plugin to block unauthenticated REST API access entirely.
- Deploy a must-use plugin that rejects unauthenticated requests to
/batch/v1atrest_pre_dispatch. The WordPress advisory provides an example implementation (WordPress, 2026):
<?php
/**
* Plugin Name: Disable Unauthenticated REST Batch API
* Description: Requires an authenticated WordPress user for REST batch requests.
* Version: 1.0.0
* Requires at least: 5.6
* License: GPL-2.0-or-later
*/
defined( 'ABSPATH' ) || exit;
function wporg_require_authentication_for_rest_batch( $result, $server, $request ) {
if ( '/batch/v1' !== strtolower( untrailingslashit( $request->get_route() ) ) || is_user_logged_in() ) {
return $result;
}
return new WP_Error(
'rest_batch_authentication_required',
'Authentication is required to use the batch API.',
array( 'status' => 401 )
);
}
add_filter( 'rest_pre_dispatch', 'wporg_require_authentication_for_rest_batch', -1000, 3 );
Reset passwords even on SQL-injection-only sites running WordPress 6.8.x. The SQL injection can read the wp_users table, meaning an attacker may already have obtained password hashes. Force a password reset for every account, prioritizing administrators, rotate all API keys, and instruct users to change any passwords they reused elsewhere. Patching alone does not protect sites that may have been compromised before the update was applied (Eye Security, 2026).
Linux Forensics & Incident Response
Let the database rows and file timestamps lead; use logs only to corroborate source IP, timing, and entry point.
- Database (primary evidence) Dump
wp_users,wp_usermeta, andwp_optionsfrom a copy viamysqldump, not the live DB. Look for unexpected admin rows and the artifacts above. - Web root (
/var/www/htmlor the site docroot) Hunt recently modified/created PHP files —find DOCROOT -name "*.php" -newermt "2026-07-16" -printf "%TY-%Tm-%Td %TH:%TM %p\n"is a fast first pass. For a faster, highly reliable check of core file integrity, runwp core verify-checksumsvia WP-CLI. This will instantly flag any core files the attacker modified to plant a backdoor. - Web server logs (
/var/log/apache2/,/var/log/nginx/) Access log shows the batch entry point and any later webshell hits, but not the injection. The Apacheerror.logcan leak an injected query if a UNION threw a SQL syntax error. - PHP / FPM logs (
/var/log/php*-fpm.log, per-directoryerror_log) Fatal errors/warnings around the intrusion window corroborate. - Sessions & temp (
/var/lib/php/sessions/,/tmp) Dropped payloads or staging files. - System & auth (
/var/log/auth.logorsecure,~/.bash_historyforwww-dataand service accounts,/etc/crontab,/etc/cron.*, per-user crontabs,/etc/passwd) Persistence and added local accounts.
Preserve evidence before beginning remediation. Create read-only images of the database and web root before making changes.
Exploit Process
A fully functional proof of concept has been publicly available on GitHub since shortly after disclosure and has been independently verified to execute the complete attack chain against an affected default installation (Icex0, 2026; Eye Security, 2026). Searchlight delayed publishing its technical analysis to give defenders the weekend to respond. However, because WordPress Core is open source and the security release identified the modified files, other researchers were able to reverse-engineer the patch and publish details of the exploit mechanism within a day (The Hacker News, 2026). The exploit chain proceeds as follows (Searchlight Cyber, 2026):
- Route confusion (CVE-2026-63030) The unauthenticated
/wp-json/batch/v1endpoint validates and executes sub-requests across three parallel arrays —requests,matches, andvalidation— insideWP_REST_Server::serve_batch_request_v1(). A malformed sub-request appends tovalidationbut not matches (the continue skips it), shifting the arrays out of step by one. A validated request then executes under a different request's handler. - Bypass the method allow-list The batch API rejects GET, but the SQLi sink is GET-only. The PoC nests the batch call recursively — the outer desync means the inner request's method field is never validated — allowing a GET to reach
/wp/v2/posts. - Reach the SQLi sink (CVE-2026-60137) Via the desync,
author_excludelands inWP_Query'sauthor__not_in. A scalar string (rather than an array) skips theabsintsanitization and is interpolated directly into raw SQL. A payload like0) OR 1=1 --confirms injection; UNION-based reads leak arbitrary DB values as a forgedwp_posts-shaped row. - Escalate to admin without cracking hashes UNION-forged fake posts poison the in-memory post cache; the oEmbed feature is abused to fabricate real
oembed_cacherows, which are then recast (via cache/DB reconciliation and a self-parent cycle-detection gadget) into acustomize_changesetthat applies changes with administrator (user ID 1) authority. A craftedparse_requesthook replays the batch request under the temporarily assumed admin role, allowing an embedded "create administrator" sub-request to succeed on the second pass. - Code execution The attacker logs in as the generated administrator and uploads a backdoor plugin from a ZIP, achieving RCE.
Steps 1 through 4 occur before authentication. Only the final plugin-upload step is authenticated as the attacker-created admin. A complete exploit may involve anywhere from a dozen to several dozen requests, with the critical actions embedded in batch POST bodies that rarely surface in access logs (Eye Security, 2026).
Exploitation Status
Multiple security firms confirmed in-the-wild exploitation of unpatched WordPress instances on July 20, 2026 (SecurityWeek, 2026). This progression was expected: mass exploitation of WordPress vulnerabilities is well established, the proof of concept and patch are both public, and the bug affects default configurations—a combination that has historically led to widespread opportunistic scanning (The Hacker News, 2026). Rapid7 also released unauthenticated vulnerability checks for InsightVM and Nexpose in its July 20 content release (Rapid7, 2026). Treat this vulnerability as actively exploited and prioritize remediation immediately.
Timeline
| Date | Event |
|---|---|
| Prior to July 17, 2026 | Vulnerabilities are disclosed to WordPress by Adam Kues (Searchlight Cyber / Assetnote) for CVE-2026-63030 and researchers TF1T, dtro, and haongo for CVE-2026-60137 |
| July 17, 2026 | WordPress releases security updates 6.9.5 and 7.0.2 to fix both flaws, enabling forced automatic updates globally |
| July 18, 2026 | Public Proof-of-Concept (PoC) exploit scripts for the full "wp2shell" chain begin circulating on GitHub |
| July 20, 2026 | In-the-wild exploitation of unpatched WordPress instances is confirmed by multiple security firms. |
TTPs & IOCs
Because rogue accounts and plugin directories use a fixed prefix followed by a random suffix, detection should match the prefix rather than an exact string. The database is the primary source of evidence, as a successful exploit may leave little or no trace in standard web server access logs. The absence of matching log entries should not be treated as evidence that a site is safe (Eye Security, 2026).
These indicators are based on the public proof of concept rather than the vendor advisory. At the time of writing, the primary disclosures do not include wp2shell-specific indicators of compromise, meaning a modified version of the exploit may not match them (SOCRadar, 2026).
Network-based:
- Requests to the exploit endpoint:
/wp-json/batch/v1and/?rest_route=/batch/v1. - Batch POST bodies containing nested
requestsarrays and theauthor_excludeparameter carrying a scalar string value.
Host / database artifacts (residual evidence the PoC leaves behind):
- Rogue admin logins matching
wp2_*orw2s_*. - Rogue admin email domains
@wp2shell.invalidor@wp2shell.shellcode.lol. oembed_cacheloopback rows inwp_posts.customize_changesetposts with very high parent IDs.- Orphaned
usermetarows and user-ID gaps inwp_users/wp_usermeta. - Unexpected administrator rows; tampered
active_plugins,siteurl/home, or injected_transient_/ object-cache entries inwp_options.
Note the PoC self-cleans the rogue admin and webshell but leaves the oEmbed cache rows and changeset/request posts — those are your durable detection anchors (Eye Security, 2026).
Centripetal's Perspective
A self-cleaning exploit chain like wp2shell illustrates why post-incident detection is a weak defense against pre-authentication vulnerabilities in WordPress Core. The critical steps are embedded in batch POST bodies that rarely appear in standard web server access logs, while the public proof of concept removes both the rogue administrator account and webshell after execution. This may leave only the oembed_cache and customize_changeset rows as the only potential residual evidence.
Because the intrusion is designed to leave minimal host-based traces, the most reliable point of control is the network edge, where the exploit request can be blocked before it reaches the vulnerable endpoint.
wp2shell is an unauthenticated, pre-authentication RCE vulnerability in WordPress Core, with a publicly available working exploit against a default installation. Confirm that your site is running a fixed release—6.8.6, 6.9.5, 7.0.2, or 7.1 beta2, depending on your branch. Verify the installed version directly; do not assume the forced update was successfully applied.
Then address the more difficult question: was the site compromised before it was patched? Run a compromise assessment, review all administrator accounts, and collect the artifacts identified above. Finding no indicators reduces the likelihood of compromise, but it does not prove the site was never accessed.
Resources
- Eye Security — wp2shell Defenders' Guide
- https://github.com/Icex0/wp2shell-poc
- https://www.aikido.dev/blog/unauthenticate d-rce-in-wordpress-wp2shell
- https://thehackernews.com/2026/07/n ew-wp2shell-wordpress-core-flaw-lets.html
- GHSA-ff9f-jf42-662q
- GHSA-fpp7-x2x2-2mjf
- https://wordpress.org/news/2026/07/wordpress-7-0-2-r elease/
- Searchlight Cyber research
- https://nvd.nist.gov/vuln/detail/CVE-2026-60137
- https://nvd.nist.gov/vuln/detail/CVE-2026-63030
- https://wp2shell.com/
- Picus Security
- Rapid7
- SOCRadar
- SecurityWeek
Know what’s coming. Stop what’s next.
Sign up for our free threat alert bulletin service here.
The Cybercrime Barrier Your Organization Deserves
Sign up for a custom demonstration from our security team of how we bring together the best minds and most complete collection of threat intelligence to provide you with a shocking level of relief.