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

Fix session verification by checking TLV negotiation #14789

Merged
merged 1 commit into from
Feb 22, 2021

Conversation

zeroSteiner
Copy link
Contributor

@zeroSteiner zeroSteiner commented Feb 22, 2021

The AutoVerifySession feature that we have does not appear to work since 6.x added full TLV encryption. This is due to the fact that the TLV negotiation takes place before session verification. The session verification itself just issues a TLV to fetch the remote machine ID to ensure that the session is responsive. If the TLV negotiation times out, a Rex::TimeoutError is raised and the session is closed. In this case the timeout takes the default value of 300 seconds and closes the session without any error message describing why.

The solution to address this proposed within this PR is to require successful TLV encryption negotiation in order for a session to be considered valid. Since v6 all Meterpreters support TLV encryption and since v5 Meterpreter sessions are incompatible anyway, we should never have a session established by a Meterpreter instance regardless of the implementation and platform that does not support TLV encryption. Metasploit should (and does) fail closed when TLV encryption can not be established, so there's no point in trying to issue the command to obtain the machine ID if it fails.

This also removes the AutoVerifySession datastore option (which previously defaulted to true) and instead always performs the verification, because again all valid Meterpreter instances should negotiate TLV encryption.

Verification

List the steps needed to make sure this thing works

  • Start msfconsole
  • Start a reverse TCP Meterpreter instance, any platform will do
    • handler -p python/meterpreter/reverse_tcp -P 4444 -H 192.168.159.128
    • Connect to the listener with netcat nc localhost 4444 and just let it sit
    • After 30 seconds, see that the session is invalid and that it is closed
  • Start a reverse HTTP Meterpreter instance, (Python is eaiest for this step to base64-decode the URL)
    • use payload/python/meterpreter/reverse_http, to_handler
    • Generate the stage and decode the Base64 blob to recover the URL (I like using CyberChef for this step)
    • Make an HTTP GET request to the recovered URL
    • See that a Meterpreter session is opened
    • After 30 seconds, see that the session is invalid and that it is closed

Output After This Patch

In this case an invalid session is opened by making an HTTP GET request to the URL. After 30 seconds (instead of 5 minutes) the session is identified as invalid and closed.

msf6 payload(python/meterpreter/reverse_http) > to_handler 
[*] Payload Handler Started as Job 1
msf6 payload(python/meterpreter/reverse_http) > 
[*] Started HTTP reverse handler on http://192.168.159.128:8080
[*] http://192.168.159.128:8080 handling request from 192.168.159.128; (UUID: mfjvr5pv) Staging python payload (39572 bytes) ...
[*] Meterpreter session 1 opened (192.168.159.128:8080 -> 192.168.159.128:33612) at 2021-02-22 11:25:22 -0500
[-] Meterpreter session 1 is not valid and will be closed
[*] 192.168.159.128 - Meterpreter session 1 closed.

Output Before This Patch

Here you see that the session is opened and then it does with no explanation. There was a period of a few minutes that elapsed between the session being opened and the message stating that it died.

msf6 payload(python/meterpreter/reverse_tcp) > to_handler 
[*] Payload Handler Started as Job 0
msf6 payload(python/meterpreter/reverse_tcp) > 
[*] Started reverse TCP handler on 192.168.159.128:4444 
[*] Sending stage (39348 bytes) to 192.168.159.128
[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.128:46070) at 2021-02-22 09:14:00 -0500
[*] 192.168.159.128 - Meterpreter session 1 closed.  Reason: Died

msf6 payload(python/meterpreter/reverse_tcp) > 

In this example, the user immediately interacts with the session and attempts to load the stdapi extension. Because the session is unresponsive, Metasploit incorrectly identifies that the Meterpreter doesn't support the load command.

msf6 payload(python/meterpreter/reverse_tcp) > 
[*] Sending stage (39344 bytes) to 192.168.159.128
[*] Meterpreter session 2 opened (192.168.159.128:4444 -> 192.168.159.128:46136) at 2021-02-22 09:17:22 -0500

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

