Skip to content

Commit

Permalink
Land #18907, add mssql_version module
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed May 3, 2024
2 parents 69cbddd + c382066 commit 4c84f88
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 110 deletions.
11 changes: 10 additions & 1 deletion lib/msf/core/exploit/remote/mssql.rb
Expand Up @@ -19,6 +19,11 @@ module Exploit::Remote::MSSQL

attr_accessor :mssql_client

ENCRYPT_OFF = 0x00 #Encryption is available but off.
ENCRYPT_ON = 0x01 #Encryption is available and on.
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
ENCRYPT_REQ = 0x03 #Encryption is required.

#
# Creates an instance of a MSSQL exploit module.
#
Expand Down Expand Up @@ -48,11 +53,15 @@ def initialize(info = {})
register_autofilter_services(%W{ ms-sql-s ms-sql2000 sybase })
end

def set_session(client)
def set_mssql_session(client)
print_status("Using existing session #{session.sid}")
@mssql_client = client
end

def create_mssql_client
@mssql_client ||= Rex::Proto::MSSQL::Client.new(self, framework, datastore['RHOST'], datastore['RPORT'])
end

#
# This method sends a UDP query packet to the server and
# parses out the reply packet into a hash
Expand Down
106 changes: 14 additions & 92 deletions lib/rex/proto/mssql/client.rb
Expand Up @@ -131,10 +131,7 @@ def detect_platform_and_arch
#

def mssql_login(user='sa', pass='', db='', domain_name='')
disconnect if self.sock
connect
mssql_prelogin

prelogin_data = mssql_prelogin
if auth == Msf::Exploit::Remote::AuthOption::KERBEROS
idx = 0
pkt = ''
Expand Down Expand Up @@ -238,6 +235,7 @@ def mssql_login(user='sa', pass='', db='', domain_name='')
info = {:errors => []}
info = mssql_parse_reply(resp, info)
self.initial_connection_info = info
self.initial_connection_info[:prelogin_data] = prelogin_data

return false if not info
return info[:login_ack] ? true : false
Expand Down Expand Up @@ -470,6 +468,7 @@ def mssql_login(user='sa', pass='', db='', domain_name='')
info = {:errors => []}
info = mssql_parse_reply(resp, info)
self.initial_connection_info = info
self.initial_connection_info[:prelogin_data] = prelogin_data

return false if not info
info[:login_ack] ? true : false
Expand All @@ -479,85 +478,20 @@ def mssql_login(user='sa', pass='', db='', domain_name='')
#this method send a prelogin packet and check if encryption is off
#
def mssql_prelogin(enc_error=false)
pkt = ""
pkt_hdr = ""
pkt_data_token = ""
pkt_data = ""


pkt_hdr = [
TYPE_PRE_LOGIN_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status
0x0000, #length
0x0000, # SPID
0x00, # PacketID
0x00 #Window
]

version = [0x55010008, 0x0000].pack("Vv")

# 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)

idx = 21 # size of pkt_data_token
pkt_data_token << [
0x00, # Token 0 type Version
idx , # VersionOffset
version.length, # VersionLength

0x01, # Token 1 type Encryption
idx = idx + version.length, # EncryptionOffset
0x01, # EncryptionLength

0x02, # Token 2 type InstOpt
idx = idx + 1, # InstOptOffset
instoptdata.length, # InstOptLength

0x03, # Token 3 type Threadid
idx + instoptdata.length, # ThreadIdOffset
0x04, # ThreadIdLength

0xFF
].pack("CnnCnnCnnCnnC")

pkt_data << pkt_data_token
pkt_data << version
pkt_data << encryption
pkt_data << instoptdata
pkt_data << threadid

pkt_hdr[2] = pkt_data.length + 8
disconnect if self.sock
connect

pkt = pkt_hdr.pack("CCnnCC") + pkt_data
pkt = mssql_prelogin_packet

resp = mssql_send_recv(pkt)

idx = 0
data = parse_prelogin_response(resp)

while resp && resp[0, 1] != "\xff" && 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
unless data[:encryption]
framework_module.print_error("Unable to parse encryption req " \
"during pre-login, this may not be a MSSQL server")
encryption_mode = ENCRYPT_NOT_SUP
data[:encryption] = ENCRYPT_NOT_SUP
end

##########################################################
Expand All @@ -575,7 +509,7 @@ def mssql_prelogin(enc_error=false)
#
##########################################################

if encryption_mode == ENCRYPT_REQ
if data[:encryption] == ENCRYPT_REQ
# restart prelogin process except that we tell SQL Server
# than we are now able to encrypt
disconnect if self.sock
Expand All @@ -588,27 +522,15 @@ def mssql_prelogin(enc_error=false)
"been enabled based on server response.")

resp = mssql_send_recv(pkt)
data = parse_prelogin_response(resp)

idx = 0

while resp && resp[0, 1] != "\xff" && 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
unless data[:encryption]
framework_module.print_error("Unable to parse encryption req " \
"during pre-login, this may not be a MSSQL server")
encryption_mode = ENCRYPT_NOT_SUP
data[:encryption] = ENCRYPT_NOT_SUP
end
end
encryption_mode
data
end

def mssql_ssl_send_recv(req, tdsproxy, timeout=15, check_status=true)
Expand Down
82 changes: 82 additions & 0 deletions lib/rex/proto/mssql/client_mixin.rb
Expand Up @@ -86,6 +86,88 @@ def mssql_print_reply(info)
end
end

