Module: Rex::Powershell::Obfu
Constant Summary collapse
- MULTI_LINE_COMMENTS_REGEX =
Regexp.new(/<#(.*?)#>/m)
- SINGLE_LINE_COMMENTS_REGEX =
Regexp.new(/^\s*#(?!.*region)(.*$)/i)
- WINDOWS_EOL_REGEX =
Regexp.new(/[\r\n]+/)
- UNIX_EOL_REGEX =
Regexp.new(/[\n]+/)
- WHITESPACE_REGEX =
Regexp.new(/\s+/)
- EMPTY_LINE_REGEX =
Regexp.new(/^$|^\s+$/)
Class Method Summary collapse
-
.descate_string_literal(string) ⇒ String
Deobfuscate a Powershell literal string value that was previously obfuscated by #scate_string_literal.
-
.scate_string_literal(string, threshold: 0.15) ⇒ String
Obfuscate a Powershell literal string value.
Instance Method Summary collapse
-
#standard_subs(subs = %w(strip_comments strip_whitespace sub_funcs sub_vars))) ⇒ String
Perform standard substitutions.
-
#strip_comments ⇒ String
Remove comments.
-
#strip_empty_lines ⇒ String
Remove empty lines.
-
#strip_whitespace ⇒ String
Remove whitespace This can break some codes using inline .NET.
-
#sub_funcs ⇒ String
Identify function names and replace them.
-
#sub_vars ⇒ String
Identify variables and replace them.
Class Method Details
.descate_string_literal(string) ⇒ String
Deobfuscate a Powershell literal string value that was previously obfuscated by #scate_string_literal.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/rex/powershell/obfu.rb', line 73 def self.descate_string_literal(string) string = string.strip nest_level = [string.match(/^(\(*)/)[0].length, string.match(/(\)*)$/)[0].length].min string = string[nest_level...-nest_level].strip if nest_level > 0 format_args = nil if (string =~ /\((?>[^)(]+|\g<0>)*\)/) == 0 format = Regexp.last_match(0) format_args = string[format.length..-1].strip unless format_args =~ /-f\s*('.',\s*)*('.')/ raise Exceptions::PowershellError, 'The obfuscated string structure is unsupported' end format_args = format_args[2..-1].strip.scan(/'(.)'/).map { |match| match[0] } string = format[1...-1].strip end unless string =~ /^'.*'$/ raise Exceptions::PowershellError, 'The obfuscated string structure is unsupported' end string = string.gsub(/'\s*\+\s*'/, '') # process all concatenation operations unless format_args.nil? # process all format string operations string = string.gsub(/\{\s*\d+\s*\}/) do |index| format_args[index[1...-1].to_i] end end string[1...-1] end |
.scate_string_literal(string, threshold: 0.15) ⇒ String
Obfuscate a Powershell literal string value. The character set of the string is limited to alpha-numeric characters and some punctuation. This routine will use a combination of of techniques including formatting and concatenation. The result is an expression that can either be passed to a function or assigned to a variable.
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/rex/powershell/obfu.rb', line 25 def self.scate_string_literal(string, threshold: 0.15) # this hasn't been thoroughly tested for strings that contain alot of punctuation, just simple ones like # 'AmsiUtils', the most important characters that are assumed to be missing are quotes and braces raise ArgumentError.new('string contains an unsupported character') if string =~ /[^a-zA-Z0-9,+=\.\/]/ raise ArgumentError.new('threshold must be between 0 and 1') unless threshold.between?(0, 1) new = original = string occurrences = {} original.each_char { |char| occurrences[char] = 0 unless occurrences.key?(char) occurrences[char] += 1 } char_map = occurrences.group_by { |k,v| v }.sort_by { |k,v| -k }.map { |k,v| v.shuffle }.flatten(1) # phase 1 format = [] char_subs = 0.0 while (char_subs / original.length.to_f) < threshold orig_char, occurrence_count = char_map.pop new = new.gsub(/(?<!\{)#{Regexp.escape(orig_char)}(?!\})/, "{#{format.length}}") format << "'#{orig_char}'" char_subs += occurrence_count end # phase 2 concat = "'+'" positions = threshold > 0 ? (0..new.length).to_a.shuffle[0..(new.length * threshold)] : [] positions.sort! positions.each_with_index do |position, index| new = new.insert(position + (index * concat.length), concat) end new = "'#{new}'" new = "(#{new})" unless threshold == 0 final = new final << "-f#{format.join(',')}" unless format.empty? final = "(#{final})" unless format.empty? && threshold == 0 final end |
Instance Method Details
#standard_subs(subs = %w(strip_comments strip_whitespace sub_funcs sub_vars))) ⇒ String
Perform standard substitutions
170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/rex/powershell/obfu.rb', line 170 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| send(modifier) end code.gsub!(EMPTY_LINE_REGEX, '') code end |
#strip_comments ⇒ String
Remove comments
105 106 107 108 109 110 111 112 |
# File 'lib/rex/powershell/obfu.rb', line 105 def strip_comments # Multi line code.gsub!(MULTI_LINE_COMMENTS_REGEX, '') # Single line code.gsub!(SINGLE_LINE_COMMENTS_REGEX, '') code end |
#strip_empty_lines ⇒ String
Remove empty lines
118 119 120 121 122 123 124 125 |
# File 'lib/rex/powershell/obfu.rb', line 118 def strip_empty_lines # Windows EOL code.gsub!(WINDOWS_EOL_REGEX, "\r\n") # UNIX EOL code.gsub!(UNIX_EOL_REGEX, "\n") code end |
#strip_whitespace ⇒ String
Remove whitespace This can break some codes using inline .NET
132 133 134 135 136 137 |
# File 'lib/rex/powershell/obfu.rb', line 132 def strip_whitespace code.gsub!(WHITESPACE_REGEX, ' ') code.strip! code end |
#sub_funcs ⇒ String
Identify function names and replace them
157 158 159 160 161 162 163 164 |
# File 'lib/rex/powershell/obfu.rb', line 157 def sub_funcs # Find out function names, make map get_func_names.each do |var, _sub| code.gsub!(var, @rig.init_var(var)) end code end |
#sub_vars ⇒ String
Identify variables and replace them
143 144 145 146 147 148 149 150 |
# File 'lib/rex/powershell/obfu.rb', line 143 def sub_vars # Get list of variables, remove reserved get_var_names.each do |var, _sub| code.gsub!(var, "$#{@rig.init_var(var)}") end code end |