meterpreter > load stdapi
[-] The "load" command is not supported by this Meterpreter type (python/python)
meterpreter >

@gwillcox-r7
Copy link
Contributor

Testing the first part of this shows this is now fixed:

Before:

msf6 payload(python/meterpreter/reverse_tcp) > handler -p python/meterpreter/reverse_tcp -P 4444 -H 172.26.198.249
[*] Payload handler running as background job 2.
msf6 payload(python/meterpreter/reverse_tcp) > 
[*] Started reverse TCP handler on 172.26.198.249:4444 
[*] Sending stage (39344 bytes) to 172.26.198.249
[*] Meterpreter session 2 opened (172.26.198.249:4444 -> 172.26.198.249:58348) at 2021-02-22 12:31:12 -0600
[*] 172.26.198.249 - Meterpreter session 2 closed.  Reason: Died

Took about 2-3 minutes to die.

After:

msf6 > handler -p python/meterpreter/reverse_tcp -P 4444 -H 172.26.198.249
[*] Payload handler running as background job 0.
msf6 > 
[*] Started reverse TCP handler on 172.26.198.249:4444 
[*] Sending stage (39344 bytes) to 172.26.198.249
[*] Meterpreter session 1 opened (172.26.198.249:4444 -> 172.26.198.249:58418) at 2021-02-22 12:45:29 -0600
[-] Meterpreter session 1 is not valid and will be closed
[*] 172.26.198.249 - Meterpreter session 1 closed.

Takes about 34-35 seconds to die and also prints out the message that the session is invalid.

@gwillcox-r7
Copy link
Contributor

Can also verify the reverse HTTP session works as expected:

msf6 > use payload/python/meterpreter/reverse_http
msf6 payload(python/meterpreter/reverse_http) > show options

Module options (payload/python/meterpreter/reverse_http):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST                   yes       The local listener hostname
   LPORT  8080             yes       The local listener port
   LURI                    no        The HTTP Path

msf6 payload(python/meterpreter/reverse_http) > set LHOST 172.26.198.249
LHOST => 172.26.198.249
msf6 payload(python/meterpreter/reverse_http) > generate -f raw -o reverse_http.py
[*] Writing 569 bytes to reverse_http.py...
msf6 payload(python/meterpreter/reverse_http) > to_handler
[*] Payload Handler Started as Job 2
msf6 payload(python/meterpreter/reverse_http) > 
[*] Started HTTP reverse handler on http://172.26.198.249:8080
[*] http://172.26.198.249:8080 handling request from 172.26.198.249; (UUID: ls1nrurh) Staging python payload (39528 bytes) ...
[*] Meterpreter session 3 opened (172.26.198.249:8080 -> 172.26.198.249:48222) at 2021-02-22 13:06:53 -0600
[-] Meterpreter session 3 is not valid and will be closed
[*] 172.26.198.249 - Meterpreter session 3 closed.

Copy link
Contributor

@gwillcox-r7 gwillcox-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good, and with the verification this looks good to land!

@gwillcox-r7 gwillcox-r7 merged commit 6f8821d into rapid7:master Feb 22, 2021
@gwillcox-r7 gwillcox-r7 added the rn-fix release notes fix label Feb 22, 2021
@gwillcox-r7
Copy link
Contributor

gwillcox-r7 commented Feb 22, 2021

Release Notes

Fixed a bug where Meterpreter sessions were incorrectly being validated due to the fact that TLV encryption for the session would take place before session verification. The fix now considers Meterpreter sessions valid if they successfully negotiate TLV encryption. This fix also removes the AutoVerifySession datastore option since all valid Meterpreter instances should negotiate TLV encryption automatically.

@zeroSteiner zeroSteiner deleted the fix/session-verification branch February 23, 2021 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants