Module: Msf::Exploit::Powershell

Included in:
JavaDeserialization, Post::Windows::Powershell, Post::Windows::Runas
Defined in:
lib/msf/core/exploit/powershell.rb,
lib/msf/core/exploit/powershell/dot_net.rb

Defined Under Namespace

Modules: DotNet, PshMethods

Instance Method Summary collapse

Instance Method Details

#bypass_powershell_protectionsString

Return all bypasses checking if PowerShell version > 3

Returns:

  • (String)

    PowerShell code to disable PowerShell Built-In Protections



262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/msf/core/exploit/powershell.rb', line 262

def bypass_powershell_protections
  # generate the protections bypass in three short steps
  # step 1: shuffle the instructions by rendering the GraphML
  script = Rex::Payloads::Shuffle.from_graphml_file(
    File.join(Msf::Config.install_root, 'data', 'evasion', 'windows', 'bypass_powershell_protections.erb.graphml'),
  )
  # step 2: obfuscate sketchy string literals by rendering the ERB template
  script = ::ERB.new(script).result(binding)
  # step 3: obfuscate variable names and remove whitespace
  script = Rex::Powershell::Script.new(script)
  script.sub_vars if datastore['Powershell::sub_vars']
  Rex::Powershell::PshMethods.uglify_ps(script.to_s)
end

#cmd_psh_payload(pay, payload_arch, opts = {}) ⇒ String

Creates a powershell command line string which will execute the payload in a hidden window in the appropriate execution environment for the payload architecture. Opts are passed through to run_hidden_psh, generate_psh_command_line and generate_psh_args

Parameters:

  • pay (String)

    The payload shellcode

  • payload_arch (String)

    The payload architecture 'x86'/'x86_64'

  • opts (Hash) (defaults to: {})

    The options to generate the command

Options Hash (opts):

  • :persist (Boolean)

    Loop the payload to cause re-execution if the shellcode finishes

  • :prepend_sleep (Integer)

    Sleep for the specified time before executing the payload

  • :exec_rc4 (Boolean)

    Encrypt payload with RC4

  • :method (String)

    The powershell injection technique to use: 'net'/'reflection'/'old'

  • :encode_inner_payload (Boolean)

    Encodes the powershell script within the hidden/architecture detection wrapper

  • :encode_final_payload (Boolean)

    Encodes the final powershell script

  • :remove_comspec (Boolean)

    Removes the %COMSPEC% environment variable at the start of the command line

  • :wrap_double_quotes (Boolean)

    Wraps the -Command argument in double quotes unless :encode_final_payload

Returns:

  • (String)

    Powershell command line with payload



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/msf/core/exploit/powershell.rb', line 227

def cmd_psh_payload(pay, payload_arch, opts = {})
  %i[persist prepend_sleep exec_in_place exec_rc4 encode_final_payload encode_inner_payload
  remove_comspec noninteractive wrap_double_quotes no_equals method prepend_protections_bypass].map do |opt|
    opts[opt] = datastore["Powershell::#{opt}"] if opts[opt].nil?
  end

  prepend_protections_bypass = opts.delete(:prepend_protections_bypass)
  if %w[ auto true ].include?(prepend_protections_bypass)
    opts[:prepend] = bypass_powershell_protections
  end

  unless opts.key? :shorten
    opts[:shorten] = (datastore['Powershell::method'] != 'old')
  end

  template_path = Rex::Powershell::Templates::TEMPLATE_DIR
  begin
    command = Rex::Powershell::Command.cmd_psh_payload(pay, payload_arch, template_path, opts)
  rescue Rex::Powershell::Exceptions::PowershellCommandLengthError => e
    raise unless prepend_protections_bypass == 'auto'

    # if prepend protections bypass is automatic, try it first but if the size is too large, turn it off and try again
    opts.delete(:prepend)
    command = Rex::Powershell::Command.cmd_psh_payload(pay, payload_arch, template_path, opts)
  end

  vprint_status("Powershell command length: #{command.length}")

  command
end

#compress_script(script_in, eof = nil) ⇒ String

Return a gzip compressed powershell script Will invoke PSH modifiers as enabled

