Squashed more commits back into 1
parent
675100946b
commit
5266860cec
|
@ -32,6 +32,11 @@ module Metasploit
|
|||
validates :windows_authentication,
|
||||
inclusion: { in: [true, false] }
|
||||
|
||||
attr_accessor :tdsencryption
|
||||
|
||||
validates :tdsencryption,
|
||||
inclusion: { in: [true, false] }
|
||||
|
||||
def attempt_login(credential)
|
||||
result_options = {
|
||||
credential: credential,
|
||||
|
@ -70,6 +75,7 @@ module Metasploit
|
|||
self.use_ntlm2_session = true if self.use_ntlm2_session.nil?
|
||||
self.use_ntlmv2 = true if self.use_ntlmv2.nil?
|
||||
self.windows_authentication = false if self.windows_authentication.nil?
|
||||
self.tdsencryption = false if self.tdsencryption.nil?
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -49,12 +49,6 @@ module Metasploit
|
|||
|
||||
disconnect if self.sock
|
||||
connect
|
||||
|
||||
# Send a prelogin packet and check that encryption is not enabled
|
||||
#if mssql_prelogin() != ENCRYPT_NOT_SUP
|
||||
# raise ::Rex::ConnectionError, "Encryption is not supported"
|
||||
#end
|
||||
|
||||
mssql_prelogin()
|
||||
|
||||
if windows_authentication
|
||||
|
@ -153,7 +147,14 @@ module Metasploit
|
|||
# has a strange behavior that differs from the specifications
|
||||
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
|
||||
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||
resp = mssql_send_recv(pkt,15, false)
|
||||
|
||||
if tdsencryption == true
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl()
|
||||
resp = proxy.send_recv(pkt)
|
||||
else
|
||||
resp = mssql_send_recv(pkt)
|
||||
end
|
||||
|
||||
# Get default data
|
||||
begin
|
||||
|
@ -202,13 +203,13 @@ module Metasploit
|
|||
|
||||
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
|
||||
|
||||
#resp = mssql_send_recv(pkt)
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl()
|
||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||
proxy.cleanup
|
||||
proxy = nil
|
||||
|
||||
if self.tdsencryption == true
|
||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||
proxy.cleanup
|
||||
proxy = nil
|
||||
else
|
||||
resp = mssql_send_recv(pkt)
|
||||
end
|
||||
|
||||
#SQL Server Authentification
|
||||
else
|
||||
|
@ -290,12 +291,16 @@ module Metasploit
|
|||
# Packet header and total length including header
|
||||
pkt = "\x10\x01" + [pkt.length + 8].pack('n') + [0].pack('n') + [1].pack('C') + "\x00" + pkt
|
||||
|
||||
#resp = mssql_send_recv(pkt)
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl()
|
||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||
proxy.cleanup
|
||||
proxy = nil
|
||||
if self.tdsencryption == true
|
||||
proxy = TDSSSLProxy.new(sock)
|
||||
proxy.setup_ssl()
|
||||
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||
proxy.cleanup
|
||||
proxy = nil
|
||||
else
|
||||
resp = mssql_send_recv(pkt)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
info = {:errors => []}
|
||||
|
@ -598,8 +603,14 @@ module Metasploit
|
|||
]
|
||||
|
||||
version = [0x55010008,0x0000].pack("Vv")
|
||||
encryption = ENCRYPT_NOT_SUP # off
|
||||
encryption = ENCRYPT_ON
|
||||
|
||||
# if manually set, we will honour
|
||||
if tdsencryption == true
|
||||
encryption = ENCRYPT_ON
|
||||
else
|
||||
encryption = ENCRYPT_NOT_SUP
|
||||
end
|
||||
|
||||
instoptdata = "MSSQLServer\0"
|
||||
|
||||
threadid = "\0\0" + Rex::Text.rand_text(2)
|
||||
|
@ -652,12 +663,57 @@ module Metasploit
|
|||
if idx > 0
|
||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||
else
|
||||
#force to ENCRYPT_NOT_SUP and hope for the best
|
||||
raise RunTimeError, "Unable to parse encryption req. "\
|
||||
"from server during prelogin()"
|
||||
encryption_mode = ENCRYPT_NOT_SUP
|
||||
end
|
||||
|
||||
if encryption_mode != ENCRYPT_NOT_SUP and enc_error
|
||||
raise RuntimeError,"Encryption is not supported"
|
||||
##########################################################
|
||||
# Our initial prelogin pkt above said we didnt support
|
||||
# encryption (it's quicker and the default).
|
||||
#
|
||||
# Per the matrix on the following link, SQL Server will
|
||||
# terminate the connection if it does require TLS,
|
||||
# otherwise it will accept an unencrypted session. As
|
||||
# part of this initial response packet, it also returns
|
||||
# ENCRYPT_REQ.
|
||||
#
|
||||
# https://msdn.microsoft.com\
|
||||
# /en-us/library/ee320519(v=sql.105).aspx
|
||||
#
|
||||
##########################################################
|
||||
|
||||
if encryption_mode == ENCRYPT_REQ
|
||||
# restart prelogin process except that we tell SQL Server
|
||||
# than we are now able to encrypt
|
||||
disconnect if self.sock
|
||||
connect
|
||||
|
||||
# offset 35 is the flag - turn it on
|
||||
pkt[35] = [ENCRYPT_ON].pack('C')
|
||||
self.tdsencryption = true
|
||||
framework_module.print_status("TLS encryption has " \
|
||||
"been enabled based on server response.")
|
||||
|
||||
resp = mssql_send_recv(pkt)
|
||||
|
||||
idx = 0
|
||||
|
||||
while resp and resp[0,1] != "\xff" and resp.length > 5
|
||||
token = resp.slice!(0,5)
|
||||
token = token.unpack("Cnn")
|
||||
idx -= 5
|
||||
if token[0] == 0x01
|
||||
idx += token[1]
|
||||
break
|
||||
end
|
||||
end
|
||||
if idx > 0
|
||||
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||
else
|
||||
raise RuntimeError, "Unable to parse encryption "\
|
||||
"req during pre-login"
|
||||
end
|
||||
end
|
||||
encryption_mode
|
||||
end
|
||||
|
|
|
@ -1,25 +1,46 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'openssl'
|
||||
require 'socket'
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/mssql_commands'
|
||||
|
||||
#
|
||||
# TDSSSLProxy:
|
||||
#
|
||||
# SQL Server uses the TDS protocol to transmit data between clients and
|
||||
# servers. Of course this sits on top of TCP.
|
||||
#
|
||||
# By default, the TDS payload is not encrypted. However, if "force
|
||||
# encryption" is set under the SQL Server protocol properties, it will
|
||||
# use SSL/TLS to encrypt the TDS data. Oddly, the entire TCP stream is
|
||||
# not encrypted (as is say for HTTPS), but instead a TDS header is
|
||||
# put on the front of the TLS packet. As a result, the full TLS/SSL
|
||||
# setup is done within a series of TDS payloads.
|
||||
#
|
||||
# This "proxy" basically creates a fake SSL endpoint (s2) from which it
|
||||
# can add/remove the TDS header as required. This is implemented as a
|
||||
# socket pair (think, a bidirectional pipe), where the other end is s1:
|
||||
#
|
||||
# sslsock <-> s1 <-> s2 <-> tdssock <-> target SQL Server.
|
||||
#
|
||||
# (tdssock is the reference to the "sock" from the scanner module)
|
||||
#
|
||||
# TO DO:
|
||||
#
|
||||
# This enables brute force of a SQL Server which requires encryption.
|
||||
# However, future updates will permit any read/write using
|
||||
# mssql_send_recv() to use crypto if required and transparently to
|
||||
# other MSF developers.
|
||||
#
|
||||
# Cheers, JH
|
||||
|
||||
class TDSSSLProxy
|
||||
|
||||
@s1 = 0 # write ssl to this sock
|
||||
@s2 = 0 # demux the unencrypted stream on this sock
|
||||
@tdssock = 0
|
||||
@sslsock = 0
|
||||
@t1 = 0
|
||||
|
||||
TYPE_TDS7_LOGIN = 16
|
||||
TYPE_PRE_LOGIN_MESSAGE = 18
|
||||
STATUS_END_OF_MESSAGE = 0x01
|
||||
|
||||
def initialize(sock)
|
||||
@tdssock = sock
|
||||
@s1,@s2 = Socket.pair(:UNIX,:STREAM,0)
|
||||
@s1,@s2 = Rex::Socket.tcp_socket_pair()
|
||||
end
|
||||
|
||||
def cleanup()
|
||||
|
@ -27,7 +48,7 @@ class TDSSSLProxy
|
|||
end
|
||||
|
||||
def setup_ssl
|
||||
@t1 = Thread.start { ssl_setup_thread(@s2) }
|
||||
@t1 = Thread.start { ssl_setup_thread() }
|
||||
ssl_context = OpenSSL::SSL::SSLContext.new(:TLSv1)
|
||||
@ssl_socket = OpenSSL::SSL::SSLSocket.new(@s1, ssl_context)
|
||||
@ssl_socket.connect
|
||||
|
@ -39,7 +60,7 @@ class TDSSSLProxy
|
|||
resp = ""
|
||||
|
||||
while(not done)
|
||||
head = @ssl_socket.sysread(8)
|
||||
head = @ssl_socket.read(8)
|
||||
if !(head and head.length == 8)
|
||||
return false
|
||||
end
|
||||
|
@ -53,7 +74,7 @@ class TDSSSLProxy
|
|||
rlen = head[2,2].unpack('n')[0] - 8
|
||||
|
||||
while(rlen > 0)
|
||||
buff = @ssl_socket.sysread(rlen)
|
||||
buff = @ssl_socket.read(rlen)
|
||||
return if not buff
|
||||
resp << buff
|
||||
rlen -= buff.length
|
||||
|
@ -63,25 +84,25 @@ class TDSSSLProxy
|
|||
resp
|
||||
end
|
||||
|
||||
def ssl_setup_thread(s_two)
|
||||
def ssl_setup_thread()
|
||||
|
||||
while true do
|
||||
res = select([@tdssock,s_two],nil,nil)
|
||||
res = select([@tdssock,@s2],nil,nil)
|
||||
for r in res[0]
|
||||
|
||||
# response from SQL Server for client
|
||||
if r == @tdssock
|
||||
resp = @tdssock.recv(4096)
|
||||
if @ssl_socket.state[0,5] == "SSLOK"
|
||||
s_two.send(resp,0)
|
||||
@s2.send(resp,0)
|
||||
else
|
||||
s_two.send(resp[8..-1],0)
|
||||
@s2.send(resp[8..-1],0)
|
||||
end
|
||||
end
|
||||
|
||||
# request from client for SQL Server
|
||||
if r == s_two
|
||||
resp = s_two.recv(4096)
|
||||
if r == @s2
|
||||
resp = @s2.recv(4096)
|
||||
# SSL negotiation completed - just send it on
|
||||
if @ssl_socket.state[0,5] == "SSLOK"
|
||||
@tdssock.send(resp,0)
|
||||
|
@ -89,7 +110,6 @@ class TDSSSLProxy
|
|||
else
|
||||
tds_pkt_len = 8 + resp.length
|
||||
pkt_hdr = ''
|
||||
pkt = ''
|
||||
pkt_hdr << [TYPE_PRE_LOGIN_MESSAGE,STATUS_END_OF_MESSAGE,tds_pkt_len,0x0000,0x00,0x00].pack('CCnnCC')
|
||||
pkt = pkt_hdr << resp
|
||||
@tdssock.send(pkt,0)
|
||||
|
|
|
@ -69,7 +69,7 @@ module Exploit::Remote::MSSQL
|
|||
Opt::RPORT(1433),
|
||||
OptString.new('USERNAME', [ false, 'The username to authenticate as', 'sa']),
|
||||
OptString.new('PASSWORD', [ false, 'The password for the specified username', '']),
|
||||
|
||||
OptBool.new('TDSENCRYPTION', [ true, 'Use TLS/SSL for TDS data "Force Encryption"', false]),
|
||||
OptBool.new('USE_WINDOWS_AUTHENT', [ true, 'Use windows authentification (requires DOMAIN option set)', false]),
|
||||
], Msf::Exploit::Remote::MSSQL)
|
||||
register_advanced_options(
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'msf/core'
|
||||
require 'metasploit/framework/credential_collection'
|
||||
require 'metasploit/framework/login_scanner/mssql'
|
||||
|
@ -32,6 +31,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def run_host(ip)
|
||||
print_status("#{rhost}:#{rport} - MSSQL - Starting authentication scanner.")
|
||||
|
||||
if datastore['TDSENCRYPTION']
|
||||
print_status("Manually enabled TLS/SSL to encrypt TDS payloads.")
|
||||
end
|
||||
|
||||
cred_collection = Metasploit::Framework::CredentialCollection.new(
|
||||
blank_passwords: datastore['BLANK_PASSWORDS'],
|
||||
pass_file: datastore['PASS_FILE'],
|
||||
|
@ -56,6 +59,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
max_send_size: datastore['TCP::max_send_size'],
|
||||
send_delay: datastore['TCP::send_delay'],
|
||||
windows_authentication: datastore['USE_WINDOWS_AUTHENT'],
|
||||
tdsencryption: datastore['TDSENCRYPTION'],
|
||||
framework: framework,
|
||||
framework_module: self,
|
||||
ssl: datastore['SSL'],
|
||||
|
|
Loading…
Reference in New Issue