Class: Msf::Sessions::Meterpreter

Overview

This class represents a session compatible interface to a meterpreter server instance running on a remote machine. It provides the means of interacting with the server instance both at an API level as well as at a console level.

Constant Summary

Constants included from Rex::Post::Meterpreter::PacketDispatcher

Rex::Post::Meterpreter::PacketDispatcher::PacketTimeout

Instance Attribute Summary collapse

Attributes included from Msf::Session::Interactive

#ring

Attributes included from Rex::Ui::Interactive

#completed, #interacting, #on_command_proc, #on_print_proc

Attributes included from Rex::Ui::Subscriber::Input

#user_input

Attributes included from Rex::Ui::Subscriber::Output

#user_output

Attributes included from Msf::Session

#alive, #db_record, #exploit, #exploit_datastore, #exploit_task, #exploit_uuid, #framework, #info, #routes, #sid, #sname, #target_host, #target_port, #username, #uuid, #workspace

Attributes included from Framework::Offspring

#framework

Attributes inherited from Rex::Post::Meterpreter::Client

#alive, #capabilities, #comm_timeout, #commands, #conn_id, #encode_unicode, #expiration, #ext, #passive_dispatcher, #response_timeout, #send_keepalives, #sock, #ssl, #url

Attributes included from Rex::Post::Meterpreter::ChannelContainer

#channels

Attributes included from Rex::Post::Meterpreter::PacketDispatcher

#comm_mutex, #passive_service, #recv_queue, #send_queue

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Msf::Session::Scriptable

#execute_script, included

Methods included from Msf::Session::Provider::SingleCommandShell

#set_shell_token_index, #shell_command_token, #shell_command_token_unix, #shell_command_token_win32, #shell_read_until_token

Methods included from Rex::Socket::Comm

#chainable?, create, #deregister_event_handler, #each_event_handler, #notify_before_socket_create, #notify_socket_created, #register_event_handler

Methods included from Msf::Session::Interactive

#interactive?, #tunnel_local, #tunnel_peer

Methods included from Rex::Ui::Interactive

#detach, #interact

Methods included from Rex::Ui::Subscriber

#copy_ui

Methods included from Rex::Ui::Subscriber::Input

#gets

Methods included from Rex::Ui::Subscriber::Output

#flush, #print, #print_debug, #print_error, #print_good, #print_line, #print_status, #print_warning

Methods included from Msf::Session

#alive?, #dead?, #inspect, #interactive?, #log_file_name, #log_from_local, #log_from_remote, #log_source, #name, #name=, #register?, #session_host, #session_host=, #session_port, #session_port=, #set_from_exploit, #set_via, #tunnel_local, #tunnel_peer, #tunnel_to_s, #via_exploit, #via_payload

Methods inherited from Rex::Post::Meterpreter::Client

#add_extension, check_ext_hash, #cleanup_meterpreter, default_timeout, #deregister_extension, #deregister_extension_alias, #dump_extension_tree, #each_extension, #generate_ssl_context, #init_meterpreter, lookup_error, #method_missing, #register_extension_alias, #register_extension_aliases, set_ext_hash, #swap_sock_plain_to_ssl, #swap_sock_ssl_to_plain, #unicode_filter_decode, #unicode_filter_encode

Methods included from Rex::Post::Meterpreter::ChannelContainer

#add_channel, #find_channel, #initialize_channels, #remove_channel

Methods included from Rex::Post::Meterpreter::PacketDispatcher

#add_response_waiter, #deregister_inbound_handler, #dispatch_inbound_packet, #initialize_inbound_handlers, #initialize_passive_dispatcher, #monitor_socket, #monitor_stop, #notify_response_waiter, #on_passive_request, #receive_packet, #register_inbound_handler, #remove_response_waiter, #send_packet, #send_packet_wait_response, #send_request, #shutdown_passive_dispatcher

Constructor Details

#initialize(rstream, opts = {}) ⇒ Meterpreter

Initializes a meterpreter session instance using the supplied rstream that is to be used as the client's connection to the server.


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/base/sessions/meterpreter.rb', line 45

