Skip to content

Conversation

@zeroSteiner
Copy link
Contributor

This enables sessions to MSSQL servers that require encryption. Currently, Metasploit can bruteforce MSSQL servers that offer/require encryption because our login method will proxy the traffic over an SSL socket using the old TDSSSLProxy class. The existing logic didn't support sessions though, so when a user wanted encryption (set SSL to true) or the server required it, Metasploit would fail to establish an interactive session or do anything after the login operation. Metasploit was effecitvely unable to communicate with the MSSQL server after the login operation when encryption was requried or enabled.

These changes add a new MsTds::Channel which leverages Rex's socket abstraction to facilitate the necessary encapsulation for the TLS negotiation. The TLS negotiation is more complex than other protocols because the frames used during the negotiation process have to be encapsulated within MS-TDS framing. Once TLS is established, the MS-TDS frames are within the tunnel. This encapsulation can be seen in the MsTds::Channel class #write and #_read_handler methods.

The MsTds::Channel has a couple of advantages over the old proxy class. The first is the channel object is now always used for the communication, there's no need to switch to writing to the proxy object or swap sockets instead when SSL is in use. The second is that it's using the new Rex::Proto::Tcp#starttls method which ensures that the TLS options are consistent with other usages in the framework.

Requires:

Closes #18745

Verification

  • Test the new MsTds::Channel object
    • Setup an MSSQL server that requires encryption
    • Use the mssql_login module to authenticate to it and establish an interactive session
  • Test pivoted TCP client channels work with Meterpreter
    • Establish a Meterpreter session (IIRC, all meterpreter support TCP client channels)
    • From the msfconsole prompt, not meterpreter, use the connect command and the -c argument to specify the session object, connect to a netcat listener
    • Test that IO works both ways from Meterpreter to the netcat listern and from the netcat listener to meterpreter
    • Test that the connection can be closed by both sides, just Ctrl+C from Metasploit, repeat the connection setup and terminate it from the netcat listener
      • This is a critical step that demonstrates the channel clean up logic is working as intended with the refactoring. Regardless of who closes the connection, the close operation should be propagated to the peer and fully processed to close the socket, exit the relay, close and cleanup the Meterpreter channel object, etc. This can also be tested for the MsTds::Channel by closing the connection from the MSSQL server using the KILL query verb just know that the KILL query can't be used to kill the connection that's making the query.
  • Optionally test PostgreSQL encryption connections work (they were switched to the new #starttls method)
  • Optionally test WebSockets still work with the refactoring, credentials are available to make this easy, just reach out

@zeroSteiner zeroSteiner force-pushed the fix/issue/18745 branch 2 times, most recently from f2aedf6 to a6c4485 Compare November 5, 2025 21:43
Comment on lines -86 to -90
def dio_close_handler(packet)
rsock.close

return super(packet)
end
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was removed because the #close method is no longer replaced with something that'd close the channel. The original logic was prone to creating recursion loops during development and would send a close command back to Meterpreter when Meterpreter initiated the close operation.

The new implementation should simply things by always closing the channel from the channel object level instead of the rsock. Now when a channel is closed by Meterpreter, the #dio_close_handler will simply close and cleanup the channel locally without echoing the command back to Meterpreter.

When the channel is closed by Metasploit Framework, it's either closed by lsock.close which causes an EOF to be read in the Relay Manager which will dispatch to the on_exit callback, which by default calls #close_write. Alternatively, the channel object can be closed directly, but there's no longer a loop as everything is notified.

@dledda-r7 dledda-r7 self-assigned this Nov 6, 2025
@zeroSteiner
Copy link
Contributor Author

I started work on a test module for testing socket channels (TCP client, TCP server and UDP). It's a WIP, but can be used for testing here: https://github.com/zeroSteiner/metasploit-framework/blob/feat/mod/socket-channel-tests/test/modules/post/test/socket_channels.rb

I think it probably makes sense to keep it in a separate branch and PR it on its own especially if there are pre-existing issues and inconsistencies.

Preview

msf post(test/socket_channels) > rerun
[*] Reloading module...
[*] Running against session 2
[*] Session type is meterpreter and platform is linux
[*] Running TCP client channel tests...
[+] Receives data from the peer
[+] Sends data to the peer
[+] Propagates close events to the peer
[+] Propagates close events from the peer
[*] Running UDP client channel tests...
[+] Receives data from the peer
[+] Sends data to the peer
[*] Passed: 6; Failed: 0; Skipped: 0
[*] Post module execution completed
msf post(test/socket_channels) >

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for encrypted mssql communications

2 participants