Module: Msf::Exploit::CmdStager

Includes:
HTTP, EXE
Defined in:
lib/msf/core/exploit/cmd_stager.rb,
lib/msf/core/exploit/cmd_stager/http.rb

Overview

This mixin provides an interface to generating cmdstagers

Defined Under Namespace

Modules: HTTP

Constant Summary collapse

STAGERS =

Constant for stagers - used when creating an stager instance.

{
  :bourne => Rex::Exploitation::CmdStagerBourne,
  :debug_asm => Rex::Exploitation::CmdStagerDebugAsm,
  :debug_write => Rex::Exploitation::CmdStagerDebugWrite,
  :echo => Rex::Exploitation::CmdStagerEcho,
  :printf => Rex::Exploitation::CmdStagerPrintf,
  :vbs => Rex::Exploitation::CmdStagerVBS,
  :vbs_adodb => Rex::Exploitation::CmdStagerVBS,
  :certutil => Rex::Exploitation::CmdStagerCertutil,
  :tftp => Rex::Exploitation::CmdStagerTFTP,
  :wget => Rex::Exploitation::CmdStagerWget,
  :curl => Rex::Exploitation::CmdStagerCurl,
  :fetch => Rex::Exploitation::CmdStagerFetch,
  :lwprequest => Rex::Exploitation::CmdStagerLwpRequest,
  :psh_invokewebrequest => Rex::Exploitation::CmdStagerPSHInvokeWebRequest,
  :ftp_http => Rex::Exploitation::CmdStagerFtpHttp,
}
DECODERS =

Constant for decoders - used when checking the default flavor decoder.

{
  :debug_asm => File.join(Rex::Exploitation::DATA_DIR, "exploits", "cmdstager", "debug_asm"),
  :debug_write => File.join(Rex::Exploitation::DATA_DIR, "exploits", "cmdstager", "debug_write"),
  :vbs => File.join(Rex::Exploitation::DATA_DIR, "exploits", "cmdstager", "vbs_b64"),
  :vbs_adodb => File.join(Rex::Exploitation::DATA_DIR, "exploits", "cmdstager", "vbs_b64_adodb")
}

Instance Attribute Summary collapse

Attributes included from Remote::SocketServer

#service

Instance Method Summary collapse

Methods included from HTTP

#on_request_uri, #start_service

Methods included from Remote::HttpServer

#add_resource, #add_robots_resource, #autofilter, #check_dependencies, #cleanup, #cli, #cli=, #close_client, #create_response, #fingerprint_user_agent, #get_resource, #get_uri, #hardcoded_uripath, #on_request_uri, #print_prefix, #random_uri, #regenerate_payload, #remove_resource, #report_user_agent, #resource_uri, #send_local_redirect, #send_not_found, #send_redirect, #send_response, #send_robots, #srvhost_addr, #srvport, #start_service, #use_zlib

Methods included from Auxiliary::Report

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Methods included from Metasploit::Framework::Require

optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines

Methods included from Remote::TcpServer

#on_client_close, #on_client_connect, #ssl, #ssl_cert, #ssl_cipher, #ssl_compression, #ssl_version, #start_service

Methods included from Remote::SocketServer

#_determine_server_comm, #bindhost, #bindport, #cleanup, #cleanup_service, #exploit, #on_client_data, #primer, #regenerate_payload, #srvhost, #srvport, #start_service, #via_string

Methods included from EXE

#exe_init_options, #exe_post_generation, #generate_payload_dccw_gdiplus_dll, #generate_payload_dll, #generate_payload_exe, #generate_payload_exe_service, #generate_payload_msi, #get_custom_exe, #get_eicar_exe

Instance Attribute Details

#cmd_listObject

Returns the value of attribute cmd_list.



42
43
44
# File 'lib/msf/core/exploit/cmd_stager.rb', line 42

def cmd_list
  @cmd_list
end

#decoderObject

Returns the value of attribute decoder.



44
45
46
# File 'lib/msf/core/exploit/cmd_stager.rb', line 44

def decoder
  @decoder
end

#exeObject

Returns the value of attribute exe.



45
46
47
# File 'lib/msf/core/exploit/cmd_stager.rb', line 45

def exe
  @exe
end

#flavorObject

Returns the value of attribute flavor.



43
44
45
# File 'lib/msf/core/exploit/cmd_stager.rb', line 43

def flavor
  @flavor
end

#stager_instanceObject

Returns the value of attribute stager_instance.



41
42
43
# File 'lib/msf/core/exploit/cmd_stager.rb', line 41

