Revert "Land #7009, egypt's rubyntlm cleanup"
This reverts commitbug/bundler_fixd90f0779f8
, reversing changes made toe3e360cc83
.
parent
1e0dcb9268
commit
1164c025a2
|
@ -36,7 +36,6 @@ PATH
|
||||||
rex-text
|
rex-text
|
||||||
rex-zip
|
rex-zip
|
||||||
robots
|
robots
|
||||||
rubyntlm
|
|
||||||
rubyzip
|
rubyzip
|
||||||
sqlite3
|
sqlite3
|
||||||
sshkey
|
sshkey
|
||||||
|
|
|
@ -9,6 +9,11 @@ module Metasploit
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
include Metasploit::Framework::Tcp::Client
|
include Metasploit::Framework::Tcp::Client
|
||||||
|
|
||||||
|
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
||||||
|
NTLM_CONST = Rex::Proto::NTLM::Constants
|
||||||
|
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
||||||
|
|
||||||
# Encryption
|
# Encryption
|
||||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||||
|
@ -16,23 +21,23 @@ module Metasploit
|
||||||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||||
|
|
||||||
# Packet Type
|
# Packet Type
|
||||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||||
TYPE_RPC = 3 # (Client) RPC
|
TYPE_RPC = 3 # (Client) RPC
|
||||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||||
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
||||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
STATUS_NORMAL = 0x00
|
STATUS_NORMAL = 0x00
|
||||||
STATUS_END_OF_MESSAGE = 0x01
|
STATUS_END_OF_MESSAGE = 0x01
|
||||||
STATUS_IGNORE_EVENT = 0x02
|
STATUS_IGNORE_EVENT = 0x02
|
||||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -50,14 +55,14 @@ module Metasploit
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_TDS7_LOGIN, #type
|
TYPE_TDS7_LOGIN, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID (unused upon specification
|
0x01, # PacketID (unused upon specification
|
||||||
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt << [
|
pkt << [
|
||||||
|
@ -80,18 +85,18 @@ module Metasploit
|
||||||
sname = Rex::Text.to_unicode( rhost )
|
sname = Rex::Text.to_unicode( rhost )
|
||||||
dname = Rex::Text.to_unicode( db )
|
dname = Rex::Text.to_unicode( db )
|
||||||
|
|
||||||
|
ntlm_options = {
|
||||||
|
:signing => false,
|
||||||
|
:usentlm2_session => use_ntlm2_session,
|
||||||
|
:use_ntlmv2 => use_ntlmv2,
|
||||||
|
:send_lm => send_lm,
|
||||||
|
:send_ntlm => send_ntlm
|
||||||
|
}
|
||||||
|
|
||||||
|
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||||
|
|
||||||
ntlm_client = ::Net::NTLM::Client.new(
|
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
|
||||||
user,
|
|
||||||
pass,
|
|
||||||
workstation: workstation_name,
|
|
||||||
domain: domain_name,
|
|
||||||
)
|
|
||||||
type1 = ntlm_client.init_context
|
|
||||||
# SQL 2012, at least, does not support KEY_EXCHANGE
|
|
||||||
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
|
|
||||||
ntlmsspblob = type1.serialize
|
|
||||||
|
|
||||||
idx = pkt.size + 50 # lengths below
|
idx = pkt.size + 50 # lengths below
|
||||||
|
|
||||||
|
@ -132,9 +137,9 @@ module Metasploit
|
||||||
pkt << ntlmsspblob
|
pkt << ntlmsspblob
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0, 4] = [pkt.length].pack('V')
|
pkt[0,4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
pkt_hdr[2] = pkt.length + 8
|
pkt_hdr[2] = pkt.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
||||||
|
|
||||||
|
@ -142,38 +147,64 @@ module Metasploit
|
||||||
# has a strange behavior that differs from the specifications
|
# 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
|
# 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
|
# is set to STATUS_NORMAL and not STATUS_END_OF_MESSAGE, then internally it waits for the ntlm_authentification
|
||||||
|
|
||||||
if tdsencryption == true
|
if tdsencryption == true
|
||||||
proxy = TDSSSLProxy.new(sock)
|
proxy = TDSSSLProxy.new(sock)
|
||||||
proxy.setup_ssl
|
proxy.setup_ssl
|
||||||
resp = proxy.send_recv(pkt, 15, false)
|
resp = proxy.send_recv(pkt)
|
||||||
else
|
else
|
||||||
resp = mssql_send_recv(pkt, 15, false)
|
resp = mssql_send_recv(pkt)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Strip the TDS header
|
# Get default data
|
||||||
resp = resp[3..-1]
|
begin
|
||||||
type3 = ntlm_client.init_context([resp].pack('m'))
|
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
||||||
type3_blob = type3.serialize
|
# a domain.length < 3 will hit this
|
||||||
|
rescue NTLM_XCEPT::NTLMMissingChallenge
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
challenge_key = blob_data[:challenge_key]
|
||||||
|
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||||
|
#netbios name
|
||||||
|
default_name = blob_data[:default_name] || ''
|
||||||
|
#netbios domain
|
||||||
|
default_domain = blob_data[:default_domain] || ''
|
||||||
|
#dns name
|
||||||
|
dns_host_name = blob_data[:dns_host_name] || ''
|
||||||
|
#dns domain
|
||||||
|
dns_domain_name = blob_data[:dns_domain_name] || ''
|
||||||
|
#Client time
|
||||||
|
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
||||||
|
|
||||||
|
spnopt = {:use_spn => send_spn, :name => rhost}
|
||||||
|
|
||||||
|
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
|
||||||
|
domain_name, default_name, default_domain,
|
||||||
|
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
|
||||||
|
spnopt, ntlm_options)
|
||||||
|
|
||||||
|
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
|
||||||
|
|
||||||
# Create an SSPIMessage
|
# Create an SSPIMessage
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_SSPI_MESSAGE, #type
|
TYPE_SSPI_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID
|
0x01, # PacketID
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt_hdr[2] = type3_blob.length + 8
|
pkt_hdr[2] = ntlmssp.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
|
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
|
||||||
|
|
||||||
if self.tdsencryption == true
|
if self.tdsencryption == true
|
||||||
resp = mssql_ssl_send_recv(pkt, proxy)
|
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||||
proxy.cleanup
|
proxy.cleanup
|
||||||
proxy = nil
|
proxy = nil
|
||||||
else
|
else
|
||||||
|
@ -252,7 +283,7 @@ module Metasploit
|
||||||
pkt << dname
|
pkt << dname
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0, 4] = [pkt.length].pack('V')
|
pkt[0,4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
# Embedded packet lengths
|
# Embedded packet lengths
|
||||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||||
|
@ -263,7 +294,7 @@ module Metasploit
|
||||||
if self.tdsencryption == true
|
if self.tdsencryption == true
|
||||||
proxy = TDSSSLProxy.new(sock)
|
proxy = TDSSSLProxy.new(sock)
|
||||||
proxy.setup_ssl
|
proxy.setup_ssl
|
||||||
resp = mssql_ssl_send_recv(pkt, proxy)
|
resp = mssql_ssl_send_recv(pkt,proxy)
|
||||||
proxy.cleanup
|
proxy.cleanup
|
||||||
proxy = nil
|
proxy = nil
|
||||||
else
|
else
|
||||||
|
@ -273,7 +304,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
|
|
||||||
info = {:errors => []}
|
info = {:errors => []}
|
||||||
info = mssql_parse_reply(resp, info)
|
info = mssql_parse_reply(resp,info)
|
||||||
|
|
||||||
disconnect
|
disconnect
|
||||||
|
|
||||||
|
@ -285,17 +316,17 @@ module Metasploit
|
||||||
# Parse an "environment change" TDS token
|
# Parse an "environment change" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_env(data, info)
|
def mssql_parse_env(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
type = buff.slice!(0, 1).unpack('C')[0]
|
type = buff.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
nval = ''
|
nval = ''
|
||||||
nlen = buff.slice!(0, 1).unpack('C')[0] || 0
|
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||||
nval = buff.slice!(0, nlen*2).gsub("\x00", '') if nlen > 0
|
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
||||||
|
|
||||||
oval = ''
|
oval = ''
|
||||||
olen = buff.slice!(0, 1).unpack('C')[0] || 0
|
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||||
oval = buff.slice!(0, olen*2).gsub("\x00", '') if olen > 0
|
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
||||||
|
|
||||||
info[:envs] ||= []
|
info[:envs] ||= []
|
||||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||||
|
@ -306,7 +337,7 @@ module Metasploit
|
||||||
# Parse a "ret" TDS token
|
# Parse a "ret" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_ret(data, info)
|
def mssql_parse_ret(data, info)
|
||||||
ret = data.slice!(0, 4).unpack('N')[0]
|
ret = data.slice!(0,4).unpack('N')[0]
|
||||||
info[:ret] = ret
|
info[:ret] = ret
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -315,7 +346,7 @@ module Metasploit
|
||||||
# Parse a "done" TDS token
|
# Parse a "done" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_done(data, info)
|
def mssql_parse_done(data, info)
|
||||||
status, cmd, rows = data.slice!(0, 8).unpack('vvV')
|
status,cmd,rows = data.slice!(0,8).unpack('vvV')
|
||||||
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -324,11 +355,11 @@ module Metasploit
|
||||||
# Parse an "error" TDS token
|
# Parse an "error" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_error(data, info)
|
def mssql_parse_error(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
|
|
||||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0, elen * 2)
|
emsg = buff.slice!(0,elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
|
@ -339,14 +370,14 @@ module Metasploit
|
||||||
# Parse an "information" TDS token
|
# Parse an "information" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_info(data, info)
|
def mssql_parse_info(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
|
|
||||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0, elen * 2)
|
emsg = buff.slice!(0,elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:infos] ||= []
|
info[:infos]||= []
|
||||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -355,8 +386,8 @@ module Metasploit
|
||||||
# Parse a "login ack" TDS token
|
# Parse a "login ack" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_login_ack(data, info)
|
def mssql_parse_login_ack(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
_buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
info[:login_ack] = true
|
info[:login_ack] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -367,7 +398,7 @@ module Metasploit
|
||||||
info[:errors] = []
|
info[:errors] = []
|
||||||
return if not data
|
return if not data
|
||||||
until data.empty?
|
until data.empty?
|
||||||
token = data.slice!(0, 1).unpack('C')[0]
|
token = data.slice!(0,1).unpack('C')[0]
|
||||||
case token
|
case token
|
||||||
when 0x81
|
when 0x81
|
||||||
mssql_parse_tds_reply(data, info)
|
mssql_parse_tds_reply(data, info)
|
||||||
|
@ -403,14 +434,14 @@ module Metasploit
|
||||||
info[:colnames] ||= []
|
info[:colnames] ||= []
|
||||||
|
|
||||||
# Parse out the columns
|
# Parse out the columns
|
||||||
cols = data.slice!(0, 2).unpack('v')[0]
|
cols = data.slice!(0,2).unpack('v')[0]
|
||||||
0.upto(cols-1) do |col_idx|
|
0.upto(cols-1) do |col_idx|
|
||||||
col = {}
|
col = {}
|
||||||
info[:colinfos][col_idx] = col
|
info[:colinfos][col_idx] = col
|
||||||
|
|
||||||
col[:utype] = data.slice!(0, 2).unpack('v')[0]
|
col[:utype] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:flags] = data.slice!(0, 2).unpack('v')[0]
|
col[:flags] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:type] = data.slice!(0, 1).unpack('C')[0]
|
col[:type] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
case col[:type]
|
case col[:type]
|
||||||
when 48
|
when 48
|
||||||
|
@ -427,8 +458,8 @@ module Metasploit
|
||||||
|
|
||||||
when 34
|
when 34
|
||||||
col[:id] = :image
|
col[:id] = :image
|
||||||
col[:max_size] = data.slice!(0, 4).unpack('V')[0]
|
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
||||||
col[:value_length] = data.slice!(0, 2).unpack('v')[0]
|
col[:value_length] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||||
|
|
||||||
when 36
|
when 36
|
||||||
|
@ -436,31 +467,31 @@ module Metasploit
|
||||||
|
|
||||||
when 38
|
when 38
|
||||||
col[:id] = :int
|
col[:id] = :int
|
||||||
col[:int_size] = data.slice!(0, 1).unpack('C')[0]
|
col[:int_size] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
when 127
|
when 127
|
||||||
col[:id] = :bigint
|
col[:id] = :bigint
|
||||||
|
|
||||||
when 165
|
when 165
|
||||||
col[:id] = :hex
|
col[:id] = :hex
|
||||||
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||||
|
|
||||||
when 173
|
when 173
|
||||||
col[:id] = :hex # binary(2)
|
col[:id] = :hex # binary(2)
|
||||||
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||||
|
|
||||||
when 231, 175, 167, 239
|
when 231,175,167,239
|
||||||
col[:id] = :string
|
col[:id] = :string
|
||||||
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:codepage] = data.slice!(0, 2).unpack('v')[0]
|
col[:codepage] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:cflags] = data.slice!(0, 2).unpack('v')[0]
|
col[:cflags] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:charset_id] = data.slice!(0, 1).unpack('C')[0]
|
col[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
else
|
else
|
||||||
col[:id] = :unknown
|
col[:id] = :unknown
|
||||||
end
|
end
|
||||||
|
|
||||||
col[:msg_len] = data.slice!(0, 1).unpack('C')[0]
|
col[:msg_len] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
if(col[:msg_len] and col[:msg_len] > 0)
|
if(col[:msg_len] and col[:msg_len] > 0)
|
||||||
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
||||||
|
@ -486,28 +517,28 @@ module Metasploit
|
||||||
case col[:id]
|
case col[:id]
|
||||||
when :hex
|
when :hex
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
if(len > 0 and len < 65535)
|
if(len > 0 and len < 65535)
|
||||||
str << data.slice!(0, len)
|
str << data.slice!(0,len)
|
||||||
end
|
end
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :string
|
when :string
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
if(len > 0 and len < 65535)
|
if(len > 0 and len < 65535)
|
||||||
str << data.slice!(0, len)
|
str << data.slice!(0,len)
|
||||||
end
|
end
|
||||||
row << str.gsub("\x00", '')
|
row << str.gsub("\x00", '')
|
||||||
|
|
||||||
when :datetime
|
when :datetime
|
||||||
row << data.slice!(0, 8).unpack("H*")[0]
|
row << data.slice!(0,8).unpack("H*")[0]
|
||||||
|
|
||||||
when :rawint
|
when :rawint
|
||||||
row << data.slice!(0, 4).unpack('V')[0]
|
row << data.slice!(0,4).unpack('V')[0]
|
||||||
|
|
||||||
when :bigint
|
when :bigint
|
||||||
row << data.slice!(0, 8).unpack("H*")[0]
|
row << data.slice!(0,8).unpack("H*")[0]
|
||||||
|
|
||||||
when :smallint
|
when :smallint
|
||||||
row << data.slice!(0, 2).unpack("v")[0]
|
row << data.slice!(0, 2).unpack("v")[0]
|
||||||
|
@ -520,8 +551,8 @@ module Metasploit
|
||||||
|
|
||||||
when :image
|
when :image
|
||||||
str = ''
|
str = ''
|
||||||
len = data.slice!(0, 1).unpack('C')[0]
|
len = data.slice!(0,1).unpack('C')[0]
|
||||||
str = data.slice!(0, len) if (len and len > 0)
|
str = data.slice!(0,len) if (len and len > 0)
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :int
|
when :int
|
||||||
|
@ -529,7 +560,7 @@ module Metasploit
|
||||||
raw = data.slice!(0, len) if (len and len > 0)
|
raw = data.slice!(0, len) if (len and len > 0)
|
||||||
|
|
||||||
case len
|
case len
|
||||||
when 0, 255
|
when 0,255
|
||||||
row << ''
|
row << ''
|
||||||
when 1
|
when 1
|
||||||
row << raw.unpack("C")[0]
|
row << raw.unpack("C")[0]
|
||||||
|
@ -542,7 +573,7 @@ module Metasploit
|
||||||
when 8
|
when 8
|
||||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||||
else
|
else
|
||||||
info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}"
|
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
info[:errors] << "unknown column type: #{col.inspect}"
|
info[:errors] << "unknown column type: #{col.inspect}"
|
||||||
|
@ -564,7 +595,7 @@ module Metasploit
|
||||||
pkt_data = ""
|
pkt_data = ""
|
||||||
|
|
||||||
|
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
|
@ -573,7 +604,7 @@ module Metasploit
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
version = [0x55010008, 0x0000].pack("Vv")
|
version = [0x55010008,0x0000].pack("Vv")
|
||||||
|
|
||||||
# if manually set, we will honour
|
# if manually set, we will honour
|
||||||
if tdsencryption == true
|
if tdsencryption == true
|
||||||
|
@ -584,45 +615,45 @@ module Metasploit
|
||||||
|
|
||||||
instoptdata = "MSSQLServer\0"
|
instoptdata = "MSSQLServer\0"
|
||||||
|
|
||||||
threadid = "\0\0" + Rex::Text.rand_text(2)
|
threadid = "\0\0" + Rex::Text.rand_text(2)
|
||||||
|
|
||||||
idx = 21 # size of pkt_data_token
|
idx = 21 # size of pkt_data_token
|
||||||
pkt_data_token << [
|
pkt_data_token << [
|
||||||
0x00, # Token 0 type Version
|
0x00, # Token 0 type Version
|
||||||
idx , # VersionOffset
|
idx , # VersionOffset
|
||||||
version.length, # VersionLength
|
version.length, # VersionLength
|
||||||
|
|
||||||
0x01, # Token 1 type Encryption
|
0x01, # Token 1 type Encryption
|
||||||
idx = idx + version.length, # EncryptionOffset
|
idx = idx + version.length, # EncryptionOffset
|
||||||
0x01, # EncryptionLength
|
0x01, # EncryptionLength
|
||||||
|
|
||||||
0x02, # Token 2 type InstOpt
|
0x02, # Token 2 type InstOpt
|
||||||
idx = idx + 1, # InstOptOffset
|
idx = idx + 1, # InstOptOffset
|
||||||
instoptdata.length, # InstOptLength
|
instoptdata.length, # InstOptLength
|
||||||
|
|
||||||
0x03, # Token 3 type Threadid
|
0x03, # Token 3 type Threadid
|
||||||
idx + instoptdata.length, # ThreadIdOffset
|
idx + instoptdata.length, # ThreadIdOffset
|
||||||
0x04, # ThreadIdLength
|
0x04, # ThreadIdLength
|
||||||
|
|
||||||
0xFF
|
0xFF
|
||||||
].pack("CnnCnnCnnCnnC")
|
].pack("CnnCnnCnnCnnC")
|
||||||
|
|
||||||
pkt_data << pkt_data_token
|
pkt_data << pkt_data_token
|
||||||
pkt_data << version
|
pkt_data << version
|
||||||
pkt_data << encryption
|
pkt_data << encryption
|
||||||
pkt_data << instoptdata
|
pkt_data << instoptdata
|
||||||
pkt_data << threadid
|
pkt_data << threadid
|
||||||
|
|
||||||
pkt_hdr[2] = pkt_data.length + 8
|
pkt_hdr[2] = pkt_data.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
||||||
|
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
while resp and resp[0,1] != "\xff" and resp.length > 5
|
||||||
token = resp.slice!(0, 5)
|
token = resp.slice!(0,5)
|
||||||
token = token.unpack("Cnn")
|
token = token.unpack("Cnn")
|
||||||
idx -= 5
|
idx -= 5
|
||||||
if token[0] == 0x01
|
if token[0] == 0x01
|
||||||
|
@ -632,7 +663,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if idx > 0
|
if idx > 0
|
||||||
encryption_mode = resp[idx, 1].unpack("C")[0]
|
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||||
else
|
else
|
||||||
raise RunTimeError, "Unable to parse encryption req. "\
|
raise RunTimeError, "Unable to parse encryption req. "\
|
||||||
"from server during prelogin"
|
"from server during prelogin"
|
||||||
|
@ -670,8 +701,8 @@ module Metasploit
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
while resp and resp[0,1] != "\xff" and resp.length > 5
|
||||||
token = resp.slice!(0, 5)
|
token = resp.slice!(0,5)
|
||||||
token = token.unpack("Cnn")
|
token = token.unpack("Cnn")
|
||||||
idx -= 5
|
idx -= 5
|
||||||
if token[0] == 0x01
|
if token[0] == 0x01
|
||||||
|
@ -680,7 +711,7 @@ module Metasploit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if idx > 0
|
if idx > 0
|
||||||
encryption_mode = resp[idx, 1].unpack("C")[0]
|
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||||
else
|
else
|
||||||
raise RuntimeError, "Unable to parse encryption "\
|
raise RuntimeError, "Unable to parse encryption "\
|
||||||
"req during pre-login"
|
"req during pre-login"
|
||||||
|
@ -704,17 +735,17 @@ module Metasploit
|
||||||
|
|
||||||
while(not done)
|
while(not done)
|
||||||
head = sock.get_once(8, timeout)
|
head = sock.get_once(8, timeout)
|
||||||
if !(head && head.length == 8)
|
if !(head and head.length == 8)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is this the last buffer?
|
# Is this the last buffer?
|
||||||
if head[1, 1] == "\x01" || !check_status
|
if(head[1,1] == "\x01" or not check_status )
|
||||||
done = true
|
done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Grab this block's length
|
# Grab this block's length
|
||||||
rlen = head[2, 2].unpack('n')[0] - 8
|
rlen = head[2,2].unpack('n')[0] - 8
|
||||||
|
|
||||||
while(rlen > 0)
|
while(rlen > 0)
|
||||||
buff = sock.get_once(rlen, timeout)
|
buff = sock.get_once(rlen, timeout)
|
||||||
|
@ -727,7 +758,7 @@ module Metasploit
|
||||||
resp
|
resp
|
||||||
end
|
end
|
||||||
|
|
||||||
def mssql_ssl_send_recv(req, tdsproxy, timeout=15, check_status=true)
|
def mssql_ssl_send_recv(req,tdsproxy,timeout=15,check_status=true)
|
||||||
tdsproxy.send_recv(req)
|
tdsproxy.send_recv(req)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'digest'
|
require 'digest'
|
||||||
|
require 'rex/proto/ntlm/crypt'
|
||||||
|
require 'rex/proto/ntlm/constants'
|
||||||
|
require 'rex/proto/ntlm/utils'
|
||||||
|
require 'rex/proto/ntlm/exceptions'
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
###
|
###
|
||||||
|
@ -12,6 +16,15 @@ module Msf
|
||||||
###
|
###
|
||||||
module Exploit::Remote::HttpClient
|
module Exploit::Remote::HttpClient
|
||||||
include Msf::Auxiliary::Report
|
include Msf::Auxiliary::Report
|
||||||
|
include Exploit::Remote::NTLM::Client
|
||||||
|
|
||||||
|
#
|
||||||
|
# Constants
|
||||||
|
#
|
||||||
|
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
||||||
|
NTLM_CONST = Rex::Proto::NTLM::Constants
|
||||||
|
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
||||||
|
|
||||||
#
|
#
|
||||||
# Initializes an exploit module that exploits a vulnerability in an HTTP
|
# Initializes an exploit module that exploits a vulnerability in an HTTP
|
||||||
|
@ -180,6 +193,12 @@ module Exploit::Remote::HttpClient
|
||||||
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
|
'uri_fake_end' => datastore['HTTP::uri_fake_end'],
|
||||||
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
|
'uri_fake_params_start' => datastore['HTTP::uri_fake_params_start'],
|
||||||
'header_folding' => datastore['HTTP::header_folding'],
|
'header_folding' => datastore['HTTP::header_folding'],
|
||||||
|
'usentlm2_session' => datastore['NTLM::UseNTLM2_session'],
|
||||||
|
'use_ntlmv2' => datastore['NTLM::UseNTLMv2'],
|
||||||
|
'send_lm' => datastore['NTLM::SendLM'],
|
||||||
|
'send_ntlm' => datastore['NTLM::SendNTLM'],
|
||||||
|
'SendSPN' => datastore['NTLM::SendSPN'],
|
||||||
|
'UseLMKey' => datastore['NTLM::UseLMKey'],
|
||||||
'domain' => datastore['DOMAIN'],
|
'domain' => datastore['DOMAIN'],
|
||||||
'DigestAuthIIS' => datastore['DigestAuthIIS']
|
'DigestAuthIIS' => datastore['DigestAuthIIS']
|
||||||
)
|
)
|
||||||
|
@ -236,6 +255,12 @@ module Exploit::Remote::HttpClient
|
||||||
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
evade_uri_fake_end: datastore['HTTP::uri_fake_end'],
|
||||||
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
evade_uri_fake_params_start: datastore['HTTP::uri_fake_params_start'],
|
||||||
evade_header_folding: datastore['HTTP::header_folding'],
|
evade_header_folding: datastore['HTTP::header_folding'],
|
||||||
|
ntlm_use_ntlmv2_session: datastore['NTLM::UseNTLM2_session'],
|
||||||
|
ntlm_use_ntlmv2: datastore['NTLM::UseNTLMv2'],
|
||||||
|
ntlm_send_lm: datastore['NTLM::SendLM'],
|
||||||
|
ntlm_send_ntlm: datastore['NTLM::SendNTLM'],
|
||||||
|
ntlm_send_spn: datastore['NTLM::SendSPN'],
|
||||||
|
ntlm_use_lm_key: datastore['NTLM::UseLMKey'],
|
||||||
ntlm_domain: datastore['DOMAIN'],
|
ntlm_domain: datastore['DOMAIN'],
|
||||||
digest_auth_iis: datastore['DigestAuthIIS']
|
digest_auth_iis: datastore['DigestAuthIIS']
|
||||||
}.merge(conf)
|
}.merge(conf)
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
# -*- coding: binary -*-
|
# -*- coding: binary -*-
|
||||||
require 'msf/core'
|
require 'msf/core'
|
||||||
require 'msf/core/exploit/mssql_commands'
|
require 'msf/core/exploit/mssql_commands'
|
||||||
|
require 'rex/proto/ntlm/crypt'
|
||||||
|
require 'rex/proto/ntlm/constants'
|
||||||
|
require 'rex/proto/ntlm/utils'
|
||||||
|
require 'rex/proto/ntlm/exceptions'
|
||||||
|
|
||||||
|
|
||||||
module Msf
|
module Msf
|
||||||
|
|
||||||
|
@ -16,32 +21,41 @@ module Exploit::Remote::MSSQL
|
||||||
include Exploit::Remote::Tcp
|
include Exploit::Remote::Tcp
|
||||||
include Exploit::Remote::NTLM::Client
|
include Exploit::Remote::NTLM::Client
|
||||||
|
|
||||||
|
#
|
||||||
|
# Constants
|
||||||
|
#
|
||||||
|
NTLM_CRYPT = Rex::Proto::NTLM::Crypt
|
||||||
|
NTLM_CONST = Rex::Proto::NTLM::Constants
|
||||||
|
NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
NTLM_XCEPT = Rex::Proto::NTLM::Exceptions
|
||||||
|
|
||||||
# Encryption
|
# Encryption
|
||||||
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
ENCRYPT_OFF = 0x00 #Encryption is available but off.
|
||||||
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
ENCRYPT_ON = 0x01 #Encryption is available and on.
|
||||||
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
|
ENCRYPT_NOT_SUP = 0x02 #Encryption is not available.
|
||||||
ENCRYPT_REQ = 0x03 #Encryption is required.
|
ENCRYPT_REQ = 0x03 #Encryption is required.
|
||||||
|
|
||||||
# Packet Type
|
# Paquet Type
|
||||||
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
TYPE_SQL_BATCH = 1 # (Client) SQL command
|
||||||
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
TYPE_PRE_TDS7_LOGIN = 2 # (Client) Pre-login with version < 7 (unused)
|
||||||
TYPE_RPC = 3 # (Client) RPC
|
TYPE_RPC = 3 # (Client) RPC
|
||||||
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
TYPE_TABLE_RESPONSE = 4 # (Server) Pre-Login Response ,Login Response, Row Data, Return Status, Return Parameters,
|
||||||
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
# Request Completion, Error and Info Messages, Attention Acknowledgement
|
||||||
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
TYPE_ATTENTION_SIGNAL = 6 # (Client) Attention
|
||||||
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
TYPE_BULK_LOAD = 7 # (Client) SQL Command with binary data
|
||||||
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
TYPE_TRANSACTION_MANAGER_REQUEST = 14 # (Client) Transaction request manager
|
||||||
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
TYPE_TDS7_LOGIN = 16 # (Client) Login
|
||||||
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
TYPE_SSPI_MESSAGE = 17 # (Client) Login
|
||||||
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
TYPE_PRE_LOGIN_MESSAGE = 18 # (Client) pre-login with version > 7
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
STATUS_NORMAL = 0x00
|
STATUS_NORMAL = 0x00
|
||||||
STATUS_END_OF_MESSAGE = 0x01
|
STATUS_END_OF_MESSAGE = 0x01
|
||||||
STATUS_IGNORE_EVENT = 0x02
|
STATUS_IGNORE_EVENT = 0x02
|
||||||
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
STATUS_RESETCONNECTION = 0x08 # TDS 7.1+
|
||||||
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
STATUS_RESETCONNECTIONSKIPTRAN = 0x10 # TDS 7.3+
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Creates an instance of a MSSQL exploit module.
|
# Creates an instance of a MSSQL exploit module.
|
||||||
#
|
#
|
||||||
|
@ -86,13 +100,16 @@ module Exploit::Remote::MSSQL
|
||||||
'MsfExploit' => self,
|
'MsfExploit' => self,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
ping_sock.put("\x02")
|
ping_sock.put("\x02")
|
||||||
resp, _saddr, _sport = ping_sock.recvfrom(65535, timeout)
|
resp, saddr, sport = ping_sock.recvfrom(65535, timeout)
|
||||||
ping_sock.close
|
ping_sock.close
|
||||||
|
|
||||||
return data if not resp
|
return data if not resp
|
||||||
return data if resp.length == 0
|
return data if resp.length == 0
|
||||||
|
|
||||||
|
var = nil
|
||||||
|
|
||||||
return mssql_ping_parse(resp)
|
return mssql_ping_parse(resp)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -128,15 +145,15 @@ module Exploit::Remote::MSSQL
|
||||||
#
|
#
|
||||||
# Execute a system command via xp_cmdshell
|
# Execute a system command via xp_cmdshell
|
||||||
#
|
#
|
||||||
def mssql_xpcmdshell(cmd, doprint=false, opts={})
|
def mssql_xpcmdshell(cmd,doprint=false,opts={})
|
||||||
force_enable = false
|
force_enable = false
|
||||||
begin
|
begin
|
||||||
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
|
res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", false, opts)
|
||||||
if res[:errors] && !res[:errors].empty?
|
if(res[:errors] and not res[:errors].empty?)
|
||||||
if res[:errors].join =~ /xp_cmdshell/
|
if(res[:errors].join =~ /xp_cmdshell/)
|
||||||
if force_enable
|
if(force_enable)
|
||||||
print_error("The xp_cmdshell procedure is not available and could not be enabled")
|
print_error("The xp_cmdshell procedure is not available and could not be enabled")
|
||||||
raise RuntimeError, "Failed to execute command"
|
raise RuntimeError, "Failed to execute command"
|
||||||
else
|
else
|
||||||
print_status("The server may have xp_cmdshell disabled, trying to enable it...")
|
print_status("The server may have xp_cmdshell disabled, trying to enable it...")
|
||||||
mssql_query(mssql_xpcmdshell_enable())
|
mssql_query(mssql_xpcmdshell_enable())
|
||||||
|
@ -150,7 +167,7 @@ module Exploit::Remote::MSSQL
|
||||||
return res
|
return res
|
||||||
|
|
||||||
rescue RuntimeError => e
|
rescue RuntimeError => e
|
||||||
if e.to_s =~ /xp_cmdshell disabled/
|
if(e.to_s =~ /xp_cmdshell disabled/)
|
||||||
force_enable = true
|
force_enable = true
|
||||||
retry
|
retry
|
||||||
end
|
end
|
||||||
|
@ -183,7 +200,7 @@ module Exploit::Remote::MSSQL
|
||||||
idx = 0
|
idx = 0
|
||||||
cnt = 500
|
cnt = 500
|
||||||
while(idx < hex.length - 1)
|
while(idx < hex.length - 1)
|
||||||
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>%TEMP%\\#{var_payload}", false)
|
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false)
|
||||||
idx += cnt
|
idx += cnt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,7 +234,7 @@ module Exploit::Remote::MSSQL
|
||||||
idx = 0
|
idx = 0
|
||||||
cnt = 500
|
cnt = 500
|
||||||
while(idx < hex.length - 1)
|
while(idx < hex.length - 1)
|
||||||
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx, cnt]}>>%TEMP%\\#{var_payload}", false)
|
mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false)
|
||||||
idx += cnt
|
idx += cnt
|
||||||
end
|
end
|
||||||
print_status("Converting the payload utilizing PowerShell EncodedCommand...")
|
print_status("Converting the payload utilizing PowerShell EncodedCommand...")
|
||||||
|
@ -243,17 +260,17 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
while(not done)
|
while(not done)
|
||||||
head = sock.get_once(8, timeout)
|
head = sock.get_once(8, timeout)
|
||||||
if !(head && head.length == 8)
|
if !(head and head.length == 8)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Is this the last buffer?
|
# Is this the last buffer?
|
||||||
if(head[1, 1] == "\x01" or not check_status )
|
if(head[1,1] == "\x01" or not check_status )
|
||||||
done = true
|
done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Grab this block's length
|
# Grab this block's length
|
||||||
rlen = head[2, 2].unpack('n')[0] - 8
|
rlen = head[2,2].unpack('n')[0] - 8
|
||||||
|
|
||||||
while(rlen > 0)
|
while(rlen > 0)
|
||||||
buff = sock.get_once(rlen, timeout)
|
buff = sock.get_once(rlen, timeout)
|
||||||
|
@ -285,77 +302,77 @@ module Exploit::Remote::MSSQL
|
||||||
pkt_data = ""
|
pkt_data = ""
|
||||||
|
|
||||||
|
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_PRE_LOGIN_MESSAGE, #type
|
TYPE_PRE_LOGIN_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x00, # PacketID
|
0x00, # PacketID
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
version = [0x55010008, 0x0000].pack("Vv")
|
version = [0x55010008,0x0000].pack("Vv")
|
||||||
encryption = ENCRYPT_NOT_SUP # off
|
encryption = ENCRYPT_NOT_SUP # off
|
||||||
instoptdata = "MSSQLServer\0"
|
instoptdata = "MSSQLServer\0"
|
||||||
|
|
||||||
threadid = "\0\0" + Rex::Text.rand_text(2)
|
threadid = "\0\0" + Rex::Text.rand_text(2)
|
||||||
|
|
||||||
idx = 21 # size of pkt_data_token
|
idx = 21 # size of pkt_data_token
|
||||||
pkt_data_token << [
|
pkt_data_token << [
|
||||||
0x00, # Token 0 type Version
|
0x00, # Token 0 type Version
|
||||||
idx, # VersionOffset
|
idx , # VersionOffset
|
||||||
version.length, # VersionLength
|
version.length, # VersionLength
|
||||||
|
|
||||||
0x01, # Token 1 type Encryption
|
0x01, # Token 1 type Encryption
|
||||||
idx = idx + version.length, # EncryptionOffset
|
idx = idx + version.length, # EncryptionOffset
|
||||||
0x01, # EncryptionLength
|
0x01, # EncryptionLength
|
||||||
|
|
||||||
0x02, # Token 2 type InstOpt
|
0x02, # Token 2 type InstOpt
|
||||||
idx = idx + 1, # InstOptOffset
|
idx = idx + 1, # InstOptOffset
|
||||||
instoptdata.length, # InstOptLength
|
instoptdata.length, # InstOptLength
|
||||||
|
|
||||||
0x03, # Token 3 type Threadid
|
0x03, # Token 3 type Threadid
|
||||||
idx + instoptdata.length, # ThreadIdOffset
|
idx + instoptdata.length, # ThreadIdOffset
|
||||||
0x04, # ThreadIdLength
|
0x04, # ThreadIdLength
|
||||||
|
|
||||||
0xFF
|
0xFF
|
||||||
].pack("CnnCnnCnnCnnC")
|
].pack("CnnCnnCnnCnnC")
|
||||||
|
|
||||||
pkt_data << pkt_data_token
|
pkt_data << pkt_data_token
|
||||||
pkt_data << version
|
pkt_data << version
|
||||||
pkt_data << encryption
|
pkt_data << encryption
|
||||||
pkt_data << instoptdata
|
pkt_data << instoptdata
|
||||||
pkt_data << threadid
|
pkt_data << threadid
|
||||||
|
|
||||||
pkt_hdr[2] = pkt_data.length + 8
|
pkt_hdr[2] = pkt_data.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
pkt = pkt_hdr.pack("CCnnCC") + pkt_data
|
||||||
|
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
|
|
||||||
while resp && resp[0, 1] != "\xff" && resp.length > 5
|
while resp and resp[0,1] != "\xff" and resp.length > 5
|
||||||
token = resp.slice!(0, 5)
|
token = resp.slice!(0,5)
|
||||||
token = token.unpack("Cnn")
|
token = token.unpack("Cnn")
|
||||||
idx -= 5
|
idx -= 5
|
||||||
if token[0] == 0x01
|
if token[0] == 0x01
|
||||||
idx += token[1]
|
|
||||||
break
|
idx += token[1]
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if idx > 0
|
||||||
|
encryption_mode = resp[idx,1].unpack("C")[0]
|
||||||
|
else
|
||||||
|
#force to ENCRYPT_NOT_SUP and hope for the best
|
||||||
|
encryption_mode = ENCRYPT_NOT_SUP
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if idx > 0
|
if encryption_mode != ENCRYPT_NOT_SUP and enc_error
|
||||||
encryption_mode = resp[idx, 1].unpack("C")[0]
|
raise RuntimeError,"Encryption is not supported"
|
||||||
else
|
end
|
||||||
# force to ENCRYPT_NOT_SUP and hope for the best
|
encryption_mode
|
||||||
encryption_mode = ENCRYPT_NOT_SUP
|
|
||||||
end
|
|
||||||
|
|
||||||
if encryption_mode != ENCRYPT_NOT_SUP && enc_error
|
|
||||||
raise RuntimeError,"Encryption is not supported"
|
|
||||||
end
|
|
||||||
encryption_mode
|
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -384,14 +401,14 @@ module Exploit::Remote::MSSQL
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_TDS7_LOGIN, #type
|
TYPE_TDS7_LOGIN, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID (unused upon specification
|
0x01, # PacketID (unused upon specification
|
||||||
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
# but ms network monitor stil prefer 1 to decode correctly, wireshark don't care)
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt << [
|
pkt << [
|
||||||
|
@ -414,18 +431,19 @@ module Exploit::Remote::MSSQL
|
||||||
sname = Rex::Text.to_unicode( rhost )
|
sname = Rex::Text.to_unicode( rhost )
|
||||||
dname = Rex::Text.to_unicode( db )
|
dname = Rex::Text.to_unicode( db )
|
||||||
|
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
ntlm_options = {
|
||||||
|
:signing => false,
|
||||||
|
:usentlm2_session => datastore['NTLM::UseNTLM2_session'],
|
||||||
|
:use_ntlmv2 => datastore['NTLM::UseNTLMv2'],
|
||||||
|
:send_lm => datastore['NTLM::SendLM'],
|
||||||
|
:send_ntlm => datastore['NTLM::SendNTLM']
|
||||||
|
}
|
||||||
|
|
||||||
ntlm_client = ::Net::NTLM::Client.new(
|
ntlmssp_flags = NTLM_UTILS.make_ntlm_flags(ntlm_options)
|
||||||
user,
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||||
pass,
|
domain_name = datastore['DOMAIN']
|
||||||
workstation: workstation_name,
|
|
||||||
domain: datastore['DOMAIN'],
|
ntlmsspblob = NTLM_UTILS::make_ntlmssp_blob_init(domain_name, workstation_name, ntlmssp_flags)
|
||||||
)
|
|
||||||
type1 = ntlm_client.init_context
|
|
||||||
# SQL 2012, at least, does not support KEY_EXCHANGE
|
|
||||||
type1.flag &= ~ ::Net::NTLM::FLAGS[:KEY_EXCHANGE]
|
|
||||||
ntlmsspblob = type1.serialize
|
|
||||||
|
|
||||||
idx = pkt.size + 50 # lengths below
|
idx = pkt.size + 50 # lengths below
|
||||||
|
|
||||||
|
@ -466,9 +484,9 @@ module Exploit::Remote::MSSQL
|
||||||
pkt << ntlmsspblob
|
pkt << ntlmsspblob
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0, 4] = [pkt.length].pack('V')
|
pkt[0,4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
pkt_hdr[2] = pkt.length + 8
|
pkt_hdr[2] = pkt.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
pkt = pkt_hdr.pack("CCnnCC") + pkt
|
||||||
|
|
||||||
|
@ -476,36 +494,56 @@ module Exploit::Remote::MSSQL
|
||||||
# has a strange behavior that differs from the specifications
|
# 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
|
# 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
|
# 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)
|
resp = mssql_send_recv(pkt,15, false)
|
||||||
|
|
||||||
unless resp.include?("NTLMSSP")
|
# Get default data
|
||||||
|
begin
|
||||||
|
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(resp)
|
||||||
|
# a domain.length < 3 will hit this
|
||||||
|
rescue NTLM_XCEPT::NTLMMissingChallenge
|
||||||
info = {:errors => []}
|
info = {:errors => []}
|
||||||
mssql_parse_reply(resp, info)
|
mssql_parse_reply(resp, info)
|
||||||
mssql_print_reply(info)
|
mssql_print_reply(info)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
challenge_key = blob_data[:challenge_key]
|
||||||
|
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||||
|
#netbios name
|
||||||
|
default_name = blob_data[:default_name] || ''
|
||||||
|
#netbios domain
|
||||||
|
default_domain = blob_data[:default_domain] || ''
|
||||||
|
#dns name
|
||||||
|
dns_host_name = blob_data[:dns_host_name] || ''
|
||||||
|
#dns domain
|
||||||
|
dns_domain_name = blob_data[:dns_domain_name] || ''
|
||||||
|
#Client time
|
||||||
|
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
||||||
|
|
||||||
# Get default data
|
spnopt = {:use_spn => datastore['NTLM::SendSPN'], :name => self.rhost}
|
||||||
resp = resp[3..-1]
|
|
||||||
type3 = ntlm_client.init_context([resp].pack('m'))
|
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, challenge_key,
|
||||||
type3_blob = type3.serialize
|
domain_name, default_name, default_domain,
|
||||||
|
dns_host_name, dns_domain_name, chall_MsvAvTimestamp,
|
||||||
|
spnopt, ntlm_options)
|
||||||
|
|
||||||
|
ntlmssp = NTLM_UTILS.make_ntlmssp_blob_auth(domain_name, workstation_name, user, resp_lm, resp_ntlm, '', ntlmssp_flags)
|
||||||
|
|
||||||
# Create an SSPIMessage
|
# Create an SSPIMessage
|
||||||
idx = 0
|
idx = 0
|
||||||
pkt = ''
|
pkt = ''
|
||||||
pkt_hdr = ''
|
pkt_hdr = ''
|
||||||
pkt_hdr = [
|
pkt_hdr = [
|
||||||
TYPE_SSPI_MESSAGE, #type
|
TYPE_SSPI_MESSAGE, #type
|
||||||
STATUS_END_OF_MESSAGE, #status
|
STATUS_END_OF_MESSAGE, #status
|
||||||
0x0000, #length
|
0x0000, #length
|
||||||
0x0000, # SPID
|
0x0000, # SPID
|
||||||
0x01, # PacketID
|
0x01, # PacketID
|
||||||
0x00 #Window
|
0x00 #Window
|
||||||
]
|
]
|
||||||
|
|
||||||
pkt_hdr[2] = type3_blob.length + 8
|
pkt_hdr[2] = ntlmssp.length + 8
|
||||||
|
|
||||||
pkt = pkt_hdr.pack("CCnnCC") + type3_blob
|
pkt = pkt_hdr.pack("CCnnCC") + ntlmssp
|
||||||
|
|
||||||
resp = mssql_send_recv(pkt)
|
resp = mssql_send_recv(pkt)
|
||||||
|
|
||||||
|
@ -582,7 +620,7 @@ module Exploit::Remote::MSSQL
|
||||||
pkt << dname
|
pkt << dname
|
||||||
|
|
||||||
# Total packet length
|
# Total packet length
|
||||||
pkt[0, 4] = [pkt.length].pack('V')
|
pkt[0,4] = [pkt.length].pack('V')
|
||||||
|
|
||||||
# Embedded packet lengths
|
# Embedded packet lengths
|
||||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||||
|
@ -599,7 +637,7 @@ module Exploit::Remote::MSSQL
|
||||||
end
|
end
|
||||||
|
|
||||||
info = {:errors => []}
|
info = {:errors => []}
|
||||||
info = mssql_parse_reply(resp, info)
|
info = mssql_parse_reply(resp,info)
|
||||||
|
|
||||||
return false if not info
|
return false if not info
|
||||||
info[:login_ack] ? true : false
|
info[:login_ack] ? true : false
|
||||||
|
@ -652,17 +690,17 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
print_status("SQL Query: #{info[:sql]}")
|
print_status("SQL Query: #{info[:sql]}")
|
||||||
|
|
||||||
if info[:done] && info[:done][:rows].to_i > 0
|
if(info[:done] and info[:done][:rows].to_i > 0)
|
||||||
print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})")
|
print_status("Row Count: #{info[:done][:rows]} (Status: #{info[:done][:status]} Command: #{info[:done][:cmd]})")
|
||||||
end
|
end
|
||||||
|
|
||||||
if info[:errors] && !info[:errors].empty?
|
if(info[:errors] and not info[:errors].empty?)
|
||||||
info[:errors].each do |err|
|
info[:errors].each do |err|
|
||||||
print_error(err)
|
print_error(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if info[:rows] && !info[:rows].empty?
|
if(info[:rows] and not info[:rows].empty?)
|
||||||
|
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
'Indent' => 1,
|
'Indent' => 1,
|
||||||
|
@ -689,14 +727,14 @@ module Exploit::Remote::MSSQL
|
||||||
info[:colnames] ||= []
|
info[:colnames] ||= []
|
||||||
|
|
||||||
# Parse out the columns
|
# Parse out the columns
|
||||||
cols = data.slice!(0, 2).unpack('v')[0]
|
cols = data.slice!(0,2).unpack('v')[0]
|
||||||
0.upto(cols-1) do |col_idx|
|
0.upto(cols-1) do |col_idx|
|
||||||
col = {}
|
col = {}
|
||||||
info[:colinfos][col_idx] = col
|
info[:colinfos][col_idx] = col
|
||||||
|
|
||||||
col[:utype] = data.slice!(0, 2).unpack('v')[0]
|
col[:utype] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:flags] = data.slice!(0, 2).unpack('v')[0]
|
col[:flags] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:type] = data.slice!(0, 1).unpack('C')[0]
|
col[:type] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
case col[:type]
|
case col[:type]
|
||||||
when 48
|
when 48
|
||||||
|
@ -713,8 +751,8 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
when 34
|
when 34
|
||||||
col[:id] = :image
|
col[:id] = :image
|
||||||
col[:max_size] = data.slice!(0, 4).unpack('V')[0]
|
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
||||||
col[:value_length] = data.slice!(0, 2).unpack('v')[0]
|
col[:value_length] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||||
|
|
||||||
when 36
|
when 36
|
||||||
|
@ -722,33 +760,33 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
when 38
|
when 38
|
||||||
col[:id] = :int
|
col[:id] = :int
|
||||||
col[:int_size] = data.slice!(0, 1).unpack('C')[0]
|
col[:int_size] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
when 127
|
when 127
|
||||||
col[:id] = :bigint
|
col[:id] = :bigint
|
||||||
|
|
||||||
when 165
|
when 165
|
||||||
col[:id] = :hex
|
col[:id] = :hex
|
||||||
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||||
|
|
||||||
when 173
|
when 173
|
||||||
col[:id] = :hex # binary(2)
|
col[:id] = :hex # binary(2)
|
||||||
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||||
|
|
||||||
when 231, 175, 167, 239
|
when 231,175,167,239
|
||||||
col[:id] = :string
|
col[:id] = :string
|
||||||
col[:max_size] = data.slice!(0, 2).unpack('v')[0]
|
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:codepage] = data.slice!(0, 2).unpack('v')[0]
|
col[:codepage] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:cflags] = data.slice!(0, 2).unpack('v')[0]
|
col[:cflags] = data.slice!(0,2).unpack('v')[0]
|
||||||
col[:charset_id] = data.slice!(0, 1).unpack('C')[0]
|
col[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
else
|
else
|
||||||
col[:id] = :unknown
|
col[:id] = :unknown
|
||||||
end
|
end
|
||||||
|
|
||||||
col[:msg_len] = data.slice!(0, 1).unpack('C')[0]
|
col[:msg_len] = data.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
if col[:msg_len] && col[:msg_len] > 0
|
if(col[:msg_len] and col[:msg_len] > 0)
|
||||||
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
||||||
end
|
end
|
||||||
info[:colnames] << (col[:name] || 'NULL')
|
info[:colnames] << (col[:name] || 'NULL')
|
||||||
|
@ -762,7 +800,7 @@ module Exploit::Remote::MSSQL
|
||||||
info[:errors] = []
|
info[:errors] = []
|
||||||
return if not data
|
return if not data
|
||||||
until data.empty?
|
until data.empty?
|
||||||
token = data.slice!(0, 1).unpack('C')[0]
|
token = data.slice!(0,1).unpack('C')[0]
|
||||||
case token
|
case token
|
||||||
when 0x81
|
when 0x81
|
||||||
mssql_parse_tds_reply(data, info)
|
mssql_parse_tds_reply(data, info)
|
||||||
|
@ -806,28 +844,28 @@ module Exploit::Remote::MSSQL
|
||||||
case col[:id]
|
case col[:id]
|
||||||
when :hex
|
when :hex
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
if len > 0 && len < 65535
|
if(len > 0 and len < 65535)
|
||||||
str << data.slice!(0, len)
|
str << data.slice!(0,len)
|
||||||
end
|
end
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :string
|
when :string
|
||||||
str = ""
|
str = ""
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
if len > 0 && len < 65535
|
if(len > 0 and len < 65535)
|
||||||
str << data.slice!(0, len)
|
str << data.slice!(0,len)
|
||||||
end
|
end
|
||||||
row << str.gsub("\x00", '')
|
row << str.gsub("\x00", '')
|
||||||
|
|
||||||
when :datetime
|
when :datetime
|
||||||
row << data.slice!(0, 8).unpack("H*")[0]
|
row << data.slice!(0,8).unpack("H*")[0]
|
||||||
|
|
||||||
when :rawint
|
when :rawint
|
||||||
row << data.slice!(0, 4).unpack('V')[0]
|
row << data.slice!(0,4).unpack('V')[0]
|
||||||
|
|
||||||
when :bigint
|
when :bigint
|
||||||
row << data.slice!(0, 8).unpack("H*")[0]
|
row << data.slice!(0,8).unpack("H*")[0]
|
||||||
|
|
||||||
when :smallint
|
when :smallint
|
||||||
row << data.slice!(0, 2).unpack("v")[0]
|
row << data.slice!(0, 2).unpack("v")[0]
|
||||||
|
@ -840,16 +878,16 @@ module Exploit::Remote::MSSQL
|
||||||
|
|
||||||
when :image
|
when :image
|
||||||
str = ''
|
str = ''
|
||||||
len = data.slice!(0, 1).unpack('C')[0]
|
len = data.slice!(0,1).unpack('C')[0]
|
||||||
str = data.slice!(0, len) if len && len > 0
|
str = data.slice!(0,len) if (len and len > 0)
|
||||||
row << str.unpack("H*")[0]
|
row << str.unpack("H*")[0]
|
||||||
|
|
||||||
when :int
|
when :int
|
||||||
len = data.slice!(0, 1).unpack("C")[0]
|
len = data.slice!(0, 1).unpack("C")[0]
|
||||||
raw = data.slice!(0, len) if len && len > 0
|
raw = data.slice!(0, len) if (len and len > 0)
|
||||||
|
|
||||||
case len
|
case len
|
||||||
when 0, 255
|
when 0,255
|
||||||
row << ''
|
row << ''
|
||||||
when 1
|
when 1
|
||||||
row << raw.unpack("C")[0]
|
row << raw.unpack("C")[0]
|
||||||
|
@ -862,7 +900,7 @@ module Exploit::Remote::MSSQL
|
||||||
when 8
|
when 8
|
||||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||||
else
|
else
|
||||||
info[:errors] << "invalid integer size: #{len} #{data[0, 16].unpack("H*")[0]}"
|
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
info[:errors] << "unknown column type: #{col.inspect}"
|
info[:errors] << "unknown column type: #{col.inspect}"
|
||||||
|
@ -877,7 +915,7 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse a "ret" TDS token
|
# Parse a "ret" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_ret(data, info)
|
def mssql_parse_ret(data, info)
|
||||||
ret = data.slice!(0, 4).unpack('N')[0]
|
ret = data.slice!(0,4).unpack('N')[0]
|
||||||
info[:ret] = ret
|
info[:ret] = ret
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -886,7 +924,7 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse a "done" TDS token
|
# Parse a "done" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_done(data, info)
|
def mssql_parse_done(data, info)
|
||||||
status, cmd, rows = data.slice!(0, 8).unpack('vvV')
|
status,cmd,rows = data.slice!(0,8).unpack('vvV')
|
||||||
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
info[:done] = { :status => status, :cmd => cmd, :rows => rows }
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -895,11 +933,11 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse an "error" TDS token
|
# Parse an "error" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_error(data, info)
|
def mssql_parse_error(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
|
|
||||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0, elen * 2)
|
emsg = buff.slice!(0,elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
|
@ -910,17 +948,17 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse an "environment change" TDS token
|
# Parse an "environment change" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_env(data, info)
|
def mssql_parse_env(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
type = buff.slice!(0, 1).unpack('C')[0]
|
type = buff.slice!(0,1).unpack('C')[0]
|
||||||
|
|
||||||
nval = ''
|
nval = ''
|
||||||
nlen = buff.slice!(0, 1).unpack('C')[0] || 0
|
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||||
nval = buff.slice!(0, nlen * 2).gsub("\x00", '') if nlen > 0
|
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
||||||
|
|
||||||
oval = ''
|
oval = ''
|
||||||
olen = buff.slice!(0, 1).unpack('C')[0] || 0
|
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||||
oval = buff.slice!(0, olen * 2).gsub("\x00", '') if olen > 0
|
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
||||||
|
|
||||||
info[:envs] ||= []
|
info[:envs] ||= []
|
||||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||||
|
@ -931,14 +969,14 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse an "information" TDS token
|
# Parse an "information" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_info(data, info)
|
def mssql_parse_info(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
|
|
||||||
errno, state, sev, elen = buff.slice!(0, 8).unpack('VCCv')
|
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||||
emsg = buff.slice!(0, elen * 2)
|
emsg = buff.slice!(0,elen * 2)
|
||||||
emsg.gsub!("\x00", '')
|
emsg.gsub!("\x00", '')
|
||||||
|
|
||||||
info[:infos] ||= []
|
info[:infos]||= []
|
||||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||||
info
|
info
|
||||||
end
|
end
|
||||||
|
@ -947,8 +985,8 @@ module Exploit::Remote::MSSQL
|
||||||
# Parse a "login ack" TDS token
|
# Parse a "login ack" TDS token
|
||||||
#
|
#
|
||||||
def mssql_parse_login_ack(data, info)
|
def mssql_parse_login_ack(data, info)
|
||||||
len = data.slice!(0, 2).unpack('v')[0]
|
len = data.slice!(0,2).unpack('v')[0]
|
||||||
_buff = data.slice!(0, len)
|
buff = data.slice!(0,len)
|
||||||
info[:login_ack] = true
|
info[:login_ack] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,12 @@ module Msf
|
||||||
|
|
||||||
module Exploit::NTLM
|
module Exploit::NTLM
|
||||||
|
|
||||||
|
NTLM_CONST = ::Rex::Proto::NTLM::Constants
|
||||||
|
NTLM_CRYPT = ::Rex::Proto::NTLM::Crypt
|
||||||
|
NTLM_UTILS = ::Rex::Proto::NTLM::Utils
|
||||||
|
NTLM_BASE = ::Rex::Proto::NTLM::Base
|
||||||
|
NTLM_MESSAGE = ::Rex::Proto::NTLM::Message
|
||||||
|
|
||||||
module Client
|
module Client
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super
|
super
|
||||||
|
|
|
@ -3,6 +3,10 @@ require 'rex/socket'
|
||||||
require 'rex/proto/http'
|
require 'rex/proto/http'
|
||||||
require 'rex/text'
|
require 'rex/text'
|
||||||
require 'digest'
|
require 'digest'
|
||||||
|
require 'rex/proto/ntlm/crypt'
|
||||||
|
require 'rex/proto/ntlm/constants'
|
||||||
|
require 'rex/proto/ntlm/utils'
|
||||||
|
require 'rex/proto/ntlm/exceptions'
|
||||||
|
|
||||||
require 'rex/proto/http/client_request'
|
require 'rex/proto/http/client_request'
|
||||||
|
|
||||||
|
@ -309,6 +313,7 @@ class Client
|
||||||
# Send a series of requests to complete Digest Authentication
|
# Send a series of requests to complete Digest Authentication
|
||||||
#
|
#
|
||||||
# @param opts [Hash] the options used to build an HTTP request
|
# @param opts [Hash] the options used to build an HTTP request
|
||||||
|
#
|
||||||
# @return [Response] the last valid HTTP response we received
|
# @return [Response] the last valid HTTP response we received
|
||||||
def digest_auth(opts={})
|
def digest_auth(opts={})
|
||||||
@nonce_count = 0
|
@nonce_count = 0
|
||||||
|
@ -452,6 +457,13 @@ class Client
|
||||||
#
|
#
|
||||||
# @return [Response] the last valid HTTP response we received
|
# @return [Response] the last valid HTTP response we received
|
||||||
def negotiate_auth(opts={})
|
def negotiate_auth(opts={})
|
||||||
|
ntlm_options = {
|
||||||
|
:signing => false,
|
||||||
|
:usentlm2_session => self.config['usentlm2_session'],
|
||||||
|
:use_ntlmv2 => self.config['use_ntlmv2'],
|
||||||
|
:send_lm => self.config['send_lm'],
|
||||||
|
:send_ntlm => self.config['send_ntlm']
|
||||||
|
}
|
||||||
|
|
||||||
to = opts['timeout'] || 20
|
to = opts['timeout'] || 20
|
||||||
opts['username'] ||= ''
|
opts['username'] ||= ''
|
||||||
|
@ -460,27 +472,28 @@ class Client
|
||||||
if opts['provider'] and opts['provider'].include? 'Negotiate'
|
if opts['provider'] and opts['provider'].include? 'Negotiate'
|
||||||
provider = "Negotiate "
|
provider = "Negotiate "
|
||||||
else
|
else
|
||||||
provider = "NTLM "
|
provider = 'NTLM '
|
||||||
end
|
end
|
||||||
|
|
||||||
opts['method']||= 'GET'
|
opts['method']||= 'GET'
|
||||||
opts['headers']||= {}
|
opts['headers']||= {}
|
||||||
|
|
||||||
|
ntlmssp_flags = ::Rex::Proto::NTLM::Utils.make_ntlm_flags(ntlm_options)
|
||||||
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
|
workstation_name = Rex::Text.rand_text_alpha(rand(8)+6)
|
||||||
domain_name = self.config['domain']
|
domain_name = self.config['domain']
|
||||||
|
|
||||||
ntlm_client = ::Net::NTLM::Client.new(
|
b64_blob = Rex::Text::encode_base64(
|
||||||
opts['username'],
|
::Rex::Proto::NTLM::Utils::make_ntlmssp_blob_init(
|
||||||
opts['password'],
|
domain_name,
|
||||||
workstation: workstation_name,
|
workstation_name,
|
||||||
domain: domain_name,
|
ntlmssp_flags
|
||||||
)
|
))
|
||||||
type1 = ntlm_client.init_context
|
|
||||||
|
ntlm_message_1 = provider + b64_blob
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# First request to get the challenge
|
# First request to get the challenge
|
||||||
opts['headers']['Authorization'] = provider + type1.encode64
|
opts['headers']['Authorization'] = ntlm_message_1
|
||||||
|
|
||||||
r = request_cgi(opts)
|
r = request_cgi(opts)
|
||||||
resp = _send_recv(r, to)
|
resp = _send_recv(r, to)
|
||||||
unless resp.kind_of? Rex::Proto::Http::Response
|
unless resp.kind_of? Rex::Proto::Http::Response
|
||||||
|
@ -493,10 +506,47 @@ class Client
|
||||||
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0]
|
ntlm_challenge = resp.headers['WWW-Authenticate'].scan(/#{provider}([A-Z0-9\x2b\x2f=]+)/ni).flatten[0]
|
||||||
return resp unless ntlm_challenge
|
return resp unless ntlm_challenge
|
||||||
|
|
||||||
ntlm_message_3 = ntlm_client.init_context(ntlm_challenge)
|
ntlm_message_2 = Rex::Text::decode_base64(ntlm_challenge)
|
||||||
|
blob_data = ::Rex::Proto::NTLM::Utils.parse_ntlm_type_2_blob(ntlm_message_2)
|
||||||
|
|
||||||
|
challenge_key = blob_data[:challenge_key]
|
||||||
|
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||||
|
default_name = blob_data[:default_name] || '' #netbios name
|
||||||
|
default_domain = blob_data[:default_domain] || '' #netbios domain
|
||||||
|
dns_host_name = blob_data[:dns_host_name] || '' #dns name
|
||||||
|
dns_domain_name = blob_data[:dns_domain_name] || '' #dns domain
|
||||||
|
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || '' #Client time
|
||||||
|
|
||||||
|
spnopt = {:use_spn => self.config['SendSPN'], :name => self.hostname}
|
||||||
|
|
||||||
|
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = ::Rex::Proto::NTLM::Utils.create_lm_ntlm_responses(
|
||||||
|
opts['username'],
|
||||||
|
opts['password'],
|
||||||
|
challenge_key,
|
||||||
|
domain_name,
|
||||||
|
default_name,
|
||||||
|
default_domain,
|
||||||
|
dns_host_name,
|
||||||
|
dns_domain_name,
|
||||||
|
chall_MsvAvTimestamp,
|
||||||
|
spnopt,
|
||||||
|
ntlm_options
|
||||||
|
)
|
||||||
|
|
||||||
|
ntlm_message_3 = ::Rex::Proto::NTLM::Utils.make_ntlmssp_blob_auth(
|
||||||
|
domain_name,
|
||||||
|
workstation_name,
|
||||||
|
opts['username'],
|
||||||
|
resp_lm,
|
||||||
|
resp_ntlm,
|
||||||
|
'',
|
||||||
|
ntlmssp_flags
|
||||||
|
)
|
||||||
|
|
||||||
|
ntlm_message_3 = Rex::Text::encode_base64(ntlm_message_3)
|
||||||
|
|
||||||
# Send the response
|
# Send the response
|
||||||
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3.encode64}"
|
opts['headers']['Authorization'] = "#{provider}#{ntlm_message_3}"
|
||||||
r = request_cgi(opts)
|
r = request_cgi(opts)
|
||||||
resp = _send_recv(r, to, true)
|
resp = _send_recv(r, to, true)
|
||||||
unless resp.kind_of? Rex::Proto::Http::Response
|
unless resp.kind_of? Rex::Proto::Http::Response
|
||||||
|
@ -508,7 +558,6 @@ class Client
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Read a response from the server
|
# Read a response from the server
|
||||||
#
|
#
|
||||||
|
@ -664,6 +713,7 @@ protected
|
||||||
|
|
||||||
attr_accessor :hostname, :port # :nodoc:
|
attr_accessor :hostname, :port # :nodoc:
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,6 @@ module Proto
|
||||||
module SMB
|
module SMB
|
||||||
class Client
|
class Client
|
||||||
|
|
||||||
require 'net/ntlm'
|
|
||||||
|
|
||||||
require 'rex/text'
|
require 'rex/text'
|
||||||
require 'rex/struct2'
|
require 'rex/struct2'
|
||||||
require 'rex/proto/smb/constants'
|
require 'rex/proto/smb/constants'
|
||||||
|
@ -168,14 +166,14 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
|
||||||
# Scan the packet receive cache for a matching response
|
# Scan the packet receive cache for a matching response
|
||||||
def smb_recv_cache_find_match(expected_type)
|
def smb_recv_cache_find_match(expected_type)
|
||||||
|
|
||||||
clean = []
|
clean = []
|
||||||
found = nil
|
found = nil
|
||||||
|
|
||||||
@smb_recv_cache.each do |cent|
|
@smb_recv_cache.each do |cent|
|
||||||
pkt, data, tstamp = cent
|
pkt, data, tstamp = cent
|
||||||
|
|
||||||
# Return matching packets and mark for removal
|
# Return matching packets and mark for removal
|
||||||
if pkt['Payload']['SMB'].v['Command'] == expected_type
|
if pkt['Payload']['SMB'].v['Command'] == expected_type
|
||||||
found = [pkt,data]
|
found = [pkt,data]
|
||||||
clean << cent
|
clean << cent
|
||||||
|
@ -625,15 +623,16 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
|
||||||
|
|
||||||
# Authenticate and establish a session
|
# Authenticate and establish a session
|
||||||
def session_setup(user='', pass='', domain='', do_recv=true)
|
def session_setup(*args)
|
||||||
|
|
||||||
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
|
if (self.dialect =~ /^(NT LANMAN 1.0|NT LM 0.12)$/)
|
||||||
|
|
||||||
if (self.challenge_key)
|
if (self.challenge_key)
|
||||||
return self.session_setup_no_ntlmssp(user, pass, domain, do_recv)
|
return self.session_setup_no_ntlmssp(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( self.extended_security )
|
if ( self.extended_security )
|
||||||
return self.session_setup_with_ntlmssp(user, pass, domain, nil, do_recv)
|
return self.session_setup_with_ntlmssp(*args)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -832,16 +831,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
name = Rex::Text.rand_text_alphanumeric(16)
|
name = Rex::Text.rand_text_alphanumeric(16)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ntlm_client = Net::NTLM::Client.new(
|
blob = NTLM_UTILS.make_ntlmssp_secblob_init(domain, name, ntlmssp_flags)
|
||||||
user,
|
|
||||||
pass,
|
|
||||||
workstation: name,
|
|
||||||
domain: domain,
|
|
||||||
flags: ntlmssp_flags
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
blob = @ntlm_client.init_context.serialize
|
|
||||||
|
|
||||||
native_data = ''
|
native_data = ''
|
||||||
native_data << self.native_os + "\x00"
|
native_data << self.native_os + "\x00"
|
||||||
|
@ -902,9 +892,37 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
# Save the temporary UserID for use in the next request
|
# Save the temporary UserID for use in the next request
|
||||||
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
temp_user_id = ack['Payload']['SMB'].v['UserID']
|
||||||
|
|
||||||
type3 = @ntlm_client.init_context([blob].pack('m'))
|
# Get default data
|
||||||
type3_blob = type3.serialize
|
blob_data = NTLM_UTILS.parse_ntlm_type_2_blob(blob)
|
||||||
self.signing_key = @ntlm_client.session_key
|
self.challenge_key = blob_data[:challenge_key]
|
||||||
|
server_ntlmssp_flags = blob_data[:server_ntlmssp_flags] #else should raise an error
|
||||||
|
#netbios name
|
||||||
|
self.default_name = blob_data[:default_name] || ''
|
||||||
|
#netbios domain
|
||||||
|
self.default_domain = blob_data[:default_domain] || ''
|
||||||
|
#dns name
|
||||||
|
self.dns_host_name = blob_data[:dns_host_name] || ''
|
||||||
|
#dns domain
|
||||||
|
self.dns_domain_name = blob_data[:dns_domain_name] || ''
|
||||||
|
#Client time
|
||||||
|
chall_MsvAvTimestamp = blob_data[:chall_MsvAvTimestamp] || ''
|
||||||
|
|
||||||
|
|
||||||
|
resp_lm, resp_ntlm, client_challenge, ntlm_cli_challenge = NTLM_UTILS.create_lm_ntlm_responses(user, pass, self.challenge_key, domain,
|
||||||
|
default_name, default_domain, dns_host_name,
|
||||||
|
dns_domain_name, chall_MsvAvTimestamp ,
|
||||||
|
self.spnopt, ntlm_options)
|
||||||
|
enc_session_key = ''
|
||||||
|
self.sequence_counter = 0
|
||||||
|
|
||||||
|
if self.require_signing
|
||||||
|
self.signing_key, enc_session_key, ntlmssp_flags = NTLM_UTILS.create_session_key(ntlmssp_flags, server_ntlmssp_flags, user, pass, domain,
|
||||||
|
self.challenge_key, client_challenge, ntlm_cli_challenge,
|
||||||
|
ntlm_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create the security blob data
|
||||||
|
blob = NTLM_UTILS.make_ntlmssp_secblob_auth(domain, name, user, resp_lm, resp_ntlm, enc_session_key, ntlmssp_flags)
|
||||||
|
|
||||||
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
pkt = CONST::SMB_SETUP_NTLMV2_PKT.make_struct
|
||||||
self.smb_defaults(pkt['Payload']['SMB'])
|
self.smb_defaults(pkt['Payload']['SMB'])
|
||||||
|
@ -926,8 +944,8 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
pkt['Payload'].v['VCNum'] = 1
|
pkt['Payload'].v['VCNum'] = 1
|
||||||
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
pkt['Payload'].v['Capabilities'] = 0x8000d05c
|
||||||
pkt['Payload'].v['SessionKey'] = self.session_id
|
pkt['Payload'].v['SessionKey'] = self.session_id
|
||||||
pkt['Payload'].v['SecurityBlobLen'] = type3_blob.length
|
pkt['Payload'].v['SecurityBlobLen'] = blob.length
|
||||||
pkt['Payload'].v['Payload'] = type3_blob + native_data
|
pkt['Payload'].v['Payload'] = blob + native_data
|
||||||
|
|
||||||
# NOTE: if do_recv is set to false, we cant reach here...
|
# NOTE: if do_recv is set to false, we cant reach here...
|
||||||
self.smb_send(pkt.to_s)
|
self.smb_send(pkt.to_s)
|
||||||
|
@ -1753,7 +1771,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
# Remove the NetBIOS header
|
# Remove the NetBIOS header
|
||||||
resp_rpkt.slice!(0, 4)
|
resp_rpkt.slice!(0, 4)
|
||||||
|
|
||||||
_resp_parm = resp_rpkt[poff, pcnt]
|
resp_parm = resp_rpkt[poff, pcnt]
|
||||||
resp_data = resp_rpkt[doff, dcnt]
|
resp_data = resp_rpkt[doff, dcnt]
|
||||||
return resp_data
|
return resp_data
|
||||||
|
|
||||||
|
@ -1779,7 +1797,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
# Remove the NetBIOS header
|
# Remove the NetBIOS header
|
||||||
resp_rpkt.slice!(0, 4)
|
resp_rpkt.slice!(0, 4)
|
||||||
|
|
||||||
_resp_parm = resp_rpkt[poff, pcnt]
|
resp_parm = resp_rpkt[poff, pcnt]
|
||||||
resp_data = resp_rpkt[doff, dcnt]
|
resp_data = resp_rpkt[doff, dcnt]
|
||||||
return resp_data
|
return resp_data
|
||||||
|
|
||||||
|
@ -1940,7 +1958,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
resp = find_next(last_search_id, last_offset, last_filename)
|
resp = find_next(last_search_id, last_offset, last_filename)
|
||||||
|
|
||||||
# Flip bit so response params will parse correctly
|
# Flip bit so response params will parse correctly
|
||||||
search_next = 1
|
search_next = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
files
|
files
|
||||||
|
@ -1955,7 +1973,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
260, # Level of interest
|
260, # Level of interest
|
||||||
resume_key, # Resume key from previous (Last name offset)
|
resume_key, # Resume key from previous (Last name offset)
|
||||||
6, # Close search if end of search
|
6, # Close search if end of search
|
||||||
].pack('vvvVv') +
|
].pack('vvvVv') +
|
||||||
last_filename.to_s + # Last filename returned from find_first or find_next
|
last_filename.to_s + # Last filename returned from find_first or find_next
|
||||||
"\x00" # Terminate the file name
|
"\x00" # Terminate the file name
|
||||||
|
|
||||||
|
@ -1988,7 +2006,7 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
search_path = "#{current_path}#{fname}\\"
|
search_path = "#{current_path}#{fname}\\"
|
||||||
file_search(search_path, regex, depth).each {|fn| files << fn }
|
file_search(search_path, regex, depth).each {|fn| files << fn }
|
||||||
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
rescue Rex::Proto::SMB::Exceptions::ErrorCode => e
|
||||||
|
|
||||||
# Ignore common errors related to permissions and non-files
|
# Ignore common errors related to permissions and non-files
|
||||||
if %W{
|
if %W{
|
||||||
STATUS_ACCESS_DENIED
|
STATUS_ACCESS_DENIED
|
||||||
|
@ -2012,9 +2030,9 @@ NTLM_UTILS = Rex::Proto::NTLM::Utils
|
||||||
|
|
||||||
# Creates a new directory on the mounted tree
|
# Creates a new directory on the mounted tree
|
||||||
def create_directory(name)
|
def create_directory(name)
|
||||||
|
files = { }
|
||||||
parm = [0].pack('V') + name + "\x00"
|
parm = [0].pack('V') + name + "\x00"
|
||||||
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
resp = trans2(CONST::TRANS2_CREATE_DIRECTORY, parm, '')
|
||||||
resp
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# public read/write methods
|
# public read/write methods
|
||||||
|
|
|
@ -75,8 +75,6 @@ Gem::Specification.new do |spec|
|
||||||
spec.add_runtime_dependency 'msgpack'
|
spec.add_runtime_dependency 'msgpack'
|
||||||
# get list of network interfaces, like eth* from OS.
|
# get list of network interfaces, like eth* from OS.
|
||||||
spec.add_runtime_dependency 'network_interface'
|
spec.add_runtime_dependency 'network_interface'
|
||||||
# NTLM authentication
|
|
||||||
spec.add_runtime_dependency 'rubyntlm'
|
|
||||||
# Needed by anemone crawler
|
# Needed by anemone crawler
|
||||||
spec.add_runtime_dependency 'nokogiri'
|
spec.add_runtime_dependency 'nokogiri'
|
||||||
# Needed by db.rb and Msf::Exploit::Capture
|
# Needed by db.rb and Msf::Exploit::Capture
|
||||||
|
|
|
@ -31,7 +31,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
def run_host(ip)
|
def run_host(ip)
|
||||||
|
|
||||||
if !mssql_login_datastore
|
if !mssql_login_datastore
|
||||||
print_error("Invalid SQL Server credentials")
|
print_error("#{rhost}:#{rport} - Invalid SQL Server credentials")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
login = create_credential_login(login_data)
|
login = create_credential_login(login_data)
|
||||||
|
|
||||||
tbl << [row[0], row[1]]
|
tbl << [row[0], row[1]]
|
||||||
print_good("Saving #{hashtype} = #{row[0]}:#{row[1]}")
|
print_good("#{rhost}:#{rport} - Saving #{hashtype} = #{row[0]}:#{row[1]}")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
|
is_sysadmin = mssql_query(mssql_is_sysadmin())[:rows][0][0]
|
||||||
|
|
||||||
if is_sysadmin == 0
|
if is_sysadmin == 0
|
||||||
print_error("The provided credentials do not have privileges to read the password hashes")
|
print_error("#{rhost}:#{rport} - The provided credentials do not have privileges to read the password hashes")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue