158 lines
4.8 KiB
Ruby
158 lines
4.8 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
require 'zlib'
|
|
require 'rex/text'
|
|
|
|
module Rex
|
|
module Powershell
|
|
module Output
|
|
#
|
|
# To String
|
|
#
|
|
# @return [String] Code
|
|
def to_s
|
|
code
|
|
end
|
|
|
|
#
|
|
# Returns code size
|
|
#
|
|
# @return [Integer] Code size
|
|
def size
|
|
code.size
|
|
end
|
|
|
|
#
|
|
# Return code with numbered lines
|
|
#
|
|
# @return [String] Powershell code with line numbers
|
|
def to_s_lineno
|
|
numbered = ''
|
|
code.split(/\r\n|\n/).each_with_index do |line, idx|
|
|
numbered << "#{idx}: #{line}"
|
|
end
|
|
|
|
numbered
|
|
end
|
|
|
|
#
|
|
# Return a zlib compressed powershell code wrapped in decode stub
|
|
#
|
|
# @param eof [String] End of file identifier to append to code
|
|
#
|
|
# @return [String] Zlib compressed powershell code wrapped in
|
|
# decompression stub
|
|
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 = "$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();'
|
|
# 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();'
|
|
|
|
# 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
|
|
end
|
|
|
|
#
|
|
# Return Base64 encoded powershell code
|
|
#
|
|
# @return [String] Base64 encoded powershell code
|
|
def encode_code(eof = nil)
|
|
@code = Rex::Text.encode_base64(Rex::Text.to_unicode(code))
|
|
end
|
|
|
|
#
|
|
# Return ASCII powershell code from base64/unicode
|
|
#
|
|
# @return [String] ASCII powershell code
|
|
def decode_code
|
|
@code = Rex::Text.to_ascii(Rex::Text.decode_base64(code))
|
|
end
|
|
|
|
#
|
|
# Return a gzip compressed powershell code wrapped in decoder stub
|
|
#
|
|
# @param eof [String] End of file identifier to append to code
|
|
#
|
|
# @return [String] Gzip compressed powershell code wrapped in
|
|
# decompression stub
|
|
def gzip_code(eof = nil)
|
|
# Compress using the Deflate algorithm
|
|
compressed_stream = Rex::Text.gzip(code)
|
|
|
|
# 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 << "[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();'
|
|
|
|
# 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
|
|
end
|
|
|
|
#
|
|
# Compresses script contents with gzip (default) or deflate
|
|
#
|
|
# @param eof [String] End of file identifier to append to code
|
|
# @param gzip [Boolean] Whether to use gzip compression or deflate
|
|
#
|
|
# @return [String] Compressed code wrapped in decompression stub
|
|
def compress_code(eof = nil, gzip = true)
|
|
@code = gzip ? gzip_code(eof) : deflate_code(eof)
|
|
end
|
|
|
|
#
|
|
# Reverse the compression process
|
|
# Try gzip, inflate if that fails
|
|
#
|
|
# @return [String] Decompressed powershell code
|
|
def decompress_code
|
|
# Extract substring with payload
|
|
encoded_stream = @code.scan(/FromBase64String\('(.*)'/).flatten.first
|
|
# Decode and decompress the string
|
|
unencoded = Rex::Text.decode_base64(encoded_stream)
|
|
begin
|
|
@code = Rex::Text.ungzip(unencoded) || Rex::Text.zlib_inflate(unencoded)
|
|
rescue Zlib::GzipFile::Error
|
|
begin
|
|
@code = Rex::Text.zlib_inflate(unencoded)
|
|
rescue Zlib::DataError => e
|
|
raise RuntimeError, 'Invalid compression'
|
|
end
|
|
end
|
|
|
|
@code
|
|
end
|
|
end
|
|
end
|
|
end
|