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
Whether this session is alive. If the socket is disconnected or broken, this will be false
The libraries available to this meterpreter server
The Communication Timeout
A list of the commands
The Connection ID
Flag indicating whether to hex-encode UTF-8 file names and other strings
The Session Expiration Timeout
The extension alias under which all extensions can be accessed by name. For example:
client.ext.stdapi
The timestamp of the last received response
The Passive Dispatcher
The timeout value to use when waiting for responses.
The total time for retrying connections
The time to wait between retry attempts
Whether to send pings every so often to determine liveness.
The socket the client is communicating over.
Use SSL (HTTPS)
Use this SSL Certificate (unified PEM)
The unique target identifier for this payload
The Connect URL
Public Class Methods
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
Returns the default timeout that request packets will use when waiting for a response.
# File lib/rex/post/meterpreter/client.rb, line 274 def Client.default_timeout return 300 end
Lookup the error that occurred
# File lib/rex/post/meterpreter/client.rb, line 56 def self.lookup_error(code) code end
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
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
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 303 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
Cleans up the meterpreter instance, terminating the dispatcher thread.
# File lib/rex/post/meterpreter/client.rb, line 86 def cleanup_meterpreter if not self.skip_cleanup ext.aliases.each_value do | extension | extension.cleanup if extension.respond_to?( 'cleanup' ) end end dispatcher_thread.kill if dispatcher_thread if not self.skip_cleanup core.shutdown rescue nil end shutdown_passive_dispatcher end
Deregisters an extension alias of the supplied name.
# File lib/rex/post/meterpreter/client.rb, line 335 def deregister_extension(name) self.ext.aliases.delete(name) end
Deregisters a previously registered extension alias.
# File lib/rex/post/meterpreter/client.rb, line 378 def deregister_extension_alias(name) self.ext_aliases.aliases.delete(name) end
Dumps the extension tree.
# File lib/rex/post/meterpreter/client.rb, line 385 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
Enumerates all of the loaded extensions.
# File lib/rex/post/meterpreter/client.rb, line 342 def each_extension(&block) self.ext.aliases.each(block) end
# File lib/rex/post/meterpreter/client.rb, line 224 def generate_ssl_context ctx = nil ssl_cert_info = nil loop do # Load a custom SSL certificate if one has been specified if self.ssl_cert wlog("Loading custom SSL certificate for Meterpreter session") ssl_cert_info = Rex::Socket::SslTcpServer.ssl_parse_pem(self.ssl_cert) wlog("Loaded custom SSL certificate for Meterpreter session") break end # Generate a certificate if necessary and cache it if ! @@ssl_cached_cert @@ssl_mutex.synchronize do wlog("Generating SSL certificate for Meterpreter sessions") @@ssl_cached_cert = Rex::Socket::SslTcpServer.ssl_generate_certificate wlog("Generated SSL certificate for Meterpreter sessions") end end # Use the cached certificate ssl_cert_info = @@ssl_cached_cert break end # Create a new context for each session ctx = OpenSSL::SSL::SSLContext.new() ctx.key = ssl_cert_info[0] ctx.cert = ssl_cert_info[1] ctx.extra_chain_cert = ssl_cert_info[2] ctx.options = 0 ctx.session_id_context = Rex::Text.rand_text(16) ctx end
Initializes the meterpreter client instance
# File lib/rex/post/meterpreter/client.rb, line 105 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.last_checkin = Time.now 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.retry_total = opts[:retry_total] self.retry_wait = opts[:retry_wait] self.passive_dispatcher = opts[:passive_dispatcher] self.response_timeout = opts[:timeout] || self.class.default_timeout self.send_keepalives = true # TODO: Clarify why we don't allow unicode to be set in initial options # self.encode_unicode = opts.has_key?(:encode_unicode) ? opts[:encode_unicode] : true self.encode_unicode = false # The SSL certificate is being passed down as a file path if opts[:ssl_cert] if ! ::File.exists? opts[:ssl_cert] elog("SSL certificate at #{opts[:ssl_cert]} does not exist and will be ignored") else # Load the certificate the same way that SslTcpServer does it self.ssl_cert = ::File.read(opts[:ssl_cert]) end end 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
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 288 def method_missing(symbol, *args) #$stdout.puts("method_missing: #{symbol}") self.ext_aliases.aliases[symbol.to_s] end
Registers an aliased extension that can be referenced through client.name.
# File lib/rex/post/meterpreter/client.rb, line 350 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
Registers zero or more aliases that are provided in an array.
# File lib/rex/post/meterpreter/client.rb, line 369 def register_extension_aliases(aliases) aliases.each { |a| register_extension_alias(a['name'], a['ext']) } end
# File lib/rex/post/meterpreter/client.rb, line 170 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 self.sock.sslhash = Rex::Text.sha1_raw(ctx.cert.to_der) tag = self.sock.get_once(-1, 30) if(not tag or tag !~ /^GET \//) raise RuntimeError, "Could not read the HTTP hello token" end end
# File lib/rex/post/meterpreter/client.rb, line 214 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.sslhash = nil self.sock = self.sock.fd self.sock.extend(::Rex::Socket::Tcp) end
Decodes (or not) a UTF-8 string
# File lib/rex/post/meterpreter/client.rb, line 403 def unicode_filter_decode(str) self.encode_unicode ? Rex::Text.unicode_filter_decode(str) : str end
Encodes (or not) a UTF-8 string
# File lib/rex/post/meterpreter/client.rb, line 396 def unicode_filter_encode(str) self.encode_unicode ? Rex::Text.unicode_filter_encode(str) : str end