def mssql_prelogin_packet
pkt = ""
pkt_hdr = ""
pkt_data_token = ""
pkt_data = ""


pkt_hdr = [
TYPE_PRE_LOGIN_MESSAGE, #type
STATUS_END_OF_MESSAGE, #status
0x0000, #length
0x0000, # SPID
0x00, # PacketID
0x00 #Window
]

version = [0x55010008, 0x0000].pack("Vv")

# 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)

idx = 21 # size of pkt_data_token
pkt_data_token << [
0x00, # Token 0 type Version
idx , # VersionOffset
version.length, # VersionLength

0x01, # Token 1 type Encryption
idx = idx + version.length, # EncryptionOffset
0x01, # EncryptionLength

0x02, # Token 2 type InstOpt
idx = idx + 1, # InstOptOffset
instoptdata.length, # InstOptLength

0x03, # Token 3 type Threadid
idx + instoptdata.length, # ThreadIdOffset
0x04, # ThreadIdLength

0xFF
].pack('CnnCnnCnnCnnC')

pkt_data << pkt_data_token
pkt_data << version
pkt_data << encryption
pkt_data << instoptdata
pkt_data << threadid

pkt_hdr[2] = pkt_data.length + 8

pkt = pkt_hdr.pack('CCnnCC') + pkt_data
pkt
end

def parse_prelogin_response(resp)
data = {}
if resp.length > 5 # minimum size for response specification
version_index = resp.slice(1, 2).unpack('n')[0]

major = resp.slice(version_index, 1).unpack('C')[0]
minor = resp.slice(version_index+1, 1).unpack('C')[0]
build = resp.slice(version_index+2, 2).unpack('n')[0]

enc_index = resp.slice(6, 2).unpack('n')[0]
data[:encryption] = resp.slice(enc_index, 1).unpack('C')[0]
end

if major && minor && build
data[:version] = "#{major}.#{minor}.#{build}"
end

return data
end

def mssql_send_recv(req, timeout=15, check_status = true)
sock.put(req)

Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_enum.rb
Expand Up @@ -25,7 +25,7 @@ module to work, valid administrative user credentials must be
def run
print_status("Running MS SQL Server Enumeration...")
if session
set_session(session.client)
set_mssql_session(session.client)
else
unless mssql_login_datastore
print_error("Login was unsuccessful. Check your credentials.")
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_escalate_dbowner.rb
Expand Up @@ -25,7 +25,7 @@ def initialize(info = {})
def run
# Check connection and issue initial query
if session
set_session(session.client)
set_mssql_session(session.client)
else
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
if mssql_login_datastore
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_escalate_execute_as.rb
Expand Up @@ -24,7 +24,7 @@ def initialize(info = {})

def run
if session
set_session(session.client)
set_mssql_session(session.client)
else
print_status("Attempting to connect to the database server at #{rhost}:#{rport} as #{datastore['USERNAME']}...")
if mssql_login_datastore
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_exec.rb
Expand Up @@ -39,7 +39,7 @@ def initialize(info = {})

def run
if session
set_session(session.client)
set_mssql_session(session.client)
else
unless mssql_login_datastore
print_error("Error with mssql_login call")
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_findandsampledata.rb
Expand Up @@ -342,7 +342,7 @@ def sql_statement()
# CREATE DATABASE CONNECTION AND SUBMIT QUERY WITH ERROR HANDLING
begin
if session
set_session(session.client)
set_mssql_session(session.client)
else
print_line(" ")
print_status("Attempting to connect to the SQL Server at #{rhost}:#{rport}...")
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_idf.rb
Expand Up @@ -88,7 +88,7 @@ def run

begin
if session
set_session(session.client)
set_mssql_session(session.client)
else
unless mssql_login_datastore
print_error('Login failed')
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_sql.rb
Expand Up @@ -40,7 +40,7 @@ def cmd_select(*args)

def run
if session
set_session(session.client)
set_mssql_session(session.client)
else
unless mssql_login_datastore
print_error("Error with mssql_login call")
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/admin/mssql/mssql_sql_file.rb
Expand Up @@ -36,7 +36,7 @@ def run

begin
if session
set_session(session.client)
set_mssql_session(session.client)
else
unless mssql_login_datastore
print_error("#{datastore['RHOST']}:#{datastore['RPORT']} - Invalid SQL Server credentials")
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/scanner/mssql/mssql_hashdump.rb
Expand Up @@ -25,7 +25,7 @@ def initialize

def run_host(ip)
if session
set_session(session.client)
set_mssql_session(session.client)
elsif !mssql_login(datastore['USERNAME'], datastore['PASSWORD'])
info = self.mssql_client.initial_connection_info
if info[:errors] && !info[:errors].empty?
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/scanner/mssql/mssql_ping.rb
Expand Up @@ -11,7 +11,7 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'MSSQL Ping Utility',
'Description' => 'This module simply queries the MSSQL instance for information.',
'Description' => 'This module simply queries the MSSQL Browser service for server information.',
'Author' => 'MC',
'License' => MSF_LICENSE
)
Expand Down
2 changes: 1 addition & 1 deletion modules/auxiliary/scanner/mssql/mssql_schemadump.rb
Expand Up @@ -31,7 +31,7 @@ def initialize

def run_host(ip)
if session
set_session(session.client)
set_mssql_session(session.client)
else
unless mssql_login_datastore
print_error("#{datastore['RHOST']}:#{datastore['RPORT']} - Invalid SQL Server credentials")
Expand Down

0 comments on commit 4c84f88

Please sign in to comment.