1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-15 04:09:01 +00:00

Added or enhanced support for the following data types:

SQLTEXT       = 0x23 - text
GUIDTYPE      = 0x24 - uniqueidentifier
NTEXTTYPE     = 0x63 - unicode text (ntext)
BITNTYPE      = 0x68 - boolean
DECIMALNTYPE  = 0x6A - decimal
NUMERICNTYPE  = 0x6C - numeric
FLTNTYPE      = 0x6D - float/real/double
MONEYNTYPE    = 0x6E - money / smallmoeny
BIGBINARYTYPE = 0xAD - binary
BIGCHARTYPE   = 0xAF - char
SQLNCHAR      = 0xEF - unicode char (nchar)

Added detection and handling of null values when processing query responses from the server.
Added DoneProc response token support
Reordered ColumnData and ColumnInfo parsers by data type code to make updates easier.
This commit is contained in:
tomsellers
2012-08-24 10:32:44 +00:00
parent fd32aec639
commit 5ba2007d69
2 changed files with 469 additions and 91 deletions

View File

@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*-
o [NSE] Updated mssql.lua library to support additional data types, enchanced
some of the existing data types, added the DoneProc response token, and
reordered code for maintainability. [Tom Sellers]
o [NSE] Added http-slowloris-check script which checks if the server is vulnerable
to a Slowloris DoS attack in a safe way. [Aleksandar Nikolic]

View File

