CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/mssql/tdssslproxy.rb
Views: 11784
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)
42
@tdssock = sock
43
@s1, @s2 = Rex::Socket.tcp_socket_pair
44
end
45
46
def cleanup
47
@running = false
48
@t1.join
49
end
50
51
def setup_ssl
52
@running = true
53
@t1 = Thread.start { ssl_setup_thread }
54
ctx = OpenSSL::SSL::SSLContext.new(:SSLv23)
55
ctx.ciphers = "ALL:!ADH:!EXPORT:!SSLv2:!SSLv3:+HIGH:+MEDIUM"
56
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ctx)
57
@ssl_socket.connect
58
end
59
60
def send_recv(pkt)
61
@ssl_socket.write(pkt)
62
done = false
63
resp = ""
64
65
while (not done)
66
head = @ssl_socket.read(8)
67
if !(head and head.length == 8)
68
return false
69
end
70
71
# Is this the last buffer?
72
if (head[1, 1] == "\x01" or not check_status)
73
done = true
74
end
75
76
# Grab this block's length
77
rlen = head[2, 2].unpack('n')[0] - 8
78
79
while (rlen > 0)
80
buff = @ssl_socket.read(rlen)
81
return if not buff
82
resp << buff
83
rlen -= buff.length
84
end
85
86
end
87
resp
88
end
89
90
def ssl_setup_thread
91
while @running do
92
res = select([@tdssock, @s2], nil, nil, 0.1)
93
if res
94
res[0].each do |r|
95
# response from SQL Server for client
96
if r == @tdssock
97
resp = @tdssock.recv(4096)
98
if @ssl_socket.state[0, 5] == "SSLOK"
99
@s2.send(resp, 0)
100
else
101
@s2.send(resp[8..-1], 0)
102
end
103
end
104
105
# request from client for SQL Server
106
if r == @s2
107
resp = @s2.recv(4096)
108
# SSL negotiation completed - just send it on
109
if @ssl_socket.state[0, 5] == "SSLOK"
110
@tdssock.send(resp, 0)
111
# Still doing SSL
112
else
113
tds_pkt_len = 8 + resp.length
114
pkt_hdr = ''
115
pkt_hdr << [TYPE_PRE_LOGIN_MESSAGE, STATUS_END_OF_MESSAGE, tds_pkt_len, 0x0000, 0x00, 0x00].pack('CCnnCC')
116
pkt = pkt_hdr << resp
117
@tdssock.send(pkt, 0)
118
end
119
end
120
end
121
end
122
end
123
@s1.close
124
@s2.close
125
end
126
end
127
128
129