Path: blob/master/lib/metasploit/framework/mssql/tdssslproxy.rb
19516 views
# -*- coding: binary -*-12require 'openssl'34#5# TDSSSLProxy:6#7# SQL Server uses the TDS protocol to transmit data between clients and8# servers. Of course this sits on top of TCP.9#10# By default, the TDS payload is not encrypted. However, if "force11# encryption" is set under the SQL Server protocol properties, it will12# use SSL/TLS to encrypt the TDS data. Oddly, the entire TCP stream is13# not encrypted (as is say for HTTPS), but instead a TDS header is14# put on the front of the TLS packet. As a result, the full TLS/SSL15# setup is done within a series of TDS payloads.16#17# This "proxy" basically creates a fake SSL endpoint (s2) from which it18# can add/remove the TDS header as required. This is implemented as a19# socket pair (think, a bidirectional pipe), where the other end is s1:20#21# sslsock <-> s1 <-> s2 <-> tdssock <-> target SQL Server.22#23# (tdssock is the reference to the "sock" from the scanner module)24#25# TO DO:26#27# This enables brute force of a SQL Server which requires encryption.28# However, future updates will permit any read/write using29# mssql_send_recv() to use crypto if required and transparently to30# other MSF developers.31#32# Cheers, JH3334class TDSSSLProxy3536TYPE_TDS7_LOGIN = 1637TYPE_PRE_LOGIN_MESSAGE = 1838STATUS_END_OF_MESSAGE = 0x013940def initialize(sock, sslkeylogfile: nil)41@tdssock = sock42@sslkeylogfile = sslkeylogfile43@s1, @s2 = Rex::Socket.tcp_socket_pair44end4546def cleanup47@running = false48@t1.join49end5051def write_to_keylog_file(ctx, sslkeylogfile)52# writing to the sslkeylogfile is required, it adds support for network capture decryption which is useful to53# decrypt TLS traffic in wireshark54if sslkeylogfile55unless ctx.respond_to?(:keylog_cb)56raise 'Unable to create sslkeylogfile - Ruby 3.2 or above required for this functionality'57end5859ctx.keylog_cb = proc do |_sock, line|60File.open(sslkeylogfile, 'ab') do |file|61file.write("#{line}\n")62end63end64end65end6667def setup_ssl68@running = true69@t1 = Thread.start { ssl_setup_thread }70ctx = OpenSSL::SSL::SSLContext.new(:SSLv23)71write_to_keylog_file(ctx, @sslkeylogfile)72ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM"73@ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ctx)74@ssl_socket.connect75end7677def send_recv(pkt)78@ssl_socket.write(pkt)79done = false80resp = ""8182while (not done)83head = @ssl_socket.read(8)84if !(head and head.length == 8)85return false86end8788# Is this the last buffer?89if (head[1, 1] == "\x01" or not check_status)90done = true91end9293# Grab this block's length94rlen = head[2, 2].unpack('n')[0] - 89596while (rlen > 0)97buff = @ssl_socket.read(rlen)98return if not buff99resp << buff100rlen -= buff.length101end102103end104resp105end106107def ssl_setup_thread108while @running do109res = select([@tdssock, @s2], nil, nil, 0.1)110if res111res[0].each do |r|112# response from SQL Server for client113if r == @tdssock114resp = @tdssock.recv(4096)115if @ssl_socket.state[0, 5] == "SSLOK"116@s2.send(resp, 0)117else118@s2.send(resp[8..-1], 0)119end120end121122# request from client for SQL Server123if r == @s2124resp = @s2.recv(4096)125# SSL negotiation completed - just send it on126if @ssl_socket.state[0, 5] == "SSLOK"127@tdssock.send(resp, 0)128# Still doing SSL129else130tds_pkt_len = 8 + resp.length131pkt_hdr = ''132pkt_hdr << [TYPE_PRE_LOGIN_MESSAGE, STATUS_END_OF_MESSAGE, tds_pkt_len, 0x0000, 0x00, 0x00].pack('CCnnCC')133pkt = pkt_hdr << resp134@tdssock.send(pkt, 0)135end136end137end138end139end140@s1.close141@s2.close142end143end144145146147