@@ -130,6 +130,11 @@ _ENV = stdnse.module("mssql", stdnse.seeall)
-- * added support for integrated NTLMv1 authentication
--
-- (Patrik Karlsson, Chris Woodbury)
-- Revised 08/19/2012 - v0.6 - added multiple data types
-- * added detection and handling of null values when processing query responses from the server
-- * added DoneProc response token support
--
-- (Tom Sellers)
local HAVE_SSL, openssl = pcall(require, "openssl")
@@ -684,20 +689,32 @@ TokenType =
EnvironmentChange = 0xE3,
NTLMSSP_CHALLENGE = 0xed,
Done = 0xFD,
DoneProc = 0xFE,
DoneInProc = 0xFF,
}
-- SQL Server/Sybase data types
DataTypes =
{
SQLTEXT = 0x23,
GUIDTYPE = 0x24,
SYBINTN = 0x26,
SYBINT2 = 0x34,
SYBINT4 = 0x38,
SYBDATETIME = 0x3D,
NTEXTTYPE = 0x63,
BITNTYPE = 0x68,
DECIMALNTYPE = 0x6A,
NUMERICNTYPE = 0x6C,
FLTNTYPE = 0x6D,
MONEYNTYPE = 0x6E,
SYBDATETIMN = 0x6F,
XSYBVARBINARY = 0xA5,
XSYBVARCHAR = 0xA7,
BIGBINARYTYPE = 0xAD,
BIGCHARTYPE = 0xAF,
XSYBNVARCHAR = 0xE7,
SQLNCHAR = 0xEF,
}
-- SQL Server login error codes
@@ -728,20 +745,25 @@ ColumnInfo =
Parse =
{
[DataTypes.XSYBNVARCHAR] = function( data, pos )
[DataTypes.SQLTEXT] = function( data, pos )
local colinfo = {}
local tmp
pos, colinfo.lts, colinfo.codepage, colinfo.flags, colinfo.charset,
colinfo.msglen = bin.unpack("<SSSCC", data, pos )
pos, colinfo.unknown, colinfo.codepage, colinfo.flags, colinfo.charset = bin.unpack("<ISSC", data, pos )
pos, colinfo.tablenamelen = bin.unpack("s", data, pos )
pos, colinfo.tablename = bin.unpack("A" .. (colinfo.tablenamelen * 2), data, pos)
pos, colinfo.msglen = bin.unpack("<C", data, pos )
pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos)
colinfo.text = Util.FromWideChar(tmp)
return pos, colinfo
end,
[DataTypes.SYBINT2] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
[DataTypes.GUIDTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.SYBINTN] = function( data, pos )
@@ -755,19 +777,12 @@ ColumnInfo =
return pos, colinfo
end,
[DataTypes.SYBINT4] = function( data, pos )
[DataTypes.SYBINT2] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
end,
[DataTypes.XSYBVARBINARY] = function( data, pos )
local colinfo = {}
local tmp
pos, colinfo.lts, colinfo.msglen = bin.unpack("<SC", data, pos)
pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
colinfo.text = Util.FromWideChar(tmp)
return pos, colinfo
[DataTypes.SYBINT4] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBDATETIME](data, pos)
end,
[DataTypes.SYBDATETIME] = function( data, pos )
@@ -781,14 +796,81 @@ ColumnInfo =
return pos, colinfo
end,
[DataTypes.NTEXTTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SQLTEXT](data, pos)
end,
[DataTypes.BITNTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.DECIMALNTYPE] = function( data, pos )
local colinfo = {}
local tmp
pos, colinfo.unknown, colinfo.precision, colinfo.scale = bin.unpack("<CCC", data, pos)
pos, colinfo.msglen = bin.unpack("<C",data,pos)
pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
colinfo.text = Util.FromWideChar(tmp)
return pos, colinfo
end,
[DataTypes.NUMERICNTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.DECIMALNTYPE](data, pos)
end,
[DataTypes.FLTNTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.MONEYNTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.SYBDATETIMN] = function( data, pos )
return ColumnInfo.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.XSYBVARBINARY] = function( data, pos )
local colinfo = {}
local tmp
pos, colinfo.lts, colinfo.msglen = bin.unpack("<SC", data, pos)
pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos )
colinfo.text = Util.FromWideChar(tmp)
return pos, colinfo
end,
[DataTypes.XSYBVARCHAR] = function( data, pos )
return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
end,
[DataTypes.BIGBINARYTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.XSYBVARBINARY](data, pos)
end,
[DataTypes.BIGCHARTYPE] = function( data, pos )
return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
end,
[DataTypes.XSYBNVARCHAR] = function( data, pos )
local colinfo = {}
local tmp
pos, colinfo.lts, colinfo.codepage, colinfo.flags, colinfo.charset,
colinfo.msglen = bin.unpack("<SSSCC", data, pos )
pos, tmp = bin.unpack("A" .. (colinfo.msglen * 2), data, pos)
colinfo.text = Util.FromWideChar(tmp)
return pos, colinfo
end,
[DataTypes.SQLNCHAR] = function( data, pos )
return ColumnInfo.Parse[DataTypes.XSYBNVARCHAR](data, pos)
end,
}
}
@@ -798,38 +880,84 @@ ColumnData =
{
Parse = {
[DataTypes.XSYBNVARCHAR] = function( data, pos )
local size, coldata
[DataTypes.SQLTEXT] = function( data, pos )
local len, coldata
pos, size = bin.unpack( "<S", data, pos )
pos, coldata = bin.unpack( "A"..size, data, pos )
-- The first len value is the size of the meta data block
-- for non-null values this seems to be 0x10 / 16 bytes
pos, len = bin.unpack( "<C", data, pos )
return pos, Util.FromWideChar(coldata)
end,
if ( len == 0 ) then
return pos, 'Null'
end
[DataTypes.XSYBVARCHAR] = function( data, pos )
local size, coldata
-- Skip over the text update time and date values, we don't need them
-- We may come back add parsing for this information.
pos = pos + len
pos, size = bin.unpack( "<S", data, pos )
pos, coldata = bin.unpack( "A"..size, data, pos )
-- skip a label, should be 'dummyTS'
pos = pos + 8
-- extract the actual data
pos, len = bin.unpack( "<I", data, pos )
pos, coldata = bin.unpack( "A"..len, data, pos )
return pos, coldata
end,
[DataTypes.XSYBVARBINARY] = function( data, pos )
local coldata, size
[DataTypes.GUIDTYPE] = function( data, pos )
local len, coldata, index, nextdata
local hex = {}
pos, len = bin.unpack("C", data, pos)
pos, size = bin.unpack( "<S", data, pos )
pos, coldata = bin.unpack( "A"..size, data, pos )
if ( len == 0 ) then
return pos, 'Null'
return pos, "0x" .. select(2, bin.unpack("H"..coldata:len(), coldata ) )
elseif ( len == 16 ) then
-- Return the first 8 bytes
for index=1, 8 do
pos, hex[index] = bin.unpack("H", data, pos)
end
-- reorder the bytes
coldata = hex[4] .. hex[3] .. hex[2] .. hex[1]
coldata = coldata .. '-' .. hex[6] .. hex[5]
coldata = coldata .. '-' .. hex[8] .. hex[7]
pos, nextdata = bin.unpack("H2", data, pos)
coldata = coldata .. '-' .. nextdata
pos, nextdata = bin.unpack("H6", data, pos)
coldata = coldata .. '-' .. nextdata
else
stdnse.print_debug("Unhandled length (%d) for GUIDTYPE", len)
return pos + len, 'Unsupported Data'
end
return pos, coldata
end,
[DataTypes.SYBINT4] = function( data, pos )
local num
pos, num = bin.unpack("<I", data, pos)
[DataTypes.SYBINTN] = function( data, pos )
local len, num
pos, len = bin.unpack("C", data, pos)
return pos, num
if ( len == 0 ) then
return pos, 'Null'
elseif ( len == 1 ) then
return bin.unpack("C", data, pos)
elseif ( len == 2 ) then
return bin.unpack("<s", data, pos)
elseif ( len == 4 ) then
return bin.unpack("<i", data, pos)
elseif ( len == 8 ) then
return bin.unpack("<l", data, pos)
else
return -1, ("Unhandled length (%d) for SYBINTN"):format(len)
end
return -1, "Error"
end,
[DataTypes.SYBINT2] = function( data, pos )
@@ -839,38 +967,264 @@ ColumnData =
return pos, num
end,
[DataTypes.SYBINTN] = function( data, pos )
local len, num
[DataTypes.SYBINT4] = function( data, pos )
local num
pos, num = bin.unpack("<I", data, pos)
return pos, num
end,
[DataTypes.SYBDATETIME] = function( data, pos )
local hi, lo, result_seconds, result
local tds_epoch, system_epoch, tds_offset_seconds
pos, hi, lo = bin.unpack("<iI", data, pos)
tds_epoch = os.time( {year = 1900, month = 1, day = 1, hour = 00, min = 00, sec = 00, isdst = nil} )
-- determine the offset between the tds_epoch and the local system epoch
system_epoch = os.time( os.date("*t", 0))
tds_offset_seconds = os.difftime(tds_epoch,system_epoch)
result_seconds = (hi*24*60*60) + (lo/300)
result = os.date("!%b %d, %Y %H:%M:%S", tds_offset_seconds + result_seconds )
return pos, result
end,
[DataTypes.NTEXTTYPE] = function( data, pos )
local len, coldata
-- The first len value is the size of the meta data block
pos, len = bin.unpack( "<C", data, pos )
if ( len == 0 ) then
return pos, 'Null'
end
-- Skip over the text update time and date values, we don't need them
-- We may come back add parsing for this information.
pos = pos + len
-- skip a label, should be 'dummyTS'
pos = pos + 8
-- extract the actual data
pos, len = bin.unpack( "<I", data, pos )
pos, coldata = bin.unpack( "A"..len, data, pos )
return pos, Util.FromWideChar(coldata)
end,
[DataTypes.BITNTYPE] = function( data, pos )
return ColumnData.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.DECIMALNTYPE] = function( precision, scale, data, pos )
local len, sign, format_string, coldata
pos, len = bin.unpack("<C", data, pos)
if ( len == 0 ) then
return pos, 'Null'
end
pos, sign = bin.unpack("<C", data, pos)
-- subtract 1 from data len to account for sign byte
len = len - 1
if ( len == 2 ) then
pos, coldata = bin.unpack("<S", data, pos)
elseif ( len == 4 ) then
pos, coldata = bin.unpack("<I", data, pos)
elseif ( len == 8 ) then
pos, coldata = bin.unpack("<L", data, pos)
else
stdnse.print_debug("Unhandled length (%d) for DECIMALNTYPE", len)
return pos + len, 'Unsupported Data'
end
if ( sign == 0 ) then
coldata = coldata * -1
end
coldata = coldata * (10^-scale)
-- format the return information to reduce truncation by lua
format_string = string.format("%%.%if", scale)
coldata = string.format(format_string,coldata)
return pos, coldata
end,
[DataTypes.NUMERICNTYPE] = function( precision, scale, data, pos )
return ColumnData.Parse[DataTypes.DECIMALNTYPE]( precision, scale, data, pos )
end,
[DataTypes.SYBDATETIME] = function( data, pos )
local hi, lo, result_seconds, result
local tds_epoch, system_epoch, tds_offset_seconds
pos, hi, lo = bin.unpack("<iI", data, pos)
tds_epoch = os.time( {year = 1900, month = 1, day = 1, hour = 00, min = 00, sec = 00, isdst = nil} )
-- determine the offset between the tds_epoch and the local system epoch
system_epoch = os.time( os.date("*t", 0))
tds_offset_seconds = os.difftime(tds_epoch,system_epoch)
result_seconds = (hi*24*60*60) + (lo/300)
result = os.date("!%b %d, %Y %H:%M:%S", tds_offset_seconds + result_seconds )
return pos, result
end,
[DataTypes.BITNTYPE] = function( data, pos )
return ColumnData.Parse[DataTypes.SYBINTN](data, pos)
end,
[DataTypes.NTEXTTYPE] = function( data, pos )
local len, coldata
-- The first len value is the size of the meta data block
pos, len = bin.unpack( "<C", data, pos )
if ( len == 0 ) then
return pos, 'Null'
end
-- Skip over the text update time and date values, we don't need them
-- We may come back add parsing for this information.
pos = pos + len
-- skip a label, should be 'dummyTS'
pos = pos + 8
-- extract the actual data
pos, len = bin.unpack( "<I", data, pos )
pos, coldata = bin.unpack( "A"..len, data, pos )
return pos, Util.FromWideChar(coldata)
end,
[DataTypes.FLTNTYPE] = function( data, pos )
local len, coldata
pos, len = bin.unpack("<C", data, pos)
if ( len == 0 ) then
return pos, 'Null'
elseif ( len == 4 ) then
pos, coldata = bin.unpack("f", data, pos)
elseif ( len == 8 ) then
pos, coldata = bin.unpack("<d", data, pos)
end
return pos, coldata
end,
[DataTypes.MONEYNTYPE] = function( data, pos )
local len, value, coldata, hi, lo
pos, len = bin.unpack("C", data, pos)
if ( len == 1 ) then
return bin.unpack("C", data, pos)
elseif ( len == 2 ) then
return bin.unpack("<S", data, pos)
if ( len == 0 ) then
return pos, 'Null'
elseif ( len == 4 ) then
return bin.unpack("<I", data, pos)
--type smallmoney
pos, value = bin.unpack("<i", data, pos)
elseif ( len == 8 ) then
return bin.unpack("<L", data, pos)
-- type money
pos, hi,lo = bin.unpack("<II", data, pos)
value = ( hi * 4294967296 ) + lo
else
return -1, ("Unhandled length (%d) for SYBINTN"):format(len)
return -1, ("Unhandled length (%d) for MONEYNTYPE"):format(len)
end
-- the datatype allows for 4 decimal places after the period to support various currency types.
-- forcing to string to avoid truncation
coldata = string.format("%.4f",value/10000)
return pos, coldata
end,
[DataTypes.SYBDATETIMN] = function( data, pos )
local len, coldata
pos, len = bin.unpack( "<C", data, pos )
if ( len == 0 ) then
return pos, 'Null'
elseif ( len == 4 ) then
-- format is smalldatetime
local days, mins
pos, days, mins = bin.unpack("<SS", data, pos)
tds_epoch = os.time( {year = 1900, month = 1, day = 1, hour = 00, min = 00, sec = 00, isdst = nil} )
-- determine the offset between the tds_epoch and the local system epoch
system_epoch = os.time( os.date("*t", 0))
tds_offset_seconds = os.difftime(tds_epoch,system_epoch)
result_seconds = (days*24*60*60) + (mins*60)
coldata = os.date("!%b %d, %Y %H:%M:%S", tds_offset_seconds + result_seconds )
return pos,coldata
elseif ( len == 8 ) then
-- format is datetime
return ColumnData.Parse[DataTypes.SYBDATETIME](data, pos)
else
return -1, ("Unhandled length (%d) for SYBDATETIMN"):format(len)
end
end,
[DataTypes.XSYBVARBINARY] = function( data, pos )
local len, coldata
pos, len = bin.unpack( "<S", data, pos )
if ( len == 65535 ) then
return pos, 'Null'
else
pos, coldata = bin.unpack( "A"..len, data, pos )
return pos, "0x" .. select(2, bin.unpack("H"..coldata:len(), coldata ) )
end
return -1, "Error"
end,
[DataTypes.SYBDATETIME] = function( data, pos )
local hi, lo, dt, result
pos, hi, lo = bin.unpack("<II", data, pos)
[DataTypes.XSYBVARCHAR] = function( data, pos )
local len, coldata
-- CET 01/01/1900
dt = -2208996000
result = os.date("%x %X", dt + (hi*24*60*60) + (lo/300) )
pos, len = bin.unpack( "<S", data, pos )
if ( len == 65535 ) then
return pos, 'Null'
end
return pos, result
pos, coldata = bin.unpack( "A"..len, data, pos )
return pos, coldata
end,
[DataTypes.SYBDATETIMN] = function( data, pos )
return ColumnData.Parse[DataTypes.SYBINTN]( data, pos )
[DataTypes.BIGBINARYTYPE] = function( data, pos )
return ColumnData.Parse[DataTypes.XSYBVARBINARY](data, pos)
end,
[DataTypes.BIGCHARTYPE] = function( data, pos )
return ColumnData.Parse[DataTypes.XSYBVARCHAR](data, pos)
end,
[DataTypes.XSYBNVARCHAR] = function( data, pos )
local len, coldata
pos, len = bin.unpack( "<S", data, pos )
if ( len == 65535 ) then
return pos, 'Null'
end
pos, coldata = bin.unpack( "A"..len, data, pos )
return pos, Util.FromWideChar(coldata)
end,
[DataTypes.SQLNCHAR] = function( data, pos )
return ColumnData.Parse[DataTypes.XSYBNVARCHAR](data, pos)
end,
}
@@ -968,6 +1322,21 @@ Token =
return pos, token
end,
--- Parses a DoneProc token recieved after executing a SP
--
-- @param data string containing "raw" data
-- @param pos number containing offset into data
-- @return pos number containing new offset after parse
-- @return token table containing token specific fields
[TokenType.DoneProc] = function( data, pos )
local token
pos, token = Token.Parse[TokenType.Done]( data, pos )
token.type = TokenType.DoneProc
return pos, token
end,
--- Parses a DoneInProc token recieved after executing a SP
--
-- @param data string containing "raw" data
@@ -1035,7 +1404,6 @@ Token =
end
pos, colinfo = ColumnInfo.Parse[ttype]( data, pos )
colinfo.usertype = usertype
colinfo.flags = flags
colinfo.type = ttype
@@ -2423,7 +2791,13 @@ Helper =
local val
if ( ColumnData.Parse[colinfo[i].type] ) then
if not ( colinfo[i].type == 106 or colinfo[i].type == 108) then
pos, val = ColumnData.Parse[colinfo[i].type](data, pos)
else
-- decimal / numeric types need precision and scale passed.
pos, val = ColumnData.Parse[colinfo[i].type]( colinfo[i].precision, colinfo[i].scale, data, pos)
end
if ( -1 == pos ) then
return false, val
end