Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/mssql/tdssslproxy.rb
19513 views
1
# -*- coding: binary -*-
2
3
require 'openssl'
4
5
#
6
# TDSSSLProxy:
7
#
8
# SQL Server uses the TDS protocol to transmit data between clients and
9
# servers. Of course this sits on top of TCP.
10
#
11
# By default, the TDS payload is not encrypted. However, if "force
12
# encryption" is set under the SQL Server protocol properties, it will
13
# use SSL/TLS to encrypt the TDS data. Oddly, the entire TCP stream is
14
# not encrypted (as is say for HTTPS), but instead a TDS header is
15
# put on the front of the TLS packet. As a result, the full TLS/SSL
16
# setup is done within a series of TDS payloads.
17
#
18
# This "proxy" basically creates a fake SSL endpoint (s2) from which it
19
# can add/remove the TDS header as required. This is implemented as a
20
# socket pair (think, a bidirectional pipe), where the other end is s1:
21
#
22
# sslsock <-> s1 <-> s2 <-> tdssock <-> target SQL Server.
23
#
24
# (tdssock is the reference to the "sock" from the scanner module)
25
#
26
# TO DO:
27
#
28
# This enables brute force of a SQL Server which requires encryption.
29
# However, future updates will permit any read/write using
30
# mssql_send_recv() to use crypto if required and transparently to
31
# other MSF developers.
32
#
33
# Cheers, JH
34
35
class TDSSSLProxy
36
37
TYPE_TDS7_LOGIN = 16
38
TYPE_PRE_LOGIN_MESSAGE = 18
39
STATUS_END_OF_MESSAGE = 0x01
40
41
def initialize(sock, sslkeylogfile: nil)
42
@tdssock = sock
43
@sslkeylogfile = sslkeylogfile
44
@s1, @s2 = Rex::Socket.tcp_socket_pair
45
end
46
47
def cleanup
48
@running = false
49
@t1.join
50
end
51
52
def write_to_keylog_file(ctx, sslkeylogfile)
53
# writing to the sslkeylogfile is required, it adds support for network capture decryption which is useful to
54
# decrypt TLS traffic in wireshark
55
if sslkeylogfile
56
unless ctx.respond_to?(:keylog_cb)
57
raise 'Unable to create sslkeylogfile - Ruby 3.2 or above required for this functionality'
58
end
59
60
ctx.keylog_cb = proc do |_sock, line|
61
File.open(sslkeylogfile, 'ab') do |file|
62
file.write("#{line}\n")
63
end
64
end
65
end
66
end
67
68
def setup_ssl
69
@running = true
70
@t1 = Thread.start { ssl_setup_thread }
71
ctx = OpenSSL::SSL::SSLContext.new(:SSLv23)
72
write_to_keylog_file(ctx, @sslkeylogfile)
73
ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM"
74
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ctx)
75
@ssl_socket.connect
76
end
77
78
def send_recv(pkt)
79
@ssl_socket.write(pkt)
80
done = false
81
resp = ""
82
83
while (not done)
84
head = @ssl_socket.read(8)
85
if !(head and head.length == 8)
86
return false
87
end
88
89
# Is this the last buffer?
90
if (head[1, 1] == "\x01" or not check_status)
91
done = true
92
end
93
94
# Grab this block's length
95
rlen = head[2, 2].unpack('n')[0] - 8
96
97
while (rlen > 0)
98
buff = @ssl_socket.read(rlen)
99
return if not buff
100
resp << buff
101
rlen -= buff.length
102
end
103
104
end
105
resp
106
end
107
108
def ssl_setup_thread
109
while @running do
110
res = select([@tdssock, @s2], nil, nil, 0.1)
111
if res
112
res[0].each do |r|
113
# response from SQL Server for client
114
if r == @tdssock
115
resp = @tdssock.recv(4096)
116
if @ssl_socket.state[0, 5] == "SSLOK"
117
@s2.send(resp, 0)
118
else
119
@s2.send(resp[8..-1], 0)
120
end
121
end
122
123
# request from client for SQL Server
124
if r == @s2
125
resp = @s2.recv(4096)
126
# SSL negotiation completed - just send it on
127
if @ssl_socket.state[0, 5] == "SSLOK"
128
@tdssock.send(resp, 0)
129
# Still doing SSL
130
else
131
tds_pkt_len = 8 + resp.length
132
pkt_hdr = ''
133
pkt_hdr << [TYPE_PRE_LOGIN_MESSAGE, STATUS_END_OF_MESSAGE, tds_pkt_len, 0x0000, 0x00, 0x00].pack('CCnnCC')
134
pkt = pkt_hdr << resp
135
@tdssock.send(pkt, 0)
136
end
137
end
138
end
139
end
140
end
141
@s1.close
142
@s2.close
143
end
144
end
145
146
147