From 998b38cf0da9894a1b964820ea39d2452d9c1fef Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 14 Jan 2021 08:41:14 -0500 Subject: [PATCH 1/9] Enumerate Meterpreter's supported core commands --- lib/msf/base/sessions/meterpreter.rb | 2 ++ lib/rex/post/meterpreter/client.rb | 1 - lib/rex/post/meterpreter/client_core.rb | 10 +++++++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index 46c8724bfb..be50d62d03 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -151,6 +151,8 @@ class Meterpreter < Rex::Post::Meterpreter::Client # TODO: This session was either staged or previously known, and so we should do some accounting here! end + session.commands.concat(session.core.get_loaded_extension_commands('core')) + # Unhook the process prior to loading stdapi to reduce logging/inspection by any AV/PSP if datastore['AutoUnhookProcess'] == true console.run_single('load unhook') diff --git a/lib/rex/post/meterpreter/client.rb b/lib/rex/post/meterpreter/client.rb index 521992674e..866d54c847 100644 --- a/lib/rex/post/meterpreter/client.rb +++ b/lib/rex/post/meterpreter/client.rb @@ -316,7 +316,6 @@ class Client # registered extension that can be reached through client.ext.[extension]. # def add_extension(name, commands=[]) - self.commands |= [] self.commands.concat(commands) # Check to see if this extension has already been loaded. diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 4ce4570955..188a6cc9b5 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -98,11 +98,15 @@ class ClientCore < Extension # # Get a list of loaded commands for the given extension. # - def get_loaded_extension_commands(extension_name) + # @param [String, Integer] extension Either the extension name or the extension ID to load the commands for. + # + # @return [Array] An array of command IDs that are supported by the specified extension. + def get_loaded_extension_commands(extension) request = Packet.create_request(COMMAND_ID_CORE_ENUMEXTCMD) - start = Rex::Post::Meterpreter::ExtensionMapper.get_extension_id(extension_name) - request.add_tlv(TLV_TYPE_UINT, start) + extension = EXTENSION_ID_CORE if extension == 'core' + extension = Rex::Post::Meterpreter::ExtensionMapper.get_extension_id(extension) unless extension.is_a? Integer + request.add_tlv(TLV_TYPE_UINT, extension) request.add_tlv(TLV_TYPE_LENGTH, COMMAND_ID_RANGE) begin From 7cf5879836b17e332f4ff1f0fb9f309801c60418 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 14 Jan 2021 12:13:16 -0500 Subject: [PATCH 2/9] Add a meterpreter test for enumerating core commands --- lib/rex/post/meterpreter/client_core.rb | 1 + test/modules/post/test/meterpreter.rb | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index 188a6cc9b5..a000f4e77f 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -104,6 +104,7 @@ class ClientCore < Extension def get_loaded_extension_commands(extension) request = Packet.create_request(COMMAND_ID_CORE_ENUMEXTCMD) + # handle 'core' as a special case since it's not a typical extension extension = EXTENSION_ID_CORE if extension == 'core' extension = Rex::Post::Meterpreter::ExtensionMapper.get_extension_id(extension) unless extension.is_a? Integer request.add_tlv(TLV_TYPE_UINT, extension) diff --git a/test/modules/post/test/meterpreter.rb b/test/modules/post/test/meterpreter.rb index 22e33b9377..670e66d984 100644 --- a/test/modules/post/test/meterpreter.rb +++ b/test/modules/post/test/meterpreter.rb @@ -47,6 +47,19 @@ class MetasploitModule < Msf::Post super end + def test_core_command_id_enumeration + commands = [] + + it "should enumerate supported core commands" do + commands.concat(session.core.get_loaded_extension_commands('core')) + !commands.empty? + end + + # 3 is arbitrary, but it's probably a good bare minimum to include enumextcmd, machine_id, and loadlib + it "should support 3 or more core commands" do + commands.length >= 3 + end + end def test_sys_process vprint_status("Starting process tests") From b9833656651845451cee5a2249f4e8de84e5e4ab Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 14 Jan 2021 13:19:37 -0500 Subject: [PATCH 3/9] Filter Meterpreter commands based on support instead of fingerprinting --- .../ui/console/command_dispatcher.rb | 2 +- .../ui/console/command_dispatcher/core.rb | 75 ++++++++++--------- 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb index 0c111df6ef..40f8a7bb7a 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb @@ -53,7 +53,7 @@ module Console::CommandDispatcher # def filter_commands(all, reqs) all.delete_if do |cmd, _desc| - reqs[cmd].any? { |req| !client.commands.include?(req) } + reqs[cmd]&.any? { |req| !client.commands.include?(req) } end end 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 abc16e33a7..34ffa25b18 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -47,7 +47,7 @@ class Console::CommandDispatcher::Core # List of supported commands. # def commands - c = { + cmds = { '?' => 'Help menu', 'background' => 'Backgrounds the current session', 'bg' => 'Alias for background', @@ -69,55 +69,58 @@ class Console::CommandDispatcher::Core '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' + 'disable_unicode_encoding' => 'Disables encoding of unicode strings', + 'migrate' => 'Migrate the server to another process', + 'pivot' => 'Manage pivot listeners', + # transport related commands + 'sleep' => 'Force Meterpreter to go quiet, then re-establish session', + 'transport' => 'Manage the transport mechanisms', + 'get_timeouts' => 'Get the current session timeout values', + 'set_timeouts' => 'Set the current session timeout values', } if client.passive_service - c['detach'] = 'Detach the meterpreter session (for http/https)' + cmds['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' - end - - c['pivot'] = 'Manage pivot listeners' - end - - if client.platform == 'windows' || client.platform == 'linux' - # Migration only supported on windows and linux - c['migrate'] = 'Migrate the server to another process' - end - - # TODO: This code currently checks both platform and architecture for the python - # and java types because technically the platform should be updated to indicate - # the OS platform rather than the meterpreter arch. When we've properly implemented - # the platform update feature we can remove some of these conditions - if client.platform == 'windows' || client.platform == 'linux' || - client.platform == 'python' || client.arch == ARCH_PYTHON || - client.platform == 'java' || client.arch == ARCH_JAVA || - client.platform == 'android' || client.arch == ARCH_DALVIK - # Yet to implement transport hopping for other meterpreters. - 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.' + if client.passive_service && client.sock.type? == 'tcp-ssl' + cmds['ssl_verify'] = 'Modify the SSL certificate verification setting' end if msf_loaded? - c['info'] = 'Displays information about a Post module' + cmds['info'] = 'Displays information about a Post module' end - c + reqs = { + 'load' => [COMMAND_ID_CORE_LOADLIB], + 'machine_id' => [COMMAND_ID_CORE_MACHINE_ID], + 'migrate' => [COMMAND_ID_CORE_MIGRATE], + 'pivot' => [COMMAND_ID_CORE_PIVOT_ADD, COMMAND_ID_CORE_PIVOT_REMOVE], + 'secure' => [COMMAND_ID_CORE_NEGOTIATE_TLV_ENCRYPTION], + # channel related commands + 'read' => [COMMAND_ID_CORE_CHANNEL_READ], + 'write' => [COMMAND_ID_CORE_CHANNEL_WRITE], + 'close' => [COMMAND_ID_CORE_CHANNEL_CLOSE], + # transport related commands + 'sleep' => [COMMAND_ID_CORE_TRANSPORT_SLEEP], + 'ssl_verify' => [COMMAND_ID_CORE_TRANSPORT_GETCERTHASH, COMMAND_ID_CORE_TRANSPORT_SETCERTHASH], + 'transport' => [ + COMMAND_ID_CORE_TRANSPORT_ADD, + COMMAND_ID_CORE_TRANSPORT_CHANGE, + COMMAND_ID_CORE_TRANSPORT_LIST, + COMMAND_ID_CORE_TRANSPORT_NEXT, + COMMAND_ID_CORE_TRANSPORT_PREV, + COMMAND_ID_CORE_TRANSPORT_REMOVE + ], + 'get_timeouts' => [COMMAND_ID_CORE_TRANSPORT_SET_TIMEOUTS], + 'set_timeouts' => [COMMAND_ID_CORE_TRANSPORT_SET_TIMEOUTS], + } + + filter_commands(cmds, reqs) end # From a587c166cb0b2e4ce04c73b15b524aea77d926d3 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 14 Jan 2021 15:09:32 -0500 Subject: [PATCH 4/9] Add and use a callback to report meterpreter commands that are disabled --- .../meterpreter/ui/console/command_dispatcher.rb | 15 ++++++++++++++- lib/rex/ui/text/dispatcher_shell.rb | 11 +++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb index 40f8a7bb7a..ba620554f1 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb @@ -38,6 +38,7 @@ module Console::CommandDispatcher def initialize(shell) @msf_loaded = nil + @filtered_commands = [] super end @@ -53,10 +54,22 @@ module Console::CommandDispatcher # def filter_commands(all, reqs) all.delete_if do |cmd, _desc| - reqs[cmd]&.any? { |req| !client.commands.include?(req) } + if reqs[cmd]&.any? { |req| !client.commands.include?(req) } + @filtered_commands << cmd + true + end end end + def unknown_command(cmd, line) + if @filtered_commands.include?(cmd) + print_error("The '#{cmd}' command is not supported by this Meterpreter type (#{client.session_type})") + return true + end + + super + end + # # Return the subdir of the `documentation/` directory that should be used # to find usage documentation diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index 5dd1f4ce55..6b79765c08 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -328,6 +328,15 @@ module DispatcherShell end addresses end + + # + # A callback that can be used to handle unknown commands. This can for example, allow a dispatcher to mark a command + # as being disabled. + # + # @return [Boolean] Returns true when the dispatcher has handled the command. + def unknown_command(method, line) + false + end end # @@ -474,6 +483,8 @@ module DispatcherShell self.on_command_proc.call(line.strip) if self.on_command_proc run_command(dispatcher, method, arguments) found = true + elsif dispatcher.unknown_command(method, line) + found = true end rescue ::Interrupt found = true From 8a8994bb571d17d30edfbaf65b872bce15c3af09 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 14 Jan 2021 17:16:15 -0500 Subject: [PATCH 5/9] Raise a more specific error when loading an unsupported extension --- lib/rex/post/meterpreter/client_core.rb | 14 ++++-- .../ui/console/command_dispatcher.rb | 2 +- .../ui/console/command_dispatcher/core.rb | 48 +++---------------- 3 files changed, 16 insertions(+), 48 deletions(-) diff --git a/lib/rex/post/meterpreter/client_core.rb b/lib/rex/post/meterpreter/client_core.rb index a000f4e77f..21c7be285a 100644 --- a/lib/rex/post/meterpreter/client_core.rb +++ b/lib/rex/post/meterpreter/client_core.rb @@ -364,16 +364,20 @@ class ClientCore < Extension end if path.nil? and image.nil? - raise RuntimeError, "No module of the name #{modnameprovided} found", caller + if Rex::Post::Meterpreter::ExtensionMapper.get_extension_names.include?(mod.downcase) + raise RuntimeError, "The \"#{mod.downcase}\" extension is not supported by this Meterpreter type (#{client.session_type})", caller + else + raise RuntimeError, "No module of the name #{modnameprovided} found", caller + end end # Load the extension DLL commands = load_library( - 'LibraryFilePath' => path, + 'LibraryFilePath' => path, 'LibraryFileImage' => image, - 'UploadLibrary' => true, - 'Extension' => true, - 'SaveToDisk' => opts['LoadFromDisk']) + 'UploadLibrary' => true, + 'Extension' => true, + 'SaveToDisk' => opts['LoadFromDisk']) end # wire the commands into the client diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb index ba620554f1..b336d7b68b 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb @@ -63,7 +63,7 @@ module Console::CommandDispatcher def unknown_command(cmd, line) if @filtered_commands.include?(cmd) - print_error("The '#{cmd}' command is not supported by this Meterpreter type (#{client.session_type})") + print_error("The \"#{cmd}\" command is not supported by this Meterpreter type (#{client.session_type})") return true end 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 34ffa25b18..f9200fbb00 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -1262,23 +1262,7 @@ class Console::CommandDispatcher::Core # Use API to get list of extensions from the gem exts.merge(MetasploitPayloads::Mettle.available_extensions(client.sys.config.sysinfo['BuildTuple'])) else - msf_path = MetasploitPayloads.msf_meterpreter_dir - gem_path = MetasploitPayloads.local_meterpreter_dir - [msf_path, gem_path].each do |path| - ::Dir.entries(path).each { |f| - if (::File.file?(::File.join(path, f))) - client.binary_suffix.each { |s| - if (f =~ /ext_server_(.*)\.#{s}/ ) - if (client.binary_suffix.size > 1) - exts.add($1 + ".#{s}") - else - exts.add($1) - end - end - } - end - } - end + exts.merge(client.binary_suffix.map { |suffix| MetasploitPayloads.list_meterpreter_extensions(suffix) }.flatten) end print(exts.to_a.join("\n") + "\n") @@ -1294,7 +1278,7 @@ class Console::CommandDispatcher::Core md = m.downcase # Temporary hack to pivot mimikatz over to kiwi until - # everone remembers to do it themselves + # everyone remembers to do it themselves if md == 'mimikatz' print_warning('The "mimikatz" extension has been replaced by "kiwi". Please use this in future.') md = 'kiwi' @@ -1312,7 +1296,7 @@ class Console::CommandDispatcher::Core end if (extensions.include?(md)) - print_error("The '#{md}' extension has already been loaded.") + print_error("The \"#{md}\" extension has already been loaded.") next end @@ -1326,6 +1310,7 @@ class Console::CommandDispatcher::Core rescue print_line log_error("Failed to load extension: #{$!}") + next end @@ -1338,30 +1323,9 @@ class Console::CommandDispatcher::Core def cmd_load_tabs(str, words) tabs = SortedSet.new if extensions.include?('stdapi') && !client.sys.config.sysinfo['BuildTuple'].blank? - # Use API to get list of extensions from the gem - MetasploitPayloads::Mettle.available_extensions(client.sys.config.sysinfo['BuildTuple']).each { |f| - if !extensions.include?(f.split('.').first) - tabs.add(f) - end - } + tabs.merge(MetasploitPayloads::Mettle.available_extensions(client.sys.config.sysinfo['BuildTuple'])) else - msf_path = MetasploitPayloads.msf_meterpreter_dir - gem_path = MetasploitPayloads.local_meterpreter_dir - [msf_path, gem_path].each do |path| - ::Dir.entries(path).each { |f| - if (::File.file?(::File.join(path, f))) - client.binary_suffix.each { |s| - if (f =~ /ext_server_(.*)\.#{s}/ ) - if (client.binary_suffix.size > 1 && !extensions.include?($1 + ".#{s}")) - tabs.add($1 + ".#{s}") - elsif (!extensions.include?($1)) - tabs.add($1) - end - end - } - end - } - end + tabs.merge(client.binary_suffix.map { |suffix| MetasploitPayloads.list_meterpreter_extensions(suffix) }.flatten) end return tabs.to_a end From 2bc8ff8db7b55e83c77245d36436db459549f6c1 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 15 Jan 2021 13:04:38 -0500 Subject: [PATCH 6/9] Consistently return nil when an id or name fails to resolve --- lib/rex/post/meterpreter/extension_mapper.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/rex/post/meterpreter/extension_mapper.rb b/lib/rex/post/meterpreter/extension_mapper.rb index d3ee0a6932..74c8f7ad95 100644 --- a/lib/rex/post/meterpreter/extension_mapper.rb +++ b/lib/rex/post/meterpreter/extension_mapper.rb @@ -16,18 +16,24 @@ class ExtensionMapper end def self.get_extension_id(name) - k = self.get_extension_klass(name) + begin + k = self.get_extension_klass(name) + rescue RuntimeError + return nil + end + k.extension_id end def self.get_extension_name(id) - self.get_extension_names.each do |name| + self.get_extension_names.find do |name| begin klass = self.get_extension_klass(name) rescue RuntimeError next end - return name if klass.extension_id == id + + klass.extension_id == id end end From 97479066ba98727b8604995b44a5935314a97357 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Tue, 19 Jan 2021 11:51:15 -0500 Subject: [PATCH 7/9] Disabling filtering via command IDs on Windows for now Filtering via command IDs would be a backwards incompatible change, so skip it on Windows until the payloads gem has had a major version bump. --- .../post/meterpreter/ui/console/command_dispatcher/core.rb | 5 +++++ 1 file changed, 5 insertions(+) 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 f9200fbb00..d0b2e27881 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -120,6 +120,11 @@ class Console::CommandDispatcher::Core 'set_timeouts' => [COMMAND_ID_CORE_TRANSPORT_SET_TIMEOUTS], } + # XXX: Remove this line once the payloads gem has had another major version bump from 2.x to 3.x and + # rapid7/metasploit-payloads#451 has been landed to correct the `enumextcmd` behavior on Windows. Until then, skip + # filtering for Windows which supports all the filtered commands anyways. + reqs.clear if client.base_platform == 'windows' + filter_commands(cmds, reqs) end From 8a0a56f584e74fd74f3890666b2967940ec7ba0b Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 21 Jan 2021 13:55:06 -0500 Subject: [PATCH 8/9] Use a more descriptive status indicator for command routines --- .../ui/console/command_dispatcher.rb | 2 +- lib/rex/ui/text/dispatcher_shell.rb | 31 ++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb index b336d7b68b..a1b08f7cd8 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher.rb @@ -64,7 +64,7 @@ module Console::CommandDispatcher def unknown_command(cmd, line) if @filtered_commands.include?(cmd) print_error("The \"#{cmd}\" command is not supported by this Meterpreter type (#{client.session_type})") - return true + return :handled end super diff --git a/lib/rex/ui/text/dispatcher_shell.rb b/lib/rex/ui/text/dispatcher_shell.rb index 6b79765c08..6e90bf0c61 100644 --- a/lib/rex/ui/text/dispatcher_shell.rb +++ b/lib/rex/ui/text/dispatcher_shell.rb @@ -333,9 +333,11 @@ module DispatcherShell # A callback that can be used to handle unknown commands. This can for example, allow a dispatcher to mark a command # as being disabled. # - # @return [Boolean] Returns true when the dispatcher has handled the command. + # @return [Symbol, nil] Returns a symbol specifying the action that was taken by the handler or `nil` if no action + # was taken. The only supported action at this time is `:handled`, signifying that the unknown command was handled + # by this dispatcher and no additional dispatchers should receive it. def unknown_command(method, line) - false + nil end end @@ -463,11 +465,16 @@ module DispatcherShell # # Run a single command line. # + # @param [String] line The command string that should be executed. + # @param [Boolean] propagate_errors Whether or not to raise exceptions that are caught while executing the command. + # + # @return [Boolean] A boolean value signifying whether or not the command was handled. Value is `true` when the + # command line was handled. def run_single(line, propagate_errors: false) - arguments = parse_line(line) - method = arguments.shift - found = false - error = false + arguments = parse_line(line) + method = arguments.shift + cmd_status = nil # currently either nil or :handled, more statuses can be added in the future + error = false # If output is disabled output will be nil output.reset_color if (output) @@ -482,12 +489,12 @@ module DispatcherShell if (dispatcher.commands.has_key?(method) or dispatcher.deprecated_commands.include?(method)) self.on_command_proc.call(line.strip) if self.on_command_proc run_command(dispatcher, method, arguments) - found = true - elsif dispatcher.unknown_command(method, line) - found = true + cmd_status = :handled + elsif cmd_status.nil? + cmd_status = dispatcher.unknown_command(method, line) end rescue ::Interrupt - found = true + cmd_status = :handled print_error("#{method}: Interrupted") raise if propagate_errors rescue OptionParser::ParseError => e @@ -515,12 +522,12 @@ module DispatcherShell break if (dispatcher_stack.length != entries) } - if (found == false and error == false) + if (cmd_status.nil? && error == false) unknown_command(method, line) end end - return found + return cmd_status == :handled end # From 7c51dd0b6853ea86cd03af8e8773127e84353a76 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 11 Feb 2021 18:59:25 -0500 Subject: [PATCH 9/9] Always define the detach and ssl_verify commands in the cmds hash Defining the commands in the cmds hash is necessary for them to be filtered and then reported to the user as incompatible when applicable. This moves their special compatibility checks into the actual command handler. --- .../ui/console/command_dispatcher/core.rb | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) 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 d0b2e27881..00e66fcdc0 100644 --- a/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/core.rb @@ -77,20 +77,14 @@ class Console::CommandDispatcher::Core 'migrate' => 'Migrate the server to another process', 'pivot' => 'Manage pivot listeners', # transport related commands + 'detach' => 'Detach the meterpreter session (for http/https)', 'sleep' => 'Force Meterpreter to go quiet, then re-establish session', 'transport' => 'Manage the transport mechanisms', 'get_timeouts' => 'Get the current session timeout values', 'set_timeouts' => 'Set the current session timeout values', + 'ssl_verify' => 'Modify the SSL certificate verification setting' } - if client.passive_service - cmds['detach'] = 'Detach the meterpreter session (for http/https)' - end - - if client.passive_service && client.sock.type? == 'tcp-ssl' - cmds['ssl_verify'] = 'Modify the SSL certificate verification setting' - end - if msf_loaded? cmds['info'] = 'Displays information about a Post module' end @@ -516,6 +510,10 @@ class Console::CommandDispatcher::Core # Disconnects the session # def cmd_detach(*args) + unless client.passive_service + print_error('The detach command is not applicable with the current transport') + return + end client.shutdown_passive_dispatcher shell.stop end @@ -726,7 +724,7 @@ class Console::CommandDispatcher::Core @@ssl_verify_opts = Rex::Parser::Arguments.new( '-e' => [ false, 'Enable SSL certificate verification' ], '-d' => [ false, 'Disable SSL certificate verification' ], - '-q' => [ false, 'Query the statis of SSL certificate verification' ], + '-q' => [ false, 'Query the status of SSL certificate verification' ], '-h' => [ false, 'Help menu' ]) # @@ -749,6 +747,11 @@ class Console::CommandDispatcher::Core return end + unless client.passive_service && client.sock.type? == 'tcp-ssl' + print_error('The ssl_verify command is not applicable with the current transport') + return + end + query = false enable = false disable = false