From 37b9cd07a262b7e5fcc20a01cccf37697c4ed79b Mon Sep 17 00:00:00 2001 From: OJ Date: Mon, 5 Jun 2017 20:20:42 +1000 Subject: [PATCH] Add support for the session GUID in the UI The Session GUID will identify active sessions, and is the beginning of work that will allow for tracking of sessions that have come back alive after failing or switching transports. --- lib/msf/base/serializer/readable_text.rb | 6 +- lib/msf/base/sessions/meterpreter_options.rb | 13 +- lib/msf/core/payload/android.rb | 3 +- .../core/payload/java/meterpreter_loader.rb | 3 +- .../core/payload/python/meterpreter_loader.rb | 7 + .../payload/windows/meterpreter_loader.rb | 3 +- .../payload/windows/x64/meterpreter_loader.rb | 3 +- lib/msf/core/session.rb | 4 + lib/rex/payloads/meterpreter/config.rb | 14 +- lib/rex/post/meterpreter/client_core.rb | 87 +++- lib/rex/post/meterpreter/packet.rb | 2 + .../ui/console/command_dispatcher/core.rb | 436 +++++++++--------- .../singles/php/meterpreter_reverse_tcp.rb | 4 + .../singles/python/meterpreter_bind_tcp.rb | 1 + .../python/meterpreter_reverse_http.rb | 3 +- .../python/meterpreter_reverse_https.rb | 3 +- .../singles/python/meterpreter_reverse_tcp.rb | 1 + .../singles/windows/meterpreter_bind_tcp.rb | 3 +- .../windows/meterpreter_reverse_http.rb | 3 +- .../windows/meterpreter_reverse_https.rb | 3 +- .../windows/meterpreter_reverse_ipv6_tcp.rb | 3 +- .../windows/meterpreter_reverse_tcp.rb | 3 +- .../windows/x64/meterpreter_bind_tcp.rb | 3 +- .../windows/x64/meterpreter_reverse_http.rb | 3 +- .../windows/x64/meterpreter_reverse_https.rb | 3 +- .../x64/meterpreter_reverse_ipv6_tcp.rb | 3 +- .../windows/x64/meterpreter_reverse_tcp.rb | 3 +- modules/payloads/stages/php/meterpreter.rb | 6 +- 28 files changed, 389 insertions(+), 240 deletions(-) diff --git a/lib/msf/base/serializer/readable_text.rb b/lib/msf/base/serializer/readable_text.rb index 4a89ca1c2b..f007fe6d04 100644 --- a/lib/msf/base/serializer/readable_text.rb +++ b/lib/msf/base/serializer/readable_text.rb @@ -618,7 +618,6 @@ class ReadableText sess_luri = session.exploit_datastore['LURI'] || "" sess_checkin = "" - sess_machine_id = session.machine_id.to_s sess_registration = "No" if session.respond_to? :platform @@ -642,15 +641,12 @@ class ReadableText out << " Tunnel: #{sess_tunnel}\n" out << " Via: #{sess_via}\n" out << " UUID: #{sess_uuid}\n" - out << " MachineID: #{sess_machine_id}\n" out << " CheckIn: #{sess_checkin}\n" out << " Registered: #{sess_registration}\n" - if !sess_luri.empty? + unless sess_luri.empty? out << " LURI: #{sess_luri}\n" end - - out << "\n" end diff --git a/lib/msf/base/sessions/meterpreter_options.rb b/lib/msf/base/sessions/meterpreter_options.rb index a9dcf83be4..00058b4c7e 100644 --- a/lib/msf/base/sessions/meterpreter_options.rb +++ b/lib/msf/base/sessions/meterpreter_options.rb @@ -51,6 +51,17 @@ module MeterpreterOptions end if valid + # always make sure that the new session has a new guid if it's not already known + guid = session.core.get_session_guid + if guid == '00000000-0000-0000-0000-000000000000' + guid = SecureRandom.uuid + session.core.set_session_guid(guid) + session.guid = guid + # TODO: New statgeless session, do some account in the DB so we can track it later. + else + session.guid = guid + # TODO: This session was either staged or previously known, and so we shold do some accounting here! + end if datastore['AutoLoadStdapi'] @@ -71,7 +82,7 @@ module MeterpreterOptions end [ 'InitialAutoRunScript', 'AutoRunScript' ].each do |key| - if !datastore[key].empty? + unless datastore[key].empty? args = Shellwords.shellwords( datastore[key] ) print_status("Session ID #{session.sid} (#{session.tunnel_to_s}) processing #{key} '#{datastore[key]}'") session.execute_script(args.shift, *args) diff --git a/lib/msf/core/payload/android.rb b/lib/msf/core/payload/android.rb index 9b17e68aa2..3c564843f2 100644 --- a/lib/msf/core/payload/android.rb +++ b/lib/msf/core/payload/android.rb @@ -51,7 +51,8 @@ module Msf::Payload::Android arch: opts[:uuid].arch, expiration: ds['SessionExpirationTimeout'].to_i, uuid: opts[:uuid], - transports: opts[:transport_config] || [transport_config(opts)] + transports: opts[:transport_config] || [transport_config(opts)], + stageless: opts[:stageless] == true } config = Rex::Payloads::Meterpreter::Config.new(config_opts).to_b diff --git a/lib/msf/core/payload/java/meterpreter_loader.rb b/lib/msf/core/payload/java/meterpreter_loader.rb index c11114c14e..8c590465a8 100644 --- a/lib/msf/core/payload/java/meterpreter_loader.rb +++ b/lib/msf/core/payload/java/meterpreter_loader.rb @@ -69,7 +69,8 @@ module Payload::Java::MeterpreterLoader arch: opts[:uuid].arch, expiration: ds['SessionExpirationTimeout'].to_i, uuid: opts[:uuid], - transports: opts[:transport_config] || [transport_config(opts)] + transports: opts[:transport_config] || [transport_config(opts)], + stageless: opts[:stageless] == true } # create the configuration instance based off the parameters diff --git a/lib/msf/core/payload/python/meterpreter_loader.rb b/lib/msf/core/payload/python/meterpreter_loader.rb index 0d5199d3aa..dfd27e3fbd 100644 --- a/lib/msf/core/payload/python/meterpreter_loader.rb +++ b/lib/msf/core/payload/python/meterpreter_loader.rb @@ -74,6 +74,13 @@ module Payload::Python::MeterpreterLoader uuid = Rex::Text.to_hex(uuid.to_raw, prefix = '') met.sub!("PAYLOAD_UUID = \'\'", "PAYLOAD_UUID = \'#{uuid}\'") + if opts[:stageless] == true + session_guid = "00" * 16 + else + session_guid = SecureRandom.uuid.gsub(/-/, '') + end + met.sub!("SESSION_GUID = \'\'", "SESSION_GUID = \'#{session_guid}\'.decode(\'hex\')") + http_user_agent = opts[:http_user_agent] || ds['MeterpreterUserAgent'] http_proxy_host = opts[:http_proxy_host] || ds['PayloadProxyHost'] || ds['PROXYHOST'] http_proxy_port = opts[:http_proxy_port] || ds['PayloadProxyPort'] || ds['PROXYPORT'] diff --git a/lib/msf/core/payload/windows/meterpreter_loader.rb b/lib/msf/core/payload/windows/meterpreter_loader.rb index 7190d4f8b9..922aa9951d 100644 --- a/lib/msf/core/payload/windows/meterpreter_loader.rb +++ b/lib/msf/core/payload/windows/meterpreter_loader.rb @@ -82,7 +82,8 @@ module Payload::Windows::MeterpreterLoader expiration: ds['SessionExpirationTimeout'].to_i, uuid: opts[:uuid], transports: opts[:transport_config] || [transport_config(opts)], - extensions: [] + extensions: [], + stageless: opts[:stageless] == true } # create the configuration instance based off the parameters diff --git a/lib/msf/core/payload/windows/x64/meterpreter_loader.rb b/lib/msf/core/payload/windows/x64/meterpreter_loader.rb index cd5ba7c708..ce70242b53 100644 --- a/lib/msf/core/payload/windows/x64/meterpreter_loader.rb +++ b/lib/msf/core/payload/windows/x64/meterpreter_loader.rb @@ -84,7 +84,8 @@ module Payload::Windows::MeterpreterLoader_x64 expiration: ds['SessionExpirationTimeout'].to_i, uuid: opts[:uuid], transports: opts[:transport_config] || [transport_config(opts)], - extensions: [] + extensions: [], + stageless: opts[:stageless] == true } # create the configuration instance based off the parameters diff --git a/lib/msf/core/session.rb b/lib/msf/core/session.rb index f781f2864b..8ab8003ad4 100644 --- a/lib/msf/core/session.rb +++ b/lib/msf/core/session.rb @@ -385,6 +385,10 @@ module Session # attr_accessor :machine_id # + # The guid that identifies an active Meterpreter session + # + attr_accessor :guid + # # The actual exploit module instance that created this session # attr_accessor :exploit diff --git a/lib/rex/payloads/meterpreter/config.rb b/lib/rex/payloads/meterpreter/config.rb index dac1c744ff..838d7f4814 100644 --- a/lib/rex/payloads/meterpreter/config.rb +++ b/lib/rex/payloads/meterpreter/config.rb @@ -3,6 +3,7 @@ require 'msf/core/payload/uuid' require 'msf/core/payload/windows' require 'msf/core/reflective_dll_loader' require 'rex/socket/x509_certificate' +require 'securerandom' class Rex::Payloads::Meterpreter::Config @@ -50,14 +51,23 @@ private uuid = opts[:uuid].to_raw exit_func = Msf::Payload::Windows.exit_types[opts[:exitfunk]] + # if no session guid is given then we'll just pass the blank + # guid through. this is important for stageless payloads + if opts[:stageless] == true + session_guid = "\x00" * 16 + else + session_guid = [SecureRandom.uuid.gsub(/-/, '')].pack('H*') + end + session_data = [ 0, # comms socket, patched in by the stager exit_func, # exit function identifer opts[:expiration], # Session expiry - uuid # the UUID + uuid, # the UUID + session_guid # the Session GUID ] - session_data.pack('VVVA*') + session_data.pack('VVVA*A*') end def transport_block(opts) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 2f7c231e60..a09b6ced5f 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -125,6 +125,9 @@ class ClientCore < Extension result end + # + # Set associated transport timeouts for the currently active transport. + # def set_transport_timeouts(opts={}) request = Packet.create_request('core_transport_set_timeouts') @@ -298,6 +301,9 @@ class ClientCore < Extension return true end + # + # Set the UUID on the target session. + # def set_uuid(uuid) request = Packet.create_request('core_set_uuid') request.add_tlv(TLV_TYPE_UUID, uuid.to_raw) @@ -307,10 +313,42 @@ class ClientCore < Extension true end + # + # Set the session GUID on the target session. + # + def set_session_guid(guid) + request = Packet.create_request('core_set_session_guid') + request.add_tlv(TLV_TYPE_SESSION_GUID, [guid.gsub(/-/, '')].pack('H*')) + + client.send_request(request) + + true + end + + # + # Get the session GUID from the target session. + # + def get_session_guid(timeout=nil) + request = Packet.create_request('core_get_session_guid') + + args = [request] + args << timeout if timeout + + response = client.send_request(*args) + + bytes = response.get_tlv_value(TLV_TYPE_SESSION_GUID) + + parts = bytes.unpack('H*')[0] + [parts[0, 8], parts[8, 4], parts[12, 4], parts[16, 4], parts[20, 12]].join('-') + end + + # + # Get the machine ID from the target session. + # def machine_id(timeout=nil) request = Packet.create_request('core_machine_id') - args = [ request ] + args = [request] args << timeout if timeout response = client.send_request(*args) @@ -325,6 +363,9 @@ class ClientCore < Extension Rex::Text.md5(mid.to_s.downcase.strip) end + # + # Get the current native arch from the target session. + # def native_arch(timeout=nil) # Not all meterpreter implementations support this request = Packet.create_request('core_native_arch') @@ -337,6 +378,9 @@ class ClientCore < Extension response.get_tlv_value(TLV_TYPE_STRING) end + # + # Remove a transport from the session based on the provided options. + # def transport_remove(opts={}) request = transport_prepare_request('core_transport_remove', opts) @@ -347,6 +391,9 @@ class ClientCore < Extension return true end + # + # Add a transport to the session based on the provided options. + # def transport_add(opts={}) request = transport_prepare_request('core_transport_add', opts) @@ -357,6 +404,9 @@ class ClientCore < Extension return true end + # + # Change the currently active transport on the session. + # def transport_change(opts={}) request = transport_prepare_request('core_transport_change', opts) @@ -367,6 +417,9 @@ class ClientCore < Extension return true end + # + # Sleep the current session for the given number of seconds. + # def transport_sleep(seconds) return false if seconds == 0 @@ -379,12 +432,18 @@ class ClientCore < Extension return true end + # + # Change the active transport to the next one in the transport list. + # def transport_next request = Packet.create_request('core_transport_next') client.send_request(request) return true end + # + # Change the active transport to the previous one in the transport list. + # def transport_prev request = Packet.create_request('core_transport_prev') client.send_request(request) @@ -623,10 +682,17 @@ class ClientCore < Extension private + # + # Get a reference to the currently active transport. + # def get_current_transport transport_list[:transports][0] end + # + # Generate a migrate stub that is specific to the current transport type and the + # target process. + # def generate_migrate_stub(target_process) stub = nil @@ -663,6 +729,10 @@ private stub end + # + # Helper function to prepare a transport request that will be sent to the + # attached session. + # def transport_prepare_request(method, opts={}) unless valid_transport?(opts[:transport]) && opts[:lport] return nil @@ -751,6 +821,9 @@ private end + # + # Create a full migration payload specific to the target process. + # def generate_migrate_payload(target_process) case client.platform when 'windows' @@ -764,6 +837,9 @@ private blob end + # + # Create a full Windows-specific migration payload specific to the target process. + # def generate_migrate_windows_payload(target_process) c = Class.new( ::Msf::Payload ) c.include( ::Msf::Payload::Stager ) @@ -783,16 +859,25 @@ private migrate_stager.stage_meterpreter end + # + # Create a full Linux-specific migration payload specific to the target process. + # def generate_migrate_linux_payload MetasploitPayloads.read('meterpreter', 'msflinker_linux_x86.bin') end + # + # Determine the elf entry poitn for the given payload. + # def elf_ep(payload) elf = Rex::ElfParsey::Elf.new( Rex::ImageSource::Memory.new( payload ) ) ep = elf.elf_header.e_entry return ep end + # + # Get the tmp folder for the session. + # def tmp_folder tmp = client.sys.config.getenv('TMPDIR') diff --git a/lib/rex/post/meterpreter/packet.rb b/lib/rex/post/meterpreter/packet.rb index 1e70ea0f13..2ab6420493 100644 --- a/lib/rex/post/meterpreter/packet.rb +++ b/lib/rex/post/meterpreter/packet.rb @@ -105,6 +105,7 @@ TLV_TYPE_TRANS_GROUP = TLV_META_TYPE_GROUP | 441 TLV_TYPE_MACHINE_ID = TLV_META_TYPE_STRING | 460 TLV_TYPE_UUID = TLV_META_TYPE_RAW | 461 +TLV_TYPE_SESSION_GUID = TLV_META_TYPE_RAW | 462 TLV_TYPE_CIPHER_NAME = TLV_META_TYPE_STRING | 500 TLV_TYPE_CIPHER_PARAMETERS = TLV_META_TYPE_GROUP | 501 @@ -215,6 +216,7 @@ class Tlv when TLV_TYPE_TRANS_RETRY_WAIT; "TRANS-RETRY-WAIT" when TLV_TYPE_MACHINE_ID; "MACHINE-ID" when TLV_TYPE_UUID; "UUID" + when TLV_TYPE_SESSION_GUID; "SESSION-GUID" #when Extensions::Stdapi::TLV_TYPE_NETWORK_INTERFACE; 'network-interface' #when Extensions::Stdapi::TLV_TYPE_IP; 'ip-address' 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 e5e4bd6925..49815f90d3 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -34,59 +34,60 @@ class Console::CommandDispatcher::Core end @@irb_opts = Rex::Parser::Arguments.new( - "-h" => [ false, "Help banner." ], - "-e" => [ true, "Expression to evaluate." ]) + '-h' => [false, 'Help banner.'], + '-e' => [true, 'Expression to evaluate.']) @@load_opts = Rex::Parser::Arguments.new( - "-l" => [ false, "List all available extensions" ], - "-h" => [ false, "Help menu." ]) + '-l' => [false, 'List all available extensions'], + '-h' => [false, 'Help menu.']) # # List of supported commands. # def commands c = { - "?" => "Help menu", - "background" => "Backgrounds the current session", - "close" => "Closes a channel", - "channel" => "Displays information or control active channels", - "exit" => "Terminate the meterpreter session", - "help" => "Help menu", - "irb" => "Drop into irb scripting mode", - "use" => "Deprecated alias for 'load'", - "load" => "Load one or more meterpreter extensions", - "machine_id" => "Get the MSF ID of the machine attached to the session", - "quit" => "Terminate the meterpreter session", - "resource" => "Run the commands stored in a file", - "uuid" => "Get the UUID for the current session", - "read" => "Reads data from a channel", - "run" => "Executes a meterpreter script or Post module", - "bgrun" => "Executes a meterpreter script as a background thread", - "bgkill" => "Kills a background meterpreter script", - "get_timeouts" => "Get the current session timeout values", - "set_timeouts" => "Set the current session timeout values", - "sessions" => "Quickly switch to another session", - "bglist" => "Lists running background scripts", - "write" => "Writes data to a channel", - "enable_unicode_encoding" => "Enables encoding of unicode strings", - "disable_unicode_encoding" => "Disables encoding of unicode strings" + '?' => 'Help menu', + 'background' => 'Backgrounds the current session', + 'close' => 'Closes a channel', + 'channel' => 'Displays information or control active channels', + 'exit' => 'Terminate the meterpreter session', + 'help' => 'Help menu', + 'irb' => 'Drop into irb scripting mode', + 'use' => 'Deprecated alias for "load"', + 'load' => 'Load one or more meterpreter extensions', + 'machine_id' => 'Get the MSF ID of the machine attached to the session', + 'guid' => 'Get the session GUID', + 'quit' => 'Terminate the meterpreter session', + 'resource' => 'Run the commands stored in a file', + 'uuid' => 'Get the UUID for the current session', + 'read' => 'Reads data from a channel', + 'run' => 'Executes a meterpreter script or Post module', + 'bgrun' => 'Executes a meterpreter script as a background thread', + 'bgkill' => 'Kills a background meterpreter script', + 'get_timeouts' => 'Get the current session timeout values', + 'set_timeouts' => 'Set the current session timeout values', + 'sessions' => 'Quickly switch to another session', + 'bglist' => 'Lists running background scripts', + 'write' => 'Writes data to a channel', + 'enable_unicode_encoding' => 'Enables encoding of unicode strings', + 'disable_unicode_encoding' => 'Disables encoding of unicode strings' } if client.passive_service - c["detach"] = "Detach the meterpreter session (for http/https)" + c['detach'] = 'Detach the meterpreter session (for http/https)' end # Currently we have some windows-specific core commands` if client.platform == 'windows' # only support the SSL switching for HTTPS if client.passive_service && client.sock.type? == 'tcp-ssl' - c["ssl_verify"] = "Modify the SSL certificate verification setting" + c['ssl_verify'] = 'Modify the SSL certificate verification setting' end end if client.platform == 'windows' || client.platform == 'linux' # Migration only supported on windows and linux - c["migrate"] = "Migrate the server to another process" + c['migrate'] = 'Migrate the server to another process' end # TODO: This code currently checks both platform and architecture for the python @@ -97,15 +98,15 @@ class Console::CommandDispatcher::Core client.platform == 'python' || client.platform == 'java' || client.arch == ARCH_PYTHON || client.platform == 'android' # Yet to implement transport hopping for other meterpreters. - c["transport"] = "Change the current transport mechanism" + c['transport'] = 'Change the current transport mechanism' # sleep functionality relies on the transport features, so only # wire that in with the transport stuff. - c["sleep"] = "Force Meterpreter to go quiet, then re-establish session." + c['sleep'] = 'Force Meterpreter to go quiet, then re-establish session.' end if msf_loaded? - c["info"] = "Displays information about a Post module" + c['info'] = 'Displays information about a Post module' end c @@ -115,7 +116,7 @@ class Console::CommandDispatcher::Core # Core baby. # def name - "Core" + 'Core' end def cmd_sessions_help @@ -141,14 +142,14 @@ class Console::CommandDispatcher::Core end def cmd_background_help - print_line "Usage: background" + print_line('Usage: background') print_line - print_line "Stop interacting with this session and return to the parent prompt" + print_line('Stop interacting with this session and return to the parent prompt') print_line end def cmd_background - print_status "Backgrounding session #{client.name}..." + print_status("Backgrounding session #{client.name}...") client.interacting = false end @@ -156,26 +157,26 @@ class Console::CommandDispatcher::Core # Displays information about active channels # @@channel_opts = Rex::Parser::Arguments.new( - "-c" => [ true, "Close the given channel." ], - "-k" => [ true, "Close the given channel." ], - "-i" => [ true, "Interact with the given channel." ], - "-l" => [ false, "List active channels." ], - "-r" => [ true, "Read from the given channel." ], - "-w" => [ true, "Write to the given channel." ], - "-h" => [ false, "Help menu." ]) + '-c' => [ true, 'Close the given channel.' ], + '-k' => [ true, 'Close the given channel.' ], + '-i' => [ true, 'Interact with the given channel.' ], + '-l' => [ false, 'List active channels.' ], + '-r' => [ true, 'Read from the given channel.' ], + '-w' => [ true, 'Write to the given channel.' ], + '-h' => [ false, 'Help menu.' ]) def cmd_channel_help - print_line "Usage: channel [options]" + print_line('Usage: channel [options]') print_line - print_line "Displays information about active channels." - print_line @@channel_opts.usage + print_line('Displays information about active channels.') + print_line(@@channel_opts.usage) end # # Performs operations on the supplied channel. # def cmd_channel(*args) - if args.empty? or args.include?("-h") or args.include?("--help") + if args.empty? || args.include?('-h') cmd_channel_help return end @@ -186,24 +187,25 @@ class Console::CommandDispatcher::Core # Parse options @@channel_opts.parse(args) { |opt, idx, val| case opt - when "-l" + when '-l' mode = :list - when "-c", "-k" + when '-c', '-k' mode = :close chan = val - when "-i" + when '-i' mode = :interact chan = val - when "-r" + when '-r' mode = :read chan = val - when "-w" + when '-w' mode = :write chan = val end + if @@channel_opts.arg_required?(opt) unless chan - print_error("Channel ID required") + print_error('Channel ID required') return end end @@ -213,12 +215,7 @@ class Console::CommandDispatcher::Core when :list tbl = Rex::Text::Table.new( 'Indent' => 4, - 'Columns' => - [ - 'Id', - 'Class', - 'Type' - ]) + 'Columns' => ['Id', 'Class', 'Type']) items = 0 client.channels.each_pair { |cid, channel| @@ -227,7 +224,7 @@ class Console::CommandDispatcher::Core } if (items == 0) - print_line("No active channels.") + print_line('No active channels.') else print("\n" + tbl.to_s + "\n") end @@ -251,7 +248,7 @@ class Console::CommandDispatcher::Core @@channel_opts.fmt.keys when 2 case words[1] - when "-k", "-c", "-i", "-r", "-w" + when '-k', '-c', '-i', '-r', '-w' tab_complete_channels else [] @@ -262,9 +259,9 @@ class Console::CommandDispatcher::Core end def cmd_close_help - print_line "Usage: close " + print_line('Usage: close ') print_line - print_line "Closes the supplied channel." + print_line('Closes the supplied channel.') print_line end @@ -272,22 +269,22 @@ class Console::CommandDispatcher::Core # Closes a supplied channel. # def cmd_close(*args) - if (args.length == 0) + if args.length == 0 cmd_close_help return true end - cid = args[0].to_i + cid = args[0].to_i channel = client.find_channel(cid) - if (!channel) - print_error("Invalid channel identifier specified.") + unless channel + print_error('Invalid channel identifier specified.') return true - else - channel._close # Issue #410 - - print_status("Closed channel #{cid}.") end + + channel._close # Issue #410 + + print_status("Closed channel #{cid}.") end def cmd_close_tabs(str, words) @@ -300,7 +297,7 @@ class Console::CommandDispatcher::Core # Terminates the meterpreter session. # def cmd_exit(*args) - print_status("Shutting down Meterpreter...") + print_status('Shutting down Meterpreter...') client.core.shutdown rescue nil client.shutdown_passive_dispatcher shell.stop @@ -309,13 +306,13 @@ class Console::CommandDispatcher::Core alias cmd_quit cmd_exit def cmd_detach_help - print_line "Detach from the victim. Only possible for non-stream sessions (http/https)" + print_line('Detach from the victim. Only possible for non-stream sessions (http/https)') print_line - print_line "The victim will continue to attempt to call back to the handler until it" - print_line "successfully connects (which may happen immediately if you have a handler" - print_line "running in the background), or reaches its expiration." + print_line('The victim will continue to attempt to call back to the handler until it') + print_line('successfully connects (which may happen immediately if you have a handler') + print_line('running in the background), or reaches its expiration.') print_line - print_line "This session may #{client.passive_service ? "" : "NOT"} be detached." + print_line("This session may #{client.passive_service ? "" : "NOT"} be detached.") print_line end @@ -328,9 +325,9 @@ class Console::CommandDispatcher::Core end def cmd_interact_help - print_line "Usage: interact " + print_line('Usage: interact ') print_line - print_line "Interacts with the supplied channel." + print_line('Interacts with the supplied channel.') print_line end @@ -338,29 +335,29 @@ class Console::CommandDispatcher::Core # Interacts with a channel. # def cmd_interact(*args) - if (args.length == 0) + if args.length == 0 cmd_info_help return true end - cid = args[0].to_i + cid = args[0].to_i channel = client.find_channel(cid) - if (channel) + if channel print_line("Interacting with channel #{cid}...\n") shell.interact_with_channel(channel) else - print_error("Invalid channel identifier specified.") + print_error('Invalid channel identifier specified.') end end alias cmd_interact_tabs cmd_close_tabs def cmd_irb_help - print_line "Usage: irb" + print_line('Usage: irb') print_line - print_line "Execute commands in a Ruby environment" + print_line('Execute commands in a Ruby environment') print @@irb_opts.usage end @@ -384,8 +381,9 @@ class Console::CommandDispatcher::Core framework = client.framework if expressions.empty? - print_status("Starting IRB shell") - print_status("The 'client' variable holds the meterpreter client\n") + print_status('Starting IRB shell') + print_status('The "client" variable holds the meterpreter client') + print_line Rex::Ui::Text::IrbShell.new(binding).run else @@ -394,11 +392,11 @@ class Console::CommandDispatcher::Core end @@set_timeouts_opts = Rex::Parser::Arguments.new( - '-c' => [ true, 'Comms timeout (seconds)' ], - '-x' => [ true, 'Expiration timout (seconds)' ], - '-t' => [ true, 'Retry total time (seconds)' ], - '-w' => [ true, 'Retry wait time (seconds)' ], - '-h' => [ false, 'Help menu' ]) + '-c' => [true, 'Comms timeout (seconds)'], + '-x' => [true, 'Expiration timout (seconds)'], + '-t' => [true, 'Retry total time (seconds)'], + '-w' => [true, 'Retry wait time (seconds)'], + '-h' => [false, 'Help menu']) def cmd_set_timeouts_help print_line('Usage: set_timeouts [options]') @@ -409,7 +407,7 @@ class Console::CommandDispatcher::Core end def cmd_set_timeouts(*args) - if ( args.length == 0 or args.include?("-h") ) + if args.length == 0 || args.include?('-h') cmd_set_timeouts_help return end @@ -430,7 +428,7 @@ class Console::CommandDispatcher::Core end if opts.keys.length == 0 - print_error("No options set") + print_error('No options set') else timeouts = client.core.set_transport_timeouts(opts) print_timeouts(timeouts) @@ -467,6 +465,14 @@ class Console::CommandDispatcher::Core print_good("Machine ID: #{client.machine_id}") end + # + # Get the session GUID + # + def cmd_guid(*args) + client.guid = client.core.get_session_guid unless client.guid + print_good("Session GUID: #{client.guid}") + end + # # Get the machine ID of the target (should always be up to date locally) # @@ -534,7 +540,7 @@ class Console::CommandDispatcher::Core if hash print_good("SSL verification is enabled. SHA1 Hash: #{hash.unpack("H*")[0]}") else - print_good("SSL verification is disabled.") + print_good('SSL verification is disabled.') end elsif enable @@ -542,14 +548,14 @@ class Console::CommandDispatcher::Core if hash print_good("SSL verification has been enabled. SHA1 Hash: #{hash.unpack("H*")[0]}") else - print_error("Failed to enable SSL verification") + print_error('Failed to enable SSL verification') end else if client.core.disable_ssl_hash_verify print_good('SSL verification has been disabled') else - print_error("Failed to disable SSL verification") + print_error('Failed to disable SSL verification') end end @@ -598,25 +604,25 @@ class Console::CommandDispatcher::Core # Arguments for transport switching # @@transport_opts = Rex::Parser::Arguments.new( - '-t' => [ true, "Transport type: #{Rex::Post::Meterpreter::ClientCore::VALID_TRANSPORTS.keys.join(', ')}" ], - '-l' => [ true, 'LHOST parameter (for reverse transports)' ], - '-p' => [ true, 'LPORT parameter' ], - '-i' => [ true, 'Specify transport by index (currently supported: remove)' ], - '-u' => [ true, 'Custom URI for HTTP/S transports (used when removing transports)' ], - '-lu' => [ true, 'Local URI for HTTP/S transports (used when adding/changing transports with a custom LURI)' ], - '-ua' => [ true, 'User agent for HTTP/S transports (optional)' ], - '-ph' => [ true, 'Proxy host for HTTP/S transports (optional)' ], - '-pp' => [ true, 'Proxy port for HTTP/S transports (optional)' ], - '-pu' => [ true, 'Proxy username for HTTP/S transports (optional)' ], - '-ps' => [ true, 'Proxy password for HTTP/S transports (optional)' ], - '-pt' => [ true, 'Proxy type for HTTP/S transports (optional: http, socks; default: http)' ], - '-c' => [ true, 'SSL certificate path for https transport verification (optional)' ], - '-to' => [ true, 'Comms timeout (seconds) (default: same as current session)' ], - '-ex' => [ true, 'Expiration timout (seconds) (default: same as current session)' ], - '-rt' => [ true, 'Retry total time (seconds) (default: same as current session)' ], - '-rw' => [ true, 'Retry wait time (seconds) (default: same as current session)' ], - '-v' => [ false, 'Show the verbose format of the transport list' ], - '-h' => [ false, 'Help menu' ]) + '-t' => [true, "Transport type: #{Rex::Post::Meterpreter::ClientCore::VALID_TRANSPORTS.keys.join(', ')}"], + '-l' => [true, 'LHOST parameter (for reverse transports)'], + '-p' => [true, 'LPORT parameter'], + '-i' => [true, 'Specify transport by index (currently supported: remove)'], + '-u' => [true, 'Custom URI for HTTP/S transports (used when removing transports)'], + '-lu' => [true, 'Local URI for HTTP/S transports (used when adding/changing transports with a custom LURI)'], + '-ua' => [true, 'User agent for HTTP/S transports (optional)'], + '-ph' => [true, 'Proxy host for HTTP/S transports (optional)'], + '-pp' => [true, 'Proxy port for HTTP/S transports (optional)'], + '-pu' => [true, 'Proxy username for HTTP/S transports (optional)'], + '-ps' => [true, 'Proxy password for HTTP/S transports (optional)'], + '-pt' => [true, 'Proxy type for HTTP/S transports (optional: http, socks; default: http)'], + '-c' => [true, 'SSL certificate path for https transport verification (optional)'], + '-to' => [true, 'Comms timeout (seconds) (default: same as current session)'], + '-ex' => [true, 'Expiration timout (seconds) (default: same as current session)'], + '-rt' => [true, 'Retry total time (seconds) (default: same as current session)'], + '-rw' => [true, 'Retry wait time (seconds) (default: same as current session)'], + '-v' => [false, 'Show the verbose format of the transport list'], + '-h' => [false, 'Help menu']) # # Display help for transport management. @@ -656,23 +662,23 @@ class Console::CommandDispatcher::Core end opts = { - :uuid => client.payload_uuid, - :transport => nil, - :lhost => nil, - :lport => nil, - :uri => nil, - :ua => nil, - :proxy_host => nil, - :proxy_port => nil, - :proxy_type => nil, - :proxy_user => nil, - :proxy_pass => nil, - :comm_timeout => nil, - :session_exp => nil, - :retry_total => nil, - :retry_wait => nil, - :cert => nil, - :verbose => false + :uuid => client.payload_uuid, + :transport => nil, + :lhost => nil, + :lport => nil, + :uri => nil, + :ua => nil, + :proxy_host => nil, + :proxy_port => nil, + :proxy_type => nil, + :proxy_user => nil, + :proxy_pass => nil, + :comm_timeout => nil, + :session_exp => nil, + :retry_total => nil, + :retry_wait => nil, + :cert => nil, + :verbose => false } valid = true @@ -742,14 +748,7 @@ class Console::CommandDispatcher::Core # this will output the session timeout first print_timeouts(result) - columns =[ - 'ID', - 'Curr', - 'URL', - 'Comms T/O', - 'Retry Total', - 'Retry Wait' - ] + columns = ['ID', 'Curr', 'URL', 'Comms T/O', 'Retry Total', 'Retry Wait'] if opts[:verbose] columns << 'User Agent' @@ -766,8 +765,8 @@ class Console::CommandDispatcher::Core 'Columns' => columns) sorted_by_url.each_with_index do |t, i| - entry = [ i+1, (current_transport_url == t[:url]) ? '*' : '', t[:url], - t[:comm_timeout], t[:retry_total], t[:retry_wait] ] + entry = [i + 1, current_transport_url == t[:url] ? '*' : '', t[:url], + t[:comm_timeout], t[:retry_total], t[:retry_wait]] if opts[:verbose] entry << t[:ua] @@ -782,42 +781,42 @@ class Console::CommandDispatcher::Core print("\n" + tbl.to_s + "\n") when 'next' - print_status("Changing to next transport ...") + print_status('Changing to next transport ...') if client.core.transport_next - print_good("Successfully changed to the next transport, killing current session.") + print_good('Successfully changed to the next transport, killing current session.') client.shutdown_passive_dispatcher shell.stop else - print_error("Failed to change transport, please check the parameters") + print_error('Failed to change transport, please check the parameters') end when 'prev' - print_status("Changing to previous transport ...") + print_status('Changing to previous transport ...') if client.core.transport_prev - print_good("Successfully changed to the previous transport, killing current session.") + print_good('Successfully changed to the previous transport, killing current session.') client.shutdown_passive_dispatcher shell.stop else - print_error("Failed to change transport, please check the parameters") + print_error('Failed to change transport, please check the parameters') end when 'change' - print_status("Changing to new transport ...") + print_status('Changing to new transport ...') if client.core.transport_change(opts) print_good("Successfully added #{opts[:transport]} transport, killing current session.") client.shutdown_passive_dispatcher shell.stop else - print_error("Failed to change transport, please check the parameters") + print_error('Failed to change transport, please check the parameters') end when 'add' - print_status("Adding new transport ...") + print_status('Adding new transport ...') if client.core.transport_add(opts) print_good("Successfully added #{opts[:transport]} transport.") else - print_error("Failed to add transport, please check the parameters") + print_error('Failed to add transport, please check the parameters') end when 'remove' if opts[:transport] && !opts[:transport].end_with?('_tcp') && opts[:uri].nil? - print_error("HTTP/S transport specified without session URI") + print_error('HTTP/S transport specified without session URI') return end @@ -829,7 +828,7 @@ class Console::CommandDispatcher::Core opts[:transport] = "reverse_#{uri.scheme}" opts[:lhost] = uri.host opts[:lport] = uri.port - opts[:uri] = uri.path[1..-2] if uri.scheme.include?("http") + opts[:uri] = uri.path[1..-2] if uri.scheme.include?('http') rescue URI::InvalidURIError print_error("Failed to parse URL: #{url_to_delete}") @@ -837,11 +836,11 @@ class Console::CommandDispatcher::Core end end - print_status("Removing transport ...") + print_status('Removing transport ...') if client.core.transport_remove(opts) print_good("Successfully removed #{opts[:transport]} transport.") else - print_error("Failed to remove transport, please check the parameters") + print_error('Failed to remove transport, please check the parameters') end end end @@ -849,8 +848,8 @@ class Console::CommandDispatcher::Core @@migrate_opts = Rex::Parser::Arguments.new( '-P' => [true, 'PID to migrate to.'], '-N' => [true, 'Process name to migrate to.'], - '-p' => [true, 'Writable path - Linux only (eg. /tmp).'], - '-t' => [true, 'The number of seconds to wait for migration to finish (default: 60).'], + '-p' => [true, 'Writable path - Linux only (eg. /tmp).'], + '-t' => [true, 'The number of seconds to wait for migration to finish (default: 60).'], '-h' => [false, 'Help menu.'] ) @@ -898,7 +897,7 @@ class Console::CommandDispatcher::Core pid = val.to_i when '-N' if val.to_s.empty? - print_error("No process name provided") + print_error('No process name provided') return end # this will migrate to the first process with a matching name @@ -980,23 +979,23 @@ class Console::CommandDispatcher::Core end def cmd_load_help - print_line("Usage: load ext1 ext2 ext3 ...") + print_line('Usage: load ext1 ext2 ext3 ...') print_line - print_line "Loads a meterpreter extension module or modules." - print_line @@load_opts.usage + print_line('Loads a meterpreter extension module or modules.') + print_line(@@load_opts.usage) end # # Loads one or more meterpreter extensions. # def cmd_load(*args) - if (args.length == 0) - args.unshift("-h") + if args.length == 0 + args.unshift('-h') end @@load_opts.parse(args) { |opt, idx, val| case opt - when "-l" + when '-l' exts = SortedSet.new msf_path = MetasploitPayloads.msf_meterpreter_dir gem_path = MetasploitPayloads.local_meterpreter_dir @@ -1010,7 +1009,7 @@ class Console::CommandDispatcher::Core print(exts.to_a.join("\n") + "\n") return true - when "-h" + when '-h' cmd_load_help return true end @@ -1038,7 +1037,7 @@ class Console::CommandDispatcher::Core next end - print_line("success.") + print_line('Success.') } return true @@ -1068,9 +1067,9 @@ class Console::CommandDispatcher::Core alias cmd_use_tabs cmd_load_tabs def cmd_read_help - print_line "Usage: read [length]" + print_line('Usage: read [length]') print_line - print_line "Reads data from the supplied channel." + print_line('Reads data from the supplied channel.') print_line end @@ -1078,7 +1077,7 @@ class Console::CommandDispatcher::Core # Reads data from a channel. # def cmd_read(*args) - if (args.length == 0) + if args.length == 0 cmd_read_help return true end @@ -1087,17 +1086,17 @@ class Console::CommandDispatcher::Core length = (args.length >= 2) ? args[1].to_i : 16384 channel = client.find_channel(cid) - if (!channel) + unless channel print_error("Channel #{cid} is not valid.") return true end data = channel.read(length) - if (data and data.length) + if data && data.length print("Read #{data.length} bytes from #{cid}:\n\n#{data}\n") else - print_error("No data was returned.") + print_error('No data was returned.') end return true @@ -1106,11 +1105,11 @@ class Console::CommandDispatcher::Core alias cmd_read_tabs cmd_close_tabs def cmd_run_help - print_line "Usage: run