-
Notifications
You must be signed in to change notification settings - Fork 13.8k
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
Fixing meterpreter to support is_weak_key byte flag from mettle #19259
Conversation
…s used. skip module loading if encryption is weak.
@@ -781,7 +788,8 @@ def negotiate_tlv_encryption(timeout: client.comm_timeout) | |||
|
|||
{ | |||
key: sym_key, | |||
type: key_type | |||
type: key_type, | |||
is_weak_key: is_weak_key |
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.
The preferred Ruby style for something like this I think would be to use the ?
suffix instead of the is_
prefix.
is_weak_key: is_weak_key | |
weak_key?: is_weak_key |
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.
Using the !is_weak_key will invert the logic? or am I wrong
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.
You're right, I was editing this and toying with the idea of calling it secure_key?: !is_weak_key
. Good catch, I've updated my suggestion.
if key_dec_data.length == 17 || key_dec_data.length == 33 | ||
sym_key = key_dec_data[0, key_dec_data.length - 1] | ||
is_weak_key = key_dec_data[key_dec_data.length - 1] != "\x00" | ||
else | ||
sym_key = key_dec_data | ||
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.
I think we might want to make this a bit more explicit to avoid any unintended behavior
Something like
key_length = { AES128 => 16, AES256 => 32 }[key_type]
# fail somehow if key_length.nil? (bad key_type)
sym_key = key_dec_data[...key_length]
key_dec_data = key_dec_data[key_length...]
# fail somehow if sym_key.length != key_length
if key_dec_data.length > 0
key_strength = key_dec_data[0]
key_dec_data = key_dec_data[1...]
is_weak_key = key_strength != "\x00"
end
By the end, key_dec_data
should be fully consumed and be empty, if not that means extra data has been added so let's ignore it because it's probably some future feild that was also appended. As it is, if we add another field in the future, the == 17 || == 33 logic wouldn't catch it because we'd be looking at a length of 18 or 34 meaning the session wouldn't establish.
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.
Everything looks good to me now. I think the warning makes sense and is appropriately stern.
In this output you can see testing the new build of mettle from both a normal environment (session 1) and a chroot environment (session 2).
metasploit-framework.pr (S:0 J:0) payload(linux/x64/meterpreter_reverse_tcp) > to_handler
[*] Payload Handler Started as Job 2
metasploit-framework.pr (S:0 J:1) payload(linux/x64/meterpreter_reverse_tcp) >
[*] Started reverse TCP handler on 192.168.159.128:4444
metasploit-framework.pr (S:0 J:1) payload(linux/x64/meterpreter_reverse_tcp) > [*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.128:41524) at 2024-06-17 17:33:46 -0400
metasploit-framework.pr (S:1 J:1) payload(linux/x64/meterpreter_reverse_tcp) > sessions -i 1
[*] Starting interaction with 1...
meterpreter > getuid
Server username: smcintyre
meterpreter > sysinfo
Computer : fedora.local
OS : Fedora 40 (Linux 6.8.11-300.fc40.x86_64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > background
[*] Backgrounding session 1...
metasploit-framework.pr (S:1 J:1) payload(linux/x64/meterpreter_reverse_tcp) >
[!] Meterpreter session 2 is using a weak encryption key.
[!] Meterpreter start up operations have been aborted. Use the session at your own risk.
[*] Meterpreter session 2 opened (192.168.159.128:4444 -> 192.168.159.128:46570) at 2024-06-17 17:34:10 -0400
metasploit-framework.pr (S:2 J:1) payload(linux/x64/meterpreter_reverse_tcp) > sessions -i 2
[*] Starting interaction with 2...
meterpreter > getuid
[-] The "getuid" command requires the "stdapi" extension to be loaded (run: `load stdapi`)
meterpreter > sysinfo
[-] The "sysinfo" command requires the "stdapi" extension to be loaded (run: `load stdapi`)
meterpreter > load stdapi
Loading extension stdapi...Success.
meterpreter > getuid
Server username: root
meterpreter > sysinfo
Computer : 192.168.250.134
OS : Fedora 40 (Linux 6.8.11-300.fc40.x86_64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
I also tested the java, python and windows Meterpreters to make sure the ones that don't send the extra byte still work as well.
if session.tlv_enc_key[:weak_key?] | ||
print_warning("Meterpreter session #{session.sid} is using a weak encryption key.") | ||
print_warning('Meterpreter start up operations have been aborted. Use the session at your own risk.') | ||
return nil |
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.
Do we want to just keep the session open for now? 👀
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.
Yes because:
- We can't be sure that the action that was taken to open the session can be retaken, ie the exploit might have only been able to run once
- We should avoid re-connecting and re-negotiating if we can
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.
Should this be:
return nil |
Unless I'm missing something more nuanaced
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.
If we remove the return nil
statement as you're suggesting, the bootstrap process will continue. In a default configuration, that means at least loading the stdapi extension and running sysinfo
. That could also mean running auto run scripts.
With the warning in place and the return nil
here, we're aborting that automation and leaving it up to the user to make a decision on what to do with the session they've opened.
sym_key = rsa_key.private_decrypt(key_enc, OpenSSL::PKey::RSA::PKCS1_PADDING) | ||
key_dec_data = rsa_key.private_decrypt(key_enc, OpenSSL::PKey::RSA::PKCS1_PADDING) | ||
sym_key = key_dec_data[0..key_length - 1] | ||
if key_dec_data.length > key_length |
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.
key_dec_data.length
might need a nil check here
request.add_tlv(TLV_TYPE_RSA_PUB_KEY, rsa_pub_key.to_der) | ||
|
||
begin | ||
response = client.send_request(request, timeout) | ||
key_enc = response.get_tlv_value(TLV_TYPE_ENC_SYM_KEY) | ||
key_type = response.get_tlv_value(TLV_TYPE_SYM_KEY_TYPE) | ||
|
||
key_length = { Packet::ENC_FLAG_AES128 => 16, Packet::ENC_FLAG_AES256 => 32 }[key_type] | ||
is_weak_key = false |
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.
Not a blocker; should this default to nil
?
I tested it out one more time both from inside and outside of a chroot environment where the issue was. Everything appears to be working as expected, so I'll get this merged. Thanks! Testing Output
|
Release NotesThis updates Metasploit to check for a new flag that is sent as part of the encryption key negotiation with Meterpreter which indicates if Meterpreter had to use a weak source of entropy to generate the key. |
This pull request is part of this mettle pr regarding the issue #255.
During the TLV encryption negotiation, if a weak key is used because the system doesn't have any strong entropy source, the c2 will be notified and a warning will be raised.
This change doesn't affect other Meterpreter implementations.