def initialize(rstream, opts={})
  super

  opts[:capabilities] = {
    :ssl => supports_ssl?,
    :zlib => supports_zlib?
  }
  if not opts[:skip_ssl]
    # the caller didn't request to skip ssl, so make sure we support it
    opts.merge!(:skip_ssl => (not supports_ssl?))
  end

  #
  # Initialize the meterpreter client
  #
  self.init_meterpreter(rstream, opts)

  #
  # Create the console instance
  #
  self.console = Rex::Post::Meterpreter::Ui::Console.new(self)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Rex::Post::Meterpreter::Client

Instance Attribute Details

#binary_suffixObject

Returns the value of attribute binary_suffix


432
433
434
# File 'lib/msf/base/sessions/meterpreter.rb', line 432

def binary_suffix
  @binary_suffix
end

#consoleObject

:nodoc:


433
434
435
# File 'lib/msf/base/sessions/meterpreter.rb', line 433

def console
  @console
end

#platformObject

Returns the value of attribute platform


431
432
433
# File 'lib/msf/base/sessions/meterpreter.rb', line 431

def platform
  @platform
end

#skip_sslObject

Returns the value of attribute skip_ssl


434
435
436
# File 'lib/msf/base/sessions/meterpreter.rb', line 434

def skip_ssl
  @skip_ssl
end

#target_idObject

Returns the value of attribute target_id


435
436
437
# File 'lib/msf/base/sessions/meterpreter.rb', line 435

def target_id
  @target_id
end

Class Method Details

.typeObject

Returns the session type as being 'meterpreter'.


71
72
73
# File 'lib/msf/base/sessions/meterpreter.rb', line 71

def self.type
  "meterpreter"
end

Instance Method Details

#_interactObject

:category: Msf::Session::Interactive implementors

Interacts with the meterpreter client at a user interface level.

Raises:

  • (EOFError)

390
391
392
393
394
395
396
397
398
399
400
401
# File 'lib/msf/base/sessions/meterpreter.rb', line 390

def _interact
  framework.events.on_session_interact(self)
  # Call the console interaction subsystem of the meterpreter client and
  # pass it a block that returns whether or not we should still be
  # interacting.  This will allow the shell to abort if interaction is
  # canceled.
  console.interact { self.interacting != true }

  # If the stop flag has been set, then that means the user exited.  Raise
  # the EOFError so we can drop this handle like a bad habit.
  raise EOFError if (console.stopped? == true)
end

#cleanupObject

:category: Msf::Session overrides

Cleans up the meterpreter client session.


186
187
188
189
190
# File 'lib/msf/base/sessions/meterpreter.rb', line 186

def cleanup
  cleanup_meterpreter

  super
end

#create(param) ⇒ Object

:category: Msf::Session::Comm implementors

Creates a connection based on the supplied parameters and returns it to the caller. The connection is created relative to the remote machine on which the meterpreter server instance is running.


411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'lib/msf/base/sessions/meterpreter.rb', line 411

def create(param)
  sock = nil

  # Notify handlers before we create the socket
  notify_before_socket_create(self, param)

  sock = net.socket.create(param)

  # sf: unsure if we should raise an exception or just return nil. returning nil for now.
  #if( sock == nil )
  #  raise Rex::UnsupportedProtocol.new(param.proto), caller
  #end

  # Notify now that we've created the socket
  notify_socket_created(self, sock, param)

  # Return the socket to the caller
  sock
end

#descObject

:category: Msf::Session overrides

Returns the session description.


197
198
199
# File 'lib/msf/base/sessions/meterpreter.rb', line 197

def desc
  "Meterpreter"
end

#execute_file(full_path, args) ⇒ Object

:category: Msf::Session::Scriptable implementors

Runs the meterpreter script in the context of a script container


207
208
209
210
# File 'lib/msf/base/sessions/meterpreter.rb', line 207

def execute_file(full_path, args)
  o = Rex::Script::Meterpreter.new(self, full_path)
  o.run(args)
end

#init_ui(input, output) ⇒ Object

:category: Msf::Session::Interactive implementors

Initializes the console's I/O handles.


218
219
220
221
222
223
224
225
# File 'lib/msf/base/sessions/meterpreter.rb', line 218

