27e12754fe
Sync critical functionality from Rex and Msf namespaces dealing with encoding and processing of powershell script for exploit or post namespaces. Import Post module. Primarily adds a psh_exec method which will be replaced in the next PR with @benpturner's work integrated into the Post module namespace. Provide a sample metasploit windows post module to show the execution pipeline - entire subs process can be removed and the module reduced to a psh_exec(datastore['SCRIPT']). This commit is designed to provide sync between the SVIT fork and upstream. Pending commits to be based on this work will provide access to .NET compiler in the Post namespace to be used for dynamic persistent payload creation on target and the import of @benpturner's work.
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
|