def stager_instance
  @stager_instance
end

Instance Method Details

#compatible_flavor?(f) ⇒ Boolean

Answers if the input flavor is compatible with the current target or module.

Parameters:

  • f (Symbol)

    The flavor to check

Returns:

  • (Boolean)

    true if compatible, false otherwise.



325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/msf/core/exploit/cmd_stager.rb', line 325

def compatible_flavor?(f)
  return true if target_flavor.nil?
  case target_flavor
  when String
    return true if target_flavor == f.to_s
  when Array
    target_flavor.each { |tr| return true if tr.to_sym == f }
  when Symbol
    return true if target_flavor == f
  end
  false
end

#create_stagerRex::Exploitation::CmdStagerBase

Create an instance of the flavored stager.

Returns:

  • (Rex::Exploitation::CmdStagerBase)

    The cmd stager to use.

Raises:

  • (NoMethodError)

    raised if the flavor doesn't exist.



213
214
215
# File 'lib/msf/core/exploit/cmd_stager.rb', line 213

def create_stager
  STAGERS[flavor].new(exe)
end

#default_decoder(f) ⇒ Symbol?

Returns the default decoder stub for the input flavor.

Parameters:

  • f (Symbol)

    the input flavor.

Returns:

  • (Symbol)

    the decoder.

  • (nil)

    if there isn't a default decoder to use for the current cmd stager flavor.



223
224
225
# File 'lib/msf/core/exploit/cmd_stager.rb', line 223

def default_decoder(f)
  DECODERS[f]
end

#execute_cmdstager(opts = {}) ⇒ void

This method returns an undefined value.

Executes the command stager while showing the progress. This method should be called from exploits using this mixin.

Parameters:

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

    Hash containing configuration options. Also allow to send opts to the Rex::Exploitation::CmdStagerBase constructor.

Options Hash (opts):

  • :flavor (Symbol)

    The CMD Stager to use.

  • :decoder (Symbol)

    The decoder stub to use.

  • :delay (Float)

    Delay between command executions.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/msf/core/exploit/cmd_stager.rb', line 86

def execute_cmdstager(opts = {})
  self.cmd_list = generate_cmdstager(opts)

  stager_instance.setup(self)

  begin
    execute_cmdstager_begin(opts)

    sent = 0
    total_bytes = 0
    cmd_list.each { |cmd| total_bytes += cmd.bytesize }

    delay = opts[:delay]
    delay ||= 0.25

    cmd_list.each do |cmd|
      # calculate string beforehand length in case exploit mutates string
      command_length = cmd.bytesize
      execute_command(cmd, opts)
      sent += command_length

      # In cases where a server has multiple threads, we want to be sure that
      # commands we execute happen in the correct (serial) order.
      ::IO.select(nil, nil, nil, delay)

      progress(total_bytes, sent)
    end

    execute_cmdstager_end(opts)
  ensure
    stager_instance.teardown(self)
  end
end

#execute_cmdstager_begin(opts = {}) ⇒ Object

Code to execute before the cmd stager stub. This method is designed to be overridden by a module this mixin.

Parameters:

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

    Hash of configuration options.



342
343
# File 'lib/msf/core/exploit/cmd_stager.rb', line 342

def execute_cmdstager_begin(opts = {})
end

#execute_cmdstager_end(opts = {}) ⇒ Object

Code to execute after the cmd stager stub. This method is designed to be overridden by a module this mixin.

Parameters:

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

    Hash of configuration options.



349
350
# File 'lib/msf/core/exploit/cmd_stager.rb', line 349

def execute_cmdstager_end(opts = {})
end

#execute_command(cmd, opts = {}) ⇒ Object

Code called to execute each command via an arbitrary module-defined vector. This method needs to be overridden by modules using this mixin.

Parameters:

  • cmd (String)

    The command to execute.

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

    Hash of configuration options.

Raises:

  • (NotImplementedError)


357
358
359
# File 'lib/msf/core/exploit/cmd_stager.rb', line 357

def execute_command(cmd, opts = {})
  raise NotImplementedError
end

#generate_cmdstager(opts = {}, pl = nil) ⇒ Array

Generates a cmd stub based on the current target's architecture and platform.

Parameters:

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

    Hash containing configuration options. Also allow to send opts to the Rex::Exploitation::CmdStagerBase constructor.

  • pl (String) (defaults to: nil)

    String containing the payload to execute

Options Hash (opts):

  • :flavor (Symbol)

    The CMD Stager to use.

  • :decoder (Symbol)

    The decoder stub to use.

Returns:

  • (Array)

    The list of commands to execute

Raises:

  • (ArgumentError)

    raised if the exe or cmd stub cannot be generated



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/msf/core/exploit/cmd_stager.rb', line 131

def generate_cmdstager(opts = {}, pl = nil)
  select_cmdstager(opts)

  exe_opts = {code: pl}.merge(
    platform: target_platform,
    arch: target_arch
  )
  self.exe = generate_payload_exe(exe_opts)

  if exe.nil?
    raise ArgumentError, 'The executable could not be generated'
  end

  self.stager_instance = create_stager

  if datastore['CMDSTAGER::TEMP']
    opts[:temp] = datastore['CMDSTAGER::TEMP']
  elsif datastore['WritableDir']
    opts[:temp] = datastore['WritableDir']
  end

  if stager_instance.respond_to?(:http?) && stager_instance.http?
    opts[:ssl] = datastore['CMDSTAGER::SSL'] unless opts.key?(:ssl)
    opts['Path'] = datastore['CMDSTAGER::URIPATH'] unless datastore['CMDSTAGER::URIPATH'].blank?
    opts[:payload_uri] = start_service(opts)
  end

  cmd_list = stager_instance.generate(opts_with_decoder(opts))

  if cmd_list.nil? || cmd_list.length.zero?
    raise ArgumentError, 'The command stager could not be generated'
  end

  vprint_status("Generated command stager: #{cmd_list.inspect}")

  cmd_list
end

#guess_flavorSymbol?

Guess the cmd stager flavor to use using information from the module, target or platform.

Returns:

  • (Symbol)

    The cmd stager flavor to use.

  • (nil)

    if the cmd stager flavor cannot be guessed.



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/msf/core/exploit/cmd_stager.rb', line 265

def guess_flavor
  # First try to guess a compatible flavor based on the module & target information.
  unless target_flavor.nil?
    case target_flavor
    when Array
      return target_flavor[0].to_sym
    when String
      return target_flavor.to_sym
    when Symbol
      return target_flavor
    end
  end

  # Second try to guess a compatible flavor based on the target platform.
  return nil unless target_platform.names.length == 1
  c_platform = target_platform.names.first
  case c_platform
  when /linux/i
    :bourne
  when /osx/i
    :bourne
  when /unix/i
    :bourne
  when /win/i
    :vbs
  else
    nil
  end
end

#initialize(info = {}) ⇒ Msf::Module::Exploit

Creates an instance of an exploit that uses an CMD Stager and register the datastore options provided by the mixin.

Parameters:

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

    Hash containing information to initialize the exploit.

Returns:

  • (Msf::Module::Exploit)

    the exploit module.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/msf/core/exploit/cmd_stager.rb', line 52

def initialize(info = {})
  super

  flavors = module_flavors
  flavors = STAGERS.keys if flavors.empty?
  flavors.unshift('auto')

  server_conditions = ['CMDSTAGER::FLAVOR', 'in', %w{auto tftp wget curl fetch lwprequest psh_invokewebrequest ftp_http}]
  register_options(
    [
      OptAddressLocal.new('SRVHOST', [true, 'The local host or network interface to listen on. This must be an address on the local machine or 0.0.0.0 to listen on all addresses.', '0.0.0.0' ], conditions: server_conditions),
      OptPort.new('SRVPORT', [true, "The local port to listen on.", 8080], conditions: server_conditions)
    ])

  register_advanced_options(
    [
      OptEnum.new('CMDSTAGER::FLAVOR', [false, 'The CMD Stager to use.', 'auto', flavors]),
      OptString.new('CMDSTAGER::DECODER', [false, 'The decoder stub to use.']),
      OptString.new('CMDSTAGER::TEMP', [false, 'Writable directory for staged files']),
      OptString.new('CMDSTAGER::URIPATH', [false, 'Payload URI path for supported stagers']),
      OptBool.new('CMDSTAGER::SSL', [false, 'Use SSL/TLS for supported stagers', false])
    ], self.class)
end

#module_flavorsArray

Returns all the compatible stager flavors specified by the module and each of its targets.

Returns:

  • (Array)

    the list of all compatible cmd stager flavors.



299
300
301
302
303
304
305
306
307
# File 'lib/msf/core/exploit/cmd_stager.rb', line 299

def module_flavors
  flavors = []
  flavors += Array(module_info['CmdStagerFlavor']) if module_info['CmdStagerFlavor']
  targets.each do |target|
    flavors += Array(target.opts['CmdStagerFlavor']) if target.opts['CmdStagerFlavor']
  end
  flavors.uniq!
  flavors.map { |flavor| flavor.to_s }
