New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better handling for incompatible Meterpreter extensions and commands (Round 2) #15109
Better handling for incompatible Meterpreter extensions and commands (Round 2) #15109
Conversation
suggestion_keys = MetasploitPayloads.list_meterpreter_extension_suffixs(ex.name) + MetasploitPayloads::Mettle.available_platforms(ex.name) | ||
suggestion_map = { | ||
'jar' => 'java', | ||
'php' => 'php', | ||
'py' => 'python', | ||
'x64.dll' => 'windows/x64', | ||
'x86.dll' => 'windows', | ||
'mips64-linux-muslsf' => 'linux/mips64', | ||
'mipsel-linux-muslsf' => 'linux/mipsle', | ||
'powerpc64le-linux-musl' => 'linux/ppc64le', | ||
'mips-linux-muslsf' => 'linux/mipsbe', | ||
'powerpc-linux-muslsf' => 'linux/ppc', | ||
's390x-linux-musl' => 'linux/zarch', | ||
'x86_64-linux-musl' => 'linux/x64', | ||
'i486-linux-musl' => 'linux/x86', | ||
'armv5l-linux-musleabi' => 'linux/armle', | ||
'aarch64-linux-musl' => 'linux/aarch64', | ||
'armv5b-linux-musleabi' => 'linux/armbe', | ||
'powerpc-e500v2-linux-musl' => 'linux/ppce500v2', | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a fan of this static mapping. Suggestions would be appreciated! The core of the issue is that MetasploitPayloads
take binary suffixes while MetasploitPayloads::Mettle
takes platforms (which are just build tuples like x86_64-linux-musl
) to get the extensions code. In both cases, there is not an easy way to reverse the mapping to a session type (like Msf::Sessions::Meterpreter_x64_Win
.
A more flexible solution would need to be implemented for MetasploitPayloads where the platform and arch could yield the binary suffix. For Mettle, the build tuple would probably need to be defined as a class attribute and then all of the session types would need to be enumerated to identify the ones that are Mettle and then a payload would need to be inferred from them based on the platform / architecture.
One thought I had was to update the Metasploit - MetasploitPayloads interface to take build tuples like Mettle does. The build tuples seem a lot more flexible and solve the problem of architectures sharing an extension and platform nicely (like 64-bit Windows does with the x64.dll
extension). This would probably involve making fake extensions though for the interpreted languages like python--
and our own for Windows like x86_64-windows-msvc
. I don't think these follow a particular standard other than being arch-platform-runtime
. This would be a fair amount of work but then each session object could have a build tuple attribute that could be enumerated. Mapping session types to payloads though would still likely need to be done using a static map or by enumerating all payloads which would include derivatives due to the stagers.
Alternatively, this is likely the only place that it makes sense to have this level of suggestions since it's where the user explicitly loads their extensions.
Delayed as this requires rapid7/metasploit-payloads#485 and rapid7/mettle#215 to be landed, which I'll be looking into today. |
The two prerequisite PRs are now landed, this should be ready for review. |
lib/rex/post/meterpreter/packet.rb
Outdated
@@ -155,7 +155,7 @@ module Meterpreter | |||
COMMAND_ID_START_POWERSHELL = 14000 | |||
COMMAND_ID_START_LANATTACKS = 15000 | |||
COMMAND_ID_START_PEINJECTOR = 16000 | |||
COMMAND_ID_START_MIMIKATZ = 17000 | |||
COMMAND_ID_START_MIMIKATZ = 17000 # removed in MSF v6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just a general comment from our discussion and does not need to be resolved but the reason this was left in here was because we didn't want people to use the same command ID as the now removed Mimikatz extension. When we do eventually get around to another major version bump, then we can remove this command ID but to avoid breaking things or getting people confused we will leave this in for now.
module ClassMethods | ||
# | ||
# Check whether or not the command dispatcher is capable of handling the | ||
# specified command. The command may still be disabled through some means | ||
# at runtime. | ||
# | ||
# @param [String] name The name of the command to check. | ||
# @return [Boolean] true if the dispatcher can handle the command. | ||
def has_command?(name) | ||
self.method_defined?("cmd_#{name}") | ||
end | ||
|
||
def included(base) | ||
# Propagate the included hook | ||
CommandDispatcher.included(base) | ||
end | ||
end | ||
|
||
def self.included(base) | ||
# Install class methods so they are inheritable | ||
base.extend(ClassMethods) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another comment to help future travelers:
This sets up a system so that anything that includes either CommandDispatcher
or ClassMethods
now has a hook so that when that class is included elsewhere (please note its only include that is hooked, see https://apidock.com/ruby/Module/included for more information on how the included()
method operates), the hook (aka the included()
method defined in ClassMethods
) propagates itself and that class is extended to include the propagation hook function via included()
and also add the has_command?()
function to whatever class it is imported into.
This has the effect of essentially propagating the has_command?()
function infinitely through every class or module that imports , either directly or indirectly, from CommandDispatcher
or ClassMethods
, thereby ensuring has_command()?
is populated as a class method through all these indirect imports.
4912abe
to
626bbeb
Compare
@gwillcox-r7 this has been rebased to resolve all of the conflicts. |
Python looks good:
|
Windows also looks to be performing well:
|
Checks look good, merging this in now... |
Release NotesAn update has been made so that when a user attempts to load an extension that isn't available for the current Meterpreter type, they will now receive a list of payloads that would yield a Meterpreter session that would be capable of loading the specified extension. Additionally, when a user runs a command that's in an extension that hasn't been loaded yet, Metasploit will now tell the user which extension needs to be loaded for the command to run. |
This expands on #14617 to make two additional improvements to help users.
List Meterpreters That Support A Particular Extension
Currently, when a user attempts to load an extension that isn't available for the current Meterpreter type, they receive and error message saying that it's incompatible. This change takes this a step further and lists payloads that would yield a Meterpreter session that would be capable of loading the specified Meterpreter session. Spoiler alert, the vast majority of the time, the solution is going to be to get a native Windows Meterpreter which by far has the most extensions. Regardless, this is a pretty flexible albeit brittle solution. This requires the changes from rapid7/metasploit-payloads#485 and rapid7/mettle#215 which allow mapping an extension name back to the binary suffix and build tuple respectively. The suggestions are payload prefixs (e.g.
windows/meterpreter*
to implywindows/meterpreter/reverse_tcp
,windows/meterpreter_reverse_http
etc.) since the prefix is sufficient to match the platform and architecture which are the key components Meterpreter type while the stager is irrelevant. This should be intuitive enough for users, but suggestions are welcome.Android is completely ignored because its extensions are baked in unlike the others in MetasploitPayloads so there are no files that can be enumerated.
List The Extension That Provides A Particular Command
Currently, when a user runs a command that's in an extension that hasn't been loaded yet, they'll get the standard "Unknown command" error which isn't particularly informative. This change updates the core dispatcher and adds an unknown command handler that will check the command dispatchers of each extension to see if one of them support the command and if so it will then tell the user which extension needs to be loaded. Now users that type in
creds_all
will be informed that they must first load thekiwi
extension that provides that command. There's no guarantee that it'll work though, the current Meterpreter may not be compatible with it so the user will need to attempt to load it and find out.Verification
List the steps needed to make sure this thing works
msfconsole
creds_all
, see that thekiwi
extension needs to be loaded firstload kiwi
command, see that thekiwi
extension isn't supported but also suggestions that it is supported by Windows payloadsmigrate
command, see that the core command dispatcher's disabled commands are still reported as unsupportedcreds_all
, see that thekiwi
extension needs to be loaded firstload kiwi
command, see that thekiwi
extension is loadedcreds_all
command, see that it either works or throws a kiwi-specific error message showing that it did infact runExample (Old)
In this example, the
creds_all
command is simply reported as unknown which isn't great. Then when the user remembers that they need to load the kiwi extension, it just reports that it's not supported but doesn't offer any solutions.Example (New)
In this case Metasploit is much more helpful, not only identifying that
creds_all
is a command but that it needs kiwi to be loaded first. When kiwi fails to load, the user is informed that it's not supported but Metasploit suggests that the user get a payload from thewindows/meterpreter
orwindows/x64/meterpreter
groups.