def init_ui(input, output)
  self.user_input = input
  self.user_output = output
  console.init_ui(input, output)
  console.set_log_source(log_source)

  super
end

#killObject

Terminates the session


240
241
242
243
244
245
246
247
# File 'lib/msf/base/sessions/meterpreter.rb', line 240

def kill
  begin
    cleanup_meterpreter
    self.sock.close
  rescue ::Exception
  end
  framework.sessions.deregister(self)
end

#load_privObject

Load the priv extension.


278
279
280
281
282
283
284
# File 'lib/msf/base/sessions/meterpreter.rb', line 278

def load_priv()
  original = console.disable_output

  console.disable_output = true
  console.run_single('load priv')
  console.disable_output = original
end

#load_session_infoObject

Populate the session information.

Also reports a session_fingerprint note for host os normalization.


291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/msf/base/sessions/meterpreter.rb', line 291

def load_session_info()
  begin
    ::Timeout.timeout(60) do
      # Gather username/system information
      username  = self.sys.config.getuid
      sysinfo   = self.sys.config.sysinfo

      safe_info = "#{username} @ #{sysinfo['Computer']}"
      safe_info.force_encoding("ASCII-8BIT") if safe_info.respond_to?(:force_encoding)
      # Should probably be using Rex::Text.ascii_safe_hex but leave
      # this as is for now since "\xNN" is arguably uglier than "_"
      # showing up in various places in the UI.
      safe_info.gsub!(/[\x00-\x08\x0b\x0c\x0e-\x19\x7f-\xff]+/n,"_")
      self.info = safe_info

      hobj = nil

      nhost = find_internet_connected_address

      original_session_host = self.session_host
      # If we found a better IP address for this session, change it up
      # only handle cases where the DB is not connected here
      if !(framework.db && framework.db.active)
        self.session_host = nhost
      end

      # The rest of this requires a database, so bail if it's not
      # there
      return if !(framework.db && framework.db.active)

      ::ActiveRecord::Base.connection_pool.with_connection {
        wspace = framework.db.find_workspace(workspace)

        # Account for finding ourselves on a different host
        if nhost and self.db_record
          # Create or switch to a new host in the database
          hobj = framework.db.report_host(:workspace => wspace, :host => nhost)
          if hobj
            self.session_host = nhost
            self.db_record.host_id = hobj[:id]
          end
        end

        framework.db.report_note({
          :type => "host.os.session_fingerprint",
          :host => self,
          :workspace => wspace,
          :data => {
            :name => sysinfo["Computer"],
            :os => sysinfo["OS"],
            :arch => sysinfo["Architecture"],
          }
        })

        if self.db_record
          self.db_record.desc = safe_info
          self.db_record.save!
        end

        framework.db.update_host_via_sysinfo(:host => self, :workspace => wspace, :info => sysinfo)

        if nhost
          framework.db.report_note({
            :type      => "host.nat.server",
            :host      => original_session_host,
            :workspace => wspace,
            :data      => { :info   => "This device is acting as a NAT gateway for #{nhost}", :client => nhost },
            :update    => :unique_data
          })
          framework.db.report_host(:host => original_session_host, :purpose => 'firewall' )

          framework.db.report_note({
            :type      => "host.nat.client",
            :host      => nhost,
            :workspace => wspace,
            :data      => { :info => "This device is traversing NAT gateway #{original_session_host}", :server => original_session_host },
            :update    => :unique_data
          })
          framework.db.report_host(:host => nhost, :purpose => 'client' )
        end
      }

    end
  rescue ::Interrupt
    dlog("Interrupt while loading sysinfo: #{e.class}: #{e}")
    raise $!
  rescue ::Exception => e
    # Log the error but otherwise ignore it so we don't kill the
    # session if reporting failed for some reason
    elog("Error loading sysinfo: #{e.class}: #{e}")
    dlog("Call stack:\n#{e.backtrace.join("\n")}")
  end
end

#load_stdapiObject

Load the stdapi extension.


268
269
270
271
272
273
# File 'lib/msf/base/sessions/meterpreter.rb', line 268

def load_stdapi()
  original = console.disable_output
  console.disable_output = true
  console.run_single('load stdapi')
  console.disable_output = original
end

