Skip to content
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

Merged

Conversation

zeroSteiner
Copy link
Contributor

@zeroSteiner zeroSteiner commented Apr 28, 2021

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 imply windows/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 the kiwi 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

  • Start msfconsole
  • Obtain a Python Meterpreter session
    • Run the command creds_all, see that the kiwi extension needs to be loaded first
    • Run the load kiwi command, see that the kiwi extension isn't supported but also suggestions that it is supported by Windows payloads
    • Run the migrate command, see that the core command dispatcher's disabled commands are still reported as unsupported
  • Obtain a Windows Meterpreter session
    • Run the command creds_all, see that the kiwi extension needs to be loaded first
    • Run the load kiwi command, see that the kiwi extension is loaded
    • Run the creds_all command, see that it either works or throws a kiwi-specific error message showing that it did infact run

Example (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.

msf6 payload(python/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > sysinfo
Computer        : localhost.localdomain
OS              : Linux 5.11.15-100.fc32.x86_64 #1 SMP Fri Apr 16 14:19:43 UTC 2021
Architecture    : x64
System Language : en_US
Meterpreter     : python/linux
meterpreter > creds_all
[-] Unknown command: creds_all.
meterpreter > load kiwi
Loading extension kiwi...
[-] Failed to load extension: The "kiwi" extension is not supported by this Meterpreter type (python/linux)
meterpreter >

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 the windows/meterpreter or windows/x64/meterpreter groups.

msf6 payload(python/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...

meterpreter > creds_all
[-] The "creds_all" command requires the "kiwi" extension to be loaded (run: `load kiwi`)
meterpreter > load kiwi
Loading extension kiwi...
[-] Failed to load extension: The "kiwi" extension is not supported by this Meterpreter type (python/linux)
[-] The "kiwi" extension is supported by the following Meterpreter payloads:
[-]   - windows/x64/meterpreter*
[-]   - windows/meterpreter*
meterpreter > migrate 1234
[-] The "migrate" command is not supported by this Meterpreter type (python/linux)
meterpreter >

Comment on lines 1324 to 1353
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',
}
Copy link
Contributor Author

@zeroSteiner zeroSteiner Apr 28, 2021

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.

@smcintyre-r7 smcintyre-r7 added blocked Blocked by one or more additional tasks library labels May 4, 2021
@gwillcox-r7
Copy link
Contributor

Delayed as this requires rapid7/metasploit-payloads#485 and rapid7/mettle#215 to be landed, which I'll be looking into today.

@gwillcox-r7
Copy link
Contributor

gwillcox-r7 commented Jun 10, 2021

The two prerequisite PRs are now landed, this should be ready for review.

@smcintyre-r7 smcintyre-r7 removed the blocked Blocked by one or more additional tasks label Jun 10, 2021
@gwillcox-r7 gwillcox-r7 self-assigned this Jun 14, 2021
@gwillcox-r7 gwillcox-r7 added enhancement rn-enhancement release notes enhancement labels Jun 15, 2021
@@ -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
Copy link
Contributor

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.

Comment on lines +31 to +52
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
Copy link
Contributor

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.

@zeroSteiner zeroSteiner force-pushed the feat/suggest-meterpreter-for-extension branch from 4912abe to 626bbeb Compare June 16, 2021 13:10
@zeroSteiner
Copy link
Contributor Author

@gwillcox-r7 this has been rebased to resolve all of the conflicts.

@gwillcox-r7
Copy link
Contributor

Python looks good:

msf6 exploit(multi/handler) > show options

Module options (exploit/multi/handler):

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------


Payload options (python/meterpreter/bind_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LPORT  4444             yes       The listen port
   RHOST  127.0.0.1        no        The target address


Exploit target:

   Id  Name
   --  ----
   0   Wildcard Target


msf6 exploit(multi/handler) > exploit

[*] Started bind TCP handler against 127.0.0.1:4444
[*] Sending stage (39392 bytes) to 127.0.0.1
[*] Meterpreter session 1 opened (0.0.0.0:0 -> 127.0.0.1:4444) at 2021-06-16 11:06:08 -0500

meterpreter > creds_all
[-] The "creds_all" command requires the "kiwi" extension to be loaded (run: `load kiwi`)
meterpreter > load kiwi
Loading extension kiwi...
[-] Failed to load extension: The "kiwi" extension is not supported by this Meterpreter type (python/linux)
[-] The "kiwi" extension is supported by the following Meterpreter payloads:
[-]   - windows/x64/meterpreter*
[-]   - windows/meterpreter*
meterpreter > migrate
[-] The "migrate" command is not supported by this Meterpreter type (python/linux)
meterpreter > 

@gwillcox-r7
Copy link
Contributor

Windows also looks to be performing well:

msf6 exploit(multi/handler) > exploit

[*] Started bind TCP handler against 192.168.251.61:4444
[*] Sending stage (200262 bytes) to 192.168.251.61
[*] Meterpreter session 3 opened (0.0.0.0:0 -> 192.168.251.61:4444) at 2021-06-16 11:12:55 -0500

meterpreter > creds_all
[-] The "creds_all" command requires the "kiwi" extension to be loaded (run: `load kiwi`)
meterpreter > load kiwi
Loading extension kiwi...
  .#####.   mimikatz 2.2.0 20191125 (x64/windows)
 .## ^ ##.  "A La Vie, A L'Amour" - (oe.eo)
 ## / \ ##  /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 ## \ / ##       > http://blog.gentilkiwi.com/mimikatz
 '## v ##'        Vincent LE TOUX            ( vincent.letoux@gmail.com )
  '#####'         > http://pingcastle.com / http://mysmartlogon.com  ***/

Success.
meterpreter > creds_all
[!] Not running as SYSTEM, execution may fail
meterpreter > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).
meterpreter > creds_all
[+] Running as SYSTEM
[*] Retrieving all credentials
msv credentials
===============

Username  Domain           NTLM                              SHA1
--------  ------           ----                              ----
test      DESKTOP-KUO5CML  * *

wdigest credentials
===================

Username          Domain           Password
--------          ------           --------
(null)            (null)           (null)
DESKTOP-KUO5CML$  WORKGROUP        (null)
test              DESKTOP-KUO5CML  (null)

kerberos credentials
====================

Username          Domain           Password
--------          ------           --------
(null)            (null)           (null)
desktop-kuo5cml$  WORKGROUP        (null)
test              DESKTOP-KUO5CML  (null)


meterpreter > 

@gwillcox-r7
Copy link
Contributor

Checks look good, merging this in now...

@gwillcox-r7 gwillcox-r7 merged commit b91c829 into rapid7:master Jun 16, 2021
@gwillcox-r7
Copy link
Contributor

Release Notes

An 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants