Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/rex/post/meterpreter/client.rb
Views: 11784
# -*- coding: binary -*-12require 'socket'3require 'openssl'45require 'rex/post/channel'6require 'rex/post/meterpreter/extension_mapper'7require 'rex/post/meterpreter/client_core'8require 'rex/post/meterpreter/channel'9require 'rex/post/meterpreter/dependencies'10require 'rex/post/meterpreter/object_aliases'11require 'rex/post/meterpreter/packet'12require 'rex/post/meterpreter/packet_parser'13require 'rex/post/meterpreter/packet_dispatcher'14require 'rex/post/meterpreter/pivot'15require 'rex/post/meterpreter/pivot_container'1617module Rex18module Post19module Meterpreter2021#22# Just to get it in there...23#24module Extensions25end2627###28#29# This class represents a logical meterpreter client class. This class30# provides an interface that is compatible with the Rex post-exploitation31# interface in terms of the feature set that it attempts to expose. This32# class is meant to drive a single meterpreter client session.33#34###35class Client3637include Rex::Post::Channel::Container38include Rex::Post::Meterpreter::PacketDispatcher39include Rex::Post::Meterpreter::PivotContainer4041#42# Extension name to class hash.43#44@@ext_hash = {}4546#47# Cached auto-generated SSL certificate48#49@@ssl_cached_cert = nil5051#52# Mutex to synchronize class-wide operations53#54@@ssl_mutex = ::Mutex.new5556#57# Lookup the error that occurred58#59def self.lookup_error(code)60code61end6263#64# Checks the extension hash to see if a class has already been associated65# with the supplied extension name.66#67def self.check_ext_hash(name)68@@ext_hash[name]69end7071#72# Stores the name to class association for the supplied extension name.73#74def self.set_ext_hash(name, klass)75@@ext_hash[name] = klass76end7778#79# Initializes the client context with the supplied socket through80# which communication with the server will be performed.81#82def initialize(sock, opts={})83init_meterpreter(sock, opts)84end8586#87# Cleans up the meterpreter instance, terminating the dispatcher thread.88#89def cleanup_meterpreter90if self.pivot_session91self.pivot_session.remove_pivot_session(self.session_guid)92end9394self.pivot_sessions.keys.each do |k|95pivot = self.pivot_sessions[k]96pivot.pivoted_session.kill('Pivot closed')97pivot.pivoted_session.shutdown_passive_dispatcher98end99100unless self.skip_cleanup101ext.aliases.each_value do | extension |102extension.cleanup if extension.respond_to?( 'cleanup' )103end104end105106dispatcher_thread.kill if dispatcher_thread107108unless self.skip_cleanup109core.shutdown rescue nil110end111112shutdown_passive_dispatcher113114shutdown_tlv_logging115end116117#118# Initializes the meterpreter client instance119#120def init_meterpreter(sock,opts={})121self.sock = sock122self.parser = PacketParser.new123self.ext = ObjectAliases.new124self.ext_aliases = ObjectAliases.new125self.alive = true126self.target_id = opts[:target_id]127self.capabilities = opts[:capabilities] || {}128self.commands = []129self.last_checkin = ::Time.now130131self.conn_id = opts[:conn_id]132self.url = opts[:url]133self.ssl = opts[:ssl]134135self.pivot_session = opts[:pivot_session]136if self.pivot_session137self.expiration = self.pivot_session.expiration138self.comm_timeout = self.pivot_session.comm_timeout139self.retry_total = self.pivot_session.retry_total140self.retry_wait = self.pivot_session.retry_wait141else142self.expiration = opts[:expiration]143self.comm_timeout = opts[:comm_timeout]144self.retry_total = opts[:retry_total]145self.retry_wait = opts[:retry_wait]146self.passive_dispatcher = opts[:passive_dispatcher]147end148149self.response_timeout = opts[:timeout] || self.class.default_timeout150self.send_keepalives = true151152# TODO: Clarify why we don't allow unicode to be set in initial options153# self.encode_unicode = opts.has_key?(:encode_unicode) ? opts[:encode_unicode] : true154self.encode_unicode = false155156self.aes_key = nil157self.session_guid = opts[:session_guid] || "\x00" * 16158159# The SSL certificate is being passed down as a file path160if opts[:ssl_cert]161if ! ::File.exist? opts[:ssl_cert]162elog("SSL certificate at #{opts[:ssl_cert]} does not exist and will be ignored")163else164# Load the certificate the same way that SslTcpServer does it165self.ssl_cert = ::File.read(opts[:ssl_cert])166end167end168# Use the debug build if specified169self.debug_build = opts[:debug_build]170171172# Protocol specific dispatch mixins go here, this may be neater with explicit Client classes173opts[:dispatch_ext].each {|dx| self.extend(dx)} if opts[:dispatch_ext]174initialize_passive_dispatcher if opts[:passive_dispatcher]175176register_extension_alias('core', ClientCore.new(self))177178initialize_inbound_handlers179initialize_channels180initialize_pivots181182# Register the channel and pivot inbound packet handlers183register_inbound_handler(Rex::Post::Meterpreter::Channel)184register_inbound_handler(Rex::Post::Meterpreter::Pivot)185186monitor_socket187end188189def swap_sock_plain_to_ssl190# Create a new SSL session on the existing socket191ctx = generate_ssl_context()192ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)193194# Use non-blocking OpenSSL operations on Windows195if !( ssl.respond_to?(:accept_nonblock) and Rex::Compat.is_windows )196ssl.accept197else198begin199ssl.accept_nonblock200201# Ruby 1.8.7 and 1.9.0/1.9.1 uses a standard Errno202rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK203IO::select(nil, nil, nil, 0.10)204retry205206# Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable207rescue ::Exception => e208if ::IO.const_defined?('WaitReadable') and e.kind_of?(::IO::WaitReadable)209IO::select( [ ssl ], nil, nil, 0.10 )210retry211end212213if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable)214IO::select( nil, [ ssl ], nil, 0.10 )215retry216end217218raise e219end220end221222self.sock.extend(Rex::Socket::SslTcp)223self.sock.sslsock = ssl224self.sock.sslctx = ctx225self.sock.sslhash = Rex::Text.sha1_raw(ctx.cert.to_der)226227tag = self.sock.get_once(-1, 30)228if(not tag or tag !~ /^GET \//)229raise RuntimeError, "Could not read the HTTP hello token"230end231end232233def swap_sock_ssl_to_plain234# Remove references to the SSLSocket and Context235self.sock.sslsock.close236self.sock.sslsock = nil237self.sock.sslctx = nil238self.sock.sslhash = nil239self.sock = self.sock.fd240self.sock.extend(::Rex::Socket::Tcp)241end242243def generate_ssl_context244245ctx = nil246ssl_cert_info = nil247248loop do249250# Load a custom SSL certificate if one has been specified251if self.ssl_cert252wlog("Loading custom SSL certificate for Meterpreter session")253ssl_cert_info = Rex::Socket::SslTcpServer.ssl_parse_pem(self.ssl_cert)254wlog("Loaded custom SSL certificate for Meterpreter session")255break256end257258# Generate a certificate if necessary and cache it259if ! @@ssl_cached_cert260@@ssl_mutex.synchronize do261wlog("Generating SSL certificate for Meterpreter sessions")262@@ssl_cached_cert = Rex::Socket::SslTcpServer.ssl_generate_certificate263wlog("Generated SSL certificate for Meterpreter sessions")264end265end266267# Use the cached certificate268ssl_cert_info = @@ssl_cached_cert269break270end271272# Create a new context for each session273ctx = OpenSSL::SSL::SSLContext.new()274ctx.key = ssl_cert_info[0]275ctx.cert = ssl_cert_info[1]276ctx.extra_chain_cert = ssl_cert_info[2]277ctx.options = 0278ctx.session_id_context = Rex::Text.rand_text(16)279280ctx281end282283##284#285# Accessors286#287##288289#290# Returns the default timeout that request packets will use when291# waiting for a response.292#293def Client.default_timeout294return 300295end296297##298#299# Alias processor300#301##302303#304# Translates unhandled methods into registered extension aliases305# if a matching extension alias exists for the supplied symbol.306#307def method_missing(symbol, *args)308#$stdout.puts("method_missing: #{symbol}")309self.ext_aliases.aliases[symbol.to_s]310end311312##313#314# Extension registration315#316##317318#319# Loads the client half of the supplied extension and initializes it as a320# registered extension that can be reached through client.ext.[extension].321#322def add_extension(name, commands=[])323self.commands.concat(commands)324325# Check to see if this extension has already been loaded.326if ((klass = self.class.check_ext_hash(name.downcase)) == nil)327klass = Rex::Post::Meterpreter::ExtensionMapper.get_extension_klass(name)328# Save the module name to class association now that the code is329# loaded.330self.class.set_ext_hash(name.downcase, klass)331end332333# Create a new instance of the extension334inst = klass.new(self)335336self.ext.aliases[inst.name] = inst337338return true339end340341#342# Deregisters an extension alias of the supplied name.343#344def deregister_extension(name)345self.ext.aliases.delete(name)346end347348#349# Enumerates all of the loaded extensions.350#351def each_extension(&block)352self.ext.aliases.each(block)353end354355#356# Registers an aliased extension that can be referenced through357# client.name.358#359def register_extension_alias(name, ext)360self.ext_aliases.aliases[name] = ext361# Whee! Syntactic sugar, where art thou?362#363# Create an instance method on this object called +name+ that returns364# +ext+. We have to do it this way instead of simply365# self.class.class_eval so that other meterpreter sessions don't get366# extension methods when this one does367(class << self; self; end).class_eval do368define_method(name.to_sym) do369ext370end371end372ext373end374375#376# Registers zero or more aliases that are provided in an array.377#378def register_extension_aliases(aliases)379aliases.each { |a|380register_extension_alias(a['name'], a['ext'])381}382end383384#385# Deregisters a previously registered extension alias.386#387def deregister_extension_alias(name)388self.ext_aliases.aliases.delete(name)389end390391#392# Dumps the extension tree.393#394def dump_extension_tree()395items = []396items.concat(self.ext.dump_alias_tree('client.ext'))397items.concat(self.ext_aliases.dump_alias_tree('client'))398399return items.sort400end401402#403# Encodes (or not) a UTF-8 string404#405def unicode_filter_encode(str)406self.encode_unicode ? Rex::Text.unicode_filter_encode(str) : str407end408409#410# Decodes (or not) a UTF-8 string411#412def unicode_filter_decode(str)413self.encode_unicode ? Rex::Text.unicode_filter_decode(str) : str414end415416#417# The extension alias under which all extensions can be accessed by name.418# For example:419#420# client.ext.stdapi421#422#423attr_reader :ext424#425# The socket the client is communicating over.426#427attr_reader :sock428#429# The timeout value to use when waiting for responses.430#431attr_accessor :response_timeout432#433# Whether to send pings every so often to determine liveness.434#435attr_accessor :send_keepalives436#437# Whether this session is alive. If the socket is disconnected or broken,438# this will be false439#440attr_accessor :alive441#442# The unique target identifier for this payload443#444attr_accessor :target_id445#446# The libraries available to this meterpreter server447#448attr_accessor :capabilities449#450# The Connection ID451#452attr_accessor :conn_id453#454# The Connect URL455#456attr_accessor :url457#458# Use SSL (HTTPS)459#460attr_accessor :ssl461#462# Use this SSL Certificate (unified PEM)463#464attr_accessor :ssl_cert465#466# The Session Expiration Timeout467#468attr_accessor :expiration469#470# The Communication Timeout471#472attr_accessor :comm_timeout473#474# The total time for retrying connections475#476attr_accessor :retry_total477#478# The time to wait between retry attempts479#480attr_accessor :retry_wait481#482# The Passive Dispatcher483#484attr_accessor :passive_dispatcher485#486# Reference to a session to pivot through487#488attr_accessor :pivot_session489#490# Flag indicating whether to hex-encode UTF-8 file names and other strings491#492attr_accessor :encode_unicode493#494# A list of the commands495#496attr_reader :commands497#498# The timestamp of the last received response499#500attr_accessor :last_checkin501#502# Whether or not to use a debug build for loaded extensions503#504attr_accessor :debug_build505506protected507attr_accessor :parser, :ext_aliases # :nodoc:508attr_writer :ext, :sock # :nodoc:509attr_writer :commands # :nodoc:510end511512end; end; end513514515