class Rex::Post::Meterpreter::Client

This class represents a logical meterpreter client class. This class provides an interface that is compatible with the Rex post-exploitation interface in terms of the feature set that it attempts to expose. This class is meant to drive a single meterpreter client session.

Attributes

alive[RW]

Whether this session is alive. If the socket is disconnected or broken, this will be false

capabilities[RW]

The libraries available to this meterpreter server

comm_timeout[RW]

The Communication Timeout

commands[R]

A list of the commands

conn_id[RW]

The Connection ID

encode_unicode[RW]

Flag indicating whether to hex-encode UTF-8 file names and other strings

expiration[RW]

The Session Expiration Timeout

ext[R]

The extension alias under which all extensions can be accessed by name. For example:

client.ext.stdapi
passive_dispatcher[RW]

The Passive Dispatcher

response_timeout[RW]

The timeout value to use when waiting for responses.

send_keepalives[RW]

Whether to send pings every so often to determine liveness.

sock[R]

The socket the client is communicating over.

ssl[RW]

Use SSL (HTTPS)

target_id[RW]

The unique target identifier for this payload

url[RW]

The Connect URL

Public Class Methods

check_ext_hash(name) click to toggle source

Checks the extension hash to see if a class has already been associated with the supplied extension name.

# File lib/rex/post/meterpreter/client.rb, line 64
def self.check_ext_hash(name)
  @@ext_hash[name]
end
default_timeout() click to toggle source

Returns the default timeout that request packets will use when waiting for a response.

# File lib/rex/post/meterpreter/client.rb, line 276
def Client.default_timeout
  return 300
end
lookup_error(code) click to toggle source

Lookup the error that occurred

# File lib/rex/post/meterpreter/client.rb, line 56
def self.lookup_error(code)
  code
end
new(sock,opts={}) click to toggle source

Initializes the client context with the supplied socket through which communication with the server will be performed.

# File lib/rex/post/meterpreter/client.rb, line 79
def initialize(sock,opts={})
  init_meterpreter(sock, opts)
end
set_ext_hash(name, klass) click to toggle source

Stores the name to class association for the supplied extension name.

# File lib/rex/post/meterpreter/client.rb, line 71
def self.set_ext_hash(name, klass)
  @@ext_hash[name] = klass
end

Public Instance Methods

add_extension(name, commands=[]) click to toggle source

Loads the client half of the supplied extension and initializes it as a registered extension that can be reached through client.ext..

# File lib/rex/post/meterpreter/client.rb, line 305
def add_extension(name, commands=[])
  self.commands |= commands

  # Check to see if this extension has already been loaded.
  if ((klass = self.class.check_ext_hash(name.downcase)) == nil)
    old = Rex::Post::Meterpreter::Extensions.constants
    require("rex/post/meterpreter/extensions/#{name.downcase}/#{name.downcase}")
    new = Rex::Post::Meterpreter::Extensions.constants

    # No new constants added?
    if ((diff = new - old).empty?)
      diff = [ name.capitalize ]
    end

    klass = Rex::Post::Meterpreter::Extensions.const_get(diff[0]).const_get(diff[0])

    # Save the module name to class association now that the code is
    # loaded.
    self.class.set_ext_hash(name.downcase, klass)
  end

  # Create a new instance of the extension
  inst = klass.new(self)

  self.ext.aliases[inst.name] = inst

  return true
end
cleanup_meterpreter() click to toggle source

Cleans up the meterpreter instance, terminating the dispatcher thread.

# File lib/rex/post/meterpreter/client.rb, line 86
def cleanup_meterpreter
  ext.aliases.each_value do | extension |
    extension.cleanup if extension.respond_to?( 'cleanup' )
  end
  dispatcher_thread.kill if dispatcher_thread
  core.shutdown rescue nil
  shutdown_passive_dispatcher
end
deregister_extension(name) click to toggle source

Deregisters an extension alias of the supplied name.

# File lib/rex/post/meterpreter/client.rb, line 337
def deregister_extension(name)
  self.ext.aliases.delete(name)
end
deregister_extension_alias(name) click to toggle source

Deregisters a previously registered extension alias.