Parameters:

  • script_in (String)

    Script contents

  • eof (String) (defaults to: nil)

    Marker to indicate the end of file appended to script

Returns:

  • (String)

    Compressed script with decompression stub



102
103
104
105
106
107
108
109
110
# File 'lib/msf/core/exploit/powershell.rb', line 102

def compress_script(script_in, eof = nil)
  opts = {}
  datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
    mod_method = k.split('::').last.intern
    opts[mod_method.to_sym] = true
  end

  Rex::Powershell::Command.compress_script(script_in, eof, opts)
end

#decode_script(script_in) ⇒ String

Return an decoded powershell script

Parameters:

  • script_in (String)

    Encoded contents

Returns:

  • (String)

    Decoded script



86
87
88
89
90
91
92
# File 'lib/msf/core/exploit/powershell.rb', line 86

def decode_script(script_in)
  return script_in unless
    script_in.to_s.match(%r{[A-Za-z0-9+/]+={0,3}})[0] == script_in.to_s &&
    (script_in.to_s.length % 4).zero?

  Rex::Powershell::Command.decode_script(script_in)
end

#decompress_script(script_in) ⇒ String

Return a decompressed powershell sript

Parameters:

  • script_in (String)

    Compressed contents with decompression stub

Returns:

  • (String)

    Decompressed script



118
119
120
121
122
# File 'lib/msf/core/exploit/powershell.rb', line 118

def decompress_script(script_in)
  return script_in unless script_in.match?(/FromBase64String/)

  Rex::Powershell::Command.decompress_script(script_in)
end

#encode_script(script_in, eof = nil) ⇒ String

Return an encoded powershell script Will invoke PSH modifiers as enabled

Parameters:

  • script_in (String)

    Script contents

Returns:

  • (String)

    Encoded script



70
71
72
73
74
75
76
77
78
# File 'lib/msf/core/exploit/powershell.rb', line 70

def encode_script(script_in, eof = nil)
  opts = {}
  datastore.select { |k, v| k =~ /^Powershell::(strip|sub)/ && v }.keys.map do |k|
    mod_method = k.split('::').last.intern
    opts[mod_method.to_sym] = true
  end

  Rex::Powershell::Command.encode_script(script_in, eof, opts)
end

#generate_psh_args(opts) ⇒ String

Generate arguments for the powershell command The format will be have no space at the start and have a space afterwards e.g. “-Arg1 x -Arg -Arg x ”

Parameters:

  • opts (Hash)

    The options to generate the command line

Options Hash (opts):

  • :shorten (Boolean)

    Whether to shorten the powershell arguments (v2.0 or greater)

  • :encodedcommand (String)

    Powershell script as an encoded command (-EncodedCommand)

  • :executionpolicy (String)

    The execution policy (-ExecutionPolicy)

  • :inputformat (String)

    The input format (-InputFormat)

  • :file (String)

    The path to a powershell file (-File)

  • :noexit (Boolean)

    Whether to exit powershell after execution (-NoExit)

  • :nologo (Boolean)

    Whether to display the logo (-NoLogo)

  • :noninteractive (Boolean)

    Whether to load a non interactive powershell (-NonInteractive)

  • :mta (Boolean)

    Whether to run as Multi-Threaded Apartment (-Mta)

  • :outputformat (String)

    The output format (-OutputFormat)

  • :sta (Boolean)

    Whether to run as Single-Threaded Apartment (-Sta)

  • :noprofile (Boolean)

    Whether to use the current users powershell profile (-NoProfile)

  • :windowstyle (String)

    The window style to use (-WindowStyle)

Returns:

  • (String)

    Powershell command arguments



169
170
171
172
173
174
175
176
177
# File 'lib/msf/core/exploit/powershell.rb', line 169

def generate_psh_args(opts)
  return '' unless opts

  unless opts.key? :shorten
    opts[:shorten] = (datastore['Powershell::method'] != 'old')
  end

  Rex::Powershell::Command.generate_psh_args(opts)
end

#generate_psh_command_line(opts) ⇒ String

Generate a powershell command line, options are passed on to generate_psh_args

Parameters:

  • opts (Hash)

    The options to generate the command line

Options Hash (opts):

  • :path (String)

    Path to the powershell binary

  • :no_full_stop (Boolean)

    Whether powershell binary should include .exe

Returns:

  • (String)

    Powershell command line with arguments



134
135
136
# File 'lib/msf/core/exploit/powershell.rb', line 134

def generate_psh_command_line(opts)
  Rex::Powershell::Command.generate_psh_command_line(opts)
end

#initialize(info = {}) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/msf/core/exploit/powershell.rb', line 6

def initialize(info = {})
  super
  register_advanced_options(
    [
      OptBool.new('Powershell::persist', [true, 'Run the payload in a loop', false]),
      OptInt.new('Powershell::prepend_sleep', [false, 'Prepend seconds of sleep']),
      OptEnum.new('Powershell::prepend_protections_bypass', [true, 'Prepend AMSI/SBL bypass', 'auto', %w[ auto true false ]]),
      OptBool.new('Powershell::strip_comments', [true, 'Strip comments', true]),
      OptBool.new('Powershell::strip_whitespace', [true, 'Strip whitespace', false]),
      OptBool.new('Powershell::sub_vars', [true, 'Substitute variable names', true]),
      OptBool.new('Powershell::sub_funcs', [true, 'Substitute function names', false]),
      OptBool.new('Powershell::exec_in_place', [true, 'Produce PSH without executable wrapper', false]),
      OptBool.new('Powershell::exec_rc4', [true, 'Encrypt PSH with RC4', false]),
      OptBool.new('Powershell::remove_comspec', [true, 'Produce script calling powershell directly', false]),
      OptBool.new('Powershell::noninteractive', [true, 'Execute powershell without interaction', true]),
      OptBool.new('Powershell::encode_final_payload', [true, 'Encode final payload for -EncodedCommand', false]),
      OptBool.new('Powershell::encode_inner_payload', [true, 'Encode inner payload for -EncodedCommand', false]),
      OptBool.new('Powershell::wrap_double_quotes', [true, 'Wraps the -Command argument in single quotes', true]),
      OptBool.new('Powershell::no_equals', [true, 'Pad base64 until no "=" remains', false]),
      OptEnum.new('Powershell::method', [true, 'Payload delivery method', 'reflection', %w[net reflection old msil]])
    ]
  )
end

#make_subs(script, subs) ⇒ Object

Insert substitutions into the powershell script If script is a path to a file then read the file otherwise treat it as the contents of a file



55
56
57
58
59
60
61
# File 'lib/msf/core/exploit/powershell.rb', line 55

def make_subs(script, subs)
  subs.each do |set|
    script.gsub!(set[0], set[1])
  end

  script
end

#process_subs(subs) ⇒ Object

Return an array of substitutions for use in make_subs



40
41
42
43
44
45
46
47
48
# File 'lib/msf/core/exploit/powershell.rb', line 40

def process_subs(subs)
  return [] if subs.nil? || subs.empty?
  new_subs = []
  subs.split(';').each do |set|
    new_subs << set.split(',', 2)
  end

  new_subs
end

#read_script(script_path) ⇒ Object

Return a script from path or string



33
34
35
# File 'lib/msf/core/exploit/powershell.rb', line 33

def read_script(script_path)
  Rex::Powershell::Script.new(script_path)
end

#run_hidden_psh(ps_code, payload_arch, encoded) ⇒ String

Wraps the powershell code to launch a hidden window and detect the execution environment and spawn the appropriate powershell executable for the payload architecture.

Parameters:

  • ps_code (String)

    Powershell code

  • payload_arch (String)

    The payload architecture 'x86'/'x86_64'

  • encoded (Boolean)

    Indicates whether ps_code is encoded or not

Returns:

  • (String)

    Wrapped powershell code



188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/msf/core/exploit/powershell.rb', line 188

def run_hidden_psh(ps_code, payload_arch, encoded)
  arg_opts = {
    noprofile: true,
    windowstyle: 'hidden'
  }

  # Old technique fails if powershell exits..
  arg_opts[:noexit] = (datastore['Powershell::method'] == 'old')
  arg_opts[:shorten] = (datastore['Powershell::method'] != 'old')

  Rex::Powershell::Command.run_hidden_psh(ps_code, payload_arch, encoded, arg_opts)
end