From 29bf296b61a7b2221653b518ef3a525ceb3aae95 Mon Sep 17 00:00:00 2001 From: RageLtMan Date: Wed, 12 Feb 2014 16:45:57 -0500 Subject: [PATCH] import rex powershell --- lib/rex/exploitation/powershell.rb | 124 +++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 31 deletions(-) diff --git a/lib/rex/exploitation/powershell.rb b/lib/rex/exploitation/powershell.rb index 28424cbc46..512c597a98 100644 --- a/lib/rex/exploitation/powershell.rb +++ b/lib/rex/exploitation/powershell.rb @@ -30,11 +30,36 @@ module Powershell end # - # Return a Base64 encoded powershell code + # Return a zlib compressed powershell code # - def encode_code(eof = nil) + def deflate_code(eof = nil) + # Compress using the Deflate algorithm + compressed_stream = ::Zlib::Deflate.deflate(code, + ::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 = "$stream = New-Object IO.MemoryStream(," + psh_expression << "$([Convert]::FromBase64String('#{encoded_stream}')));" + # Read & delete the first two bytes due to incompatibility with MS + psh_expression << "$stream.ReadByte()|Out-Null;" + psh_expression << "$stream.ReadByte()|Out-Null;" + # Uncompress and invoke the expression (execute) + psh_expression << "$(Invoke-Expression $(New-Object IO.StreamReader(" + psh_expression << "$(New-Object IO.Compression.DeflateStream(" + psh_expression << "$stream," + psh_expression << "[IO.Compression.CompressionMode]::Decompress))," + psh_expression << "[Text.Encoding]::ASCII)).ReadToEnd());" + + # If eof is set, add a marker to signify end of code output + #if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end + psh_expression << "echo '#{eof}';" if eof + # Convert expression to unicode - unicode_expression = Rex::Text.to_unicode(code) + unicode_expression = Rex::Text.to_unicode(psh_expression) # Base64 encode the unicode expression @code = Rex::Text.encode_base64(unicode_expression) @@ -42,10 +67,11 @@ module Powershell return code end + # - # Return a zlib compressed powershell code + # Return a gzip compressed powershell code # - def compress_code(eof = nil) + def gzip_code(eof = nil) # Compress using the Deflate algorithm compressed_stream = Rex::Text.gzip(code) @@ -54,30 +80,51 @@ module Powershell # Build the powershell expression # Decode base64 encoded command and create a stream object - psh_expression = "IEX(New-Object IO.StreamReader(" - psh_expression << "(New-Object IO.Compression.GzipStream(" - psh_expression << "(New-Object IO.MemoryStream(,[Convert]::FromBase64String('#{encoded_stream}')))," + psh_expression = "$stream = New-Object IO.MemoryStream(," + psh_expression << "$([Convert]::FromBase64String('#{encoded_stream}')));" + # Uncompress and invoke the expression (execute) + psh_expression << "$(Invoke-Expression $(New-Object IO.StreamReader(" + psh_expression << "$(New-Object IO.Compression.GzipStream(" + psh_expression << "$stream," psh_expression << "[IO.Compression.CompressionMode]::Decompress))," - psh_expression << "[Text.Encoding]::ASCII)).ReadToEnd();" + psh_expression << "[Text.Encoding]::ASCII)).ReadToEnd());" # If eof is set, add a marker to signify end of code output #if (eof && eof.length == 8) then psh_expression += "'#{eof}'" end psh_expression << "echo '#{eof}';" if eof - @code = psh_expression + # Convert expression to unicode + unicode_expression = Rex::Text.to_unicode(psh_expression) - return psh_expression + # Base64 encode the unicode expression + @code = Rex::Text.encode_base64(unicode_expression) + + return code + end + + # + # Compresses script contents with gzip or deflate + # + def compress_code(eof = nil, gzip = true, in_place = true) + code = gzip ? gzip_code(eof) : deflate_code(eof) + @code = code if in_place + return code end # # Reverse the compression process + # Try gzip, inflate if that fails # def decompress_code + # Decode base64 and convert to ascii + raw = Rex::Text.decode_base64(code) + ascii_expression = Rex::Text.to_ascii(raw) # Extract substring with payload encoded_stream = ascii_expression.scan(/FromBase64String\('(.*)'/).flatten.first # Decode and decompress the string - return Rex::Text.ungzip( Rex::Text.decode_base64(encoded_stream) ) - # ::Zlib::Inflate.inflate( Rex::Text.decode_base64(encoded_stream) ) + @code = ( Rex::Text.ungzip( Rex::Text.decode_base64(encoded_stream) ) || + Rex::Text.zlib_inflate( Rex::Text.decode_base64(encoded_stream)) ) + return code end end @@ -144,6 +191,7 @@ module Powershell '$WhatIfPreference' ].map(&:downcase) + # return code.scan(/\$[a-zA-Z\-\_]+/).uniq.flatten - res_vars our_vars = code.scan(/\$[a-zA-Z\-\_]+/).uniq.flatten.map(&:strip) return our_vars.select {|v| !res_vars.include?(v)} @@ -243,7 +291,17 @@ module Powershell # Multi line code.gsub!(/<#(.*?)#>/m,'') # Single line - code.gsub!(/^#.*$|^\s+#.*$/,'') + code.gsub!(/^\s*#(?!.*region)(.*$)/i,'') + end + + # + # Remove empty lines + # + def strip_empty_lines + # Windows EOL + code.gsub!(/[\r\n]+/,"\r\n") + # UNIX EOL + code.gsub!(/[\n]+/,"\n") end # @@ -254,7 +312,6 @@ module Powershell code.gsub!(/\s+/,' ') end - # # Identify variables and replace them # @@ -349,7 +406,21 @@ 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, + :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, :>=, + :!, :count, :slice, :clone, :chop!, :prepend, :succ!, :upcase, :include?, :frozen?, + :delete!, :chop, :lines, :replace, :next, :=~, :==, :rstrip!, :%, :upcase!, :each_char, + :hash, :rstrip, :length, :reverse, :setbyte, :bytesize, :squeeze, :>, :center, :[], + :<=, :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, + :[]=, :encode, :*, :hex, :to_f, :strip!, :rpartition, :ord, :capitalize, :upto, :force_encoding, + :end_with? # def method_missing(meth, *args, &block) # code.send(meth,*args,&block) || (raise NoMethodError.new, meth) @@ -379,12 +450,11 @@ module Powershell ## # - # Convert binary to byte array, read from file if able + # Build a byte array to load into powershell code # - def self.to_byte_array(input_data,var_name="buf") - return "[Byte[]]$#{name} = ''" if input_data.nil? or input_data.empty? - - code = input_data.unpack('C*') + def self.build_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3)) + code = ::File.file?(input_data) ? ::File.read(input_data) : input_data + code = code.unpack('C*') psh = "[Byte[]] $#{var_name} = 0x#{code[0].to_s(16)}" lines = [] 1.upto(code.length-1) do |byte| @@ -394,16 +464,7 @@ module Powershell lines.push ",0x#{code[byte].to_s(16)}" end end - - return psh << lines.join("") + "\r\n" - end - - # - # Build a byte array to load into powershell code - # - def self.build_byte_array(input_data,var_name = Rex::Text.rand_text_alpha(rand(3)+3)) - code = ::File.file?(input_data) ? ::File.read(input_data) : input_data - return to_byte_array(input_data,var_name) + psh << lines.join("") + "\r\n" end def self.psp_funcs(dir) @@ -423,3 +484,4 @@ module Powershell end end end +