#lookup_error(code) ⇒ Object

Called by PacketDispatcher to resolve error codes to names. This is the default version (return the number itself)


177
178
179
# File 'lib/msf/base/sessions/meterpreter.rb', line 177

def lookup_error(code)
  "#{code}"
end

#queue_cmd(cmd) ⇒ Object

Run the supplied command as if it came from suer input.


252
253
254
# File 'lib/msf/base/sessions/meterpreter.rb', line 252

def queue_cmd(cmd)
  console.queue_cmd(cmd)
end

#reset_uiObject

:category: Msf::Session::Interactive implementors

Resets the console's I/O handles.


232
233
234
235
# File 'lib/msf/base/sessions/meterpreter.rb', line 232

def reset_ui
  console.unset_log_source
  console.reset_ui
end

#run_cmd(cmd) ⇒ Object

:category: Msf::Session::Interactive implementors

Explicitly runs a command in the meterpreter console.


261
262
263
# File 'lib/msf/base/sessions/meterpreter.rb', line 261

def run_cmd(cmd)
  console.run_single(cmd)
end

#shell_closeObject

:category: Msf::Session::Provider::SingleCommandShell implementors

Terminate the shell channel


148
149
150
151
# File 'lib/msf/base/sessions/meterpreter.rb', line 148

def shell_close
  @shell.close
  @shell = nil
end

#shell_command(cmd) ⇒ Object


153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/msf/base/sessions/meterpreter.rb', line 153

def shell_command(cmd)
  # Send the shell channel's stdin.
  shell_write(cmd + "\n")

  timeout = 5
  etime = ::Time.now.to_f + timeout
  buff = ""

  # Keep reading data until no more data is available or the timeout is
  # reached.
  while (::Time.now.to_f < etime)
    res = shell_read(-1, timeout)
    break unless res
    timeout = etime - ::Time.now.to_f
    buff << res
  end

  buff
end

#shell_initObject

:category: Msf::Session::Provider::SingleCommandShell implementors

Create a channelized shell process on the target


87
88
89
90
91
92
93
94
95
# File 'lib/msf/base/sessions/meterpreter.rb', line 87

def shell_init
  return true if @shell

  # COMSPEC is special-cased on all meterpreters to return a viable
  # shell.
  sh = fs.file.expand_path("%COMSPEC%")
  @shell = sys.process.execute(sh, nil, { "Hidden" => true, "Channelized" => true })

end

#shell_read(length = nil, timeout = 1) ⇒ Object

:category: Msf::Session::Provider::SingleCommandShell implementors

Read from the command shell.


102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/msf/base/sessions/meterpreter.rb', line 102

def shell_read(length=nil, timeout=1)
  shell_init

  length = nil if length.nil? or length < 0
  begin
    rv = nil
    # Meterpreter doesn't offer a way to timeout on the victim side, so
    # we have to do it here.  I'm concerned that this will cause loss
    # of data.
    Timeout.timeout(timeout) {
      rv = @shell.channel.read(length)
    }
    framework.events.on_session_output(self, rv) if rv
    return rv
  rescue ::Timeout::Error
    return nil
  rescue ::Exception => e
    shell_close
    raise e
  end
end

#shell_write(buf) ⇒ Object

:category: Msf::Session::Provider::SingleCommandShell implementors

Write to the command shell.


129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/msf/base/sessions/meterpreter.rb', line 129

def shell_write(buf)
  shell_init

  begin
    framework.events.on_session_command(self, buf.strip)
    len = @shell.channel.write("#{buf}\n")
  rescue ::Exception => e
    shell_close
    raise e
  end

  len
end

#supports_ssl?Boolean

Override for server implementations that can't do ssl

Returns:

  • (Boolean)

34
35
36
# File 'lib/msf/base/sessions/meterpreter.rb', line 34

def supports_ssl?
  true
end

#supports_zlib?Boolean

Returns:

  • (Boolean)

37
38
39
# File 'lib/msf/base/sessions/meterpreter.rb', line 37

def supports_zlib?
  true
end

#typeObject

Calls the class method


78
79
80
# File 'lib/msf/base/sessions/meterpreter.rb', line 78

def type
  self.class.type
end