end

#opts_with_decoder(opts = {}) ⇒ Hash

Returns a hash with the :decoder option if possible

Parameters:

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

    Input Hash.

Returns:

  • (Hash)

    Hash with the input data and a :decoder option when possible.



202
203
204
205
206
# File 'lib/msf/core/exploit/cmd_stager.rb', line 202

def opts_with_decoder(opts = {})
  return opts if opts.include?(:decoder)
  return opts.merge(:decoder => decoder) if decoder
  opts
end

#progress(total, sent) ⇒ void

This method returns an undefined value.

Show the progress of the upload while cmd staging

Parameters:

  • total (Float)

    The total number of bytes to send.

  • sent (Float)

    The number of bytes sent.



174
175
176
177
178
# File 'lib/msf/core/exploit/cmd_stager.rb', line 174

def progress(total, sent)
  done = (sent.to_f / total.to_f) * 100
  percent = "%3.2f%%" % done.to_f
  print_status("Command Stager progress - %7s done (%d/%d bytes)" % [percent, sent, total])
end

#select_cmdstager(opts = {}) ⇒ void

This method returns an undefined value.

Selects the correct cmd stager and decoder stub to use

Parameters:

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

    Hash containing the options to select the correct cmd stager and decoder.

Options Hash (opts):

  • :flavor (Symbol)

    The cmd stager to use.

  • :decoder (Symbol)

    The decoder stub to use.

Raises:

  • (ArgumentError)

    raised if a cmd stager cannot be selected or it isn't compatible with the target platform.



189
190
191
192
193
194
# File 'lib/msf/core/exploit/cmd_stager.rb', line 189

def select_cmdstager(opts = {})
  self.flavor = select_flavor(opts)
  raise ArgumentError, "Unable to select CMD Stager" if flavor.nil?
  raise ArgumentError, "The CMD Stager '#{flavor}' isn't compatible with the target" unless compatible_flavor?(flavor)
  self.decoder = select_decoder(opts)
end

#select_decoder(opts = {}) ⇒ String?

Selects the correct cmd stager decoder to use based on three rules: (1) use the decoder provided in input options, (2) use the decoder provided by the user through datastore options, (3) select the default decoder for the current cmd stager flavor if available.

Parameters:

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

    Hash containing the options to select the correct decoder.

Options Hash (opts):

  • :decoder (String)

    The decoder stub to use.

Returns:

  • (String)

    The decoder.

  • (nil)

    if a decoder cannot be selected.



237
238
239
240
241
# File 'lib/msf/core/exploit/cmd_stager.rb', line 237

def select_decoder(opts = {})
  return opts[:decoder] if opts.include?(:decoder)
  return datastore['CMDSTAGER::DECODER'] unless datastore['CMDSTAGER::DECODER'].blank?
  default_decoder(flavor)
end

#select_flavor(opts = {}) ⇒ Symbol?

Selects the correct cmd stager to use based on three rules: (1) use the flavor provided in options, (2) use the flavor provided by the user through datastore options, (3) guess the flavor using the target platform.

Parameters:

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

    Hash containing the options to select the correct cmd stager

Options Hash (opts):

  • :flavor (Symbol)

    The cmd stager flavor to use.

Returns:

  • (Symbol)

    The flavor to use.

  • (nil)

    if a flavor cannot be selected.



252
253
254
255
256
257
258
# File 'lib/msf/core/exploit/cmd_stager.rb', line 252

def select_flavor(opts = {})
  return opts[:flavor].to_sym if opts.include?(:flavor)
  unless datastore['CMDSTAGER::FLAVOR'].blank? or datastore['CMDSTAGER::FLAVOR'] == 'auto'
    return datastore['CMDSTAGER::FLAVOR'].to_sym
  end
  guess_flavor
end

#target_flavorArray, ...

Returns the compatible stager flavors for the current target or module.

Returns:

  • (Array)

    the list of compatible cmd stager flavors.

  • (Symbol)

    the compatible cmd stager flavor.

  • (String)

    the compatible cmd stager flavor.

  • (nil)

    if there isn't any compatible flavor defined.



315
316
317
318
319
# File 'lib/msf/core/exploit/cmd_stager.rb', line 315

def target_flavor
  return target.opts['CmdStagerFlavor'] if target && target.opts['CmdStagerFlavor']
  return module_info['CmdStagerFlavor'] if module_info['CmdStagerFlavor']
  nil
end