Fixes #379. Massive rewrite of the MSSQL mixin. This moves everything to TDS 7.0 instead of the old crusty protocol
git-svn-id: file:///home/svn/framework3/trunk@7178 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
8d9356f40d
commit
681e2c940c
|
@ -80,86 +80,197 @@ module Exploit::Remote::MSSQL
|
|||
return res
|
||||
end
|
||||
|
||||
#
|
||||
# Send and receive using TDS
|
||||
#
|
||||
def mssql_send_recv(req, timeout=15)
|
||||
sock.put(req)
|
||||
|
||||
# Read the 8 byte header to get the length and status
|
||||
# Read the length to get the data
|
||||
# If the status is 0, read another header and more data
|
||||
|
||||
done = false
|
||||
resp = ""
|
||||
|
||||
while(not done)
|
||||
head = sock.get_once(8, timeout)
|
||||
if(not (head and head.length == 8))
|
||||
return false
|
||||
end
|
||||
|
||||
# Is this the last buffer?
|
||||
if(head[1,1] == "\x01")
|
||||
done = true
|
||||
end
|
||||
|
||||
# Grab this block's length
|
||||
rlen = head[2,2].unpack('n')[0] - 8
|
||||
|
||||
while(rlen > 0)
|
||||
buff = sock.get_once(rlen, timeout)
|
||||
return if not buff
|
||||
resp << buff
|
||||
rlen -= buff.length
|
||||
end
|
||||
end
|
||||
|
||||
resp
|
||||
end
|
||||
|
||||
|
||||
#
|
||||
# Encrypt according to the TDS protocol (encode)
|
||||
#
|
||||
def mssql_tds_encrypt(pass)
|
||||
# Convert to unicode, swap 4 bits both ways, xor with 0xa5
|
||||
Rex::Text.to_unicode(pass).unpack('C*').map {|c| (((c & 0x0f) << 4) + ((c & 0xf0) >> 4)) ^ 0xa5 }.pack("C*")
|
||||
end
|
||||
|
||||
#
|
||||
# This method connects to the server over TCP and attempts
|
||||
# to authenticate with the supplied username and password
|
||||
# The global socket is used and left connected after auth
|
||||
#
|
||||
def mssql_login(user='sa', pass='')
|
||||
def mssql_login(user='sa', pass='', db='')
|
||||
|
||||
disconnect if self.sock
|
||||
connect
|
||||
|
||||
p_hdr =
|
||||
"\x02\x00\x02\x00\x00\x00\x02\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
pkt = ""
|
||||
idx = 0
|
||||
|
||||
p_pk2 =
|
||||
"\x30\x30\x30\x30\x30\x30\x61\x30\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x20\x18\x81\xb8\x2c\x08\x03"+
|
||||
"\x01\x06\x0a\x09\x01\x01\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x73\x71\x75\x65\x6c\x64\x61"+
|
||||
"\x20\x31\x2e\x30\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00"
|
||||
|
||||
p_pk3 =
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x04\x02\x00\x00\x4d\x53\x44"+
|
||||
"\x42\x4c\x49\x42\x00\x00\x00\x07\x06\x00\x00"+
|
||||
"\x00\x00\x0d\x11\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00"
|
||||
pkt << [
|
||||
0x00000000, # Dummy size
|
||||
0x71000001, # TDS Version
|
||||
0x00000000, # Size
|
||||
0x00000007, # Version
|
||||
rand(1024+1), # PID
|
||||
0x00000000, # ConnectionID
|
||||
0xe0, # Option Flags 1
|
||||
0x03, # Option Flags 2
|
||||
0x00, # SQL Type Flags
|
||||
0x00, # Reserved Flags
|
||||
0x00000000, # Time Zone
|
||||
0x00000000 # Collation
|
||||
].pack('VVVVVVCCCCVV')
|
||||
|
||||
|
||||
p_lang =
|
||||
"\x02\x01\x00\x47\x00\x00\x02\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+
|
||||
"\x00\x00\x00\x00\x00\x00\x30\x30\x30\x00\x00"+
|
||||
"\x00\x03\x00\x00\x00"
|
||||
cname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) )
|
||||
uname = Rex::Text.to_unicode( user )
|
||||
pname = mssql_tds_encrypt( pass )
|
||||
aname = Rex::Text.to_unicode( Rex::Text.rand_text_alpha(rand(8)+1) )
|
||||
sname = Rex::Text.to_unicode( rhost )
|
||||
dname = Rex::Text.to_unicode( db )
|
||||
|
||||
user = user.slice(0, 29)
|
||||
pass = pass.slice(0, 29)
|
||||
idx = pkt.size + 50 # lengths below
|
||||
|
||||
ulen = user.length.chr
|
||||
plen = pass.length.chr
|
||||
pkt << [idx, cname.length / 2].pack('vv')
|
||||
idx += cname.length
|
||||
|
||||
user << ("\x00" * (30-user.length))
|
||||
pass << ("\x00" * (30-pass.length))
|
||||
pkt << [idx, uname.length / 2].pack('vv')
|
||||
idx += uname.length
|
||||
|
||||
p_login = p_hdr + user + ulen + pass + plen
|
||||
p_login << p_pk2 + plen + pass + p_pk3
|
||||
pkt << [idx, pname.length / 2].pack('vv')
|
||||
idx += pname.length
|
||||
|
||||
sock.put(p_login)
|
||||
sock.put(p_lang)
|
||||
pkt << [idx, aname.length / 2].pack('vv')
|
||||
idx += aname.length
|
||||
|
||||
pkt << [idx, sname.length / 2].pack('vv')
|
||||
idx += sname.length
|
||||
|
||||
pkt << [0, 0].pack('vv')
|
||||
|
||||
pkt << [idx, aname.length / 2].pack('vv')
|
||||
idx += aname.length
|
||||
|
||||
pkt << [idx, 0].pack('vv')
|
||||
|
||||
pkt << [idx, dname.length / 2].pack('vv')
|
||||
idx += dname.length
|
||||
|
||||
# The total length has to be embedded twice more here
|
||||
pkt << [
|
||||
0,
|
||||
0,
|
||||
0x12345678,
|
||||
0x12345678
|
||||
].pack('vVVV')
|
||||
|
||||
pkt << cname
|
||||
pkt << uname
|
||||
pkt << pname
|
||||
pkt << aname
|
||||
pkt << sname
|
||||
pkt << aname
|
||||
pkt << dname
|
||||
|
||||
# Total packet length
|
||||
pkt[0,4] = [pkt.length].pack('V')
|
||||
|
||||
# Embedded packet lengths
|
||||
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
|
||||
|
||||
# Packet header and total length including header
|
||||
pkt = "\x10\x01" + [pkt.length + 8].pack('n') + [0].pack('n') + [1].pack('C') + "\x00" + pkt
|
||||
|
||||
resp = mssql_send_recv(pkt)
|
||||
|
||||
info = {:errors => []}
|
||||
info = mssql_parse_reply(resp,info)
|
||||
|
||||
info[:login_ack] ? true : false
|
||||
end
|
||||
|
||||
|
||||
=begin
|
||||
def mssql_login_obsolete(user='sa', pass='')
|
||||
|
||||
disconnect if self.sock
|
||||
connect
|
||||
|
||||
pkt = ''
|
||||
|
||||
host = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||
proc = Rex::Text.rand_text(rand(8)+1)
|
||||
appname = Rex::Text.rand_text_alpha(rand(12)+1)
|
||||
proname = Rex::Text.rand_text_alpha(rand(8)+1)
|
||||
provers = rand(100)
|
||||
srvname = ""
|
||||
rempass = ""
|
||||
lang = ""
|
||||
|
||||
pkt << [host, host.length].pack('Z30C')
|
||||
pkt << [user, user.length].pack('Z30C')
|
||||
pkt << [pass, pass.length].pack('Z30C')
|
||||
pkt << [proc, proc.length].pack('Z8C')
|
||||
pkt << Rex::Text.rand_text(6)
|
||||
pkt << [
|
||||
0x03, # Int2
|
||||
0x00, # Int4
|
||||
0x06, # Ascii
|
||||
0x05, # Fload
|
||||
0x00, # UseDB
|
||||
0x00, # DumpLoad
|
||||
0x00, # Interface
|
||||
0x00, # Type
|
||||
0x00 # SSPI required
|
||||
].pack('C*')
|
||||
pkt << [appname, appname.length].pack('Z30C')
|
||||
pkt << [srvname, srvname.length].pack('Z30C')
|
||||
pkt << [rempass, rempass.length].pack('Z255C')
|
||||
pkt << [0x04020000].pack('N')
|
||||
pkt << [proname, proname.length].pack('Z10C')
|
||||
pkt << [provers].pack('N')
|
||||
pkt << [lang, lang.length].pack('Z30C')
|
||||
pkt << [0x00].pack('C') # set lang
|
||||
pkt << [1024].pack('N') # pkt size
|
||||
pkt << Rex::Text.rand_text(rand(8)+1) # Padding
|
||||
|
||||
pkt = "\x02\x01" + [pkt.length + 8].pack('n') + [rand(0x100)].pack('n') + [rand(0x100)].pack('C') + "\x00" + pkt
|
||||
|
||||
sock.put(pkt)
|
||||
resp = sock.get_once
|
||||
|
||||
if (resp and resp.length > 10 and resp[8,1].unpack('C')[0] == 0xe3)
|
||||
|
@ -168,20 +279,21 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
return false
|
||||
end
|
||||
=end
|
||||
|
||||
def mssql_login_datastore
|
||||
mssql_login(datastore['MSSQL_USER'], datastore['MSSQL_PASS'])
|
||||
def mssql_login_datastore(db='')
|
||||
mssql_login(datastore['MSSQL_USER'], datastore['MSSQL_PASS'], db)
|
||||
end
|
||||
|
||||
def mssql_query(sql, doprint=false, opts={})
|
||||
info = { :sql => sql }
|
||||
opts[:timeout] ||= 15
|
||||
def mssql_query(sqla, doprint=false, opts={})
|
||||
info = { :sql => sqla }
|
||||
|
||||
opts[:timeout] ||= 15
|
||||
|
||||
sql = Rex::Text.to_unicode(sqla)
|
||||
pkt = "\x01\x01" + [sql.length + 8].pack('n') + [rand(0x100)].pack('n') + [rand(0x100)].pack('C') + "\x00" + sql
|
||||
sock.put(pkt)
|
||||
|
||||
resp = sock.get(opts[:timeout])
|
||||
|
||||
resp = mssql_send_recv(pkt, opts[:timeout])
|
||||
mssql_parse_reply(resp, info)
|
||||
mssql_print_reply(info) if doprint
|
||||
info
|
||||
|
@ -191,12 +303,6 @@ module Exploit::Remote::MSSQL
|
|||
mssql_query("xp_cmdshell '#{cmd}'", doprint, opts)
|
||||
end
|
||||
|
||||
def mssql_parse_header(header)
|
||||
type, status, size, chan, pkt, window = header.unpack('CCnnCC')
|
||||
return [status, size, pkt]
|
||||
end
|
||||
|
||||
|
||||
def mssql_print_reply(info)
|
||||
|
||||
print_status("SQL Query: #{info[:sql]}")
|
||||
|
@ -227,275 +333,187 @@ module Exploit::Remote::MSSQL
|
|||
end
|
||||
end
|
||||
|
||||
def mssql_parse_reply(resp, info)
|
||||
info[:errors] = []
|
||||
|
||||
data = ""
|
||||
def mssql_parse_tds_reply(data, info)
|
||||
info[:errors] ||= []
|
||||
info[:colinfos] ||= []
|
||||
info[:colnames] ||= []
|
||||
|
||||
status = 0
|
||||
while status == 0
|
||||
break if not resp
|
||||
status, size, pkt = mssql_parse_header(resp.slice!(0,8))
|
||||
break if not (status and size and pkt)
|
||||
data << resp.slice!(0,(size-8))
|
||||
# Parse out the columns
|
||||
cols = data.slice!(0,2).unpack('v')[0]
|
||||
0.upto(cols-1) do |col_idx|
|
||||
col = {}
|
||||
info[:colinfos][col_idx] = col
|
||||
|
||||
col[:utype] = 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]
|
||||
|
||||
case col[:type]
|
||||
when 48
|
||||
col[:id] = :tinyint
|
||||
|
||||
when 52
|
||||
col[:id] = :smallint
|
||||
|
||||
when 56
|
||||
col[:id] = :rawint
|
||||
|
||||
when 61
|
||||
col[:id] = :datetime
|
||||
|
||||
when 34
|
||||
col[:id] = :image
|
||||
col[:max_size] = data.slice!(0,4).unpack('V')[0]
|
||||
col[:value_length] = data.slice!(0,2).unpack('v')[0]
|
||||
col[:value] = data.slice!(0, col[:value_length] * 2).gsub("\x00", '')
|
||||
|
||||
when 36
|
||||
col[:id] = :string
|
||||
|
||||
when 38
|
||||
col[:id] = :int
|
||||
col[:int_size] = data.slice!(0,1).unpack('C')[0]
|
||||
|
||||
when 127
|
||||
col[:id] = :bigint
|
||||
|
||||
when 165
|
||||
col[:id] = :hex
|
||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||
|
||||
when 173
|
||||
col[:id] = :hex # binary(2)
|
||||
col[:max_size] = data.slice!(0,2).unpack('v')[0]
|
||||
|
||||
when 231,175,167,173,239
|
||||
col[:id] = :string
|
||||
col[:max_size] = 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[:charset_id] = data.slice!(0,1).unpack('C')[0]
|
||||
|
||||
else
|
||||
col[:id] = :unknown
|
||||
end
|
||||
|
||||
col[:msg_len] = data.slice!(0,1).unpack('C')[0]
|
||||
|
||||
if(col[:msg_len] and col[:msg_len] > 0)
|
||||
col[:name] = data.slice!(0, col[:msg_len] * 2).gsub("\x00", '')
|
||||
end
|
||||
info[:colnames] << (col[:name] || 'NULL')
|
||||
end
|
||||
end
|
||||
|
||||
def mssql_parse_reply(data, info)
|
||||
info[:errors] = []
|
||||
|
||||
until data.empty?
|
||||
token = data.slice!(0,1).unpack('C')[0]
|
||||
case token
|
||||
when 0xa0
|
||||
mssql_parse_column_name(data, info)
|
||||
when 0xa1
|
||||
mssql_parse_column_info(data, info)
|
||||
when 0x81
|
||||
mssql_parse_tds_reply(data, info)
|
||||
when 0xd1
|
||||
mssql_parse_row(data, info)
|
||||
mssql_parse_tds_row(data, info)
|
||||
when 0xe3
|
||||
mssql_parse_env(data, info)
|
||||
when 0x79
|
||||
mssql_parse_ret(data, info)
|
||||
when 0xfd, 0xfe, 0xff
|
||||
mssql_parse_done(data, info)
|
||||
when 0xad
|
||||
mssql_parse_login_ack(data, info)
|
||||
when 0xab
|
||||
mssql_parse_info(data, info)
|
||||
when 0xaa
|
||||
mssql_parse_error(data, info)
|
||||
when nil
|
||||
break
|
||||
else
|
||||
# info[:errors] << "unsupported token: #{token}"
|
||||
info[:errors] << "unsupported token: #{token}"
|
||||
end
|
||||
end
|
||||
|
||||
info
|
||||
end
|
||||
|
||||
def mssql_parse_column_name(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
str = data.slice!(0,len)
|
||||
info[:colnames] ||= []
|
||||
while(not str.empty?)
|
||||
len = str.slice!(0,1).unpack('C')[0]
|
||||
col = len == 0 ? 'NULL' : str.slice!(0,len)
|
||||
info[:colnames] << col
|
||||
end
|
||||
info
|
||||
end
|
||||
|
||||
def mssql_parse_column_info(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
str = data.slice!(0,len)
|
||||
|
||||
info[:colinfos] ||= []
|
||||
|
||||
idx = -1
|
||||
|
||||
while(not str.empty?)
|
||||
|
||||
idx += 1
|
||||
|
||||
### int
|
||||
if(
|
||||
str[0,5] == "\x07\x00\x20\x00\x38" or
|
||||
str[0,5] == "\x07\x00\x08\x00\x38" or # int
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:int, 4]
|
||||
next
|
||||
end
|
||||
|
||||
### smallint
|
||||
if(
|
||||
str[0,5] == "\x0d\x00\x09\x00\x26" or
|
||||
str[0,5] == "\x0d\x00\x21\x00\x26" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:smallint, str.slice!(0,1).unpack('C')[0]]
|
||||
next
|
||||
end
|
||||
|
||||
### smallint2
|
||||
if(
|
||||
str[0,5] == "\x06\x00\x08\x00\x34" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:smallint2, 2]
|
||||
next
|
||||
end
|
||||
|
||||
### image
|
||||
if(
|
||||
str[0,9] == "\x14\x00\x21\x00\x22\x00\x10\x00\x00" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,9)
|
||||
len = str.slice!(0,2).unpack('v')[0]
|
||||
nam = str.slice!(0,len)
|
||||
info[:colinfos] << [:image, 0, nam]
|
||||
next
|
||||
end
|
||||
|
||||
### tinyint
|
||||
if(
|
||||
str[0,5] == "\x05\x00\x08\x00\x30" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:tinyint, 1]
|
||||
next
|
||||
end
|
||||
|
||||
|
||||
### longint
|
||||
if(
|
||||
str[0,6] == "\x19\x00\x20\x00\x6c\x11" or
|
||||
str[0,6] == "\x19\x00\x21\x00\x6c\x11" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,6)
|
||||
info[:colinfos] << [:long, str.slice!(0,2).unpack("v")[0]]
|
||||
next
|
||||
end
|
||||
|
||||
### hex
|
||||
if(str[0,5] == "\x04\x00\x20\x00\x25")
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:hex, str.slice!(0,1).unpack('C')[0]]
|
||||
next
|
||||
end
|
||||
|
||||
### string
|
||||
if(
|
||||
str[0,5] == "\x02\x00\x21\x00\x27" or # varchar
|
||||
str[0,5] == "\x02\x00\x08\x00\x27" or
|
||||
str[0,5] == "\x02\x00\x01\x00\x27" or
|
||||
str[0,5] == "\x12\x00\x08\x00\x27" or
|
||||
str[0,5] == "\x12\x00\x09\x00\x27" or
|
||||
str[0,5] == "\x04\x00\x09\x00\x25" or # varbinary
|
||||
str[0,5] == "\x04\x00\x21\x00\x25" or
|
||||
str[0,5] == "\x04\x00\x09\x00\x25" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:string, str.slice!(0,1).unpack('C')[0]]
|
||||
next
|
||||
end
|
||||
|
||||
### char(x)
|
||||
if(
|
||||
str[0,5] == "\x01\x00\x08\x00\x2f" or
|
||||
str[0,5] == "\x02\x00\x09\x00\x27" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:string, str.slice!(0,1).unpack("C")[0]]
|
||||
next
|
||||
end
|
||||
|
||||
### datetime
|
||||
if(
|
||||
str[0,5] == "\x0c\x00\x08\x00\x3d" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,5)
|
||||
info[:colinfos] << [:datetime, 8]
|
||||
next
|
||||
end
|
||||
|
||||
### datetime2
|
||||
if(
|
||||
str[0,6] == "\x0f\x00\x21\x00\x6f\x08" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,6)
|
||||
info[:colinfos] << [:datetime2, 8]
|
||||
next
|
||||
end
|
||||
|
||||
### binary
|
||||
if(
|
||||
str[0,6] == "\x04\x00\x21\x00\x25\x06" or
|
||||
false
|
||||
)
|
||||
str.slice!(0,6)
|
||||
info[:colinfos] << [:binary]
|
||||
next
|
||||
end
|
||||
|
||||
info[:errors] << "unknown column type: #{info[:colnames][idx]} == #{str.unpack("H*")[0]}"
|
||||
info[:colinfos] << [:unknown, 6]
|
||||
break
|
||||
end
|
||||
info
|
||||
end
|
||||
def mssql_parse_row(data, info)
|
||||
|
||||
def mssql_parse_tds_row(data, info)
|
||||
info[:rows] ||= []
|
||||
row = []
|
||||
|
||||
info[:colinfos].each do |col|
|
||||
case col[0]
|
||||
when :int
|
||||
row << data.slice!(0,4).unpack('V')[0]
|
||||
|
||||
when :smallint
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
raw = data.slice!(0,len)
|
||||
row << raw.unpack('v')[0]
|
||||
|
||||
when :smallint2
|
||||
row << data.slice!(0,2).unpack('v')[0]
|
||||
|
||||
when :tinyint
|
||||
row << data.slice!(0,1).unpack('C')[0]
|
||||
|
||||
when :long
|
||||
|
||||
# XXX: completely made up bignum parsing...
|
||||
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
raw = data.slice!(0,len)
|
||||
sum = 0
|
||||
bit = raw.unpack("C*").reverse
|
||||
bit.each_index do |idx|
|
||||
base = (256 ** (bit.length - idx - 1))
|
||||
if (base > 0)
|
||||
sum += base * bit[idx]
|
||||
else
|
||||
sum += bit[idx]
|
||||
end
|
||||
end
|
||||
|
||||
row << "?#{raw.unpack("H*")[0]}"
|
||||
if(data.length == 0)
|
||||
row << "<EMPTY>"
|
||||
next
|
||||
end
|
||||
|
||||
case col[:id]
|
||||
when :hex
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
row << ((len > 0) ? data.slice!(0,len) : '')
|
||||
str = ""
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
str << data.slice!(0,len)
|
||||
end
|
||||
row << str.unpack("H*")[0]
|
||||
|
||||
when :string
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
row << ((len > 0) ? data.slice!(0,len) : '')
|
||||
str = ""
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
if(len > 0 and len < 65535)
|
||||
str << data.slice!(0,len)
|
||||
end
|
||||
row << str.gsub("\x00", '')
|
||||
|
||||
when :datetime
|
||||
# XXX: convert to unix time
|
||||
row << data.slice!(0,8).unpack("H*")[0]
|
||||
|
||||
when :datetime2
|
||||
# XXX: convert to unix time
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
row << data.slice!(0,len).unpack("H*")[0]
|
||||
when :rawint
|
||||
row << data.slice!(0,4).unpack('V')[0]
|
||||
|
||||
when :bigint
|
||||
row << data.slice!(0,8).unpack("H*")[0]
|
||||
|
||||
when :smallint
|
||||
row << data.slice!(0, 2).unpack("v")[0]
|
||||
|
||||
when :smallint3
|
||||
row << [data.slice!(0, 3)].pack("Z4").unpack("V")[0]
|
||||
|
||||
when :tinyint
|
||||
row << data.slice!(0, 1).unpack("C")[0]
|
||||
|
||||
when :image
|
||||
str = ''
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
row << ((len > 0) ? data.slice!(0,len).unpack("H*")[0] : '')
|
||||
str = data.slice!(0,len) if (len and len > 0)
|
||||
row << str.unpack("H*")[0]
|
||||
|
||||
when :binary
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
row << ((len > 0) ? data.slice!(0,len).unpack("H*")[0] : '')
|
||||
when :int
|
||||
len = data.slice!(0, 1).unpack("C")[0]
|
||||
raw = data.slice!(0, len) if (len and len > 0)
|
||||
|
||||
when :unknown
|
||||
len = data.slice!(0,1).unpack('C')[0]
|
||||
row << "????-" + data.slice!(0,len) if len
|
||||
case len
|
||||
when 0,255
|
||||
row << ''
|
||||
when 1
|
||||
row << raw.unpack("C")[0]
|
||||
when 2
|
||||
row << raw.unpack('v')[0]
|
||||
when 4
|
||||
row << raw.unpack('V')[0]
|
||||
when 5
|
||||
row << raw.unpack('V')[0] # XXX: missing high byte
|
||||
when 8
|
||||
row << raw.unpack('VV')[0] # XXX: missing high dword
|
||||
else
|
||||
info[:errors] << "invalid integer size: #{len} #{data[0,16].unpack("H*")[0]}"
|
||||
end
|
||||
else
|
||||
info[:errors] << "unknown column type: #{col.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
info[:rows] << row
|
||||
info
|
||||
end
|
||||
|
@ -507,7 +525,7 @@ module Exploit::Remote::MSSQL
|
|||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
@ -517,10 +535,49 @@ module Exploit::Remote::MSSQL
|
|||
buff = data.slice!(0,len)
|
||||
|
||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||
emsg = buff.slice!(0,elen)
|
||||
emsg = buff.slice!(0,elen * 2)
|
||||
emsg.gsub!("\x00", '')
|
||||
|
||||
info[:errors] << "SQL Server Error ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||
info
|
||||
end
|
||||
|
||||
def mssql_parse_env(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
type = buff.slice!(0,1).unpack('C')[0]
|
||||
|
||||
nval = ''
|
||||
nlen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||
nval = buff.slice!(0,nlen*2).gsub("\x00", '') if nlen > 0
|
||||
|
||||
oval = ''
|
||||
olen = buff.slice!(0,1).unpack('C')[0] || 0
|
||||
oval = buff.slice!(0,olen*2).gsub("\x00", '') if olen > 0
|
||||
|
||||
info[:envs] ||= []
|
||||
info[:envs] << { :type => type, :old => oval, :new => nval }
|
||||
info
|
||||
end
|
||||
|
||||
def mssql_parse_info(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
|
||||
errno,state,sev,elen = buff.slice!(0,8).unpack('VCCv')
|
||||
emsg = buff.slice!(0,elen * 2)
|
||||
emsg.gsub!("\x00", '')
|
||||
|
||||
info[:infos]||= []
|
||||
info[:infos] << "SQL Server Info ##{errno} (State:#{state} Severity:#{sev}): #{emsg}"
|
||||
info
|
||||
end
|
||||
|
||||
def mssql_parse_login_ack(data, info)
|
||||
len = data.slice!(0,2).unpack('v')[0]
|
||||
buff = data.slice!(0,len)
|
||||
info[:login_ack] = true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue