Skip to content

Commit 07f7e5e

Browse files
committed
Convert non-loginscanner MSSQL to rubyntlm
1 parent 4b3f6c5 commit 07f7e5e

File tree

3 files changed

+38
-71
lines changed

3 files changed

+38
-71
lines changed

lib/metasploit/framework/mssql/client.rb

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def mssql_login(user='sa', pass='', db='', domain_name='')
8989
domain: domain_name,
9090
)
9191
type1 = ntlm_client.init_context
92-
# At least SQL 2012 does not support KEY_EXCHANGE
92+
# SQL 2012, at least, does not support KEY_EXCHANGE
9393
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
9494
ntlmsspblob = type1.serialize
9595

@@ -142,13 +142,12 @@ def mssql_login(user='sa', pass='', db='', domain_name='')
142142
# has a strange behavior that differs from the specifications
143143
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
144144
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
145-
146145
if tdsencryption == true
147146
proxy = TDSSSLProxy.new(sock)
148147
proxy.setup_ssl
149-
resp = proxy.send_recv(pkt)
148+
resp = proxy.send_recv(pkt, 15, false)
150149
else
151-
resp = mssql_send_recv(pkt)
150+
resp = mssql_send_recv(pkt, 15, false)
152151
end
153152

154153
# Strip the TDS header
@@ -160,16 +159,16 @@ def mssql_login(user='sa', pass='', db='', domain_name='')
160159
idx = 0
161160
pkt = ''
162161
pkt_hdr = ''
163-
pkt_hdr = [
164-
TYPE_SSPI_MESSAGE, #type
165-
STATUS_END_OF_MESSAGE, #status
166-
0x0000, #length
167-
0x0000, # SPID
168-
0x01, # PacketID
169-
0x00 #Window
162+
pkt_hdr = [
163+
TYPE_SSPI_MESSAGE, #type
164+
STATUS_END_OF_MESSAGE, #status
165+
0x0000, #length
166+
0x0000, # SPID
167+
0x01, # PacketID
168+
0x00 #Window
170169
]
171170

172-
pkt_hdr[2] = type3_blob.length + 8
171+
pkt_hdr[2] = type3_blob.length + 8
173172

174173
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
175174

lib/msf/core/exploit/mssql.rb

Lines changed: 24 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# -*- coding: binary -*-
22
require 'msf/core'
33
require 'msf/core/exploit/mssql_commands'
4-
require 'rex/proto/ntlm/crypt'
5-
require 'rex/proto/ntlm/constants'
6-
require 'rex/proto/ntlm/utils'
7-
require 'rex/proto/ntlm/exceptions'
84

95

106
module Msf
@@ -21,14 +17,6 @@ module Exploit::Remote::MSSQL
2117
include Exploit::Remote::Tcp
2218
include Exploit::Remote::NTLM::Client
2319

24-
#
25-
# Constants
26-
#
27-
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
28-
NTLM_CONST = Rex::Proto::NTLM::Constants
29-
NTLM_UTILS = Rex::Proto::NTLM::Utils
30-
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
31-
3220
# Encryption
3321
ENCRYPT_OFF = 0x00 #Encryption is available but off.
3422
ENCRYPT_ON = 0x01 #Encryption is available and on.
@@ -394,10 +382,7 @@ def mssql_login(user='sa', pass='', db='')
394382
return false
395383
end
396384

397-
$stderr.puts 'login'
398-
399385
if datastore['USE_WINDOWS_AUTHENT']
400-
$stderr.puts 'windows auth'
401386

402387
idx = 0
403388
pkt = ''
@@ -436,12 +421,15 @@ def mssql_login(user='sa', pass='', db='')
436421
domain_name = datastore['DOMAIN']
437422

438423
ntlm_client = ::Net::NTLM::Client.new(
439-
opts['username'],
440-
opts['password'],
424+
user,
425+
pass,
441426
workstation: workstation_name,
442427
domain: domain_name,
443428
)
444-
ntlmsspblob = ntlm_client.init_context
429+
type1 = ntlm_client.init_context
430+
# SQL 2012, at least, does not support KEY_EXCHANGE
431+
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
432+
ntlmsspblob = type1.serialize
445433

446434
idx = pkt.size + 50 # lengths below
447435

@@ -492,56 +480,36 @@ def mssql_login(user='sa', pass='', db='')
492480
# has a strange behavior that differs from the specifications
493481
# upon receiving the ntlm_negociate request it send an ntlm_challenge but the status flag of the tds packet header
494482
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
495-
resp = mssql_send_recv(pkt,15, false)
483+
resp = mssql_send_recv(pkt, 15, false)
496484

497-
# Get default data
498-
begin
499-
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
500-
# a domain.length < 3 will hit this
501-
rescue NTLM_XCEPT::NTLMMissingChallenge
485+
unless resp.include?("NTLMSSP")
502486
info = {:errors => []}
503487
mssql_parse_reply(resp, info)
504488
mssql_print_reply(info)
505489
return false
506490
end
507-
challenge_key = blob_data[:challenge_key]
508-
_server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
509-
#netbios name
510-
default_name = blob_data[:default_name] || ''
511-
#netbios domain
512-
default_domain = blob_data[:default_domain] || ''
513-
#dns name
514-
dns_host_name = blob_data[:dns_host_name] || ''
515-
#dns domain
516-
dns_domain_name = blob_data[:dns_domain_name] || ''
517-
#Client time
518-
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
519-
520-
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
521-
522-
resp_lm, resp_ntlm, _client_challenge, _ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
523-
domain_name, default_name, default_domain,
524-
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
525-
spnopt, ntlm_options)
526-
527-
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
491+
492+
# Get default data
493+
resp = resp[3..-1]
494+
type3 = ntlm_client.init_context([resp].pack('m'))
495+
type3_blob = type3.serialize
528496

529497
# Create an SSPIMessage
530498
idx = 0
531499
pkt = ''
532500
pkt_hdr = ''
533-
pkt_hdr = [
534-
TYPE_SSPI_MESSAGE, #type
535-
STATUS_END_OF_MESSAGE, #status
536-
0x0000, #length
537-
0x0000, # SPID
538-
0x01, # PacketID
539-
0x00 #Window
540-
]
501+
pkt_hdr = [
502+
TYPE_SSPI_MESSAGE, #type
503+
STATUS_END_OF_MESSAGE, #status
504+
0x0000, #length
505+
0x0000, # SPID
506+
0x01, # PacketID
507+
0x00 #Window
508+
]
541509

542-
pkt_hdr[2] = ntlmssp.length + 8
510+
pkt_hdr[2] = type3_blob.length + 8
543511

544-
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
512+
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
545513

546514
resp = mssql_send_recv(pkt)
547515

@@ -984,7 +952,7 @@ def mssql_parse_info(data, info)
984952
#
985953
def mssql_parse_login_ack(data, info)
986954
len = data.slice!(0,2).unpack('v')[0]
987-
buff = data.slice!(0,len)
955+
_buff = data.slice!(0,len)
988956
info[:login_ack] = true
989957
end
990958
end

modules/auxiliary/scanner/mssql/mssql_hashdump.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def initialize
3131
def run_host(ip)
3232

3333
if !mssql_login_datastore
34-
print_error("#{rhost}:#{rport} - Invalid SQL Server credentials")
34+
print_error("Invalid SQL Server credentials")
3535
return
3636
end
3737

@@ -150,7 +150,7 @@ def report_hashes(mssql_hashes, version_year)
150150
login = create_credential_login(login_data)
151151

152152
tbl << [row[0], row[1]]
153-
print_good("#{rhost}:#{rport} - Saving #{hashtype} = #{row[0]}:#{row[1]}")
153+
print_good("Saving #{hashtype} = #{row[0]}:#{row[1]}")
154154
end
155155
end
156156

@@ -160,7 +160,7 @@ def mssql_hashdump(version_year)
160160
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
161161

162162
if is_sysadmin == 0
163-
print_error("#{rhost}:#{rport} - The provided credentials do not have privileges to read the password hashes")
163+
print_error("The provided credentials do not have privileges to read the password hashes")
164164
return nil
165165
end
166166

0 commit comments

Comments
 (0)