175 lines
4.9 KiB
Ruby
175 lines
4.9 KiB
Ruby
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
require 'msf/core'
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
include Msf::Auxiliary::Scanner
|
|
include Msf::Auxiliary::Report
|
|
|
|
# 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 Version Scanner',
|
|
'Description' => %q{
|
|
This module scans a Joomla install for information about the underlying
|
|
operating system and Joomla version.
|
|
},
|
|
'Author' => [ 'newpid0' ],
|
|
'License' => MSF_LICENSE
|
|
)
|
|
register_options(
|
|
[
|
|
OptString.new('TARGETURI', [ true, "The path to the Joomla install", '/'])
|
|
], self.class)
|
|
end
|
|
|
|
def peer
|
|
return "#{rhost}:#{rport}"
|
|
end
|
|
|
|
def os_fingerprint(response)
|
|
if not response.headers.has_key?('Server')
|
|
return "Unkown OS (No Server Header)"
|
|
end
|
|
|
|
case response.headers['Server']
|
|
when /Win32/, /\(Windows/, /IIS/
|
|
os = "Windows"
|
|
when /Apache\//
|
|
os = "*Nix"
|
|
else
|
|
os = "Unknown Server Header Reporting: "+response.headers['Server']
|
|
end
|
|
return os
|
|
end
|
|
|
|
def fingerprint(response)
|
|
case response.body
|
|
when /<version.*\/?>(.+)<\/version\/?>/i
|
|
v = $1
|
|
out = (v =~ /^6/) ? "Joomla #{v}" : " #{v}"
|
|
when /system\.css 20196 2011\-01\-09 02\:40\:25Z ian/,
|
|
/MooTools\.More\=\{version\:\"1\.3\.0\.1\"/,
|
|
/en-GB\.ini 20196 2011\-01\-09 02\:40\:25Z ian/,
|
|
/en-GB\.ini 20990 2011\-03\-18 16\:42\:30Z infograf768/,
|
|
/20196 2011\-01\-09 02\:40\:25Z ian/
|
|
out = "1.6"
|
|
when /system\.css 21322 2011\-05\-11 01\:10\:29Z dextercowley /,
|
|
/MooTools\.More\=\{version\:\"1\.3\.2\.1\"/,
|
|
/22183 2011\-09\-30 09\:04\:32Z infograf768/,
|
|
/21660 2011\-06\-23 13\:25\:32Z infograf768/
|
|
out = "1.7"
|
|
when /Joomla! 1.5/,
|
|
/MooTools\=\{version\:\'1\.12\'\}/,
|
|
/11391 2009\-01\-04 13\:35\:50Z ian/
|
|
out = "1.5"
|
|
when /Copyright \(C\) 2005 \- 2012 Open Source Matters/,
|
|
/MooTools.More\=\{version\:\"1\.4\.0\.1\"/
|
|
out = "2.5"
|
|
when /<meta name=\"Keywords\" content=\"(.*)\">\s+<meta name/
|
|
out = $1.split(/,/)[0]
|
|
when /(Copyright \(C\) 2005 - 200(6|7))/,
|
|
/47 2005\-09\-15 02\:55\:27Z rhuk/,
|
|
/423 2005\-10\-09 18\:23\:50Z stingrey/,
|
|
/1005 2005\-11\-13 17\:33\:59Z stingrey/,
|
|
/1570 2005\-12\-29 05\:53\:33Z eddieajau/,
|
|
/2368 2006\-02\-14 17\:40\:02Z stingrey/,
|
|
/4085 2006\-06\-21 16\:03\:54Z stingrey/,
|
|
/4756 2006\-08\-25 16\:07\:11Z stingrey/,
|
|
/5973 2006\-12\-11 01\:26\:33Z robs/,
|
|
/5975 2006\-12\-11 01\:26\:33Z robs/
|
|
out = "1.0"
|
|
else
|
|
out = 'Unknown Joomla'
|
|
end
|
|
return out
|
|
end
|
|
|
|
def check_file(tpath, file, ip)
|
|
res = send_request_cgi({
|
|
'uri' => "#{tpath}#{file}",
|
|
'method' => 'GET'
|
|
})
|
|
|
|
return :abort if res.nil?
|
|
|
|
res.body.gsub!(/[\r|\n]/, ' ')
|
|
|
|
if (res.code == 200)
|
|
os = os_fingerprint(res)
|
|
out = fingerprint(res)
|
|
return false if not out
|
|
|
|
if(out =~ /Unknown Joomla/)
|
|
print_error("#{peer} - Unable to identify Joomla Version with #{file}")
|
|
return false
|
|
else
|
|
print_good("#{peer} - Joomla Version:#{out} from: #{file} ")
|
|
print_good("#{peer} - OS: #{os}")
|
|
report_note(
|
|
:host => ip,
|
|
:port => datastore['RPORT'],
|
|
:proto => 'http',
|
|
:ntype => 'joomla_version',
|
|
:data => out
|
|
)
|
|
return true
|
|
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} denied 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} #{res.code} #{res.message}")
|
|
end
|
|
return :abort
|
|
end
|
|
|
|
return false
|
|
|
|
rescue OpenSSL::SSL::SSLError
|
|
vprint_error("#{peer} - SSL error")
|
|
return :abort
|
|
rescue Errno::ENOPROTOOPT, Errno::ECONNRESET, ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::ArgumentError
|
|
vprint_error("#{peer} - Unable to Connect")
|
|
return :abort
|
|
rescue ::Timeout::Error, ::Errno::EPIPE
|
|
vprint_error("#{peer} - Timeout error")
|
|
return :abort
|
|
end
|
|
|
|
def run_host(ip)
|
|
tpath = normalize_uri(target_uri.path)
|
|
if tpath[-1,1] != '/'
|
|
tpath += '/'
|
|
end
|
|
|
|
files = [
|
|
'language/en-GB/en-GB.xml',
|
|
'templates/system/css/system.css',
|
|
'media/system/js/mootools-more.js',
|
|
'language/en-GB/en-GB.ini',
|
|
'htaccess.txt',
|
|
'language/en-GB/en-GB.com_media.ini'
|
|
]
|
|
|
|
vprint_status("#{peer} - Checking Joomla version")
|
|
files.each do |file|
|
|
joomla_found = check_file(tpath, file, ip)
|
|
return if joomla_found == :abort
|
|
break if joomla_found
|
|
end
|
|
end
|
|
|
|
end
|