Module: Rex::Powershell::Output

Included in:
Function, Script
Defined in:
lib/rex/powershell/output.rb

Instance Method Summary collapse

Instance Method Details

#compress_code(eof = nil, gzip = true) ⇒ String

Compresses script contents with gzip (default) or deflate

Parameters:

  • eof (String) (defaults to: nil)

    End of file identifier to append to code

  • gzip (Boolean) (defaults to: true)

    Whether to use gzip compression or deflate

Returns:

  • (String)

    Compressed code wrapped in decompression stub



133
134
135
# File 'lib/rex/powershell/output.rb', line 133

def compress_code(eof = nil, gzip = true)
  @code = gzip ? gzip_code(eof) : deflate_code(eof)
end

#decode_codeString

Return ASCII powershell code from base64/unicode

Returns:

  • (String)

    ASCII powershell code



87
88
89
# File 'lib/rex/powershell/output.rb', line 87

def decode_code
  @code = Rex::Text.to_ascii(Rex::Text.decode_base64(code))
end

#decompress_codeString

Reverse the compression process Try gzip, inflate if that fails

Returns:

  • (String)

    Decompressed powershell code



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/rex/powershell/output.rb', line 142

def decompress_code
  # Extract substring with payload4
  if @code =~ /FromBase64String\('([a-zA-z0-9\+\/=]*)'\)/
    encoded_stream = Regexp.last_match(1)
  elsif @code =~ /FromBase64String(\((?>[^)(]+|\g<1>)*\))/
    encoded_stream = Obfu.descate_string_literal(Regexp.last_match(1))
  else
    raise Exceptions::PowershellError, 'Failed to identify the base64 data'
  end

  # 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 Exceptions::PowershellError, 'Invalid compression'
    end
  end

  @code
end

#deflate_code(eof = nil) ⇒ String

Return a zlib compressed powershell code wrapped in decode stub

decompression stub

Parameters:

  • eof (String) (defaults to: nil)

    End of file identifier to append to code

Returns:

  • (String)

    Zlib compressed powershell code wrapped in



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/rex/powershell/output.rb', line 45

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 System.IO.MemoryStream(,"
  psh_expression << "[System.Convert]::FromBase64String(#{Obfu.scate_string_literal(encoded_stream, threshold: 0.01)}));"
  # 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 System.IO.StreamReader('
  psh_expression << 'New-Object System.IO.Compression.DeflateStream('
  psh_expression << '$s,'
  psh_expression << '[System.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

#encode_code(eof = nil) ⇒ String

Return Base64 encoded powershell code

Returns:

  • (String)

    Base64 encoded powershell code



79
80
81
# File 'lib/rex/powershell/output.rb', line 79

def encode_code(eof = nil)
  @code = Rex::Text.encode_base64(Rex::Text.to_unicode(code))
end

#gzip_code(eof = nil) ⇒ String

Return a gzip compressed powershell code wrapped in decoder stub

decompression stub

Parameters:

  • eof (String) (defaults to: nil)

    End of file identifier to append to code

Returns:

  • (String)

    Gzip compressed powershell code wrapped in



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/rex/powershell/output.rb', line 98

def gzip_code(eof = nil)
  # Compress using the Gzip 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
  # Create and execute script lock fed by the IO.StreamReader
  psh_expression = '&([scriptblock]::create((New-Object System.IO.StreamReader('
  # Feed StreamREader from a GzipStream
  psh_expression << 'New-Object System.IO.Compression.GzipStream('
  # GzipStream operates on the Memory Stream
  psh_expression << '(New-Object System.IO.MemoryStream(,'
  # MemoryStream consists of base64 encoded compressed data
  psh_expression << "[System.Convert]::FromBase64String(#{Obfu.scate_string_literal(encoded_stream, threshold: 0.01)})))"
  # Set the GzipStream to decompress its MemoryStream contents
  psh_expression << ',[System.IO.Compression.CompressionMode]::Decompress)'
  # Read the decoded, decompressed result into scriptblock contents
  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

#sizeInteger

Returns code size

Returns:

  • (Integer)

    Code size



21
22
23
# File 'lib/rex/powershell/output.rb', line 21

def size
  code.size
end

#to_sString

To String

Returns:

  • (String)

    Code



13
14
15
# File 'lib/rex/powershell/output.rb', line 13

def to_s
  code
end

#to_s_linenoString

Return code with numbered lines

Returns:

  • (String)

    Powershell code with line numbers



29
30
31
32
33
34
35
36
# File 'lib/rex/powershell/output.rb', line 29

def to_s_lineno
  numbered = ''
  code.split(/\r\n|\n/).each_with_index do |line, idx|
    numbered << "#{idx}: #{line}"
  end

  numbered
end