2013-01-25 19:44:49 +00:00
|
|
|
##
|
2014-10-17 16:47:33 +00:00
|
|
|
# This module requires Metasploit: http://metasploit.com/download
|
2013-10-15 18:50:46 +00:00
|
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
2013-01-25 19:44:49 +00:00
|
|
|
##
|
|
|
|
require 'msf/core'
|
|
|
|
|
2016-03-08 13:02:44 +00:00
|
|
|
class MetasploitModule < Msf::Auxiliary
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
include Msf::Auxiliary::Report
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
# Huge thanks to @zeroSteiner for helping me. Also thanks to @kaospunk. Finally thanks to
|
|
|
|
# Joomscan and various MSF modules for code examples.
|
|
|
|
def initialize
|
|
|
|
super(
|
|
|
|
'Name' => 'Joomla Plugins Scanner',
|
|
|
|
'Description' => %q{
|
|
|
|
This module scans a Joomla install for plugins and potential
|
|
|
|
vulnerabilities.
|
|
|
|
},
|
|
|
|
'Author' => [ 'newpid0' ],
|
|
|
|
'License' => MSF_LICENSE
|
|
|
|
)
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptString.new('TARGETURI', [ true, "The path to the Joomla install", '/']),
|
2013-09-26 19:34:48 +00:00
|
|
|
OptPath.new('PLUGINS', [ true, "Path to list of plugins to enumerate", File.join(Msf::Config.data_directory, "wordlists", "joomla.txt")])
|
2013-08-30 21:28:54 +00:00
|
|
|
], self.class)
|
|
|
|
end
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def run_host(ip)
|
|
|
|
tpath = normalize_uri(target_uri.path)
|
|
|
|
if tpath[-1,1] != '/'
|
|
|
|
tpath += '/'
|
|
|
|
end
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2016-02-01 22:06:34 +00:00
|
|
|
vprint_status("Checking for interesting plugins")
|
2013-08-30 21:28:54 +00:00
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => tpath,
|
|
|
|
'method' => 'GET'
|
|
|
|
})
|
|
|
|
return if res.nil?
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
res.body.gsub!(/[\r|\n]/, ' ')
|
|
|
|
File.open(datastore['PLUGINS'], 'rb').each_line do |line|
|
|
|
|
papp = line.chomp
|
|
|
|
plugin_search(tpath, papp, ip, res.body.size)
|
|
|
|
end
|
|
|
|
end
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
def plugin_search(tpath, papp, ip, osize)
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => "#{tpath}#{papp}",
|
|
|
|
'method' => 'GET'
|
|
|
|
})
|
|
|
|
return if res.nil?
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
res.body.gsub!(/[\r|\n]/, ' ')
|
|
|
|
nsize = res.body.size
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
if (res.code == 200 and res.body !~/#404 Component not found/ and res.body !~/<h1>Joomla! Administration Login<\/h1>/ and osize != nsize)
|
2016-02-01 22:06:34 +00:00
|
|
|
print_good("Plugin: #{tpath}#{papp} ")
|
2013-08-30 21:28:54 +00:00
|
|
|
report_note(
|
|
|
|
:host => ip,
|
|
|
|
:port => rport,
|
|
|
|
:proto => 'http',
|
|
|
|
:ntype => 'joomla_plugin',
|
|
|
|
:data => "#{tpath}#{papp}",
|
|
|
|
:update => :unique_data
|
|
|
|
)
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
if (papp =~/passwd/ and res.body =~/root/)
|
2016-02-01 22:06:34 +00:00
|
|
|
print_good("Vulnerability: Potential LFI")
|
2013-08-30 21:28:54 +00:00
|
|
|
report_web_vuln(
|
|
|
|
:host => ip,
|
|
|
|
:port => rport,
|
|
|
|
:vhost => vhost,
|
|
|
|
:ssl => ssl,
|
|
|
|
:path => tpath,
|
|
|
|
:method => "GET",
|
|
|
|
:pname => "",
|
|
|
|
:proof => "Response with code #{res.code} contains the 'root' signature",
|
|
|
|
:risk => 1,
|
|
|
|
:confidence => 10,
|
|
|
|
:category => 'Local File Inclusion',
|
|
|
|
:description => "Joomla: Potential LFI at #{tpath}#{papp}",
|
|
|
|
:name => 'Local File Inclusion'
|
|
|
|
)
|
|
|
|
elsif (res.body =~/SQL syntax/)
|
2016-02-01 22:06:34 +00:00
|
|
|
print_good("Vulnerability: Potential SQL Injection")
|
2013-08-30 21:28:54 +00:00
|
|
|
report_web_vuln(
|
|
|
|
:host => ip,
|
|
|
|
:port => rport,
|
|
|
|
:vhost => vhost,
|
|
|
|
:ssl => ssl,
|
|
|
|
:path => tpath,
|
|
|
|
:method => "GET",
|
|
|
|
:pname => "",
|
|
|
|
:proof => "Response with code #{res.code} contains the 'SQL syntax' signature",
|
|
|
|
:risk => 1,
|
|
|
|
:confidence => 10,
|
|
|
|
:category => 'SQL Injection',
|
|
|
|
:description => "Joomla: Potential SQLI at #{tpath}#{papp}",
|
|
|
|
:name => 'SQL Injection'
|
|
|
|
)
|
|
|
|
elsif (papp =~/>alert/ and res.body =~/>alert/)
|
2016-02-01 22:06:34 +00:00
|
|
|
print_good("Vulnerability: Potential XSS")
|
2013-08-30 21:28:54 +00:00
|
|
|
report_web_vuln(
|
|
|
|
:host => ip,
|
|
|
|
:port => rport,
|
|
|
|
:vhost => vhost,
|
|
|
|
:ssl => ssl,
|
|
|
|
:path => tpath,
|
|
|
|
:method => "GET",
|
|
|
|
:pname => "",
|
|
|
|
:proof => "Response with code #{res.code} contains the '>alert' signature",
|
|
|
|
:risk => 1,
|
|
|
|
:confidence => 10,
|
|
|
|
:category => 'Cross Site Scripting',
|
|
|
|
:description => "Joomla: Potential XSS at #{tpath}#{papp}",
|
|
|
|
:name => 'Cross Site Scripting'
|
|
|
|
)
|
|
|
|
elsif (papp =~/com_/)
|
|
|
|
vars = papp.split('_')
|
|
|
|
pages = vars[1].gsub('/','')
|
|
|
|
res1 = send_request_cgi({
|
|
|
|
'uri' => "#{tpath}index.php?option=com_#{pages}",
|
|
|
|
'method' => 'GET'
|
|
|
|
})
|
|
|
|
if (res1.code == 200)
|
2016-02-01 22:06:34 +00:00
|
|
|
print_good("Page: #{tpath}index.php?option=com_#{pages}")
|
2013-08-30 21:28:54 +00:00
|
|
|
report_note(
|
|
|
|
:host => ip,
|
|
|
|
:port => datastore['RPORT'],
|
|
|
|
:proto => 'http',
|
|
|
|
:ntype => 'joomla_page',
|
|
|
|
:data => "Page: #{tpath}index.php?option=com_#{pages}",
|
|
|
|
:update => :unique_data
|
|
|
|
)
|
|
|
|
else
|
2016-02-01 22:06:34 +00:00
|
|
|
vprint_error("Page: #{tpath}index.php?option=com_#{pages} gave a #{res1.code} response")
|
2013-08-30 21:28:54 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
elsif (res.code == 403)
|
|
|
|
if (res.body =~ /secured with Secure Sockets Layer/ or res.body =~ /Secure Channel Required/ or res.body =~ /requires a secure connection/)
|
|
|
|
vprint_status("#{ip} ip access to #{ip} (SSL Required)")
|
|
|
|
elsif (res.body =~ /has a list of IP addresses that are not allowed/)
|
|
|
|
vprint_status("#{ip} restricted access by IP")
|
|
|
|
elsif (res.body =~ /SSL client certificate is required/)
|
|
|
|
vprint_status("#{ip} requires a SSL client certificate")
|
|
|
|
else
|
|
|
|
vprint_status("#{ip} denied access to #{ip}#{tpath}#{papp} - #{res.code} #{res.message}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return
|
2013-01-25 19:44:49 +00:00
|
|
|
|
2013-08-30 21:28:54 +00:00
|
|
|
rescue OpenSSL::SSL::SSLError
|
2016-02-01 22:06:34 +00:00
|
|
|
vprint_error("SSL error")
|
2013-08-30 21:28:54 +00:00
|
|
|
return
|
|
|
|
rescue Errno::ENOPROTOOPT, Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::ArgumentError
|
2016-02-01 22:06:34 +00:00
|
|
|
vprint_error("Unable to Connect")
|
2013-08-30 21:28:54 +00:00
|
|
|
return
|
|
|
|
rescue ::Timeout::Error, ::Errno::EPIPE
|
2016-02-01 22:06:34 +00:00
|
|
|
vprint_error("Timeout error")
|
2013-08-30 21:28:54 +00:00
|
|
|
return
|
|
|
|
end
|
2013-01-25 19:44:49 +00:00
|
|
|
|
|
|
|
end
|