Add WordPress pre-auth RCE exploit (CVE-2026-63030 + CVE-2026-60137) - #21760
Add WordPress pre-auth RCE exploit (CVE-2026-63030 + CVE-2026-60137)#21760M4xSec wants to merge 1 commit into
Conversation
Exploit module for WordPress REST API Batch Route Confusion chained with Blind SQLi to achieve unauthenticated RCE on WP 6.8.0-6.8.5, 6.9.0-6.9.4, 7.0.0-7.0.1. Includes Cloudflare WAF bypass and self-cleaning webshell with stealth mode.
|
is this different than #21686 ? Doesn't look to be, so just wanted to be sure. |
|
Hey @h00die, thanks for the heads up on #21686. I saw Crypto-Cat's module got merged nice work on their end. My module covers the same CVE chain but has several differences that I think add value on top of what's already merged: WAF Bypass: built-in Cloudflare/ModSecurity bypass via JSON unicode escaping of SQL keywords, colon primer path, Stealth Shell: two shell modes: a stealth webshell that authenticates via a custom HTTP header and receives commands in POST body (never logged in access logs), and a classic REST API route shell. The merged module only has one approach. Separate Scanner Module: I also submitted #21761 as a standalone non-destructive auxiliary scanner that does version fingerprinting + optional blind SQLi confirmation without any exploitation. Useful for bulk assessment without touching anything. Additional check() hardening: my |
|
@Nayeraneru Note the test env here has a vuln docker for wordpress. haven't tried it yet, but that may get you through some of the setup issues. Many plugins are just wget+unzip after that (if they dont have post-install setup). |
Thanks for the pointer @h00die ! I was trying to check the repo in the PR description out to see the entries in the compose file and watch the boot sequence live |
|
@Nayeraneru its public now |
|
tried it @h00die also not really a swap-in for my existing WP module either way, since this PR's chain (unauth REST API + blind SQLi) is a totally different vuln class from one thing I will borrow now regardless: their setup.sh retries the install until the DB's actually ready, vs. my Provisioner which fires once which is cheap, low-risk improvement to make either way |
There was a problem hiding this comment.
Pull request overview
Adds a new Metasploit exploit module and accompanying documentation for a chained WordPress Core pre-auth attack path (REST batch route confusion + blind SQLi) that culminates in plugin-based code execution, targeting specific vulnerable WordPress version ranges.
Changes:
- Introduces
exploit/multi/http/wp_wordpress_batch_rceimplementing version fingerprinting, SQLi timing oracle/exfiltration, DB row forgery, admin creation, and plugin-upload shell execution/cleanup. - Adds end-user module documentation with setup steps, options, and example scenarios (including a WAF-bypass mode).
Impact Analysis: isolated change; no meaningful downstream impact identified from diff.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| modules/exploits/multi/http/wp_wordpress_batch_rce.rb | New exploit module implementing the described WordPress pre-auth RCE chain. |
| documentation/modules/exploit/multi/http/wp_wordpress_batch_rce.md | New documentation describing vulnerable versions, options, and usage scenarios. |
Suppressed comments (2)
modules/exploits/multi/http/wp_wordpress_batch_rce.rb:728
- Suggestion: Problem: JSON parsing uses
JSON.parse(res.body) rescue {}, which silently hides malformed/empty JSON and can lead to false negatives/marker mismatches. Impact: makes failures harder to debug and inconsistent with response helpers. Fix: useres.get_json_documentand validate a Hash before accessing keys.
if res && res.code == 200
result = JSON.parse(res.body) rescue {}
if result['marker'] == marker
print_good("RCE CONFIRMED — whoami: #{result['output'].strip}")
end
modules/exploits/multi/http/wp_wordpress_batch_rce.rb:751
- Suggestion: Problem: second JSON parse site also uses
JSON.parse(res.body) rescue {}and swallows parse errors. Impact: can mask server-side failures and make payload output handling unreliable. Fix: useres.get_json_documentand validate a Hash.
if res && res.code == 200
result = JSON.parse(res.body) rescue {}
if result['marker'] == marker && !result['output'].to_s.empty?
print_good("Payload output: #{result['output'].strip}")
end
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| ## | ||
| # This module requires Metasploit: https://metasploit.com/download | ||
| # Current source: https://github.com/rapid7/metasploit-framework | ||
| ## |
| return CheckCode::Vulnerable("SQLi confirmed but version extraction empty") | ||
| end | ||
| print_good("DB version: #{db_ver}") | ||
|
|
||
| CheckCode::Vulnerable("WordPress #{ver} | DB: #{db_ver} | SQLi confirmed") |
| def target_host | ||
| datastore['VHOST'].to_s.empty? ? datastore['RHOST'] : datastore['VHOST'] | ||
| end | ||
|
|
||
| def full_uri(path = '') | ||
| proto = datastore['SSL'] ? 'https' : 'http' | ||
| port_str = '' | ||
| if (datastore['SSL'] && datastore['RPORT'] != 443) || | ||
| (!datastore['SSL'] && datastore['RPORT'] != 80) | ||
| port_str = ":#{datastore['RPORT']}" | ||
| end | ||
| "#{proto}://#{target_host}#{port_str}#{normalize_uri(base_uri, path)}" | ||
| end |
| unless res && res.code == 200 | ||
| fail_with(Failure::UnexpectedReply, "Could not fetch posts list") | ||
| end | ||
| posts = JSON.parse(res.body) rescue [] |
| `/wp-json/` permalink path instead of `?rest_route=`, JSON unicode escaping of | ||
| SQL keywords (`SLEEP` instead of `SLEEP`), and | ||
| Origin/Referer headers. Default: `false` |
| def wp_hash_check(password, stored_hash) | ||
| if stored_hash =~ /\A\$wp\$2[aby]\$/ | ||
| bcrypt_hash = stored_hash.sub(/\A\$wp\$/, '$').sub(/\A\$2y\$/, '$2a$') | ||
| begin | ||
| gem 'bcrypt' |
Summary
This adds a new exploit module for two chained WordPress Core vulnerabilities:
author__not_inparameterChaining these achieves unauthenticated Remote Code Execution on WordPress 6.8.0–6.8.5, 6.9.0–6.9.4, and 7.0.0–7.0.1. Fixed in 6.8.6, 6.9.5, and 7.0.2.
Attack Chain
parse_request()forges an administrator accountModule Features
check()method with full SQLi confirmation (timing differential + DB version extraction)Files
modules/exploits/multi/http/wp_wordpress_batch_rce.rb— exploit moduledocumentation/modules/exploit/multi/http/wp_wordpress_batch_rce.md— module documentation with setup instructions and sample scenariosVerification
Tested against WordPress 6.9.0 on Docker (MariaDB 11.4) with and without ModSecurity WAF:
Test Environment
Docker lab available at: https://github.com/M4xSec/wp2shell-lab