# File lib/rex/post/meterpreter/client.rb, line 380
def deregister_extension_alias(name)
  self.ext_aliases.aliases.delete(name)
end
dump_extension_tree() click to toggle source

Dumps the extension tree.

# File lib/rex/post/meterpreter/client.rb, line 387
def dump_extension_tree()
  items = []
  items.concat(self.ext.dump_alias_tree('client.ext'))
  items.concat(self.ext_aliases.dump_alias_tree('client'))

  return items.sort
end
each_extension(&block) click to toggle source

Enumerates all of the loaded extensions.

# File lib/rex/post/meterpreter/client.rb, line 344
def each_extension(&block)
  self.ext.aliases.each(block)
end
generate_ssl_context() click to toggle source
# File lib/rex/post/meterpreter/client.rb, line 201
def generate_ssl_context
  @@ssl_mutex.synchronize do
  if not @@ssl_ctx

  wlog("Generating SSL certificate for Meterpreter sessions")

  key  = OpenSSL::PKey::RSA.new(1024){ }
  cert = OpenSSL::X509::Certificate.new
  cert.version = 2
  cert.serial  = rand(0xFFFFFFFF)

  # Depending on how the socket was created, getsockname will
  # return either a struct sockaddr as a String (the default ruby
  # Socket behavior) or an Array (the extend'd Rex::Socket::Tcp
  # behavior). Avoid the ambiguity by always picking a random
  # hostname. See #7350.
  subject_cn = Rex::Text.rand_hostname

  subject = OpenSSL::X509::Name.new([
      ["C","US"],
      ['ST', Rex::Text.rand_state()],
      ["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
      ["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
      ["CN", subject_cn],
    ])
  issuer = OpenSSL::X509::Name.new([
      ["C","US"],
      ['ST', Rex::Text.rand_state()],
      ["L", Rex::Text.rand_text_alpha(rand(20) + 10)],
      ["O", Rex::Text.rand_text_alpha(rand(20) + 10)],
      ["CN", Rex::Text.rand_text_alpha(rand(20) + 10)],
    ])

  cert.subject = subject
  cert.issuer = issuer
  cert.not_before = Time.now - (3600 * 365) + rand(3600 * 14)
  cert.not_after = Time.now + (3600 * 365) + rand(3600 * 14)
  cert.public_key = key.public_key
  ef = OpenSSL::X509::ExtensionFactory.new(nil,cert)
  cert.extensions = [
    ef.create_extension("basicConstraints","CA:FALSE"),
    ef.create_extension("subjectKeyIdentifier","hash"),
    ef.create_extension("extendedKeyUsage","serverAuth"),
    ef.create_extension("keyUsage","keyEncipherment,dataEncipherment,digitalSignature")
  ]
  ef.issuer_certificate = cert
  cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
  cert.sign(key, OpenSSL::Digest::SHA1.new)

  ctx = OpenSSL::SSL::SSLContext.new(:SSLv3)
  ctx.key = key
  ctx.cert = cert

  ctx.session_id_context = Rex::Text.rand_text(16)

  wlog("Generated SSL certificate for Meterpreter sessions")

  @@ssl_ctx = ctx

  end # End of if not @ssl_ctx
  end # End of mutex.synchronize

  @@ssl_ctx
end
init_meterpreter(sock,opts={}) click to toggle source

Initializes the meterpreter client instance

# File lib/rex/post/meterpreter/client.rb, line 98
def init_meterpreter(sock,opts={})
  self.sock         = sock
  self.parser       = PacketParser.new
  self.ext          = ObjectAliases.new
  self.ext_aliases  = ObjectAliases.new
  self.alive        = true
  self.target_id    = opts[:target_id]
  self.capabilities = opts[:capabilities] || {}
  self.commands     = []


  self.conn_id      = opts[:conn_id]
  self.url          = opts[:url]
  self.ssl          = opts[:ssl]
  self.expiration   = opts[:expiration]
  self.comm_timeout = opts[:comm_timeout]
  self.passive_dispatcher = opts[:passive_dispatcher]

  self.response_timeout = opts[:timeout] || self.class.default_timeout
  self.send_keepalives  = true
  # self.encode_unicode   = opts.has_key?(:encode_unicode) ? opts[:encode_unicode] : true
  self.encode_unicode = false

  if opts[:passive_dispatcher]
    initialize_passive_dispatcher

    register_extension_alias('core', ClientCore.new(self))

    initialize_inbound_handlers
    initialize_channels

    # Register the channel inbound packet handler
    register_inbound_handler(Rex::Post::Meterpreter::Channel)
  else
    # Switch the socket to SSL mode and receive the hello if needed
    if capabilities[:ssl] and not opts[:skip_ssl]
      swap_sock_plain_to_ssl()
    end

    register_extension_alias('core', ClientCore.new(self))

    initialize_inbound_handlers
    initialize_channels

    # Register the channel inbound packet handler
    register_inbound_handler(Rex::Post::Meterpreter::Channel)

    monitor_socket
  end
end
method_missing(symbol, *args) click to toggle source

Translates unhandled methods into registered extension aliases if a matching extension alias exists for the supplied symbol.

# File lib/rex/post/meterpreter/client.rb, line 290
def method_missing(symbol, *args)
  #$stdout.puts("method_missing: #{symbol}")
  self.ext_aliases.aliases[symbol.to_s]
end
register_extension_alias(name, ext) click to toggle source

Registers an aliased extension that can be referenced through client.name.

# File lib/rex/post/meterpreter/client.rb, line 352
def register_extension_alias(name, ext)
  self.ext_aliases.aliases[name] = ext
  # Whee!  Syntactic sugar, where art thou?
  #
  # Create an instance method on this object called +name+ that returns
  # +ext+.  We have to do it this way instead of simply
  # self.class.class_eval so that other meterpreter sessions don't get
  # extension methods when this one does
  (class << self; self; end).class_eval do
    define_method(name.to_sym) do
      ext
    end
  end
  ext
end
register_extension_aliases(aliases) click to toggle source

Registers zero or more aliases that are provided in an array.

# File lib/rex/post/meterpreter/client.rb, line 371
def register_extension_aliases(aliases)
  aliases.each { |a|
    register_extension_alias(a['name'], a['ext'])
  }
end
swap_sock_plain_to_ssl() click to toggle source
# File lib/rex/post/meterpreter/client.rb, line 149
def swap_sock_plain_to_ssl
  # Create a new SSL session on the existing socket
  ctx = generate_ssl_context()
  ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)

  # Use non-blocking OpenSSL operations on Windows
  if !( ssl.respond_to?(:accept_nonblock) and Rex::Compat.is_windows )
    ssl.accept
  else
    begin
      ssl.accept_nonblock

    # Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno
    rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK
        IO::select(nil, nil, nil, 0.10)
        retry

    # Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable
    rescue ::Exception => e
      if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)
        IO::select( [ ssl ], nil, nil, 0.10 )
        retry
      end

      if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)
        IO::select( nil, [ ssl ], nil, 0.10 )
        retry
      end

      raise e
    end
  end

  self.sock.extend(Rex::Socket::SslTcp)
  self.sock.sslsock = ssl
  self.sock.sslctx  = ctx

  tag = self.sock.get_once(-1, 30)
  if(not tag or tag !~ /^GET \//)
    raise RuntimeError, "Could not read the HTTP hello token"
  end
end
swap_sock_ssl_to_plain() click to toggle source
# File lib/rex/post/meterpreter/client.rb, line 192
def swap_sock_ssl_to_plain
  # Remove references to the SSLSocket and Context
  self.sock.sslsock.close
  self.sock.sslsock = nil
  self.sock.sslctx  = nil
  self.sock = self.sock.fd
  self.sock.extend(::Rex::Socket::Tcp)
end
unicode_filter_decode(str) click to toggle source

Decodes (or not) a UTF-8 string

# File lib/rex/post/meterpreter/client.rb, line 405
def unicode_filter_decode(str)
  self.encode_unicode ? Rex::Text.unicode_filter_decode(str) : str
end
unicode_filter_encode(str) click to toggle source

Encodes (or not) a UTF-8 string

# File lib/rex/post/meterpreter/client.rb, line 398
def unicode_filter_encode(str)
  self.encode_unicode ? Rex::Text.unicode_filter_encode(str) : str
end