diff --git a/nselib/bitcoin.lua b/nselib/bitcoin.lua index f00b979ad..330167ef9 100644 --- a/nselib/bitcoin.lua +++ b/nselib/bitcoin.lua @@ -44,400 +44,400 @@ _ENV = stdnse.module("bitcoin", stdnse.seeall) -- A class that supports the BitCoin network address structure NetworkAddress = { - NODE_NETWORK = 1, + NODE_NETWORK = 1, - -- Creates a new instance of the NetworkAddress class - -- @param host table as received by the action method - -- @param port table as received by the action method - -- @return o instance of NetworkAddress - new = function(self, host, port) - local o = { - host = "table" == type(host) and host.ip or host, - port = "table" == type(port) and port.number or port, - service = NetworkAddress.NODE_NETWORK, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the NetworkAddress class + -- @param host table as received by the action method + -- @param port table as received by the action method + -- @return o instance of NetworkAddress + new = function(self, host, port) + local o = { + host = "table" == type(host) and host.ip or host, + port = "table" == type(port) and port.number or port, + service = NetworkAddress.NODE_NETWORK, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Creates a new instance of NetworkAddress based on the data string - -- @param data string of bytes - -- @return na instance of NetworkAddress - fromString = function(data) - assert(26 == #data, "Expected 26 bytes of data") + -- Creates a new instance of NetworkAddress based on the data string + -- @param data string of bytes + -- @return na instance of NetworkAddress + fromString = function(data) + assert(26 == #data, "Expected 26 bytes of data") - local na = NetworkAddress:new() - local _ - _, na.service, na.ipv6_prefix, na.host, na.port = bin.unpack("S", data) - na.host = ipOps.fromdword(na.host) - return na - end, + local na = NetworkAddress:new() + local _ + _, na.service, na.ipv6_prefix, na.host, na.port = bin.unpack("S", data) + na.host = ipOps.fromdword(na.host) + return na + end, - -- Converts the NetworkAddress instance to string - -- @return data string containing the NetworkAddress instance - __tostring = function(self) - local ipv6_prefix = "00 00 00 00 00 00 00 00 00 00 FF FF" - local ip = ipOps.todword(self.host) - return bin.pack("IS", self.service, ipv6_prefix, ip, self.port ) - end + -- Converts the NetworkAddress instance to string + -- @return data string containing the NetworkAddress instance + __tostring = function(self) + local ipv6_prefix = "00 00 00 00 00 00 00 00 00 00 FF FF" + local ip = ipOps.todword(self.host) + return bin.pack("IS", self.service, ipv6_prefix, ip, self.port ) + end } -- The request class container Request = { - -- The version request - Version = { + -- The version request + Version = { - -- Creates a new instance of the Version request - -- @param host table as received by the action method - -- @param port table as received by the action method - -- @param lhost string containing the source IP - -- @param lport number containing the source port - -- @return o instance of Version - new = function(self, host, port, lhost, lport) - local o = { - host = host, - port = port, - lhost= lhost, - lport= lport, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the Version request + -- @param host table as received by the action method + -- @param port table as received by the action method + -- @param lhost string containing the source IP + -- @param lport number containing the source port + -- @return o instance of Version + new = function(self, host, port, lhost, lport) + local o = { + host = host, + port = port, + lhost= lhost, + lport= lport, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts the Version request to a string - -- @return data as string - __tostring = function(self) - local magic = 0xD9B4BEF9 - local cmd = "version\0\0\0\0\0" - local len = 85 - -- ver: 0.4.0 - local ver = 0x9c40 + -- Converts the Version request to a string + -- @return data as string + __tostring = function(self) + local magic = 0xD9B4BEF9 + local cmd = "version\0\0\0\0\0" + local len = 85 + -- ver: 0.4.0 + local ver = 0x9c40 - -- NODE_NETWORK = 1 - local services = 1 - local timestamp = os.time() - local ra = NetworkAddress:new(self.host, self.port) - local sa = NetworkAddress:new(self.lhost, self.lport) - local nodeid = openssl.rand_bytes(8) - local useragent = "\0" - local lastblock = 0 + -- NODE_NETWORK = 1 + local services = 1 + local timestamp = os.time() + local ra = NetworkAddress:new(self.host, self.port) + local sa = NetworkAddress:new(self.lhost, self.lport) + local nodeid = openssl.rand_bytes(8) + local useragent = "\0" + local lastblock = 0 - -- Construct payload in order to calculate checksum for the header - local payload = bin.pack("IA12II", data) - return header - end, - }, + pos, header.magic, header.cmd, header.length, header.checksum = bin.unpack(">IA12II", data) + return header + end, + }, - Alert = { + Alert = { - type = "Alert", - -- Creates a new instance of Version based on data string - -- @param data string containing the raw response - -- @return o instance of Version - new = function(self, data) - local o = { - data = data, - } - setmetatable(o, self) - self.__index = self - o:parse() - return o - end, + type = "Alert", + -- Creates a new instance of Version based on data string + -- @param data string containing the raw response + -- @return o instance of Version + new = function(self, data) + local o = { + data = data, + } + setmetatable(o, self) + self.__index = self + o:parse() + return o + end, - -- Parses the raw data and builds the Version instance - parse = function(self) - local pos = Response.Header.size + 1 - self.header = Response.Header.parse(self.data) + -- Parses the raw data and builds the Version instance + parse = function(self) + local pos = Response.Header.size + 1 + self.header = Response.Header.parse(self.data) - local p_length - pos, p_length = Util.decodeVarInt(self.data, pos) - local data - pos, data = bin.unpack("A" .. p_length, self.data, pos) + local p_length + pos, p_length = Util.decodeVarInt(self.data, pos) + local data + pos, data = bin.unpack("A" .. p_length, self.data, pos) - -- - -- TODO: Alert decoding goes here - -- + -- + -- TODO: Alert decoding goes here + -- - return - end, - }, + return + end, + }, - -- The version response message - Version = { + -- The version response message + Version = { - -- Creates a new instance of Version based on data string - -- @param data string containing the raw response - -- @return o instance of Version - new = function(self, data) - local o = { data = data } - setmetatable(o, self) - self.__index = self - o:parse() - return o - end, + -- Creates a new instance of Version based on data string + -- @param data string containing the raw response + -- @return o instance of Version + new = function(self, data) + local o = { data = data } + setmetatable(o, self) + self.__index = self + o:parse() + return o + end, - -- Parses the raw data and builds the Version instance - parse = function(self) - local pos, ra, sa + -- Parses the raw data and builds the Version instance + parse = function(self) + local pos, ra, sa - -- After 2012-02-20, version messages contain checksums - pos, self.magic, self.cmd, self.len, self.checksum, self.ver_raw, self.service, - self.timestamp, ra, sa, self.nodeid, - self.subver, self.lastblock = bin.unpack(" 31402 ) then - local timestamp, data - pos, timestamp, data = bin.unpack(" 31402 ) then + local timestamp, data + pos, timestamp, data = bin.unpack("timeout - the socket timeout in ms - -- @return instance of BCSocket - new = function(self, host, port, options) - local o = { - host = host, - port = port, - timeout = "table" == type(options) and options.timeout or 10000 - } - setmetatable(o, self) - self.__index = self - o.Socket = nmap.new_socket() - o.Buffer = nil - return o - end, + -- Creates a new BCSocket instance + -- @param host table as received by the action method + -- @param port table as received by the action method + -- @param options table containing additional options + -- timeout - the socket timeout in ms + -- @return instance of BCSocket + new = function(self, host, port, options) + local o = { + host = host, + port = port, + timeout = "table" == type(options) and options.timeout or 10000 + } + setmetatable(o, self) + self.__index = self + o.Socket = nmap.new_socket() + o.Buffer = nil + return o + end, - --- Establishes a connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - connect = function( self ) - self.Socket:set_timeout( self.timeout ) - return self.Socket:connect( self.host, self.port ) - end, + --- Establishes a connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + connect = function( self ) + self.Socket:set_timeout( self.timeout ) + return self.Socket:connect( self.host, self.port ) + end, - --- Closes an open connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - close = function( self ) - return self.Socket:close() - end, + --- Closes an open connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + close = function( self ) + return self.Socket:close() + end, - --- Opposed to the socket:receive_bytes function, that returns - -- at least x bytes, this function returns the amount of bytes requested. - -- - -- @param count of bytes to read - -- @return true on success, false on failure - -- @return data containing bytes read from the socket - -- err containing error message if status is false - recv = function( self, count ) - local status, data + --- Opposed to the socket:receive_bytes function, that returns + -- at least x bytes, this function returns the amount of bytes requested. + -- + -- @param count of bytes to read + -- @return true on success, false on failure + -- @return data containing bytes read from the socket + -- err containing error message if status is false + recv = function( self, count ) + local status, data - self.Buffer = self.Buffer or "" + self.Buffer = self.Buffer or "" - if ( #self.Buffer < count ) then - status, data = self.Socket:receive_bytes( count - #self.Buffer ) - if ( not(status) or #data < count - #self.Buffer ) then - return false, data - end - self.Buffer = self.Buffer .. data - end + if ( #self.Buffer < count ) then + status, data = self.Socket:receive_bytes( count - #self.Buffer ) + if ( not(status) or #data < count - #self.Buffer ) then + return false, data + end + self.Buffer = self.Buffer .. data + end - data = self.Buffer:sub( 1, count ) - self.Buffer = self.Buffer:sub( count + 1) + data = self.Buffer:sub( 1, count ) + self.Buffer = self.Buffer:sub( count + 1) - return true, data - end, + return true, data + end, - --- Sends data over the socket - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, data ) - return self.Socket:send( data ) - end, + --- Sends data over the socket + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, data ) + return self.Socket:send( data ) + end, } -- The Helper class used as a primary interface to scripts Helper = { - -- Creates a new Helper instance - -- @param host table as received by the action method - -- @param port table as received by the action method - -- @param options table containing additional options - -- timeout - the socket timeout in ms - -- @return instance of Helper - new = function(self, host, port, options) - local o = { - host = host, - port = port, - options = options - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new Helper instance + -- @param host table as received by the action method + -- @param port table as received by the action method + -- @param options table containing additional options + -- timeout - the socket timeout in ms + -- @return instance of Helper + new = function(self, host, port, options) + local o = { + host = host, + port = port, + options = options + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Connects to the BitCoin Server - -- @return status true on success false on failure - -- @return err string containing the error message in case status is false - connect = function(self) - self.socket = BCSocket:new(self.host, self.port, self.options) - local status, err = self.socket:connect() + -- Connects to the BitCoin Server + -- @return status true on success false on failure + -- @return err string containing the error message in case status is false + connect = function(self) + self.socket = BCSocket:new(self.host, self.port, self.options) + local status, err = self.socket:connect() - if ( not(status) ) then - return false, err - end - status, self.lhost, self.lport = self.socket.Socket:get_info() - return status, (status and nil or self.lhost) - end, + if ( not(status) ) then + return false, err + end + status, self.lhost, self.lport = self.socket.Socket:get_info() + return status, (status and nil or self.lhost) + end, - -- Performs a version handshake with the server - -- @return status, true on success false on failure - -- @return version instance if status is true - -- err string containing an error message if status is false - exchVersion = function(self) - if ( not(self.socket) ) then - return false - end + -- Performs a version handshake with the server + -- @return status, true on success false on failure + -- @return version instance if status is true + -- err string containing an error message if status is false + exchVersion = function(self) + if ( not(self.socket) ) then + return false + end - local req = Request.Version:new( - self.host, self.port, self.lhost, self.lport - ) + local req = Request.Version:new( + self.host, self.port, self.lhost, self.lport + ) - local status, err = self.socket:send(tostring(req)) - if ( not(status) ) then - return false, "Failed to send \"Version\" request to server" - end + local status, err = self.socket:send(tostring(req)) + if ( not(status) ) then + return false, "Failed to send \"Version\" request to server" + end - local version - status, version = Response.recvPacket(self.socket) + local version + status, version = Response.recvPacket(self.socket) - if ( not(status) or not(version) or version.cmd ~= "version\0\0\0\0\0" ) then - return false, "Failed to read \"Version\" response from server" - end + if ( not(status) or not(version) or version.cmd ~= "version\0\0\0\0\0" ) then + return false, "Failed to read \"Version\" response from server" + end - if ( version.ver_raw > 29000 ) then - local status, verack = Response.recvPacket(self.socket) - end + if ( version.ver_raw > 29000 ) then + local status, verack = Response.recvPacket(self.socket) + end - local verack = Request.VerAck:new() - local status, err = self.socket:send(tostring(verack)) - if ( not(status) ) then - return false, "Failed to send \"Version\" request to server" - end + local verack = Request.VerAck:new() + local status, err = self.socket:send(tostring(verack)) + if ( not(status) ) then + return false, "Failed to send \"Version\" request to server" + end - self.version = version.ver_raw - return status, version - end, + self.version = version.ver_raw + return status, version + end, - getNodes = function(self) - local req = Request.GetAddr:new( - self.host, self.port, self.lhost, self.lport - ) + getNodes = function(self) + local req = Request.GetAddr:new( + self.host, self.port, self.lhost, self.lport + ) - local status, err = self.socket:send(tostring(req)) - if ( not(status) ) then - return false, "Failed to send \"Version\" request to server" - end + local status, err = self.socket:send(tostring(req)) + if ( not(status) ) then + return false, "Failed to send \"Version\" request to server" + end - -- take care of any alerts that may be incoming - local status, response = Response.recvPacket(self.socket, self.version) - while ( status and response and response.type == "Alert" ) do - status, response = Response.recvPacket(self.socket, self.version) - end + -- take care of any alerts that may be incoming + local status, response = Response.recvPacket(self.socket, self.version) + while ( status and response and response.type == "Alert" ) do + status, response = Response.recvPacket(self.socket, self.version) + end - return status, response - end, + return status, response + end, - -- Reads a message from the server - -- @return status true on success, false on failure - -- @return response instance of response packet if status is true - -- err string containing the error message if status is false - readMessage = function(self) - assert(self.version, "Version handshake has not been performed") - return Response.recvPacket(self.socket, self.version) - end, + -- Reads a message from the server + -- @return status true on success, false on failure + -- @return response instance of response packet if status is true + -- err string containing the error message if status is false + readMessage = function(self) + assert(self.version, "Version handshake has not been performed") + return Response.recvPacket(self.socket, self.version) + end, - -- Closes the connection to the server - -- @return status true on success false on failure - -- @return err code, if status is false - close = function(self) - return self.socket:close() - end + -- Closes the connection to the server + -- @return status true on success false on failure + -- @return err code, if status is false + close = function(self) + return self.socket:close() + end } return _ENV; diff --git a/nselib/bittorrent.lua b/nselib/bittorrent.lua index 3450d4215..c9389ca62 100644 --- a/nselib/bittorrent.lua +++ b/nselib/bittorrent.lua @@ -107,41 +107,41 @@ _ENV = stdnse.module("bittorrent", stdnse.seeall) -- a bencoded string there and returns it as a normal lua string, as well as -- the position after the string local bdec_string = function(buf, pos) - local len = "" - local tmp_pos = pos - while tonumber(string.char(buf:byte(pos))) do - len = len .. tonumber(string.char(buf:byte(pos))) - pos = pos + 1 - end - len = tonumber(len) + local len = "" + local tmp_pos = pos + while tonumber(string.char(buf:byte(pos))) do + len = len .. tonumber(string.char(buf:byte(pos))) + pos = pos + 1 + end + len = tonumber(len) - if string.char(buf:byte(pos)) ~= ":" then - return nil, tmp_pos - end - pos = pos+1 + if string.char(buf:byte(pos)) ~= ":" then + return nil, tmp_pos + end + pos = pos+1 - local str = buf:sub(pos,pos+len-1) - pos = pos+len - return str, pos + local str = buf:sub(pos,pos+len-1) + pos = pos+len + return str, pos end --- Given a buffer and a starting position in the buffer, this function decodes -- a bencoded number there and returns it as a normal lua number, as well as -- the position after the number local bdec_number = function(buf, pos) - local s, n = string.match(buf, "^i(%-*)(%d+)e", pos) - if not n then return nil end + local s, n = string.match(buf, "^i(%-*)(%d+)e", pos) + if not n then return nil end - local num = tonumber(n) - -- 1 for the "i", 1 for the "e", 1 if there is a "-" plus the length of n - pos = pos + 2 + #n + local num = tonumber(n) + -- 1 for the "i", 1 for the "e", 1 if there is a "-" plus the length of n + pos = pos + 2 + #n - if s == "-" then - num = -num - pos = pos + 1 - end + if s == "-" then + num = -num + pos = pos + 1 + end - return num, pos + return num, pos end --- Parses a bencoded buffer @@ -149,164 +149,164 @@ end -- @return bool indicating if parsing went ok -- @return table containing the decoded structure, or error string bdecode = function(buf) - local len = #buf + local len = #buf - -- the main table - local t = {} - local stack = {} + -- the main table + local t = {} + local stack = {} - local pos = 1 - local cur = {} - cur.type = "list" - cur.ref = t - table.insert(stack, cur) - cur.ref.type="list" + local pos = 1 + local cur = {} + cur.type = "list" + cur.ref = t + table.insert(stack, cur) + cur.ref.type="list" - while true do - if pos == len or (len-pos)==-1 then break end + while true do + if pos == len or (len-pos)==-1 then break end - if cur.type == "list" then - -- next element is a string - if tonumber( string.char( buf:byte(pos) ) ) then - local str - str, pos = bdec_string(buf, pos) - if not str then return nil, "Error parsing string", pos end - table.insert(cur.ref, str) + if cur.type == "list" then + -- next element is a string + if tonumber( string.char( buf:byte(pos) ) ) then + local str + str, pos = bdec_string(buf, pos) + if not str then return nil, "Error parsing string", pos end + table.insert(cur.ref, str) - -- next element is a number - elseif "i" == string.char(buf:byte(pos)) then - local num - num, pos = bdec_number(buf, pos) - if not num then return nil, "Error parsing number", pos end - table.insert(cur.ref, num) + -- next element is a number + elseif "i" == string.char(buf:byte(pos)) then + local num + num, pos = bdec_number(buf, pos) + if not num then return nil, "Error parsing number", pos end + table.insert(cur.ref, num) - -- next element is a list - elseif "l" == string.char(buf:byte(pos)) then - local new_list = {} - new_list.type="list" - table.insert(cur.ref, new_list) + -- next element is a list + elseif "l" == string.char(buf:byte(pos)) then + local new_list = {} + new_list.type="list" + table.insert(cur.ref, new_list) - cur = {} - cur.type = "list" - cur.ref = new_list - table.insert(stack, cur) - pos = pos+1 + cur = {} + cur.type = "list" + cur.ref = new_list + table.insert(stack, cur) + pos = pos+1 - --next element is a dict - elseif "d" == string.char(buf:byte(pos)) then - local new_dict = {} - new_dict.type = "dict" - table.insert(cur.ref, new_dict) + --next element is a dict + elseif "d" == string.char(buf:byte(pos)) then + local new_dict = {} + new_dict.type = "dict" + table.insert(cur.ref, new_dict) - cur = {} - cur.type = "dict" - cur.ref = new_dict - table.insert(stack, cur) - pos = pos+1 + cur = {} + cur.type = "dict" + cur.ref = new_dict + table.insert(stack, cur) + pos = pos+1 - --escape from the list - elseif "e" == string.char(buf:byte(pos)) then - table.remove(stack, #stack) - cur = stack[#stack] - if not cur then return nil, "Problem with list closure:", pos end - pos = pos+1 - else - return nil, "Unknown type found.", pos - end + --escape from the list + elseif "e" == string.char(buf:byte(pos)) then + table.remove(stack, #stack) + cur = stack[#stack] + if not cur then return nil, "Problem with list closure:", pos end + pos = pos+1 + else + return nil, "Unknown type found.", pos + end - elseif cur.type == "dict" then - local item = {} -- {key = , value = <.*>} - -- used to skip reading the value when escaping from a structure - local escape_flag = false + elseif cur.type == "dict" then + local item = {} -- {key = , value = <.*>} + -- used to skip reading the value when escaping from a structure + local escape_flag = false - -- fill the key - if tonumber( string.char( buf:byte(pos) ) ) then - local str - local tmp_pos = pos - str, pos = bdec_string(buf, pos) - if not str then return nil, "Error parsing string.", pos end - item.key = str - elseif "e" == string.char(buf:byte(pos)) then - table.remove(stack, #stack) - cur = stack[#stack] - if not cur then return nil, "Problem with list closure:", pos end - pos = pos+1 + -- fill the key + if tonumber( string.char( buf:byte(pos) ) ) then + local str + local tmp_pos = pos + str, pos = bdec_string(buf, pos) + if not str then return nil, "Error parsing string.", pos end + item.key = str + elseif "e" == string.char(buf:byte(pos)) then + table.remove(stack, #stack) + cur = stack[#stack] + if not cur then return nil, "Problem with list closure:", pos end + pos = pos+1 - escape_flag = true + escape_flag = true - else - return nil, "A dict key has to be a string or escape.", pos - end + else + return nil, "A dict key has to be a string or escape.", pos + end - if not escape_flag then - -- value - -- next element is a string - if tonumber( string.char( buf:byte(pos) ) ) then - local str - str, pos = bdec_string(buf, pos) - if not str then return nil, "Error parsing string.", pos end - item.value = str - table.insert(cur.ref, item) + if not escape_flag then + -- value + -- next element is a string + if tonumber( string.char( buf:byte(pos) ) ) then + local str + str, pos = bdec_string(buf, pos) + if not str then return nil, "Error parsing string.", pos end + item.value = str + table.insert(cur.ref, item) - --next element is a number - elseif "i" == string.char(buf:byte(pos)) then - local num - num, pos = bdec_number(buf, pos) - if not num then return nil, "Error parsing number.", pos end - item.value = num - table.insert(cur.ref, item) + --next element is a number + elseif "i" == string.char(buf:byte(pos)) then + local num + num, pos = bdec_number(buf, pos) + if not num then return nil, "Error parsing number.", pos end + item.value = num + table.insert(cur.ref, item) - -- next element is a list - elseif "l" == string.char(buf:byte(pos)) then - item.value = {} - item.value.type = "list" - table.insert(cur.ref, item) + -- next element is a list + elseif "l" == string.char(buf:byte(pos)) then + item.value = {} + item.value.type = "list" + table.insert(cur.ref, item) - cur = {} - cur.type = "list" - cur.ref = item.value + cur = {} + cur.type = "list" + cur.ref = item.value - table.insert(stack, cur) - pos = pos+1 + table.insert(stack, cur) + pos = pos+1 - --next element is a dict - elseif "d" == string.char(buf:byte(pos)) then - item.value = {} - item.value.type = "dict" - table.insert(cur.ref, item) + --next element is a dict + elseif "d" == string.char(buf:byte(pos)) then + item.value = {} + item.value.type = "dict" + table.insert(cur.ref, item) - cur = {} - cur.type = "dict" - cur.ref = item.value + cur = {} + cur.type = "dict" + cur.ref = item.value - table.insert(stack, cur) - pos = pos+1 + table.insert(stack, cur) + pos = pos+1 - --escape from the dict - elseif "e" == string.char(buf:byte(pos)) then - table.remove(stack, #stack) - cur = stack[#stack] - if not cur then return false, "Problem with dict closure", pos end - pos = pos+1 - else - return false, "Error parsing file, unknown type found", pos - end - end -- if not escape_flag - else -- elseif type == "dict" - return false, "Invalid type of structure. Fix the code." - end - end -- while(true) + --escape from the dict + elseif "e" == string.char(buf:byte(pos)) then + table.remove(stack, #stack) + cur = stack[#stack] + if not cur then return false, "Problem with dict closure", pos end + pos = pos+1 + else + return false, "Error parsing file, unknown type found", pos + end + end -- if not escape_flag + else -- elseif type == "dict" + return false, "Invalid type of structure. Fix the code." + end + end -- while(true) - -- The code below is commented out because some responses from trackers are - -- not according to standards + -- The code below is commented out because some responses from trackers are + -- not according to standards - -- next(stack) is never gonna be nil because we're always in the main list - -- next(stack, next(stack)) should be nil if we're in the main list --- if next(stack, next(stack)) then --- return false, "Probably file incorrect format" --- end + -- next(stack) is never gonna be nil because we're always in the main list + -- next(stack, next(stack)) should be nil if we're in the main list + -- if next(stack, next(stack)) then + -- return false, "Probably file incorrect format" + -- end - return true, t + return true, t end --- This is the thread function which sends a DHT ping probe to every peer in @@ -316,94 +316,94 @@ end -- order to be processed byt the find_node_thread(). This operation is done -- during the specified timeout which has a default value of about 30 seconds. local dht_ping_thread = function(pnt, timeout) - local condvar = nmap.condvar(pnt) - local socket = nmap.new_socket("udp") - socket:set_timeout(3000) - local status, data + local condvar = nmap.condvar(pnt) + local socket = nmap.new_socket("udp") + socket:set_timeout(3000) + local status, data - local transaction_id = 0 - local start = os.time() + local transaction_id = 0 + local start = os.time() - while os.time() - start < timeout do - local num_peers = 0 - --ping a 100 peers if there are as many + while os.time() - start < timeout do + local num_peers = 0 + --ping a 100 peers if there are as many - while next(pnt.peers_dht_ping) ~= nil and num_peers <= 100 and os.time() - start < timeout do - num_peers = num_peers +1 - local peer_ip, peer_info = next(pnt.peers_dht_ping) + while next(pnt.peers_dht_ping) ~= nil and num_peers <= 100 and os.time() - start < timeout do + num_peers = num_peers +1 + local peer_ip, peer_info = next(pnt.peers_dht_ping) - --transaction ids are 2 bytes long - local t_ID_hex = stdnse.tohex(transaction_id % 0xffff) - t_ID_hex = string.rep("0",4-#t_ID_hex)..t_ID_hex - peer_info.transaction_id = bin.pack("H",t_ID_hex) + --transaction ids are 2 bytes long + local t_ID_hex = stdnse.tohex(transaction_id % 0xffff) + t_ID_hex = string.rep("0",4-#t_ID_hex)..t_ID_hex + peer_info.transaction_id = bin.pack("H",t_ID_hex) - -- mark it as received so we can distinguish from the others and - -- successfully iterate while receiving - peer_info.received = false + -- mark it as received so we can distinguish from the others and + -- successfully iterate while receiving + peer_info.received = false - pnt.peers[peer_ip] = peer_info - pnt.peers_dht_ping[peer_ip] = nil + pnt.peers[peer_ip] = peer_info + pnt.peers_dht_ping[peer_ip] = nil - -- bencoded ping query describing a dictionary with y = q (query), q = ping - -- {"t":, "y":"q", "q":"ping", "a":{"id":}} - local ping_query = "d1:ad2:id20:" .. pnt.node_id .. "e1:q4:ping1:t2:" .. - peer_info.transaction_id .. "1:y1:qe" + -- bencoded ping query describing a dictionary with y = q (query), q = ping + -- {"t":, "y":"q", "q":"ping", "a":{"id":}} + local ping_query = "d1:ad2:id20:" .. pnt.node_id .. "e1:q4:ping1:t2:" .. + peer_info.transaction_id .. "1:y1:qe" - status, data = socket:sendto(peer_ip, peer_info.port, ping_query) + status, data = socket:sendto(peer_ip, peer_info.port, ping_query) - transaction_id = transaction_id +1 - if transaction_id % 0xffff == 0 then - transaction_id = 0 - end - end + transaction_id = transaction_id +1 + if transaction_id % 0xffff == 0 then + transaction_id = 0 + end + end - -- receive responses up to a 100 - for c = 1, 100 do - if os.time() - start >= timeout then break end - status, data = socket:receive() - if not status then break end + -- receive responses up to a 100 + for c = 1, 100 do + if os.time() - start >= timeout then break end + status, data = socket:receive() + if not status then break end - local s, r = bdecode(data) - -- if the response is decoded process it - if s then - local error_flag = true - local good_response = false - local node_id = nil - local trans_id = nil + local s, r = bdecode(data) + -- if the response is decoded process it + if s then + local error_flag = true + local good_response = false + local node_id = nil + local trans_id = nil - for _, i in ipairs(r[1]) do - if i.key == "y" and i.value == "r" then - error_flag = false - elseif i.key == "r" and i.value and i.value[1] and i.value[1].value then - node_id = i.value[1].value - good_response = true - elseif i.key == "t" then - trans_id = i.value - end - end + for _, i in ipairs(r[1]) do + if i.key == "y" and i.value == "r" then + error_flag = false + elseif i.key == "r" and i.value and i.value[1] and i.value[1].value then + node_id = i.value[1].value + good_response = true + elseif i.key == "t" then + trans_id = i.value + end + end - if (not error_flag) and good_response and node_id and trans_id then - local peer_ip - for ip, info in pairs(pnt.peers) do - if info.transaction_id == trans_id then - info.received = nil - peer_ip = ip - break - end - end - if peer_ip then - pnt.peers[peer_ip].node_id = node_id - if not (pnt.nodes_find_node[peer_ip] or pnt.nodes_get_peers[peer_ip] or - pnt.nodes[peer_ip]) then - pnt.nodes_find_node[peer_ip] = pnt.peers[peer_ip] - end - end - end - end -- if s then - end -- /for c = 1, 100 - end -- /while true - socket:close() - condvar("signal") + if (not error_flag) and good_response and node_id and trans_id then + local peer_ip + for ip, info in pairs(pnt.peers) do + if info.transaction_id == trans_id then + info.received = nil + peer_ip = ip + break + end + end + if peer_ip then + pnt.peers[peer_ip].node_id = node_id + if not (pnt.nodes_find_node[peer_ip] or pnt.nodes_get_peers[peer_ip] or + pnt.nodes[peer_ip]) then + pnt.nodes_find_node[peer_ip] = pnt.peers[peer_ip] + end + end + end + end -- if s then + end -- /for c = 1, 100 + end -- /while true + socket:close() + condvar("signal") end @@ -414,70 +414,70 @@ end -- the pnt.nodes_find_node list. This action is done for a timeout with a -- default value of 30 seconds. local find_node_thread = function(pnt, timeout) - local condvar = nmap.condvar(pnt) - local socket = nmap.new_socket("udp") - socket:set_timeout(3000) - local status, data + local condvar = nmap.condvar(pnt) + local socket = nmap.new_socket("udp") + socket:set_timeout(3000) + local status, data - local start = os.time() - while true do - if os.time() - start >= timeout then break end - local num_peers = 0 + local start = os.time() + while true do + if os.time() - start >= timeout then break end + local num_peers = 0 - while next(pnt.nodes_find_node) ~= nil and num_peers <= 100 do - num_peers = num_peers +1 - local node_ip, node_info = next(pnt.nodes_find_node) + while next(pnt.nodes_find_node) ~= nil and num_peers <= 100 do + num_peers = num_peers +1 + local node_ip, node_info = next(pnt.nodes_find_node) - -- standard bittorrent protocol specified find_node query with y = q (query), - -- q = "find_node" (type of query), - -- find_node Query = {"t":, "y":"q", "q":"find_node", "a": {"id":, "target":}} - local find_node_query = "d1:ad2:id20:" .. pnt.node_id .. "6:target20:" .. - pnt.info_hash .. "e1:q9:find_node1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe" + -- standard bittorrent protocol specified find_node query with y = q (query), + -- q = "find_node" (type of query), + -- find_node Query = {"t":, "y":"q", "q":"find_node", "a": {"id":, "target":}} + local find_node_query = "d1:ad2:id20:" .. pnt.node_id .. "6:target20:" .. + pnt.info_hash .. "e1:q9:find_node1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe" - -- add the traversed nodes to pnt.nodes_get_peers so they can be traversed by get_peers_thread - pnt.nodes_get_peers[node_ip] = node_info - pnt.nodes_find_node[node_ip] = nil + -- add the traversed nodes to pnt.nodes_get_peers so they can be traversed by get_peers_thread + pnt.nodes_get_peers[node_ip] = node_info + pnt.nodes_find_node[node_ip] = nil - status, data = socket:sendto(node_ip, node_info.port, find_node_query) - end + status, data = socket:sendto(node_ip, node_info.port, find_node_query) + end - for c = 1, 100 do - if os.time() - start >= timeout then break end - status, data = socket:receive() - if not status then break end - local s, r = bdecode(data) + for c = 1, 100 do + if os.time() - start >= timeout then break end + status, data = socket:receive() + if not status then break end + local s, r = bdecode(data) - if s then - local nodes = nil - if r[1] and r[1][1] and r[1][1].key == "r" and r[1][1].value then - for _, el in ipairs(r[1][1].value) do - if el.key == "nodes" then - nodes = el.value - end - end - end + if s then + local nodes = nil + if r[1] and r[1][1] and r[1][1].key == "r" and r[1][1].value then + for _, el in ipairs(r[1][1].value) do + if el.key == "nodes" then + nodes = el.value + end + end + end - --parse the nodes an add them to pnt.nodes_find_node - if nodes then - for node_id, bin_node_ip, bin_node_port in nodes:gmatch("(....................)(....)(..)") do - local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2), - bin_node_ip:byte(3), bin_node_ip:byte(4)) - local node_port = bit.lshift(bin_node_port:byte(1),8) + bin_node_port:byte(2) - local node_info = {} - node_info.port = node_port - node_info.node_id = node_id + --parse the nodes an add them to pnt.nodes_find_node + if nodes then + for node_id, bin_node_ip, bin_node_port in nodes:gmatch("(....................)(....)(..)") do + local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2), + bin_node_ip:byte(3), bin_node_ip:byte(4)) + local node_port = bit.lshift(bin_node_port:byte(1),8) + bin_node_port:byte(2) + local node_info = {} + node_info.port = node_port + node_info.node_id = node_id - if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] - or pnt.nodes_find_node[node_ip]) then - pnt.nodes_find_node[node_ip] = node_info - end - end - end -- if nodes - end -- if s - end -- for c = 1, 100 - end -- while true - socket:close() - condvar("signal") + if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] + or pnt.nodes_find_node[node_ip]) then + pnt.nodes_find_node[node_ip] = node_info + end + end + end -- if nodes + end -- if s + end -- for c = 1, 100 + end -- while true + socket:close() + condvar("signal") end @@ -488,757 +488,757 @@ end -- response is sent when the queried node has no peers, and contains more nodes -- which are added to the pnt.nodes_find_node list. local get_peers_thread = function(pnt, timeout) - local condvar = nmap.condvar(pnt) - local socket = nmap.new_socket("udp") - socket:set_timeout(3000) - local status, data + local condvar = nmap.condvar(pnt) + local socket = nmap.new_socket("udp") + socket:set_timeout(3000) + local status, data - local start = os.time() - while true do - if os.time() - start >= timeout then break end - local num_peers = 0 + local start = os.time() + while true do + if os.time() - start >= timeout then break end + local num_peers = 0 - while next(pnt.nodes_get_peers) ~= nil and num_peers <= 100 do - num_peers = num_peers +1 - local node_ip, node_info = next(pnt.nodes_get_peers) + while next(pnt.nodes_get_peers) ~= nil and num_peers <= 100 do + num_peers = num_peers +1 + local node_ip, node_info = next(pnt.nodes_get_peers) - -- standard bittorrent protocol specified get_peers query with y ="q" (query) - -- and q = "get_peers" (type of query) - -- {"t":, "y":"q", "q":"get_peers", "a": {"id":, "info_hash":}} - local get_peers_query = "d1:ad2:id20:" .. pnt.node_id .. "9:info_hash20:" .. - pnt.info_hash .. "e1:q9:get_peers1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe" + -- standard bittorrent protocol specified get_peers query with y ="q" (query) + -- and q = "get_peers" (type of query) + -- {"t":, "y":"q", "q":"get_peers", "a": {"id":, "info_hash":}} + local get_peers_query = "d1:ad2:id20:" .. pnt.node_id .. "9:info_hash20:" .. + pnt.info_hash .. "e1:q9:get_peers1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe" - pnt.nodes[node_ip] = node_info - pnt.nodes_get_peers[node_ip] = nil + pnt.nodes[node_ip] = node_info + pnt.nodes_get_peers[node_ip] = nil - status, data = socket:sendto(node_ip, node_info.port, get_peers_query) - end + status, data = socket:sendto(node_ip, node_info.port, get_peers_query) + end - for c = 1, 100 do - if os.time() - start >= timeout then break end - status, data = socket:receive() - if not status then break end - local s, r = bdecode(data) + for c = 1, 100 do + if os.time() - start >= timeout then break end + status, data = socket:receive() + if not status then break end + local s, r = bdecode(data) - if s then - local good_response = false - local nodes = nil - local peers = nil - for _,el in ipairs(r[1]) do - if el.key == "y" and el.value == "r" then - good_response = true - elseif el.key == "r" then - for _,i in ipairs(el.value) do - -- the key will either be for nodes or peers - if i.key == "nodes" then -- nodes - nodes = i.value - break - elseif i.key == "values" then -- peers - peers = i.value - break - end - end - end - end + if s then + local good_response = false + local nodes = nil + local peers = nil + for _,el in ipairs(r[1]) do + if el.key == "y" and el.value == "r" then + good_response = true + elseif el.key == "r" then + for _,i in ipairs(el.value) do + -- the key will either be for nodes or peers + if i.key == "nodes" then -- nodes + nodes = i.value + break + elseif i.key == "values" then -- peers + peers = i.value + break + end + end + end + end - if not good_response then - break - end + if not good_response then + break + end - if nodes then + if nodes then - for node_id, bin_node_ip, bin_node_port in - nodes:gmatch("(....................)(....)(..)") do + for node_id, bin_node_ip, bin_node_port in + nodes:gmatch("(....................)(....)(..)") do - local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2), - bin_node_ip:byte(3), bin_node_ip:byte(4)) - local node_port = bit.lshift(bin_node_port:byte(1),8) + bin_node_port:byte(2) - local node_info = {} - node_info.port = node_port - node_info.node_id = node_id + local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2), + bin_node_ip:byte(3), bin_node_ip:byte(4)) + local node_port = bit.lshift(bin_node_port:byte(1),8) + bin_node_port:byte(2) + local node_info = {} + node_info.port = node_port + node_info.node_id = node_id - if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or - pnt.nodes_find_node[node_ip]) then - pnt.nodes_find_node[node_ip] = node_info - end - end + if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or + pnt.nodes_find_node[node_ip]) then + pnt.nodes_find_node[node_ip] = node_info + end + end - elseif peers then + elseif peers then - for _, peer in ipairs(peers) do - local bin_ip, bin_port = peer:match("(....)(..)") - local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1), - bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) - local port = bit.lshift(bin_port:byte(1),8)+bin_port:byte(2) + for _, peer in ipairs(peers) do + local bin_ip, bin_port = peer:match("(....)(..)") + local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1), + bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) + local port = bit.lshift(bin_port:byte(1),8)+bin_port:byte(2) - if not (pnt.peers[ip] or pnt.peers_dht_ping[ip]) then - pnt.peers_dht_ping[ip] = {} - pnt.peers_dht_ping[ip].port = port - end - end + if not (pnt.peers[ip] or pnt.peers_dht_ping[ip]) then + pnt.peers_dht_ping[ip] = {} + pnt.peers_dht_ping[ip].port = port + end + end - end -- if nodes / elseif peers - end -- if s then - end -- for c = 1,100 - end -- while true - socket:close() - condvar("signal") + end -- if nodes / elseif peers + end -- if s then + end -- for c = 1,100 + end -- while true + socket:close() + condvar("signal") end Torrent = { - new = function(self) - local o ={} - setmetatable(o, self) - self.__index = self - - self.buffer = nil -- buffer to keep the torrent - self.tor_struct = nil -- the decoded structure from the bencoded buffer - - self.trackers = {} -- list of trackers {"tr1", "tr2", "tr3"...} - self.port = 6881 -- port on which our peer "listens" / it doesn't actually listen - self.size = nil -- size of the files in the torrent - - self.info_buf = nil --buffer for info_hash - self.info_hash = nil --info_hash binary string - self.info_hash_url = nil --info_hash escaped - - self.peers = {} -- peers = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} - self.nodes = {} -- nodes = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} - return o - end, - - --- Loads trackers and similar information for a torrent from a magnet link. - load_from_magnet = function(self, magnet) - local info_hash_hex = magnet:match("^magnet:%?xt=urn:btih:(%w+)&") - if not info_hash_hex then - return false, "Erroneous magnet link" - end - self.info_hash = bin.pack("H",info_hash_hex) - - local pos = #info_hash_hex + 21 - local name = magnet:sub(pos,#magnet):match("^&dn=(.-)&") - if name then - pos = pos + 4 + #name - end - magnet = magnet:sub(pos,#magnet) - for tracker in magnet:gmatch("&tr=([^&]+)") do - local trac = url.unescape(tracker) - table.insert(self.trackers, trac) - end - self.size = 50 - end, - - --- Reads a torrent file, loads self.buffer and parses it using - -- self:parse_buffer(), then self:calc_info_hash() - -- - -- @param filename, string containing filename of the torrent file - -- @return boolean indicating whether loading went alright - -- @return err string with error message if loadin went wrong - load_from_file = function(self, filename) - if not filename then return false, "No filename specified." end - - local file = io.open(filename, "r") - if not file then return false, "Cannot open file: "..filename end - - self.buffer = file:read("*a") - - local status, err = self:parse_buffer() - if not status then - return false, "Could not parse file: ".. err - end - - status, err = self:calc_info_hash() - if not status then - return false, "Could not calculate info_hash: " .. err - end - - status, err = self:load_trackers() - if not status then - return false, "Could not load trackers: " .. err - end - - status, err = self:calc_torrent_size() - if not status then - if not err then err = "" end - return false, "Could not calculate torrent size: " .. err - end - - file:close() - return true - end, - - --- Gets peers available from the loaded trackers - trackers_peers = function(self) - for _, tracker in ipairs(self.trackers) do - local status, err - - if tracker:match("^http://") then -- http tracker - status, err = self:http_tracker_peers(tracker) - if not status then - stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err) - end - elseif tracker:match("^udp://") then -- udp tracker - status, err = self:udp_tracker_peers(tracker) - if not status then - stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err) - end - else -- unknown tracker - stdnse.print_debug("Unknown tracker protocol for: "..tracker) - end - --if not status then return false, err end - end - - return true - end, - - --- Runs the three threads which do a DHT discovery of nodes and peers. - -- The default timeout for this discovery is 30 seconds but it can be - -- set through the timeout argument. - dht_peers = function(self, timeout) - stdnse.print_debug("bittorrent: Starting DHT peers discovery") - - if next(self.peers) == nil then - stdnse.print_debug("bittorrent: No peers detected") - return - end - - if not timeout or type(timeout)~="number" then timeout = 30 end - - -- peer node table aka the condvar! - local pnt = {} - pnt.peers = {} - pnt.peers_dht_ping = self.peers - - pnt.nodes = {} - pnt.nodes_get_peers = {} - pnt.nodes_find_node = self.nodes - - pnt.node_id = openssl.rand_bytes(20) - pnt.info_hash = self.info_hash - - local condvar = nmap.condvar(pnt) - - local dht_ping_co = stdnse.new_thread(dht_ping_thread, pnt, timeout) - local find_node_co = stdnse.new_thread(find_node_thread, pnt, timeout) - local get_peers_co = stdnse.new_thread(get_peers_thread, pnt, timeout) - - while true do - stdnse.sleep(0.5) - if coroutine.status(dht_ping_co) == "dead" and - coroutine.status(find_node_co) == "dead" and - coroutine.status(get_peers_co) == "dead" then - break - end - end - - self.peers = pnt.peers - self.nodes = pnt.nodes - - -- Add some residue nodes and peers - for peer_ip, peer_info in pairs(pnt.peers_dht_ping) do - if not self.peers[peer_ip] then - self.peers[peer_ip] = peer_info - end - end - for node_ip, node_info in pairs(pnt.nodes_find_node) do - if not self.nodes[node_ip] then - self.nodes[node_ip] = node_info - end - end - for node_ip, node_info in pairs(pnt.nodes_get_peers) do - if not self.nodes[node_ip] then - self.nodes[node_ip] = node_info - end - end - end, - - --- Parses self.buffer, fills self.tor_struct, self.info_buf - -- This function is similar to the bdecode function but it has a few - -- additions for calculating torrent file specific fields - parse_buffer = function(self) - local buf = self.buffer - - local len = #buf - - -- the main table - local t = {} - self.tor_struct = t - local stack = {} - - local pos = 1 - local cur = {} - cur.type = "list" - cur.ref = t - table.insert(stack, cur) - cur.ref.type="list" - - -- starting and ending position of the info dict - local info_pos_start, info_pos_end, info_buf_count = nil, nil, 0 - - while true do - if pos == len or (len-pos)==-1 then break end - - if cur.type == "list" then - -- next element is a string - if tonumber( string.char( buf:byte(pos) ) ) then - local str - str, pos = bdec_string(buf, pos) - if not str then return nil, "Error parsing string", pos end - table.insert(cur.ref, str) - - -- next element is a number - elseif "i" == string.char(buf:byte(pos)) then - local num - num, pos = bdec_number(buf, pos) - if not num then return nil, "Error parsing number", pos end - table.insert(cur.ref, num) - - -- next element is a list - elseif "l" == string.char(buf:byte(pos)) then - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count +1 - end - - local new_list = {} - new_list.type="list" - table.insert(cur.ref, new_list) - - cur = {} - cur.type = "list" - cur.ref = new_list - table.insert(stack, cur) - pos = pos+1 - - --next element is a dict - elseif "d" == string.char(buf:byte(pos)) then - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count +1 - end - - local new_dict = {} - new_dict.type = "dict" - table.insert(cur.ref, new_dict) - - cur = {} - cur.type = "dict" - cur.ref = new_dict - table.insert(stack, cur) - pos = pos+1 - - --escape from the list - elseif "e" == string.char(buf:byte(pos)) then - if info_buf_count == 0 then - info_pos_end = pos-1 - end - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count -1 - end - - table.remove(stack, #stack) - cur = stack[#stack] - if not cur then return nil, "Problem with list closure:", pos end - pos = pos+1 - else - return nil, "Unknown type found.", pos - end - - elseif cur.type == "dict" then - local item = {} -- {key = , value = <.*>} - -- key - if tonumber( string.char( buf:byte(pos) ) ) then - local str - local tmp_pos = pos - str, pos = bdec_string(buf, pos) - if not str then return nil, "Error parsing string.", pos end - item.key = str - -- fill the info_pos_start - if item.key == "info" and not info_pos_start then info_pos_start = pos end - - elseif "e" == string.char(buf:byte(pos)) then - if info_buf_count == 0 then - info_pos_end = pos-1 - end - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count -1 - end - - table.remove(stack, #stack) - cur = stack[#stack] - if not cur then return nil, "Problem with list closure:", pos end - pos = pos+1 - - else - return nil, "A dict key has to be a string or escape.", pos - end - - -- value - -- next element is a string - if tonumber( string.char( buf:byte(pos) ) ) then - local str - str, pos = bdec_string(buf, pos) - if not str then return nil, "Error parsing string.", pos end - item.value = str - table.insert(cur.ref, item) - - --next element is a number - elseif "i" == string.char(buf:byte(pos)) then - local num - num, pos = bdec_number(buf, pos) - if not num then return nil, "Error parsing number.", pos end - item.value = num - table.insert(cur.ref, item) - - -- next element is a list - elseif "l" == string.char(buf:byte(pos)) then - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count +1 - end - - item.value = {} - item.value.type = "list" - table.insert(cur.ref, item) - - cur = {} - cur.type = "list" - cur.ref = item.value - - table.insert(stack, cur) - pos = pos+1 - - --next element is a dict - elseif "d" == string.char(buf:byte(pos)) then - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count +1 - end - - item.value = {} - item.value.type = "dict" - table.insert(cur.ref, item) - - cur = {} - cur.type = "dict" - cur.ref = item.value - - table.insert(stack, cur) - pos = pos+1 - - --escape from the dict - elseif "e" == string.char(buf:byte(pos)) then - if info_buf_count == 0 then - info_pos_end = pos-1 - end - if info_pos_start and (not info_pos_end) then - info_buf_count = info_buf_count -1 - end - - table.remove(stack, #stack) - cur = stack[#stack] - if not cur then return false, "Problem with dict closure", pos end - pos = pos+1 - else - return false, "Error parsing file, unknown type found", pos - end - else - return false, "Invalid type of structure. Fix the code." - end - end -- while(true) - - -- next(stack) is never gonna be nil because we're always in the main list - -- next(stack, next(stack)) should be nil if we're in the main list - if next(stack, next(stack)) then - return false, "Probably file incorrect format" - end - - self.info_buf = buf:sub(info_pos_start, info_pos_end) - - return true - end, - - --- Loads the list of trackers in self.trackers from self.tor_struct - load_trackers = function(self) - local tor = self.tor_struct - local trackers = {} - self.trackers = trackers - - -- load the announce tracker - if tor and tor[1] and tor[1][1] and tor[1][1].key and - tor[1][1].key == "announce" and tor[1][1].value then - - if tor[1][1].value.type and tor[1][1].value.type == "list" then - for _, trac in ipairs(tor[1][1].value) do - table.insert(trackers, trac) - end - else - table.insert(trackers, tor[1][1].value) - end - else - return nil, "Announce field not found" - end - - -- load the announce-list trackers - if tor[1][2] and tor[1][2].key and tor[1][2].key == "announce-list" and tor[1][2].value then - for _, trac_list in ipairs(tor[1][2].value) do - if trac_list.type and trac_list.type == "list" then - for _, trac in ipairs(trac_list) do - table.insert(trackers, trac) - end - else - table.insert(trackers, trac_list) - end - end - end - - return true - end, - - --- Calculates the size of the torrent in bytes - -- @param tor, decoded bencoded torrent file structure - calc_torrent_size = function(self) - local tor = self.tor_struct - local size = nil - if tor[1].type ~= "dict" then return nil end - for _, m in ipairs(tor[1]) do - if m.key == "info" then - if m.value.type ~= "dict" then return nil end - for _, n in ipairs(m.value) do - if n.key == "files" then - size = 0 - for _, f in ipairs(n.value) do - for _, k in ipairs(f) do - if k.key == "length" then - size = size + k.value - break - end - end - end - break - elseif n.key == "length" then - size = n.value - break - end - end - end - end - self.size=size - if size == 0 then return false end - end, - - --- Calculates the info hash using self.info_buf. The info_hash value - -- is used in many communication transactions for identifying the file - -- shared among the bittorrent peers - calc_info_hash = function(self) - local info_hash = openssl.sha1(self.info_buf) - self.info_hash_url = url.escape(info_hash) - self.info_hash = info_hash - self.info_buf = nil - return true - end, - - --- Generates a peer_id similar to the ones generated by Ktorrent version 4.1.1 - generate_peer_id = function(self) - -- let's fool trackers that we use ktorrent just in case they control - -- which client they give peers to - local fingerprint = "-KT4110-" - local chars = {} - local peer_id = fingerprint - -- the full length of a peer_id is 20 bytes but we already have 8 from the fingerprint - for i = 1,12 do - local n = math.random(1,3) - - if n == 1 then - peer_id = peer_id .. string.char( math.random( string.byte("a") , string.byte("z") ) ) - elseif n==2 then - peer_id = peer_id .. string.char( math.random( string.byte("A") , string.byte("Z") ) ) - else - peer_id = peer_id .. string.char( math.random( string.byte("0") , string.byte("9") ) ) - end - end - - return peer_id - end, - - --- Gets the peers from a http tracker when supplied the URL of the tracker - http_tracker_peers = function(self, tracker) - local url, trac_port, url_ext = tracker:match("^http://(.-):(%d-)(/.*)") - if not url then - --probably no port specification - url, url_ext = tracker:match("^http://(.-)(/.*)") - trac_port = "80" - end - - trac_port = tonumber(trac_port) - -- a http torrent tracker request specifying the info_hash of the torrent, our random - -- generated peer_id (with some mods), notifying the tracker that we are just starting - -- to download the torrent, with 0 downloaded and 0 uploaded bytes, an as many bytes - -- left to download as the size of the torrent, requesting 200 peers in a compact format - -- because some trackers refuse connection if they are not explicitly requested that way - local request = "?info_hash=" .. self.info_hash_url .. "&peer_id=" .. self:generate_peer_id() .. - "&port=" .. self.port .. "&uploaded=0&downloaded=0&left=" .. self.size .. - "&event=started&numwant=200&compact=1" - - local response = http.get(url, trac_port, url_ext .. request, nil) - - if not response then - return false, "No response from tracker: " .. tracker - end - - local status, t = bdecode(response.body) - - if not status then - return false, "Could not parse response:"..t - end - - if not t[1] then - return nil, "No response from server." - end - - for _, k in ipairs(t[1]) do - if k.key == "peers" and type(k.value) == "string" then - -- binary peers - for bin_ip, bin_port in string.gmatch(k.value, "(....)(..)") do - local ip = string.format("%d.%d.%d.%d", - bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) - local port = bit.lshift(bin_port:byte(1), 8) + bin_port:byte(2) - local peer = {} - peer.ip = ip - peer.port = port - - if not self.peers[peer.ip] then - self.peers[peer.ip] = {} - self.peers[peer.ip].port = peer.port - if peer.id then self.peers[peer.ip].id = peer.id end - end - end - break - elseif k.key == "peers" and type(k.value) == "table" then - -- table peers - for _, peer_table in ipairs(k.value) do - local peer = {} - for _, f in ipairs(peer_table) do - if f.key == "peer_id" then - peer.id = f.value - elseif f.key == "ip" then - peer.ip = f.value - elseif f.key == "port" then - peer.port = f.value - end - end - if not peer.id then peer.id = "" end - if not self.peers[peer.ip] then - self.peers[peer.ip] = {} - self.peers[peer.ip].port = peer.port - self.peers[peer.ip].id = peer.id - else - self.peers[peer.ip].port = peer.port - end - end - break - end - end - - return true - end, - - --- Gets the peers from udp trackers when supplied the URL of the tracker - -- First we establish a connection to the udp server and then we can request peers - -- for a good specification refer to: - -- http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html - udp_tracker_peers = function(self, tracker) - local host, port = tracker:match("^udp://(.-):(.+)") - if (not host) or (not port) then - return false, "Could not parse tracker url" - end - - local socket = nmap.new_socket("udp") - - -- The initial connection parameters' variables have hello_ prefixed names - local hello_transaction_id = openssl.rand_bytes(4) - local hello_action = "00 00 00 00" -- 0 for a connection request - local hello_connection_id = "00 00 04 17 27 10 19 80" -- identification of the protocol - local hello_packet = bin.pack("HHA", hello_connection_id, hello_action, hello_transaction_id) - local status, msg = socket:sendto(host, port, hello_packet) - if not status then return false, msg end - - status, msg = socket:receive() - if not status then return false, "Could not connect to tracker:"..tracker.." reason:"..msg end - - local _, r_action, r_transaction_id, r_connection_id =bin.unpack("H4A4A8",msg) - - if not (r_transaction_id == hello_transaction_id) then - return false, "Received transaction ID not equivalent to sent transaction ID" - end - - -- the action in the response has to be 0 too - if not r_action == "00000000" then - return false, "Wrong action field, usualy caused by an erroneous request" - end - - -- established a connection, and now for an announce message, to which a - -- response holds the peers - - -- the announce connection parameters' variables are prefixed with a_ - local a_action = "00 00 00 01" -- 1 for announce - local a_transaction_id = openssl.rand_bytes(4) - local a_info_hash = self.info_hash -- info_hash of the torrent - local a_peer_id = self:generate_peer_id() - local a_downloaded = "00 00 00 00 00 00 00 00" -- 0 bytes downloaded - - local a_left = stdnse.tohex(self.size) -- bytes left to download is the size of torrent - a_left = string.rep("0", 16-#a_left) .. a_left - - local a_uploaded = "00 00 00 00 00 00 00 00" -- 0 bytes uploaded - local a_event = "00 00 00 02" -- value of 2 for started torrent - local a_ip = "00 00 00 00" -- not necessary to specify our ip since it's resolved - -- by tracker automatically - local a_key = openssl.rand_bytes(4) - local a_num_want = "FF FF FF FF" -- request for many many peers - local a_port = "1A E1" -- 6881 the port "we are listening on" - local a_extensions = "00 00" -- client recognizes no extensions of the bittorrent proto - local announce_packet = bin.pack("AHAAAHHHHHAHHH", r_connection_id, a_action, a_transaction_id, - a_info_hash, a_peer_id, a_downloaded, a_left, a_uploaded, a_event, a_ip, a_key, - a_num_want, a_port, a_extensions) - - status, msg = socket:sendto(host, port, announce_packet) - if not status then - return false, "Couldn't send announce message, reason: "..msg - end - - status, msg = socket:receive() - if not status then - return false, "Didn't receive response to announce message, reason: "..msg - end - local pos, p_action, p_transaction_id, p_interval, p_leechers, p_seeders = bin.unpack("H4A4H4H4H4",msg) - - -- the action field in the response has to be 1 (like the sent response) - if not (p_action == "00000001") then - return false, "Action in response to announce erroneous" - end - if not (p_transaction_id == a_transaction_id) then - return false, "Transaction ID in response to announce message not equal to original" - end - - -- parse peers from msg:sub(pos, #msg) - - for bin_ip, bin_port in msg:sub(pos,#msg):gmatch("(....)(..)") do - local ip = string.format("%d.%d.%d.%d", - bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) - local port = bit.lshift(bin_port:byte(1), 8) + bin_port:byte(2) - local peer = {} - peer.ip = ip - peer.port = port - if not self.peers[peer.ip] then - self.peers[peer.ip] = {} - self.peers[peer.ip].port = peer.port - else - self.peers[peer.ip].port = peer.port - end - end - - return true - end + new = function(self) + local o ={} + setmetatable(o, self) + self.__index = self + + self.buffer = nil -- buffer to keep the torrent + self.tor_struct = nil -- the decoded structure from the bencoded buffer + + self.trackers = {} -- list of trackers {"tr1", "tr2", "tr3"...} + self.port = 6881 -- port on which our peer "listens" / it doesn't actually listen + self.size = nil -- size of the files in the torrent + + self.info_buf = nil --buffer for info_hash + self.info_hash = nil --info_hash binary string + self.info_hash_url = nil --info_hash escaped + + self.peers = {} -- peers = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} + self.nodes = {} -- nodes = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...} + return o + end, + + --- Loads trackers and similar information for a torrent from a magnet link. + load_from_magnet = function(self, magnet) + local info_hash_hex = magnet:match("^magnet:%?xt=urn:btih:(%w+)&") + if not info_hash_hex then + return false, "Erroneous magnet link" + end + self.info_hash = bin.pack("H",info_hash_hex) + + local pos = #info_hash_hex + 21 + local name = magnet:sub(pos,#magnet):match("^&dn=(.-)&") + if name then + pos = pos + 4 + #name + end + magnet = magnet:sub(pos,#magnet) + for tracker in magnet:gmatch("&tr=([^&]+)") do + local trac = url.unescape(tracker) + table.insert(self.trackers, trac) + end + self.size = 50 + end, + + --- Reads a torrent file, loads self.buffer and parses it using + -- self:parse_buffer(), then self:calc_info_hash() + -- + -- @param filename, string containing filename of the torrent file + -- @return boolean indicating whether loading went alright + -- @return err string with error message if loadin went wrong + load_from_file = function(self, filename) + if not filename then return false, "No filename specified." end + + local file = io.open(filename, "r") + if not file then return false, "Cannot open file: "..filename end + + self.buffer = file:read("*a") + + local status, err = self:parse_buffer() + if not status then + return false, "Could not parse file: ".. err + end + + status, err = self:calc_info_hash() + if not status then + return false, "Could not calculate info_hash: " .. err + end + + status, err = self:load_trackers() + if not status then + return false, "Could not load trackers: " .. err + end + + status, err = self:calc_torrent_size() + if not status then + if not err then err = "" end + return false, "Could not calculate torrent size: " .. err + end + + file:close() + return true + end, + + --- Gets peers available from the loaded trackers + trackers_peers = function(self) + for _, tracker in ipairs(self.trackers) do + local status, err + + if tracker:match("^http://") then -- http tracker + status, err = self:http_tracker_peers(tracker) + if not status then + stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err) + end + elseif tracker:match("^udp://") then -- udp tracker + status, err = self:udp_tracker_peers(tracker) + if not status then + stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err) + end + else -- unknown tracker + stdnse.print_debug("Unknown tracker protocol for: "..tracker) + end + --if not status then return false, err end + end + + return true + end, + + --- Runs the three threads which do a DHT discovery of nodes and peers. + -- The default timeout for this discovery is 30 seconds but it can be + -- set through the timeout argument. + dht_peers = function(self, timeout) + stdnse.print_debug("bittorrent: Starting DHT peers discovery") + + if next(self.peers) == nil then + stdnse.print_debug("bittorrent: No peers detected") + return + end + + if not timeout or type(timeout)~="number" then timeout = 30 end + + -- peer node table aka the condvar! + local pnt = {} + pnt.peers = {} + pnt.peers_dht_ping = self.peers + + pnt.nodes = {} + pnt.nodes_get_peers = {} + pnt.nodes_find_node = self.nodes + + pnt.node_id = openssl.rand_bytes(20) + pnt.info_hash = self.info_hash + + local condvar = nmap.condvar(pnt) + + local dht_ping_co = stdnse.new_thread(dht_ping_thread, pnt, timeout) + local find_node_co = stdnse.new_thread(find_node_thread, pnt, timeout) + local get_peers_co = stdnse.new_thread(get_peers_thread, pnt, timeout) + + while true do + stdnse.sleep(0.5) + if coroutine.status(dht_ping_co) == "dead" and + coroutine.status(find_node_co) == "dead" and + coroutine.status(get_peers_co) == "dead" then + break + end + end + + self.peers = pnt.peers + self.nodes = pnt.nodes + + -- Add some residue nodes and peers + for peer_ip, peer_info in pairs(pnt.peers_dht_ping) do + if not self.peers[peer_ip] then + self.peers[peer_ip] = peer_info + end + end + for node_ip, node_info in pairs(pnt.nodes_find_node) do + if not self.nodes[node_ip] then + self.nodes[node_ip] = node_info + end + end + for node_ip, node_info in pairs(pnt.nodes_get_peers) do + if not self.nodes[node_ip] then + self.nodes[node_ip] = node_info + end + end + end, + + --- Parses self.buffer, fills self.tor_struct, self.info_buf + -- This function is similar to the bdecode function but it has a few + -- additions for calculating torrent file specific fields + parse_buffer = function(self) + local buf = self.buffer + + local len = #buf + + -- the main table + local t = {} + self.tor_struct = t + local stack = {} + + local pos = 1 + local cur = {} + cur.type = "list" + cur.ref = t + table.insert(stack, cur) + cur.ref.type="list" + + -- starting and ending position of the info dict + local info_pos_start, info_pos_end, info_buf_count = nil, nil, 0 + + while true do + if pos == len or (len-pos)==-1 then break end + + if cur.type == "list" then + -- next element is a string + if tonumber( string.char( buf:byte(pos) ) ) then + local str + str, pos = bdec_string(buf, pos) + if not str then return nil, "Error parsing string", pos end + table.insert(cur.ref, str) + + -- next element is a number + elseif "i" == string.char(buf:byte(pos)) then + local num + num, pos = bdec_number(buf, pos) + if not num then return nil, "Error parsing number", pos end + table.insert(cur.ref, num) + + -- next element is a list + elseif "l" == string.char(buf:byte(pos)) then + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count +1 + end + + local new_list = {} + new_list.type="list" + table.insert(cur.ref, new_list) + + cur = {} + cur.type = "list" + cur.ref = new_list + table.insert(stack, cur) + pos = pos+1 + + --next element is a dict + elseif "d" == string.char(buf:byte(pos)) then + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count +1 + end + + local new_dict = {} + new_dict.type = "dict" + table.insert(cur.ref, new_dict) + + cur = {} + cur.type = "dict" + cur.ref = new_dict + table.insert(stack, cur) + pos = pos+1 + + --escape from the list + elseif "e" == string.char(buf:byte(pos)) then + if info_buf_count == 0 then + info_pos_end = pos-1 + end + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count -1 + end + + table.remove(stack, #stack) + cur = stack[#stack] + if not cur then return nil, "Problem with list closure:", pos end + pos = pos+1 + else + return nil, "Unknown type found.", pos + end + + elseif cur.type == "dict" then + local item = {} -- {key = , value = <.*>} + -- key + if tonumber( string.char( buf:byte(pos) ) ) then + local str + local tmp_pos = pos + str, pos = bdec_string(buf, pos) + if not str then return nil, "Error parsing string.", pos end + item.key = str + -- fill the info_pos_start + if item.key == "info" and not info_pos_start then info_pos_start = pos end + + elseif "e" == string.char(buf:byte(pos)) then + if info_buf_count == 0 then + info_pos_end = pos-1 + end + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count -1 + end + + table.remove(stack, #stack) + cur = stack[#stack] + if not cur then return nil, "Problem with list closure:", pos end + pos = pos+1 + + else + return nil, "A dict key has to be a string or escape.", pos + end + + -- value + -- next element is a string + if tonumber( string.char( buf:byte(pos) ) ) then + local str + str, pos = bdec_string(buf, pos) + if not str then return nil, "Error parsing string.", pos end + item.value = str + table.insert(cur.ref, item) + + --next element is a number + elseif "i" == string.char(buf:byte(pos)) then + local num + num, pos = bdec_number(buf, pos) + if not num then return nil, "Error parsing number.", pos end + item.value = num + table.insert(cur.ref, item) + + -- next element is a list + elseif "l" == string.char(buf:byte(pos)) then + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count +1 + end + + item.value = {} + item.value.type = "list" + table.insert(cur.ref, item) + + cur = {} + cur.type = "list" + cur.ref = item.value + + table.insert(stack, cur) + pos = pos+1 + + --next element is a dict + elseif "d" == string.char(buf:byte(pos)) then + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count +1 + end + + item.value = {} + item.value.type = "dict" + table.insert(cur.ref, item) + + cur = {} + cur.type = "dict" + cur.ref = item.value + + table.insert(stack, cur) + pos = pos+1 + + --escape from the dict + elseif "e" == string.char(buf:byte(pos)) then + if info_buf_count == 0 then + info_pos_end = pos-1 + end + if info_pos_start and (not info_pos_end) then + info_buf_count = info_buf_count -1 + end + + table.remove(stack, #stack) + cur = stack[#stack] + if not cur then return false, "Problem with dict closure", pos end + pos = pos+1 + else + return false, "Error parsing file, unknown type found", pos + end + else + return false, "Invalid type of structure. Fix the code." + end + end -- while(true) + + -- next(stack) is never gonna be nil because we're always in the main list + -- next(stack, next(stack)) should be nil if we're in the main list + if next(stack, next(stack)) then + return false, "Probably file incorrect format" + end + + self.info_buf = buf:sub(info_pos_start, info_pos_end) + + return true + end, + + --- Loads the list of trackers in self.trackers from self.tor_struct + load_trackers = function(self) + local tor = self.tor_struct + local trackers = {} + self.trackers = trackers + + -- load the announce tracker + if tor and tor[1] and tor[1][1] and tor[1][1].key and + tor[1][1].key == "announce" and tor[1][1].value then + + if tor[1][1].value.type and tor[1][1].value.type == "list" then + for _, trac in ipairs(tor[1][1].value) do + table.insert(trackers, trac) + end + else + table.insert(trackers, tor[1][1].value) + end + else + return nil, "Announce field not found" + end + + -- load the announce-list trackers + if tor[1][2] and tor[1][2].key and tor[1][2].key == "announce-list" and tor[1][2].value then + for _, trac_list in ipairs(tor[1][2].value) do + if trac_list.type and trac_list.type == "list" then + for _, trac in ipairs(trac_list) do + table.insert(trackers, trac) + end + else + table.insert(trackers, trac_list) + end + end + end + + return true + end, + + --- Calculates the size of the torrent in bytes + -- @param tor, decoded bencoded torrent file structure + calc_torrent_size = function(self) + local tor = self.tor_struct + local size = nil + if tor[1].type ~= "dict" then return nil end + for _, m in ipairs(tor[1]) do + if m.key == "info" then + if m.value.type ~= "dict" then return nil end + for _, n in ipairs(m.value) do + if n.key == "files" then + size = 0 + for _, f in ipairs(n.value) do + for _, k in ipairs(f) do + if k.key == "length" then + size = size + k.value + break + end + end + end + break + elseif n.key == "length" then + size = n.value + break + end + end + end + end + self.size=size + if size == 0 then return false end + end, + + --- Calculates the info hash using self.info_buf. The info_hash value + -- is used in many communication transactions for identifying the file + -- shared among the bittorrent peers + calc_info_hash = function(self) + local info_hash = openssl.sha1(self.info_buf) + self.info_hash_url = url.escape(info_hash) + self.info_hash = info_hash + self.info_buf = nil + return true + end, + + --- Generates a peer_id similar to the ones generated by Ktorrent version 4.1.1 + generate_peer_id = function(self) + -- let's fool trackers that we use ktorrent just in case they control + -- which client they give peers to + local fingerprint = "-KT4110-" + local chars = {} + local peer_id = fingerprint + -- the full length of a peer_id is 20 bytes but we already have 8 from the fingerprint + for i = 1,12 do + local n = math.random(1,3) + + if n == 1 then + peer_id = peer_id .. string.char( math.random( string.byte("a") , string.byte("z") ) ) + elseif n==2 then + peer_id = peer_id .. string.char( math.random( string.byte("A") , string.byte("Z") ) ) + else + peer_id = peer_id .. string.char( math.random( string.byte("0") , string.byte("9") ) ) + end + end + + return peer_id + end, + + --- Gets the peers from a http tracker when supplied the URL of the tracker + http_tracker_peers = function(self, tracker) + local url, trac_port, url_ext = tracker:match("^http://(.-):(%d-)(/.*)") + if not url then + --probably no port specification + url, url_ext = tracker:match("^http://(.-)(/.*)") + trac_port = "80" + end + + trac_port = tonumber(trac_port) + -- a http torrent tracker request specifying the info_hash of the torrent, our random + -- generated peer_id (with some mods), notifying the tracker that we are just starting + -- to download the torrent, with 0 downloaded and 0 uploaded bytes, an as many bytes + -- left to download as the size of the torrent, requesting 200 peers in a compact format + -- because some trackers refuse connection if they are not explicitly requested that way + local request = "?info_hash=" .. self.info_hash_url .. "&peer_id=" .. self:generate_peer_id() .. + "&port=" .. self.port .. "&uploaded=0&downloaded=0&left=" .. self.size .. + "&event=started&numwant=200&compact=1" + + local response = http.get(url, trac_port, url_ext .. request, nil) + + if not response then + return false, "No response from tracker: " .. tracker + end + + local status, t = bdecode(response.body) + + if not status then + return false, "Could not parse response:"..t + end + + if not t[1] then + return nil, "No response from server." + end + + for _, k in ipairs(t[1]) do + if k.key == "peers" and type(k.value) == "string" then + -- binary peers + for bin_ip, bin_port in string.gmatch(k.value, "(....)(..)") do + local ip = string.format("%d.%d.%d.%d", + bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) + local port = bit.lshift(bin_port:byte(1), 8) + bin_port:byte(2) + local peer = {} + peer.ip = ip + peer.port = port + + if not self.peers[peer.ip] then + self.peers[peer.ip] = {} + self.peers[peer.ip].port = peer.port + if peer.id then self.peers[peer.ip].id = peer.id end + end + end + break + elseif k.key == "peers" and type(k.value) == "table" then + -- table peers + for _, peer_table in ipairs(k.value) do + local peer = {} + for _, f in ipairs(peer_table) do + if f.key == "peer_id" then + peer.id = f.value + elseif f.key == "ip" then + peer.ip = f.value + elseif f.key == "port" then + peer.port = f.value + end + end + if not peer.id then peer.id = "" end + if not self.peers[peer.ip] then + self.peers[peer.ip] = {} + self.peers[peer.ip].port = peer.port + self.peers[peer.ip].id = peer.id + else + self.peers[peer.ip].port = peer.port + end + end + break + end + end + + return true + end, + + --- Gets the peers from udp trackers when supplied the URL of the tracker + -- First we establish a connection to the udp server and then we can request peers + -- for a good specification refer to: + -- http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html + udp_tracker_peers = function(self, tracker) + local host, port = tracker:match("^udp://(.-):(.+)") + if (not host) or (not port) then + return false, "Could not parse tracker url" + end + + local socket = nmap.new_socket("udp") + + -- The initial connection parameters' variables have hello_ prefixed names + local hello_transaction_id = openssl.rand_bytes(4) + local hello_action = "00 00 00 00" -- 0 for a connection request + local hello_connection_id = "00 00 04 17 27 10 19 80" -- identification of the protocol + local hello_packet = bin.pack("HHA", hello_connection_id, hello_action, hello_transaction_id) + local status, msg = socket:sendto(host, port, hello_packet) + if not status then return false, msg end + + status, msg = socket:receive() + if not status then return false, "Could not connect to tracker:"..tracker.." reason:"..msg end + + local _, r_action, r_transaction_id, r_connection_id =bin.unpack("H4A4A8",msg) + + if not (r_transaction_id == hello_transaction_id) then + return false, "Received transaction ID not equivalent to sent transaction ID" + end + + -- the action in the response has to be 0 too + if not r_action == "00000000" then + return false, "Wrong action field, usualy caused by an erroneous request" + end + + -- established a connection, and now for an announce message, to which a + -- response holds the peers + + -- the announce connection parameters' variables are prefixed with a_ + local a_action = "00 00 00 01" -- 1 for announce + local a_transaction_id = openssl.rand_bytes(4) + local a_info_hash = self.info_hash -- info_hash of the torrent + local a_peer_id = self:generate_peer_id() + local a_downloaded = "00 00 00 00 00 00 00 00" -- 0 bytes downloaded + + local a_left = stdnse.tohex(self.size) -- bytes left to download is the size of torrent + a_left = string.rep("0", 16-#a_left) .. a_left + + local a_uploaded = "00 00 00 00 00 00 00 00" -- 0 bytes uploaded + local a_event = "00 00 00 02" -- value of 2 for started torrent + local a_ip = "00 00 00 00" -- not necessary to specify our ip since it's resolved + -- by tracker automatically + local a_key = openssl.rand_bytes(4) + local a_num_want = "FF FF FF FF" -- request for many many peers + local a_port = "1A E1" -- 6881 the port "we are listening on" + local a_extensions = "00 00" -- client recognizes no extensions of the bittorrent proto + local announce_packet = bin.pack("AHAAAHHHHHAHHH", r_connection_id, a_action, a_transaction_id, + a_info_hash, a_peer_id, a_downloaded, a_left, a_uploaded, a_event, a_ip, a_key, + a_num_want, a_port, a_extensions) + + status, msg = socket:sendto(host, port, announce_packet) + if not status then + return false, "Couldn't send announce message, reason: "..msg + end + + status, msg = socket:receive() + if not status then + return false, "Didn't receive response to announce message, reason: "..msg + end + local pos, p_action, p_transaction_id, p_interval, p_leechers, p_seeders = bin.unpack("H4A4H4H4H4",msg) + + -- the action field in the response has to be 1 (like the sent response) + if not (p_action == "00000001") then + return false, "Action in response to announce erroneous" + end + if not (p_transaction_id == a_transaction_id) then + return false, "Transaction ID in response to announce message not equal to original" + end + + -- parse peers from msg:sub(pos, #msg) + + for bin_ip, bin_port in msg:sub(pos,#msg):gmatch("(....)(..)") do + local ip = string.format("%d.%d.%d.%d", + bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4)) + local port = bit.lshift(bin_port:byte(1), 8) + bin_port:byte(2) + local peer = {} + peer.ip = ip + peer.port = port + if not self.peers[peer.ip] then + self.peers[peer.ip] = {} + self.peers[peer.ip].port = peer.port + else + self.peers[peer.ip].port = peer.port + end + end + + return true + end } diff --git a/nselib/brute.lua b/nselib/brute.lua index 5bde62192..5265fa6a9 100644 --- a/nselib/brute.lua +++ b/nselib/brute.lua @@ -40,23 +40,23 @@ -- The following example code demonstrates how the Error object can be used. -- -- --- -- After a number of incorrect attempts VNC blocks us, so we abort --- if ( not(status) and x:match("Too many authentication failures") ) then --- local err = brute.Error:new( data ) --- -- signal the engine to abort --- err:setAbort( true ) --- return false, err --- elseif ( not(status) ) then --- local err = brute.Error:new( "VNC handshake failed" ) --- -- This might be temporary, signal the engine to retry --- err:setRetry( true ) --- return false, err --- end --- . --- . --- . --- -- Return a simple error, no retry needed --- return false, brute.Error:new( "Incorrect password" ) +-- -- After a number of incorrect attempts VNC blocks us, so we abort +-- if ( not(status) and x:match("Too many authentication failures") ) then +-- local err = brute.Error:new( data ) +-- -- signal the engine to abort +-- err:setAbort( true ) +-- return false, err +-- elseif ( not(status) ) then +-- local err = brute.Error:new( "VNC handshake failed" ) +-- -- This might be temporary, signal the engine to retry +-- err:setRetry( true ) +-- return false, err +-- end +-- . +-- . +-- . +-- -- Return a simple error, no retry needed +-- return false, brute.Error:new( "Incorrect password" ) -- -- -- The purpose of the check method is to be able to determine @@ -80,51 +80,51 @@ -- Driver that sends each username and password over a socket. -- -- --- Driver = { --- new = function(self, host, port, options) --- local o = {} --- setmetatable(o, self) --- self.__index = self --- o.host = host --- o.port = port --- o.options = options --- return o --- end, --- connect = function( self ) --- self.socket = nmap.new_socket() --- return self.socket:connect( self.host, self.port ) --- end, --- disconnect = function( self ) --- return self.socket:close() --- end, --- check = function( self ) --- return true --- end, --- login = function( self, username, password ) --- local status, err, data --- status, err = self.socket:send( username .. ":" .. password) --- status, data = self.socket:receive_bytes(1) +-- Driver = { +-- new = function(self, host, port, options) +-- local o = {} +-- setmetatable(o, self) +-- self.__index = self +-- o.host = host +-- o.port = port +-- o.options = options +-- return o +-- end, +-- connect = function( self ) +-- self.socket = nmap.new_socket() +-- return self.socket:connect( self.host, self.port ) +-- end, +-- disconnect = function( self ) +-- return self.socket:close() +-- end, +-- check = function( self ) +-- return true +-- end, +-- login = function( self, username, password ) +-- local status, err, data +-- status, err = self.socket:send( username .. ":" .. password) +-- status, data = self.socket:receive_bytes(1) -- --- if ( data:match("SUCCESS") ) then --- return true, brute.Account:new(username, password, "OPEN") --- end --- return false, brute.Error:new( "login failed" ) --- end, --- } +-- if ( data:match("SUCCESS") ) then +-- return true, brute.Account:new(username, password, "OPEN") +-- end +-- return false, brute.Error:new( "login failed" ) +-- end, +-- } -- -- -- The following sample code illustrates how to pass the Driver -- off to the brute engine. -- -- --- action = function(host, port) --- local options = { key1 = val1, key2 = val2 } --- local status, accounts = brute.Engine:new(Driver, host, port, options):start() --- if( not(status) ) then --- return accounts --- end --- return stdnse.format_output( true, accounts ) +-- action = function(host, port) +-- local options = { key1 = val1, key2 = val2 } +-- local status, accounts = brute.Engine:new(Driver, host, port, options):start() +-- if( not(status) ) then +-- return accounts -- end +-- return stdnse.format_output( true, accounts ) +-- end -- -- -- For a complete example of a brute implementation consult the @@ -171,11 +171,11 @@ -- Version 0.73 -- Created 06/12/2010 - v0.1 - created by Patrik Karlsson -- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver --- +-- -- Revised 07/21/2010 - v0.3 - documented missing argument brute.mode -- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to --- include statistics, and to display "no accounts --- found" message. +-- include statistics, and to display "no accounts +-- found" message. -- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per -- David's request. -- Revised 08/30/2010 - v0.6 - added support for custom iterators and did some @@ -184,7 +184,7 @@ -- Revised 07/07/2011 - v0.71- fixed some minor bugs, and changed credential -- iterator to use a file handle instead of table -- Revised 07/21/2011 - v0.72- added code to allow script reporting invalid --- (non existing) accounts using setInvalidAccount +-- (non existing) accounts using setInvalidAccount -- Revised 11/12/2011 - v0.73- added support for max guesses per account to -- prevent account lockouts. -- bugfix: added support for guessing the username @@ -225,101 +225,101 @@ _ENV = stdnse.module("brute", stdnse.seeall) -- Options = { - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self - o.emptypass = self.checkBoolArg("brute.emptypass", false) - o.useraspass = self.checkBoolArg("brute.useraspass", true) - o.firstonly = self.checkBoolArg("brute.firstonly", false) - o.passonly = self.checkBoolArg("brute.passonly", false) - o.max_retries = tonumber( nmap.registry.args["brute.retries"] ) or 3 - o.delay = tonumber( nmap.registry.args["brute.delay"] ) or 0 - o.max_guesses = tonumber( nmap.registry.args["brute.guesses"] ) or 0 + o.emptypass = self.checkBoolArg("brute.emptypass", false) + o.useraspass = self.checkBoolArg("brute.useraspass", true) + o.firstonly = self.checkBoolArg("brute.firstonly", false) + o.passonly = self.checkBoolArg("brute.passonly", false) + o.max_retries = tonumber( nmap.registry.args["brute.retries"] ) or 3 + o.delay = tonumber( nmap.registry.args["brute.delay"] ) or 0 + o.max_guesses = tonumber( nmap.registry.args["brute.guesses"] ) or 0 - return o - end, + return o + end, - --- Checks if a script argument is boolean true or false - -- - -- @param arg string containing the name of the argument to check - -- @param default boolean containing the default value - -- @return boolean, true if argument evaluates to 1 or true, else false - checkBoolArg = function( arg, default ) - local val = stdnse.get_script_args(arg) or default - return (val == "true" or val==true or tonumber(val)==1) - end, + --- Checks if a script argument is boolean true or false + -- + -- @param arg string containing the name of the argument to check + -- @param default boolean containing the default value + -- @return boolean, true if argument evaluates to 1 or true, else false + checkBoolArg = function( arg, default ) + local val = stdnse.get_script_args(arg) or default + return (val == "true" or val==true or tonumber(val)==1) + end, - --- Sets the brute mode to either iterate over users or passwords - -- @see description for more information. - -- - -- @param mode string containing either "user" or "password" - -- @return status true on success else false - -- @return err string containing the error message on failure - setMode = function( self, mode ) - local modes = { "password", "user", "creds" } - local supported = false + --- Sets the brute mode to either iterate over users or passwords + -- @see description for more information. + -- + -- @param mode string containing either "user" or "password" + -- @return status true on success else false + -- @return err string containing the error message on failure + setMode = function( self, mode ) + local modes = { "password", "user", "creds" } + local supported = false - for _, m in ipairs(modes) do - if ( mode == m ) then supported = true end - end + for _, m in ipairs(modes) do + if ( mode == m ) then supported = true end + end - if ( not(supported) ) then - stdnse.print_debug("ERROR: brute.options.setMode: mode %s not supported", mode) - return false, "Unsupported mode" - else - self.mode = mode - end - return true - end, + if ( not(supported) ) then + stdnse.print_debug("ERROR: brute.options.setMode: mode %s not supported", mode) + return false, "Unsupported mode" + else + self.mode = mode + end + return true + end, - --- Sets an option parameter - -- - -- @param param string containing the parameter name - -- @param value string containing the parameter value - setOption = function( self, param, value ) self[param] = value end, + --- Sets an option parameter + -- + -- @param param string containing the parameter name + -- @param value string containing the parameter value + setOption = function( self, param, value ) self[param] = value end, - --- Set an alternate title for the result output (default: Accounts) - -- - -- @param title string containing the title value - setTitle = function(self, title) self.title = title end, + --- Set an alternate title for the result output (default: Accounts) + -- + -- @param title string containing the title value + setTitle = function(self, title) self.title = title end, } -- The account object which is to be reported back from each driver Account = { - --- Creates a new instance of the Account class - -- - -- @param username containing the user's name - -- @param password containing the user's password - -- @param state containing the account state and should be one of the - -- following OPEN, LOCKED, - -- DISABLED. - new = function(self, username, password, state) - local o = { username = username, password = password, state = state } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new instance of the Account class + -- + -- @param username containing the user's name + -- @param password containing the user's password + -- @param state containing the account state and should be one of the + -- following OPEN, LOCKED, + -- DISABLED. + new = function(self, username, password, state) + local o = { username = username, password = password, state = state } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts an account object to a printable script - -- - -- @return string representation of object - toString = function( self ) - local c - if ( #self.username > 0 ) then - c = ("%s:%s"):format( self.username, #self.password > 0 and self.password or "" ) - else - c = ("%s"):format( ( self.password and #self.password > 0 ) and self.password or "" ) - end - if ( creds.StateMsg[self.state] ) then - return ( "%s - %s"):format(c, creds.StateMsg[self.state] ) - else - return ("%s"):format(c) - end - end, + --- Converts an account object to a printable script + -- + -- @return string representation of object + toString = function( self ) + local c + if ( #self.username > 0 ) then + c = ("%s:%s"):format( self.username, #self.password > 0 and self.password or "" ) + else + c = ("%s"):format( ( self.password and #self.password > 0 ) and self.password or "" ) + end + if ( creds.StateMsg[self.state] ) then + return ( "%s - %s"):format(c, creds.StateMsg[self.state] ) + else + return ("%s"):format(c) + end + end, } @@ -327,613 +327,613 @@ Account = -- It also contains the error message, if one was returned from the driver. Error = { - retry = false, + retry = false, - new = function(self, msg) - local o = { msg = msg, done = false } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self, msg) + local o = { msg = msg, done = false } + setmetatable(o, self) + self.__index = self + return o + end, - --- Is the error recoverable? - -- - -- @return status true if the error is recoverable, false if not - isRetry = function( self ) return self.retry end, + --- Is the error recoverable? + -- + -- @return status true if the error is recoverable, false if not + isRetry = function( self ) return self.retry end, - --- Set the error as recoverable - -- - -- @param r boolean true if the engine should attempt to retry the - -- credentials, unset or false if not - setRetry = function( self, r ) self.retry = r end, + --- Set the error as recoverable + -- + -- @param r boolean true if the engine should attempt to retry the + -- credentials, unset or false if not + setRetry = function( self, r ) self.retry = r end, - --- Set the error as abort all threads - -- - -- @param b boolean true if the engine should abort guessing on all threads - setAbort = function( self, b ) self.abort = b end, + --- Set the error as abort all threads + -- + -- @param b boolean true if the engine should abort guessing on all threads + setAbort = function( self, b ) self.abort = b end, - --- Was the error abortable - -- - -- @return status true if the driver flagged the engine to abort - isAbort = function( self ) return self.abort end, + --- Was the error abortable + -- + -- @return status true if the driver flagged the engine to abort + isAbort = function( self ) return self.abort end, - --- Get the error message reported - -- - -- @return msg string containing the error message - getMessage = function( self ) return self.msg end, + --- Get the error message reported + -- + -- @return msg string containing the error message + getMessage = function( self ) return self.msg end, - --- Is the thread done? - -- - -- @return status true if done, false if not - isDone = function( self ) return self.done end, + --- Is the thread done? + -- + -- @return status true if done, false if not + isDone = function( self ) return self.done end, - --- Signals the engine that the thread is done and should be terminated - -- - -- @param b boolean true if done, unset or false if not - setDone = function( self, b ) self.done = b end, + --- Signals the engine that the thread is done and should be terminated + -- + -- @param b boolean true if done, unset or false if not + setDone = function( self, b ) self.done = b end, - -- Marks the username as invalid, aborting further guessing. - -- @param username - setInvalidAccount = function(self, username) - self.invalid_account = username - end, + -- Marks the username as invalid, aborting further guessing. + -- @param username + setInvalidAccount = function(self, username) + self.invalid_account = username + end, - -- Checks if the error reported the account as invalid. - -- @return username string containing the invalid account - isInvalidAccount = function(self) - return self.invalid_account - end, + -- Checks if the error reported the account as invalid. + -- @return username string containing the invalid account + isInvalidAccount = function(self) + return self.invalid_account + end, } -- The brute engine, doing all the nasty work Engine = { - STAT_INTERVAL = 20, + STAT_INTERVAL = 20, - --- Creates a new Engine instance - -- - -- @param driver, the driver class that should be instantiated - -- @param host table as passed to the action method of the script - -- @param port table as passed to the action method of the script - -- @param options table containing any script specific options - -- @return o new Engine instance - new = function(self, driver, host, port, options) - local o = { - driver = driver, - host = host, - port = port, - driver_options = options, - terminate_all = false, - error = nil, - counter = 0, - threads = {}, - tps = {}, - iterator = nil , - usernames = usernames_iterator(), - passwords = passwords_iterator(), - found_accounts = {}, - account_guesses = {}, - options = Options:new(), - } - setmetatable(o, self) - self.__index = self - o.max_threads = stdnse.get_script_args("brute.threads") or 10 - return o - end, + --- Creates a new Engine instance + -- + -- @param driver, the driver class that should be instantiated + -- @param host table as passed to the action method of the script + -- @param port table as passed to the action method of the script + -- @param options table containing any script specific options + -- @return o new Engine instance + new = function(self, driver, host, port, options) + local o = { + driver = driver, + host = host, + port = port, + driver_options = options, + terminate_all = false, + error = nil, + counter = 0, + threads = {}, + tps = {}, + iterator = nil , + usernames = usernames_iterator(), + passwords = passwords_iterator(), + found_accounts = {}, + account_guesses = {}, + options = Options:new(), + } + setmetatable(o, self) + self.__index = self + o.max_threads = stdnse.get_script_args("brute.threads") or 10 + return o + end, - --- Sets the username iterator - -- - -- @param usernameIterator function to set as a username iterator - setUsernameIterator = function(self,usernameIterator) - self.usernames = usernameIterator - end, + --- Sets the username iterator + -- + -- @param usernameIterator function to set as a username iterator + setUsernameIterator = function(self,usernameIterator) + self.usernames = usernameIterator + end, - --- Sets the password iterator - -- - -- @param passwordIterator function to set as a password iterator - setPasswordIterator = function(self,passwordIterator) - self.passwords = passwordIterator - end, + --- Sets the password iterator + -- + -- @param passwordIterator function to set as a password iterator + setPasswordIterator = function(self,passwordIterator) + self.passwords = passwordIterator + end, - --- Limit the number of worker threads - -- - -- @param max number containing the maximum number of allowed threads - setMaxThreads = function( self, max ) self.max_threads = max end, + --- Limit the number of worker threads + -- + -- @param max number containing the maximum number of allowed threads + setMaxThreads = function( self, max ) self.max_threads = max end, - --- Returns the number of non-dead threads - -- - -- @return count number of non-dead threads - threadCount = function( self ) - local count = 0 + --- Returns the number of non-dead threads + -- + -- @return count number of non-dead threads + threadCount = function( self ) + local count = 0 - for thread in pairs(self.threads) do - if ( coroutine.status(thread) == "dead" ) then - self.threads[thread] = nil - else - count = count + 1 - end - end - return count - end, + for thread in pairs(self.threads) do + if ( coroutine.status(thread) == "dead" ) then + self.threads[thread] = nil + else + count = count + 1 + end + end + return count + end, - --- Calculates the number of threads that are actually doing any work - -- - -- @return count number of threads performing activity - activeThreads = function( self ) - local count = 0 - for thread, v in pairs(self.threads) do - if ( v.guesses ~= nil ) then count = count + 1 end - end - return count - end, + --- Calculates the number of threads that are actually doing any work + -- + -- @return count number of threads performing activity + activeThreads = function( self ) + local count = 0 + for thread, v in pairs(self.threads) do + if ( v.guesses ~= nil ) then count = count + 1 end + end + return count + end, - --- Iterator wrapper used to iterate over all registered iterators - -- - -- @return iterator function - get_next_credential = function( self ) - local function next_credential () - for user, pass in self.iterator do - -- makes sure the credentials have not been tested before - self.used_creds = self.used_creds or {} - pass = pass or "nil" - if ( not(self.used_creds[user..pass]) ) then - self.used_creds[user..pass] = true - coroutine.yield( user, pass ) - end - end - while true do coroutine.yield(nil, nil) end - end - return coroutine.wrap( next_credential ) - end, + --- Iterator wrapper used to iterate over all registered iterators + -- + -- @return iterator function + get_next_credential = function( self ) + local function next_credential () + for user, pass in self.iterator do + -- makes sure the credentials have not been tested before + self.used_creds = self.used_creds or {} + pass = pass or "nil" + if ( not(self.used_creds[user..pass]) ) then + self.used_creds[user..pass] = true + coroutine.yield( user, pass ) + end + end + while true do coroutine.yield(nil, nil) end + end + return coroutine.wrap( next_credential ) + end, - --- Does the actual authentication request - -- - -- @return true on success, false on failure - -- @return response Account on success, Error on failure - doAuthenticate = function( self ) + --- Does the actual authentication request + -- + -- @return true on success, false on failure + -- @return response Account on success, Error on failure + doAuthenticate = function( self ) - local status, response - local next_credential = self:get_next_credential() - local retries = self.options.max_retries - local username, password + local status, response + local next_credential = self:get_next_credential() + local retries = self.options.max_retries + local username, password - repeat - local driver = self.driver:new( self.host, self.port, self.driver_options ) - status = driver:connect() + repeat + local driver = self.driver:new( self.host, self.port, self.driver_options ) + status = driver:connect() - -- Did we succesfully connect? - if ( status ) then - if ( not(username) and not(password) ) then - repeat - username, password = next_credential() - if ( not(username) and not(password) ) then - driver:disconnect() - self.threads[coroutine.running()].terminate = true - return false - end - until ( ( not(self.found_accounts) or not(self.found_accounts[username]) ) and - ( self.options.max_guesses == 0 or not(self.account_guesses[username]) or - self.options.max_guesses > self.account_guesses[username] ) ) + -- Did we succesfully connect? + if ( status ) then + if ( not(username) and not(password) ) then + repeat + username, password = next_credential() + if ( not(username) and not(password) ) then + driver:disconnect() + self.threads[coroutine.running()].terminate = true + return false + end + until ( ( not(self.found_accounts) or not(self.found_accounts[username]) ) and + ( self.options.max_guesses == 0 or not(self.account_guesses[username]) or + self.options.max_guesses > self.account_guesses[username] ) ) - -- increases the number of guesses for an account - self.account_guesses[username] = self.account_guesses[username] and self.account_guesses[username] + 1 or 1 - end + -- increases the number of guesses for an account + self.account_guesses[username] = self.account_guesses[username] and self.account_guesses[username] + 1 or 1 + end - -- make sure that all threads locked in connect stat terminate quickly - if ( Engine.terminate_all ) then - driver:disconnect() - return false - end + -- make sure that all threads locked in connect stat terminate quickly + if ( Engine.terminate_all ) then + driver:disconnect() + return false + end - local c - -- Do we have a username or not? - if ( username and #username > 0 ) then - c = ("%s/%s"):format(username, #password > 0 and password or "") - else - c = ("%s"):format(#password > 0 and password or "") - end + local c + -- Do we have a username or not? + if ( username and #username > 0 ) then + c = ("%s/%s"):format(username, #password > 0 and password or "") + else + c = ("%s"):format(#password > 0 and password or "") + end - local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or "Trying" - stdnse.print_debug(2, "%s %s against %s:%d", msg, c, self.host.ip, self.port.number ) - status, response = driver:login( username, password ) + local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or "Trying" + stdnse.print_debug(2, "%s %s against %s:%d", msg, c, self.host.ip, self.port.number ) + status, response = driver:login( username, password ) - driver:disconnect() - driver = nil - end + driver:disconnect() + driver = nil + end - retries = retries - 1 + retries = retries - 1 - -- End if: - -- * The guess was successfull - -- * The response was not set to retry - -- * We've reached the maximum retry attempts - until( status or ( response and not( response:isRetry() ) ) or retries == 0) + -- End if: + -- * The guess was successfull + -- * The response was not set to retry + -- * We've reached the maximum retry attempts + until( status or ( response and not( response:isRetry() ) ) or retries == 0) - -- Increase the amount of total guesses - self.counter = self.counter + 1 + -- Increase the amount of total guesses + self.counter = self.counter + 1 - -- did we exhaust all retries, terminate and report? - if ( retries == 0 ) then - Engine.terminate_all = true - self.error = "Too many retries, aborted ..." - response = Error:new("Too many retries, aborted ...") - response.abort = true - end - return status, response - end, + -- did we exhaust all retries, terminate and report? + if ( retries == 0 ) then + Engine.terminate_all = true + self.error = "Too many retries, aborted ..." + response = Error:new("Too many retries, aborted ...") + response.abort = true + end + return status, response + end, - login = function(self, cvar ) - local condvar = nmap.condvar( cvar ) - local thread_data = self.threads[coroutine.running()] - local interval_start = os.time() + login = function(self, cvar ) + local condvar = nmap.condvar( cvar ) + local thread_data = self.threads[coroutine.running()] + local interval_start = os.time() - while( true ) do - -- Should we terminate all threads? - if ( self.terminate_all or thread_data.terminate ) then break end + while( true ) do + -- Should we terminate all threads? + if ( self.terminate_all or thread_data.terminate ) then break end - local status, response = self:doAuthenticate() + local status, response = self:doAuthenticate() - if ( status ) then - -- Prevent locked accounts from appearing several times - if ( not(self.found_accounts) or self.found_accounts[response.username] == nil ) then - if ( not(self.options.nostore) ) then - creds.Credentials:new( self.options.script_name, self.host, self.port ):add(response.username, response.password, response.state ) - else - self.credstore = self.credstore or {} - table.insert(self.credstore, response:toString() ) - end + if ( status ) then + -- Prevent locked accounts from appearing several times + if ( not(self.found_accounts) or self.found_accounts[response.username] == nil ) then + if ( not(self.options.nostore) ) then + creds.Credentials:new( self.options.script_name, self.host, self.port ):add(response.username, response.password, response.state ) + else + self.credstore = self.credstore or {} + table.insert(self.credstore, response:toString() ) + end - stdnse.print_debug("Discovered account: %s", response:toString()) + stdnse.print_debug("Discovered account: %s", response:toString()) - -- if we're running in passonly mode, and want to continue guessing - -- we will have a problem as the username is always the same. - -- in this case we don't log the account as found. - if ( not(self.options.passonly) ) then - self.found_accounts[response.username] = true - end + -- if we're running in passonly mode, and want to continue guessing + -- we will have a problem as the username is always the same. + -- in this case we don't log the account as found. + if ( not(self.options.passonly) ) then + self.found_accounts[response.username] = true + end - -- Check if firstonly option was set, if so abort all threads - if ( self.options.firstonly ) then self.terminate_all = true end - end - else - if ( response and response:isAbort() ) then - self.terminate_all = true - self.error = response:getMessage() - break - elseif( response and response:isDone() ) then - break - elseif ( response and response:isInvalidAccount() ) then - self.found_accounts[response:isInvalidAccount()] = true - end - end + -- Check if firstonly option was set, if so abort all threads + if ( self.options.firstonly ) then self.terminate_all = true end + end + else + if ( response and response:isAbort() ) then + self.terminate_all = true + self.error = response:getMessage() + break + elseif( response and response:isDone() ) then + break + elseif ( response and response:isInvalidAccount() ) then + self.found_accounts[response:isInvalidAccount()] = true + end + end - local timediff = (os.time() - interval_start) + local timediff = (os.time() - interval_start) - -- This thread made another guess - thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 ) + -- This thread made another guess + thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 ) - -- Dump statistics at regular intervals - if ( timediff > Engine.STAT_INTERVAL ) then - interval_start = os.time() - local tps = self.counter / ( os.time() - self.starttime ) - table.insert(self.tps, tps ) - stdnse.print_debug(2, "threads=%d,tps=%d", self:activeThreads(), tps ) - end + -- Dump statistics at regular intervals + if ( timediff > Engine.STAT_INTERVAL ) then + interval_start = os.time() + local tps = self.counter / ( os.time() - self.starttime ) + table.insert(self.tps, tps ) + stdnse.print_debug(2, "threads=%d,tps=%d", self:activeThreads(), tps ) + end - -- if delay was speciefied, do sleep - if ( self.options.delay > 0 ) then stdnse.sleep( self.options.delay ) end - end - condvar "signal" - end, + -- if delay was speciefied, do sleep + if ( self.options.delay > 0 ) then stdnse.sleep( self.options.delay ) end + end + condvar "signal" + end, - --- Starts the brute-force - -- - -- @return status true on success, false on failure - -- @return err string containing error message on failure - start = function(self) + --- Starts the brute-force + -- + -- @return status true on success, false on failure + -- @return err string containing error message on failure + start = function(self) - local cvar = {} - local condvar = nmap.condvar( cvar ) + local cvar = {} + local condvar = nmap.condvar( cvar ) - assert(self.options.script_name, "SCRIPT_NAME was not set in options.script_name") - assert(self.port.number and self.port.protocol, "Invalid port table detected") - self.port.service = self.port.service or "unknown" + assert(self.options.script_name, "SCRIPT_NAME was not set in options.script_name") + assert(self.port.number and self.port.protocol, "Invalid port table detected") + self.port.service = self.port.service or "unknown" - -- Only run the check method if it exist. We should phase this out - -- in favor of a check in the action function of the script - if ( self.driver:new( self.host, self.port, self.driver_options ).check ) then - -- check if the driver is ready! - local status, response = self.driver:new( self.host, self.port, self.driver_options ):check() - if( not(status) ) then return false, response end - end + -- Only run the check method if it exist. We should phase this out + -- in favor of a check in the action function of the script + if ( self.driver:new( self.host, self.port, self.driver_options ).check ) then + -- check if the driver is ready! + local status, response = self.driver:new( self.host, self.port, self.driver_options ):check() + if( not(status) ) then return false, response end + end - local usernames = self.usernames - local passwords = self.passwords + local usernames = self.usernames + local passwords = self.passwords - if ( "function" ~= type(usernames) ) then - return false, "Invalid usernames iterator" - end - if ( "function" ~= type(passwords) ) then - return false, "Invalid passwords iterator" - end + if ( "function" ~= type(usernames) ) then + return false, "Invalid usernames iterator" + end + if ( "function" ~= type(passwords) ) then + return false, "Invalid passwords iterator" + end - local mode = self.options.mode or stdnse.get_script_args("brute.mode") + local mode = self.options.mode or stdnse.get_script_args("brute.mode") - -- if no mode was given, but a credfile is present, assume creds mode - if ( not(mode) and stdnse.get_script_args("brute.credfile") ) then - if ( stdnse.get_script_args("userdb") or - stdnse.get_script_args("passdb") ) then - return false, "\n ERROR: brute.credfile can't be used in combination with userdb/passdb" - end - mode = 'creds' - end + -- if no mode was given, but a credfile is present, assume creds mode + if ( not(mode) and stdnse.get_script_args("brute.credfile") ) then + if ( stdnse.get_script_args("userdb") or + stdnse.get_script_args("passdb") ) then + return false, "\n ERROR: brute.credfile can't be used in combination with userdb/passdb" + end + mode = 'creds' + end - -- Are we guessing against a service that has no username (eg. VNC) - if ( self.options.passonly ) then - local function single_user_iter(next) - local function next_user() coroutine.yield( "" ) end - return coroutine.wrap(next_user) - end - -- only add this iterator if no other iterator was specified - if self.iterator == nil then - self.iterator = Iterators.user_pw_iterator( single_user_iter(), passwords ) - end - elseif ( mode == 'creds' ) then - local credfile = stdnse.get_script_args("brute.credfile") - if ( not(credfile) ) then - return false, "No credential file specified (see brute.credfile)" - end + -- Are we guessing against a service that has no username (eg. VNC) + if ( self.options.passonly ) then + local function single_user_iter(next) + local function next_user() coroutine.yield( "" ) end + return coroutine.wrap(next_user) + end + -- only add this iterator if no other iterator was specified + if self.iterator == nil then + self.iterator = Iterators.user_pw_iterator( single_user_iter(), passwords ) + end + elseif ( mode == 'creds' ) then + local credfile = stdnse.get_script_args("brute.credfile") + if ( not(credfile) ) then + return false, "No credential file specified (see brute.credfile)" + end - local f = io.open( credfile, "r" ) - if ( not(f) ) then - return false, ("Failed to open credfile (%s)"):format(credfile) - end + local f = io.open( credfile, "r" ) + if ( not(f) ) then + return false, ("Failed to open credfile (%s)"):format(credfile) + end - self.iterator = Iterators.credential_iterator( f ) - elseif ( mode and mode == 'user' ) then - self.iterator = self.iterator or Iterators.user_pw_iterator( usernames, passwords ) - elseif( mode and mode == 'pass' ) then - self.iterator = self.iterator or Iterators.pw_user_iterator( usernames, passwords ) - elseif ( mode ) then - return false, ("Unsupported mode: %s"):format(mode) - -- Default to the pw_user_iterator in case no iterator was specified - elseif ( self.iterator == nil ) then - self.iterator = Iterators.pw_user_iterator( usernames, passwords ) - end + self.iterator = Iterators.credential_iterator( f ) + elseif ( mode and mode == 'user' ) then + self.iterator = self.iterator or Iterators.user_pw_iterator( usernames, passwords ) + elseif( mode and mode == 'pass' ) then + self.iterator = self.iterator or Iterators.pw_user_iterator( usernames, passwords ) + elseif ( mode ) then + return false, ("Unsupported mode: %s"):format(mode) + -- Default to the pw_user_iterator in case no iterator was specified + elseif ( self.iterator == nil ) then + self.iterator = Iterators.pw_user_iterator( usernames, passwords ) + end - if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.useraspass ) then - -- if we're only guessing passwords, this doesn't make sense - if ( not(self.options.passonly) ) then - self.iterator = unpwdb.concat_iterators(Iterators.pw_same_as_user_iterator(usernames, "lower"),self.iterator) - end - end + if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.useraspass ) then + -- if we're only guessing passwords, this doesn't make sense + if ( not(self.options.passonly) ) then + self.iterator = unpwdb.concat_iterators(Iterators.pw_same_as_user_iterator(usernames, "lower"),self.iterator) + end + end - if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.emptypass ) then - local function empty_pass_iter() - local function next_pass() - coroutine.yield( "" ) - end - return coroutine.wrap(next_pass) - end - self.iterator = Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass") - end + if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.emptypass ) then + local function empty_pass_iter() + local function next_pass() + coroutine.yield( "" ) + end + return coroutine.wrap(next_pass) + end + self.iterator = Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass") + end - self.starttime = os.time() + self.starttime = os.time() - -- Startup all worker threads - for i=1, self.max_threads do - local co = stdnse.new_thread( self.login, self, cvar ) - self.threads[co] = {} - self.threads[co].running = true - end + -- Startup all worker threads + for i=1, self.max_threads do + local co = stdnse.new_thread( self.login, self, cvar ) + self.threads[co] = {} + self.threads[co].running = true + end - -- wait for all threads to finnish running - while self:threadCount()>0 do condvar "wait" end + -- wait for all threads to finnish running + while self:threadCount()>0 do condvar "wait" end - local valid_accounts + local valid_accounts - if ( not(self.options.nostore) ) then - valid_accounts = creds.Credentials:new(self.options.script_name, self.host, self.port):getTable() - else - valid_accounts = self.credstore - end + if ( not(self.options.nostore) ) then + valid_accounts = creds.Credentials:new(self.options.script_name, self.host, self.port):getTable() + else + valid_accounts = self.credstore + end - local result = {} - -- Did we find any accounts, if so, do formatting - if ( valid_accounts and #valid_accounts > 0 ) then - valid_accounts.name = self.options.title or "Accounts" - table.insert( result, valid_accounts ) - else - table.insert( result, {"No valid accounts found", name="Accounts"} ) - end + local result = {} + -- Did we find any accounts, if so, do formatting + if ( valid_accounts and #valid_accounts > 0 ) then + valid_accounts.name = self.options.title or "Accounts" + table.insert( result, valid_accounts ) + else + table.insert( result, {"No valid accounts found", name="Accounts"} ) + end - -- calculate the average tps - local sum = 0 - for _, v in ipairs( self.tps ) do sum = sum + v end - local time_diff = ( os.time() - self.starttime ) - time_diff = ( time_diff == 0 ) and 1 or time_diff - local tps = ( sum == 0 ) and ( self.counter / time_diff ) or ( sum / #self.tps ) + -- calculate the average tps + local sum = 0 + for _, v in ipairs( self.tps ) do sum = sum + v end + local time_diff = ( os.time() - self.starttime ) + time_diff = ( time_diff == 0 ) and 1 or time_diff + local tps = ( sum == 0 ) and ( self.counter / time_diff ) or ( sum / #self.tps ) - -- Add the statistics to the result - local stats = {} - table.insert(stats, ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) ) - stats.name = "Statistics" - table.insert( result, stats ) + -- Add the statistics to the result + local stats = {} + table.insert(stats, ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) ) + stats.name = "Statistics" + table.insert( result, stats ) - if ( self.options.max_guesses > 0 ) then - -- we only display a warning if the guesses are equal to max_guesses - for user, guesses in pairs(self.account_guesses) do - if ( guesses == self.options.max_guesses ) then - table.insert( result, { name = "Information", - ("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses) } ) - break - end - end - end + if ( self.options.max_guesses > 0 ) then + -- we only display a warning if the guesses are equal to max_guesses + for user, guesses in pairs(self.account_guesses) do + if ( guesses == self.options.max_guesses ) then + table.insert( result, { name = "Information", + ("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses) } ) + break + end + end + end - result = ( #result ) and stdnse.format_output( true, result ) or "" + result = ( #result ) and stdnse.format_output( true, result ) or "" - -- Did any error occure? If so add this to the result. - if ( self.error ) then - result = result .. (" \n ERROR: %s"):format( self.error ) - return false, result - end - return true, result - end, + -- Did any error occure? If so add this to the result. + if ( self.error ) then + result = result .. (" \n ERROR: %s"):format( self.error ) + return false, result + end + return true, result + end, } --- Default username iterator that uses unpwdb -- usernames_iterator = function() - local status, usernames = unpwdb.usernames() - if ( not(status) ) then return "Failed to load usernames" end - return usernames + local status, usernames = unpwdb.usernames() + if ( not(status) ) then return "Failed to load usernames" end + return usernames end --- Default password iterator that uses unpwdb -- passwords_iterator = function() - local status, passwords = unpwdb.passwords() - if ( not(status) ) then return "Failed to load passwords" end - return passwords + local status, passwords = unpwdb.passwords() + if ( not(status) ) then return "Failed to load passwords" end + return passwords end Iterators = { - --- Iterates over each user and password - -- - -- @param users table/function containing list of users - -- @param pass table/function containing list of passwords - -- @param mode string, should be either 'user' or 'pass' and controls - -- whether the users or passwords are in the 'outer' loop - -- @return function iterator - account_iterator = function(users, pass, mode) - local function next_credential () - local outer, inner - if "table" == type(users) then - users = unpwdb.table_iterator(users) - end - if "table" == type(pass) then - pass = unpwdb.table_iterator(pass) - end + --- Iterates over each user and password + -- + -- @param users table/function containing list of users + -- @param pass table/function containing list of passwords + -- @param mode string, should be either 'user' or 'pass' and controls + -- whether the users or passwords are in the 'outer' loop + -- @return function iterator + account_iterator = function(users, pass, mode) + local function next_credential () + local outer, inner + if "table" == type(users) then + users = unpwdb.table_iterator(users) + end + if "table" == type(pass) then + pass = unpwdb.table_iterator(pass) + end - if ( mode == 'pass' ) then - outer, inner = pass, users - elseif ( mode == 'user' ) then - outer, inner = users, pass - else - return - end + if ( mode == 'pass' ) then + outer, inner = pass, users + elseif ( mode == 'user' ) then + outer, inner = users, pass + else + return + end - for o in outer do - for i in inner do - if ( mode == 'pass' ) then - coroutine.yield( i, o ) - else - coroutine.yield( o, i ) - end - end - inner("reset") - end - while true do coroutine.yield(nil, nil) end - end - return coroutine.wrap( next_credential ) - end, + for o in outer do + for i in inner do + if ( mode == 'pass' ) then + coroutine.yield( i, o ) + else + coroutine.yield( o, i ) + end + end + inner("reset") + end + while true do coroutine.yield(nil, nil) end + end + return coroutine.wrap( next_credential ) + end, - --- Try each password for each user (user in outer loop) - -- - -- @param users table/function containing list of users - -- @param pass table/function containing list of passwords - -- @return function iterator - user_pw_iterator = function( users, pass ) - return Iterators.account_iterator( users, pass, "user" ) - end, + --- Try each password for each user (user in outer loop) + -- + -- @param users table/function containing list of users + -- @param pass table/function containing list of passwords + -- @return function iterator + user_pw_iterator = function( users, pass ) + return Iterators.account_iterator( users, pass, "user" ) + end, - --- Try each user for each password (password in outer loop) - -- - -- @param users table/function containing list of users - -- @param pass table/function containing list of passwords - -- @return function iterator - pw_user_iterator = function( users, pass ) - return Iterators.account_iterator( users, pass, "pass" ) - end, + --- Try each user for each password (password in outer loop) + -- + -- @param users table/function containing list of users + -- @param pass table/function containing list of passwords + -- @return function iterator + pw_user_iterator = function( users, pass ) + return Iterators.account_iterator( users, pass, "pass" ) + end, - --- An iterator that returns the username as password - -- - -- @param users function returning the next user - -- @param case string [optional] 'upper' or 'lower', specifies if user - -- and password pairs should be case converted. - -- @return function iterator - pw_same_as_user_iterator = function( users, case ) - local function next_credential () - for user in users do - if ( case == 'upper' ) then - coroutine.yield(user, user:upper()) - elseif( case == 'lower' ) then - coroutine.yield(user, user:lower()) - else - coroutine.yield(user, user) - end - end - users("reset") - while true do coroutine.yield(nil, nil) end - end - return coroutine.wrap( next_credential ) - end, + --- An iterator that returns the username as password + -- + -- @param users function returning the next user + -- @param case string [optional] 'upper' or 'lower', specifies if user + -- and password pairs should be case converted. + -- @return function iterator + pw_same_as_user_iterator = function( users, case ) + local function next_credential () + for user in users do + if ( case == 'upper' ) then + coroutine.yield(user, user:upper()) + elseif( case == 'lower' ) then + coroutine.yield(user, user:lower()) + else + coroutine.yield(user, user) + end + end + users("reset") + while true do coroutine.yield(nil, nil) end + end + return coroutine.wrap( next_credential ) + end, - --- An iterator that returns the username and uppercase password - -- - -- @param users table containing list of users - -- @param pass table containing list of passwords - -- @param mode string, should be either 'user' or 'pass' and controls - -- whether the users or passwords are in the 'outer' loop - -- @return function iterator - pw_ucase_iterator = function( users, passwords, mode ) - local function next_credential () - for user, pass in Iterators.account_iterator(users, passwords, mode) do - coroutine.yield( user, pass:upper() ) - end - while true do coroutine.yield(nil, nil) end - end - return coroutine.wrap( next_credential ) - end, + --- An iterator that returns the username and uppercase password + -- + -- @param users table containing list of users + -- @param pass table containing list of passwords + -- @param mode string, should be either 'user' or 'pass' and controls + -- whether the users or passwords are in the 'outer' loop + -- @return function iterator + pw_ucase_iterator = function( users, passwords, mode ) + local function next_credential () + for user, pass in Iterators.account_iterator(users, passwords, mode) do + coroutine.yield( user, pass:upper() ) + end + while true do coroutine.yield(nil, nil) end + end + return coroutine.wrap( next_credential ) + end, - --- Credential iterator (for default or known user/pass combinations) - -- - -- @param f file handle to file containing credentials separated by '/' - -- @return function iterator - credential_iterator = function( f ) - local function next_credential () - local c = {} - for line in f:lines() do - if ( not(line:match("^#!comment:")) ) then - local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end - line = trim(line) - local user, pass = line:match("^([^%/]*)%/(.*)$") - coroutine.yield( user, pass ) - end - end - f:close() - while true do coroutine.yield( nil, nil ) end - end - return coroutine.wrap( next_credential ) - end, + --- Credential iterator (for default or known user/pass combinations) + -- + -- @param f file handle to file containing credentials separated by '/' + -- @return function iterator + credential_iterator = function( f ) + local function next_credential () + local c = {} + for line in f:lines() do + if ( not(line:match("^#!comment:")) ) then + local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end + line = trim(line) + local user, pass = line:match("^([^%/]*)%/(.*)$") + coroutine.yield( user, pass ) + end + end + f:close() + while true do coroutine.yield( nil, nil ) end + end + return coroutine.wrap( next_credential ) + end, - unpwdb_iterator = function( mode ) - local status, users, passwords + unpwdb_iterator = function( mode ) + local status, users, passwords - status, users = unpwdb.usernames() - if ( not(status) ) then return end + status, users = unpwdb.usernames() + if ( not(status) ) then return end - status, passwords = unpwdb.passwords() - if ( not(status) ) then return end + status, passwords = unpwdb.passwords() + if ( not(status) ) then return end - return Iterators.account_iterator( users, passwords, mode ) - end, + return Iterators.account_iterator( users, passwords, mode ) + end, } diff --git a/nselib/citrixxml.lua b/nselib/citrixxml.lua index 8d9c2bb57..fac6aa20e 100644 --- a/nselib/citrixxml.lua +++ b/nselib/citrixxml.lua @@ -517,7 +517,7 @@ function request_reconnect_session_data(host, port, params) end if params.DeviceId then - xmldata = xmldata .. "" .. params.DeviceId .. "" + xmldata = xmldata .. "" .. params.DeviceId .. "" end for _, srvtype in pairs(params.ServerType) do diff --git a/nselib/dhcp.lua b/nselib/dhcp.lua index 3ce30b754..37a9f1f86 100644 --- a/nselib/dhcp.lua +++ b/nselib/dhcp.lua @@ -26,14 +26,14 @@ _ENV = stdnse.module("dhcp", stdnse.seeall) request_types = { - DHCPDISCOVER = 1, - DHCPOFFER = 2, - DHCPREQUEST = 3, - DHCPDECLINE = 4, - DHCPACK = 5, - DHCPNAK = 6, - DHCPRELEASE = 7, - DHCPINFORM = 8 + DHCPDISCOVER = 1, + DHCPOFFER = 2, + DHCPREQUEST = 3, + DHCPDECLINE = 4, + DHCPACK = 5, + DHCPNAK = 6, + DHCPRELEASE = 7, + DHCPINFORM = 8 } request_types_str = {} @@ -54,28 +54,28 @@ request_types_str[8] = "DHCPINFORM" --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_ip(data, pos, length) - if(length ~= 4) then - if((length % 4) ~= 0) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for an ip address (%d)", length) - pos = pos + length + if(length ~= 4) then + if((length % 4) ~= 0) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for an ip address (%d)", length) + pos = pos + length - return pos, nil - else - local results = {} - for i=1, length, 4 do - local value - pos, value = bin.unpack("request_types_str @@ -114,15 +114,15 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_message_type(data, pos, length) - local value + local value - pos, value = read_1_byte(data, pos, length) - if(value == nil) then - stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte message type") - return pos, nil - end + pos, value = read_1_byte(data, pos, length) + if(value == nil) then + stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte message type") + return pos, nil + end - return pos, request_types_str[value] + return pos, request_types_str[value] end ---Read a single byte, and return 'false' if it's 0, or 'true' if it's non-zero. Print an error if the @@ -134,17 +134,17 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_boolean(data, pos, length) - local result - pos, result = read_1_byte(data, pos, length) + local result + pos, result = read_1_byte(data, pos, length) - if(result == nil) then - stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte boolean") - return pos, nil - elseif(result == 0) then - return pos, "false" - else - return pos, "true" - end + if(result == nil) then + stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte boolean") + return pos, nil + elseif(result == 0) then + return pos, "false" + else + return pos, "true" + end end ---Read a 2-byte unsigned little endian value. Print an error if the length isn't 2. @@ -155,12 +155,12 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_2_bytes(data, pos, length) - if(length ~= 2) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 2) - pos = pos + length - return pos, nil - end - return bin.unpack(">S", data, pos) + if(length ~= 2) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 2) + pos = pos + length + return pos, nil + end + return bin.unpack(">S", data, pos) end @@ -173,21 +173,21 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_2_bytes_list(data, pos, length) - if((length % 2) ~= 0) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 2) - pos = pos + length + if((length % 2) ~= 0) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 2) + pos = pos + length - return pos, nil - else - local results = {} - for i=1, length, 2 do - local value - pos, value = bin.unpack(">S", data, pos) - table.insert(results, value) - end + return pos, nil + else + local results = {} + for i=1, length, 2 do + local value + pos, value = bin.unpack(">S", data, pos) + table.insert(results, value) + end - return pos, results - end + return pos, results + end end @@ -199,12 +199,12 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_4_bytes(data, pos, length) - if(length ~= 4) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) - pos = pos + length - return pos, nil - end - return bin.unpack(">I", data, pos) + if(length ~= 4) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) + pos = pos + length + return pos, nil + end + return bin.unpack(">I", data, pos) end ---Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an @@ -216,32 +216,32 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_time(data, pos, length) - local result - if(length ~= 4) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) - pos = pos + length - return pos, nil - end - pos, result = bin.unpack(">I", data, pos) + local result + if(length ~= 4) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) + pos = pos + length + return pos, nil + end + pos, result = bin.unpack(">I", data, pos) - -- This code was mostly taken from snmp-sysdescr.nse. It should probably be abstracted into stdnse.lua [TODO] - local days, hours, minutes, seconds, htime, mtime, stime - days = math.floor(result / 86400) - htime = math.fmod(result, 86400) - hours = math.floor(htime / 3600) - mtime = math.fmod(htime, 3600) - minutes = math.floor(mtime / 60) - seconds = math.fmod(mtime, 60) + -- This code was mostly taken from snmp-sysdescr.nse. It should probably be abstracted into stdnse.lua [TODO] + local days, hours, minutes, seconds, htime, mtime, stime + days = math.floor(result / 86400) + htime = math.fmod(result, 86400) + hours = math.floor(htime / 3600) + mtime = math.fmod(htime, 3600) + minutes = math.floor(mtime / 60) + seconds = math.fmod(mtime, 60) - local dayLabel + local dayLabel - if days == 1 then - dayLabel = "day" - else - dayLabel = "days" - end + if days == 1 then + dayLabel = "day" + else + dayLabel = "days" + end - return pos, string.format("%d %s, %d:%02d:%02d", days, dayLabel, hours, minutes, seconds) + return pos, string.format("%d %s, %d:%02d:%02d", days, dayLabel, hours, minutes, seconds) end ---Read a list of static routes. Each of them are a pair of IP addresses, a destination and a @@ -253,22 +253,22 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_static_route(data, pos, length) - if((length % 8) ~= 0) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) - pos = pos + length + if((length % 8) ~= 0) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) + pos = pos + length - return pos, nil - else - local results = {} - for i=1, length, 8 do - local destination, router - pos, destination = read_ip(data, pos, 4) - pos, router = read_ip(data, pos, 4) - table.insert(results, {destination=destination, router=router}) - end + return pos, nil + else + local results = {} + for i=1, length, 8 do + local destination, router + pos, destination = read_ip(data, pos, 4) + pos, router = read_ip(data, pos, 4) + table.insert(results, {destination=destination, router=router}) + end - return pos, results - end + return pos, results + end end ---Read a list of policy filters. Each of them are a pair of IP addresses, an address and a @@ -280,22 +280,22 @@ end --@return The new position (will always be pos + length, no matter what we think it should be) --@return The value of the field, or nil if the field length was wrong. local function read_policy_filter(data, pos, length) - if((length % 8) ~= 0) then - stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) - pos = pos + length + if((length % 8) ~= 0) then + stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) + pos = pos + length - return pos, nil - else - local results = {} - for i=1, length, 8 do - local address, router, mask - pos, address = read_ip(data, pos, 4) - pos, mask = read_ip(data, pos, 4) - table.insert(results, {address=address, mask=mask}) - end + return pos, nil + else + local results = {} + for i=1, length, 8 do + local address, router, mask + pos, address = read_ip(data, pos, 4) + pos, mask = read_ip(data, pos, 4) + table.insert(results, {address=address, mask=mask}) + end - return pos, results - end + return pos, results + end end ---These are the different fields for DHCP. These have to come after the read_* function @@ -366,25 +366,25 @@ actions[252]= {name="WPAD", func=read_string, --- Does the send/receive, doesn't build/parse anything. local function dhcp_send(socket, host, packet) - -- Send out the packet - return socket:sendto(host, { number=67, protocol="udp" }, packet) + -- Send out the packet + return socket:sendto(host, { number=67, protocol="udp" }, packet) end local function dhcp_receive(socket, transaction_id) - local status, data = socket:receive() - if ( not(status) ) then - socket:close() - return false, data - end + local status, data = socket:receive() + if ( not(status) ) then + socket:close() + return false, data + end - -- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly - -- generated and different for every instance of a script (to prevent collisions) - while status and data:sub(5, 8) ~= transaction_id do - status, data = socket:receive() - end + -- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly + -- generated and different for every instance of a script (to prevent collisions) + while status and data:sub(5, 8) ~= transaction_id do + status, data = socket:receive() + end - return status, data + return status, data end --- Builds a DHCP packet @@ -417,58 +417,58 @@ end --@return status (true or false) --@return The parsed response, as a table. function dhcp_build(request_type, ip_address, mac_address, options, request_options, overrides, lease_time, transaction_id) - local packet = '' + local packet = '' - -- Set up the default overrides - if(overrides == nil) then - overrides = {} - end + -- Set up the default overrides + if(overrides == nil) then + overrides = {} + end - if(request_options == nil) then - -- Request the defaults, or there's no verbosity; otherwise, request everything! - request_options = '' - for i = 1, 61, 1 do - if(nmap.verbosity() > 0) then - request_options = request_options .. string.char(i) - else - if(actions[i] and actions[i].default) then - request_options = request_options .. string.char(i) - end - end - end - end + if(request_options == nil) then + -- Request the defaults, or there's no verbosity; otherwise, request everything! + request_options = '' + for i = 1, 61, 1 do + if(nmap.verbosity() > 0) then + request_options = request_options .. string.char(i) + else + if(actions[i] and actions[i].default) then + request_options = request_options .. string.char(i) + end + end + end + end - -- Header - packet = packet .. bin.pack(">CCCC", overrides['op'] or 1, overrides['htype'] or 1, overrides['hlen'] or 6, overrides['hops'] or 0) -- BOOTREQUEST, 10mb ethernet, 6 bytes long, 0 hops - packet = packet .. ( overrides['xid'] or transaction_id ) -- Transaction ID = - packet = packet .. bin.pack(">SS", overrides['secs'] or 0, overrides['flags'] or 0x0000) -- Secs, flags - packet = packet .. bin.pack("A", ip_address) -- Client address - packet = packet .. bin.pack("I", overrides['cookie'] or 0x63825363) -- Magic cookie + -- Header + packet = packet .. bin.pack(">CCCC", overrides['op'] or 1, overrides['htype'] or 1, overrides['hlen'] or 6, overrides['hops'] or 0) -- BOOTREQUEST, 10mb ethernet, 6 bytes long, 0 hops + packet = packet .. ( overrides['xid'] or transaction_id ) -- Transaction ID = + packet = packet .. bin.pack(">SS", overrides['secs'] or 0, overrides['flags'] or 0x0000) -- Secs, flags + packet = packet .. bin.pack("A", ip_address) -- Client address + packet = packet .. bin.pack("I", overrides['cookie'] or 0x63825363) -- Magic cookie - -- Options - packet = packet .. bin.pack(">CCC", 0x35, 1, request_type) -- Request type + -- Options + packet = packet .. bin.pack(">CCC", 0x35, 1, request_type) -- Request type - for _, option in ipairs(options or {}) do - packet = packet .. bin.pack(">C", option.number) - if ( "string" == option.type ) then - packet = packet .. bin.pack("p", option.value) - elseif( "ip" == option.type ) then - packet = packet .. bin.pack(">CI", 4, option.value) - end - end + for _, option in ipairs(options or {}) do + packet = packet .. bin.pack(">C", option.number) + if ( "string" == option.type ) then + packet = packet .. bin.pack("p", option.value) + elseif( "ip" == option.type ) then + packet = packet .. bin.pack(">CI", 4, option.value) + end + end - packet = packet .. bin.pack(">CCA", 0x37, #request_options, request_options) -- Request options - packet = packet .. bin.pack(">CCI", 0x33, 4, lease_time or 1) -- Lease time + packet = packet .. bin.pack(">CCA", 0x37, #request_options, request_options) -- Request options + packet = packet .. bin.pack(">CCI", 0x33, 4, lease_time or 1) -- Lease time - packet = packet .. bin.pack(">C", 0xFF) -- Termination + packet = packet .. bin.pack(">C", 0xFF) -- Termination - return true, packet + return true, packet end ---Parse a DHCP packet (either a request or a response) and return the results as a table. The @@ -480,96 +480,96 @@ end --@param data The DHCP packet data. Any padding at the end of the packet will be ignored (by default, -- DHCP packets are padded with \x00 bytes). function dhcp_parse(data, transaction_id) - local pos = 1 - local result = {} + local pos = 1 + local result = {} - -- Receive the first bit and make sure we got the correct operation back - pos, result['op'], result['htype'], result['hlen'], result['hops'] = bin.unpack(">CCCC", data, pos) - if(result['op'] ~= 2) then - return false, string.format("DHCP server returned invalid reply ('op' wasn't BOOTREPLY (it was 0x%02x))", result['op']) - end + -- Receive the first bit and make sure we got the correct operation back + pos, result['op'], result['htype'], result['hlen'], result['hops'] = bin.unpack(">CCCC", data, pos) + if(result['op'] ~= 2) then + return false, string.format("DHCP server returned invalid reply ('op' wasn't BOOTREPLY (it was 0x%02x))", result['op']) + end - -- Confirm the transaction id - pos, result['xid'] = bin.unpack("A4", data, pos) - if(result['xid'] ~= transaction_id) then - return false, string.format("DHCP server returned invalid reply (transaction id didn't match (%s != %s))", result['xid'], transaction_id) - end + -- Confirm the transaction id + pos, result['xid'] = bin.unpack("A4", data, pos) + if(result['xid'] ~= transaction_id) then + return false, string.format("DHCP server returned invalid reply (transaction id didn't match (%s != %s))", result['xid'], transaction_id) + end - -- Unpack the secs, flags, addresses, sname, and file - pos, result['secs'], result['flags'] = bin.unpack(">SS", data, pos) - pos, result['ciaddr'] = bin.unpack("SS", data, pos) + pos, result['ciaddr'] = bin.unpack("I", data, pos) - if(result['cookie'] ~= 0x63825363) then - return false, "DHCP server returned invalid reply (the magic cookie was invalid)" - end + -- Confirm the cookie + pos, result['cookie'] = bin.unpack(">I", data, pos) + if(result['cookie'] ~= 0x63825363) then + return false, "DHCP server returned invalid reply (the magic cookie was invalid)" + end - -- Parse the options - result['options'] = {} - while true do - local option, length - pos, option, length = bin.unpack(">CC", data, pos) + -- Parse the options + result['options'] = {} + while true do + local option, length + pos, option, length = bin.unpack(">CC", data, pos) - -- Check for termination condition - if(option == 0xFF) then - break; - end + -- Check for termination condition + if(option == 0xFF) then + break; + end - -- Get the action from the array, based on the code - local action = actions[option] + -- Get the action from the array, based on the code + local action = actions[option] - -- Verify we got a valid code (if we didn't, we're probably in big trouble) - local value - if(action == nil) then - stdnse.print_debug(1, "dhcp-discover: Unknown option: %d", option) - pos = pos + length - else - -- Call the function to parse the option, and insert the result into our results table + -- Verify we got a valid code (if we didn't, we're probably in big trouble) + local value + if(action == nil) then + stdnse.print_debug(1, "dhcp-discover: Unknown option: %d", option) + pos = pos + length + else + -- Call the function to parse the option, and insert the result into our results table - stdnse.print_debug(2, "dhcp-discover: Attempting to parse %s", action['name']) - pos, value = action['func'](data, pos, length) + stdnse.print_debug(2, "dhcp-discover: Attempting to parse %s", action['name']) + pos, value = action['func'](data, pos, length) - if(nmap.verbosity() == 0 and action.default == false) then - stdnse.print_debug(1, "dhcp-discover: Server returned unrequested option (%s => %s)", action['name'], value) + if(nmap.verbosity() == 0 and action.default == false) then + stdnse.print_debug(1, "dhcp-discover: Server returned unrequested option (%s => %s)", action['name'], value) - else - if(value) then - table.insert(result['options'], {name=action['name'], value=value}) - else - stdnse.print_debug(1, "dhcp-discover: Couldn't determine value for %s", action['name']); - end - end - end + else + if(value) then + table.insert(result['options'], {name=action['name'], value=value}) + else + stdnse.print_debug(1, "dhcp-discover: Couldn't determine value for %s", action['name']); + end + end + end - -- Handle the 'Option Overload' option specially -- if it's set, it tells us to use the file and/or sname values after we - -- run out of data. - if(option == 52) then - if(value == 1) then - data = data .. result['file'] - elseif(value == 2) then - data = data .. result['sname'] - elseif(value == 3) then - data = data .. result['file'] .. result['sname'] - else - stdnse.print_debug(1, "dhcp-discover: Warning: 'Option Overload' gave an unsupported value: %d", value) - end - end - end + -- Handle the 'Option Overload' option specially -- if it's set, it tells us to use the file and/or sname values after we + -- run out of data. + if(option == 52) then + if(value == 1) then + data = data .. result['file'] + elseif(value == 2) then + data = data .. result['sname'] + elseif(value == 3) then + data = data .. result['file'] .. result['sname'] + else + stdnse.print_debug(1, "dhcp-discover: Warning: 'Option Overload' gave an unsupported value: %d", value) + end + end + end - return true, result + return true, result end ---Build and send any kind of DHCP packet, and parse the response. This is the only interface @@ -616,43 +616,43 @@ end --@return status (true or false) --@return The parsed response, as a table. function make_request(target, request_type, ip_address, mac_address, options, request_options, overrides, lease_time) - -- A unique id that identifies this particular session (and lets us filter out what we don't want to see) - local transaction_id = overrides and overrides['xid'] or bin.pack("I", ipOps.todword(ip_address)), mac_address, options, request_options, overrides, lease_time, transaction_id) - if(not(status)) then - stdnse.print_debug(1, "dhcp: Couldn't build packet: " .. packet) - return false, "Couldn't build packet: " .. packet - end + -- Generate the packet + local status, packet = dhcp_build(request_type, bin.pack(">I", ipOps.todword(ip_address)), mac_address, options, request_options, overrides, lease_time, transaction_id) + if(not(status)) then + stdnse.print_debug(1, "dhcp: Couldn't build packet: " .. packet) + return false, "Couldn't build packet: " .. packet + end - local socket = nmap.new_socket("udp") - socket:bind(nil, 68) - socket:set_timeout(5000) + local socket = nmap.new_socket("udp") + socket:bind(nil, 68) + socket:set_timeout(5000) - -- Send the packet and get the response - local status, response = dhcp_send(socket, target, packet) - if(not(status)) then - stdnse.print_debug(1, "dhcp: Couldn't send packet: " .. response) - return false, "Couldn't send packet: " .. response - end + -- Send the packet and get the response + local status, response = dhcp_send(socket, target, packet) + if(not(status)) then + stdnse.print_debug(1, "dhcp: Couldn't send packet: " .. response) + return false, "Couldn't send packet: " .. response + end - status, response = dhcp_receive(socket, transaction_id) - socket:close() + status, response = dhcp_receive(socket, transaction_id) + socket:close() - if ( not(status) ) then - stdnse.print_debug(1, "dhcp: Couldn't receive packet: " .. response) - return false, "Couldn't receive packet: " .. response - end + if ( not(status) ) then + stdnse.print_debug(1, "dhcp: Couldn't receive packet: " .. response) + return false, "Couldn't receive packet: " .. response + end - -- Parse the response - local status, parsed = dhcp_parse(response, transaction_id) - if(not(status)) then - stdnse.print_debug(1, "dhcp: Couldn't parse response: " .. parsed) - return false, "Couldn't parse response: " .. parsed - end + -- Parse the response + local status, parsed = dhcp_parse(response, transaction_id) + if(not(status)) then + stdnse.print_debug(1, "dhcp: Couldn't parse response: " .. parsed) + return false, "Couldn't parse response: " .. parsed + end - return true, parsed + return true, parsed end diff --git a/nselib/dhcp6.lua b/nselib/dhcp6.lua index 0771ecfee..ac7e9cd8e 100644 --- a/nselib/dhcp6.lua +++ b/nselib/dhcp6.lua @@ -2,11 +2,11 @@ -- Minimalistic DHCP6 (Dynamic Host Configuration Protocol for IPv6) -- implementation supporting basic DHCP6 Solicit requests The library -- is structured around the following classes: --- * DHCP6.Option - DHCP6 options encoders (for requests) and decoders --- (for responses) --- * DHCP6.Request - DHCP6 request encoder and decoder --- * DHCP6.Response - DHCP6 response encoder and decoder --- * Helper - The helper class, primary script interface +-- * DHCP6.Option - DHCP6 options encoders (for requests) and decoders +-- (for responses) +-- * DHCP6.Request - DHCP6 request encoder and decoder +-- * DHCP6.Response - DHCP6 response encoder and decoder +-- * Helper - The helper class, primary script interface -- -- The following sample code sends a DHCP6 Solicit request and returns a -- response suitable for script output: @@ -35,478 +35,478 @@ DHCP6 = {} -- DHCP6 request and response types DHCP6.Type = { - SOLICIT = 1, - ADVERTISE = 2, - REQUEST = 3, + SOLICIT = 1, + ADVERTISE = 2, + REQUEST = 3, } -- DHCP6 type as string DHCP6.TypeStr = { - [DHCP6.Type.SOLICIT] = "Solicit", - [DHCP6.Type.ADVERTISE] = "Advertise", - [DHCP6.Type.REQUEST] = "Request", + [DHCP6.Type.SOLICIT] = "Solicit", + [DHCP6.Type.ADVERTISE] = "Advertise", + [DHCP6.Type.REQUEST] = "Request", } -- DHCP6 option types DHCP6.OptionTypes = { - OPTION_CLIENTID = 0x01, - OPTION_SERVERID = 0x02, - OPTION_IA_NA = 0x03, - OPTION_IAADDR = 0x05, - OPTION_ELAPSED_TIME = 0x08, - OPTION_STATUS_CODE = 0x0d, - OPTION_DNS_SERVERS = 0x17, - OPTION_DOMAIN_LIST = 0x18, - OPTION_IA_PD = 0x19, - OPTION_SNTP_SERVERS = 0x1f, - OPTION_CLIENT_FQDN = 0x27, + OPTION_CLIENTID = 0x01, + OPTION_SERVERID = 0x02, + OPTION_IA_NA = 0x03, + OPTION_IAADDR = 0x05, + OPTION_ELAPSED_TIME = 0x08, + OPTION_STATUS_CODE = 0x0d, + OPTION_DNS_SERVERS = 0x17, + OPTION_DOMAIN_LIST = 0x18, + OPTION_IA_PD = 0x19, + OPTION_SNTP_SERVERS = 0x1f, + OPTION_CLIENT_FQDN = 0x27, } -- DHCP6 options DHCP6.Option = { - [DHCP6.OptionTypes.OPTION_ELAPSED_TIME] = { + [DHCP6.OptionTypes.OPTION_ELAPSED_TIME] = { - -- Create a new class instance - -- @param time in ms since last request - -- @return o new instance of class - new = function(self, time) - local o = { - type = DHCP6.OptionTypes.OPTION_ELAPSED_TIME, - time = time, - -- in case no time was created, we need this to be able to - -- calculate time since instantiation - created = os.time(), - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param time in ms since last request + -- @return o new instance of class + new = function(self, time) + local o = { + type = DHCP6.OptionTypes.OPTION_ELAPSED_TIME, + time = time, + -- in case no time was created, we need this to be able to + -- calculate time since instantiation + created = os.time(), + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(self) - local data - if ( self.time ) then - data = bin.pack(">S", self.time) - else - data = bin.pack(">S", (os.time() - self.created) * 1000) - end - return bin.pack(">SP", self.type, data) - end, + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(self) + local data + if ( self.time ) then + data = bin.pack(">S", self.time) + else + data = bin.pack(">S", (os.time() - self.created) * 1000) + end + return bin.pack(">SP", self.type, data) + end, - }, + }, - [DHCP6.OptionTypes.OPTION_CLIENTID] = { + [DHCP6.OptionTypes.OPTION_CLIENTID] = { - -- Create a new class instance - -- @param mac string containing the mac address - -- @param duid number the duid of the client - -- @param hwtype number the hwtype of the client - -- @param time number time since 2000-01-01 00:00:00 - -- @return o new instance of class - new = function(self, mac, duid, hwtype, time) - local o = { - type = DHCP6.OptionTypes.OPTION_CLIENTID, - duid = duid or 1, - hwtype = hwtype or 1, - time = time or os.time() - os.time({year=2000, day=1, month=1, hour=0, min=0, sec=0}), - mac = mac, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param mac string containing the mac address + -- @param duid number the duid of the client + -- @param hwtype number the hwtype of the client + -- @param time number time since 2000-01-01 00:00:00 + -- @return o new instance of class + new = function(self, mac, duid, hwtype, time) + local o = { + type = DHCP6.OptionTypes.OPTION_CLIENTID, + duid = duid or 1, + hwtype = hwtype or 1, + time = time or os.time() - os.time({year=2000, day=1, month=1, hour=0, min=0, sec=0}), + mac = mac, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID]:new() - local pos - pos, opt.duid = bin.unpack(">S", data, pos) - if ( 1 ~= opt.duid ) then - stdnse.print_debug("Unexpected DUID type (%d)", opt.duid) - return - end - pos, opt.hwtype, opt.time, opt.mac = bin.unpack(">SIA" .. (#data - pos - 4 - 2 + 1), data, pos) - opt.time = opt.time + os.time({year=2000, day=1, month=1, hour=0, min=0, sec=0}) - return opt - end, + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID]:new() + local pos + pos, opt.duid = bin.unpack(">S", data, pos) + if ( 1 ~= opt.duid ) then + stdnse.print_debug("Unexpected DUID type (%d)", opt.duid) + return + end + pos, opt.hwtype, opt.time, opt.mac = bin.unpack(">SIA" .. (#data - pos - 4 - 2 + 1), data, pos) + opt.time = opt.time + os.time({year=2000, day=1, month=1, hour=0, min=0, sec=0}) + return opt + end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(self) - local data = bin.pack(">SSIA", self.duid, self.hwtype, self.time, self.mac) - return bin.pack(">SP", self.type, data) - end, - }, + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(self) + local data = bin.pack(">SSIA", self.duid, self.hwtype, self.time, self.mac) + return bin.pack(">SP", self.type, data) + end, + }, - [DHCP6.OptionTypes.OPTION_SERVERID] = { - -- Create a new class instance - -- @param mac string containing the mac address - -- @param duid number the duid of the client - -- @param hwtype number the hwtype of the client - -- @param time number time since 2000-01-01 00:00:00 - -- @return o new instance of class - new = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].new(...) end, + [DHCP6.OptionTypes.OPTION_SERVERID] = { + -- Create a new class instance + -- @param mac string containing the mac address + -- @param duid number the duid of the client + -- @param hwtype number the hwtype of the client + -- @param time number time since 2000-01-01 00:00:00 + -- @return o new instance of class + new = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].new(...) end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].parse(...) end, + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].parse(...) end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].__tostring(...) end, - }, + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].__tostring(...) end, + }, - [DHCP6.OptionTypes.OPTION_STATUS_CODE] = { + [DHCP6.OptionTypes.OPTION_STATUS_CODE] = { - -- Create a new class instance - -- @param code number containing the error code - -- @param msg string containing the error message - -- @return o new instance of class - new = function(self, code, msg) - local o = { - type = DHCP6.OptionTypes.OPTION_STATUS_CODE, - code = code, - msg = msg, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param code number containing the error code + -- @param msg string containing the error message + -- @return o new instance of class + new = function(self, code, msg) + local o = { + type = DHCP6.OptionTypes.OPTION_STATUS_CODE, + code = code, + msg = msg, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_STATUS_CODE]:new() - local pos + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_STATUS_CODE]:new() + local pos - pos, opt.code, opt.msg = bin.unpack(">SA" .. (#data - 2), data) - return opt - end, + pos, opt.code, opt.msg = bin.unpack(">SA" .. (#data - 2), data) + return opt + end, - }, + }, - [DHCP6.OptionTypes.OPTION_DNS_SERVERS] = { + [DHCP6.OptionTypes.OPTION_DNS_SERVERS] = { - -- Create a new class instance - -- @param servers table containing DNS servers - -- @return o new instance of class - new = function(self, servers) - local o = { - type = DHCP6.OptionTypes.OPTION_DNS_SERVERS, - servers = servers or {}, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param servers table containing DNS servers + -- @return o new instance of class + new = function(self, servers) + local o = { + type = DHCP6.OptionTypes.OPTION_DNS_SERVERS, + servers = servers or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DNS_SERVERS]:new() - local pos, count = 1, #data/16 + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DNS_SERVERS]:new() + local pos, count = 1, #data/16 - for i=1,count do - local srv - pos, srv = bin.unpack(">B16", data, pos) - table.insert(opt.servers, srv) - end - return opt - end, + for i=1,count do + local srv + pos, srv = bin.unpack(">B16", data, pos) + table.insert(opt.servers, srv) + end + return opt + end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(self) - local len = #self.servers * 16 - local data= bin.pack(">SS", self.type, self.len) - for _, ipv6 in ipairs(self.servers) do - data = data .. ipOps.ip_to_str(ipv6) - end - return data - end - }, + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(self) + local len = #self.servers * 16 + local data= bin.pack(">SS", self.type, self.len) + for _, ipv6 in ipairs(self.servers) do + data = data .. ipOps.ip_to_str(ipv6) + end + return data + end + }, - [DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = { + [DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = { - -- Create a new class instance - -- @param domain table containing the search domains - -- @return o new instance of class - new = function(self, domains) - local o = { - type = DHCP6.OptionTypes.OPTION_DOMAIN_LIST, - domains = domains or {}, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param domain table containing the search domains + -- @return o new instance of class + new = function(self, domains) + local o = { + type = DHCP6.OptionTypes.OPTION_DOMAIN_LIST, + domains = domains or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DOMAIN_LIST]:new() - local pos = 1 + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DOMAIN_LIST]:new() + local pos = 1 - repeat - local domain = {} - repeat - local part - pos, part = bin.unpack("p", data, pos) - if ( part ~= "" ) then - table.insert(domain, part) - end - until( part == "" ) - table.insert(opt.domains, stdnse.strjoin(".", domain)) - until( pos > #data ) - return opt - end, + repeat + local domain = {} + repeat + local part + pos, part = bin.unpack("p", data, pos) + if ( part ~= "" ) then + table.insert(domain, part) + end + until( part == "" ) + table.insert(opt.domains, stdnse.strjoin(".", domain)) + until( pos > #data ) + return opt + end, - }, + }, - [DHCP6.OptionTypes.OPTION_IA_PD] = { + [DHCP6.OptionTypes.OPTION_IA_PD] = { - -- Create a new class instance - -- @param iad number containing iad - -- @param t1 number containing t1 - -- @param t2 number containing t2 - -- @param option string containing any options - -- @return o new instance of class - new = function(self, iaid, t1, t2, options) - local o = { - type = DHCP6.OptionTypes.OPTION_IA_PD, - iaid = iaid, - t1 = t1 or 0, - t2 = t2 or 0, - options = options or "", - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param iad number containing iad + -- @param t1 number containing t1 + -- @param t2 number containing t2 + -- @param option string containing any options + -- @return o new instance of class + new = function(self, iaid, t1, t2, options) + local o = { + type = DHCP6.OptionTypes.OPTION_IA_PD, + iaid = iaid, + t1 = t1 or 0, + t2 = t2 or 0, + options = options or "", + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(self) - local data = bin.pack(">IIIA", self.iaid, self.t1, self.t2, self.options) - return bin.pack(">SP", self.type, data) - end, + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(self) + local data = bin.pack(">IIIA", self.iaid, self.t1, self.t2, self.options) + return bin.pack(">SP", self.type, data) + end, - }, + }, - [DHCP6.OptionTypes.OPTION_IA_NA] = { + [DHCP6.OptionTypes.OPTION_IA_NA] = { - -- Create a new class instance - -- @param iad number containing iad - -- @param t1 number containing t1 - -- @param t2 number containing t2 - -- @param option table containing any options - -- @return o new instance of class - new = function(self, iaid, t1, t2, options) - local o = { - type = DHCP6.OptionTypes.OPTION_IA_NA, - iaid = iaid, - t1 = t1 or 0, - t2 = t2 or 0, - options = options or {}, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param iad number containing iad + -- @param t1 number containing t1 + -- @param t2 number containing t2 + -- @param option table containing any options + -- @return o new instance of class + new = function(self, iaid, t1, t2, options) + local o = { + type = DHCP6.OptionTypes.OPTION_IA_NA, + iaid = iaid, + t1 = t1 or 0, + t2 = t2 or 0, + options = options or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_IA_NA]:new() - local pos + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_IA_NA]:new() + local pos - pos, opt.iaid, opt.t1, opt.t2 = bin.unpack(">III", data) + pos, opt.iaid, opt.t1, opt.t2 = bin.unpack(">III", data) - -- do we have any options - while ( pos < #data ) do - local typ, len, ipv6, pref_lt, valid_lt, options - pos, typ, len = bin.unpack(">SS", data, pos) + -- do we have any options + while ( pos < #data ) do + local typ, len, ipv6, pref_lt, valid_lt, options + pos, typ, len = bin.unpack(">SS", data, pos) - if ( 5 == DHCP6.OptionTypes.OPTION_IAADDR ) then - local addr = { type = DHCP6.OptionTypes.OPTION_IAADDR } - pos, addr.ipv6, addr.pref_lt, addr.valid_lt = bin.unpack(">A16II", data, pos) - table.insert(opt.options, addr) - else - pos = pos + len - end - end - return opt - end, + if ( 5 == DHCP6.OptionTypes.OPTION_IAADDR ) then + local addr = { type = DHCP6.OptionTypes.OPTION_IAADDR } + pos, addr.ipv6, addr.pref_lt, addr.valid_lt = bin.unpack(">A16II", data, pos) + table.insert(opt.options, addr) + else + pos = pos + len + end + end + return opt + end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(self) - local data = bin.pack(">III", self.iaid, self.t1, self.t2) + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(self) + local data = bin.pack(">III", self.iaid, self.t1, self.t2) - -- TODO: we don't cover self.options here, we should probably add that - return bin.pack(">SP", self.type, data) - end, - }, + -- TODO: we don't cover self.options here, we should probably add that + return bin.pack(">SP", self.type, data) + end, + }, - [DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = { + [DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = { - -- Create a new class instance - -- @param servers table containing the NTP servers - -- @return o new instance of class - new = function(self, servers) - local o = { - type = DHCP6.OptionTypes.OPTION_SNTP_SERVERS, - servers = servers or {}, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param servers table containing the NTP servers + -- @return o new instance of class + new = function(self, servers) + local o = { + type = DHCP6.OptionTypes.OPTION_SNTP_SERVERS, + servers = servers or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_SNTP_SERVERS]:new() - local pos, server = 1 + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_SNTP_SERVERS]:new() + local pos, server = 1 - repeat - pos, server = bin.unpack(">B16", data, pos) - table.insert( opt.servers, ipOps.bin_to_ip(server) ) - until( pos > #data ) - return opt - end, - }, + repeat + pos, server = bin.unpack(">B16", data, pos) + table.insert( opt.servers, ipOps.bin_to_ip(server) ) + until( pos > #data ) + return opt + end, + }, - [DHCP6.OptionTypes.OPTION_CLIENT_FQDN] = { + [DHCP6.OptionTypes.OPTION_CLIENT_FQDN] = { - -- Create a new class instance - -- @param fqdn string containing the fqdn - -- @return o new instance of class - new = function(self, fqdn) - local o = { - type = DHCP6.OptionTypes.OPTION_CLIENT_FQDN, - fqdn = fqdn or "", - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param fqdn string containing the fqdn + -- @return o new instance of class + new = function(self, fqdn) + local o = { + type = DHCP6.OptionTypes.OPTION_CLIENT_FQDN, + fqdn = fqdn or "", + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENT_FQDN]:new() - local pos = 2 - local pieces = {} + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENT_FQDN]:new() + local pos = 2 + local pieces = {} - repeat - local tmp - pos, tmp = bin.unpack("p", data, pos) - table.insert(pieces, tmp) - until(pos >= #data) - opt.fqdn = stdnse.strjoin(".", pieces) - return opt - end, + repeat + local tmp + pos, tmp = bin.unpack("p", data, pos) + table.insert(pieces, tmp) + until(pos >= #data) + opt.fqdn = stdnse.strjoin(".", pieces) + return opt + end, - } + } } DHCP6.Request = { - -- Create a new class instance - -- @param msgtype number containing the message type - -- @param xid number containing the transaction id - -- @param opts table containing any request options - -- @return o new instance of class - new = function(self, msgtype, xid, opts) - local o = { - type = msgtype, - xid = xid or math.random(1048575), - opts = opts or {} - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Create a new class instance + -- @param msgtype number containing the message type + -- @param xid number containing the transaction id + -- @param opts table containing any request options + -- @return o new instance of class + new = function(self, msgtype, xid, opts) + local o = { + type = msgtype, + xid = xid or math.random(1048575), + opts = opts or {} + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Adds a new DHCP6 option to the request - -- @param opt instance of object to add to the request - addOption = function(self, opt) - table.insert(self.opts, opt) - end, + -- Adds a new DHCP6 option to the request + -- @param opt instance of object to add to the request + addOption = function(self, opt) + table.insert(self.opts, opt) + end, - -- Converts option to a string - -- @return str string containing the class instance as string - __tostring = function(self) - local tmp = bit.lshift(self.type, 24) + self.xid - local data = "" + -- Converts option to a string + -- @return str string containing the class instance as string + __tostring = function(self) + local tmp = bit.lshift(self.type, 24) + self.xid + local data = "" - for _, opt in ipairs(self.opts) do - data = data .. tostring(opt) - end - return bin.pack(">IA", tmp, data) - end, + for _, opt in ipairs(self.opts) do + data = data .. tostring(opt) + end + return bin.pack(">IA", tmp, data) + end, } -- The Response class handles responses from the server DHCP6.Response = { - -- Creates a new instance of the response class - -- @param msgtype number containing the type of DHCP6 message - -- @param xid number containing the transaction ID - new = function(self, msgtype, xid, opts) - local o = { - msgtype = msgtype, - xid = xid, - opts = opts or {}, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the response class + -- @param msgtype number containing the type of DHCP6 message + -- @param xid number containing the transaction ID + new = function(self, msgtype, xid, opts) + local o = { + msgtype = msgtype, + xid = xid, + opts = opts or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parse the data string and create an instance of the class - -- @param data string containing the data as received over the socket - -- @return opt new instance of option - parse = function(data) - local resp = DHCP6.Response:new() - local pos, tmp = bin.unpack(">I", data) + -- Parse the data string and create an instance of the class + -- @param data string containing the data as received over the socket + -- @return opt new instance of option + parse = function(data) + local resp = DHCP6.Response:new() + local pos, tmp = bin.unpack(">I", data) - resp.msgtype = bit.band(tmp, 0xFF000000) - resp.msgtype = bit.rshift(resp.msgtype, 24) - resp.xid = bit.band(tmp, 0x00FFFFFF) - while( pos < #data ) do - local opt = {} - pos, opt.type, opt.data = bin.unpack(">SP", data, pos) - if ( DHCP6.Option[opt.type] and DHCP6.Option[opt.type].parse ) then - local opt_parsed = DHCP6.Option[opt.type].parse(opt.data) - if ( not(opt_parsed) ) then - table.insert(resp.opts, { type = opt.type, raw = opt.data }) - else - table.insert(resp.opts, { type = opt.type, resp = opt_parsed, raw = opt.data }) - end - else - stdnse.print_debug(2, "No option decoder for type: %d; len: %d", opt.type, #(opt.data or "")) - table.insert(resp.opts, { type = opt.type, raw = opt.data }) - end - end - return resp - end + resp.msgtype = bit.band(tmp, 0xFF000000) + resp.msgtype = bit.rshift(resp.msgtype, 24) + resp.xid = bit.band(tmp, 0x00FFFFFF) + while( pos < #data ) do + local opt = {} + pos, opt.type, opt.data = bin.unpack(">SP", data, pos) + if ( DHCP6.Option[opt.type] and DHCP6.Option[opt.type].parse ) then + local opt_parsed = DHCP6.Option[opt.type].parse(opt.data) + if ( not(opt_parsed) ) then + table.insert(resp.opts, { type = opt.type, raw = opt.data }) + else + table.insert(resp.opts, { type = opt.type, resp = opt_parsed, raw = opt.data }) + end + else + stdnse.print_debug(2, "No option decoder for type: %d; len: %d", opt.type, #(opt.data or "")) + table.insert(resp.opts, { type = opt.type, raw = opt.data }) + end + end + return resp + end } @@ -518,141 +518,141 @@ DHCP6.Response = { -- respective class. OptionToString = { - [DHCP6.OptionTypes.OPTION_CLIENTID] = function(opt) - local HWTYPE_ETHER = 1 - if ( HWTYPE_ETHER == opt.hwtype ) then - local mac = stdnse.tohex(opt.mac):upper() - mac = mac:gsub("..", "%1:"):sub(1, -2) - local tm = os.date("%Y-%m-%d %H:%M:%S", opt.time) - return "Client identifier", ("MAC: %s; Time: %s"):format(mac, tm) - end - end, + [DHCP6.OptionTypes.OPTION_CLIENTID] = function(opt) + local HWTYPE_ETHER = 1 + if ( HWTYPE_ETHER == opt.hwtype ) then + local mac = stdnse.tohex(opt.mac):upper() + mac = mac:gsub("..", "%1:"):sub(1, -2) + local tm = os.date("%Y-%m-%d %H:%M:%S", opt.time) + return "Client identifier", ("MAC: %s; Time: %s"):format(mac, tm) + end + end, - [DHCP6.OptionTypes.OPTION_SERVERID] = function(opt) - local topic, str = OptionToString[DHCP6.OptionTypes.OPTION_CLIENTID](opt) - return "Server identifier", str - end, + [DHCP6.OptionTypes.OPTION_SERVERID] = function(opt) + local topic, str = OptionToString[DHCP6.OptionTypes.OPTION_CLIENTID](opt) + return "Server identifier", str + end, - [DHCP6.OptionTypes.OPTION_IA_NA] = function(opt) - if ( opt.options and 1 == #opt.options ) then - local ipv6 = opt.options[1].ipv6 - ipv6 = select(2, bin.unpack("B" .. #ipv6, ipv6)) - ipv6 = ipOps.bin_to_ip(ipv6) - return "Non-temporary Address", ipv6 - end - end, + [DHCP6.OptionTypes.OPTION_IA_NA] = function(opt) + if ( opt.options and 1 == #opt.options ) then + local ipv6 = opt.options[1].ipv6 + ipv6 = select(2, bin.unpack("B" .. #ipv6, ipv6)) + ipv6 = ipOps.bin_to_ip(ipv6) + return "Non-temporary Address", ipv6 + end + end, - [DHCP6.OptionTypes.OPTION_DNS_SERVERS] = function(opt) - local servers = {} - for _, srv in ipairs(opt.servers) do - local ipv6 = ipOps.bin_to_ip(srv) - table.insert(servers, ipv6) - end - return "DNS Servers", stdnse.strjoin(",", servers) - end, + [DHCP6.OptionTypes.OPTION_DNS_SERVERS] = function(opt) + local servers = {} + for _, srv in ipairs(opt.servers) do + local ipv6 = ipOps.bin_to_ip(srv) + table.insert(servers, ipv6) + end + return "DNS Servers", stdnse.strjoin(",", servers) + end, - [DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = function(opt) - return "Domain Search", stdnse.strjoin(", ", opt.domains) - end, + [DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = function(opt) + return "Domain Search", stdnse.strjoin(", ", opt.domains) + end, - [DHCP6.OptionTypes.OPTION_STATUS_CODE] = function(opt) - return "Error", ("Code: %d; Message: %s"):format(opt.code, opt.msg) - end, + [DHCP6.OptionTypes.OPTION_STATUS_CODE] = function(opt) + return "Error", ("Code: %d; Message: %s"):format(opt.code, opt.msg) + end, - [DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = function(opt) - return "NTP Servers", stdnse.strjoin(", ", opt.servers) - end, + [DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = function(opt) + return "NTP Servers", stdnse.strjoin(", ", opt.servers) + end, } -- The Helper class serves as the main interface to scripts Helper = { - -- Creates a new Helper class instance - -- @param iface string containing the interface name - -- @param options table containing any options, currently - -- timeout - socket timeout in ms - -- @return o new instance of Helper - new = function(self, iface, options) - local o = { - iface = iface, - options = options or {}, - } - setmetatable(o, self) - self.__index = self + -- Creates a new Helper class instance + -- @param iface string containing the interface name + -- @param options table containing any options, currently + -- timeout - socket timeout in ms + -- @return o new instance of Helper + new = function(self, iface, options) + local o = { + iface = iface, + options = options or {}, + } + setmetatable(o, self) + self.__index = self - local info, err = nmap.get_interface_info(iface) - -- if we faile to get interface info, don't return a helper - -- this is true on OS X for interfaces like: p2p0 and vboxnet0 - if ( not(info) and err ) then - return - end - o.mac = info.mac - o.socket = nmap.new_socket("udp") - o.socket:bind(nil, 546) - o.socket:set_timeout(o.options.timeout or 5000) - return o - end, + local info, err = nmap.get_interface_info(iface) + -- if we faile to get interface info, don't return a helper + -- this is true on OS X for interfaces like: p2p0 and vboxnet0 + if ( not(info) and err ) then + return + end + o.mac = info.mac + o.socket = nmap.new_socket("udp") + o.socket:bind(nil, 546) + o.socket:set_timeout(o.options.timeout or 5000) + return o + end, - -- Sends a DHCP6 Solicit message to the server, essentiall requesting a new - -- IPv6 non-temporary address - -- @return table of results suitable for use with - -- stdnse.format_output - solicit = function(self) - local req = DHCP6.Request:new( DHCP6.Type.SOLICIT ) - local option = DHCP6.Option - req:addOption(option[DHCP6.OptionTypes.OPTION_ELAPSED_TIME]:new()) - req:addOption(option[DHCP6.OptionTypes.OPTION_CLIENTID]:new(self.mac)) + -- Sends a DHCP6 Solicit message to the server, essentiall requesting a new + -- IPv6 non-temporary address + -- @return table of results suitable for use with + -- stdnse.format_output + solicit = function(self) + local req = DHCP6.Request:new( DHCP6.Type.SOLICIT ) + local option = DHCP6.Option + req:addOption(option[DHCP6.OptionTypes.OPTION_ELAPSED_TIME]:new()) + req:addOption(option[DHCP6.OptionTypes.OPTION_CLIENTID]:new(self.mac)) - local iaid = select(2, bin.unpack(">I", self.mac:sub(3))) - req:addOption(option[DHCP6.OptionTypes.OPTION_IA_NA]:new(iaid, 3600, 5400)) + local iaid = select(2, bin.unpack(">I", self.mac:sub(3))) + req:addOption(option[DHCP6.OptionTypes.OPTION_IA_NA]:new(iaid, 3600, 5400)) - self.host, self.port = { ip = "ff02::1:2" }, { number = 547, protocol = "udp"} - local status, err = self.socket:sendto( self.host, self.port, tostring(req) ) - if ( not(status) ) then - self.host.ip = ("%s%%%s"):format(self.host.ip, self.iface) - status, err = self.socket:sendto( self.host, self.port, tostring(req) ) - if ( not(status) ) then - return false, "Failed to send DHCP6 request to server" - end - end + self.host, self.port = { ip = "ff02::1:2" }, { number = 547, protocol = "udp"} + local status, err = self.socket:sendto( self.host, self.port, tostring(req) ) + if ( not(status) ) then + self.host.ip = ("%s%%%s"):format(self.host.ip, self.iface) + status, err = self.socket:sendto( self.host, self.port, tostring(req) ) + if ( not(status) ) then + return false, "Failed to send DHCP6 request to server" + end + end - local resp, retries = {}, 3 - repeat - retries = retries - 1 - local status, data = self.socket:receive() - if ( not(status) ) then - return false, "Failed to receive DHCP6 request from server" - end + local resp, retries = {}, 3 + repeat + retries = retries - 1 + local status, data = self.socket:receive() + if ( not(status) ) then + return false, "Failed to receive DHCP6 request from server" + end - resp = DHCP6.Response.parse(data) - if ( not(resp) ) then - return false, "Failed to decode DHCP6 response from server" - end - until( req.xid == resp.xid or retries == 0 ) + resp = DHCP6.Response.parse(data) + if ( not(resp) ) then + return false, "Failed to decode DHCP6 response from server" + end + until( req.xid == resp.xid or retries == 0 ) - if ( req.xid ~= resp.xid ) then - return false, "Failed to receive DHCP6 response from server" - end + if ( req.xid ~= resp.xid ) then + return false, "Failed to receive DHCP6 response from server" + end - local result, result_options = {}, { name = "Options" } - local resptype = DHCP6.TypeStr[resp.msgtype] or ("Unknown (%d)"):format(resp.msgtype) + local result, result_options = {}, { name = "Options" } + local resptype = DHCP6.TypeStr[resp.msgtype] or ("Unknown (%d)"):format(resp.msgtype) - table.insert(result, ("Message type: %s"):format(resptype)) - table.insert(result, ("Transaction id: %d"):format(resp.xid)) + table.insert(result, ("Message type: %s"):format(resptype)) + table.insert(result, ("Transaction id: %d"):format(resp.xid)) - for _, opt in ipairs(resp.opts or {}) do - if ( OptionToString[opt.type] ) then - local topic, str = OptionToString[opt.type](opt.resp) - if ( topic and str ) then - table.insert(result_options, ("%s: %s"):format(topic, str)) - end - else - stdnse.print_debug(2, "No decoder for option type: %d", opt.type) - end - end - table.insert(result, result_options) - return true, result - end, + for _, opt in ipairs(resp.opts or {}) do + if ( OptionToString[opt.type] ) then + local topic, str = OptionToString[opt.type](opt.resp) + if ( topic and str ) then + table.insert(result_options, ("%s: %s"):format(topic, str)) + end + else + stdnse.print_debug(2, "No decoder for option type: %d", opt.type) + end + end + table.insert(result, result_options) + return true, result + end, } diff --git a/nselib/dns.lua b/nselib/dns.lua index aa5731243..6ef1072ca 100644 --- a/nselib/dns.lua +++ b/nselib/dns.lua @@ -48,28 +48,28 @@ get_servers = nmap.get_dns_servers -- @name types -- @class table types = { - A = 1, - NS = 2, - SOA = 6, - CNAME = 5, - PTR = 12, - HINFO = 13, - MX = 15, - TXT = 16, - AAAA = 28, - SRV = 33, - OPT = 41, - SSHFP = 44, - NSEC = 47, - NSEC3 = 50, - AXFR = 252, - ANY = 255 + A = 1, + NS = 2, + SOA = 6, + CNAME = 5, + PTR = 12, + HINFO = 13, + MX = 15, + TXT = 16, + AAAA = 28, + SRV = 33, + OPT = 41, + SSHFP = 44, + NSEC = 47, + NSEC3 = 50, + AXFR = 252, + ANY = 255 } CLASS = { - IN = 1, - CH = 3, - ANY = 255 + IN = 1, + CH = 3, + ANY = 255 } @@ -84,51 +84,51 @@ CLASS = { -- @return Status (true or false). -- @return Response (if status is true). local function sendPacketsUDP(data, host, port, timeout, cnt, multiple) - local socket = nmap.new_socket("udp") - local responses = {} + local socket = nmap.new_socket("udp") + local responses = {} - socket:set_timeout(timeout) + socket:set_timeout(timeout) - if ( not(multiple) ) then - socket:connect( host, port, "udp" ) + if ( not(multiple) ) then + socket:connect( host, port, "udp" ) + end + + for i = 1, cnt do + local status, err + + if ( multiple ) then + status, err = socket:sendto(host, port, data) + else + status, err = socket:send(data) end - for i = 1, cnt do - local status, err + if (not(status)) then return false, err end - if ( multiple ) then - status, err = socket:sendto(host, port, data) - else - status, err = socket:send(data) - end + local response - if (not(status)) then return false, err end + if ( multiple ) then + while(true) do + status, response = socket:receive() + if( not(status) ) then break end - local response - - if ( multiple ) then - while(true) do - status, response = socket:receive() - if( not(status) ) then break end - - local status, _, _, ip, _ = socket:get_info() - table.insert(responses, { data = response, peer = ip } ) - end - else - status, response = socket:receive() - if ( status ) then - local status, _, _, ip, _ = socket:get_info() - table.insert(responses, { data = response, peer = ip } ) - end - end - - if (#responses>0) then - socket:close() - return true, responses - end + local status, _, _, ip, _ = socket:get_info() + table.insert(responses, { data = response, peer = ip } ) + end + else + status, response = socket:receive() + if ( status ) then + local status, _, _, ip, _ = socket:get_info() + table.insert(responses, { data = response, peer = ip } ) + end end - socket:close() - return false + + if (#responses>0) then + socket:close() + return true, responses + end + end + socket:close() + return false end --- @@ -140,28 +140,28 @@ end -- @return Status (true or false). -- @return Response (if status is true). local function sendPacketsTCP(data, host, port, timeout) - local socket = nmap.new_socket() - local response - local responses = {} - socket:set_timeout(timeout) - socket:connect(host, port) - -- add payload size we are assuming a minimum size here of 256? - local send_data = '\000' .. string.char(#data) .. data - socket:send(send_data) - local response = '' - while true do - local status, recv_data = socket:receive_bytes(1) - if not status then break end - response = response .. recv_data - end - local status, _, _, ip, _ = socket:get_info() - -- remove payload size - table.insert(responses, { data = string.sub(response,3), peer = ip } ) - socket:close() - if (#responses>0) then - return true, responses - end - return false + local socket = nmap.new_socket() + local response + local responses = {} + socket:set_timeout(timeout) + socket:connect(host, port) + -- add payload size we are assuming a minimum size here of 256? + local send_data = '\000' .. string.char(#data) .. data + socket:send(send_data) + local response = '' + while true do + local status, recv_data = socket:receive_bytes(1) + if not status then break end + response = response .. recv_data + end + local status, _, _, ip, _ = socket:get_info() + -- remove payload size + table.insert(responses, { data = string.sub(response,3), peer = ip } ) + socket:close() + if (#responses>0) then + return true, responses + end + return false end @@ -175,11 +175,11 @@ end -- @param multiple If true, keep reading multiple responses until timeout. -- @return Status (true or false). local function sendPackets(data, host, port, timeout, cnt, multiple, proto) - if proto == nil or proto == 'udp' then - return sendPacketsUDP(data, host, port, timeout, cnt, multiple) - else - return sendPacketsTCP(data, host, port, timeout) - end + if proto == nil or proto == 'udp' then + return sendPacketsUDP(data, host, port, timeout, cnt, multiple) + else + return sendPacketsTCP(data, host, port, timeout) + end end --- @@ -187,35 +187,35 @@ end -- @param rPkt Decoded DNS response packet. -- @return True if useful, false if not. local function gotAnswer(rPkt) - -- have we even got answers? - if #rPkt.answers > 0 then + -- have we even got answers? + if #rPkt.answers > 0 then - -- some MDNS implementation incorrectly return an empty question section - -- if this is the case return true - if rPkt.questions[1] == nil then + -- some MDNS implementation incorrectly return an empty question section + -- if this is the case return true + if rPkt.questions[1] == nil then + return true + end + + -- are those answers not just cnames? + if rPkt.questions[1].dtype == types.A then + for _, v in ipairs(rPkt.answers) do + -- if at least one answer is an A record, it's an answer + if v.dtype == types.A then return true end - - -- are those answers not just cnames? - if rPkt.questions[1].dtype == types.A then - for _, v in ipairs(rPkt.answers) do - -- if at least one answer is an A record, it's an answer - if v.dtype == types.A then - return true - end - end - -- if none was an A record, it's not really an answer - return false - else -- there was no A request, CNAMEs are not of interest - return true - end - -- no such name is the answer - elseif rPkt.flags.RC3 and rPkt.flags.RC4 then - return true - -- really no answer - else - return false + end + -- if none was an A record, it's not really an answer + return false + else -- there was no A request, CNAMEs are not of interest + return true end + -- no such name is the answer + elseif rPkt.flags.RC3 and rPkt.flags.RC4 then + return true + -- really no answer + else + return false + end end @@ -225,66 +225,66 @@ end -- @param rPkt Decoded DNS response packet -- @return String or table of next server(s) to query, or false. local function getAuthDns(rPkt) - if #rPkt.auth == 0 then - if #rPkt.answers == 0 then - return false - else - if rPkt.answers[1].dtype == types.CNAME then - return {cname = rPkt.answers[1].domain} - end - end + if #rPkt.auth == 0 then + if #rPkt.answers == 0 then + return false + else + if rPkt.answers[1].dtype == types.CNAME then + return {cname = rPkt.answers[1].domain} + end end - if rPkt.auth[1].dtype == types.NS then - if #rPkt.add > 0 then - local hosts = {} - for _, v in ipairs(rPkt.add) do - if v.dtype == types.A then - table.insert(hosts, v.ip) - end - end - if #hosts > 0 then return hosts end + end + if rPkt.auth[1].dtype == types.NS then + if #rPkt.add > 0 then + local hosts = {} + for _, v in ipairs(rPkt.add) do + if v.dtype == types.A then + table.insert(hosts, v.ip) end - local status, next = query(rPkt.auth[1].domain, {dtype = "A" }) - return next + end + if #hosts > 0 then return hosts end end - return false + local status, next = query(rPkt.auth[1].domain, {dtype = "A" }) + return next + end + return false end local function processResponse( response, dname, dtype, options ) - local rPkt = decode(response) - -- is it a real answer? - if gotAnswer(rPkt) then - if (options.retPkt) then - return true, rPkt - else - return findNiceAnswer(dtype, rPkt, options.retAll) - end - elseif ( not(options.noauth) ) then -- if not, ask the next server in authority + local rPkt = decode(response) + -- is it a real answer? + if gotAnswer(rPkt) then + if (options.retPkt) then + return true, rPkt + else + return findNiceAnswer(dtype, rPkt, options.retAll) + end + elseif ( not(options.noauth) ) then -- if not, ask the next server in authority - local next_server = getAuthDns(rPkt) + local next_server = getAuthDns(rPkt) - -- if we got a CNAME, ask for the CNAME - if type(next_server) == 'table' and next_server.cname then - options.tries = options.tries - 1 - return query(next_server.cname, options) - end + -- if we got a CNAME, ask for the CNAME + if type(next_server) == 'table' and next_server.cname then + options.tries = options.tries - 1 + return query(next_server.cname, options) + end - -- only ask next server in authority, if - -- we got an auth dns and - -- it isn't the one we just asked - if next_server and next_server ~= options.host and options.tries > 1 then - options.host = next_server - options.tries = options.tries - 1 - return query(dname, options) - end - elseif ( options.retPkt ) then - return true, rPkt - end + -- only ask next server in authority, if + -- we got an auth dns and + -- it isn't the one we just asked + if next_server and next_server ~= options.host and options.tries > 1 then + options.host = next_server + options.tries = options.tries - 1 + return query(dname, options) + end + elseif ( options.retPkt ) then + return true, rPkt + end - -- nothing worked - stdnse.print_debug(1, "dns.query() failed to resolve the requested query%s%s", dname and ": " or ".", dname or "") - return false, "No Answers" + -- nothing worked + stdnse.print_debug(1, "dns.query() failed to resolve the requested query%s%s", dname and ": " or ".", dname or "") + return false, "No Answers" end @@ -313,92 +313,92 @@ end -- @return String answer of the requested type, table of answers or a String error message of one of the following: -- "No Such Name", "No Servers", "No Answers", "Unable to handle response" function query(dname, options) - if not options then options = {} end + if not options then options = {} end - local dtype, host, port, proto = options.dtype, options.host, options.port, options.proto - if proto == nil then proto = 'udp' end - if port == nil then port = '53' end + local dtype, host, port, proto = options.dtype, options.host, options.port, options.proto + if proto == nil then proto = 'udp' end + if port == nil then port = '53' end - local class = options.class or CLASS.IN - if not options.tries then options.tries = 10 end -- don't get into an infinite loop + local class = options.class or CLASS.IN + if not options.tries then options.tries = 10 end -- don't get into an infinite loop - if not options.sendCount then options.sendCount = 2 end + if not options.sendCount then options.sendCount = 2 end - if type( options.timeout ) ~= "number" then options.timeout = get_default_timeout() end + if type( options.timeout ) ~= "number" then options.timeout = get_default_timeout() end - if type(dtype) == "string" then - dtype = types[dtype] - end - if not dtype then dtype = types.A end + if type(dtype) == "string" then + dtype = types[dtype] + end + if not dtype then dtype = types.A end - local srv - local srvI = 1 - if not port then port = 53 end - if not host then - srv = get_servers() - if srv and srv[1] then - host = srv[1] - else - return false, "No Servers" - end - elseif type(host) == "table" then - srv = host - host = srv[1] - end - - local pkt = newPacket() - addQuestion(pkt, dname, dtype, class) - if options.norecurse then pkt.flags.RD = false end - - local dnssec = {} - if ( options.dnssec ) then - dnssec = { DO = true } - end - - if ( options.nsid ) then - addNSID(pkt, dnssec) - elseif ( options.subnet ) then - local family = { ["inet"] = 1, ["inet6"] = 2 } - assert( family[options.subnet.family], "Unsupported subnet family") - options.subnet.family = family[options.subnet.family] - addClientSubnet(pkt, dnssec, options.subnet ) - elseif ( dnssec.DO ) then - addOPT(pkt, {DO = true}) - end - - if ( options.flags ) then pkt.flags.raw = options.flags end - if ( options.id ) then pkt.id = options.id end - - local data = encode(pkt) - - local status, response = sendPackets(data, host, port, options.timeout, options.sendCount, options.multiple, proto) - - - -- if working with know nameservers, try the others - while((not status) and srv and srvI < #srv) do - srvI = srvI + 1 - host = srv[srvI] - status, response = sendPackets(data, host, port, options.timeout, options.sendCount) - end - - -- if we got any response: - if status then - if ( options.multiple ) then - local multiresponse = {} - for _, r in ipairs( response ) do - local status, presponse = processResponse( r.data, dname, dtype, options ) - if( status ) then - table.insert( multiresponse, { ['output']=presponse, ['peer']=r.peer } ) - end - end - return true, multiresponse - else - return processResponse( response[1].data, dname, dtype, options) - end + local srv + local srvI = 1 + if not port then port = 53 end + if not host then + srv = get_servers() + if srv and srv[1] then + host = srv[1] else - stdnse.print_debug(1, "dns.query() got zero responses attempting to resolve query%s%s", dname and ": " or ".", dname or "") - return false, "No Answers" + return false, "No Servers" end + elseif type(host) == "table" then + srv = host + host = srv[1] + end + + local pkt = newPacket() + addQuestion(pkt, dname, dtype, class) + if options.norecurse then pkt.flags.RD = false end + + local dnssec = {} + if ( options.dnssec ) then + dnssec = { DO = true } + end + + if ( options.nsid ) then + addNSID(pkt, dnssec) + elseif ( options.subnet ) then + local family = { ["inet"] = 1, ["inet6"] = 2 } + assert( family[options.subnet.family], "Unsupported subnet family") + options.subnet.family = family[options.subnet.family] + addClientSubnet(pkt, dnssec, options.subnet ) + elseif ( dnssec.DO ) then + addOPT(pkt, {DO = true}) + end + + if ( options.flags ) then pkt.flags.raw = options.flags end + if ( options.id ) then pkt.id = options.id end + + local data = encode(pkt) + + local status, response = sendPackets(data, host, port, options.timeout, options.sendCount, options.multiple, proto) + + + -- if working with know nameservers, try the others + while((not status) and srv and srvI < #srv) do + srvI = srvI + 1 + host = srv[srvI] + status, response = sendPackets(data, host, port, options.timeout, options.sendCount) + end + + -- if we got any response: + if status then + if ( options.multiple ) then + local multiresponse = {} + for _, r in ipairs( response ) do + local status, presponse = processResponse( r.data, dname, dtype, options ) + if( status ) then + table.insert( multiresponse, { ['output']=presponse, ['peer']=r.peer } ) + end + end + return true, multiresponse + else + return processResponse( response[1].data, dname, dtype, options) + end + else + stdnse.print_debug(1, "dns.query() got zero responses attempting to resolve query%s%s", dname and ": " or ".", dname or "") + return false, "No Answers" + end end @@ -408,35 +408,35 @@ end -- @return "Domain"-style representation of IP as subdomain of in-addr.arpa or -- ip6.arpa. function reverse(ip) - ip = ipOps.expand_ip(ip) - if type(ip) ~= "string" then return nil end - local delim = "%." - local arpa = ".in-addr.arpa" - if ip:match(":") then - delim = ":" - arpa = ".ip6.arpa" + ip = ipOps.expand_ip(ip) + if type(ip) ~= "string" then return nil end + local delim = "%." + local arpa = ".in-addr.arpa" + if ip:match(":") then + delim = ":" + arpa = ".ip6.arpa" + end + local ipParts = stdnse.strsplit(delim, ip) + if #ipParts == 8 then + -- padding + local mask = "0000" + for i, part in ipairs(ipParts) do + ipParts[i] = mask:sub(1, #mask - #part) .. part end - local ipParts = stdnse.strsplit(delim, ip) - if #ipParts == 8 then - -- padding - local mask = "0000" - for i, part in ipairs(ipParts) do - ipParts[i] = mask:sub(1, #mask - #part) .. part - end - -- 32 parts from 8 - local temp = {} - for i, hdt in ipairs(ipParts) do - for part in hdt:gmatch("%x") do - temp[#temp+1] = part - end - end - ipParts = temp + -- 32 parts from 8 + local temp = {} + for i, hdt in ipairs(ipParts) do + for part in hdt:gmatch("%x") do + temp[#temp+1] = part + end end - local ipReverse = {} - for i = #ipParts, 1, -1 do - table.insert(ipReverse, ipParts[i]) - end - return table.concat(ipReverse, ".") .. arpa + ipParts = temp + end + local ipReverse = {} + for i = #ipParts, 1, -1 do + table.insert(ipReverse, ipParts[i]) + end + return table.concat(ipReverse, ".") .. arpa end -- Table for answer fetching functions. @@ -448,26 +448,26 @@ local answerFetcher = {} -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first dns TXT record or Table of TXT records or String Error message. answerFetcher[types.TXT] = function(dec, retAll) - local answers = {} - if not retAll and dec.answers[1].data then - return true, string.sub(dec.answers[1].data, 2) - elseif not retAll then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT") - return false, "No Answers" - else - for _, v in ipairs(dec.answers) do - if v.TXT and v.TXT.text then - for _, v in ipairs( v.TXT.text ) do - table.insert(answers, v) - end - end + local answers = {} + if not retAll and dec.answers[1].data then + return true, string.sub(dec.answers[1].data, 2) + elseif not retAll then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT") + return false, "No Answers" + else + for _, v in ipairs(dec.answers) do + if v.TXT and v.TXT.text then + for _, v in ipairs( v.TXT.text ) do + table.insert(answers, v) end + end end - if #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT") - return false, "No Answers" - end - return true, answers + end + if #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT") + return false, "No Answers" + end + return true, answers end -- Answer fetcher for A records @@ -476,20 +476,20 @@ end -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first dns A record or Table of A records or String Error message. answerFetcher[types.A] = function(dec, retAll) - local answers = {} - for _, ans in ipairs(dec.answers) do - if ans.dtype == types.A then - if not retAll then - return true, ans.ip - end - table.insert(answers, ans.ip) - end + local answers = {} + for _, ans in ipairs(dec.answers) do + if ans.dtype == types.A then + if not retAll then + return true, ans.ip + end + table.insert(answers, ans.ip) end - if not retAll or #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: A") - return false, "No Answers" - end - return true, answers + end + if not retAll or #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: A") + return false, "No Answers" + end + return true, answers end @@ -499,22 +499,22 @@ end -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first Domain entry or Table of domain entries or String Error message. answerFetcher[types.CNAME] = function(dec, retAll) - local answers = {} - if not retAll and dec.answers[1].domain then - return true, dec.answers[1].domain - elseif not retAll then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: NS, PTR or CNAME") - return false, "No Answers" - else - for _, v in ipairs(dec.answers) do - if v.domain then table.insert(answers, v.domain) end - end + local answers = {} + if not retAll and dec.answers[1].domain then + return true, dec.answers[1].domain + elseif not retAll then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: NS, PTR or CNAME") + return false, "No Answers" + else + for _, v in ipairs(dec.answers) do + if v.domain then table.insert(answers, v.domain) end end - if #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: NS, PTR or CNAME") - return false, "No Answers" - end - return true, answers + end + if #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: NS, PTR or CNAME") + return false, "No Answers" + end + return true, answers end -- Answer fetcher for MX records. @@ -525,30 +525,30 @@ end -- Note that the format of a returned MX answer is "preference:hostname:IPaddress" where zero -- or more IP addresses may be present. answerFetcher[types.MX] = function(dec, retAll) - local mx, ip, answers = {}, {}, {} - for _, ans in ipairs(dec.answers) do - if ans.MX then mx[#mx+1] = ans.MX end - if not retAll then break end + local mx, ip, answers = {}, {}, {} + for _, ans in ipairs(dec.answers) do + if ans.MX then mx[#mx+1] = ans.MX end + if not retAll then break end + end + if #mx == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: MX") + return false, "No Answers" + end + for _, add in ipairs(dec.add) do + if ip[add.dname] then table.insert(ip[add.dname], add.ip) + else ip[add.dname] = {add.ip} end + end + for _, mxrec in ipairs(mx) do + if ip[mxrec.server] then + table.insert( answers, ("%s:%s:%s"):format(mxrec.pref or "-", mxrec.server or "-", table.concat(ip[mxrec.server], ":")) ) + if not retAll then return true, answers[1] end + else + -- no IP ? + table.insert( answers, ("%s:%s"):format(mxrec.pref or "-", mxrec.server or "-") ) + if not retAll then return true, answers[1] end end - if #mx == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: MX") - return false, "No Answers" - end - for _, add in ipairs(dec.add) do - if ip[add.dname] then table.insert(ip[add.dname], add.ip) - else ip[add.dname] = {add.ip} end - end - for _, mxrec in ipairs(mx) do - if ip[mxrec.server] then - table.insert( answers, ("%s:%s:%s"):format(mxrec.pref or "-", mxrec.server or "-", table.concat(ip[mxrec.server], ":")) ) - if not retAll then return true, answers[1] end - else - -- no IP ? - table.insert( answers, ("%s:%s"):format(mxrec.pref or "-", mxrec.server or "-") ) - if not retAll then return true, answers[1] end - end - end - return true, answers + end + return true, answers end -- Answer fetcher for SRV records. @@ -561,16 +561,16 @@ end answerFetcher[types.SRV] = function(dec, retAll) local srv, ip, answers = {}, {}, {} for _, ans in ipairs(dec.answers) do - if ans.dtype == types.SRV then - if not retAll then - return true, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) - end - table.insert( answers, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) ) - end + if ans.dtype == types.SRV then + if not retAll then + return true, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) + end + table.insert( answers, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) ) + end end if #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: SRV") - return false, "No Answers" + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: SRV") + return false, "No Answers" end return true, answers @@ -583,20 +583,20 @@ end -- @return String first dns NSEC record or Table of NSEC records or String Error message. -- Note that the format of a returned NSEC answer is "name:dname:types". answerFetcher[types.NSEC] = function(dec, retAll) - local nsec, answers = {}, {} - for _, auth in ipairs(dec.auth) do - if auth.NSEC then nsec[#nsec+1] = auth.NSEC end - if not retAll then break end - end - if #nsec == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: NSEC") - return false, "No Answers" - end - for _, nsecrec in ipairs(nsec) do - table.insert( answers, ("%s:%s:%s"):format(nsecrec.name or "-", nsecrec.dname or "-", stdnse.strjoin(":", nsecrec.types) or "-")) - end - if not retAll then return true, answers[1] end - return true, answers + local nsec, answers = {}, {} + for _, auth in ipairs(dec.auth) do + if auth.NSEC then nsec[#nsec+1] = auth.NSEC end + if not retAll then break end + end + if #nsec == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: NSEC") + return false, "No Answers" + end + for _, nsecrec in ipairs(nsec) do + table.insert( answers, ("%s:%s:%s"):format(nsecrec.name or "-", nsecrec.dname or "-", stdnse.strjoin(":", nsecrec.types) or "-")) + end + if not retAll then return true, answers[1] end + return true, answers end -- Answer fetcher for NS records. @@ -622,20 +622,20 @@ answerFetcher[types.PTR] = answerFetcher[types.CNAME] -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first dns AAAA record or Table of AAAA records or String Error message. answerFetcher[types.AAAA] = function(dec, retAll) - local answers = {} - for _, ans in ipairs(dec.answers) do - if ans.dtype == types.AAAA then - if not retAll then - return true, ans.ipv6 - end - table.insert(answers, ans.ipv6) - end + local answers = {} + for _, ans in ipairs(dec.answers) do + if ans.dtype == types.AAAA then + if not retAll then + return true, ans.ipv6 + end + table.insert(answers, ans.ipv6) end - if not retAll or #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA") - return false, "No Answers" - end - return true, answers + end + if not retAll or #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA") + return false, "No Answers" + end + return true, answers end @@ -648,19 +648,19 @@ end -- @return True if one or more answers of the required type were found - otherwise false. -- @return Answer according to the answer fetcher for dtype or an Error message. function findNiceAnswer(dtype, dec, retAll) - if (#dec.answers > 0) then - if answerFetcher[dtype] then - return answerFetcher[dtype](dec, retAll) - else - stdnse.print_debug(1, "dns.findNiceAnswer() does not have an answerFetcher for dtype %s", tostring(dtype)) - return false, "Unable to handle response" - end - elseif (dec.flags.RC3 and dec.flags.RC4) then - return false, "No Such Name" + if (#dec.answers > 0) then + if answerFetcher[dtype] then + return answerFetcher[dtype](dec, retAll) else - stdnse.print_debug(1, "dns.findNiceAnswer() found zero answers in a response, but got an unexpected flags.replycode") - return false, "No Answers" + stdnse.print_debug(1, "dns.findNiceAnswer() does not have an answerFetcher for dtype %s", tostring(dtype)) + return false, "Unable to handle response" end + elseif (dec.flags.RC3 and dec.flags.RC4) then + return false, "No Such Name" + else + stdnse.print_debug(1, "dns.findNiceAnswer() found zero answers in a response, but got an unexpected flags.replycode") + return false, "No Answers" + end end -- Table for additional fetching functions. @@ -677,26 +677,26 @@ local additionalFetcher = {} -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first dns TXT record or Table of TXT records or String Error message. additionalFetcher[types.TXT] = function(dec, retAll) - local answers = {} - if not retAll and dec.add[1].data then - return true, string.sub(dec.add[1].data, 2) - elseif not retAll then - stdnse.print_debug(1, "dns.aditionalFetcher found no records of the required type: TXT") - return false, "No Answers" - else - for _, v in ipairs(dec.add) do - if v.TXT and v.TXT.text then - for _, v in ipairs( v.TXT.text ) do - table.insert(answers, v) - end - end + local answers = {} + if not retAll and dec.add[1].data then + return true, string.sub(dec.add[1].data, 2) + elseif not retAll then + stdnse.print_debug(1, "dns.aditionalFetcher found no records of the required type: TXT") + return false, "No Answers" + else + for _, v in ipairs(dec.add) do + if v.TXT and v.TXT.text then + for _, v in ipairs( v.TXT.text ) do + table.insert(answers, v) end + end end - if #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT") - return false, "No Answers" - end - return true, answers + end + if #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: TXT") + return false, "No Answers" + end + return true, answers end -- Additional fetcher for A records @@ -705,20 +705,20 @@ end -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first dns A record or Table of A records or String Error message. additionalFetcher[types.A] = function(dec, retAll) - local answers = {} - for _, ans in ipairs(dec.add) do - if ans.dtype == types.A then - if not retAll then - return true, ans.ip - end - table.insert(answers, ans.ip) - end + local answers = {} + for _, ans in ipairs(dec.add) do + if ans.dtype == types.A then + if not retAll then + return true, ans.ip + end + table.insert(answers, ans.ip) end - if not retAll or #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: A") - return false, "No Answers" - end - return true, answers + end + if not retAll or #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: A") + return false, "No Answers" + end + return true, answers end @@ -732,16 +732,16 @@ end additionalFetcher[types.SRV] = function(dec, retAll) local srv, ip, answers = {}, {}, {} for _, ans in ipairs(dec.add) do - if ans.dtype == types.SRV then - if not retAll then - return true, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) - end - table.insert( answers, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) ) - end + if ans.dtype == types.SRV then + if not retAll then + return true, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) + end + table.insert( answers, ("%s:%s:%s:%s"):format( ans.SRV.prio, ans.SRV.weight, ans.SRV.port, ans.SRV.target ) ) + end end if #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: SRV") - return false, "No Answers" + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: SRV") + return false, "No Answers" end return true, answers @@ -754,20 +754,20 @@ end -- @return True if one or more answers of the required type were found - otherwise false. -- @return String first dns AAAA record or Table of AAAA records or String Error message. additionalFetcher[types.AAAA] = function(dec, retAll) - local answers = {} - for _, ans in ipairs(dec.add) do - if ans.dtype == types.AAAA then - if not retAll then - return true, ans.ipv6 - end - table.insert(answers, ans.ipv6) - end + local answers = {} + for _, ans in ipairs(dec.add) do + if ans.dtype == types.AAAA then + if not retAll then + return true, ans.ipv6 + end + table.insert(answers, ans.ipv6) end - if not retAll or #answers == 0 then - stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA") - return false, "No Answers" - end - return true, answers + end + if not retAll or #answers == 0 then + stdnse.print_debug(1, "dns.answerFetcher found no records of the required type: AAAA") + return false, "No Answers" + end + return true, answers end --- @@ -779,20 +779,20 @@ end -- @return True if one or more answers of the required type were found - otherwise false. -- @return Answer according to the answer fetcher for dtype or an Error message. function findNiceAdditional(dtype, dec, retAll) - if (#dec.add > 0) then - if additionalFetcher[dtype] then - return additionalFetcher[dtype](dec, retAll) - else - stdnse.print_debug(1, "dns.findNiceAdditional() does not have an additionalFetcher for dtype %s", - (type(dtype) == 'string' and dtype) or type(dtype) or "nil") - return false, "Unable to handle response" - end - elseif (dec.flags.RC3 and dec.flags.RC4) then - return false, "No Such Name" + if (#dec.add > 0) then + if additionalFetcher[dtype] then + return additionalFetcher[dtype](dec, retAll) else - stdnse.print_debug(1, "dns.findNiceAdditional() found zero answers in a response, but got an unexpected flags.replycode") - return false, "No Answers" + stdnse.print_debug(1, "dns.findNiceAdditional() does not have an additionalFetcher for dtype %s", + (type(dtype) == 'string' and dtype) or type(dtype) or "nil") + return false, "Unable to handle response" end + elseif (dec.flags.RC3 and dec.flags.RC4) then + return false, "No Such Name" + else + stdnse.print_debug(1, "dns.findNiceAdditional() found zero answers in a response, but got an unexpected flags.replycode") + return false, "No Answers" + end end -- @@ -800,15 +800,15 @@ end -- @param fqdn containing the fully qualified domain name -- @return encQ containing the encoded value local function encodeFQDN(fqdn) - if ( not(fqdn) or #fqdn == 0 ) then return string.char(0) end + if ( not(fqdn) or #fqdn == 0 ) then return string.char(0) end - local parts = stdnse.strsplit("%.", fqdn) - local encQ = "" - for _, part in ipairs(parts) do - encQ = encQ .. bin.pack("p", part) - end - encQ = encQ .. string.char(0) - return encQ + local parts = stdnse.strsplit("%.", fqdn) + local encQ = "" + for _, part in ipairs(parts) do + encQ = encQ .. bin.pack("p", part) + end + encQ = encQ .. string.char(0) + return encQ end --- @@ -816,13 +816,13 @@ end -- @param questions Table of questions. -- @return Encoded question string. local function encodeQuestions(questions) - if type(questions) ~= "table" then return nil end - local encQ = "" - for _, v in ipairs(questions) do - encQ = encQ .. encodeFQDN(v.dname) - encQ = encQ .. bin.pack(">SS", v.dtype, v.class) - end - return encQ + if type(questions) ~= "table" then return nil end + local encQ = "" + for _, v in ipairs(questions) do + encQ = encQ .. encodeFQDN(v.dname) + encQ = encQ .. bin.pack(">SS", v.dtype, v.class) + end + return encQ end --- @@ -830,17 +830,17 @@ end -- @param questions Table of questions. -- @return Encoded question string. local function encodeZones(zones) - return encodeQuestions(zones) + return encodeQuestions(zones) end local function encodeUpdates(updates) - if type(updates) ~= "table" then return nil end - local encQ = "" - for _, v in ipairs(updates) do - encQ = encQ .. encodeFQDN(v.dname) - encQ = encQ .. bin.pack(">SSISA", v.dtype, v.class, v.ttl, #v.data, v.data) - end - return encQ + if type(updates) ~= "table" then return nil end + local encQ = "" + for _, v in ipairs(updates) do + encQ = encQ .. encodeFQDN(v.dname) + encQ = encQ .. bin.pack(">SSISA", v.dtype, v.class, v.ttl, #v.data, v.data) + end + return encQ end --- @@ -850,12 +850,12 @@ end -- and rdata. -- @return Encoded additional string. local function encodeAdditional(additional) - if type(additional) ~= "table" then return nil end - local encA = "" - for _, v in ipairs(additional) do - encA = encA .. bin.pack(">xSSISA", v.type, v.class, v.ttl, v.rdlen, v.rdata) - end - return encA + if type(additional) ~= "table" then return nil end + local encA = "" + for _, v in ipairs(additional) do + encA = encA .. bin.pack(">xSSISA", v.type, v.class, v.ttl, v.rdlen, v.rdata) + end + return encA end --- @@ -864,24 +864,24 @@ end -- RA, RCx). -- @return Binary digit string representing flags. local function encodeFlags(flags) - if type(flags) == "string" then return flags end - if type(flags) ~= "table" then return nil end - local fb = "" - if flags.QR then fb = fb .. "1" else fb = fb .. "0" end - if flags.OC1 then fb = fb .. "1" else fb = fb .. "0" end - if flags.OC2 then fb = fb .. "1" else fb = fb .. "0" end - if flags.OC3 then fb = fb .. "1" else fb = fb .. "0" end - if flags.OC4 then fb = fb .. "1" else fb = fb .. "0" end - if flags.AA then fb = fb .. "1" else fb = fb .. "0" end - if flags.TC then fb = fb .. "1" else fb = fb .. "0" end - if flags.RD then fb = fb .. "1" else fb = fb .. "0" end - if flags.RA then fb = fb .. "1" else fb = fb .. "0" end - fb = fb .. "000" - if flags.RC1 then fb = fb .. "1" else fb = fb .. "0" end - if flags.RC2 then fb = fb .. "1" else fb = fb .. "0" end - if flags.RC3 then fb = fb .. "1" else fb = fb .. "0" end - if flags.RC4 then fb = fb .. "1" else fb = fb .. "0" end - return fb + if type(flags) == "string" then return flags end + if type(flags) ~= "table" then return nil end + local fb = "" + if flags.QR then fb = fb .. "1" else fb = fb .. "0" end + if flags.OC1 then fb = fb .. "1" else fb = fb .. "0" end + if flags.OC2 then fb = fb .. "1" else fb = fb .. "0" end + if flags.OC3 then fb = fb .. "1" else fb = fb .. "0" end + if flags.OC4 then fb = fb .. "1" else fb = fb .. "0" end + if flags.AA then fb = fb .. "1" else fb = fb .. "0" end + if flags.TC then fb = fb .. "1" else fb = fb .. "0" end + if flags.RD then fb = fb .. "1" else fb = fb .. "0" end + if flags.RA then fb = fb .. "1" else fb = fb .. "0" end + fb = fb .. "000" + if flags.RC1 then fb = fb .. "1" else fb = fb .. "0" end + if flags.RC2 then fb = fb .. "1" else fb = fb .. "0" end + if flags.RC3 then fb = fb .. "1" else fb = fb .. "0" end + if flags.RC4 then fb = fb .. "1" else fb = fb .. "0" end + return fb end --- @@ -892,30 +892,30 @@ end -- newPacket. -- @return Encoded DNS packet. function encode(pkt) - if type(pkt) ~= "table" then return nil end - local encFlags = encodeFlags(pkt.flags) - local additional = encodeAdditional(pkt.additional) - local aorplen = #pkt.answers - local data, qorzlen, aorulen + if type(pkt) ~= "table" then return nil end + local encFlags = encodeFlags(pkt.flags) + local additional = encodeAdditional(pkt.additional) + local aorplen = #pkt.answers + local data, qorzlen, aorulen - if ( #pkt.questions > 0 ) then - data = encodeQuestions( pkt.questions ) - qorzlen = #pkt.questions - aorulen = 0 - else - -- The packet has no questions, assume we're dealing with an update - data = encodeZones( pkt.zones ) .. encodeUpdates( pkt.updates ) - qorzlen = #pkt.zones - aorulen = #pkt.updates - end + if ( #pkt.questions > 0 ) then + data = encodeQuestions( pkt.questions ) + qorzlen = #pkt.questions + aorulen = 0 + else + -- The packet has no questions, assume we're dealing with an update + data = encodeZones( pkt.zones ) .. encodeUpdates( pkt.updates ) + qorzlen = #pkt.zones + aorulen = #pkt.updates + end - local encStr - if ( pkt.flags.raw ) then - encStr = bin.pack(">SSS4", pkt.id, pkt.flags.raw, qorzlen, aorplen, aorulen, #pkt.additional) .. data .. additional - else - encStr = bin.pack(">SBS4", pkt.id, encFlags, qorzlen, aorplen, aorulen, #pkt.additional) .. data .. additional - end - return encStr + local encStr + if ( pkt.flags.raw ) then + encStr = bin.pack(">SSS4", pkt.id, pkt.flags.raw, qorzlen, aorplen, aorulen, #pkt.additional) .. data .. additional + else + encStr = bin.pack(">SBS4", pkt.id, encFlags, qorzlen, aorplen, aorulen, #pkt.additional) .. data .. additional + end + return encStr end @@ -926,40 +926,40 @@ end -- @return Position after decoding. -- @return Decoded domain, or nil on error. function decStr(data, pos) - local function dec(data, pos, limit) - local partlen - local parts = {} - local part + local function dec(data, pos, limit) + local partlen + local parts = {} + local part - -- Avoid infinite recursion on malformed compressed messages. - limit = limit or 10 - if limit < 0 then - return pos, nil - end - - pos, partlen = bin.unpack(">C", data, pos) - while (partlen ~= 0) do - if (partlen < 64) then - pos, part = bin.unpack("A" .. partlen, data, pos) - if part == nil then - return pos - end - table.insert(parts, part) - pos, partlen = bin.unpack(">C", data, pos) - else - pos, partlen = bin.unpack(">S", data, pos - 1) - local _, part = dec(data, partlen - 0xC000 + 1, limit - 1) - if part == nil then - return pos - end - table.insert(parts, part) - partlen = 0 - end - end - return pos, table.concat(parts, ".") + -- Avoid infinite recursion on malformed compressed messages. + limit = limit or 10 + if limit < 0 then + return pos, nil end - return dec(data, pos) + pos, partlen = bin.unpack(">C", data, pos) + while (partlen ~= 0) do + if (partlen < 64) then + pos, part = bin.unpack("A" .. partlen, data, pos) + if part == nil then + return pos + end + table.insert(parts, part) + pos, partlen = bin.unpack(">C", data, pos) + else + pos, partlen = bin.unpack(">S", data, pos - 1) + local _, part = dec(data, partlen - 0xC000 + 1, limit - 1) + if part == nil then + return pos + end + table.insert(parts, part) + partlen = 0 + end + end + return pos, table.concat(parts, ".") + end + + return dec(data, pos) end @@ -971,14 +971,14 @@ end -- @return Position after decoding. -- @return Table of decoded questions. local function decodeQuestions(data, count, pos) - local q = {} - for i = 1, count do - local currQ = {} - pos, currQ.dname = decStr(data, pos) - pos, currQ.dtype, currQ.class = bin.unpack(">SS", data, pos) - table.insert(q, currQ) - end - return pos, q + local q = {} + for i = 1, count do + local currQ = {} + pos, currQ.dname = decStr(data, pos) + pos, currQ.dtype, currQ.class = bin.unpack(">SS", data, pos) + table.insert(q, currQ) + end + return pos, q end @@ -989,23 +989,23 @@ local decoder = {} -- Decodes IP of A record, puts it in entry.ip. -- @param entry RR in packet. decoder[types.A] = function(entry) - local ip = {} - local _ - _, ip[1], ip[2], ip[3], ip[4] = bin.unpack(">C4", entry.data) - entry.ip = table.concat(ip, ".") + local ip = {} + local _ + _, ip[1], ip[2], ip[3], ip[4] = bin.unpack(">C4", entry.data) + entry.ip = table.concat(ip, ".") end -- Decodes IP of AAAA record, puts it in entry.ipv6. -- @param entry RR in packet. decoder[types.AAAA] = function(entry) - local ip = {} - local pos = 1 - local num - for i = 1, 8 do - pos, num = bin.unpack(">S", entry.data, pos) - table.insert(ip, string.format('%x', num)) - end - entry.ipv6 = table.concat(ip, ":") + local ip = {} + local pos = 1 + local num + for i = 1, 8 do + pos, num = bin.unpack(">S", entry.data, pos) + table.insert(ip, string.format('%x', num)) + end + entry.ipv6 = table.concat(ip, ":") end -- Decodes SSH fingerprint record, puts it in entry.SSHFP as @@ -1015,10 +1015,10 @@ end -- fptype, and fingerprint. -- @param entry RR in packet. decoder[types.SSHFP] = function(entry) - local _ - entry.SSHFP = {} - _, entry.SSHFP.algorithm, - entry.SSHFP.fptype, entry.SSHFP.fingerprint = bin.unpack(">C2H" .. (#entry.data - 2), entry.data) + local _ + entry.SSHFP = {} + _, entry.SSHFP.algorithm, + entry.SSHFP.fptype, entry.SSHFP.fingerprint = bin.unpack(">C2H" .. (#entry.data - 2), entry.data) end @@ -1032,38 +1032,38 @@ end -- @param pos Position in packet after RR. decoder[types.SOA] = function(entry, data, pos) - local np = pos - #entry.data + local np = pos - #entry.data - entry.SOA = {} + entry.SOA = {} - np, entry.SOA.mname = decStr(data, np) - np, entry.SOA.rname = decStr(data, np) - np, entry.SOA.serial, - entry.SOA.refresh, - entry.SOA.retry, - entry.SOA.expire, - entry.SOA.minimum - = bin.unpack(">I5", data, np) + np, entry.SOA.mname = decStr(data, np) + np, entry.SOA.rname = decStr(data, np) + np, entry.SOA.serial, + entry.SOA.refresh, + entry.SOA.retry, + entry.SOA.expire, + entry.SOA.minimum + = bin.unpack(">I5", data, np) end -- An iterator that returns the positions of nonzero bits in the given binary -- string. local function bit_iter(bits) - return coroutine.wrap(function() - for i = 1, #bits do - local n = string.byte(bits, i) - local j = 0 - local mask = 0x80 + return coroutine.wrap(function() + for i = 1, #bits do + local n = string.byte(bits, i) + local j = 0 + local mask = 0x80 - while mask > 0 do - if bit.band(n, mask) ~= 0 then - coroutine.yield((i - 1) * 8 + j) - end - j = j + 1 - mask = bit.rshift(mask, 1) - end + while mask > 0 do + if bit.band(n, mask) ~= 0 then + coroutine.yield((i - 1) * 8 + j) end - end) + j = j + 1 + mask = bit.rshift(mask, 1) + end + end + end) end -- Decodes NSEC records, puts result in entry.NSEC. See RFC 4034, @@ -1075,18 +1075,18 @@ end -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. decoder[types.NSEC] = function (entry, data, pos) - local np = pos - #entry.data - entry.NSEC = {} - entry.NSEC.dname = entry.dname - np, entry.NSEC.next_dname = decStr(data, np) - while np < pos do - local block_num, type_bitmap - np, block_num, type_bitmap = bin.unpack(">Cp", data, np) - entry.NSEC.types = {} - for i in bit_iter(type_bitmap) do - entry.NSEC.types[(block_num - 1) * 256 + i] = true - end + local np = pos - #entry.data + entry.NSEC = {} + entry.NSEC.dname = entry.dname + np, entry.NSEC.next_dname = decStr(data, np) + while np < pos do + local block_num, type_bitmap + np, block_num, type_bitmap = bin.unpack(">Cp", data, np) + entry.NSEC.types = {} + for i in bit_iter(type_bitmap) do + entry.NSEC.types[(block_num - 1) * 256 + i] = true end + end end -- Decodes NSEC3 records, puts result in entry.NSEC3. See RFC 5155. -- @@ -1099,37 +1099,37 @@ end -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. decoder[types.NSEC3] = function (entry, data, pos) - local np = pos - #entry.data - local _ - local flags + local np = pos - #entry.data + local _ + local flags - entry.NSEC3 = {} - entry.NSEC3.dname = entry.dname - entry.NSEC3.salt, entry.NSEC3.hash = {}, {} + entry.NSEC3 = {} + entry.NSEC3.dname = entry.dname + entry.NSEC3.salt, entry.NSEC3.hash = {}, {} - np, entry.NSEC3.hash.alg,flags,entry.NSEC3.iterations = bin.unpack(">CBS", data, np) - -- do we even need to decode these do we care about opt out? - -- entry.NSEC3.flags = decodeFlagsNSEC3(flags) + np, entry.NSEC3.hash.alg,flags,entry.NSEC3.iterations = bin.unpack(">CBS", data, np) + -- do we even need to decode these do we care about opt out? + -- entry.NSEC3.flags = decodeFlagsNSEC3(flags) - np, entry.NSEC3.salt.bin = bin.unpack(">p", data, np) - _, entry.NSEC3.salt.hex = bin.unpack("H" .. #entry.NSEC3.salt.bin, entry.NSEC3.salt.bin) + np, entry.NSEC3.salt.bin = bin.unpack(">p", data, np) + _, entry.NSEC3.salt.hex = bin.unpack("H" .. #entry.NSEC3.salt.bin, entry.NSEC3.salt.bin) - np, entry.NSEC3.hash.bin = bin.unpack(">p" , data, np) - _, entry.NSEC3.hash.hex = bin.unpack(">H" .. #entry.NSEC3.hash.bin , entry.NSEC3.hash.bin) - entry.NSEC3.hash.base32 = base32.enc(entry.NSEC3.hash.bin, true) + np, entry.NSEC3.hash.bin = bin.unpack(">p" , data, np) + _, entry.NSEC3.hash.hex = bin.unpack(">H" .. #entry.NSEC3.hash.bin , entry.NSEC3.hash.bin) + entry.NSEC3.hash.base32 = base32.enc(entry.NSEC3.hash.bin, true) - np, entry.NSEC3.WinBlockNo, entry.NSEC3.bmplength = bin.unpack(">CC", data, np) - np, entry.NSEC3.bin = bin.unpack(">B".. entry.NSEC3.bmplength, data, np) - entry.NSEC3.types = {} - if entry.NSEC3.bin == nil then - entry.NSEC3.bin = "" - end - for i=1, string.len(entry.NSEC3.bin) do - local bit = string.sub(entry.NSEC3.bin,i,i) - if bit == "1" then - table.insert(entry.NSEC3.types, (entry.NSEC3.WinBlockNo*256+i-1)) - end - end + np, entry.NSEC3.WinBlockNo, entry.NSEC3.bmplength = bin.unpack(">CC", data, np) + np, entry.NSEC3.bin = bin.unpack(">B".. entry.NSEC3.bmplength, data, np) + entry.NSEC3.types = {} + if entry.NSEC3.bin == nil then + entry.NSEC3.bin = "" + end + for i=1, string.len(entry.NSEC3.bin) do + local bit = string.sub(entry.NSEC3.bin,i,i) + if bit == "1" then + table.insert(entry.NSEC3.types, (entry.NSEC3.WinBlockNo*256+i-1)) + end + end end -- Decodes records that consist only of one domain, for example CNAME, NS, PTR. @@ -1138,10 +1138,10 @@ end -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. local function decDomain(entry, data, pos) - local np = pos - #entry.data - local _ - _, entry.domain = decStr(data, np) - end + local np = pos - #entry.data + local _ + _, entry.domain = decStr(data, np) +end -- Decodes CNAME records. -- Puts result in entry.domain. @@ -1178,27 +1178,27 @@ decoder[types.PTR] = decDomain -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. decoder[types.TXT] = - function (entry, data, pos) +function (entry, data, pos) - local len = entry.data:len() - local np = pos - #entry.data - local txt_len - local txt - - if len > 0 then - entry.TXT = {} - entry.TXT.text = {} - end - - while len > 0 do - np, txt_len = bin.unpack("C", data, np) - np, txt = bin.unpack("A" .. txt_len, data, np ) - len = len - txt_len - 1 - table.insert( entry.TXT.text, txt ) - end + local len = entry.data:len() + local np = pos - #entry.data + local txt_len + local txt + if len > 0 then + entry.TXT = {} + entry.TXT.text = {} end + while len > 0 do + np, txt_len = bin.unpack("C", data, np) + np, txt = bin.unpack("A" .. txt_len, data, np ) + len = len - txt_len - 1 + table.insert( entry.TXT.text, txt ) + end + +end + --- -- Decodes OPT record, puts it in entry.OPT. -- @@ -1209,13 +1209,13 @@ decoder[types.TXT] = -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. decoder[types.OPT] = - function(entry, data, pos) - local np = pos - #entry.data - 6 - local opt = { bufsize = entry.class } - np, opt.rcode, opt.version, opt.zflags, opt.rdlen = bin.unpack(">CCSS", data, np) - np, opt.data = bin.unpack("A" .. opt.rdlen, data, np) - entry.OPT = opt - end +function(entry, data, pos) + local np = pos - #entry.data - 6 + local opt = { bufsize = entry.class } + np, opt.rcode, opt.version, opt.zflags, opt.rdlen = bin.unpack(">CCSS", data, np) + np, opt.data = bin.unpack("A" .. opt.rdlen, data, np) + entry.OPT = opt +end -- Decodes MX record, puts it in entry.MX. @@ -1226,13 +1226,13 @@ decoder[types.OPT] = -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. decoder[types.MX] = - function(entry, data, pos) - local np = pos - #entry.data + 2 - local _ - entry.MX = {} - _, entry.MX.pref = bin.unpack(">S", entry.data) - _, entry.MX.server = decStr(data, np) - end +function(entry, data, pos) + local np = pos - #entry.data + 2 + local _ + entry.MX = {} + _, entry.MX.pref = bin.unpack(">S", entry.data) + _, entry.MX.server = decStr(data, np) +end -- Decodes SRV record, puts it in entry.SRV. -- @@ -1243,13 +1243,13 @@ decoder[types.MX] = -- @param data Complete encoded DNS packet. -- @param pos Position in packet after RR. decoder[types.SRV] = - function(entry, data, pos) - local np = pos - #entry.data - local _ - entry.SRV = {} - np, entry.SRV.prio, entry.SRV.weight, entry.SRV.port = bin.unpack(">S>S>S", data, np) - np, entry.SRV.target = decStr(data, np) - end +function(entry, data, pos) + local np = pos - #entry.data + local _ + entry.SRV = {} + np, entry.SRV.prio, entry.SRV.weight, entry.SRV.port = bin.unpack(">S>S>S", data, np) + np, entry.SRV.target = decStr(data, np) +end -- Decodes returned resource records (answer, authority, or additional part). -- @param data Complete encoded DNS packet. @@ -1257,25 +1257,25 @@ decoder[types.SRV] = -- @param pos Starting position in packet. -- @return Table of RRs. local function decodeRR(data, count, pos) - local ans = {} - for i = 1, count do - local currRR = {} - pos, currRR.dname = decStr(data, pos) - pos, currRR.dtype, currRR.class, currRR.ttl = bin.unpack(">SSI", data, pos) + local ans = {} + for i = 1, count do + local currRR = {} + pos, currRR.dname = decStr(data, pos) + pos, currRR.dtype, currRR.class, currRR.ttl = bin.unpack(">SSI", data, pos) - local reslen - pos, reslen = bin.unpack(">S", data, pos) + local reslen + pos, reslen = bin.unpack(">S", data, pos) - pos, currRR.data = bin.unpack("A" .. reslen, data, pos) + pos, currRR.data = bin.unpack("A" .. reslen, data, pos) - -- try to be smart: decode per type - if decoder[currRR.dtype] then - decoder[currRR.dtype](currRR, data, pos) - end - - table.insert(ans, currRR) + -- try to be smart: decode per type + if decoder[currRR.dtype] then + decoder[currRR.dtype](currRR, data, pos) end - return pos, ans + + table.insert(ans, currRR) + end + return pos, ans end --- @@ -1283,11 +1283,11 @@ end -- @param str String to be split up. -- @return Table of characters. local function str2tbl(str) - local tbl = {} - for i = 1, #str do - table.insert(tbl, string.sub(str, i, i)) - end - return tbl + local tbl = {} + for i = 1, #str do + table.insert(tbl, string.sub(str, i, i)) + end + return tbl end --- @@ -1295,22 +1295,22 @@ end -- @param flgStr Flags as a binary digit string. -- @return Table representing flags. local function decodeFlags(flgStr) - local flags = {} - local flgTbl = str2tbl(flgStr) - if flgTbl[1] == '1' then flags.QR = true end - if flgTbl[2] == '1' then flags.OC1 = true end - if flgTbl[3] == '1' then flags.OC2 = true end - if flgTbl[4] == '1' then flags.OC3 = true end - if flgTbl[5] == '1' then flags.OC4 = true end - if flgTbl[6] == '1' then flags.AA = true end - if flgTbl[7] == '1' then flags.TC = true end - if flgTbl[8] == '1' then flags.RD = true end - if flgTbl[9] == '1' then flags.RA = true end - if flgTbl[13] == '1' then flags.RC1 = true end - if flgTbl[14] == '1' then flags.RC2 = true end - if flgTbl[15] == '1' then flags.RC3 = true end - if flgTbl[16] == '1' then flags.RC4 = true end - return flags + local flags = {} + local flgTbl = str2tbl(flgStr) + if flgTbl[1] == '1' then flags.QR = true end + if flgTbl[2] == '1' then flags.OC1 = true end + if flgTbl[3] == '1' then flags.OC2 = true end + if flgTbl[4] == '1' then flags.OC3 = true end + if flgTbl[5] == '1' then flags.OC4 = true end + if flgTbl[6] == '1' then flags.AA = true end + if flgTbl[7] == '1' then flags.TC = true end + if flgTbl[8] == '1' then flags.RD = true end + if flgTbl[9] == '1' then flags.RA = true end + if flgTbl[13] == '1' then flags.RC1 = true end + if flgTbl[14] == '1' then flags.RC2 = true end + if flgTbl[15] == '1' then flags.RC3 = true end + if flgTbl[16] == '1' then flags.RC4 = true end + return flags end --- @@ -1318,29 +1318,29 @@ end -- @param data Encoded DNS packet. -- @return Table representing DNS packet. function decode(data) - local pos - local pkt = {} - local encFlags - local cnt = {} - pos, pkt.id, encFlags, cnt.q, cnt.a, cnt.auth, cnt.add = bin.unpack(">SB2S4", data) - -- for now, don't decode the flags - pkt.flags = decodeFlags(encFlags) + local pos + local pkt = {} + local encFlags + local cnt = {} + pos, pkt.id, encFlags, cnt.q, cnt.a, cnt.auth, cnt.add = bin.unpack(">SB2S4", data) + -- for now, don't decode the flags + pkt.flags = decodeFlags(encFlags) - -- - -- check whether this is an update response or not - -- a quick fix to allow decoding of non updates and not break for updates - -- the flags are enough for the current code to determine whether an update was successful or not - -- - local strflags=encodeFlags(pkt.flags) - if ( strflags:sub(1,4) == "1010" ) then - return pkt - else - pos, pkt.questions = decodeQuestions(data, cnt.q, pos) - pos, pkt.answers = decodeRR(data, cnt.a, pos) - pos, pkt.auth = decodeRR(data, cnt.auth, pos) - pos, pkt.add = decodeRR(data, cnt.add, pos) - end + -- + -- check whether this is an update response or not + -- a quick fix to allow decoding of non updates and not break for updates + -- the flags are enough for the current code to determine whether an update was successful or not + -- + local strflags=encodeFlags(pkt.flags) + if ( strflags:sub(1,4) == "1010" ) then return pkt + else + pos, pkt.questions = decodeQuestions(data, cnt.q, pos) + pos, pkt.answers = decodeRR(data, cnt.a, pos) + pos, pkt.auth = decodeRR(data, cnt.auth, pos) + pos, pkt.add = decodeRR(data, cnt.add, pos) + end + return pkt end @@ -1348,17 +1348,17 @@ end -- Creates a new table representing a DNS packet. -- @return Table representing a DNS packet. function newPacket() - local pkt = {} - pkt.id = 1 - pkt.flags = {} - pkt.flags.RD = true - pkt.questions = {} - pkt.zones = {} - pkt.updates = {} - pkt.answers = {} - pkt.auth = {} - pkt.additional = {} - return pkt + local pkt = {} + pkt.id = 1 + pkt.flags = {} + pkt.flags.RD = true + pkt.questions = {} + pkt.zones = {} + pkt.updates = {} + pkt.answers = {} + pkt.auth = {} + pkt.additional = {} + return pkt end @@ -1368,15 +1368,15 @@ end -- @param dname Domain name to be asked. -- @param dtype RR to be asked. function addQuestion(pkt, dname, dtype, class) - if type(pkt) ~= "table" then return nil end - if type(pkt.questions) ~= "table" then return nil end - local class = class or CLASS.IN - local q = {} - q.dname = dname - q.dtype = dtype - q.class = class - table.insert(pkt.questions, q) - return pkt + if type(pkt) ~= "table" then return nil end + if type(pkt.questions) ~= "table" then return nil end + local class = class or CLASS.IN + local q = {} + q.dname = dname + q.dtype = dtype + q.class = class + table.insert(pkt.questions, q) + return pkt end @@ -1390,9 +1390,9 @@ end -- @param pkt Table representing DNS packet. -- @param dname Domain name to be asked. function addZone(pkt, dname) - if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end - table.insert(pkt.zones, { dname=dname, dtype=types.SOA, class=CLASS.IN }) - return pkt + if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end + table.insert(pkt.zones, { dname=dname, dtype=types.SOA, class=CLASS.IN }) + return pkt end --- @@ -1400,15 +1400,15 @@ end -- @param flags Flag table, each entry representing a flag (only DO flag implmented). -- @return Binary digit string representing flags. local function encodeOPT_Z(flags) - if type(flags) == "string" then return flags end - if type(flags) ~= "table" then return nil end - local bits = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - for n, k in pairs({[1] = "DO"}) do - if flags[k] then - bits[n] = 1 - end + if type(flags) == "string" then return flags end + if type(flags) ~= "table" then return nil end + local bits = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + for n, k in pairs({[1] = "DO"}) do + if flags[k] then + bits[n] = 1 end - return table.concat(bits) + end + return table.concat(bits) end --- @@ -1421,12 +1421,12 @@ end -- mask - byte containing the length of the subnet mask -- address - string containing the IP address function addClientSubnet(pkt,Z,subnet) - local udp_payload_size = 4096 - local code = 20730 -- temporary option-code http://comments.gmane.org/gmane.ietf.dnsext/19776 - local scope_mask = 0 -- In requests, it MUST be set to 0 see draft - local data = bin.pack(">SCCA",subnet.family or 1,subnet.mask,scope_mask,ipOps.ip_to_str(subnet.address)) - local opt = bin.pack(">SS",code, #data) .. data - addOPT(pkt,Z,opt) + local udp_payload_size = 4096 + local code = 20730 -- temporary option-code http://comments.gmane.org/gmane.ietf.dnsext/19776 + local scope_mask = 0 -- In requests, it MUST be set to 0 see draft + local data = bin.pack(">SCCA",subnet.family or 1,subnet.mask,scope_mask,ipOps.ip_to_str(subnet.address)) + local opt = bin.pack(">SS",code, #data) .. data + addOPT(pkt,Z,opt) end --- @@ -1434,9 +1434,9 @@ end -- @param pkt Table representing DNS packet. -- @param Z Table of Z flags. Only DO is supported. function addNSID (pkt,Z) - local udp_payload_size = 4096 - local opt = bin.pack(">SS",3, 0) -- nsid data - addOPT(pkt,Z,opt) + local udp_payload_size = 4096 + local opt = bin.pack(">SS",3, 0) -- nsid data + addOPT(pkt,Z,opt) end --- @@ -1445,19 +1445,19 @@ end -- @param pkt Table representing DNS packet. -- @param Z Table of Z flags. Only DO is supported. function addOPT(pkt, Z, opt) - local rdata = opt or "" - if type(pkt) ~= "table" then return nil end - if type(pkt.additional) ~= "table" then return nil end - local _, Z_int = bin.unpack(">S", bin.pack("B", encodeOPT_Z(Z))) - local opt = { - type = types.OPT, - class = 4096, -- Actually the sender UDP payload size. - ttl = 0 * (0x01000000) + 0 * (0x00010000) + Z_int, - rdlen = #rdata, - rdata = rdata, - } - table.insert(pkt.additional, opt) - return pkt + local rdata = opt or "" + if type(pkt) ~= "table" then return nil end + if type(pkt.additional) ~= "table" then return nil end + local _, Z_int = bin.unpack(">S", bin.pack("B", encodeOPT_Z(Z))) + local opt = { + type = types.OPT, + class = 4096, -- Actually the sender UDP payload size. + ttl = 0 * (0x01000000) + 0 * (0x00010000) + Z_int, + rdlen = #rdata, + rdata = rdata, + } + table.insert(pkt.additional, opt) + return pkt end --- @@ -1468,9 +1468,9 @@ end -- @param ttl the time-to-live of the record -- @param data type specific data function addUpdate(pkt, dname, dtype, ttl, data, class) - if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end - table.insert(pkt.updates, { dname=dname, dtype=dtype, class=class, ttl=ttl, data=(data or "") } ) - return pkt + if ( type(pkt) ~= "table" ) or (type(pkt.updates) ~= "table") then return nil end + table.insert(pkt.updates, { dname=dname, dtype=dtype, class=class, ttl=ttl, data=(data or "") } ) + return pkt end @@ -1506,75 +1506,75 @@ end -- * update( "_ldap._tcp.cqure.net", { host=host, port=port, dtype="SRV", data="", ttl=0 } ) -- function update(dname, options) - local options = options or {} - local pkt = newPacket() - local flags = pkt.flags - local host, port = options.host, options.port - local timeout = ( type(options.timeout) == "number" ) and options.timeout or get_default_timeout() - local sendcount = options.sendCount or 2 - local dtype = ( type(options.dtype) == "string" ) and types[options.dtype] or types.A - local updata = options.data - local ttl = options.ttl or 86400 - local zone = options.zone or dname:match("^.-%.(.+)$") - local class = CLASS.IN + local options = options or {} + local pkt = newPacket() + local flags = pkt.flags + local host, port = options.host, options.port + local timeout = ( type(options.timeout) == "number" ) and options.timeout or get_default_timeout() + local sendcount = options.sendCount or 2 + local dtype = ( type(options.dtype) == "string" ) and types[options.dtype] or types.A + local updata = options.data + local ttl = options.ttl or 86400 + local zone = options.zone or dname:match("^.-%.(.+)$") + local class = CLASS.IN - assert(host, "dns.update needs a valid host in options") - assert(port, "dns.update needs a valid port in options") + assert(host, "dns.update needs a valid host in options") + assert(port, "dns.update needs a valid port in options") - if ( options.zone ) then dname = dname .. "." .. options.zone end + if ( options.zone ) then dname = dname .. "." .. options.zone end - if ( not(zone) and not( dname:match("^.-%..+") ) ) then - return false, "hostname needs to be supplied as FQDN" + if ( not(zone) and not( dname:match("^.-%..+") ) ) then + return false, "hostname needs to be supplied as FQDN" + end + + flags.RD = false + flags.OC1, flags.OC2, flags.OC3, flags.OC4 = false, true, false, true + + -- If ttl is zero and updata is string and zero length or nil, assume delete record + if ( ttl == 0 and ( ( type(updata) == "string" and #updata == 0 ) or not(updata) ) ) then + class = CLASS.ANY + updata = "" + if ( types.MX == dtype and not(options.zone) ) then zone=dname end + if ( types.SRV == dtype and not(options.zone) ) then + zone=dname:match("^_.-%._.-%.(.+)$") end - - flags.RD = false - flags.OC1, flags.OC2, flags.OC3, flags.OC4 = false, true, false, true - - -- If ttl is zero and updata is string and zero length or nil, assume delete record - if ( ttl == 0 and ( ( type(updata) == "string" and #updata == 0 ) or not(updata) ) ) then - class = CLASS.ANY - updata = "" - if ( types.MX == dtype and not(options.zone) ) then zone=dname end - if ( types.SRV == dtype and not(options.zone) ) then - zone=dname:match("^_.-%._.-%.(.+)$") - end -- if not, let's try to update the zone + else + if ( dtype == types.A ) then + updata = updata and bin.pack(">I", ipOps.todword(updata)) or "" + elseif( dtype == types.CNAME ) then + updata = encodeFQDN(updata) + elseif( dtype == types.MX ) then + assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table") + if ( not(options.zone) ) then zone = dname end + local data = bin.pack(">S", updata.pref) + data = data .. encodeFQDN(updata.mx) + updata = data + elseif ( dtype == types.SRV ) then + assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table") + local data = bin.pack(">SSS", updata.prio, updata.weight, updata.port ) + data = data .. encodeFQDN(updata.target) + updata = data + zone = options.zone or dname:match("^_.-%._.-%.(.+)$") else - if ( dtype == types.A ) then - updata = updata and bin.pack(">I", ipOps.todword(updata)) or "" - elseif( dtype == types.CNAME ) then - updata = encodeFQDN(updata) - elseif( dtype == types.MX ) then - assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table") - if ( not(options.zone) ) then zone = dname end - local data = bin.pack(">S", updata.pref) - data = data .. encodeFQDN(updata.mx) - updata = data - elseif ( dtype == types.SRV ) then - assert( not( type(updata) ~= "table" ), "dns.update expected options.data to be a table") - local data = bin.pack(">SSS", updata.prio, updata.weight, updata.port ) - data = data .. encodeFQDN(updata.target) - updata = data - zone = options.zone or dname:match("^_.-%._.-%.(.+)$") - else - return false, "Unsupported record type" - end + return false, "Unsupported record type" end + end - pkt = addZone(pkt, zone) - pkt = addUpdate(pkt, dname, dtype, ttl, updata, class) + pkt = addZone(pkt, zone) + pkt = addUpdate(pkt, dname, dtype, ttl, updata, class) - local data = encode(pkt) - local status, response = sendPackets(data, host, port, timeout, sendcount, false) + local data = encode(pkt) + local status, response = sendPackets(data, host, port, timeout, sendcount, false) - if ( status ) then - local decoded = decode(response[1].data) - local flags=encodeFlags(decoded.flags) - if (flags:sub(-4) == "0000") then - return true - end + if ( status ) then + local decoded = decode(response[1].data) + local flags=encodeFlags(decoded.flags) + if (flags:sub(-4) == "0000") then + return true end - return false + end + return false end return _ENV; diff --git a/nselib/dnsbl.lua b/nselib/dnsbl.lua index a6d8ab066..547484d11 100644 --- a/nselib/dnsbl.lua +++ b/nselib/dnsbl.lua @@ -72,492 +72,492 @@ _ENV = stdnse.module("dnsbl", stdnse.seeall) -- SERVICES = { - SPAM = { + SPAM = { - ["dnsbl.inps.de"] = { - -- This service supports both long and short mode - ns_type = { - ["short"] = "A", - ["long"] = "TXT", - }, - -- Creates a new Service instance - -- @param ip host that needs to be checked - -- @param mode string (short|long) specifying whether short or long - -- results are to be returned - -- @param config service configuration in case this service provider - -- needs user supplied configuration - -- @return o instance of Helper - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - -- Sample fmt_query function, if no function is specified, the library - -- will assume that the IP should be reversed add suffixed with the - -- service name. - fmt_query = function(self) - local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") - return ("%s.spam.dnsbl.sorbs.net"):format(rev_ip) - end, - -- This function parses the response and supports borth long and - -- short mode. - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "SPAM", - } - if ( ("short" == self.mode and r[1]) ) then - return responses[r[1]] - else - return { state = "SPAM", details = { r[1] } } - end - end, - }, + ["dnsbl.inps.de"] = { + -- This service supports both long and short mode + ns_type = { + ["short"] = "A", + ["long"] = "TXT", + }, + -- Creates a new Service instance + -- @param ip host that needs to be checked + -- @param mode string (short|long) specifying whether short or long + -- results are to be returned + -- @param config service configuration in case this service provider + -- needs user supplied configuration + -- @return o instance of Helper + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + -- Sample fmt_query function, if no function is specified, the library + -- will assume that the IP should be reversed add suffixed with the + -- service name. + fmt_query = function(self) + local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") + return ("%s.spam.dnsbl.sorbs.net"):format(rev_ip) + end, + -- This function parses the response and supports borth long and + -- short mode. + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "SPAM", + } + if ( ("short" == self.mode and r[1]) ) then + return responses[r[1]] + else + return { state = "SPAM", details = { r[1] } } + end + end, + }, - ["spam.dnsbl.sorbs.net"] = { - ns_type = { - ["short"] = "A" - }, - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - return ( r[1] == "127.0.0.6" and { state = "SPAM" } ) - end, - }, + ["spam.dnsbl.sorbs.net"] = { + ns_type = { + ["short"] = "A" + }, + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + return ( r[1] == "127.0.0.6" and { state = "SPAM" } ) + end, + }, - ["bl.nszones.com"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "SPAM", - ["127.0.0.3"] = "DYNAMIC" - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["bl.nszones.com"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "SPAM", + ["127.0.0.3"] = "DYNAMIC" + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["all.spamrats.com"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.36"] = "DYNAMIC", - ["127.0.0.38"] = "SPAM", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["all.spamrats.com"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.36"] = "DYNAMIC", + ["127.0.0.38"] = "SPAM", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["list.quorum.to"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - -- this service appears to return 127.0.0.0 when the service is - -- "blocked because it has never been seen to send mail". - -- This would essentially return every host as SPAM and we - -- don't want that. - return ( ( r[1] and r[1] ~= "127.0.0.0" ) and { state = "SPAM" } ) - end - }, + ["list.quorum.to"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + -- this service appears to return 127.0.0.0 when the service is + -- "blocked because it has never been seen to send mail". + -- This would essentially return every host as SPAM and we + -- don't want that. + return ( ( r[1] and r[1] ~= "127.0.0.0" ) and { state = "SPAM" } ) + end + }, - ["sbl.spamhaus.org"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "SPAM", - ["127.0.0.3"] = "SPAM", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["sbl.spamhaus.org"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "SPAM", + ["127.0.0.3"] = "SPAM", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["bl.spamcop.net"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "SPAM", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["bl.spamcop.net"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "SPAM", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["dnsbl.ahbl.org"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.4"] = "SPAM", - ["127.0.0.5"] = "SPAM", - ["127.0.0.6"] = "SPAM", - ["127.0.0.7"] = "SPAM", - ["127.0.0.8"] = "SPAM", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["dnsbl.ahbl.org"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.4"] = "SPAM", + ["127.0.0.5"] = "SPAM", + ["127.0.0.6"] = "SPAM", + ["127.0.0.7"] = "SPAM", + ["127.0.0.8"] = "SPAM", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["l2.apews.org"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "SPAM", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["l2.apews.org"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "SPAM", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - }, + }, - PROXY = { + PROXY = { - ["dnsbl.tornevall.org"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - if ( "short" == self.mode and r[1] ) then - return { state = "PROXY" } - elseif ( "long" == self.mode ) then - local responses = { - [1] = "Proxy has been scanned", - [2] = "Proxy is working", - [4] = "?", - [8] = "Proxy was tested, but timed out on connection", - [16] = "Proxy was tested but failed at connection", - [32] = "Proxy was tested but the IP was different", - [64] = "IP marked as \"abusive host\"", - [128] = "Proxy has a different anonymous-state" - } + ["dnsbl.tornevall.org"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + if ( "short" == self.mode and r[1] ) then + return { state = "PROXY" } + elseif ( "long" == self.mode ) then + local responses = { + [1] = "Proxy has been scanned", + [2] = "Proxy is working", + [4] = "?", + [8] = "Proxy was tested, but timed out on connection", + [16] = "Proxy was tested but failed at connection", + [32] = "Proxy was tested but the IP was different", + [64] = "IP marked as \"abusive host\"", + [128] = "Proxy has a different anonymous-state" + } - local code = tonumber(r[1]:match("%.(%d*)$")) - local result = {} + local code = tonumber(r[1]:match("%.(%d*)$")) + local result = {} - for k, v in pairs(responses) do - if ( bit.band( code, k ) == k ) then - table.insert(result, v) - end - end - return { state = "PROXY", details = result } - end - end, - }, + for k, v in pairs(responses) do + if ( bit.band( code, k ) == k ) then + table.insert(result, v) + end + end + return { state = "PROXY", details = result } + end + end, + }, - ["ip-port.exitlist.torproject.org"] = { - configuration = { - ["port"] = "the port to which the target can relay to", - ["ip"] = "the IP address to which the target can relay to" - }, - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - fmt_query = function(self) - if ( not(self.config.port) or not(self.config.ip) ) then - return - end + ["ip-port.exitlist.torproject.org"] = { + configuration = { + ["port"] = "the port to which the target can relay to", + ["ip"] = "the IP address to which the target can relay to" + }, + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + fmt_query = function(self) + if ( not(self.config.port) or not(self.config.ip) ) then + return + end - local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") - return ("%s.%s.%s.ip-port.exitlist.torproject.org"):format(rev_ip, - self.config.port, self.config.ip) - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "PROXY", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") + return ("%s.%s.%s.ip-port.exitlist.torproject.org"):format(rev_ip, + self.config.port, self.config.ip) + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "PROXY", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["tor.dan.me.uk"] = { - ns_type = { - ["short"] = "A", - ["long"] = "TXT", - }, - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.100"] = "PROXY", - } - if ( "short" == self.mode and r[1] ) then - return { state = responses[r[1]] } - else - local flagsinfo = { - ["E"] = "Exit", - ["A"] = "Authority", - ["B"] = "BadExit", - ["D"] = "V2Dir", - ["F"] = "Fast", - ["G"] = "Guard", - ["H"] = "HSDir", - ["N"] = "Named", - ["R"] = "Running", - ["S"] = "Stable", - ["U"] = "Unnamed", - ["V"] = "Valid" - } + ["tor.dan.me.uk"] = { + ns_type = { + ["short"] = "A", + ["long"] = "TXT", + }, + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.100"] = "PROXY", + } + if ( "short" == self.mode and r[1] ) then + return { state = responses[r[1]] } + else + local flagsinfo = { + ["E"] = "Exit", + ["A"] = "Authority", + ["B"] = "BadExit", + ["D"] = "V2Dir", + ["F"] = "Fast", + ["G"] = "Guard", + ["H"] = "HSDir", + ["N"] = "Named", + ["R"] = "Running", + ["S"] = "Stable", + ["U"] = "Unnamed", + ["V"] = "Valid" + } - local name, ports, flagsfound = r[1]:match( - "N:(.+)/P:([%d,]+)/F:([EABDFGHNRSUV]+)") + local name, ports, flagsfound = r[1]:match( + "N:(.+)/P:([%d,]+)/F:([EABDFGHNRSUV]+)") - local flags = {} - flags['name'] = "Flags" + local flags = {} + flags['name'] = "Flags" - for k, v in pairs(flagsinfo) do - if flagsfound:match(k) then - table.insert(flags, v) - end - end + for k, v in pairs(flagsinfo) do + if flagsfound:match(k) then + table.insert(flags, v) + end + end - local result = { - ("Name: %s"):format(name), - ("Ports: %s"):format(ports), - flags - } + local result = { + ("Name: %s"):format(name), + ("Ports: %s"):format(ports), + flags + } - return { state = "PROXY", details = result } - end - end, - }, + return { state = "PROXY", details = result } + end + end, + }, - ["dnsbl.ahbl.org"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.3"] = "PROXY", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["dnsbl.ahbl.org"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.3"] = "PROXY", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["http.dnsbl.sorbs.net"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "PROXY", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["http.dnsbl.sorbs.net"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "PROXY", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["socks.dnsbl.sorbs.net"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.3"] = "PROXY", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - }, + ["socks.dnsbl.sorbs.net"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.3"] = "PROXY", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + }, - ["misc.dnsbl.sorbs.net"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.4"] = "PROXY", - } - return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } - end, - } + ["misc.dnsbl.sorbs.net"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.4"] = "PROXY", + } + return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] } + end, + } - }, + }, - ATTACK = { - ["dnsbl.httpbl.org"] = { - configuration = { - ["apikey"] = "the http:BL API key" - }, - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - fmt_query = function(self) - if ( not(self.config.apikey) ) then - return - end + ATTACK = { + ["dnsbl.httpbl.org"] = { + configuration = { + ["apikey"] = "the http:BL API key" + }, + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + fmt_query = function(self) + if ( not(self.config.apikey) ) then + return + end - local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") - return ("%s.%s.dnsbl.httpbl.org"):format(self.config.apikey, rev_ip) - end, - resp_parser = function(self, r) - if ( not(r[1]) ) then - return - end + local rev_ip = dns.reverse(self.ip):match("^(.*)%.in%-addr%.arpa$") + return ("%s.%s.dnsbl.httpbl.org"):format(self.config.apikey, rev_ip) + end, + resp_parser = function(self, r) + if ( not(r[1]) ) then + return + end - local parts, err = ipOps.get_parts_as_number(r[1]) + local parts, err = ipOps.get_parts_as_number(r[1]) - if ( not(parts) or err ) then - -- TODO Should we return failure in the result? - stdnse.print_debug("The dnsbl.httpbl.org provider failed to return a valid address") - return - end + if ( not(parts) or err ) then + -- TODO Should we return failure in the result? + stdnse.print_debug("The dnsbl.httpbl.org provider failed to return a valid address") + return + end - local octet1, octet2, octet3, octet4 = table.unpack(parts) + local octet1, octet2, octet3, octet4 = table.unpack(parts) - if ( octet1 ~= 127 ) then - -- This should'nt happen :P - stdnse.print_debug(string.format( - "The request made to dnsbl.httpbl.org was considered invalid (%i)", octet1)) - elseif ( "short" == self.mode ) then - return { state = "ATTACK" } - else - local search = { - [0] = "Undocumented", - [1] = "AltaVista", - [2] = "Ask", - [3] = "Baidu", - [4] = "Excite", - [5] = "Google", - [6] = "Looksmart", - [7] = "Lycos", - [8] = "MSN", - [9] = "Yahoo", - [10] = "Cuil", - [11] = "InfoSeek", - [12] = "Miscellaneous" - } + if ( octet1 ~= 127 ) then + -- This should'nt happen :P + stdnse.print_debug(string.format( + "The request made to dnsbl.httpbl.org was considered invalid (%i)", octet1)) + elseif ( "short" == self.mode ) then + return { state = "ATTACK" } + else + local search = { + [0] = "Undocumented", + [1] = "AltaVista", + [2] = "Ask", + [3] = "Baidu", + [4] = "Excite", + [5] = "Google", + [6] = "Looksmart", + [7] = "Lycos", + [8] = "MSN", + [9] = "Yahoo", + [10] = "Cuil", + [11] = "InfoSeek", + [12] = "Miscellaneous" + } - local result = {} + local result = {} - -- Search engines are a special case. - if ( octet4 == 0 ) then - table.insert(result, ("Search engine: %s"):format( - search[octet3])) - else - table.insert(result, ("Last activity: %i days"):format( - octet2)) - table.insert(result, ("Threat score: %i"):format( - octet3)) + -- Search engines are a special case. + if ( octet4 == 0 ) then + table.insert(result, ("Search engine: %s"):format( + search[octet3])) + else + table.insert(result, ("Last activity: %i days"):format( + octet2)) + table.insert(result, ("Threat score: %i"):format( + octet3)) - local activity = {} - activity['name'] = "Activity" - -- Suspicious activity - if ( bit.band(octet4, 1) == 1) then - table.insert(activity, "Suspicious") - end + local activity = {} + activity['name'] = "Activity" + -- Suspicious activity + if ( bit.band(octet4, 1) == 1) then + table.insert(activity, "Suspicious") + end - -- Harvester - if ( bit.band(octet4, 2) == 2) then - table.insert(activity, "Harvester") - end + -- Harvester + if ( bit.band(octet4, 2) == 2) then + table.insert(activity, "Harvester") + end - -- Comment spammer - if ( bit.band(octet4, 4) == 4) then - table.insert(activity, "Comment spammer") - end + -- Comment spammer + if ( bit.band(octet4, 4) == 4) then + table.insert(activity, "Comment spammer") + end - table.insert(result, activity) - end + table.insert(result, activity) + end - return { state = "ATTACK", details = result } - end - end, - }, + return { state = "ATTACK", details = result } + end + end, + }, - ["all.bl.blocklist.de"] = { - new = function(self, ip, mode, config) - local o = { ip = ip, mode = mode, config = config } - setmetatable(o, self) - self.__index = self - return o - end, - resp_parser = function(self, r) - local responses = { - ["127.0.0.2"] = "Amavis", - ["127.0.0.3"] = "DDoS", - ["127.0.0.4"] = "Asterisk, SIP, VoIP", - ["127.0.0.5"] = "Badbot", - ["127.0.0.6"] = "FTP", - ["127.0.0.7"] = "IMAP", - ["127.0.0.8"] = "IRC bot", - ["127.0.0.9"] = "Mail", - ["127.0.0.10"] = "POP3", - ["127.0.0.11"] = "Registration bot", - ["127.0.0.12"] = "Remote file inclusion", - ["127.0.0.13"] = "SASL", - ["127.0.0.14"] = "SSH", - ["127.0.0.15"] = "w00tw00t", - ["127.0.0.16"] = "Port flood", - } - if ( "short" == self.mode and r[1] ) then - return "ATTACK" - else - return ( r[1] and responses[r[1]] ) and { state = "ATTACK", - details = { - ("Type: %s"):format(responses[r[1]]) - } - } - end - end, - } - }, + ["all.bl.blocklist.de"] = { + new = function(self, ip, mode, config) + local o = { ip = ip, mode = mode, config = config } + setmetatable(o, self) + self.__index = self + return o + end, + resp_parser = function(self, r) + local responses = { + ["127.0.0.2"] = "Amavis", + ["127.0.0.3"] = "DDoS", + ["127.0.0.4"] = "Asterisk, SIP, VoIP", + ["127.0.0.5"] = "Badbot", + ["127.0.0.6"] = "FTP", + ["127.0.0.7"] = "IMAP", + ["127.0.0.8"] = "IRC bot", + ["127.0.0.9"] = "Mail", + ["127.0.0.10"] = "POP3", + ["127.0.0.11"] = "Registration bot", + ["127.0.0.12"] = "Remote file inclusion", + ["127.0.0.13"] = "SASL", + ["127.0.0.14"] = "SSH", + ["127.0.0.15"] = "w00tw00t", + ["127.0.0.16"] = "Port flood", + } + if ( "short" == self.mode and r[1] ) then + return "ATTACK" + else + return ( r[1] and responses[r[1]] ) and { state = "ATTACK", + details = { + ("Type: %s"):format(responses[r[1]]) + } + } + end + end, + } +}, } @@ -565,166 +565,166 @@ SERVICES = { Helper = { - -- Creates a new Helper instance - -- @param category string containing a valid DNSBL service category - -- @param mode string (short|long) specifying whether short or long - -- results are to be returned - -- @return o instance of Helper - new = function(self, category, mode) - local o = { category = category:upper(), mode = mode } - assert(category and SERVICES[category:upper()], "Invalid category was supplied, aborting") - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new Helper instance + -- @param category string containing a valid DNSBL service category + -- @param mode string (short|long) specifying whether short or long + -- results are to be returned + -- @return o instance of Helper + new = function(self, category, mode) + local o = { category = category:upper(), mode = mode } + assert(category and SERVICES[category:upper()], "Invalid category was supplied, aborting") + setmetatable(o, self) + self.__index = self + return o + end, - -- Lists all DNSBL services for the category - -- @return services table of service names - listServices = function(self) - local services = {} - for name, svc in pairs(SERVICES[self.category]) do - if ( svc.configuration ) then - local service = {} - service['name'] = name + -- Lists all DNSBL services for the category + -- @return services table of service names + listServices = function(self) + local services = {} + for name, svc in pairs(SERVICES[self.category]) do + if ( svc.configuration ) then + local service = {} + service['name'] = name - for config, description in pairs(svc.configuration) do - table.insert(service, ("config: %s.%s - %s"):format( - name, config, description)) - end + for config, description in pairs(svc.configuration) do + table.insert(service, ("config: %s.%s - %s"):format( + name, config, description)) + end - table.insert(services, service ) - else - table.insert(services, name) - end - end - return services - end, + table.insert(services, service ) + else + table.insert(services, name) + end + end + return services + end, - -- Validates the filter set by setFilter to make sure it contains only - -- valid service names. - -- @return status boolean, true on success false on failure - -- @return err string containing an error message on failure - validateFilter = function(self) + -- Validates the filter set by setFilter to make sure it contains only + -- valid service names. + -- @return status boolean, true on success false on failure + -- @return err string containing an error message on failure + validateFilter = function(self) - if ( not(self.filterstr) ) then - return true - end + if ( not(self.filterstr) ) then + return true + end - local all = SERVICES[self.category] - self.filter = {} - for _, f in pairs(stdnse.strsplit(",%s*", self.filterstr)) do - if ( not(SERVICES[self.category][f]) ) then - self.filter = nil - return false, ("Service does not exist '%s'"):format(f) - end - self.filter[f] = true - end - return true - end, + local all = SERVICES[self.category] + self.filter = {} + for _, f in pairs(stdnse.strsplit(",%s*", self.filterstr)) do + if ( not(SERVICES[self.category][f]) ) then + self.filter = nil + return false, ("Service does not exist '%s'"):format(f) + end + self.filter[f] = true + end + return true + end, - -- Sets a new service filter to choose only a limited subset of services - -- within a category. - -- @param filter string containing a comma separated list of service names - setFilter = function(self, filter) self.filterstr = filter end, + -- Sets a new service filter to choose only a limited subset of services + -- within a category. + -- @param filter string containing a comma separated list of service names + setFilter = function(self, filter) self.filterstr = filter end, - -- Gets a list of filtered services, or all services if no filter is in use - -- @return services table containing a list of services - getServices = function(self) - if ( not(self:validateFilter()) ) then - return nil - end + -- Gets a list of filtered services, or all services if no filter is in use + -- @return services table containing a list of services + getServices = function(self) + if ( not(self:validateFilter()) ) then + return nil + end - if ( self.filter ) then - local filtered = {} - for name, svc in pairs(SERVICES[self.category]) do - if ( self.filter[name] ) then - filtered[name] = svc - end - end - return filtered - else - return SERVICES[self.category] - end - end, + if ( self.filter ) then + local filtered = {} + for name, svc in pairs(SERVICES[self.category]) do + if ( self.filter[name] ) then + filtered[name] = svc + end + end + return filtered + else + return SERVICES[self.category] + end + end, - doQuery = function(self, ip, name, svc, answers) + doQuery = function(self, ip, name, svc, answers) - local condvar = nmap.condvar(answers) - local config = {} + local condvar = nmap.condvar(answers) + local config = {} - if ( svc.configuration ) then - for key in pairs(svc.configuration) do - config[key] = stdnse.get_script_args(("%s.%s"):format(name, key)) - end - end + if ( svc.configuration ) then + for key in pairs(svc.configuration) do + config[key] = stdnse.get_script_args(("%s.%s"):format(name, key)) + end + end - svc = svc:new(ip, self.mode, config) + svc = svc:new(ip, self.mode, config) - local ns_type = ( svc.ns_type and svc.ns_type[self.mode] ) and svc.ns_type[self.mode] or "A" - local query + local ns_type = ( svc.ns_type and svc.ns_type[self.mode] ) and svc.ns_type[self.mode] or "A" + local query - if ( not(svc.fmt_query) ) then - local rev_ip = dns.reverse(ip):match("^(.*)%.in%-addr%.arpa$") - query = ("%s.%s"):format(rev_ip, name) - else - query = svc:fmt_query() - end + if ( not(svc.fmt_query) ) then + local rev_ip = dns.reverse(ip):match("^(.*)%.in%-addr%.arpa$") + query = ("%s.%s"):format(rev_ip, name) + else + query = svc:fmt_query() + end - if ( query ) then - local status, answer = dns.query(query, {dtype=ns_type, retAll=true} ) - answers[name] = { status = status, answer = answer, svc = svc } - else - stdnse.print_debug("Query function returned nothing, skipping '%s'", name) - end + if ( query ) then + local status, answer = dns.query(query, {dtype=ns_type, retAll=true} ) + answers[name] = { status = status, answer = answer, svc = svc } + else + stdnse.print_debug("Query function returned nothing, skipping '%s'", name) + end - condvar "signal" - end, + condvar "signal" + end, - -- Runs the DNS blacklist check for the given IP against all non-filtered - -- services in the given category. - -- @param ip string containing the IP address to check - -- @return result table containing the results of the BL checks - checkBL = function(self, ip) - local result, answers, threads = {}, {}, {} - local condvar = nmap.condvar(answers) + -- Runs the DNS blacklist check for the given IP against all non-filtered + -- services in the given category. + -- @param ip string containing the IP address to check + -- @return result table containing the results of the BL checks + checkBL = function(self, ip) + local result, answers, threads = {}, {}, {} + local condvar = nmap.condvar(answers) - for name, svc in pairs(self:getServices()) do - local co = stdnse.new_thread(self.doQuery, self, ip, name, svc, answers) - threads[co] = true - end + for name, svc in pairs(self:getServices()) do + local co = stdnse.new_thread(self.doQuery, self, ip, name, svc, answers) + threads[co] = true + end - repeat - for t in pairs(threads) do - if ( coroutine.status(t) == "dead" ) then threads[t] = nil end - end - if ( next(threads) ) then - condvar "wait" - end - until( next(threads) == nil ) + repeat + for t in pairs(threads) do + if ( coroutine.status(t) == "dead" ) then threads[t] = nil end + end + if ( next(threads) ) then + condvar "wait" + end + until( next(threads) == nil ) - for name, answer in pairs(answers) do - local status, answer, svc = answer.status, answer.answer, answer.svc - if ( status ) then - local svc_result = svc:resp_parser(answer) - if ( not(svc_result) ) then - local resp = ( #answer > 0 and ("UNKNOWN (%s)"):format(answer[1]) or "UNKNOWN" ) - stdnse.print_debug(2, ("%s received %s"):format(name, resp)) - end + for name, answer in pairs(answers) do + local status, answer, svc = answer.status, answer.answer, answer.svc + if ( status ) then + local svc_result = svc:resp_parser(answer) + if ( not(svc_result) ) then + local resp = ( #answer > 0 and ("UNKNOWN (%s)"):format(answer[1]) or "UNKNOWN" ) + stdnse.print_debug(2, ("%s received %s"):format(name, resp)) + end - if ( svc_result ) then - table.insert(result, { name = name, result = svc_result }) - end + if ( svc_result ) then + table.insert(result, { name = name, result = svc_result }) + end - -- if status is false, and the response was "No Such Name", it - -- simply means that the IP isn't listed, we haven't failed at - -- this point. It would obviously be better to check this against - -- an error code, or in some other way, but this is what we've got. - elseif ( answer ~= "No Such Name" ) then - table.insert(result, { name = name, result = { state = "FAIL" }}) - end - end - return result - end, + -- if status is false, and the response was "No Such Name", it + -- simply means that the IP isn't listed, we haven't failed at + -- this point. It would obviously be better to check this against + -- an error code, or in some other way, but this is what we've got. + elseif ( answer ~= "No Such Name" ) then + table.insert(result, { name = name, result = { state = "FAIL" }}) + end + end + return result + end, } diff --git a/nselib/drda.lua b/nselib/drda.lua index 538f51855..cfff34247 100644 --- a/nselib/drda.lua +++ b/nselib/drda.lua @@ -32,10 +32,10 @@ -- to interface with the library: -- -- --- db2helper = drda.Helper:new() --- status, err = db2helper:connect(host, port) --- status, res = db2helper:getServerInfo() --- status, err = db2helper:close() +-- db2helper = drda.Helper:new() +-- status, err = db2helper:connect(host, port) +-- status, res = db2helper:getServerInfo() +-- status, err = db2helper:close() -- -- -- The implementation is based on packet dumps and the excellent decoding @@ -52,11 +52,11 @@ -- Version 0.2 -- Created 05/08/2010 - v0.1 - created by Patrik Karlsson -- Revised 07/27/2010 - v0.2 - Added the comm class and made a few improvements --- to sending and receiving packets. Changed the --- helper login method to support: --- x IBM DB2 --- x Apache Derby --- x IBM Informix Dynamic Server +-- to sending and receiving packets. Changed the +-- helper login method to support: +-- x IBM DB2 +-- x Apache Derby +-- x IBM Informix Dynamic Server local bin = require "bin" local bit = require "bit" @@ -69,450 +69,450 @@ _ENV = stdnse.module("drda", stdnse.seeall) -- CodePoint constants CodePoint = { - CODEPNT = 0x000c, - TYPDEFNAM = 0x002f, - TYPDEFOVR = 0x0035, - ACCSEC = 0x106d, - SECCHK = 0x106e, - EXCSAT = 0x1041, - PRDID = 0x112e, - SRVCLSNM = 0x1147, - SVRCOD = 0x1149, - SYNERRCD = 0x114a, - SRVRLSLV = 0x115a, - EXTNAM = 0x115e, - SRVNAM = 0x116d, - USRID = 0x11a0, - PASSWORD = 0x11a1, - SECMEC = 0x11a2, - SECCHKCD = 0x11a4, - SECCHKRM = 0x1219, - SYNTAXRM = 0x124c, - MGRLVLLS = 0x1404, - EXCSATRD = 0x1443, - ACCSECRD = 0x14ac, - ACCRDB = 0x2001, - PRDDATA = 0x2104, - RDBACCL = 0x210f, - RDBNAM = 0x2110, - CRRTKN = 0x2135, - ACCRDBRM = 0x2201, - RDBNFNRM = 0x2211, - RDBAFLRM = 0x221a, - RDBATHRM = 0x22cb, + CODEPNT = 0x000c, + TYPDEFNAM = 0x002f, + TYPDEFOVR = 0x0035, + ACCSEC = 0x106d, + SECCHK = 0x106e, + EXCSAT = 0x1041, + PRDID = 0x112e, + SRVCLSNM = 0x1147, + SVRCOD = 0x1149, + SYNERRCD = 0x114a, + SRVRLSLV = 0x115a, + EXTNAM = 0x115e, + SRVNAM = 0x116d, + USRID = 0x11a0, + PASSWORD = 0x11a1, + SECMEC = 0x11a2, + SECCHKCD = 0x11a4, + SECCHKRM = 0x1219, + SYNTAXRM = 0x124c, + MGRLVLLS = 0x1404, + EXCSATRD = 0x1443, + ACCSECRD = 0x14ac, + ACCRDB = 0x2001, + PRDDATA = 0x2104, + RDBACCL = 0x210f, + RDBNAM = 0x2110, + CRRTKN = 0x2135, + ACCRDBRM = 0x2201, + RDBNFNRM = 0x2211, + RDBAFLRM = 0x221a, + RDBATHRM = 0x22cb, } -- Security Mechanism SecMec = { - USER_PASSWORD = 0x0003, - USER_ONLY = 0x0004, - CHANGE_PASSWORD = 0x0005, - USER_PASS_SUBST = 0x0006, - USER_ENC_PASS = 0x0007, - ENC_USER_ENC_PASS = 0x0009, - ENC_CHANGE_PASS = 0x000A, - KERBEROS = 0x000B, - ENC_USER_DATA = 0x000C, - ENC_USER_ENC_PASS_ENC_DATA = 0x000D, - ENC_USER_ENC_PASS_ENC_NEWPASS_ENC_DATA = 0x000E, + USER_PASSWORD = 0x0003, + USER_ONLY = 0x0004, + CHANGE_PASSWORD = 0x0005, + USER_PASS_SUBST = 0x0006, + USER_ENC_PASS = 0x0007, + ENC_USER_ENC_PASS = 0x0009, + ENC_CHANGE_PASS = 0x000A, + KERBEROS = 0x000B, + ENC_USER_DATA = 0x000C, + ENC_USER_ENC_PASS_ENC_DATA = 0x000D, + ENC_USER_ENC_PASS_ENC_NEWPASS_ENC_DATA = 0x000E, } DRDAPacket = { - new = function(self, drda_array) - local o = { - drda_array = drda_array, - count = #drda_array - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self, drda_array) + local o = { + drda_array = drda_array, + count = #drda_array + } + setmetatable(o, self) + self.__index = self + return o + end, - getDRDAByCodePoint = function( self, codepoint ) - for i=1, #self.drda_array do - if ( self.drda_array[i].DDM.CodePoint == codepoint ) then - return self.drda_array[i] - end - end - end, + getDRDAByCodePoint = function( self, codepoint ) + for i=1, #self.drda_array do + if ( self.drda_array[i].DDM.CodePoint == codepoint ) then + return self.drda_array[i] + end + end + end, - getDRDA = function( self, n ) - return ( #self.drda_array >= n ) and self.drda_array[n] or nil - end, + getDRDA = function( self, n ) + return ( #self.drda_array >= n ) and self.drda_array[n] or nil + end, - __tostring = function( self ) - local data = "" - -- do some DDM fixup in here - for i=1, #self.drda_array do - if ( i == 1 and #self.drda_array > 1 ) then - self.drda_array[1].DDM.Format = 0x41 - else - self.drda_array[i].DDM.Format = 0x01 - end - self.drda_array[i].DDM.CorelId = i - data = data .. tostring(self.drda_array[i]) - end - return data - end + __tostring = function( self ) + local data = "" + -- do some DDM fixup in here + for i=1, #self.drda_array do + if ( i == 1 and #self.drda_array > 1 ) then + self.drda_array[1].DDM.Format = 0x41 + else + self.drda_array[i].DDM.Format = 0x01 + end + self.drda_array[i].DDM.CorelId = i + data = data .. tostring(self.drda_array[i]) + end + return data + end } -- Distributed Relational Database Architecture (DRDA) Class DRDA = { - new = function(self, ddm) - local o = { - Parameters = {}, - DDM = ddm - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self, ddm) + local o = { + Parameters = {}, + DDM = ddm + } + setmetatable(o, self) + self.__index = self + return o + end, - --- Sets the DDM - -- - -- @param ddm DDM to assign to the DRDA - -- @return status boolean true on success, false on failure - setDDM = function( self, ddm ) - if ( not(ddm) ) then - return false, "DDM cannot be nil" - end - self.DDM = ddm - return true - end, + --- Sets the DDM + -- + -- @param ddm DDM to assign to the DRDA + -- @return status boolean true on success, false on failure + setDDM = function( self, ddm ) + if ( not(ddm) ) then + return false, "DDM cannot be nil" + end + self.DDM = ddm + return true + end, - --- Adds a DRDA parameter to the table - -- - -- @param param DRDAParam containing the parameter to add to the table - -- @return status bool true on success, false on failure - -- @return err string containing the error message if status is false - addParameter = function( self, param ) - if ( not(self.DDM) ) then - stdnse.print_debug("drda.DRDA.addParameter: DDM must be set prior to adding parameters") - return false, "DDM must be set prior to adding parameters" - end - if ( not(param) ) then - stdnse.print_debug("drda.DRDA.addParameter: Param cannot be nil") - return false, "Param cannot be nil" - end + --- Adds a DRDA parameter to the table + -- + -- @param param DRDAParam containing the parameter to add to the table + -- @return status bool true on success, false on failure + -- @return err string containing the error message if status is false + addParameter = function( self, param ) + if ( not(self.DDM) ) then + stdnse.print_debug("drda.DRDA.addParameter: DDM must be set prior to adding parameters") + return false, "DDM must be set prior to adding parameters" + end + if ( not(param) ) then + stdnse.print_debug("drda.DRDA.addParameter: Param cannot be nil") + return false, "Param cannot be nil" + end - table.insert(self.Parameters, param) + table.insert(self.Parameters, param) - -- update the DDM length fields - self.DDM.Length = self.DDM.Length + param.Length - self.DDM.Length2 = self.DDM.Length2 + param.Length + -- update the DDM length fields + self.DDM.Length = self.DDM.Length + param.Length + self.DDM.Length2 = self.DDM.Length2 + param.Length - return true - end, + return true + end, - --- Gets a parameter from the DRDA parameter table - -- - -- @param codepoint number containing the parameter type ro retrieve - -- @return param DRDAParameter containing the requested parameter - getParameter = function( self, codepoint ) - for _, v in ipairs( self.Parameters ) do - if ( v.CodePoint == codepoint ) then - return v - end - end - return - end, + --- Gets a parameter from the DRDA parameter table + -- + -- @param codepoint number containing the parameter type ro retrieve + -- @return param DRDAParameter containing the requested parameter + getParameter = function( self, codepoint ) + for _, v in ipairs( self.Parameters ) do + if ( v.CodePoint == codepoint ) then + return v + end + end + return + end, - --- Converts the DRDA class to a string - -- - -- @return data containing the object instance - __tostring = function(self) - if ( not(self.DDM) ) then - stdnse.print_debug("drda.DRDA.toString: DDM cannot be nil") - return nil - end + --- Converts the DRDA class to a string + -- + -- @return data containing the object instance + __tostring = function(self) + if ( not(self.DDM) ) then + stdnse.print_debug("drda.DRDA.toString: DDM cannot be nil") + return nil + end - local data = bin.pack(">SCCSSS", self.DDM.Length, self.DDM.Magic, self.DDM.Format, self.DDM.CorelId, self.DDM.Length2, self.DDM.CodePoint ) - for k,v in ipairs(self.Parameters) do - data = data .. tostring(v) - end - return data - end, + local data = bin.pack(">SCCSSS", self.DDM.Length, self.DDM.Magic, self.DDM.Format, self.DDM.CorelId, self.DDM.Length2, self.DDM.CodePoint ) + for k,v in ipairs(self.Parameters) do + data = data .. tostring(v) + end + return data + end, - --- Sends the DRDA over the db2socket - -- - -- @param db2socket DB2Socket over which to send the data - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, db2socket ) - return db2socket:send( tostring(self) ) - end, + --- Sends the DRDA over the db2socket + -- + -- @param db2socket DB2Socket over which to send the data + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, db2socket ) + return db2socket:send( tostring(self) ) + end, - --- Receives data from the db2socket and builds a DRDA object - -- - -- @param db2socket from which to read data - -- @return Status (true or false). - -- @return Data (if status is true) or error string (if status is false). - receive = function( self, db2socket ) - local DDM_SIZE = 10 - local pos = 1 + --- Receives data from the db2socket and builds a DRDA object + -- + -- @param db2socket from which to read data + -- @return Status (true or false). + -- @return Data (if status is true) or error string (if status is false). + receive = function( self, db2socket ) + local DDM_SIZE = 10 + local pos = 1 - -- first read atleast enough so that we can populate the DDM - local status, data = db2socket:receive_buf( match.numbytes(DDM_SIZE), true ) - if ( not(status) ) then - stdnse.print_debug("drda.DRDA.receive: %s", data) - return false, ("Failed to read at least %d bytes from socket"):format(DDM_SIZE) - end + -- first read atleast enough so that we can populate the DDM + local status, data = db2socket:receive_buf( match.numbytes(DDM_SIZE), true ) + if ( not(status) ) then + stdnse.print_debug("drda.DRDA.receive: %s", data) + return false, ("Failed to read at least %d bytes from socket"):format(DDM_SIZE) + end - local ddm = DDM:new() - ddm:fromString( data ) - self:setDDM( ddm ) + local ddm = DDM:new() + ddm:fromString( data ) + self:setDDM( ddm ) - status, data = db2socket:receive_buf( match.numbytes(ddm.Length - 10), true ) - if ( not(status) ) then - return false, ("Failed to read the remaining %d bytes of the DRDA message") - end + status, data = db2socket:receive_buf( match.numbytes(ddm.Length - 10), true ) + if ( not(status) ) then + return false, ("Failed to read the remaining %d bytes of the DRDA message") + end - -- add parameters until pos reaches the "end" - repeat - local param = DRDAParameter:new() - pos = param:fromString( data, pos ) - self:addParameter( param ) - until ( #data <= pos ) + -- add parameters until pos reaches the "end" + repeat + local param = DRDAParameter:new() + pos = param:fromString( data, pos ) + self:addParameter( param ) + until ( #data <= pos ) - return true - end, + return true + end, } -- The DRDAParameter class implements the DRDA parameters DRDAParameter = { - --- DRDA Parameter constructor - -- - -- @param codepoint number containing the codepoint value - -- @param data string containing the data portion of the DRDA parameter - -- @return o DRDAParameter object - new = function(self, codepoint, data) - local o = { - CodePoint = codepoint, - Data = data, - Length = ( data and #data + 4 or 4 ) - } - setmetatable(o, self) - self.__index = self - return o - end, + --- DRDA Parameter constructor + -- + -- @param codepoint number containing the codepoint value + -- @param data string containing the data portion of the DRDA parameter + -- @return o DRDAParameter object + new = function(self, codepoint, data) + local o = { + CodePoint = codepoint, + Data = data, + Length = ( data and #data + 4 or 4 ) + } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DRDA Parameter object to a string - -- - -- @return data string containing the DRDA Parameter - __tostring = function( self ) - local data = bin.pack(">SS", self.Length, self.CodePoint ) - if ( self.Data ) then - data = data .. bin.pack("A", self.Data) - end - return data - end, + --- Converts the DRDA Parameter object to a string + -- + -- @return data string containing the DRDA Parameter + __tostring = function( self ) + local data = bin.pack(">SS", self.Length, self.CodePoint ) + if ( self.Data ) then + data = data .. bin.pack("A", self.Data) + end + return data + end, - --- Builds a DRDA Parameter from a string - -- - -- @param data string from which to build the DRDA Parameter - -- @param pos number containing the offset into data - -- @return pos the new position after processing, -1 on error - fromString = function( self, data, pos ) - if( #data < 4 ) then - return -1 - end - pos, self.Length, self.CodePoint = bin.unpack( ">SS", data, pos ) + --- Builds a DRDA Parameter from a string + -- + -- @param data string from which to build the DRDA Parameter + -- @param pos number containing the offset into data + -- @return pos the new position after processing, -1 on error + fromString = function( self, data, pos ) + if( #data < 4 ) then + return -1 + end + pos, self.Length, self.CodePoint = bin.unpack( ">SS", data, pos ) - -- make sure the Length is assigned a value even though 0(nil) is returned - self.Length = self.Length or 0 + -- make sure the Length is assigned a value even though 0(nil) is returned + self.Length = self.Length or 0 - if ( self.Length > 0 ) then - pos, self.Data = bin.unpack("A" .. self.Length - 4, data, pos ) - end - return pos - end, + if ( self.Length > 0 ) then + pos, self.Data = bin.unpack("A" .. self.Length - 4, data, pos ) + end + return pos + end, - --- Returns the data portion of the parameter as an ASCII string - -- - -- @return str containing the data portion of the DRDA parameter as ASCII - getDataAsASCII = function( self ) - return StringUtil.toASCII( self.Data ) - end, + --- Returns the data portion of the parameter as an ASCII string + -- + -- @return str containing the data portion of the DRDA parameter as ASCII + getDataAsASCII = function( self ) + return StringUtil.toASCII( self.Data ) + end, - --- Returns the data in EBCDIC format - -- - -- @return str containing the data portion of the DRDA parameter in EBCDIC - getData = function( self ) - return self.Data - end, + --- Returns the data in EBCDIC format + -- + -- @return str containing the data portion of the DRDA parameter in EBCDIC + getData = function( self ) + return self.Data + end, } -- Distributed data management (DDM) DDM = { - Formats = - { - RESERVED = 0x80, - CHAINED = 0x40, - CONTINUE = 0x20, - SAME_CORRELATION = 0x10, - }, + Formats = + { + RESERVED = 0x80, + CHAINED = 0x40, + CONTINUE = 0x20, + SAME_CORRELATION = 0x10, + }, - Length = 10, - Magic = 0xD0, - Format = 0x41, - CorelId = 1, - Length2 = 4, - CodePoint = 0, + Length = 10, + Magic = 0xD0, + Format = 0x41, + CorelId = 1, + Length2 = 4, + CodePoint = 0, - --- Creates a new DDM packet - -- - -- @param codepoint - -- @param format - -- @param corelid - -- @return DDM object - new = function(self, codepoint, format, corelid) - local o = { - CodePoint = codepoint, - Format = format, - CorelId = corelid - } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new DDM packet + -- + -- @param codepoint + -- @param format + -- @param corelid + -- @return DDM object + new = function(self, codepoint, format, corelid) + local o = { + CodePoint = codepoint, + Format = format, + CorelId = corelid + } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DDM object to a string - __tostring = function( self ) - return bin.pack(">SCCSSS", self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint) - end, + --- Converts the DDM object to a string + __tostring = function( self ) + return bin.pack(">SCCSSS", self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint) + end, - --- Constructs a DDM object from a string - -- - -- @param str containing the data from which to construct the object - fromString = function( self, str ) - local DDM_SIZE = 10 - local pos = 1 + --- Constructs a DDM object from a string + -- + -- @param str containing the data from which to construct the object + fromString = function( self, str ) + local DDM_SIZE = 10 + local pos = 1 - if ( #str < DDM_SIZE ) then - return -1, ("drda.DDM.fromString: str was less than DDM_SIZE (%d)"):format( DDM_SIZE ) - end + if ( #str < DDM_SIZE ) then + return -1, ("drda.DDM.fromString: str was less than DDM_SIZE (%d)"):format( DDM_SIZE ) + end - pos, self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint = bin.unpack( ">SCCSSS", str ) - return pos - end, + pos, self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint = bin.unpack( ">SCCSSS", str ) + return pos + end, - --- Verifiers if there are additional DRDA's following - -- - -- @return true if the DRDA is to be chained, false if it's the last one - isChained = function( self ) - if ( bit.band( self.Format, DDM.Formats.CHAINED ) == DDM.Formats.CHAINED ) then - return true - end - return false - end, + --- Verifiers if there are additional DRDA's following + -- + -- @return true if the DRDA is to be chained, false if it's the last one + isChained = function( self ) + if ( bit.band( self.Format, DDM.Formats.CHAINED ) == DDM.Formats.CHAINED ) then + return true + end + return false + end, - --- Set the DRDA as chained (more following) - -- - -- @param chained boolean true if more DRDA's are following - setChained = function( self, chained ) - if ( self:isChained() ) then - self.Format = bit.bxor( self.Format, self.Formats.CHAINED ) - else - self.Format = bit.bor( self.Format, self.Formats.CHAINED ) - end - end, + --- Set the DRDA as chained (more following) + -- + -- @param chained boolean true if more DRDA's are following + setChained = function( self, chained ) + if ( self:isChained() ) then + self.Format = bit.bxor( self.Format, self.Formats.CHAINED ) + else + self.Format = bit.bor( self.Format, self.Formats.CHAINED ) + end + end, } -- static DRDA packet construction class Command = { - --- Builds an EXCSAT DRDA packet - -- - -- @param extname string containing the external name - -- @param srvname string containing the server name - -- @param rellev string containing the server product release level - -- @param mgrlvlls string containing the manager level list - -- @param srvclass string containing the server class name - -- @return drda DRDA instance - EXCSAT = function( extname, srvname, rellev, mgrlvlls, srvclass ) - local drda = DRDA:new( DDM:new( CodePoint.EXCSAT ) ) + --- Builds an EXCSAT DRDA packet + -- + -- @param extname string containing the external name + -- @param srvname string containing the server name + -- @param rellev string containing the server product release level + -- @param mgrlvlls string containing the manager level list + -- @param srvclass string containing the server class name + -- @return drda DRDA instance + EXCSAT = function( extname, srvname, rellev, mgrlvlls, srvclass ) + local drda = DRDA:new( DDM:new( CodePoint.EXCSAT ) ) - drda:addParameter( DRDAParameter:new( CodePoint.EXTNAM, StringUtil.toEBCDIC( extname ) ) ) - drda:addParameter( DRDAParameter:new( CodePoint.SRVNAM, StringUtil.toEBCDIC( srvname ) ) ) - drda:addParameter( DRDAParameter:new( CodePoint.SRVRLSLV, StringUtil.toEBCDIC( rellev ) ) ) - drda:addParameter( DRDAParameter:new( CodePoint.MGRLVLLS, mgrlvlls ) ) - drda:addParameter( DRDAParameter:new( CodePoint.SRVCLSNM, StringUtil.toEBCDIC( srvclass ) ) ) + drda:addParameter( DRDAParameter:new( CodePoint.EXTNAM, StringUtil.toEBCDIC( extname ) ) ) + drda:addParameter( DRDAParameter:new( CodePoint.SRVNAM, StringUtil.toEBCDIC( srvname ) ) ) + drda:addParameter( DRDAParameter:new( CodePoint.SRVRLSLV, StringUtil.toEBCDIC( rellev ) ) ) + drda:addParameter( DRDAParameter:new( CodePoint.MGRLVLLS, mgrlvlls ) ) + drda:addParameter( DRDAParameter:new( CodePoint.SRVCLSNM, StringUtil.toEBCDIC( srvclass ) ) ) - return drda - end, + return drda + end, - --- Builds an ACCSEC DRDA packet - -- - -- @param secmec number containing the security mechanism ID - -- @param database string containing the database name - -- @return drda DRDA instance - ACCSEC = function( secmec, database ) - local drda = DRDA:new( DDM:new( CodePoint.ACCSEC ) ) - drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec )) - drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) )) + --- Builds an ACCSEC DRDA packet + -- + -- @param secmec number containing the security mechanism ID + -- @param database string containing the database name + -- @return drda DRDA instance + ACCSEC = function( secmec, database ) + local drda = DRDA:new( DDM:new( CodePoint.ACCSEC ) ) + drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec )) + drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) )) - return drda - end, + return drda + end, - --- Builds a SECCHK DRDA packet - -- - -- @param secmec number containing the security mechanism ID - -- @param database string containing the database name - -- @param username string - -- @param password string - -- @return drda DRDA instance - SECCHK = function( secmec, database, username, password ) - local drda = DRDA:new( DDM:new( CodePoint.SECCHK ) ) - drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec )) - drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) )) - drda:addParameter( DRDAParameter:new( CodePoint.USRID, StringUtil.toEBCDIC(username) ) ) - drda:addParameter( DRDAParameter:new( CodePoint.PASSWORD, StringUtil.toEBCDIC(password) ) ) + --- Builds a SECCHK DRDA packet + -- + -- @param secmec number containing the security mechanism ID + -- @param database string containing the database name + -- @param username string + -- @param password string + -- @return drda DRDA instance + SECCHK = function( secmec, database, username, password ) + local drda = DRDA:new( DDM:new( CodePoint.SECCHK ) ) + drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec )) + drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) )) + drda:addParameter( DRDAParameter:new( CodePoint.USRID, StringUtil.toEBCDIC(username) ) ) + drda:addParameter( DRDAParameter:new( CodePoint.PASSWORD, StringUtil.toEBCDIC(password) ) ) - return drda - end, + return drda + end, - --- Builds an ACCRDB DRDA packet - -- - -- @param database string containing the database name - -- @param rdbaccl string containing the RDB access manager class - -- @param prdid string containing the product id - -- @param typdefnam string containing the data type definition name - -- @param typdefovr string containing the data type definition override - -- @return drda DRDA instance - ACCRDB = function( database, rdbaccl, prdid, prddata, typdefnam, crrtkn, typdefovr ) - local drda = DRDA:new( DDM:new( CodePoint.ACCRDB ) ) - drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) ) ) + --- Builds an ACCRDB DRDA packet + -- + -- @param database string containing the database name + -- @param rdbaccl string containing the RDB access manager class + -- @param prdid string containing the product id + -- @param typdefnam string containing the data type definition name + -- @param typdefovr string containing the data type definition override + -- @return drda DRDA instance + ACCRDB = function( database, rdbaccl, prdid, prddata, typdefnam, crrtkn, typdefovr ) + local drda = DRDA:new( DDM:new( CodePoint.ACCRDB ) ) + drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) ) ) - if ( rdbaccl ) then - drda:addParameter( DRDAParameter:new( CodePoint.RDBACCL, rdbaccl ) ) - end - if ( prdid ) then - drda:addParameter( DRDAParameter:new( CodePoint.PRDID, StringUtil.toEBCDIC( prdid ) ) ) - end - if ( prddata ) then - drda:addParameter( DRDAParameter:new( CodePoint.PRDDATA, StringUtil.toEBCDIC( prddata ) ) ) - end - if( typdefnam ) then - drda:addParameter( DRDAParameter:new( CodePoint.TYPDEFNAM, StringUtil.toEBCDIC( typdefnam ) ) ) - end - if( crrtkn ) then - drda:addParameter( DRDAParameter:new( CodePoint.CRRTKN, crrtkn ) ) - end - if( typdefovr ) then - drda:addParameter( DRDAParameter:new( CodePoint.TYPDEFOVR, typdefovr ) ) - end + if ( rdbaccl ) then + drda:addParameter( DRDAParameter:new( CodePoint.RDBACCL, rdbaccl ) ) + end + if ( prdid ) then + drda:addParameter( DRDAParameter:new( CodePoint.PRDID, StringUtil.toEBCDIC( prdid ) ) ) + end + if ( prddata ) then + drda:addParameter( DRDAParameter:new( CodePoint.PRDDATA, StringUtil.toEBCDIC( prddata ) ) ) + end + if( typdefnam ) then + drda:addParameter( DRDAParameter:new( CodePoint.TYPDEFNAM, StringUtil.toEBCDIC( typdefnam ) ) ) + end + if( crrtkn ) then + drda:addParameter( DRDAParameter:new( CodePoint.CRRTKN, crrtkn ) ) + end + if( typdefovr ) then + drda:addParameter( DRDAParameter:new( CodePoint.TYPDEFOVR, typdefovr ) ) + end - return drda - end + return drda + end } @@ -520,208 +520,208 @@ Command = -- Helper Class Helper = { - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Connect to the DB2 host - -- - -- @param host table - -- @param port table - -- @return Status (true or false). - -- @return Error code (if status is false). - connect = function( self, host, port ) - self.comm = Comm:new( host, port ) - return self.comm:connect() - end, + --- Connect to the DB2 host + -- + -- @param host table + -- @param port table + -- @return Status (true or false). + -- @return Error code (if status is false). + connect = function( self, host, port ) + self.comm = Comm:new( host, port ) + return self.comm:connect() + end, - --- Closes an open connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - close = function( self ) - self.comm:close() - end, + --- Closes an open connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + close = function( self ) + self.comm:close() + end, - --- Returns Server Information (name, platform, version) - -- - -- @return table containing extname, srvclass, - -- srvname and prodrel - getServerInfo = function( self ) - local mgrlvlls = bin.pack("H", "1403000724070008240f00081440000814740008") - local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" ) - local response, param, err + --- Returns Server Information (name, platform, version) + -- + -- @return table containing extname, srvclass, + -- srvname and prodrel + getServerInfo = function( self ) + local mgrlvlls = bin.pack("H", "1403000724070008240f00081440000814740008") + local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" ) + local response, param, err - local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat } ) ) - if ( not(status) ) then return false, err end + local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat } ) ) + if ( not(status) ) then return false, err end - local drda = packet:getDRDAByCodePoint( CodePoint.EXCSATRD ) - if ( drda ) then - response = {} - param = drda:getParameter( CodePoint.EXTNAM ) - if ( param ) then - response.extname = param:getDataAsASCII() - end - param = drda:getParameter( CodePoint.SRVCLSNM ) - if ( param ) then - response.srvclass = param:getDataAsASCII() - end - param = drda:getParameter( CodePoint.SRVNAM ) - if ( param ) then - response.srvname = param:getDataAsASCII() - end - param = drda:getParameter( CodePoint.SRVRLSLV ) - if ( param ) then - response.prodrel = param:getDataAsASCII() - end - else - return false, "The response contained no EXCSATRD" - end + local drda = packet:getDRDAByCodePoint( CodePoint.EXCSATRD ) + if ( drda ) then + response = {} + param = drda:getParameter( CodePoint.EXTNAM ) + if ( param ) then + response.extname = param:getDataAsASCII() + end + param = drda:getParameter( CodePoint.SRVCLSNM ) + if ( param ) then + response.srvclass = param:getDataAsASCII() + end + param = drda:getParameter( CodePoint.SRVNAM ) + if ( param ) then + response.srvname = param:getDataAsASCII() + end + param = drda:getParameter( CodePoint.SRVRLSLV ) + if ( param ) then + response.prodrel = param:getDataAsASCII() + end + else + return false, "The response contained no EXCSATRD" + end - return true, response - end, + return true, response + end, - --- Login to DB2 database server - -- - -- @param database containing the name of the database - -- @param username containing the authentication username - -- @param password containing the authentication password - -- @return Status (true or false) - -- @return err message (if status if false) - login = function( self, database, username, password ) - local mgrlvlls = bin.pack("H", "1403000724070008240f00081440000814740008") - local secmec, prdid = "\00\03", "JCC03010" - local tdovr = bin.pack("H", "0006119c04b80006119d04b00006119e04b8") - local crrtkn= bin.pack("H", "d5c6f0f0f0f0f0f14bc3c6f4c4012a11168414") + --- Login to DB2 database server + -- + -- @param database containing the name of the database + -- @param username containing the authentication username + -- @param password containing the authentication password + -- @return Status (true or false) + -- @return err message (if status if false) + login = function( self, database, username, password ) + local mgrlvlls = bin.pack("H", "1403000724070008240f00081440000814740008") + local secmec, prdid = "\00\03", "JCC03010" + local tdovr = bin.pack("H", "0006119c04b80006119d04b00006119e04b8") + local crrtkn= bin.pack("H", "d5c6f0f0f0f0f0f14bc3c6f4c4012a11168414") - local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" ) - local drda_accsec = Command.ACCSEC( secmec, database ) - local drda_secchk = Command.SECCHK( secmec, database, username, password ) - local drda_accrdb = Command.ACCRDB( database, string.char(0x24,0x07), "DNC10060", nil, "QTDSQLASC", crrtkn, tdovr) + local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" ) + local drda_accsec = Command.ACCSEC( secmec, database ) + local drda_secchk = Command.SECCHK( secmec, database, username, password ) + local drda_accrdb = Command.ACCRDB( database, string.char(0x24,0x07), "DNC10060", nil, "QTDSQLASC", crrtkn, tdovr) - local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat, drda_accsec } ) ) - if( not(status) ) then return false, packet end + local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat, drda_accsec } ) ) + if( not(status) ) then return false, packet end - if ( packet:getDRDAByCodePoint( CodePoint.RDBNFNRM ) or - packet:getDRDAByCodePoint( CodePoint.RDBAFLRM ) ) then - stdnse.print_debug("drda.Helper.login: ERROR: RDB not found") - return false, "ERROR: Database not found" - end + if ( packet:getDRDAByCodePoint( CodePoint.RDBNFNRM ) or + packet:getDRDAByCodePoint( CodePoint.RDBAFLRM ) ) then + stdnse.print_debug("drda.Helper.login: ERROR: RDB not found") + return false, "ERROR: Database not found" + end - local drda = packet:getDRDAByCodePoint( CodePoint.ACCSECRD ) - if ( not(drda) ) then - return false, "ERROR: Response did not contain any valid security mechanisms" - end + local drda = packet:getDRDAByCodePoint( CodePoint.ACCSECRD ) + if ( not(drda) ) then + return false, "ERROR: Response did not contain any valid security mechanisms" + end - local param = drda:getParameter( CodePoint.SECMEC ) - if ( not(param) ) then - stdnse.print_debug("drda.Helper.login: ERROR: Response did not contain any valid security mechanisms") - return false, "ERROR: Response did not contain any valid security mechanisms" - end + local param = drda:getParameter( CodePoint.SECMEC ) + if ( not(param) ) then + stdnse.print_debug("drda.Helper.login: ERROR: Response did not contain any valid security mechanisms") + return false, "ERROR: Response did not contain any valid security mechanisms" + end - if ( select(2, bin.unpack(">S", param:getData())) ~= SecMec.USER_PASSWORD ) then - stdnse.print_debug("drda.Helper.login: ERROR: Securite Mechanism not supported") - return false, "ERROR: Security mechanism not supported" - end + if ( select(2, bin.unpack(">S", param:getData())) ~= SecMec.USER_PASSWORD ) then + stdnse.print_debug("drda.Helper.login: ERROR: Securite Mechanism not supported") + return false, "ERROR: Security mechanism not supported" + end - status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_secchk, drda_accrdb } ) ) - if( not(status) ) then return false, "ERROR: Login failed" end + status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_secchk, drda_accrdb } ) ) + if( not(status) ) then return false, "ERROR: Login failed" end - -- - -- At this point we have a few differences in behaviour - -- * DB2 has told us earlier if the DB does not exist - -- * Apache Derby will do so here, regardless of the login was - -- successfull or not - -- * Informix will tell us that the DB does not exist IF the - -- login was successfull - -- - -- Therefore the order of these checks are important!! - if ( packet:getDRDAByCodePoint( CodePoint.ACCRDBRM ) ) then - return true - -- Apache Derby responds differently with usernames containing spaces - elseif ( packet:getDRDAByCodePoint( CodePoint.RDBATHRM ) ) then - return false, "ERROR: Login failed" - -- Informix responds with a SECCHKRM DDM response - elseif ( packet:getDRDAByCodePoint( CodePoint.SECCHKRM ) ) then - drda = packet:getDRDAByCodePoint( CodePoint.SECCHKRM ) - param= drda:getParameter( CodePoint.SECCHKCD ) - if ( param and param:getData() == "\0" ) then - return true - end - elseif ( packet:getDRDAByCodePoint( CodePoint.RDBNFNRM ) or - packet:getDRDAByCodePoint( CodePoint.RDBAFLRM ) ) then - return false, "ERROR: Database not found" - end - return false, "ERROR: Login failed" - end, + -- + -- At this point we have a few differences in behaviour + -- * DB2 has told us earlier if the DB does not exist + -- * Apache Derby will do so here, regardless of the login was + -- successfull or not + -- * Informix will tell us that the DB does not exist IF the + -- login was successfull + -- + -- Therefore the order of these checks are important!! + if ( packet:getDRDAByCodePoint( CodePoint.ACCRDBRM ) ) then + return true + -- Apache Derby responds differently with usernames containing spaces + elseif ( packet:getDRDAByCodePoint( CodePoint.RDBATHRM ) ) then + return false, "ERROR: Login failed" + -- Informix responds with a SECCHKRM DDM response + elseif ( packet:getDRDAByCodePoint( CodePoint.SECCHKRM ) ) then + drda = packet:getDRDAByCodePoint( CodePoint.SECCHKRM ) + param= drda:getParameter( CodePoint.SECCHKCD ) + if ( param and param:getData() == "\0" ) then + return true + end + elseif ( packet:getDRDAByCodePoint( CodePoint.RDBNFNRM ) or + packet:getDRDAByCodePoint( CodePoint.RDBAFLRM ) ) then + return false, "ERROR: Database not found" + end + return false, "ERROR: Login failed" + end, } -- The communication class Comm = { - new = function(self, host, port) - local o = { - host = host, - port = port, - socket = nmap.new_socket() - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self, host, port) + local o = { + host = host, + port = port, + socket = nmap.new_socket() + } + setmetatable(o, self) + self.__index = self + return o + end, - connect = function(self) - return self.socket:connect(self.host, self.port) - end, + connect = function(self) + return self.socket:connect(self.host, self.port) + end, - close = function(self) - return self.socket:close() - end, + close = function(self) + return self.socket:close() + end, - recvDRDA = function( self ) - local drda_tbl = {} + recvDRDA = function( self ) + local drda_tbl = {} - repeat - local drda = DRDA:new() - local status, err = drda:receive( self.socket ) - if ( not(status) ) then - return false, err - end - table.insert(drda_tbl, drda) - until ( not(drda.DDM:isChained()) ) - return true, drda_tbl - end, + repeat + local drda = DRDA:new() + local status, err = drda:receive( self.socket ) + if ( not(status) ) then + return false, err + end + table.insert(drda_tbl, drda) + until ( not(drda.DDM:isChained()) ) + return true, drda_tbl + end, - --- Sends a packet to the server and receives the response - -- - -- @param DRDAPacket - -- @return status true on success, false on failure - -- @return packet an instance of DRDAPacket - exchDRDAPacket = function( self, packet ) - local drda, err - local status, err = self.socket:send( tostring(packet) ) + --- Sends a packet to the server and receives the response + -- + -- @param DRDAPacket + -- @return status true on success, false on failure + -- @return packet an instance of DRDAPacket + exchDRDAPacket = function( self, packet ) + local drda, err + local status, err = self.socket:send( tostring(packet) ) - if ( not(status) ) then - stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", err ) - return false, ("ERROR: DB2Socket error: %s"):format( err ) - end + if ( not(status) ) then + stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", err ) + return false, ("ERROR: DB2Socket error: %s"):format( err ) + end - status, drda = self:recvDRDA() - if( not(status) ) then - stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", drda ) - return false, ("ERROR: DB2Socket error: %s"):format( drda ) - end - return true, DRDAPacket:new( drda ) - end + status, drda = self:recvDRDA() + if( not(status) ) then + stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", drda ) + return false, ("ERROR: DB2Socket error: %s"):format( drda ) + end + return true, DRDAPacket:new( drda ) + end } -- EBCDIC/ASCII Conversion tables -a2e_hex = "00010203372D2E2F1605250B0C0D0E0F101112133C3D322618193F271C1D1E1F" +a2e_hex = "00010203372D2E2F1605250B0C0D0E0F101112133C3D322618193F271C1D1E1F" a2e_hex = a2e_hex .. "405A7F7B5B6C507D4D5D5C4E6B604B61F0F1F2F3F4F5F6F7F8F97A5E4C7E6E6F" a2e_hex = a2e_hex .. "7CC1C2C3C4C5C6C7C8C9D1D2D3D4D5D6D7D8D9E2E3E4E5E6E7E8E9ADE0BD5F6D" a2e_hex = a2e_hex .. "79818283848586878889919293949596979899A2A3A4A5A6A7A8A9C04FD0A107" @@ -730,7 +730,7 @@ a2e_hex = a2e_hex .. "4142434445464748495152535455565758596263646566676869707172 a2e_hex = a2e_hex .. "767778808A8B8C8D8E8F909A9B9C9D9E9FA0AAABAC4AAEAFB0B1B2B3B4B5B6B7" a2e_hex = a2e_hex .. "B8B9BABBBC6ABEBFCACBCCCDCECFDADBDCDDDEDFEAEBECEDEEEFFAFBFCFDFEFF" -e2a_hex = "000102039C09867F978D8E0B0C0D0E0F101112139D8508871819928F1C1D1E1F" +e2a_hex = "000102039C09867F978D8E0B0C0D0E0F101112139D8508871819928F1C1D1E1F" e2a_hex = e2a_hex .. "80818283840A171B88898A8B8C050607909116939495960498999A9B14159E1A" e2a_hex = e2a_hex .. "20A0A1A2A3A4A5A6A7A8D52E3C282B7C26A9AAABACADAEAFB0B121242A293B5E" e2a_hex = e2a_hex .. "2D2FB2B3B4B5B6B7B8B9E52C255F3E3FBABBBCBDBEBFC0C1C2603A2340273D22" @@ -746,49 +746,49 @@ e2a_tbl = bin.pack("H", e2a_hex) -- Handle EBCDIC/ASCII conversion StringUtil = { - --- Converts an ASCII string to EBCDIC - -- - -- @param ascii string containing the ASCII value - -- @return string containing the EBCDIC value - toEBCDIC = function( ascii ) - local ret = "" + --- Converts an ASCII string to EBCDIC + -- + -- @param ascii string containing the ASCII value + -- @return string containing the EBCDIC value + toEBCDIC = function( ascii ) + local ret = "" - for i=1, #ascii do - local val = ascii.byte(ascii,i) + 1 - ret = ret .. a2e_tbl:sub(val, val) - end - return ret - end, + for i=1, #ascii do + local val = ascii.byte(ascii,i) + 1 + ret = ret .. a2e_tbl:sub(val, val) + end + return ret + end, - --- Converts an EBCDIC string to ASCII - -- - -- @param ebcdic string containing EBCDIC value - -- @return string containing ASCII value - toASCII = function( ebcdic ) - local ret = "" + --- Converts an EBCDIC string to ASCII + -- + -- @param ebcdic string containing EBCDIC value + -- @return string containing ASCII value + toASCII = function( ebcdic ) + local ret = "" - for i=1, #ebcdic do - local val = ebcdic.byte(ebcdic,i) + 1 - ret = ret .. e2a_tbl:sub(val, val) - end - return ret - end, + for i=1, #ebcdic do + local val = ebcdic.byte(ebcdic,i) + 1 + ret = ret .. e2a_tbl:sub(val, val) + end + return ret + end, - --- Pads a string with a character - -- - -- @param str string to pad - -- @param chr char to pad with - -- @param len the total length of the finnished string - -- @return str string containing the padded string - padWithChar = function( str, chr, len ) - if ( len < #str ) then - return str - end - for i=1, (len - #str) do - str = str .. chr - end - return str - end, + --- Pads a string with a character + -- + -- @param str string to pad + -- @param chr char to pad with + -- @param len the total length of the finnished string + -- @return str string containing the padded string + padWithChar = function( str, chr, len ) + if ( len < #str ) then + return str + end + for i=1, (len - #str) do + str = str .. chr + end + return str + end, } return _ENV; diff --git a/nselib/giop.lua b/nselib/giop.lua index 60b58fa54..33bc240da 100644 --- a/nselib/giop.lua +++ b/nselib/giop.lua @@ -3,7 +3,7 @@ -- -- Summary -- ------- --- The library currently provides functionality to connect and query the +-- The library currently provides functionality to connect and query the -- CORBA naming service for a list of available objects. -- -- @@ -12,20 +12,20 @@ -- The library contains the following classes: -- -- o Packet.* --- - The Packet classes contain specific packets and function to serialize +-- - The Packet classes contain specific packets and function to serialize -- them to strings that can be sent over the wire. Each class may also -- contain a function to parse the servers response. -- -- o Comm --- - Implements a number of functions to handle communication over the +-- - Implements a number of functions to handle communication over the -- the Socket class. -- -- o Helper --- - A helper class that provides easy access to the rest of the library +-- - A helper class that provides easy access to the rest of the library -- -- o Socket --- - This is a copy of the DB2Socket class which provides fundamental --- buffering +-- - This is a copy of the DB2Socket class which provides fundamental +-- buffering -- -- -- Example @@ -34,7 +34,7 @@ -- to interface the library: -- -- --- helper = giop.Helper:new(host, port) +-- helper = giop.Helper:new(host, port) -- status, err = helper:Connect() -- status, ctx = helper:GetNamingContext() -- status, objs = helper:ListObjects(ctx) @@ -67,24 +67,24 @@ _ENV = stdnse.module("giop", stdnse.seeall) -- A bunch of constants Constants = { - SyncScope = { - WITH_TARGET = 3, - }, + SyncScope = { + WITH_TARGET = 3, + }, - ServiceContext = { - CODESETS = 1, - SENDING_CONTEXT_RUNTIME = 6, - NEO_FIRST_SERVICE_CONTEXT = 1313165056, - }, + ServiceContext = { + CODESETS = 1, + SENDING_CONTEXT_RUNTIME = 6, + NEO_FIRST_SERVICE_CONTEXT = 1313165056, + }, - ReplyStatus = { - SYSTEM_EXCEPTION = 2, - }, + ReplyStatus = { + SYSTEM_EXCEPTION = 2, + }, - VERSION_1_0 = 1, - VERSION_1_2 = 0x0201, + VERSION_1_0 = 1, + VERSION_1_2 = 0x0201, - NAMESERVICE = "NameService\0", + NAMESERVICE = "NameService\0", } @@ -92,331 +92,331 @@ Packet = {} Packet.GIOP = { - magic = "GIOP", - version = 0x0001, - byte_order = 0, + magic = "GIOP", + version = 0x0001, + byte_order = 0, - --- Creates a Packet.GIOP - -- - -- @param msgtype number containing the messaget type - -- @param data string conatining the message data - -- @return obj a new Packet.GIOP instance - new = function( self, msgtype, data ) - local o = {} - setmetatable(o, self) - self.__index = self - o.type = msgtype - o.data = data - o.size = data and #data or 0 - return o - end, + --- Creates a Packet.GIOP + -- + -- @param msgtype number containing the messaget type + -- @param data string conatining the message data + -- @return obj a new Packet.GIOP instance + new = function( self, msgtype, data ) + local o = {} + setmetatable(o, self) + self.__index = self + o.type = msgtype + o.data = data + o.size = data and #data or 0 + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the instance data - __tostring = function( self ) - return bin.pack("IA", self.magic, self.version, self.byte_order, self.type, self.size, self.data ) - end, + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the instance data + __tostring = function( self ) + return bin.pack("IA", self.magic, self.version, self.byte_order, self.type, self.size, self.data ) + end, - --- Sets the packet version - -- - -- @param version number containing the version to use - setVersion = function( self, version ) self.version = version end, + --- Sets the packet version + -- + -- @param version number containing the version to use + setVersion = function( self, version ) self.version = version end, - --- Receives the packet over the socket - -- - -- @param socket containing the already connected socket - -- @return status true on success, false on failure - -- @return err containing the error message if status is false - recv = function( self, socket ) - local status, data = socket:recv( 12 ) - local pos + --- Receives the packet over the socket + -- + -- @param socket containing the already connected socket + -- @return status true on success, false on failure + -- @return err containing the error message if status is false + recv = function( self, socket ) + local status, data = socket:recv( 12 ) + local pos - if ( not(status) ) then return false, "Failed to read Packet.GIOP" end + if ( not(status) ) then return false, "Failed to read Packet.GIOP" end - pos, self.magic, self.version, self.byte_order, - self.type = bin.unpack("" or "<") .. "I", data, pos ) + pos, self.size = bin.unpack( ( self.byte_order == 0 and ">" or "<") .. "I", data, pos ) - status, data = socket:recv( self.size ) - if ( not(status) ) then return false, "Failed to read Packet.GIOP" end + status, data = socket:recv( self.size ) + if ( not(status) ) then return false, "Failed to read Packet.GIOP" end - self.data = data - return true - end, + self.data = data + return true + end, } ServiceContext = { - --- Creates a ServiceContext - -- - -- @param id number containing the context id - -- @param data the service context data - -- @param pad [optional] number used to pad after the service context - -- @return obj a new ServiceContext instance - new = function( self, id, data, pad ) - local o = {} - setmetatable(o, self) - self.__index = self - o.id = id - o.data = data or "" - o.pad = pad - return o - end, + --- Creates a ServiceContext + -- + -- @param id number containing the context id + -- @param data the service context data + -- @param pad [optional] number used to pad after the service context + -- @return obj a new ServiceContext instance + new = function( self, id, data, pad ) + local o = {} + setmetatable(o, self) + self.__index = self + o.id = id + o.data = data or "" + o.pad = pad + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the instance data - __tostring = function( self ) - if ( self.pad ) then - return bin.pack(">IIAS", self.id, #self.data, self.data, self.pad) - else - return bin.pack(">IIA", self.id, #self.data, self.data) - end - end, + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the instance data + __tostring = function( self ) + if ( self.pad ) then + return bin.pack(">IIAS", self.id, #self.data, self.data, self.pad) + else + return bin.pack(">IIA", self.id, #self.data, self.data) + end + end, } --- Creates a SendingContextRuntime SendingContextRuntime = { - --- Creates a SendingContextRuntime - -- - -- @param lhost string containing the source ip address - -- @return obj a new SendingContextRuntime instance - new = function(self, lhost ) - local o = {} - setmetatable(o, self) - self.__index = self - o.data = bin.pack(">HIAH", - [[ - 000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746 - 578742f436f6465426173653a312e300000000001000000000000006e000102 - 00 - ]], #lhost + 1, lhost .. "\0", - [[ - 00ec5100000019afabcb000000000249765d6900000008000000000000000014 - 0000000000000200000001000000200000000000010001000000020501000100 - 01002000010109000000010001010000000026000000020002 - ]] ) - return o - end, + --- Creates a SendingContextRuntime + -- + -- @param lhost string containing the source ip address + -- @return obj a new SendingContextRuntime instance + new = function(self, lhost ) + local o = {} + setmetatable(o, self) + self.__index = self + o.data = bin.pack(">HIAH", + [[ + 000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746 + 578742f436f6465426173653a312e300000000001000000000000006e000102 + 00 + ]], #lhost + 1, lhost .. "\0", + [[ + 00ec5100000019afabcb000000000249765d6900000008000000000000000014 + 0000000000000200000001000000200000000000010001000000020501000100 + 01002000010109000000010001010000000026000000020002 + ]] ) + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the instance data - __tostring = function( self ) return self.data end, + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the instance data + __tostring = function( self ) return self.data end, } Packet.GIOP.reply = { - --- Creates a new Packet.GIOP.reply instance - -- - -- @return obj a new Packet.GIOP.get instance - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - self.sc = {} - self.GIOP = Packet.GIOP:new() - return o - end, + --- Creates a new Packet.GIOP.reply instance + -- + -- @return obj a new Packet.GIOP.get instance + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + self.sc = {} + self.GIOP = Packet.GIOP:new() + return o + end, - --- Receives a Packet.GIOP.reply from the socket - -- - -- @param socket already connected to the server - -- @return status true on success, false on failure - -- @return err error message if status is false - recv = function( self, socket ) - local status, err = self.GIOP:recv( socket ) - local pos, tmp - local bo = ( self.GIOP.byte_order == 0 and ">" or "<") + --- Receives a Packet.GIOP.reply from the socket + -- + -- @param socket already connected to the server + -- @return status true on success, false on failure + -- @return err error message if status is false + recv = function( self, socket ) + local status, err = self.GIOP:recv( socket ) + local pos, tmp + local bo = ( self.GIOP.byte_order == 0 and ">" or "<") - if( not(status) ) then return false, err end + if( not(status) ) then return false, err end - if ( self.GIOP.version == Constants.VERSION_1_2 ) then - pos, self.request_id, self.reply_status = bin.unpack(bo .. "II", self.GIOP.data, pos ) - pos, tmp = bin.unpack( bo .. "I", self.GIOP.data, pos ) - elseif ( self.GIOP.version == Constants.VERSION_1_0 ) then - pos, tmp = bin.unpack( bo .. "I", self.GIOP.data ) - end + if ( self.GIOP.version == Constants.VERSION_1_2 ) then + pos, self.request_id, self.reply_status = bin.unpack(bo .. "II", self.GIOP.data, pos ) + pos, tmp = bin.unpack( bo .. "I", self.GIOP.data, pos ) + elseif ( self.GIOP.version == Constants.VERSION_1_0 ) then + pos, tmp = bin.unpack( bo .. "I", self.GIOP.data ) + end - for i=1, tmp do - local ctx_id, ctx_len, ctx_data - pos, ctx_id, ctx_len = bin.unpack( bo .. "II", self.GIOP.data, pos ) - pos, ctx_data = bin.unpack("A" .. ctx_len, self.GIOP.data, pos ) - if ( i ~= tmp ) then pos = pos + 2 end - table.insert( self.sc, ServiceContext:new( ctx_id, ctx_data ) ) - end + for i=1, tmp do + local ctx_id, ctx_len, ctx_data + pos, ctx_id, ctx_len = bin.unpack( bo .. "II", self.GIOP.data, pos ) + pos, ctx_data = bin.unpack("A" .. ctx_len, self.GIOP.data, pos ) + if ( i ~= tmp ) then pos = pos + 2 end + table.insert( self.sc, ServiceContext:new( ctx_id, ctx_data ) ) + end - if ( self.GIOP.version == Constants.VERSION_1_0 ) then - pos, self.request_id, self.reply_status, self.stub_data = bin.unpack( bo .. "IIA" .. ( #self.GIOP.data - pos - 8 ), self.GIOP.data, pos ) - elseif ( pos < #self.GIOP.data ) then - pos, self.data = bin.unpack("A" .. (#self.GIOP.data - pos), self.GIOP.data, pos ) - end + if ( self.GIOP.version == Constants.VERSION_1_0 ) then + pos, self.request_id, self.reply_status, self.stub_data = bin.unpack( bo .. "IIA" .. ( #self.GIOP.data - pos - 8 ), self.GIOP.data, pos ) + elseif ( pos < #self.GIOP.data ) then + pos, self.data = bin.unpack("A" .. (#self.GIOP.data - pos), self.GIOP.data, pos ) + end - return true - end, + return true + end, } Packet.GIOP.get = { - resp_expected = 1, - key_length = 4, - princ_len = 0, + resp_expected = 1, + key_length = 4, + princ_len = 0, - --- Creates a new Packet.GIOP._is_a instance - -- - -- @param id the packet identifier - -- @param key number containing the object key - -- @param data string containing the stub data - -- @return obj a new Packet.GIOP.get instance - new = function( self, id, key, data ) - local o = {} - setmetatable(o, self) - self.__index = self - o.op = "get\0" - o.id = id - o.key = key - o.data = data - o.sc = {} - return o - end, + --- Creates a new Packet.GIOP._is_a instance + -- + -- @param id the packet identifier + -- @param key number containing the object key + -- @param data string containing the stub data + -- @return obj a new Packet.GIOP.get instance + new = function( self, id, key, data ) + local o = {} + setmetatable(o, self) + self.__index = self + o.op = "get\0" + o.id = id + o.key = key + o.data = data + o.sc = {} + return o + end, - --- Creates and adds a service context to the packet - -- - -- @param id number containing the context id - -- @param data the service context data - -- @param pad [optional] number used to pad after the service context - addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, + --- Creates and adds a service context to the packet + -- + -- @param id number containing the context id + -- @param data the service context data + -- @param pad [optional] number used to pad after the service context + addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function( self ) - local data = bin.pack(">I", #self.sc) - local pad = 0 + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function( self ) + local data = bin.pack(">I", #self.sc) + local pad = 0 - for i=1, #self.sc do - local tmp = tostring( self.sc[i]) - data = data .. bin.pack("A", tmp ) - end + for i=1, #self.sc do + local tmp = tostring( self.sc[i]) + data = data .. bin.pack("A", tmp ) + end - data = data .. bin.pack( ">ICCCCIIIAIA", self.id, self.resp_expected, pad, pad, pad, - self.key_length, self.key, #self.op, self.op, self.princ_len, self.data ) + data = data .. bin.pack( ">ICCCCIIIAIA", self.id, self.resp_expected, pad, pad, pad, + self.key_length, self.key, #self.op, self.op, self.princ_len, self.data ) - return tostring( Packet.GIOP:new( 0, data ) ) - end, + return tostring( Packet.GIOP:new( 0, data ) ) + end, } Packet.GIOP._is_a = { - --- Creates a new Packet.GIOP._is_a instance - -- - -- @param id the packet identifier - -- @param flags [optional] - -- @param keyaddr string containing the keyaddr data - -- @return obj a new Packet.GIOP._is_a instance - new = function( self, id, flags, key_addr ) - local o = {} - setmetatable(o, self) - self.__index = self - o.op = "_is_a\0" - o.id = id - o.target_addr = 0 -- KeyAddr - o.key_addr = key_addr - o.flags = flags or Constants.SyncScope.WITH_TARGET -- SyncScope WITH_TARGET - o.sc = {} - return o - end, + --- Creates a new Packet.GIOP._is_a instance + -- + -- @param id the packet identifier + -- @param flags [optional] + -- @param keyaddr string containing the keyaddr data + -- @return obj a new Packet.GIOP._is_a instance + new = function( self, id, flags, key_addr ) + local o = {} + setmetatable(o, self) + self.__index = self + o.op = "_is_a\0" + o.id = id + o.target_addr = 0 -- KeyAddr + o.key_addr = key_addr + o.flags = flags or Constants.SyncScope.WITH_TARGET -- SyncScope WITH_TARGET + o.sc = {} + return o + end, - --- Creates and adds a service context to the packet - -- - -- @param id number containing the context id - -- @param data the service context data - -- @param pad [optional] number used to pad after the service context - addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, + --- Creates and adds a service context to the packet + -- + -- @param id number containing the context id + -- @param data the service context data + -- @param pad [optional] number used to pad after the service context + addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function( self ) - local TYPE_ID = "IDL:omg.org/CosNaming/NamingContextExt:1.0\0" - local RESERVED = 0 - local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 0 - local data = bin.pack(">ICCCCSSIAIASI", self.id, self.flags, RESERVED, RESERVED, RESERVED, self.target_addr, - UNKNOWN, #self.key_addr, self.key_addr, #self.op, self.op, UNKNOWN2, #self.sc ) + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function( self ) + local TYPE_ID = "IDL:omg.org/CosNaming/NamingContextExt:1.0\0" + local RESERVED = 0 + local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 0 + local data = bin.pack(">ICCCCSSIAIASI", self.id, self.flags, RESERVED, RESERVED, RESERVED, self.target_addr, + UNKNOWN, #self.key_addr, self.key_addr, #self.op, self.op, UNKNOWN2, #self.sc ) - for i=1, #self.sc do - local tmp = tostring( self.sc[i]) - data = data .. bin.pack("A", tmp ) - end + for i=1, #self.sc do + local tmp = tostring( self.sc[i]) + data = data .. bin.pack("A", tmp ) + end - data = data .. bin.pack(">IA", #TYPE_ID, TYPE_ID) + data = data .. bin.pack(">IA", #TYPE_ID, TYPE_ID) - local packet = Packet.GIOP:new( 0, data ) - packet:setVersion( Constants.VERSION_1_2 ) + local packet = Packet.GIOP:new( 0, data ) + packet:setVersion( Constants.VERSION_1_2 ) - return tostring( packet ) - end, + return tostring( packet ) + end, } Packet.GIOP.list = { - --- Creates a new Packet.GIOP.list instance - -- - -- @param id the packet identifier - -- @param flags [optional] - -- @param keyaddr string containing the keyaddr data - -- @param how_many string containing the value to retrieve - -- @return obj a new Packet.GIOP.list instance - new = function( self, id, flags, keyaddr, how_many ) - local o = {} - setmetatable(o, self) - self.__index = self - o.op = "list\0" - o.id = id - o.flags = flags or Constants.SyncScope.WITH_TARGET - o.target_addr = 0 -- KeyAddr - o.key_addr = keyaddr - o.how_many = how_many or 1000 - o.sc = {} - return o - end, + --- Creates a new Packet.GIOP.list instance + -- + -- @param id the packet identifier + -- @param flags [optional] + -- @param keyaddr string containing the keyaddr data + -- @param how_many string containing the value to retrieve + -- @return obj a new Packet.GIOP.list instance + new = function( self, id, flags, keyaddr, how_many ) + local o = {} + setmetatable(o, self) + self.__index = self + o.op = "list\0" + o.id = id + o.flags = flags or Constants.SyncScope.WITH_TARGET + o.target_addr = 0 -- KeyAddr + o.key_addr = keyaddr + o.how_many = how_many or 1000 + o.sc = {} + return o + end, - --- Creates and adds a service context to the packet - -- - -- @param id number containing the context id - -- @param data the service context data - -- @param pad [optional] number used to pad after the service context - addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, + --- Creates and adds a service context to the packet + -- + -- @param id number containing the context id + -- @param data the service context data + -- @param pad [optional] number used to pad after the service context + addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function( self ) - local RESERVED = 0 - local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 6 + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function( self ) + local RESERVED = 0 + local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 6 - local data = bin.pack(">ICCCCSSIAIACCCI", self.id, self.flags, RESERVED, RESERVED, - RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr, - #self.op, self.op, RESERVED, RESERVED, UNKNOWN2, #self.sc ) + local data = bin.pack(">ICCCCSSIAIACCCI", self.id, self.flags, RESERVED, RESERVED, + RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr, + #self.op, self.op, RESERVED, RESERVED, UNKNOWN2, #self.sc ) - for i=1, #self.sc do - local tmp = tostring( self.sc[i]) - data = data .. bin.pack("A", tmp ) - end + for i=1, #self.sc do + local tmp = tostring( self.sc[i]) + data = data .. bin.pack("A", tmp ) + end - data = data .. bin.pack(">II", UNKNOWN3, self.how_many ) - local packet = Packet.GIOP:new( 0, data ) - packet:setVersion( Constants.VERSION_1_2 ) + data = data .. bin.pack(">II", UNKNOWN3, self.how_many ) + local packet = Packet.GIOP:new( 0, data ) + packet:setVersion( Constants.VERSION_1_2 ) - return tostring( packet ) - end, + return tostring( packet ) + end, } @@ -424,282 +424,282 @@ Packet.GIOP.list = -- reading of an exact number of bytes, instead of atleast ... Socket = { - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.Socket = socket or nmap.new_socket() - o.Buffer = nil - return o - end, + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.Socket = socket or nmap.new_socket() + o.Buffer = nil + return o + end, - getSrcIp = function( self ) - local status, lhost, _, _, _ = self.Socket:get_info() - if (not(status)) then return false, "Error failed to get socket information" end - return true, lhost - end, + getSrcIp = function( self ) + local status, lhost, _, _, _ = self.Socket:get_info() + if (not(status)) then return false, "Error failed to get socket information" end + return true, lhost + end, - --- Establishes a connection. - -- - -- @param hostid Hostname or IP address. - -- @param port Port number. - -- @param protocol "tcp", "udp", or - -- @return Status (true or false). - -- @return Error code (if status is false). - connect = function( self, hostid, port, protocol ) - local status = self.Socket:set_timeout(10000) - return self.Socket:connect( hostid, port, protocol ) - end, + --- Establishes a connection. + -- + -- @param hostid Hostname or IP address. + -- @param port Port number. + -- @param protocol "tcp", "udp", or + -- @return Status (true or false). + -- @return Error code (if status is false). + connect = function( self, hostid, port, protocol ) + local status = self.Socket:set_timeout(10000) + return self.Socket:connect( hostid, port, protocol ) + end, - --- Closes an open connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - close = function( self ) - return self.Socket:close() - end, + --- Closes an open connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + close = function( self ) + return self.Socket:close() + end, - --- Opposed to the socket:receive_bytes function, that returns - -- at least x bytes, this function returns the amount of bytes requested. - -- - -- @param count of bytes to read - -- @return true on success, false on failure - -- @return data containing bytes read from the socket - -- err containing error message if status is false - recv = function( self, count ) - local status, data + --- Opposed to the socket:receive_bytes function, that returns + -- at least x bytes, this function returns the amount of bytes requested. + -- + -- @param count of bytes to read + -- @return true on success, false on failure + -- @return data containing bytes read from the socket + -- err containing error message if status is false + recv = function( self, count ) + local status, data - self.Buffer = self.Buffer or "" + self.Buffer = self.Buffer or "" - if ( #self.Buffer < count ) then - status, data = self.Socket:receive_bytes( count - #self.Buffer ) - if ( not(status) or #data < count - #self.Buffer ) then - return false, data - end - self.Buffer = self.Buffer .. data - end + if ( #self.Buffer < count ) then + status, data = self.Socket:receive_bytes( count - #self.Buffer ) + if ( not(status) or #data < count - #self.Buffer ) then + return false, data + end + self.Buffer = self.Buffer .. data + end - data = self.Buffer:sub( 1, count ) - self.Buffer = self.Buffer:sub( count + 1) + data = self.Buffer:sub( 1, count ) + self.Buffer = self.Buffer:sub( count + 1) - return true, data - end, + return true, data + end, - --- Sends data over the socket - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, data ) - return self.Socket:send( data ) - end, + --- Sends data over the socket + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, data ) + return self.Socket:send( data ) + end, } -- Static class containing various message decoders MessageDecoder = { - --- Decodes a get response - -- - -- @param packet the GIOP packet as recieved by the comm - -- exchGIOPPacket function - -- @return status true on success, false on failure - -- @return table containing ip and ctx - ["get"] = function( packet ) - local bo = ( packet.GIOP.byte_order == 0 and ">" or "<") - local pos, len = bin.unpack(bo .. "I", packet.stub_data) - local ip, ctx + --- Decodes a get response + -- + -- @param packet the GIOP packet as recieved by the comm + -- exchGIOPPacket function + -- @return status true on success, false on failure + -- @return table containing ip and ctx + ["get"] = function( packet ) + local bo = ( packet.GIOP.byte_order == 0 and ">" or "<") + local pos, len = bin.unpack(bo .. "I", packet.stub_data) + local ip, ctx - pos = pos + len + 16 + pos = pos + len + 16 - pos, len = bin.unpack(bo .. "I", packet.stub_data, pos) - pos, ip = bin.unpack( bo .. "A" .. len, packet.stub_data, pos) + pos, len = bin.unpack(bo .. "I", packet.stub_data, pos) + pos, ip = bin.unpack( bo .. "A" .. len, packet.stub_data, pos) - pos = pos + 3 - pos, len = bin.unpack(bo .. "I", packet.stub_data, pos) - pos, ctx = bin.unpack( bo .. "A" .. len, packet.stub_data, pos) + pos = pos + 3 + pos, len = bin.unpack(bo .. "I", packet.stub_data, pos) + pos, ctx = bin.unpack( bo .. "A" .. len, packet.stub_data, pos) - return true, { ip = ip, ctx = ctx} - end, + return true, { ip = ip, ctx = ctx} + end, - --- Decodes a _is_a response (not implemented) - -- - -- @param packet the GIOP packet as recieved by the comm - -- exchGIOPPacket function - -- @return status, always true - ["_is_a"] = function( packet ) - return true - end, + --- Decodes a _is_a response (not implemented) + -- + -- @param packet the GIOP packet as recieved by the comm + -- exchGIOPPacket function + -- @return status, always true + ["_is_a"] = function( packet ) + return true + end, - --- Decodes a list response - -- - -- @param packet the GIOP packet as recieved by the comm - -- exchGIOPPacket function - -- @return status true on success, false on failure - -- @return table containing id, kind and - -- enum or error message if status is false - ["list"] = function( packet ) - local bo = ( packet.GIOP.byte_order == 0 and ">" or "<") - local pos, seq_len = bin.unpack( bo .. "I", packet.data, 7) - local objs = {} + --- Decodes a list response + -- + -- @param packet the GIOP packet as recieved by the comm + -- exchGIOPPacket function + -- @return status true on success, false on failure + -- @return table containing id, kind and + -- enum or error message if status is false + ["list"] = function( packet ) + local bo = ( packet.GIOP.byte_order == 0 and ">" or "<") + local pos, seq_len = bin.unpack( bo .. "I", packet.data, 7) + local objs = {} - for i=1, seq_len do - local seq_len_of_bind_name - local len, name - local obj = {} + for i=1, seq_len do + local seq_len_of_bind_name + local len, name + local obj = {} - pos, seq_len_of_bind_name = bin.unpack( bo .. "I", packet.data, pos) - if ( seq_len_of_bind_name ~= 1 ) then return false, "Sequence length of Binding_binding_name was greater than 1" end + pos, seq_len_of_bind_name = bin.unpack( bo .. "I", packet.data, pos) + if ( seq_len_of_bind_name ~= 1 ) then return false, "Sequence length of Binding_binding_name was greater than 1" end - pos, len = bin.unpack( bo .. "I", packet.data, pos ) - pos, obj.id = bin.unpack( "A" .. len - 1, packet.data, pos ) + pos, len = bin.unpack( bo .. "I", packet.data, pos ) + pos, obj.id = bin.unpack( "A" .. len - 1, packet.data, pos ) - -- Account for terminating zero - pos = pos + 1 + -- Account for terminating zero + pos = pos + 1 - -- Account for undecoded data - pos = pos + ( ( len % 4 > 0 ) and ( 4 - ( len % 4 ) ) or 0 ) - pos = pos + 3 + -- Account for undecoded data + pos = pos + ( ( len % 4 > 0 ) and ( 4 - ( len % 4 ) ) or 0 ) + pos = pos + 3 - pos, obj.kind = bin.unpack("C", packet.data, pos) + pos, obj.kind = bin.unpack("C", packet.data, pos) - -- Account for undecoded data - pos = pos + 4 - pos, obj.enum = bin.unpack( bo .. "I", packet.data, pos ) - table.insert( objs, obj ) - end + -- Account for undecoded data + pos = pos + 4 + pos, obj.enum = bin.unpack( bo .. "I", packet.data, pos ) + table.insert( objs, obj ) + end - return true, objs - end, + return true, objs + end, } Comm = { - --- Creates a new Comm instance - -- - -- @param socket containing a buffered socket connected to the server - -- @return a new Comm instance - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.socket = socket - return o - end, + --- Creates a new Comm instance + -- + -- @param socket containing a buffered socket connected to the server + -- @return a new Comm instance + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.socket = socket + return o + end, - --- Sends and recieves a GIOP packet - -- - -- @param packet containing a Packet.* object, the object must - -- implement the __tostring meta method - -- @return status true on success, false on failure - -- @return data decoder specific data, see the corresponding - -- MessageDecoder for more information. - exchGIOPPacket = function( self, packet ) - local status, err = self.socket:send( tostring(packet) ) - local op = packet.op:sub(1, -2) - local data + --- Sends and recieves a GIOP packet + -- + -- @param packet containing a Packet.* object, the object must + -- implement the __tostring meta method + -- @return status true on success, false on failure + -- @return data decoder specific data, see the corresponding + -- MessageDecoder for more information. + exchGIOPPacket = function( self, packet ) + local status, err = self.socket:send( tostring(packet) ) + local op = packet.op:sub(1, -2) + local data - if( not(status) ) then return false, err end - packet = Packet.GIOP.reply:new() + if( not(status) ) then return false, err end + packet = Packet.GIOP.reply:new() - status, err = packet:recv( self.socket ) - if( not(status) ) then return false, err end + status, err = packet:recv( self.socket ) + if( not(status) ) then return false, err end - if ( MessageDecoder[op] ) then - status, data = MessageDecoder[op]( packet ) - else - return false, ("No message decoder for op (%s)"):format(op) - end + if ( MessageDecoder[op] ) then + status, data = MessageDecoder[op]( packet ) + else + return false, ("No message decoder for op (%s)"):format(op) + end - return status, data - end, + return status, data + end, } Helper = { - new = function(self, host, port ) - local o = {} - setmetatable(o, self) - self.__index = self - o.host = host - o.port = port - o.socket = Socket:new() - return o - end, + new = function(self, host, port ) + local o = {} + setmetatable(o, self) + self.__index = self + o.host = host + o.port = port + o.socket = Socket:new() + return o + end, - GetNamingContext = function( self ) - local packet = Packet.GIOP.get:new( 5, 0x494e4954, bin.pack(">IA", #Constants.NAMESERVICE, Constants.NAMESERVICE) ) - local status, ctx, lhost, pos, len, bo, tmp + GetNamingContext = function( self ) + local packet = Packet.GIOP.get:new( 5, 0x494e4954, bin.pack(">IA", #Constants.NAMESERVICE, Constants.NAMESERVICE) ) + local status, ctx, lhost, pos, len, bo, tmp - packet:addServiceContext( 17, string.char(0x00, 0x02), 0) - packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0) - packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 ) + packet:addServiceContext( 17, string.char(0x00, 0x02), 0) + packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0) + packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 ) - status, packet = self.comm:exchGIOPPacket( packet ) - if( not(status) ) then return status, packet end + status, packet = self.comm:exchGIOPPacket( packet ) + if( not(status) ) then return status, packet end - return true, packet.ctx - end, + return true, packet.ctx + end, - ListObjects = function( self, keyaddr ) - -- SyncScope WITH_TARGET - local packet = Packet.GIOP._is_a:new( 5, Constants.SyncScope.WITH_TARGET, keyaddr ) - local status, err, lhost + ListObjects = function( self, keyaddr ) + -- SyncScope WITH_TARGET + local packet = Packet.GIOP._is_a:new( 5, Constants.SyncScope.WITH_TARGET, keyaddr ) + local status, err, lhost - status, err = self:Reconnect() - if( not(status) ) then return false, err end + status, err = self:Reconnect() + if( not(status) ) then return false, err end - packet:addServiceContext( 17, "\0\2", 0x000d) - packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" ) - packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x5d69) - packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 ) + packet:addServiceContext( 17, "\0\2", 0x000d) + packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" ) + packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x5d69) + packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 ) - status, packet = self.comm:exchGIOPPacket( packet ) - if( not(status) ) then return status, packet end + status, packet = self.comm:exchGIOPPacket( packet ) + if( not(status) ) then return status, packet end - packet = Packet.GIOP.list:new( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, Constants.SyncScope.WITH_TARGET, keyaddr, 1000 ) - packet:addServiceContext( 17, "\0\2", 0x000d) - packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" ) - packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x9c9b) + packet = Packet.GIOP.list:new( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, Constants.SyncScope.WITH_TARGET, keyaddr, 1000 ) + packet:addServiceContext( 17, "\0\2", 0x000d) + packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" ) + packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x9c9b) - status, packet = self.comm:exchGIOPPacket( packet ) - if( not(status) ) then return status, packet end + status, packet = self.comm:exchGIOPPacket( packet ) + if( not(status) ) then return status, packet end - return true, packet - end, + return true, packet + end, - --- Connects and performs protocol negotiation with the Oracle server - -- - -- @return true on success, false on failure - -- @return err containing error message when status is false - Connect = function( self ) - local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) - if( not(status) ) then return status, data end - self.comm = Comm:new( self.socket ) + --- Connects and performs protocol negotiation with the Oracle server + -- + -- @return true on success, false on failure + -- @return err containing error message when status is false + Connect = function( self ) + local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) + if( not(status) ) then return status, data end + self.comm = Comm:new( self.socket ) - status, self.lhost = self.socket:getSrcIp() - if ( not(status) ) then - self.socket:close() - return false, self.lhost - end + status, self.lhost = self.socket:getSrcIp() + if ( not(status) ) then + self.socket:close() + return false, self.lhost + end - return true - end, + return true + end, - Close = function( self ) - return self.socket:close() - end, + Close = function( self ) + return self.socket:close() + end, - Reconnect = function( self ) - local status = self:Close() - if( not(status) ) then return false, "Failed to close socket" end + Reconnect = function( self ) + local status = self:Close() + if( not(status) ) then return false, "Failed to close socket" end - status = self:Connect() - if( not(status) ) then return false, "Failed to re-connect socket" end + status = self:Connect() + if( not(status) ) then return false, "Failed to re-connect socket" end - return true - end, + return true + end, } return _ENV; diff --git a/nselib/ike.lua b/nselib/ike.lua index 4f7d6ee41..b916b0ae9 100644 --- a/nselib/ike.lua +++ b/nselib/ike.lua @@ -12,19 +12,19 @@ description = [[ A very basic IKE library. The current funcionality includes: - 1. Generating a Main or Aggressive Mode IKE request packet with a variable amount of transforms and a vpn group. - 2. Sending a packet - 3. Receiving the response - 4. Parsing the response for VIDs - 5. Searching for the VIDs in 'ike-fingerprints.lua' - 6. returning a parsed info table + 1. Generating a Main or Aggressive Mode IKE request packet with a variable amount of transforms and a vpn group. + 2. Sending a packet + 3. Receiving the response + 4. Parsing the response for VIDs + 5. Searching for the VIDs in 'ike-fingerprints.lua' + 6. returning a parsed info table This library is meant for extension, which could include: - 1. complete parsing of the response packet (might allow for better fingerprinting) - 2. adding more options to the request packet - vendor field (might give better fingerprinting of services, e.g. Checkpoint) - 3. backoff pattern analyses - ... + 1. complete parsing of the response packet (might allow for better fingerprinting) + 2. adding more options to the request packet + vendor field (might give better fingerprinting of services, e.g. Checkpoint) + 3. backoff pattern analyses + ... An a implementation resembling 'ike-scan' could be built. ]] @@ -37,64 +37,64 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"discovery", "safe"} local ENC_METHODS = { - ["des"] = 0x80010001, - ["3des"] = 0x80010005, - ["cast"] = 0x80010006, - ["aes/128"] = { 0x80010007, 0x800E0080 }, - ["aes/192"] = { 0x80010007, 0x800E00C0 }, - ["aes/256"] = { 0x80010007, 0x800E0100 }, + ["des"] = 0x80010001, + ["3des"] = 0x80010005, + ["cast"] = 0x80010006, + ["aes/128"] = { 0x80010007, 0x800E0080 }, + ["aes/192"] = { 0x80010007, 0x800E00C0 }, + ["aes/256"] = { 0x80010007, 0x800E0100 }, } local AUTH_TYPES = { - ["psk"] = 0x80030001, - ["rsa"] = 0x80030003, - ["ECDSA"] = 0x80030008, - ["Hybrid"] = 0x8003FADD, - ["XAUTH"] = 0x8003FDE9, + ["psk"] = 0x80030001, + ["rsa"] = 0x80030003, + ["ECDSA"] = 0x80030008, + ["Hybrid"] = 0x8003FADD, + ["XAUTH"] = 0x8003FDE9, } local HASH_ALGORITHM = { - ["md5"] = 0x80020001, - ["sha1"] = 0x80020002, - ["sha2-256"] = 0x80020004, - ["sha2-384"] = 0x80020005, - ["sha2-512"] = 0x80020006, + ["md5"] = 0x80020001, + ["sha1"] = 0x80020002, + ["sha2-256"] = 0x80020004, + ["sha2-384"] = 0x80020005, + ["sha2-512"] = 0x80020006, } local GROUP_DESCRIPTION = { - ["768"] = 0x80040001, - ["1024"] = 0x80040002, - ["1536"] = 0x80040005, - ["2048"] = 0x0004000E, + ["768"] = 0x80040001, + ["1024"] = 0x80040002, + ["1536"] = 0x80040005, + ["2048"] = 0x0004000E, } local EXCHANGE_MODE = { - ["Main"] = 0x02, - ["Aggressive"] = 0x04, + ["Main"] = 0x02, + ["Aggressive"] = 0x04, } local PROTOCOL_IDS = { - ["tcp"] = "06", - ["udp"] = "11", + ["tcp"] = "06", + ["udp"] = "11", } -- Response packet types local EXCHANGE_TYPE = { - ["02"] = "Main", - ["04"] = "Aggressive", - ["05"] = "Informational", + ["02"] = "Main", + ["04"] = "Aggressive", + ["05"] = "Informational", } -- Payload names local PAYLOADS = { - ["00"] = "None", - ["01"] = "SA", - ["03"] = "Transform", - ["04"] = "Key Exchange", - ["05"] = "ID", - ["08"] = "Hash", - ["0A"] = "Nonce", - ["0D"] = "VID", + ["00"] = "None", + ["01"] = "SA", + ["03"] = "Transform", + ["04"] = "Key Exchange", + ["05"] = "ID", + ["08"] = "Hash", + ["0A"] = "Nonce", + ["0D"] = "VID", } @@ -102,98 +102,98 @@ local PAYLOADS = { -- (located in: nselib/data/ike-fingerprints.lua) -- local function load_fingerprints() - local file, filename_full, fingerprints + local file, filename_full, fingerprints - -- Check if fingerprints are cached - if(nmap.registry.ike_fingerprints ~= nil) then - stdnse.print_debug(1, "ike: Loading cached fingerprints") - return nmap.registry.ike_fingerprints - end + -- Check if fingerprints are cached + if(nmap.registry.ike_fingerprints ~= nil) then + stdnse.print_debug(1, "ike: Loading cached fingerprints") + return nmap.registry.ike_fingerprints + end - -- Try and find the file - -- If it isn't in Nmap's directories, take it as a direct path - filename_full = nmap.fetchfile('nselib/data/ike-fingerprints.lua') + -- Try and find the file + -- If it isn't in Nmap's directories, take it as a direct path + filename_full = nmap.fetchfile('nselib/data/ike-fingerprints.lua') - -- Load the file - stdnse.print_debug(1, "ike: Loading fingerprints: %s", filename_full) - local env = setmetatable({fingerprints = {}}, {__index = _G}); - file = loadfile(filename_full, "t", env) - if( not(file) ) then - stdnse.print_debug(1, "ike: Couldn't load the file: %s", filename_full) - return false, "Couldn't load fingerprint file: " .. filename_full - end - file() - fingerprints = env.fingerprints + -- Load the file + stdnse.print_debug(1, "ike: Loading fingerprints: %s", filename_full) + local env = setmetatable({fingerprints = {}}, {__index = _G}); + file = loadfile(filename_full, "t", env) + if( not(file) ) then + stdnse.print_debug(1, "ike: Couldn't load the file: %s", filename_full) + return false, "Couldn't load fingerprint file: " .. filename_full + end + file() + fingerprints = env.fingerprints - -- Check there are fingerprints to use - if(#fingerprints == 0 ) then - return false, "No fingerprints were loaded after processing ".. filename_full - end + -- Check there are fingerprints to use + if(#fingerprints == 0 ) then + return false, "No fingerprints were loaded after processing ".. filename_full + end - return true, fingerprints + return true, fingerprints end -- generate a random hex-string of length 'length' -- local function generate_random(length) - local rnd = "" + local rnd = "" - for i=1, length do - rnd = rnd .. string.format("%.2X", math.random(255)) - end - return rnd + for i=1, length do + rnd = rnd .. string.format("%.2X", math.random(255)) + end + return rnd end -- convert a string to a hex-string (of the ASCII representation) -- local function convert_to_hex(id) - local hex_str = "" - for c in string.gmatch(id, ".") do - hex_str = hex_str .. string.format("%X", c:byte()) - end - return hex_str + local hex_str = "" + for c in string.gmatch(id, ".") do + hex_str = hex_str .. string.format("%X", c:byte()) + end + return hex_str end -- Extract Payloads local function extract_payloads(packet) - -- packet only contains HDR - if packet:len() < 61 then return {} end + -- packet only contains HDR + if packet:len() < 61 then return {} end - local np = packet:sub(33,34) -- next payload - local index = 61 -- starting point for search - local ike_headers = {} -- ike headers - local payload = '' + local np = packet:sub(33,34) -- next payload + local index = 61 -- starting point for search + local ike_headers = {} -- ike headers + local payload = '' - -- loop over packet - while PAYLOADS[np] ~= "None" and index <= packet:len() do - local payload_length = tonumber("0x"..packet:sub(index, index+3)) * 2 - payload = string.lower(packet:sub(index+4, index+payload_length-5)) + -- loop over packet + while PAYLOADS[np] ~= "None" and index <= packet:len() do + local payload_length = tonumber("0x"..packet:sub(index, index+3)) * 2 + payload = string.lower(packet:sub(index+4, index+payload_length-5)) - -- debug - if PAYLOADS[np] == 'VID' then - stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s - %s', np, PAYLOADS[np], payload) - else - stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s', np, PAYLOADS[np]) - end + -- debug + if PAYLOADS[np] == 'VID' then + stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s - %s', np, PAYLOADS[np], payload) + else + stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s', np, PAYLOADS[np]) + end - -- Store payload - if ike_headers[PAYLOADS[np]] == nil then - ike_headers[PAYLOADS[np]] = {payload} - else - table.insert(ike_headers[PAYLOADS[np]], payload) - end + -- Store payload + if ike_headers[PAYLOADS[np]] == nil then + ike_headers[PAYLOADS[np]] = {payload} + else + table.insert(ike_headers[PAYLOADS[np]], payload) + end - -- find the next payload type - np = packet:sub(index-4, index-3) + -- find the next payload type + np = packet:sub(index-4, index-3) - -- jump to the next payload - index = index + payload_length - end - return ike_headers + -- jump to the next payload + index = index + payload_length + end + return ike_headers end @@ -201,152 +201,152 @@ end -- Search the fingerprint database for matches --- This is a (currently) divided into two parts --- 1) version detection based on single fingerprints --- 2) version detection based on the order of all vendor ids +-- This is a (currently) divided into two parts +-- 1) version detection based on single fingerprints +-- 2) version detection based on the order of all vendor ids -- --- NOTE: the second step currently only has support for CISCO devices +-- NOTE: the second step currently only has support for CISCO devices -- -- Input is a table of collected vendor-ids, output is a table -- with fields: --- vendor, version, name, attributes (table), guess (table), os +-- vendor, version, name, attributes (table), guess (table), os local function lookup(vendor_ids) - if vendor_ids == {} or vendor_ids == nil then return {} end + if vendor_ids == {} or vendor_ids == nil then return {} end - -- concat all vids to one string - local all_vids = '' - for _,vid in pairs(vendor_ids) do all_vids = all_vids .. vid end + -- concat all vids to one string + local all_vids = '' + for _,vid in pairs(vendor_ids) do all_vids = all_vids .. vid end - -- the results - local info = { - vendor = nil, - attribs = {}, - } + -- the results + local info = { + vendor = nil, + attribs = {}, + } - local status, fingerprints - status, fingerprints = load_fingerprints() + local status, fingerprints + status, fingerprints = load_fingerprints() - if status then + if status then - -- loop over the vendor_ids returned in ike request - for _,vendor_id in pairs(vendor_ids) do + -- loop over the vendor_ids returned in ike request + for _,vendor_id in pairs(vendor_ids) do - -- loop over the fingerprints found in database - for _,row in pairs(fingerprints) do + -- loop over the fingerprints found in database + for _,row in pairs(fingerprints) do - if vendor_id:find(row.fingerprint) then + if vendor_id:find(row.fingerprint) then - -- if a match is found, check if it's a version detection or attribute - if row.category == 'vendor' then - local debug_string = '' - if row.vendor ~= nil then debug_string = debug_string .. row.vendor .. ' ' end - if row.version ~= nil then debug_string = debug_string .. row.version end - stdnse.print_debug(2, "IKE: Fingerprint: %s matches %s", vendor_id, debug_string) + -- if a match is found, check if it's a version detection or attribute + if row.category == 'vendor' then + local debug_string = '' + if row.vendor ~= nil then debug_string = debug_string .. row.vendor .. ' ' end + if row.version ~= nil then debug_string = debug_string .. row.version end + stdnse.print_debug(2, "IKE: Fingerprint: %s matches %s", vendor_id, debug_string) - -- Only store the first match - if info.vendor == nil then - -- the fingerprint contains information about the VID - info.vendor = row - end + -- Only store the first match + if info.vendor == nil then + -- the fingerprint contains information about the VID + info.vendor = row + end - elseif row.category == 'attribute' then - info.attribs[ #info.attribs + 1] = row - stdnse.print_debug(2, "IKE: Attribute: %s matches %s", vendor_id, row.text) - break - end - end - end - end - end + elseif row.category == 'attribute' then + info.attribs[ #info.attribs + 1] = row + stdnse.print_debug(2, "IKE: Attribute: %s matches %s", vendor_id, row.text) + break + end + end + end + end + end - --------------------------------------------------- - -- Search for the order of the vids - -- Uses category 'vid_ordering' - --- + --------------------------------------------------- + -- Search for the order of the vids + -- Uses category 'vid_ordering' + --- - -- search in the 'vid_ordering' category - local debug_string = '' - for _,row in pairs(fingerprints) do + -- search in the 'vid_ordering' category + local debug_string = '' + for _,row in pairs(fingerprints) do - if row.category == 'vid_ordering' and all_vids:find(row.fingerprint) then + if row.category == 'vid_ordering' and all_vids:find(row.fingerprint) then - -- Use ordering information if there where no vendor matches from prevoius step - if info.vendor == nil then - info.vendor = row + -- Use ordering information if there where no vendor matches from prevoius step + if info.vendor == nil then + info.vendor = row - -- Debugging info - debug_string = '' - if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' ' end - if info.vendor.version ~= nil then debug_string = debug_string .. info.vendor.version .. ' ' end - if info.vendor.ostype ~= nil then debug_string = debug_string .. info.vendor.ostype end - stdnse.print_debug(2, 'IKE: No vendor match, but ordering match found: %s', debug_string) + -- Debugging info + debug_string = '' + if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' ' end + if info.vendor.version ~= nil then debug_string = debug_string .. info.vendor.version .. ' ' end + if info.vendor.ostype ~= nil then debug_string = debug_string .. info.vendor.ostype end + stdnse.print_debug(2, 'IKE: No vendor match, but ordering match found: %s', debug_string) - return info + return info - -- Update OS based on ordering - elseif info.vendor.vendor == row.vendor then - info.vendor.ostype = row.ostype + -- Update OS based on ordering + elseif info.vendor.vendor == row.vendor then + info.vendor.ostype = row.ostype - -- Debugging info - debug_string = '' - if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' to ' end - if row.ostype ~= nil then debug_string = debug_string .. row.ostype end - stdnse.print_debug(2, 'IKE: Vendor and ordering match. OS updated: %s', debug_string) + -- Debugging info + debug_string = '' + if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' to ' end + if row.ostype ~= nil then debug_string = debug_string .. row.ostype end + stdnse.print_debug(2, 'IKE: Vendor and ordering match. OS updated: %s', debug_string) - return info + return info - -- Only print debugging information if conflicting information is detected - else - -- Debugging info - debug_string = '' - if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' vs ' end - if row.vendor ~= nil then debug_string = debug_string .. row.vendor end - stdnse.print_debug(2, 'IKE: Found an ordering match, but vendors do not match. %s', debug_string) + -- Only print debugging information if conflicting information is detected + else + -- Debugging info + debug_string = '' + if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' vs ' end + if row.vendor ~= nil then debug_string = debug_string .. row.vendor end + stdnse.print_debug(2, 'IKE: Found an ordering match, but vendors do not match. %s', debug_string) - end - end - end + end + end + end - return info + return info end -- Handle a response packet --- A very limited response parser --- Currently only the VIDs are extracted --- This could be made more advanced to --- allow for fingerprinting via the order --- of the returned headers +-- A very limited response parser +-- Currently only the VIDs are extracted +-- This could be made more advanced to +-- allow for fingerprinting via the order +-- of the returned headers --- function response(packet) - local resp = { ["mode"] = "", ["info"] = nil, ['vids']={}, ['success'] = false } + local resp = { ["mode"] = "", ["info"] = nil, ['vids']={}, ['success'] = false } - if packet:len() > 38 then + if packet:len() > 38 then - -- extract the return type - local resp_type = EXCHANGE_TYPE[packet:sub(37,38)] - local ike_headers = {} + -- extract the return type + local resp_type = EXCHANGE_TYPE[packet:sub(37,38)] + local ike_headers = {} - -- simple check that the type is something other than 'Informational' - -- as this type does not include VIDs - if resp_type ~= "Informational" then - resp["mode"] = resp_type + -- simple check that the type is something other than 'Informational' + -- as this type does not include VIDs + if resp_type ~= "Informational" then + resp["mode"] = resp_type - ike_headers = extract_payloads(packet) + ike_headers = extract_payloads(packet) - -- Extract the VIDs - resp['vids'] = ike_headers['VID'] + -- Extract the VIDs + resp['vids'] = ike_headers['VID'] - -- search for fingerprints - resp["info"] = lookup(resp['vids']) + -- search for fingerprints + resp["info"] = lookup(resp['vids']) - -- indicate that a packet 'useful' packet was returned - resp['success'] = true - end - end + -- indicate that a packet 'useful' packet was returned + resp['success'] = true + end + end - return resp + return resp end @@ -356,212 +356,212 @@ end -- function send_request( host, port, packet ) - local socket = nmap.new_socket() - local s_status, r_status, data, i, hexstring, _ + local socket = nmap.new_socket() + local s_status, r_status, data, i, hexstring, _ - -- lock resource (port 500/udp) - local mutex = nmap.mutex("ike_port_500"); - mutex "lock"; + -- lock resource (port 500/udp) + local mutex = nmap.mutex("ike_port_500"); + mutex "lock"; - -- send the request packet - socket:set_timeout(1000) - socket:bind(nil, port.number) - socket:connect(host, port, "udp") - s_status,_ = socket:send(packet) + -- send the request packet + socket:set_timeout(1000) + socket:bind(nil, port.number) + socket:connect(host, port, "udp") + s_status,_ = socket:send(packet) - -- receive answer - if s_status then - r_status, data = socket:receive_lines(1) + -- receive answer + if s_status then + r_status, data = socket:receive_lines(1) - if r_status then - i, hexstring = bin.unpack("H" .. data:len(), data) - socket:close() + if r_status then + i, hexstring = bin.unpack("H" .. data:len(), data) + socket:close() - -- release mutex - mutex "done"; - return response(hexstring) - else - socket:close() - end - else - socket:close() - end + -- release mutex + mutex "done"; + return response(hexstring) + else + socket:close() + end + else + socket:close() + end - -- release mutex - mutex "done"; + -- release mutex + mutex "done"; - return {} + return {} end -- Create the aggressive part of a packet --- Aggressive mode includes the user-id, so the --- length of this has to be taken into account +-- Aggressive mode includes the user-id, so the +-- length of this has to be taken into account -- local function generate_aggressive(port, protocol, id, diffie) - local hex_port = string.format("%.4X", port) - local hex_prot = PROTOCOL_IDS[protocol] - local id_len = string.format("%.4X", 8 + id:len()) + local hex_port = string.format("%.4X", port) + local hex_prot = PROTOCOL_IDS[protocol] + local id_len = string.format("%.4X", 8 + id:len()) - -- get length of key data based on diffie - local key_length - if diffie == 1 then - key_length = 96 - elseif diffie == 2 then - key_length = 128 - elseif diffie == 5 then - key_length = 192 - end + -- get length of key data based on diffie + local key_length + if diffie == 1 then + key_length = 96 + elseif diffie == 2 then + key_length = 128 + elseif diffie == 5 then + key_length = 192 + end - return bin.pack(">SHHSSHSHCHHH", - -- Key Exchange - 0x0a00 , -- Next payload (Nonce) - string.format("%04X", key_length+4) , -- Length (132-bit) - generate_random(key_length) , -- Random key data + return bin.pack(">SHHSSHSHCHHH", + -- Key Exchange + 0x0a00, -- Next payload (Nonce) + string.format("%04X", key_length+4), -- Length (132-bit) + generate_random(key_length), -- Random key data - -- Nonce - 0x0500 , -- Next payload (Identification) - 0x0018 , -- Length (24) - generate_random(20) , -- Nonce data + -- Nonce + 0x0500, -- Next payload (Identification) + 0x0018, -- Length (24) + generate_random(20), -- Nonce data - -- Identification - 0x0000 , -- Next Payload (None) - id_len , -- Payload length (id + 8) - 0x03 , -- ID Type (USER_FQDN) - hex_prot , -- Protocol ID (UDP) - hex_port , -- Port (500) - convert_to_hex(id) -- Id Data (as hex) - ) + -- Identification + 0x0000, -- Next Payload (None) + id_len, -- Payload length (id + 8) + 0x03, -- ID Type (USER_FQDN) + hex_prot, -- Protocol ID (UDP) + hex_port, -- Port (500) + convert_to_hex(id) -- Id Data (as hex) + ) end -- Create the transform --- AES encryption needs an extra value to define the key length --- Currently only DES, 3DES and AES encryption is supported +-- AES encryption needs an extra value to define the key length +-- Currently only DES, 3DES and AES encryption is supported -- local function generate_transform(auth, encryption, hash, group, number, total) - local key_length, trans_length, aes_enc, sep, enc - local next_payload, payload_number + local key_length, trans_length, aes_enc, sep, enc + local next_payload, payload_number - -- handle special case of aes - if encryption:sub(1,3) == "aes" then - trans_length = 0x0028 - enc = ENC_METHODS[encryption][1] - key_length = ENC_METHODS[encryption][2] - else - trans_length = 0x0024 - enc = ENC_METHODS[encryption] - key_length = nil - end + -- handle special case of aes + if encryption:sub(1,3) == "aes" then + trans_length = 0x0028 + enc = ENC_METHODS[encryption][1] + key_length = ENC_METHODS[encryption][2] + else + trans_length = 0x0024 + enc = ENC_METHODS[encryption] + key_length = nil + end - -- check if there are more transforms - if number == total then - next_payload = 0x0000 -- none - else - next_payload = 0x0300 -- transform - end + -- check if there are more transforms + if number == total then + next_payload = 0x0000 -- none + else + next_payload = 0x0300 -- transform + end - -- set the payload number - payload_number = string.format("%.2X", number) + -- set the payload number + payload_number = string.format("%.2X", number) - local trans = bin.pack(">SSHCSIIII", - next_payload , -- Next payload - trans_length , -- Transform length - payload_number , -- Transform number - 0x01 , -- Transform ID (IKE) - 0x0000 , -- spacers ? - enc , -- Encryption algorithm - HASH_ALGORITHM[hash] , -- Hash algorithm - AUTH_TYPES[auth] , -- Authentication method - GROUP_DESCRIPTION[group] -- Group Description - ) + local trans = bin.pack(">SSHCSIIII", + next_payload, -- Next payload + trans_length, -- Transform length + payload_number, -- Transform number + 0x01, -- Transform ID (IKE) + 0x0000, -- spacers ? + enc, -- Encryption algorithm + HASH_ALGORITHM[hash], -- Hash algorithm + AUTH_TYPES[auth], -- Authentication method + GROUP_DESCRIPTION[group] -- Group Description + ) - if key_length ~= nil then - trans = trans .. bin.pack(">I", key_length) -- only set for aes - end + if key_length ~= nil then + trans = trans .. bin.pack(">I", key_length) -- only set for aes + end - trans = trans .. bin.pack(">IL", - 0x800b0001 , -- Life type (seconds) - 0x000c000400007080 -- Life duration (28800) - ) + trans = trans .. bin.pack(">IL", + 0x800b0001, -- Life type (seconds) + 0x000c000400007080 -- Life duration (28800) + ) - return trans + return trans end -- Generate multiple transforms --- Input nust be a table of complete transforms +-- Input nust be a table of complete transforms -- local function generate_transforms(transform_table) - local transforms = '' + local transforms = '' - for i,t in pairs(transform_table) do - transforms = transforms .. generate_transform(t.auth, t.encryption, t.hash, t.group, i, #transform_table) - end + for i,t in pairs(transform_table) do + transforms = transforms .. generate_transform(t.auth, t.encryption, t.hash, t.group, i, #transform_table) + end - return transforms + return transforms end -- Create a request packet --- Support for multiple transforms, which minimizes the --- the amount of traffic/packets needed to be sendt +-- Support for multiple transforms, which minimizes the +-- the amount of traffic/packets needed to be sendt -- function request(port, proto, mode, transforms, diffie, id) - local payload_after_sa, str_aggressive, l, l_sa, l_pro - local number_transforms, transform_string + local payload_after_sa, str_aggressive, l, l_sa, l_pro + local number_transforms, transform_string - transform_string = generate_transforms(transforms) - number_transforms = string.format("%.2X", #transforms) + transform_string = generate_transforms(transforms) + number_transforms = string.format("%.2X", #transforms) - -- check for aggressive vs Main mode - if mode == "Aggressive" then - str_aggressive = generate_aggressive(port, proto, id, diffie) - payload_after_sa = 0x0400 - else - str_aggressive = "" - payload_after_sa = 0x0000 - end + -- check for aggressive vs Main mode + if mode == "Aggressive" then + str_aggressive = generate_aggressive(port, proto, id, diffie) + payload_after_sa = 0x0400 + else + str_aggressive = "" + payload_after_sa = 0x0000 + end - -- calculate lengths - l = string.format("%.8X", 48 + transform_string:len() + str_aggressive:len()) - l_sa = string.format("%.4X", 20 + transform_string:len()) - l_pro = string.format("%.4X", 8 + transform_string:len()) + -- calculate lengths + l = string.format("%.8X", 48 + transform_string:len() + str_aggressive:len()) + l_sa = string.format("%.4X", 20 + transform_string:len()) + l_pro = string.format("%.4X", 8 + transform_string:len()) - -- Build the packet - local packet = bin.pack(">HLCCCCIHSHIISHCCCH", - generate_random(8) , -- Initiator cookie - 0x0000000000000000 , -- Responder cookie - 0x01 , -- Next payload (SA) - 0x10 , -- Version - EXCHANGE_MODE[mode] , -- Exchange type - 0x00 , -- Flags - 0x00000000 , -- Message id - l , -- packet length + -- Build the packet + local packet = bin.pack(">HLCCCCIHSHIISHCCCH", + generate_random(8), -- Initiator cookie + 0x0000000000000000, -- Responder cookie + 0x01, -- Next payload (SA) + 0x10, -- Version + EXCHANGE_MODE[mode], -- Exchange type + 0x00, -- Flags + 0x00000000, -- Message id + l, -- packet length - -- Security Association - payload_after_sa , -- Next payload (Key exchange, if aggressive mode) - l_sa , -- Length - 0x00000001 , -- IPSEC - 0x00000001 , -- Situation + -- Security Association + payload_after_sa, -- Next payload (Key exchange, if aggressive mode) + l_sa, -- Length + 0x00000001, -- IPSEC + 0x00000001, -- Situation - --## Proposal - 0x0000 , -- Next payload (None) - l_pro , -- Payload length - 0x01 , -- Proposal number - 0x01 , -- Protocol ID (ISAKMP) - 0x00 , -- SPI Size - number_transforms -- Proposal transforms - ) + --## Proposal + 0x0000, -- Next payload (None) + l_pro, -- Payload length + 0x01, -- Proposal number + 0x01, -- Protocol ID (ISAKMP) + 0x00, -- SPI Size + number_transforms -- Proposal transforms + ) - packet = packet .. transform_string -- transform + packet = packet .. transform_string -- transform - if mode == 'Aggressive' then - packet = packet .. str_aggressive - end + if mode == 'Aggressive' then + packet = packet .. str_aggressive + end - return packet + return packet end diff --git a/nselib/informix.lua b/nselib/informix.lua index e533e25fb..2ea2deeca 100644 --- a/nselib/informix.lua +++ b/nselib/informix.lua @@ -14,7 +14,7 @@ -- The library contains the following classes: -- -- o Packet.* --- - The Packet classes contain specific packets and function to serialize +-- - The Packet classes contain specific packets and function to serialize -- them to strings that can be sent over the wire. Each class may also -- contain a function to parse the servers response. -- @@ -22,11 +22,11 @@ -- - A class holding the meta data for each column -- -- o Comm --- - Implements a number of functions to handle communication over the +-- - Implements a number of functions to handle communication over the -- the Socket class. -- --- o Helper --- - A helper class that provides easy access to the rest of the library +-- o Helper +-- - A helper class that provides easy access to the rest of the library -- -- o Socket -- - This is a copy of the DB2Socket class which provides fundamental @@ -49,10 +49,10 @@ -- to interface the library: -- -- --- helper = informix.Helper:new( host, port, "on_demo" ) --- status, err = helper:Connect() --- status, res = helper:Login("informix", "informix") --- status, err = helper:Close() +-- helper = informix.Helper:new( host, port, "on_demo" ) +-- status, err = helper:Connect() +-- status, res = helper:Login("informix", "informix") +-- status, err = helper:Close() -- -- -- Additional information @@ -72,7 +72,7 @@ -- Version 0.1 -- Created 07/23/2010 - v0.1 - created by Patrik Karlsson -- Revised 07/28/2010 - v0.2 - added support for SELECT, INSERT and UPDATE --- queries +-- queries -- local bin = require "bin" @@ -84,167 +84,167 @@ _ENV = stdnse.module("informix", stdnse.seeall) -- A bunch of constants Constants = { - -- A subset of supported messages - Message = { - SQ_COMMAND = 0x01, - SQ_PREPARE = 0x02, - SQ_ID = 0x04, - SQ_DESCRIBE = 0x08, - SQ_EOT = 0x0c, - SQ_ERR = 0x0d, - SQ_TUPLE = 0x0e, - SQ_DONE = 0x0f, - SQ_DBLIST = 0x1a, - SQ_DBOPEN = 0x24, - SQ_EXIT = 0x38, - SQ_INFO = 0x51, - SQ_PROTOCOLS = 0x7e, - }, + -- A subset of supported messages + Message = { + SQ_COMMAND = 0x01, + SQ_PREPARE = 0x02, + SQ_ID = 0x04, + SQ_DESCRIBE = 0x08, + SQ_EOT = 0x0c, + SQ_ERR = 0x0d, + SQ_TUPLE = 0x0e, + SQ_DONE = 0x0f, + SQ_DBLIST = 0x1a, + SQ_DBOPEN = 0x24, + SQ_EXIT = 0x38, + SQ_INFO = 0x51, + SQ_PROTOCOLS = 0x7e, + }, - -- A subset of supported data types - DataType = { - CHAR = 0x00, - SMALLINT = 0x01, - INT = 0x02, - FLOAT = 0x03, - SERIAL = 0x06, - DATE = 0x07, - DATETIME = 0x0a, - VARCHAR = 0x0d, - }, + -- A subset of supported data types + DataType = { + CHAR = 0x00, + SMALLINT = 0x01, + INT = 0x02, + FLOAT = 0x03, + SERIAL = 0x06, + DATE = 0x07, + DATETIME = 0x0a, + VARCHAR = 0x0d, + }, - -- These were the ones I ran into when developing :-) - ErrorMsg = { - [-201] = "A syntax error has occurred.", - [-206] = "The specified table is not in the database.", - [-208] = "Memory allocation failed during query processing.", - [-258] = "System error - invalid statement id received by the sqlexec process.", - [-217] = "Column (%s) not found in any table in the query (or SLV is undefined).", - [-310] = "Table (%s) already exists in database.", - [-363] = "CURSOR not on SELECT statement.", - [-555] = "Cannot use a select or any of the database statements in a multi-query prepare.", - [-664] = "Wrong number of arguments to system function(%s).", - [-761] = "INFORMIXSERVER does not match either DBSERVERNAME or DBSERVERALIASES.", - [-951] = "Incorrect password or user is not known on the database server.", - [-329] = "Database not found or no system permission.", - [-9628] = "Type (%s) not found.", - [-23101] = "Unable to load locale categories.", - } + -- These were the ones I ran into when developing :-) + ErrorMsg = { + [-201] = "A syntax error has occurred.", + [-206] = "The specified table is not in the database.", + [-208] = "Memory allocation failed during query processing.", + [-258] = "System error - invalid statement id received by the sqlexec process.", + [-217] = "Column (%s) not found in any table in the query (or SLV is undefined).", + [-310] = "Table (%s) already exists in database.", + [-363] = "CURSOR not on SELECT statement.", + [-555] = "Cannot use a select or any of the database statements in a multi-query prepare.", + [-664] = "Wrong number of arguments to system function(%s).", + [-761] = "INFORMIXSERVER does not match either DBSERVERNAME or DBSERVERALIASES.", + [-951] = "Incorrect password or user is not known on the database server.", + [-329] = "Database not found or no system permission.", + [-9628] = "Type (%s) not found.", + [-23101] = "Unable to load locale categories.", + } } -- A socket implementation that provides fundamental buffering and allows for -- reading of an exact number of bytes, instead of atleast ... Socket = { - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.Socket = socket or nmap.new_socket() - o.Buffer = nil - return o - end, + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.Socket = socket or nmap.new_socket() + o.Buffer = nil + return o + end, - --- Establishes a connection. - -- - -- @param hostid Hostname or IP address. - -- @param port Port number. - -- @param protocol "tcp", "udp", or - -- @return Status (true or false). - -- @return Error code (if status is false). - connect = function( self, hostid, port, protocol ) - -- Some Informix server seem to take a LOT of time to respond?! - local status = self.Socket:set_timeout(20000) - return self.Socket:connect( hostid, port, protocol ) - end, + --- Establishes a connection. + -- + -- @param hostid Hostname or IP address. + -- @param port Port number. + -- @param protocol "tcp", "udp", or + -- @return Status (true or false). + -- @return Error code (if status is false). + connect = function( self, hostid, port, protocol ) + -- Some Informix server seem to take a LOT of time to respond?! + local status = self.Socket:set_timeout(20000) + return self.Socket:connect( hostid, port, protocol ) + end, - --- Closes an open connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - close = function( self ) - return self.Socket:close() - end, + --- Closes an open connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + close = function( self ) + return self.Socket:close() + end, - --- Opposed to the socket:receive_bytes function, that returns - -- at least x bytes, this function returns the amount of bytes requested. - -- - -- @param count of bytes to read - -- @return true on success, false on failure - -- @return data containing bytes read from the socket - -- err containing error message if status is false - recv = function( self, count ) - local status, data + --- Opposed to the socket:receive_bytes function, that returns + -- at least x bytes, this function returns the amount of bytes requested. + -- + -- @param count of bytes to read + -- @return true on success, false on failure + -- @return data containing bytes read from the socket + -- err containing error message if status is false + recv = function( self, count ) + local status, data - self.Buffer = self.Buffer or "" + self.Buffer = self.Buffer or "" - if ( #self.Buffer < count ) then - status, data = self.Socket:receive_bytes( count - #self.Buffer ) - if ( not(status) or #data < count - #self.Buffer ) then - return false, data - end - self.Buffer = self.Buffer .. data - end + if ( #self.Buffer < count ) then + status, data = self.Socket:receive_bytes( count - #self.Buffer ) + if ( not(status) or #data < count - #self.Buffer ) then + return false, data + end + self.Buffer = self.Buffer .. data + end - data = self.Buffer:sub( 1, count ) - self.Buffer = self.Buffer:sub( count + 1) + data = self.Buffer:sub( 1, count ) + self.Buffer = self.Buffer:sub( count + 1) - return true, data - end, + return true, data + end, - --- Sends data over the socket - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, data ) - return self.Socket:send( data ) - end, + --- Sends data over the socket + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, data ) + return self.Socket:send( data ) + end, } -- The ColMetaData class ColMetaData = { - ---Creates a new ColMetaData instance - -- - -- @return object a new instance of ColMetaData - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + ---Creates a new ColMetaData instance + -- + -- @return object a new instance of ColMetaData + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Sets the datatype - -- - -- @param typ number containing the datatype - setType = function( self, typ ) self.type = typ end, + --- Sets the datatype + -- + -- @param typ number containing the datatype + setType = function( self, typ ) self.type = typ end, - --- Sets the name - -- - -- @param name string containing the name - setName = function( self, name) self.name = name end, + --- Sets the name + -- + -- @param name string containing the name + setName = function( self, name) self.name = name end, - --- Sets the length - -- - -- @param len number containing the length of the column - setLength = function( self, len ) self.len = len end, + --- Sets the length + -- + -- @param len number containing the length of the column + setLength = function( self, len ) self.len = len end, - --- Gets the column type - -- - -- @return typ the column type - getType = function( self ) return self.type end, + --- Gets the column type + -- + -- @return typ the column type + getType = function( self ) return self.type end, - --- Gets the column name - -- - -- @return name the column name - getName = function( self ) return self.name end, + --- Gets the column name + -- + -- @return name the column name + getName = function( self ) return self.name end, - --- Gets the column length - -- - -- @return len the column length - getLength = function( self ) return self.len end, + --- Gets the column length + -- + -- @return len the column length + getLength = function( self ) return self.len end, } Packet = {} @@ -255,77 +255,77 @@ Packet = {} -- The decoders, should be self explanatory MetaDataDecoders = { - [Constants.DataType.INT] = function( data ) - local col_md = ColMetaData:new( ) - local pos = 19 + [Constants.DataType.INT] = function( data ) + local col_md = ColMetaData:new( ) + local pos = 19 - if ( #data < pos ) then return false, "Failed to decode meta data for data type INT" end + if ( #data < pos ) then return false, "Failed to decode meta data for data type INT" end - local _, len = bin.unpack(">S", data, pos) - col_md:setLength(len) - col_md:setType( Constants.DataType.INT ) + local _, len = bin.unpack(">S", data, pos) + col_md:setLength(len) + col_md:setType( Constants.DataType.INT ) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.CHAR] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then - return false, "Failed to decode metadata for data type CHAR" - end - col_md:setType( Constants.DataType.CHAR ) + [Constants.DataType.CHAR] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then + return false, "Failed to decode metadata for data type CHAR" + end + col_md:setType( Constants.DataType.CHAR ) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.VARCHAR] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then return false, "Failed to decode metadata for data type CHAR" end - col_md:setType( Constants.DataType.VARCHAR ) + [Constants.DataType.VARCHAR] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then return false, "Failed to decode metadata for data type CHAR" end + col_md:setType( Constants.DataType.VARCHAR ) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.SMALLINT] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end - col_md:setType( Constants.DataType.SMALLINT ) + [Constants.DataType.SMALLINT] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end + col_md:setType( Constants.DataType.SMALLINT ) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.SERIAL] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end - col_md:setType( Constants.DataType.SERIAL ) + [Constants.DataType.SERIAL] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then return false, "Failed to decode metadata for data type SMALLINT" end + col_md:setType( Constants.DataType.SERIAL ) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.DATETIME] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end - col_md:setType( Constants.DataType.DATETIME ) - col_md:setLength(10) + [Constants.DataType.DATETIME] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end + col_md:setType( Constants.DataType.DATETIME ) + col_md:setLength(10) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.FLOAT] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end - col_md:setType( Constants.DataType.FLOAT ) + [Constants.DataType.FLOAT] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end + col_md:setType( Constants.DataType.FLOAT ) - return true, col_md - end, + return true, col_md + end, - [Constants.DataType.DATE] = function( data ) - local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) - if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end - col_md:setType( Constants.DataType.DATE ) + [Constants.DataType.DATE] = function( data ) + local status, col_md = MetaDataDecoders[Constants.DataType.INT]( data ) + if( not(status) ) then return false, "Failed to decode metadata for data type DATETIME" end + col_md:setType( Constants.DataType.DATE ) - return true, col_md - end, + return true, col_md + end, } @@ -337,42 +337,42 @@ MetaDataDecoders = { -- The decoders, should be self explanatory DataTypeDecoders = { - [Constants.DataType.INT] = function( data, pos ) - return bin.unpack(">i", data, pos) - end, + [Constants.DataType.INT] = function( data, pos ) + return bin.unpack(">i", data, pos) + end, - [Constants.DataType.FLOAT] = function( data, pos ) - return bin.unpack(">d", data, pos) - end, + [Constants.DataType.FLOAT] = function( data, pos ) + return bin.unpack(">d", data, pos) + end, - [Constants.DataType.DATE] = function( data, pos ) - return pos + 4, "DATE" - end, + [Constants.DataType.DATE] = function( data, pos ) + return pos + 4, "DATE" + end, - [Constants.DataType.SERIAL] = function( data, pos ) - return bin.unpack(">I", data, pos) - end, + [Constants.DataType.SERIAL] = function( data, pos ) + return bin.unpack(">I", data, pos) + end, - [Constants.DataType.SMALLINT] = function( data, pos ) - return bin.unpack(">s", data, pos) - end, + [Constants.DataType.SMALLINT] = function( data, pos ) + return bin.unpack(">s", data, pos) + end, - [Constants.DataType.CHAR] = function( data, pos, len ) - local pos, ret = bin.unpack("A" .. len, data, pos) - return pos, Util.ifxToLuaString( ret ) - end, + [Constants.DataType.CHAR] = function( data, pos, len ) + local pos, ret = bin.unpack("A" .. len, data, pos) + return pos, Util.ifxToLuaString( ret ) + end, - [Constants.DataType.VARCHAR] = function( data, pos, len ) - local pos, len = bin.unpack("C", data, pos) - local ret + [Constants.DataType.VARCHAR] = function( data, pos, len ) + local pos, len = bin.unpack("C", data, pos) + local ret - pos, ret = bin.unpack("A" .. len, data, pos) - return pos, Util.ifxToLuaString( ret ) - end, + pos, ret = bin.unpack("A" .. len, data, pos) + return pos, Util.ifxToLuaString( ret ) + end, - [Constants.DataType.DATETIME] = function( data, pos ) - return pos + 10, "DATETIME" - end, + [Constants.DataType.DATETIME] = function( data, pos ) + return pos + 10, "DATETIME" + end, } @@ -380,285 +380,285 @@ DataTypeDecoders = { -- The MessageDecoders class "holding" the Response Decoders MessageDecoders = { - --- Decodes the SQ_ERR error message - -- - -- @param socket already connected to the Informix database server - -- @return status true on success, false on failure - -- @return errmsg, Informix error message or decoding error message if - -- status is false - [Constants.Message.SQ_ERR] = function( socket ) - local status, data = socket:recv(8) - local _, svcerr, oserr, errmsg, str, len, pos + --- Decodes the SQ_ERR error message + -- + -- @param socket already connected to the Informix database server + -- @return status true on success, false on failure + -- @return errmsg, Informix error message or decoding error message if + -- status is false + [Constants.Message.SQ_ERR] = function( socket ) + local status, data = socket:recv(8) + local _, svcerr, oserr, errmsg, str, len, pos - if( not(status) ) then return false, "Failed to decode error response" end + if( not(status) ) then return false, "Failed to decode error response" end - pos, svcerr, oserr, _, len = bin.unpack(">ssss", data ) + pos, svcerr, oserr, _, len = bin.unpack(">ssss", data ) - if( len and len > 0 ) then - status, data = socket:recv(len) - if( not(status) ) then return false, "Failed to decode error response" end - _, str = bin.unpack("A" .. len, data) - end + if( len and len > 0 ) then + status, data = socket:recv(len) + if( not(status) ) then return false, "Failed to decode error response" end + _, str = bin.unpack("A" .. len, data) + end - status, data = socket:recv(2) + status, data = socket:recv(2) - errmsg = Constants.ErrorMsg[svcerr] - if ( errmsg and str ) then - errmsg = errmsg:format(str) - end - return false, errmsg or ("Informix returned an error (svcerror: %d, oserror: %d)"):format( svcerr, oserr ) - end, + errmsg = Constants.ErrorMsg[svcerr] + if ( errmsg and str ) then + errmsg = errmsg:format(str) + end + return false, errmsg or ("Informix returned an error (svcerror: %d, oserror: %d)"):format( svcerr, oserr ) + end, - --- Decodes the SQ_PROTOCOLS message - -- - -- @param socket already connected to the Informix database server - -- @return status true on success, false on failure - -- @return err error message if status is false - [Constants.Message.SQ_PROTOCOLS] = function( socket ) - local status, data - local len, _ + --- Decodes the SQ_PROTOCOLS message + -- + -- @param socket already connected to the Informix database server + -- @return status true on success, false on failure + -- @return err error message if status is false + [Constants.Message.SQ_PROTOCOLS] = function( socket ) + local status, data + local len, _ - status, data = socket:recv(2) - if( not(status) ) then return false, "Failed to decode SQ_PROTOCOLS response" end - _, len = bin.unpack(">S", data ) + status, data = socket:recv(2) + if( not(status) ) then return false, "Failed to decode SQ_PROTOCOLS response" end + _, len = bin.unpack(">S", data ) - -- read the remaining data - return socket:recv(len + 2) - end, + -- read the remaining data + return socket:recv(len + 2) + end, - --- Decodes the SQ_EOT message - -- - -- @return status, always true - [Constants.Message.SQ_EOT] = function( socket ) - return true - end, + --- Decodes the SQ_EOT message + -- + -- @return status, always true + [Constants.Message.SQ_EOT] = function( socket ) + return true + end, - --- Decodes the SQ_DONE message - -- - -- @param socket already connected to the Informix database server - -- @return status true on success, false on failure - -- @return err error message if status is false - [Constants.Message.SQ_DONE] = function( socket ) - local status, data = socket:recv(2) - local _, len, tmp - if( not(status) ) then return false, "Failed to decode SQ_DONE response" end - _, len = bin.unpack(">S", data ) + --- Decodes the SQ_DONE message + -- + -- @param socket already connected to the Informix database server + -- @return status true on success, false on failure + -- @return err error message if status is false + [Constants.Message.SQ_DONE] = function( socket ) + local status, data = socket:recv(2) + local _, len, tmp + if( not(status) ) then return false, "Failed to decode SQ_DONE response" end + _, len = bin.unpack(">S", data ) - -- For some *@#! reason the SQ_DONE packet sometimes contains an - -- length exeeding the length of the packet by one. Attempt to - -- detect this and fix. - status, data = socket:recv( len ) - _, tmp = bin.unpack(">S", data, len - 2) - return socket:recv( (tmp == 0) and 3 or 4 ) - end, + -- For some *@#! reason the SQ_DONE packet sometimes contains an + -- length exeeding the length of the packet by one. Attempt to + -- detect this and fix. + status, data = socket:recv( len ) + _, tmp = bin.unpack(">S", data, len - 2) + return socket:recv( (tmp == 0) and 3 or 4 ) + end, - --- Decodes the metadata for a result set - -- - -- @param socket already connected to the Informix database server - -- @return status true on success, false on failure - -- @return column_meta table containing the metadata - [Constants.Message.SQ_DESCRIBE] = function( socket ) - local status, data = socket:recv(14) - local pos, cols, col_type, col_name, col_len, col_md, stmt_id - local coldesc_len, x - local column_meta = {} + --- Decodes the metadata for a result set + -- + -- @param socket already connected to the Informix database server + -- @return status true on success, false on failure + -- @return column_meta table containing the metadata + [Constants.Message.SQ_DESCRIBE] = function( socket ) + local status, data = socket:recv(14) + local pos, cols, col_type, col_name, col_len, col_md, stmt_id + local coldesc_len, x + local column_meta = {} - if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end - pos, cols, coldesc_len = bin.unpack(">SS", data, 11) - pos, stmt_id = bin.unpack(">S", data, 3) + if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end + pos, cols, coldesc_len = bin.unpack(">SS", data, 11) + pos, stmt_id = bin.unpack(">S", data, 3) - if ( cols <= 0 ) then - -- We can end up here if we executed a CREATE, UPDATE OR INSERT statement - local tmp - status, data = socket:recv(2) - if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end + if ( cols <= 0 ) then + -- We can end up here if we executed a CREATE, UPDATE OR INSERT statement + local tmp + status, data = socket:recv(2) + if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end - pos, tmp = bin.unpack(">S", data) + pos, tmp = bin.unpack(">S", data) - -- This was the result of a CREATE or UPDATE statement - if ( tmp == 0x0f ) then - status, data = socket:recv(26) - -- This was the result of a INSERT statement - elseif( tmp == 0x5e ) then - status, data = socket:recv(46) - end - return true - end + -- This was the result of a CREATE or UPDATE statement + if ( tmp == 0x0f ) then + status, data = socket:recv(26) + -- This was the result of a INSERT statement + elseif( tmp == 0x5e ) then + status, data = socket:recv(46) + end + return true + end - status, data = socket:recv(6) - if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end + status, data = socket:recv(6) + if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end - for i=1, cols do + for i=1, cols do - status, data = socket:recv(2) - if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end - pos, col_type = bin.unpack("C", data, 2) + status, data = socket:recv(2) + if( not(status) ) then return false, "Failed to decode SQ_DESCRIBE response" end + pos, col_type = bin.unpack("C", data, 2) - if ( MetaDataDecoders[col_type] ) then + if ( MetaDataDecoders[col_type] ) then - status, data = socket:recv(20) - if( not(status) ) then - return false, "Failed to read column meta data" - end + status, data = socket:recv(20) + if( not(status) ) then + return false, "Failed to read column meta data" + end - status, col_md = MetaDataDecoders[col_type]( data ) - if ( not(status) ) then - return false, col_md - end - else - return false, ("No metadata decoder for column type: %d"):format(col_type) - end + status, col_md = MetaDataDecoders[col_type]( data ) + if ( not(status) ) then + return false, col_md + end + else + return false, ("No metadata decoder for column type: %d"):format(col_type) + end - if ( iS", data) - if( data == Constants.Message.SQ_DONE ) then - status, data = socket:recv(26) - else - status, data = socket:recv(10) - end - return true, { metadata = column_meta, stmt_id = stmt_id } - end, + pos, data = bin.unpack(">S", data) + if( data == Constants.Message.SQ_DONE ) then + status, data = socket:recv(26) + else + status, data = socket:recv(10) + end + return true, { metadata = column_meta, stmt_id = stmt_id } + end, - --- Processes the result from a query - -- - -- @param socket already connected to the Informix database server - -- @param info table containing the following fields: - -- metadata as recieved from SQ_DESCRIBE - -- rows containing already retrieved rows - -- id containing the statement id as sent to SQ_ID - -- @return status true on success, false on failure - -- @return rows table containing the resulting columns and rows as: - -- { { col, col2, col3 } } - -- or error message if status is false - [Constants.Message.SQ_TUPLE] = function( socket, info ) - local status, data - local row = {} - local count = 1 + --- Processes the result from a query + -- + -- @param socket already connected to the Informix database server + -- @param info table containing the following fields: + -- metadata as recieved from SQ_DESCRIBE + -- rows containing already retrieved rows + -- id containing the statement id as sent to SQ_ID + -- @return status true on success, false on failure + -- @return rows table containing the resulting columns and rows as: + -- { { col, col2, col3 } } + -- or error message if status is false + [Constants.Message.SQ_TUPLE] = function( socket, info ) + local status, data + local row = {} + local count = 1 - if ( not( info.rows ) ) then info.rows = {} end + if ( not( info.rows ) ) then info.rows = {} end - while (true) do - local pos = 1 + while (true) do + local pos = 1 - status, data = socket:recv(6) - if( not(status) ) then return false, "Failed to read column data" end + status, data = socket:recv(6) + if( not(status) ) then return false, "Failed to read column data" end - local _, total_len = bin.unpack(">I", data, 3) - status, data = socket:recv( ( total_len % 2 == 0 ) and total_len or total_len + 1) - if( not(status) ) then return false, "Failed to read column data" end + local _, total_len = bin.unpack(">I", data, 3) + status, data = socket:recv( ( total_len % 2 == 0 ) and total_len or total_len + 1) + if( not(status) ) then return false, "Failed to read column data" end - row = {} - for _, col in ipairs(info.metadata) do - local typ, len, name = col:getType(), col:getLength(), col:getName() - local val + row = {} + for _, col in ipairs(info.metadata) do + local typ, len, name = col:getType(), col:getLength(), col:getName() + local val - if( DataTypeDecoders[typ] ) then - pos, val = DataTypeDecoders[typ]( data, pos, len ) - else - return false, ("No data type decoder for type: 0x%d"):format(typ) - end - table.insert( row, val ) - end + if( DataTypeDecoders[typ] ) then + pos, val = DataTypeDecoders[typ]( data, pos, len ) + else + return false, ("No data type decoder for type: 0x%d"):format(typ) + end + table.insert( row, val ) + end - status, data = socket:recv(2) + status, data = socket:recv(2) - local _, flags = bin.unpack(">S", data) + local _, flags = bin.unpack(">S", data) - count = count + 1 - table.insert( info.rows, row ) + count = count + 1 + table.insert( info.rows, row ) - -- Check if we're done - if ( Constants.Message.SQ_DONE == flags ) then - break - end + -- Check if we're done + if ( Constants.Message.SQ_DONE == flags ) then + break + end - -- If there's more data we need to send a new SQ_ID packet - if ( flags == Constants.Message.SQ_EOT ) then - local status, tmp = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "continue" ) ) ) - local pkt_type + -- If there's more data we need to send a new SQ_ID packet + if ( flags == Constants.Message.SQ_EOT ) then + local status, tmp = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "continue" ) ) ) + local pkt_type - status, tmp = socket:recv( 2 ) - pos, pkt_type = bin.unpack(">S", tmp) + status, tmp = socket:recv( 2 ) + pos, pkt_type = bin.unpack(">S", tmp) - return MessageDecoders[pkt_type]( socket, info ) - end + return MessageDecoders[pkt_type]( socket, info ) + end - end + end - -- read the remaining data - status, data = socket:recv( 26 ) - if( not(status) ) then return false, "Failed to read column data" end + -- read the remaining data + status, data = socket:recv( 26 ) + if( not(status) ) then return false, "Failed to read column data" end - -- signal finnish reading - status, data = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "end" ) ) ) - status, data = socket:recv( 2 ) + -- signal finnish reading + status, data = socket:send( tostring(Packet.SQ_ID:new( info.id, nil, "end" ) ) ) + status, data = socket:recv( 2 ) - return true, info + return true, info - end, + end, - --- Decodes a SQ_DBLIST response - -- - -- @param socket already connected to the Informix database server - -- @return status true on success, false on failure - -- @return databases array of database names - [Constants.Message.SQ_DBLIST] = function( socket ) + --- Decodes a SQ_DBLIST response + -- + -- @param socket already connected to the Informix database server + -- @return status true on success, false on failure + -- @return databases array of database names + [Constants.Message.SQ_DBLIST] = function( socket ) - local status, data, pos, len, db - local databases = {} + local status, data, pos, len, db + local databases = {} - while( true ) do - status, data = socket:recv(2) - if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end + while( true ) do + status, data = socket:recv(2) + if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end - pos, len = bin.unpack(">S", data) - if ( 0 == len ) then break end + pos, len = bin.unpack(">S", data) + if ( 0 == len ) then break end - status, data = socket:recv(len) - if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end + status, data = socket:recv(len) + if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end - pos, db = bin.unpack("A" .. len, data ) - table.insert( databases, db ) + pos, db = bin.unpack("A" .. len, data ) + table.insert( databases, db ) - if ( len %2 == 1 ) then - socket:recv(1) - if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end - end - end + if ( len %2 == 1 ) then + socket:recv(1) + if ( not(status) ) then return false, "Failed to parse SQ_DBLIST response" end + end + end - -- read SQ_EOT - status, data = socket:recv(2) + -- read SQ_EOT + status, data = socket:recv(2) - return true, databases - end, + return true, databases + end, - [Constants.Message.SQ_EXIT] = function( socket ) - local status, data = socket:recv(2) - if ( not(status) ) then return false, "Failed to parse SQ_EXIT response" end + [Constants.Message.SQ_EXIT] = function( socket ) + local status, data = socket:recv(2) + if ( not(status) ) then return false, "Failed to parse SQ_EXIT response" end - return true - end + return true + end } @@ -666,22 +666,22 @@ MessageDecoders = { -- Packet used to request a list of available databases Packet.SQ_DBLIST = { - --- Creates a new Packet.SQ_DBLIST instance - -- - -- @return object new instance of Packet.SQ_DBLIST - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new Packet.SQ_DBLIST instance + -- + -- @return object new instance of Packet.SQ_DBLIST + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - return bin.pack(">SS", Constants.Message.SQ_DBLIST, Constants.Message.SQ_EOT) - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + return bin.pack(">SS", Constants.Message.SQ_DBLIST, Constants.Message.SQ_EOT) + end } @@ -689,140 +689,140 @@ Packet.SQ_DBLIST = Packet.SQ_DBOPEN = { - --- Creates a new Packet.SQ_DBOPEN instance - -- - -- @param database string containing the name of the database to open - -- @return object new instance of Packet.SQ_DBOPEN - new = function( self, database ) - local o = {} - setmetatable(o, self) - self.__index = self - o.database = database - return o - end, + --- Creates a new Packet.SQ_DBOPEN instance + -- + -- @param database string containing the name of the database to open + -- @return object new instance of Packet.SQ_DBOPEN + new = function( self, database ) + local o = {} + setmetatable(o, self) + self.__index = self + o.database = database + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - return bin.pack(">SSASS", Constants.Message.SQ_DBOPEN, #self.database, - Util.padToOdd(self.database), 0x00, - Constants.Message.SQ_EOT) - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + return bin.pack(">SSASS", Constants.Message.SQ_DBOPEN, #self.database, + Util.padToOdd(self.database), 0x00, + Constants.Message.SQ_EOT) + end } -- This packet is "a mess" and requires further analysis Packet.SQ_ID = { - --- Creates a new Packet.SQ_ID instance - -- - -- @param id number containing the statement identifier - -- @param s1 number unknown, should be 0 on first call and 1 when more data is requested - -- @return object new instance of Packet.SQ_ID - new = function( self, id, id2, mode ) - local o = {} - setmetatable(o, self) - self.__index = self - o.id = ("_ifxc%.13d"):format( id2 or 0 ) - o.seq = id - o.mode = mode - return o - end, + --- Creates a new Packet.SQ_ID instance + -- + -- @param id number containing the statement identifier + -- @param s1 number unknown, should be 0 on first call and 1 when more data is requested + -- @return object new instance of Packet.SQ_ID + new = function( self, id, id2, mode ) + local o = {} + setmetatable(o, self) + self.__index = self + o.id = ("_ifxc%.13d"):format( id2 or 0 ) + o.seq = id + o.mode = mode + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - if ( self.mode == "continue" ) then - return bin.pack( ">SSSSSS", Constants.Message.SQ_ID, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT ) - elseif ( self.mode == "end" ) then - return bin.pack( ">SSSS", Constants.Message.SQ_ID, self.seq, 0x000a, Constants.Message.SQ_EOT) - else - return bin.pack(">SSSSASSSSSSS", Constants.Message.SQ_ID, self.seq, 0x0003, #self.id, self.id, - 0x0006, 0x0004, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT ) - end - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + if ( self.mode == "continue" ) then + return bin.pack( ">SSSSSS", Constants.Message.SQ_ID, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT ) + elseif ( self.mode == "end" ) then + return bin.pack( ">SSSS", Constants.Message.SQ_ID, self.seq, 0x000a, Constants.Message.SQ_EOT) + else + return bin.pack(">SSSSASSSSSSS", Constants.Message.SQ_ID, self.seq, 0x0003, #self.id, self.id, + 0x0006, 0x0004, self.seq, 0x0009, 0x1000, 0x0000, Constants.Message.SQ_EOT ) + end + end } Packet.SQ_INFO = { - -- The default parameters - DEFAULT_PARAMETERS = { - [1] = { ["DBTEMP"] = "/tmp" }, - [2] = { ["SUBQCACHESZ"] = "10" }, - }, + -- The default parameters + DEFAULT_PARAMETERS = { + [1] = { ["DBTEMP"] = "/tmp" }, + [2] = { ["SUBQCACHESZ"] = "10" }, + }, - --- Creates a new Packet.SQ_INFO instance - -- - -- @param params containing any additional parameters to use - -- @return object new instance of Packet.SQ_INFO - new = function( self, params ) - local o = {} - local params = params or Packet.SQ_INFO.DEFAULT_PARAMETERS - setmetatable(o, self) - self.__index = self - o.parameters = {} + --- Creates a new Packet.SQ_INFO instance + -- + -- @param params containing any additional parameters to use + -- @return object new instance of Packet.SQ_INFO + new = function( self, params ) + local o = {} + local params = params or Packet.SQ_INFO.DEFAULT_PARAMETERS + setmetatable(o, self) + self.__index = self + o.parameters = {} - for _, v in ipairs( params ) do - for k2, v2 in pairs(v) do - o:addParameter( k2, v2 ) - end - end - return o - end, + for _, v in ipairs( params ) do + for k2, v2 in pairs(v) do + o:addParameter( k2, v2 ) + end + end + return o + end, - addParameter = function( self, key, value ) - table.insert( self.parameters, { [key] = value } ) - end, + addParameter = function( self, key, value ) + table.insert( self.parameters, { [key] = value } ) + end, - paramToString = function( self, key, value ) - return bin.pack(">SASA", #key, Util.padToOdd(key), #value, Util.padToOdd( value ) ) - end, + paramToString = function( self, key, value ) + return bin.pack(">SASA", #key, Util.padToOdd(key), #value, Util.padToOdd( value ) ) + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function( self ) - local params = "" - local data + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function( self ) + local params = "" + local data - for _, v in ipairs( self.parameters ) do - for k2, v2 in pairs( v ) do - params = params .. self:paramToString( k2, v2 ) - end - end + for _, v in ipairs( self.parameters ) do + for k2, v2 in pairs( v ) do + params = params .. self:paramToString( k2, v2 ) + end + end - data = bin.pack(">SSSSS", Constants.Message.SQ_INFO, 0x0006, #params + 6, 0x000c, 0x0004 ) - data = data .. params .. bin.pack(">SSS", 0x0000, 0x0000, Constants.Message.SQ_EOT) - return data - end + data = bin.pack(">SSSSS", Constants.Message.SQ_INFO, 0x0006, #params + 6, 0x000c, 0x0004 ) + data = data .. params .. bin.pack(">SSS", 0x0000, 0x0000, Constants.Message.SQ_EOT) + return data + end } -- Performs protocol negotiation? Packet.SQ_PROTOCOLS = { - -- hex-encoded data to send as protocol negotiation - data = "0007fffc7ffc3c8c8a00000c", + -- hex-encoded data to send as protocol negotiation + data = "0007fffc7ffc3c8c8a00000c", - --- Creates a new Packet.SQ_PROTOCOLS instance - -- - -- @return object new instance of Packet.SQ_PROTOCOLS - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new Packet.SQ_PROTOCOLS instance + -- + -- @return object new instance of Packet.SQ_PROTOCOLS + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - return bin.pack(">SH", Constants.Message.SQ_PROTOCOLS, self.data) - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + return bin.pack(">SH", Constants.Message.SQ_PROTOCOLS, self.data) + end } @@ -830,24 +830,24 @@ Packet.SQ_PROTOCOLS = Packet.SQ_PREPARE = { - --- Creates a new Packet.SQ_PREPARE instance - -- - -- @param query string containing the query to execute - -- @return object new instance of Packet.SQ_PREPARE - new = function( self, query ) - local o = {} - setmetatable(o, self) - self.__index = self - o.query = Util.padToEven(query) - return o - end, + --- Creates a new Packet.SQ_PREPARE instance + -- + -- @param query string containing the query to execute + -- @return object new instance of Packet.SQ_PREPARE + new = function( self, query ) + local o = {} + setmetatable(o, self) + self.__index = self + o.query = Util.padToEven(query) + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - return bin.pack(">SIACSSS", Constants.Message.SQ_PREPARE, #self.query, self.query, 0, 0x0016, 0x0031, Constants.Message.SQ_EOT) - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + return bin.pack(">SIACSSS", Constants.Message.SQ_PREPARE, #self.query, self.query, 0, 0x0016, 0x0031, Constants.Message.SQ_EOT) + end } @@ -855,147 +855,147 @@ Packet.SQ_PREPARE = Packet.SQ_COMMAND = { - --- Creates a new Packet.SQ_COMMAND instance - -- - -- @param query string containing the query to execute - -- @return object new instance of Packet.SQ_COMMAND - new = function( self, query ) - local o = {} - setmetatable(o, self) - self.__index = self - o.query = Util.padToEven(query) - return o - end, + --- Creates a new Packet.SQ_COMMAND instance + -- + -- @param query string containing the query to execute + -- @return object new instance of Packet.SQ_COMMAND + new = function( self, query ) + local o = {} + setmetatable(o, self) + self.__index = self + o.query = Util.padToEven(query) + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - return bin.pack(">SIACSSSS", Constants.Message.SQ_COMMAND, #self.query, self.query, 0, 0x0016, 0x0007, 0x000b, Constants.Message.SQ_EOT) - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + return bin.pack(">SIACSSSS", Constants.Message.SQ_COMMAND, #self.query, self.query, 0, 0x0016, 0x0007, 0x000b, Constants.Message.SQ_EOT) + end } Packet.SQ_EXIT = { - --- Creates a new Packet.SQ_EXIT instance - -- - -- @return object new instance of Packet.SQ_EXIT - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new Packet.SQ_EXIT instance + -- + -- @return object new instance of Packet.SQ_EXIT + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function(self) - return bin.pack(">S", Constants.Message.SQ_EXIT) - end + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function(self) + return bin.pack(">S", Constants.Message.SQ_EXIT) + end } -- The Utility Class Util = { - --- Converts a connection parameter to string - -- - -- @param param string containing the parameter name - -- @param value string containing the parameter value - -- @return string containing the encoded parameter as string - paramToString = function( param, value ) - return bin.pack(">PP", param, value ) - end, + --- Converts a connection parameter to string + -- + -- @param param string containing the parameter name + -- @param value string containing the parameter value + -- @return string containing the encoded parameter as string + paramToString = function( param, value ) + return bin.pack(">PP", param, value ) + end, - --- Pads a string to an even number of characters - -- - -- @param str the string to pad - -- @param pad the character to pad with - -- @return result the padded string - padToEven = function( str, pad ) - return (#str % 2 == 1) and str or str .. ( pad and pad or "\0") - end, + --- Pads a string to an even number of characters + -- + -- @param str the string to pad + -- @param pad the character to pad with + -- @return result the padded string + padToEven = function( str, pad ) + return (#str % 2 == 1) and str or str .. ( pad and pad or "\0") + end, - --- Pads a string to an odd number of characters - -- - -- @param str the string to pad - -- @param pad the character to pad with - -- @return result the padded string - padToOdd = function( str, pad ) - return (#str % 2 == 0) and str or str .. ( pad and pad or "\0") - end, + --- Pads a string to an odd number of characters + -- + -- @param str the string to pad + -- @param pad the character to pad with + -- @return result the padded string + padToOdd = function( str, pad ) + return (#str % 2 == 0) and str or str .. ( pad and pad or "\0") + end, - --- Formats a table to suitable script output - -- - -- @param info as returned from ExecutePrepare - -- @return table suitable for use by stdnse.format_output - formatTable = function( info ) - local header, row = "", "" - local result = {} - local metadata = info.metadata - local rows = info.rows + --- Formats a table to suitable script output + -- + -- @param info as returned from ExecutePrepare + -- @return table suitable for use by stdnse.format_output + formatTable = function( info ) + local header, row = "", "" + local result = {} + local metadata = info.metadata + local rows = info.rows - if ( info.error ) then - table.insert(result, info.error) - return result - end + if ( info.error ) then + table.insert(result, info.error) + return result + end - if ( info.info ) then - table.insert(result, info.info) - return result - end + if ( info.info ) then + table.insert(result, info.info) + return result + end - if ( not(metadata) ) then return "" end + if ( not(metadata) ) then return "" end - for i=1, #metadata do - if ( metadata[i]:getType() == Constants.DataType.CHAR and metadata[i]:getLength() < 50) then - header = header .. ("%-" .. metadata[i]:getLength() .. "s "):format(metadata[i]:getName()) - else - header = header .. metadata[i]:getName() - if ( i<#metadata ) then - header = header .. "\t" - end - end - end - table.insert( result, header ) + for i=1, #metadata do + if ( metadata[i]:getType() == Constants.DataType.CHAR and metadata[i]:getLength() < 50) then + header = header .. ("%-" .. metadata[i]:getLength() .. "s "):format(metadata[i]:getName()) + else + header = header .. metadata[i]:getName() + if ( i<#metadata ) then + header = header .. "\t" + end + end + end + table.insert( result, header ) - for j=1, #rows do - row = "" - for i=1, #metadata do - row = row .. rows[j][i] .. " " - if ( metadata[i]:getType() ~= Constants.DataType.CHAR and i<#metadata and metadata[i]:getLength() < 50 ) then row = row .. "\t" end - end - table.insert( result, row ) - end + for j=1, #rows do + row = "" + for i=1, #metadata do + row = row .. rows[j][i] .. " " + if ( metadata[i]:getType() ~= Constants.DataType.CHAR and i<#metadata and metadata[i]:getLength() < 50 ) then row = row .. "\t" end + end + table.insert( result, row ) + end - return result - end, + return result + end, - -- Removes trailing nulls - -- - -- @param str containing the informix string - -- @return ret the string with any trailing nulls removed - ifxToLuaString = function( str ) - local ret + -- Removes trailing nulls + -- + -- @param str containing the informix string + -- @return ret the string with any trailing nulls removed + ifxToLuaString = function( str ) + local ret - if ( not(str) ) then return "" end + if ( not(str) ) then return "" end - if ( str:sub(-1, -1 ) ~= "\0" ) then - return str - end + if ( str:sub(-1, -1 ) ~= "\0" ) then + return str + end - for i=1, #str do - if ( str:sub(-i,-i) == "\0" ) then - ret = str:sub(1, -i - 1) - else - break - end - end + for i=1, #str do + if ( str:sub(-i,-i) == "\0" ) then + ret = str:sub(1, -i - 1) + else + break + end + end - return ret - end, + return ret + end, } -- The connection Class, used to connect and authenticate to the server @@ -1005,188 +1005,188 @@ Util = -- code connecting to Informix using JDBC. Packet.Connect = { - -- default parameters sent using JDBC - DEFAULT_PARAMETERS = { - [1] = { ['LOCKDOWN'] = 'no' }, - [2] = { ['DBDATE'] = 'Y4MD-' }, - [3] = { ['SINGLELEVEL'] = 'no' }, - [4] = { ['NODEFDAC'] = 'no' }, - [5] = { ['CLNT_PAM_CAPABLE'] = '1' }, - [6] = { ['SKALL'] = '0' }, - [7] = { ['LKNOTIFY'] = 'yes' }, - [8] = { ['SKSHOW'] = '0' }, - [9] = { ['IFX_UPDDESC'] = '1' }, - [10] = { ['DBPATH'] = '.' }, - [11] = { ['CLIENT_LOCALE'] = 'en_US.8859-1' }, - [12] = { ['SKINHIBIT'] = '0' }, - }, + -- default parameters sent using JDBC + DEFAULT_PARAMETERS = { + [1] = { ['LOCKDOWN'] = 'no' }, + [2] = { ['DBDATE'] = 'Y4MD-' }, + [3] = { ['SINGLELEVEL'] = 'no' }, + [4] = { ['NODEFDAC'] = 'no' }, + [5] = { ['CLNT_PAM_CAPABLE'] = '1' }, + [6] = { ['SKALL'] = '0' }, + [7] = { ['LKNOTIFY'] = 'yes' }, + [8] = { ['SKSHOW'] = '0' }, + [9] = { ['IFX_UPDDESC'] = '1' }, + [10] = { ['DBPATH'] = '.' }, + [11] = { ['CLIENT_LOCALE'] = 'en_US.8859-1' }, + [12] = { ['SKINHIBIT'] = '0' }, + }, - --- Creates a new Connection packet - -- - -- @param username string containing the username for authentication - -- @param password string containing the password for authentication - -- @param instance string containing the instance to connect to - -- @return a new Packet.Connect instance - new = function(self, username, password, instance, parameters) - local o = {} - setmetatable(o, self) - self.__index = self - o.username = username and username .. "\0" - o.password = password and password .. "\0" - o.instance = instance and instance .. "\0" - o.parameters = parameters - return o - end, + --- Creates a new Connection packet + -- + -- @param username string containing the username for authentication + -- @param password string containing the password for authentication + -- @param instance string containing the instance to connect to + -- @return a new Packet.Connect instance + new = function(self, username, password, instance, parameters) + local o = {} + setmetatable(o, self) + self.__index = self + o.username = username and username .. "\0" + o.password = password and password .. "\0" + o.instance = instance and instance .. "\0" + o.parameters = parameters + return o + end, - --- Adds the default set of parameters - addDefaultParameters = function( self ) - for _, v in ipairs( self.DEFAULT_PARAMETERS ) do - for k2, v2 in pairs( v ) do - self:addParameter( k2, v2 ) - end - end - end, + --- Adds the default set of parameters + addDefaultParameters = function( self ) + for _, v in ipairs( self.DEFAULT_PARAMETERS ) do + for k2, v2 in pairs( v ) do + self:addParameter( k2, v2 ) + end + end + end, - --- Adds a parameter to the connection packet - -- - -- @param param string containing the parameter name - -- @param value string containing the parameter value - -- @return status, always true - addParameter = function( self, param, value ) - local tbl = {} - tbl[param] = value - table.insert( self.parameters, tbl ) + --- Adds a parameter to the connection packet + -- + -- @param param string containing the parameter name + -- @param value string containing the parameter value + -- @return status, always true + addParameter = function( self, param, value ) + local tbl = {} + tbl[param] = value + table.insert( self.parameters, tbl ) - return true - end, + return true + end, - --- Retrieves the OS error code - -- - -- @return oserror number containing the OS error code - getOsError = function( self ) return self.oserror end, + --- Retrieves the OS error code + -- + -- @return oserror number containing the OS error code + getOsError = function( self ) return self.oserror end, - --- Retrieves the Informix service error - -- - -- @return svcerror number containing the service error - getSvcError = function( self ) return self.svcerror end, + --- Retrieves the Informix service error + -- + -- @return svcerror number containing the service error + getSvcError = function( self ) return self.svcerror end, - --- Retrieves the Informix error message - -- - -- @return errmsg string containing the "mapped" error message - getErrMsg = function( self ) return self.errmsg end, + --- Retrieves the Informix error message + -- + -- @return errmsg string containing the "mapped" error message + getErrMsg = function( self ) return self.errmsg end, - --- Reads and decodes the response to the connect packet from the server. - -- The function will return true even if the response contains an Informix - -- error. In order to verify if the connection was successful, check for OS - -- or service errors using the getSvcError and getOsError methods. - -- - -- @param socket already connected to the server - -- @return status true on success, false on failure - -- @return err msg if status is false - readResponse = function( self, socket ) - local status, data = socket:recv( 2 ) - local len, pos, tmp + --- Reads and decodes the response to the connect packet from the server. + -- The function will return true even if the response contains an Informix + -- error. In order to verify if the connection was successful, check for OS + -- or service errors using the getSvcError and getOsError methods. + -- + -- @param socket already connected to the server + -- @return status true on success, false on failure + -- @return err msg if status is false + readResponse = function( self, socket ) + local status, data = socket:recv( 2 ) + local len, pos, tmp - if ( not(status) ) then return false, data end - pos, len = bin.unpack(">S", data) - status, data = socket:recv( len - 2 ) - if ( not(status) ) then return false, data end + if ( not(status) ) then return false, data end + pos, len = bin.unpack(">S", data) + status, data = socket:recv( len - 2 ) + if ( not(status) ) then return false, data end - pos = 13 - pos, tmp = bin.unpack(">S", data, pos) - pos = pos + tmp + pos = 13 + pos, tmp = bin.unpack(">S", data, pos) + pos = pos + tmp - pos, tmp = bin.unpack(">S", data, pos) + pos, tmp = bin.unpack(">S", data, pos) - if ( 108 ~= tmp ) then - return false, "Connect recieved unexpected response" - end + if ( 108 ~= tmp ) then + return false, "Connect recieved unexpected response" + end - pos = pos + 12 - -- version - pos, len = bin.unpack(">S", data, pos) - pos, self.version = bin.unpack("A" .. len, data, pos) + pos = pos + 12 + -- version + pos, len = bin.unpack(">S", data, pos) + pos, self.version = bin.unpack("A" .. len, data, pos) - -- serial - pos, len = bin.unpack(">S", data, pos) - pos, self.serial = bin.unpack("A" .. len, data, pos) + -- serial + pos, len = bin.unpack(">S", data, pos) + pos, self.serial = bin.unpack("A" .. len, data, pos) - -- applid - pos, len = bin.unpack(">S", data, pos) - pos, self.applid = bin.unpack("A" .. len, data, pos) + -- applid + pos, len = bin.unpack(">S", data, pos) + pos, self.applid = bin.unpack("A" .. len, data, pos) - -- skip 14 bytes ahead - pos = pos + 14 + -- skip 14 bytes ahead + pos = pos + 14 - -- do some more skipping - pos, tmp = bin.unpack(">S", data, pos) - pos = pos + tmp + -- do some more skipping + pos, tmp = bin.unpack(">S", data, pos) + pos = pos + tmp - -- do some more skipping - pos, tmp = bin.unpack(">S", data, pos) - pos = pos + tmp + -- do some more skipping + pos, tmp = bin.unpack(">S", data, pos) + pos = pos + tmp - -- skip another 24 bytes - pos = pos + 24 - pos, tmp = bin.unpack(">S", data, pos) + -- skip another 24 bytes + pos = pos + 24 + pos, tmp = bin.unpack(">S", data, pos) - if ( tmp ~= 102 ) then - return false, "Connect recieved unexpected response" - end + if ( tmp ~= 102 ) then + return false, "Connect recieved unexpected response" + end - pos = pos + 6 - pos, self.svcerror = bin.unpack(">s", data, pos) - pos, self.oserror = bin.unpack(">s", data, pos ) + pos = pos + 6 + pos, self.svcerror = bin.unpack(">s", data, pos) + pos, self.oserror = bin.unpack(">s", data, pos ) - if ( self.svcerror ~= 0 ) then - self.errmsg = Constants.ErrorMsg[self.svcerror] or ("Unknown error %d occured"):format( self.svcerror ) - end + if ( self.svcerror ~= 0 ) then + self.errmsg = Constants.ErrorMsg[self.svcerror] or ("Unknown error %d occured"):format( self.svcerror ) + end - return true - end, + return true + end, - --- Converts the class to a string suitable to send over the socket - -- - -- @return string containing the packet data - __tostring = function( self ) - local data - local unknown = [[ - 013c0000006400650000003d0006494545454d00006c73716c65786563000000 - 00000006392e32383000000c524453235230303030303000000573716c690000 - 00013300000000000000000001 - ]] + --- Converts the class to a string suitable to send over the socket + -- + -- @return string containing the packet data + __tostring = function( self ) + local data + local unknown = [[ + 013c0000006400650000003d0006494545454d00006c73716c65786563000000 + 00000006392e32383000000c524453235230303030303000000573716c690000 + 00013300000000000000000001 + ]] - local unknown2 = [[ - 6f6c0000000000000000003d746c697463700000000000010068000b - 00000003 - ]] + local unknown2 = [[ + 6f6c0000000000000000003d746c697463700000000000010068000b + 00000003 + ]] - local unknown3 = [[ - 00000000000000000000006a - ]] + local unknown3 = [[ + 00000000000000000000006a + ]] - local unknown4 = [[ 007f ]] + local unknown4 = [[ 007f ]] - if ( not(self.parameters) ) then - self.parameters = {} - self:addDefaultParameters() - end + if ( not(self.parameters) ) then + self.parameters = {} + self:addDefaultParameters() + end - data = bin.pack(">HPPHPHS", unknown, self.username, self.password, unknown2, self.instance, unknown3, #self.parameters ) + data = bin.pack(">HPPHPHS", unknown, self.username, self.password, unknown2, self.instance, unknown3, #self.parameters ) - if ( self.parameters ) then - for _, v in ipairs( self.parameters ) do - for k2, v2 in pairs( v ) do - data = data .. Util.paramToString( k2 .. "\0", v2 .. "\0" ) - end - end - end + if ( self.parameters ) then + for _, v in ipairs( self.parameters ) do + for k2, v2 in pairs( v ) do + data = data .. Util.paramToString( k2 .. "\0", v2 .. "\0" ) + end + end + end - data = data .. bin.pack("H", unknown4) - data = bin.pack(">S", #data + 2) .. data + data = data .. bin.pack("H", unknown4) + data = bin.pack(">S", #data + 2) .. data - return data - end, + return data + end, } @@ -1194,202 +1194,202 @@ Packet.Connect = { -- The communication class Comm = { - --- Creates a new Comm instance - -- - -- @param socket containing a buffered socket connected to the server - -- @return a new Comm instance - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.socket = socket - return o - end, + --- Creates a new Comm instance + -- + -- @param socket containing a buffered socket connected to the server + -- @return a new Comm instance + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.socket = socket + return o + end, - --- Sends and packet and attempts to handle the response - -- - -- @param packets an instance of a Packet.* class - -- @param info any additional info to pass as the second parameter to the - -- decoder - -- @return status true on success, false on failure - -- @return data returned from the ResponseDecoder - exchIfxPacket = function( self, packet, info ) - local _, typ - local status, data = self.socket:send( tostring(packet) ) - if ( not(status) ) then return false, data end + --- Sends and packet and attempts to handle the response + -- + -- @param packets an instance of a Packet.* class + -- @param info any additional info to pass as the second parameter to the + -- decoder + -- @return status true on success, false on failure + -- @return data returned from the ResponseDecoder + exchIfxPacket = function( self, packet, info ) + local _, typ + local status, data = self.socket:send( tostring(packet) ) + if ( not(status) ) then return false, data end - status, data = self.socket:recv( 2 ) - _, typ = bin.unpack(">S", data) + status, data = self.socket:recv( 2 ) + _, typ = bin.unpack(">S", data) - if ( MessageDecoders[typ] ) then - status, data = MessageDecoders[typ]( self.socket, info ) - else - return false, ("Unsupported data returned from server (type: 0x%x)"):format(typ) - end + if ( MessageDecoders[typ] ) then + status, data = MessageDecoders[typ]( self.socket, info ) + else + return false, ("Unsupported data returned from server (type: 0x%x)"):format(typ) + end - return status, data - end + return status, data + end } -- The Helper class providing easy access to the other db functionality Helper = { - --- Creates a new Helper instance - -- - -- @param host table as passed to the action script function - -- @param port table as passed to the action script function - -- @param instance [optional] string containing the instance to connect to - -- in case left empty it's populated by the informix.instance script - -- argument. - -- @return Helper instance - new = function(self, host, port, instance) - local o = {} - setmetatable(o, self) - self.__index = self - o.host = host - o.port = port - o.socket = Socket:new() - o.instance = instance or "nmap_probe" - return o - end, + --- Creates a new Helper instance + -- + -- @param host table as passed to the action script function + -- @param port table as passed to the action script function + -- @param instance [optional] string containing the instance to connect to + -- in case left empty it's populated by the informix.instance script + -- argument. + -- @return Helper instance + new = function(self, host, port, instance) + local o = {} + setmetatable(o, self) + self.__index = self + o.host = host + o.port = port + o.socket = Socket:new() + o.instance = instance or "nmap_probe" + return o + end, - --- Connects to the Informix server - -- - -- @return true on success, false on failure - -- @return err containing error message when status is false - Connect = function( self ) - local status, data - local conn, packet + --- Connects to the Informix server + -- + -- @return true on success, false on failure + -- @return err containing error message when status is false + Connect = function( self ) + local status, data + local conn, packet - status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) + status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) - if( not(status) ) then - return status, data - end + if( not(status) ) then + return status, data + end - self.comm = Comm:new( self.socket ) + self.comm = Comm:new( self.socket ) - return true - end, + return true + end, - --- Attempts to login to the Informix database server - -- The optional parameters parameter takes any informix specific parameters - -- used to connect to the database. In case it's ommited a set of default - -- parameters are set. Parameters should be past as key, value pairs inside - -- of a table array as the following example: - -- - -- local params = { - -- [1] = { ["PARAM1"] = "VALUE1" }, - -- [2] = { ["PARAM2"] = "VALUE2" }, - -- } - -- - -- @param username string containing the username for authentication - -- @param password string containing the password for authentication - -- @param parameters [optional] table of informix specific parameters - -- @param database [optional] database to connect to - -- @param retry [optional] used when autodetecting instance - -- @return status true on success, false on failure - -- @return err containing the error message if status is false - Login = function( self, username, password, parameters, database, retry ) - local conn, status, data, len, packet + --- Attempts to login to the Informix database server + -- The optional parameters parameter takes any informix specific parameters + -- used to connect to the database. In case it's ommited a set of default + -- parameters are set. Parameters should be past as key, value pairs inside + -- of a table array as the following example: + -- + -- local params = { + -- [1] = { ["PARAM1"] = "VALUE1" }, + -- [2] = { ["PARAM2"] = "VALUE2" }, + -- } + -- + -- @param username string containing the username for authentication + -- @param password string containing the password for authentication + -- @param parameters [optional] table of informix specific parameters + -- @param database [optional] database to connect to + -- @param retry [optional] used when autodetecting instance + -- @return status true on success, false on failure + -- @return err containing the error message if status is false + Login = function( self, username, password, parameters, database, retry ) + local conn, status, data, len, packet - conn = Packet.Connect:new( username, password, self.instance, parameters ) + conn = Packet.Connect:new( username, password, self.instance, parameters ) - status, data = self.socket:send( tostring(conn) ) - if ( not(status) ) then return false, "Helper.Login failed to send login request" end - status = conn:readResponse( self.socket ) - if ( not(status) ) then return false, "Helper.Login failed to read response" end + status, data = self.socket:send( tostring(conn) ) + if ( not(status) ) then return false, "Helper.Login failed to send login request" end + status = conn:readResponse( self.socket ) + if ( not(status) ) then return false, "Helper.Login failed to read response" end - if ( status and ( conn:getOsError() ~= 0 or conn:getSvcError() ~= 0 ) ) then - -- Check if we didn't supply the correct instance name, if not attempt to - -- reconnect using the instance name returned by the server - if ( conn:getSvcError() == -761 and not(retry) ) then - self.instance = conn.applid - self:Close() - self:Connect() - return self:Login( username, password, parameters, database, 1 ) - end - return false, conn:getErrMsg() - end + if ( status and ( conn:getOsError() ~= 0 or conn:getSvcError() ~= 0 ) ) then + -- Check if we didn't supply the correct instance name, if not attempt to + -- reconnect using the instance name returned by the server + if ( conn:getSvcError() == -761 and not(retry) ) then + self.instance = conn.applid + self:Close() + self:Connect() + return self:Login( username, password, parameters, database, 1 ) + end + return false, conn:getErrMsg() + end - status, packet = self.comm:exchIfxPacket( Packet.SQ_PROTOCOLS:new() ) - if ( not(status) ) then return false, packet end + status, packet = self.comm:exchIfxPacket( Packet.SQ_PROTOCOLS:new() ) + if ( not(status) ) then return false, packet end - status, packet = self.comm:exchIfxPacket( Packet.SQ_INFO:new() ) - if ( not(status) ) then return false, packet end + status, packet = self.comm:exchIfxPacket( Packet.SQ_INFO:new() ) + if ( not(status) ) then return false, packet end - -- If a database was supplied continue further protocol negotiation and - -- attempt to open the database. - if ( database ) then - status, packet = self:OpenDatabase( database ) - if ( not(status) ) then return false, packet end - end + -- If a database was supplied continue further protocol negotiation and + -- attempt to open the database. + if ( database ) then + status, packet = self:OpenDatabase( database ) + if ( not(status) ) then return false, packet end + end - return true - end, + return true + end, - --- Opens a database - -- - -- @param database string containing the database name - -- @return status true on success, false on failure - -- @return err string containing the error message if status is false - OpenDatabase = function( self, database ) - return self.comm:exchIfxPacket( Packet.SQ_DBOPEN:new( database ) ) - end, + --- Opens a database + -- + -- @param database string containing the database name + -- @return status true on success, false on failure + -- @return err string containing the error message if status is false + OpenDatabase = function( self, database ) + return self.comm:exchIfxPacket( Packet.SQ_DBOPEN:new( database ) ) + end, - --- Attempts to retrieve a list of available databases - -- - -- @return status true on success, false on failure - -- @return databases array of database names or err on failure - GetDatabases = function( self ) - return self.comm:exchIfxPacket( Packet.SQ_DBLIST:new() ) - end, + --- Attempts to retrieve a list of available databases + -- + -- @return status true on success, false on failure + -- @return databases array of database names or err on failure + GetDatabases = function( self ) + return self.comm:exchIfxPacket( Packet.SQ_DBLIST:new() ) + end, - Query = function( self, query ) - local status, metadata, data, res - local id, seq = 0, 1 - local result = {} + Query = function( self, query ) + local status, metadata, data, res + local id, seq = 0, 1 + local result = {} - if ( type(query) == "string" ) then - query = stdnse.strsplit(";%s*", query) - end + if ( type(query) == "string" ) then + query = stdnse.strsplit(";%s*", query) + end - for _, q in ipairs( query ) do - if ( q:upper():match("^%s*SELECT") ) then - status, data = self.comm:exchIfxPacket( Packet.SQ_PREPARE:new( q ) ) - seq = seq + 1 - else - status, data = self.comm:exchIfxPacket( Packet.SQ_COMMAND:new( q .. ";" ) ) - end + for _, q in ipairs( query ) do + if ( q:upper():match("^%s*SELECT") ) then + status, data = self.comm:exchIfxPacket( Packet.SQ_PREPARE:new( q ) ) + seq = seq + 1 + else + status, data = self.comm:exchIfxPacket( Packet.SQ_COMMAND:new( q .. ";" ) ) + end - if( status and data ) then - metadata = data.metadata - status, data = self.comm:exchIfxPacket( Packet.SQ_ID:new( data.stmt_id, seq, "begin" ), { metadata = metadata, id = id, rows = nil, query=q } ) + if( status and data ) then + metadata = data.metadata + status, data = self.comm:exchIfxPacket( Packet.SQ_ID:new( data.stmt_id, seq, "begin" ), { metadata = metadata, id = id, rows = nil, query=q } ) - -- check if any rows were returned - if ( not( data.rows ) ) then - data = { query = q, info = "No rows returned" } - end - --if( not(status) ) then return false, data end - elseif( not(status) ) then - data = { query = q, ["error"] = "ERROR: " .. data } - else - data = { query = q, info = "No rows returned" } - end - table.insert( result, data ) - end + -- check if any rows were returned + if ( not( data.rows ) ) then + data = { query = q, info = "No rows returned" } + end + --if( not(status) ) then return false, data end + elseif( not(status) ) then + data = { query = q, ["error"] = "ERROR: " .. data } + else + data = { query = q, info = "No rows returned" } + end + table.insert( result, data ) + end - return true, result - end, + return true, result + end, - --- Closes the connection to the server - -- - -- @return status true on success, false on failure - Close = function( self ) - local status, packet = self.comm:exchIfxPacket( Packet.SQ_EXIT:new() ) - return self.socket:close() - end, + --- Closes the connection to the server + -- + -- @return status true on success, false on failure + Close = function( self ) + local status, packet = self.comm:exchIfxPacket( Packet.SQ_EXIT:new() ) + return self.socket:close() + end, } diff --git a/nselib/iscsi.lua b/nselib/iscsi.lua index 6a5688b2b..420b695f3 100644 --- a/nselib/iscsi.lua +++ b/nselib/iscsi.lua @@ -33,7 +33,7 @@ -- Version 0.2 -- Created 2010/11/18 - v0.1 - created by Patrik Karlsson -- Revised 2010/11/28 - v0.2 - improved error handling, fixed discovery issues --- with multiple addresses +-- with multiple addresses local bin = require "bin" @@ -49,378 +49,378 @@ _ENV = stdnse.module("iscsi", stdnse.seeall) Packet = { - Opcode = { - LOGIN = 0x03, - TEXT = 0x04, - LOGOUT = 0x06, - }, + Opcode = { + LOGIN = 0x03, + TEXT = 0x04, + LOGOUT = 0x06, + }, - LoginRequest = { + LoginRequest = { - CSG = { - SecurityNegotiation = 0, - LoginOperationalNegotiation = 1, - FullFeaturePhase = 3, - }, + CSG = { + SecurityNegotiation = 0, + LoginOperationalNegotiation = 1, + FullFeaturePhase = 3, + }, - NSG = { - SecurityNegotiation = 0, - LoginOperationalNegotiation = 1, - FullFeaturePhase = 3, - }, + NSG = { + SecurityNegotiation = 0, + LoginOperationalNegotiation = 1, + FullFeaturePhase = 3, + }, - --- Creates a new instance of LoginRequest - -- - -- @return instance of LoginRequest - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - o.immediate = 0 - o.opcode = Packet.Opcode.LOGIN - o.flags = {} - o.ver_max = 0 - o.ver_min = 0 - o.total_ahs_len = 0 - o.data_seg_len = 0 - o.isid = { t=0x01, a=0x00, b=0x0001, c=0x37, d=0 } - o.tsih = 0 - o.initiator_task_tag = 1 - o.cid = 1 - o.cmdsn = 0 - o.expstatsn = 1 - o.kvp = KVP:new() - return o - end, + --- Creates a new instance of LoginRequest + -- + -- @return instance of LoginRequest + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + o.immediate = 0 + o.opcode = Packet.Opcode.LOGIN + o.flags = {} + o.ver_max = 0 + o.ver_min = 0 + o.total_ahs_len = 0 + o.data_seg_len = 0 + o.isid = { t=0x01, a=0x00, b=0x0001, c=0x37, d=0 } + o.tsih = 0 + o.initiator_task_tag = 1 + o.cid = 1 + o.cmdsn = 0 + o.expstatsn = 1 + o.kvp = KVP:new() + return o + end, - setImmediate = function(self, b) self.immediate = ( b and 1 or 0 ) end, + setImmediate = function(self, b) self.immediate = ( b and 1 or 0 ) end, - --- Sets the transit bit - -- - -- @param b boolean containing the new transit value - setTransit = function(self, b) self.flags.transit = ( b and 1 or 0 ) end, + --- Sets the transit bit + -- + -- @param b boolean containing the new transit value + setTransit = function(self, b) self.flags.transit = ( b and 1 or 0 ) end, - --- Sets the continue bit - -- - -- @param b boolean containing the new continue value - setContinue = function(self, b) self.flags.continue = ( b and 1 or 0 ) end, + --- Sets the continue bit + -- + -- @param b boolean containing the new continue value + setContinue = function(self, b) self.flags.continue = ( b and 1 or 0 ) end, - --- Sets the CSG values - -- - -- @param csg number containing the new NSG value - setCSG = function(self, csg) self.flags.csg = csg end, + --- Sets the CSG values + -- + -- @param csg number containing the new NSG value + setCSG = function(self, csg) self.flags.csg = csg end, - --- Sets the NSG values - -- - -- @param nsg number containing the new NSG value - setNSG = function(self, nsg) self.flags.nsg = nsg end, + --- Sets the NSG values + -- + -- @param nsg number containing the new NSG value + setNSG = function(self, nsg) self.flags.nsg = nsg end, - --- Converts the class instance to string - -- - -- @return string containing the converted instance - __tostring = function( self ) - local reserved = 0 - local kvps = tostring(self.kvp) + --- Converts the class instance to string + -- + -- @return string containing the converted instance + __tostring = function( self ) + local reserved = 0 + local kvps = tostring(self.kvp) - self.data_seg_len = #kvps + self.data_seg_len = #kvps - local pad = 4 - ((#kvps + 48) % 4) - pad = ( pad == 4 ) and 0 or pad + local pad = 4 - ((#kvps + 48) % 4) + pad = ( pad == 4 ) and 0 or pad - for i=1, pad do kvps = kvps .. "\0" end + for i=1, pad do kvps = kvps .. "\0" end - local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len - local flags = bit.lshift( ( self.flags.transit or 0 ), 7 ) - flags = flags + bit.lshift( ( self.flags.continue or 0 ), 6) - flags = flags + ( self.flags.nsg or 0 ) - flags = flags + bit.lshift( ( self.flags.csg or 0 ), 2 ) + local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len + local flags = bit.lshift( ( self.flags.transit or 0 ), 7 ) + flags = flags + bit.lshift( ( self.flags.continue or 0 ), 6) + flags = flags + ( self.flags.nsg or 0 ) + flags = flags + bit.lshift( ( self.flags.csg or 0 ), 2 ) - local opcode = self.opcode + bit.lshift((self.immediate or 0), 6) + local opcode = self.opcode + bit.lshift((self.immediate or 0), 6) - local data = bin.pack(">CCCCICSCSSISSIILLA", opcode, - flags, self.ver_max, self.ver_min, len, - bit.lshift( self.isid.t, 6 ) + bit.band( self.isid.a, 0x3f), - self.isid.b, self.isid.c, self.isid.d, self.tsih, - self.initiator_task_tag, self.cid, reserved, self.cmdsn, - self.expstatsn, reserved, reserved, kvps ) + local data = bin.pack(">CCCCICSCSSISSIILLA", opcode, + flags, self.ver_max, self.ver_min, len, + bit.lshift( self.isid.t, 6 ) + bit.band( self.isid.a, 0x3f), + self.isid.b, self.isid.c, self.isid.d, self.tsih, + self.initiator_task_tag, self.cid, reserved, self.cmdsn, + self.expstatsn, reserved, reserved, kvps ) - return data - end + return data + end - }, + }, - LoginResponse = { + LoginResponse = { - -- Error messages - ErrorMsgs = { - [0x0000] = "Success", - [0x0101] = "Target moved temporarily", - [0x0102] = "Target moved permanently", - [0x0200] = "Initiator error", - [0x0201] = "Authentication failure", - [0x0202] = "Authorization failure", - [0x0203] = "Target not found", - [0x0204] = "Target removed", - [0x0205] = "Unsupported version", - [0x0206] = "Too many connections", - [0x0207] = "Missing parameter", - [0x0208] = "Can't include in session", - [0x0209] = "Session type not supported", - [0x020a] = "Session does not exist", - [0x020b] = "Invalid request during login", - [0x0300] = "Target error", - [0x0301] = "Service unavailable", - [0x0302] = "Out of resources", - }, + -- Error messages + ErrorMsgs = { + [0x0000] = "Success", + [0x0101] = "Target moved temporarily", + [0x0102] = "Target moved permanently", + [0x0200] = "Initiator error", + [0x0201] = "Authentication failure", + [0x0202] = "Authorization failure", + [0x0203] = "Target not found", + [0x0204] = "Target removed", + [0x0205] = "Unsupported version", + [0x0206] = "Too many connections", + [0x0207] = "Missing parameter", + [0x0208] = "Can't include in session", + [0x0209] = "Session type not supported", + [0x020a] = "Session does not exist", + [0x020b] = "Invalid request during login", + [0x0300] = "Target error", + [0x0301] = "Service unavailable", + [0x0302] = "Out of resources", + }, - -- Error constants - Errors = { - SUCCESS = 0, - AUTH_FAILED = 0x0201, - }, + -- Error constants + Errors = { + SUCCESS = 0, + AUTH_FAILED = 0x0201, + }, - --- Creates a new instance of LoginResponse - -- - -- @return instance of LoginResponse - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new instance of LoginResponse + -- + -- @return instance of LoginResponse + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Returns the error message - getErrorMessage = function( self ) - return Packet.LoginResponse.ErrorMsgs[self.status_code] or "Unknown error" - end, + --- Returns the error message + getErrorMessage = function( self ) + return Packet.LoginResponse.ErrorMsgs[self.status_code] or "Unknown error" + end, - --- Returns the error code - getErrorCode = function( self ) return self.status_code or 0 end, + --- Returns the error code + getErrorCode = function( self ) return self.status_code or 0 end, - --- Creates a LoginResponse with data read from the socket - -- - -- @return status true on success, false on failure - -- @return resp instance of LoginResponse - fromSocket = function( s ) - local status, header = s:recv(48) + --- Creates a LoginResponse with data read from the socket + -- + -- @return status true on success, false on failure + -- @return resp instance of LoginResponse + fromSocket = function( s ) + local status, header = s:recv(48) - if ( not(status) ) then - return false, "Failed to read header from socket" - end + if ( not(status) ) then + return false, "Failed to read header from socket" + end - local resp = Packet.LoginResponse:new() - local pos, len = bin.unpack(">I", header, 5) + local resp = Packet.LoginResponse:new() + local pos, len = bin.unpack(">I", header, 5) - resp.total_ahs_len = bit.rshift(len, 24) - resp.data_seg_len = bit.band(len, 0x00ffffff) - pos, resp.status_code = bin.unpack(">S", header, 37) + resp.total_ahs_len = bit.rshift(len, 24) + resp.data_seg_len = bit.band(len, 0x00ffffff) + pos, resp.status_code = bin.unpack(">S", header, 37) - local pad = ( 4 - ( resp.data_seg_len % 4 ) ) - pad = ( pad == 4 ) and 0 or pad + local pad = ( 4 - ( resp.data_seg_len % 4 ) ) + pad = ( pad == 4 ) and 0 or pad - local status, data = s:recv( resp.data_seg_len + pad ) - if ( not(status) ) then - return false, "Failed to read data from socket" - end + local status, data = s:recv( resp.data_seg_len + pad ) + if ( not(status) ) then + return false, "Failed to read data from socket" + end - resp.kvp = KVP:new() - for _, kvp in ipairs(stdnse.strsplit( "\0", data )) do - local k, v = kvp:match("(.*)=(.*)") - if ( v ) then resp.kvp:add( k, v ) end - end + resp.kvp = KVP:new() + for _, kvp in ipairs(stdnse.strsplit( "\0", data )) do + local k, v = kvp:match("(.*)=(.*)") + if ( v ) then resp.kvp:add( k, v ) end + end - return true, resp - end, + return true, resp + end, - }, + }, - TextRequest = { + TextRequest = { - --- Creates a new instance of TextRequest - -- - -- @return instance of TextRequest - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - o.opcode = Packet.Opcode.TEXT - o.flags = {} - o.flags.final = 0 - o.flags.continue = 0 - o.total_ahs_len = 0 - o.data_seg_len = 0 - o.lun = 0 - o.initiator_task_tag = 1 - o.target_trans_tag = 0xffffffff - o.cmdsn = 2 - o.expstatsn = 1 - o.kvp = KVP:new() - return o - end, + --- Creates a new instance of TextRequest + -- + -- @return instance of TextRequest + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + o.opcode = Packet.Opcode.TEXT + o.flags = {} + o.flags.final = 0 + o.flags.continue = 0 + o.total_ahs_len = 0 + o.data_seg_len = 0 + o.lun = 0 + o.initiator_task_tag = 1 + o.target_trans_tag = 0xffffffff + o.cmdsn = 2 + o.expstatsn = 1 + o.kvp = KVP:new() + return o + end, - --- Sets the final bit of the TextRequest - setFinal = function( self, b ) self.flags.final = ( b and 1 or 0 ) end, + --- Sets the final bit of the TextRequest + setFinal = function( self, b ) self.flags.final = ( b and 1 or 0 ) end, - --- Sets the continue bit of the TextRequest - setContinue = function( self, b ) self.flags.continue = ( b and 1 or 0 ) end, + --- Sets the continue bit of the TextRequest + setContinue = function( self, b ) self.flags.continue = ( b and 1 or 0 ) end, - --- Converts the class instance to string - -- - -- @return string containing the converted instance - __tostring = function(self) - local flags = bit.lshift( ( self.flags.final or 0 ), 7 ) - flags = flags + bit.lshift( (self.flags.continue or 0), 6 ) + --- Converts the class instance to string + -- + -- @return string containing the converted instance + __tostring = function(self) + local flags = bit.lshift( ( self.flags.final or 0 ), 7 ) + flags = flags + bit.lshift( (self.flags.continue or 0), 6 ) - local kvps = tostring(self.kvp) - for i=1, (#kvps % 2) do kvps = kvps .. "\0" end - self.data_seg_len = #kvps + local kvps = tostring(self.kvp) + for i=1, (#kvps % 2) do kvps = kvps .. "\0" end + self.data_seg_len = #kvps - local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len - local reserved = 0 - local data = bin.pack(">CCSILIIIILLA", self.opcode, flags, reserved, - len, self.lun, self.initiator_task_tag, self.target_trans_tag, - self.cmdsn, self.expstatsn, reserved, reserved, kvps) + local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len + local reserved = 0 + local data = bin.pack(">CCSILIIIILLA", self.opcode, flags, reserved, + len, self.lun, self.initiator_task_tag, self.target_trans_tag, + self.cmdsn, self.expstatsn, reserved, reserved, kvps) - return data - end, + return data + end, - }, + }, - TextResponse = { + TextResponse = { - --- Creates a new instance of TextResponse - -- - -- @return instance of TextResponse - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new instance of TextResponse + -- + -- @return instance of TextResponse + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Creates a TextResponse with data read from the socket - -- - -- @return status true on success, false on failure - -- @return instance of TextResponse - -- err string containing error message - fromSocket = function( s ) - local resp = Packet.TextResponse:new() - local textdata = "" + --- Creates a TextResponse with data read from the socket + -- + -- @return status true on success, false on failure + -- @return instance of TextResponse + -- err string containing error message + fromSocket = function( s ) + local resp = Packet.TextResponse:new() + local textdata = "" - repeat - local status, header = s:recv(48) - local pos, _, flags, _, _, len = bin.unpack(">CCCCI", header) - local cont = ( bit.band(flags, 0x40) == 0x40 ) + repeat + local status, header = s:recv(48) + local pos, _, flags, _, _, len = bin.unpack(">CCCCI", header) + local cont = ( bit.band(flags, 0x40) == 0x40 ) - resp.total_ahs_len = bit.rshift(len, 24) - resp.data_seg_len = bit.band(len, 0x00ffffff) + resp.total_ahs_len = bit.rshift(len, 24) + resp.data_seg_len = bit.band(len, 0x00ffffff) - local data - status, data = s:recv( resp.data_seg_len ) + local data + status, data = s:recv( resp.data_seg_len ) - textdata = textdata .. data + textdata = textdata .. data - until( not(cont) ) + until( not(cont) ) - resp.records = {} + resp.records = {} - local kvps = stdnse.strsplit( "\0", textdata ) - local record + local kvps = stdnse.strsplit( "\0", textdata ) + local record - -- Each target record starts with one text key of the form: - -- TargetName= - -- Followed by zero or more address keys of the form: - -- TargetAddress=[:], - -- - for _, kvp in ipairs(kvps) do - local k, v = kvp:match("(.*)%=(.*)") - if ( k == "TargetName" ) then - if ( record ) then - table.insert(resp.records, record) - record = {} - end - if ( #resp.records == 0 ) then record = {} end - record.name = v - elseif ( k == "TargetAddress" ) then - record.addr = record.addr or {} - table.insert( record.addr, v ) - elseif ( not(k) ) then - -- this should be the ending empty kvp - table.insert(resp.records, record) - break - else - stdnse.print_debug("ERROR: iscsi.TextResponse: Unknown target record (%s)", k) - end - end + -- Each target record starts with one text key of the form: + -- TargetName= + -- Followed by zero or more address keys of the form: + -- TargetAddress=[:], + -- + for _, kvp in ipairs(kvps) do + local k, v = kvp:match("(.*)%=(.*)") + if ( k == "TargetName" ) then + if ( record ) then + table.insert(resp.records, record) + record = {} + end + if ( #resp.records == 0 ) then record = {} end + record.name = v + elseif ( k == "TargetAddress" ) then + record.addr = record.addr or {} + table.insert( record.addr, v ) + elseif ( not(k) ) then + -- this should be the ending empty kvp + table.insert(resp.records, record) + break + else + stdnse.print_debug("ERROR: iscsi.TextResponse: Unknown target record (%s)", k) + end + end - return true, resp - end, - }, + return true, resp + end, + }, - --- Class handling a login request - LogoutRequest = { + --- Class handling a login request + LogoutRequest = { - --- Creates a new instance of LogoutRequest - -- - -- @return instance of LogoutRequest - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - o.opcode = Packet.Opcode.LOGOUT - o.immediate = 1 - o.reasoncode = 0 - o.total_ahs_len = 0 - o.data_seg_len = 0 - o.initiator_task_tag = 2 - o.cid = 1 - o.cmdsn = 0 - o.expstatsn = 1 - return o - end, + --- Creates a new instance of LogoutRequest + -- + -- @return instance of LogoutRequest + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + o.opcode = Packet.Opcode.LOGOUT + o.immediate = 1 + o.reasoncode = 0 + o.total_ahs_len = 0 + o.data_seg_len = 0 + o.initiator_task_tag = 2 + o.cid = 1 + o.cmdsn = 0 + o.expstatsn = 1 + return o + end, - --- Converts the class instance to string - -- - -- @return string containing the converted instance - __tostring = function(self) - local opcode = self.opcode + bit.lshift((self.immediate or 0), 6) - local reserved = 0 - local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len - local data = bin.pack(">CCSILISSIILL", opcode, (0x80 + self.reasoncode), - reserved, len, reserved,self.initiator_task_tag, self.cid, - reserved, self.cmdsn, self.expstatsn, reserved, reserved ) + --- Converts the class instance to string + -- + -- @return string containing the converted instance + __tostring = function(self) + local opcode = self.opcode + bit.lshift((self.immediate or 0), 6) + local reserved = 0 + local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len + local data = bin.pack(">CCSILISSIILL", opcode, (0x80 + self.reasoncode), + reserved, len, reserved,self.initiator_task_tag, self.cid, + reserved, self.cmdsn, self.expstatsn, reserved, reserved ) - return data - end, - }, + return data + end, + }, - --- Class handling the Logout response - LogoutResponse = { + --- Class handling the Logout response + LogoutResponse = { - --- Creates a new instance of LogoutResponse - -- - -- @return instance of LogoutResponse - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new instance of LogoutResponse + -- + -- @return instance of LogoutResponse + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Creates a LogoutResponse with data read from the socket - -- - -- @return status true on success, false on failure - -- @return instance of LogoutResponse - -- err string containing error message - fromSocket = function( s ) - local resp = Packet.LogoutResponse:new() - local status, header = s:recv(48) - if ( not(status) ) then return status, header end - return true, resp - end + --- Creates a LogoutResponse with data read from the socket + -- + -- @return status true on success, false on failure + -- @return instance of LogoutResponse + -- err string containing error message + fromSocket = function( s ) + local resp = Packet.LogoutResponse:new() + local status, header = s:recv(48) + if ( not(status) ) then return status, header end + return true, resp + end - } + } } --- The communication class handles socket reads and writes @@ -428,43 +428,43 @@ Packet = { -- packets and updates cmdsn and expstatsn accordingly. Comm = { - --- Creates a new instance of Comm - -- - -- @return instance of Comm - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.expstatsn = 0 - o.cmdsn = 1 - o.socket = socket - return o - end, + --- Creates a new instance of Comm + -- + -- @return instance of Comm + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.expstatsn = 0 + o.cmdsn = 1 + o.socket = socket + return o + end, - --- Sends a packet and retrieves the response - -- - -- @param out_packet instance of a packet to send - -- @param in_class class of the packet to read - -- @return status true on success, false on failure - -- @return r decoded instance of in_class - exchange = function( self, out_packet, in_class ) + --- Sends a packet and retrieves the response + -- + -- @param out_packet instance of a packet to send + -- @param in_class class of the packet to read + -- @return status true on success, false on failure + -- @return r decoded instance of in_class + exchange = function( self, out_packet, in_class ) - local expstatsn = ( self.expstatsn == 0 ) and 1 or self.expstatsn + local expstatsn = ( self.expstatsn == 0 ) and 1 or self.expstatsn - if ( out_packet.immediate and out_packet.immediate == 1 ) then - self.cmdsn = self.cmdsn + 1 - end + if ( out_packet.immediate and out_packet.immediate == 1 ) then + self.cmdsn = self.cmdsn + 1 + end - out_packet.expstatsn = expstatsn - out_packet.cmdsn = self.cmdsn + out_packet.expstatsn = expstatsn + out_packet.cmdsn = self.cmdsn - self.socket:send( tostring( out_packet ) ) + self.socket:send( tostring( out_packet ) ) - local status, r = in_class.fromSocket( self.socket ) - self.expstatsn = self.expstatsn + 1 + local status, r = in_class.fromSocket( self.socket ) + self.expstatsn = self.expstatsn + 1 - return status, r - end, + return status, r + end, } @@ -473,322 +473,322 @@ Comm = { Socket = { - --- Creates a new instance of Socket - -- - -- @return instance of Socket - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - o.Socket = nmap.new_socket() - o.Buffer = nil - return o - end, + --- Creates a new instance of Socket + -- + -- @return instance of Socket + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + o.Socket = nmap.new_socket() + o.Buffer = nil + return o + end, - --- Establishes a connection. - -- - -- @param hostid Hostname or IP address. - -- @param port Port number. - -- @param protocol "tcp", "udp", or - -- @return Status (true or false). - -- @return Error code (if status is false). - connect = function( self, hostid, port, protocol ) - self.Socket:set_timeout(10000) - return self.Socket:connect( hostid, port, protocol ) - end, + --- Establishes a connection. + -- + -- @param hostid Hostname or IP address. + -- @param port Port number. + -- @param protocol "tcp", "udp", or + -- @return Status (true or false). + -- @return Error code (if status is false). + connect = function( self, hostid, port, protocol ) + self.Socket:set_timeout(10000) + return self.Socket:connect( hostid, port, protocol ) + end, - --- Closes an open connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - close = function( self ) - return self.Socket:close() - end, + --- Closes an open connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + close = function( self ) + return self.Socket:close() + end, - --- Opposed to the socket:receive_bytes function, that returns - -- at least x bytes, this function returns the amount of bytes requested. - -- - -- @param count of bytes to read - -- @return true on success, false on failure - -- @return data containing bytes read from the socket - -- err containing error message if status is false - recv = function( self, count ) - local status, data + --- Opposed to the socket:receive_bytes function, that returns + -- at least x bytes, this function returns the amount of bytes requested. + -- + -- @param count of bytes to read + -- @return true on success, false on failure + -- @return data containing bytes read from the socket + -- err containing error message if status is false + recv = function( self, count ) + local status, data - self.Buffer = self.Buffer or "" + self.Buffer = self.Buffer or "" - if ( #self.Buffer < count ) then - status, data = self.Socket:receive_bytes( count - #self.Buffer ) - if ( not(status) or #data < count - #self.Buffer ) then - return false, data - end - self.Buffer = self.Buffer .. data - end + if ( #self.Buffer < count ) then + status, data = self.Socket:receive_bytes( count - #self.Buffer ) + if ( not(status) or #data < count - #self.Buffer ) then + return false, data + end + self.Buffer = self.Buffer .. data + end - data = self.Buffer:sub( 1, count ) - self.Buffer = self.Buffer:sub( count + 1) + data = self.Buffer:sub( 1, count ) + self.Buffer = self.Buffer:sub( count + 1) - return true, data - end, + return true, data + end, - --- Sends data over the socket - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, data ) - return self.Socket:send( data ) - end, + --- Sends data over the socket + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, data ) + return self.Socket:send( data ) + end, } --- Key/Value pairs class KVP = { - --- Creates a new instance of KVP - -- - -- @return instance of KVP - new = function( self ) - local o = {} - setmetatable(o, self) - self.__index = self - o.kvp = {} - return o - end, + --- Creates a new instance of KVP + -- + -- @return instance of KVP + new = function( self ) + local o = {} + setmetatable(o, self) + self.__index = self + o.kvp = {} + return o + end, - --- Adds a key/value pair - -- - -- @param key string containing the key name - -- @param value string containing the value - add = function( self, key, value ) - table.insert( self.kvp, {[key]=value} ) - end, + --- Adds a key/value pair + -- + -- @param key string containing the key name + -- @param value string containing the value + add = function( self, key, value ) + table.insert( self.kvp, {[key]=value} ) + end, - --- Gets all values for a specific key - -- - -- @param key string containing the name of the key to retrieve - -- @return values table containing all values for the specified key - get = function( self, key ) - local values = {} - for _, kvp in ipairs(self.kvp) do - for k, v in pairs( kvp ) do - if ( key == k ) then - table.insert( values, v ) - end - end - end - return values - end, + --- Gets all values for a specific key + -- + -- @param key string containing the name of the key to retrieve + -- @return values table containing all values for the specified key + get = function( self, key ) + local values = {} + for _, kvp in ipairs(self.kvp) do + for k, v in pairs( kvp ) do + if ( key == k ) then + table.insert( values, v ) + end + end + end + return values + end, - --- Returns all key value pairs as string delimited by \0 - -- eg. "key1=val1\0key2=val2\0" - -- - -- @return string containing all key/value pairs - __tostring = function( self ) - local ret = "" - for _, kvp in ipairs(self.kvp) do - for k, v in pairs( kvp ) do - ret = ret .. ("%s=%s\0"):format(k,v) - end - end - return ret - end, + --- Returns all key value pairs as string delimited by \0 + -- eg. "key1=val1\0key2=val2\0" + -- + -- @return string containing all key/value pairs + __tostring = function( self ) + local ret = "" + for _, kvp in ipairs(self.kvp) do + for k, v in pairs( kvp ) do + ret = ret .. ("%s=%s\0"):format(k,v) + end + end + return ret + end, } --- CHAP authentication class CHAP = { - --- Calculate a CHAP - response - -- - -- @param identifier number containing the CHAP identifier - -- @param challenge string containing the challenge - -- @param secret string containing the users password - -- @return response string containing the CHAP response - calcResponse = function( identifier, challenge, secret ) - return openssl.md5( identifier .. secret .. challenge ) - end, + --- Calculate a CHAP - response + -- + -- @param identifier number containing the CHAP identifier + -- @param challenge string containing the challenge + -- @param secret string containing the users password + -- @return response string containing the CHAP response + calcResponse = function( identifier, challenge, secret ) + return openssl.md5( identifier .. secret .. challenge ) + end, } --- The helper class contains functions with more descriptive names Helper = { - --- Creates a new instance of the Helper class - -- - -- @param host table as received by the script action function - -- @param port table as received by the script action function - -- @return o instance of Helper - new = function( self, host, port ) - local o = {} - setmetatable(o, self) - self.__index = self - o.host, o.port = host, port - o.socket = Socket:new() - return o - end, + --- Creates a new instance of the Helper class + -- + -- @param host table as received by the script action function + -- @param port table as received by the script action function + -- @return o instance of Helper + new = function( self, host, port ) + local o = {} + setmetatable(o, self) + self.__index = self + o.host, o.port = host, port + o.socket = Socket:new() + return o + end, - --- Connects to the iSCSI target - -- - -- @return status true on success, false on failure - -- @return err string containing error message is status is false - connect = function( self ) - local status, err = self.socket:connect(self.host, self.port, "tcp") - if ( not(status) ) then return false, err end + --- Connects to the iSCSI target + -- + -- @return status true on success, false on failure + -- @return err string containing error message is status is false + connect = function( self ) + local status, err = self.socket:connect(self.host, self.port, "tcp") + if ( not(status) ) then return false, err end - self.comm = Comm:new( self.socket ) - return true - end, + self.comm = Comm:new( self.socket ) + return true + end, - --- Attempts to discover accessible iSCSI targets on the remote server - -- - -- @return status true on success, false on failure - -- @return targets table containing discovered targets - -- each table entry is a target table with name - -- and addr. - -- err string containing an error message is status is false - discoverTargets = function( self ) - local p = Packet.LoginRequest:new() + --- Attempts to discover accessible iSCSI targets on the remote server + -- + -- @return status true on success, false on failure + -- @return targets table containing discovered targets + -- each table entry is a target table with name + -- and addr. + -- err string containing an error message is status is false + discoverTargets = function( self ) + local p = Packet.LoginRequest:new() - p:setTransit(true) - p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) - p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" ) - p.kvp:add( "SessionType", "Discovery" ) - p.kvp:add( "AuthMethod", "None" ) + p:setTransit(true) + p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) + p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" ) + p.kvp:add( "SessionType", "Discovery" ) + p.kvp:add( "AuthMethod", "None" ) - local status, resp = self.comm:exchange( p, Packet.LoginResponse ) - if ( not(status) ) then - return false, ("ERROR: iscsi.Helper.discoverTargets: %s"):format(resp) - end + local status, resp = self.comm:exchange( p, Packet.LoginResponse ) + if ( not(status) ) then + return false, ("ERROR: iscsi.Helper.discoverTargets: %s"):format(resp) + end - local auth_method = resp.kvp:get("AuthMethod")[1] - if ( auth_method:upper() ~= "NONE" ) then - return false, "ERROR: iscsi.Helper.discoverTargets: Unsupported authentication method" - end + local auth_method = resp.kvp:get("AuthMethod")[1] + if ( auth_method:upper() ~= "NONE" ) then + return false, "ERROR: iscsi.Helper.discoverTargets: Unsupported authentication method" + end - p = Packet.LoginRequest:new() - p:setTransit(true) - p:setNSG(Packet.LoginRequest.NSG.FullFeaturePhase) - p:setCSG(Packet.LoginRequest.CSG.LoginOperationalNegotiation) - p.kvp:add( "HeaderDigest", "None") - p.kvp:add( "DataDigest", "None") - p.kvp:add( "MaxRecvDataSegmentLength", "65536") - p.kvp:add( "DefaultTime2Wait", "0") - p.kvp:add( "DefaultTime2Retain", "60") + p = Packet.LoginRequest:new() + p:setTransit(true) + p:setNSG(Packet.LoginRequest.NSG.FullFeaturePhase) + p:setCSG(Packet.LoginRequest.CSG.LoginOperationalNegotiation) + p.kvp:add( "HeaderDigest", "None") + p.kvp:add( "DataDigest", "None") + p.kvp:add( "MaxRecvDataSegmentLength", "65536") + p.kvp:add( "DefaultTime2Wait", "0") + p.kvp:add( "DefaultTime2Retain", "60") - status, resp = self.comm:exchange( p, Packet.LoginResponse ) + status, resp = self.comm:exchange( p, Packet.LoginResponse ) - p = Packet.TextRequest:new() - p:setFinal(true) - p.kvp:add( "SendTargets", "All" ) - status, resp = self.comm:exchange( p, Packet.TextResponse ) + p = Packet.TextRequest:new() + p:setFinal(true) + p.kvp:add( "SendTargets", "All" ) + status, resp = self.comm:exchange( p, Packet.TextResponse ) - if ( not(resp.records) ) then - return false, "iscsi.discoverTargets: response returned no targets" - end + if ( not(resp.records) ) then + return false, "iscsi.discoverTargets: response returned no targets" + end - for _, record in ipairs(resp.records) do - table.sort( record.addr, function(a, b) local c = ipOps.compare_ip(a:match("(.-):"), "le", b:match("(.-):")); return c end ) - end - return true, resp.records - end, + for _, record in ipairs(resp.records) do + table.sort( record.addr, function(a, b) local c = ipOps.compare_ip(a:match("(.-):"), "le", b:match("(.-):")); return c end ) + end + return true, resp.records + end, - --- Logs out from the iSCSI target - -- - -- @return status true on success, false on failure - logout = function(self) - local p = Packet.LogoutRequest:new() - local status, resp = self.comm:exchange( p, Packet.LogoutResponse ) - return status - end, + --- Logs out from the iSCSI target + -- + -- @return status true on success, false on failure + logout = function(self) + local p = Packet.LogoutRequest:new() + local status, resp = self.comm:exchange( p, Packet.LogoutResponse ) + return status + end, - --- Authenticate to the iSCSI service - -- - -- @param target_name string containing the name of the iSCSI target - -- @param username string containing the username - -- @param password string containing the password - -- @param auth_method string containing either "None" or "Chap" - -- @return status true on success false on failure - -- @return response containing the loginresponse or - -- err string containing an error message if status is false - login = function( self, target_name, username, password, auth_method ) + --- Authenticate to the iSCSI service + -- + -- @param target_name string containing the name of the iSCSI target + -- @param username string containing the username + -- @param password string containing the password + -- @param auth_method string containing either "None" or "Chap" + -- @return status true on success false on failure + -- @return response containing the loginresponse or + -- err string containing an error message if status is false + login = function( self, target_name, username, password, auth_method ) - local auth_method = auth_method or "None" + local auth_method = auth_method or "None" - if ( not(target_name) ) then - return false, "No target name specified" - end + if ( not(target_name) ) then + return false, "No target name specified" + end - if ( auth_method:upper()~= "NONE" and - auth_method:upper()~= "CHAP" ) then - return false, "Unknown authentication method" - end + if ( auth_method:upper()~= "NONE" and + auth_method:upper()~= "CHAP" ) then + return false, "Unknown authentication method" + end - local p = Packet.LoginRequest:new() + local p = Packet.LoginRequest:new() - p:setTransit(true) - p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) - p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" ) - p.kvp:add( "SessionType", "Normal" ) - p.kvp:add( "TargetName", target_name ) - p.kvp:add( "AuthMethod", auth_method ) + p:setTransit(true) + p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) + p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" ) + p.kvp:add( "SessionType", "Normal" ) + p.kvp:add( "TargetName", target_name ) + p.kvp:add( "AuthMethod", auth_method ) - if ( not(self.comm) ) then - return false, "ERROR: iscsi.Helper.login: Not connected" - end - local status, resp = self.comm:exchange( p, Packet.LoginResponse ) - if ( not(status) ) then - return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) - end + if ( not(self.comm) ) then + return false, "ERROR: iscsi.Helper.login: Not connected" + end + local status, resp = self.comm:exchange( p, Packet.LoginResponse ) + if ( not(status) ) then + return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) + end - if ( resp.status_code ~= 0 ) then - stdnse.print_debug(3, "ERROR: iscsi.Helper.login: Authentication failed (error code: %d)", resp.status_code) - return false, resp - elseif ( auth_method:upper()=="NONE" ) then - return true, resp - end + if ( resp.status_code ~= 0 ) then + stdnse.print_debug(3, "ERROR: iscsi.Helper.login: Authentication failed (error code: %d)", resp.status_code) + return false, resp + elseif ( auth_method:upper()=="NONE" ) then + return true, resp + end - p = Packet.LoginRequest:new() - p.kvp:add( "CHAP_A", "5" ) - status, resp = self.comm:exchange( p, Packet.LoginResponse ) - if ( not(status) ) then - return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) - end + p = Packet.LoginRequest:new() + p.kvp:add( "CHAP_A", "5" ) + status, resp = self.comm:exchange( p, Packet.LoginResponse ) + if ( not(status) ) then + return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) + end - local alg = resp.kvp:get("CHAP_A")[1] - if ( alg ~= "5" ) then return false, "Unsupported authentication algorithm" end + local alg = resp.kvp:get("CHAP_A")[1] + if ( alg ~= "5" ) then return false, "Unsupported authentication algorithm" end - local chall = resp.kvp:get("CHAP_C")[1] - if ( not(chall) ) then return false, "Failed to decode challenge" end - chall = bin.pack("H", chall:sub(3)) + local chall = resp.kvp:get("CHAP_C")[1] + if ( not(chall) ) then return false, "Failed to decode challenge" end + chall = bin.pack("H", chall:sub(3)) - local ident = resp.kvp:get("CHAP_I")[1] - if (not(ident)) then return false, "Failed to decoded identifier" end - ident = string.char(tonumber(ident)) + local ident = resp.kvp:get("CHAP_I")[1] + if (not(ident)) then return false, "Failed to decoded identifier" end + ident = string.char(tonumber(ident)) - local resp = CHAP.calcResponse( ident, chall, password ) - resp = "0x" .. select(2, bin.unpack("H16", resp)) + local resp = CHAP.calcResponse( ident, chall, password ) + resp = "0x" .. select(2, bin.unpack("H16", resp)) - p = Packet.LoginRequest:new() - p:setImmediate(true) - p:setTransit(true) - p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) - p.kvp:add("CHAP_N", username) - p.kvp:add("CHAP_R", resp) + p = Packet.LoginRequest:new() + p:setImmediate(true) + p:setTransit(true) + p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation) + p.kvp:add("CHAP_N", username) + p.kvp:add("CHAP_R", resp) - status, resp = self.comm:exchange( p, Packet.LoginResponse ) - if ( not(status) ) then - return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) - end + status, resp = self.comm:exchange( p, Packet.LoginResponse ) + if ( not(status) ) then + return false, ("ERROR: iscsi.Helper.login: %s"):format(resp) + end - if ( resp:getErrorCode() ~= Packet.LoginResponse.Errors.SUCCESS ) then - return false, "Login failed" - end + if ( resp:getErrorCode() ~= Packet.LoginResponse.Errors.SUCCESS ) then + return false, "Login failed" + end - return true, resp - end, + return true, resp + end, - --- Disconnects the socket from the server - close = function(self) self.socket:close() end + --- Disconnects the socket from the server + close = function(self) self.socket:close() end } diff --git a/nselib/jdwp.lua b/nselib/jdwp.lua index 414460afe..d880b0a83 100644 --- a/nselib/jdwp.lua +++ b/nselib/jdwp.lua @@ -19,7 +19,7 @@ -- -- local status,socket = jdwp.connect(host,port) -- if not status then --- stdnse.print_debug("error, %s",socket) +-- stdnse.print_debug("error, %s",socket) -- end -- local version_info -- status, version_info = jdwp.getVersion(socket,0) @@ -44,68 +44,68 @@ _ENV = stdnse.module("jdwp", stdnse.seeall) -- JDWP protocol specific constants JDWP_CONSTANTS = { - handshake = "JDWP-Handshake" -- Connection initialization handshake + handshake = "JDWP-Handshake" -- Connection initialization handshake } -- List of error codes from: -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_Error ERROR_CODES = { - [0] = "NONE No error has occurred.", - [10] = "INVALID_THREAD Passed thread is null, is not a valid thread or has exited.", - [11] = "INVALID_THREAD_GROUP Thread group invalid.", - [12] = "INVALID_PRIORITY Invalid priority.", - [13] = "THREAD_NOT_SUSPENDED If the specified thread has not been suspended by an event.", - [14] = "THREAD_SUSPENDED Thread already suspended.", - [20] = "INVALID_OBJECT If this reference type has been unloaded and garbage collected.", - [21] = "INVALID_CLASS Invalid class.", - [22] = "CLASS_NOT_PREPARED Class has been loaded but not yet prepared.", - [23] = "INVALID_METHODID Invalid method.", - [24] = "INVALID_LOCATION Invalid location.", - [25] = "INVALID_FIELDID Invalid field.", - [30] = "INVALID_FRAMEID Invalid jframeID.", - [31] = "NO_MORE_FRAMES There are no more Java or JNI frames on the call stack.", - [32] = "OPAQUE_FRAME Information about the frame is not available.", - [33] = "NOT_CURRENT_FRAME Operation can only be performed on current frame.", - [34] = "TYPE_MISMATCH The variable is not an appropriate type for the function used.", - [35] = "INVALID_SLOT Invalid slot.", - [40] = "DUPLICATE Item already set.", - [41] = "NOT_FOUND Desired element not found.", - [50] = "INVALID_MONITOR Invalid monitor.", - [51] = "NOT_MONITOR_OWNER This thread doesn't own the monitor.", - [52] = "INTERRUPT The call has been interrupted before completion.", - [60] = "INVALID_CLASS_FORMAT The virtual machine attempted to read a class file and determined that the file is malformed or otherwise cannot be interpreted as a class file.", - [61] = "CIRCULAR_CLASS_DEFINITION A circularity has been detected while initializing a class.", - [62] = "FAILS_VERIFICATION The verifier detected that a class file, though well formed, contained some sort of internal inconsistency or security problem.", - [63] = "ADD_METHOD_NOT_IMPLEMENTED Adding methods has not been implemented.", - [64] = "SCHEMA_CHANGE_NOT_IMPLEMENTED Schema change has not been implemented.", - [65] = "INVALID_TYPESTATE The state of the thread has been modified, and is now inconsistent.", - [66] = "HIERARCHY_CHANGE_NOT_IMPLEMENTED A direct superclass is different for the new class version, or the set of directly implemented interfaces is different and canUnrestrictedlyRedefineClasses is false.", - [67] = "DELETE_METHOD_NOT_IMPLEMENTED The new class version does not declare a method declared in the old class version and canUnrestrictedlyRedefineClasses is false.", - [68] = "UNSUPPORTED_VERSION A class file has a version number not supported by this VM.", - [69] = "NAMES_DONT_MATCH The class name defined in the new class file is different from the name in the old class object.", - [70] = "CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED The new class version has different modifiers and and canUnrestrictedlyRedefineClasses is false.", - [71] = "METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED A method in the new class version has different modifiers than its counterpart in the old class version and and canUnrestrictedlyRedefineClasses is false.", - [99] = "NOT_IMPLEMENTED The functionality is not implemented in this virtual machine.", - [100] = "NULL_POINTER Invalid pointer.", - [101] = "ABSENT_INFORMATION Desired information is not available.", - [102] = "INVALID_EVENT_TYPE The specified event type id is not recognized.", - [103] = "ILLEGAL_ARGUMENT Illegal argument.", - [110] = "OUT_OF_MEMORY The function needed to allocate memory and no more memory was available for allocation.", - [111] = "ACCESS_DENIED Debugging has not been enabled in this virtual machine. JVMDI cannot be used.", - [112] = "VM_DEAD The virtual machine is not running.", - [113] = "INTERNAL An unexpected internal error has occurred.", - [115] = "UNATTACHED_THREAD The thread being used to call this function is not attached to the virtual machine. Calls must be made from attached threads.", - [500] = "INVALID_TAG object type id or class tag.", - [502] = "ALREADY_INVOKING Previous invoke not complete.", - [503] = "INVALID_INDEX Index is invalid.", - [504] = "INVALID_LENGTH The length is invalid.", - [506] = "INVALID_STRING The string is invalid.", - [507] = "INVALID_CLASS_LOADER The class loader is invalid.", - [508] = "INVALID_ARRAY The array is invalid.", - [509] = "TRANSPORT_LOAD Unable to load the transport.", - [510] = "TRANSPORT_INIT Unable to initialize the transport.", - [511] = "NATIVE_METHOD", - [512] = "INVALID_COUNT The count is invalid." + [0] = "NONE No error has occurred.", + [10] = "INVALID_THREAD Passed thread is null, is not a valid thread or has exited.", + [11] = "INVALID_THREAD_GROUP Thread group invalid.", + [12] = "INVALID_PRIORITY Invalid priority.", + [13] = "THREAD_NOT_SUSPENDED If the specified thread has not been suspended by an event.", + [14] = "THREAD_SUSPENDED Thread already suspended.", + [20] = "INVALID_OBJECT If this reference type has been unloaded and garbage collected.", + [21] = "INVALID_CLASS Invalid class.", + [22] = "CLASS_NOT_PREPARED Class has been loaded but not yet prepared.", + [23] = "INVALID_METHODID Invalid method.", + [24] = "INVALID_LOCATION Invalid location.", + [25] = "INVALID_FIELDID Invalid field.", + [30] = "INVALID_FRAMEID Invalid jframeID.", + [31] = "NO_MORE_FRAMES There are no more Java or JNI frames on the call stack.", + [32] = "OPAQUE_FRAME Information about the frame is not available.", + [33] = "NOT_CURRENT_FRAME Operation can only be performed on current frame.", + [34] = "TYPE_MISMATCH The variable is not an appropriate type for the function used.", + [35] = "INVALID_SLOT Invalid slot.", + [40] = "DUPLICATE Item already set.", + [41] = "NOT_FOUND Desired element not found.", + [50] = "INVALID_MONITOR Invalid monitor.", + [51] = "NOT_MONITOR_OWNER This thread doesn't own the monitor.", + [52] = "INTERRUPT The call has been interrupted before completion.", + [60] = "INVALID_CLASS_FORMAT The virtual machine attempted to read a class file and determined that the file is malformed or otherwise cannot be interpreted as a class file.", + [61] = "CIRCULAR_CLASS_DEFINITION A circularity has been detected while initializing a class.", + [62] = "FAILS_VERIFICATION The verifier detected that a class file, though well formed, contained some sort of internal inconsistency or security problem.", + [63] = "ADD_METHOD_NOT_IMPLEMENTED Adding methods has not been implemented.", + [64] = "SCHEMA_CHANGE_NOT_IMPLEMENTED Schema change has not been implemented.", + [65] = "INVALID_TYPESTATE The state of the thread has been modified, and is now inconsistent.", + [66] = "HIERARCHY_CHANGE_NOT_IMPLEMENTED A direct superclass is different for the new class version, or the set of directly implemented interfaces is different and canUnrestrictedlyRedefineClasses is false.", + [67] = "DELETE_METHOD_NOT_IMPLEMENTED The new class version does not declare a method declared in the old class version and canUnrestrictedlyRedefineClasses is false.", + [68] = "UNSUPPORTED_VERSION A class file has a version number not supported by this VM.", + [69] = "NAMES_DONT_MATCH The class name defined in the new class file is different from the name in the old class object.", + [70] = "CLASS_MODIFIERS_CHANGE_NOT_IMPLEMENTED The new class version has different modifiers and and canUnrestrictedlyRedefineClasses is false.", + [71] = "METHOD_MODIFIERS_CHANGE_NOT_IMPLEMENTED A method in the new class version has different modifiers than its counterpart in the old class version and and canUnrestrictedlyRedefineClasses is false.", + [99] = "NOT_IMPLEMENTED The functionality is not implemented in this virtual machine.", + [100] = "NULL_POINTER Invalid pointer.", + [101] = "ABSENT_INFORMATION Desired information is not available.", + [102] = "INVALID_EVENT_TYPE The specified event type id is not recognized.", + [103] = "ILLEGAL_ARGUMENT Illegal argument.", + [110] = "OUT_OF_MEMORY The function needed to allocate memory and no more memory was available for allocation.", + [111] = "ACCESS_DENIED Debugging has not been enabled in this virtual machine. JVMDI cannot be used.", + [112] = "VM_DEAD The virtual machine is not running.", + [113] = "INTERNAL An unexpected internal error has occurred.", + [115] = "UNATTACHED_THREAD The thread being used to call this function is not attached to the virtual machine. Calls must be made from attached threads.", + [500] = "INVALID_TAG object type id or class tag.", + [502] = "ALREADY_INVOKING Previous invoke not complete.", + [503] = "INVALID_INDEX Index is invalid.", + [504] = "INVALID_LENGTH The length is invalid.", + [506] = "INVALID_STRING The string is invalid.", + [507] = "INVALID_CLASS_LOADER The class loader is invalid.", + [508] = "INVALID_ARRAY The array is invalid.", + [509] = "TRANSPORT_LOAD Unable to load the transport.", + [510] = "TRANSPORT_INIT Unable to initialize the transport.", + [511] = "NATIVE_METHOD", + [512] = "INVALID_COUNT The count is invalid." } -- JDWP protocol Command packet as described at @@ -114,37 +114,37 @@ ERROR_CODES = { -- for that command. JDWPCommandPacket = { - new = function(self,id,command_set,command, data) - local o = { - id = id, - flags = 0, -- current specification has no flags defined for Command Packets - command_set = command_set, - command = command, - data = data - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self,id,command_set,command, data) + local o = { + id = id, + flags = 0, -- current specification has no flags defined for Command Packets + command_set = command_set, + command = command, + data = data + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Packs command packet as a string od bytes, ready to be sent - -- to the target debugee. - pack = function(self) - local packed_packet - if self.data == nil then - packed_packet = bin.pack(">I",11) -- lenght - minimal header is 11 bytes - else - packed_packet = bin.pack(">I",11 + #self.data) -- lenght with data - end - packed_packet = packed_packet .. bin.pack(">I",self.id) - packed_packet = packed_packet .. bin.pack(">C",0) -- flag - packed_packet = packed_packet .. bin.pack(">C",self.command_set) - packed_packet = packed_packet .. bin.pack(">C",self.command) - if self.data then - packed_packet = packed_packet .. self.data - end - return packed_packet - end + -- Packs command packet as a string od bytes, ready to be sent + -- to the target debugee. + pack = function(self) + local packed_packet + if self.data == nil then + packed_packet = bin.pack(">I",11) -- lenght - minimal header is 11 bytes + else + packed_packet = bin.pack(">I",11 + #self.data) -- lenght with data + end + packed_packet = packed_packet .. bin.pack(">I",self.id) + packed_packet = packed_packet .. bin.pack(">C",0) -- flag + packed_packet = packed_packet .. bin.pack(">C",self.command_set) + packed_packet = packed_packet .. bin.pack(">C",self.command) + if self.data then + packed_packet = packed_packet .. self.data + end + return packed_packet + end } -- JDWP protocol Reply packet as described at @@ -152,33 +152,33 @@ JDWPCommandPacket = { -- Reply packets are recognized by 0x80 in flag field. JDWPReplyPacket = { - new = function(self,length,id,error_code,data) - local o = { - length = length, - id = id, - flags = 0x80, -- no other flag is currently specified in the specification - error_code = error_code, -- see ERROR_CODES table - data = data -- reply data, contents depend on the command - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self,length,id,error_code,data) + local o = { + length = length, + id = id, + flags = 0x80, -- no other flag is currently specified in the specification + error_code = error_code, -- see ERROR_CODES table + data = data -- reply data, contents depend on the command + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses the reply into JDWPReplyPacket table. - parse_reply = function(self,reply_packet) - local pos,length,id,flags,error_code,data - pos, length = bin.unpack(">I",reply_packet) - pos, id = bin.unpack(">I",reply_packet,pos) - pos, flags = bin.unpack(">C",reply_packet,pos) - pos, error_code = bin.unpack(">S",reply_packet,pos) - data = string.sub(reply_packet,pos) - if flags == 0x80 then - return true, JDWPReplyPacket:new(length,id,error_code,data) - end - stdnse.print_debug(2,"JDWP error parsing reply. Wrong reply packet flag. Raw data: ", stdnse.tohex(reply_packet)) - return false, "JDWP error parsing reply." - end + -- Parses the reply into JDWPReplyPacket table. + parse_reply = function(self,reply_packet) + local pos,length,id,flags,error_code,data + pos, length = bin.unpack(">I",reply_packet) + pos, id = bin.unpack(">I",reply_packet,pos) + pos, flags = bin.unpack(">C",reply_packet,pos) + pos, error_code = bin.unpack(">S",reply_packet,pos) + data = string.sub(reply_packet,pos) + if flags == 0x80 then + return true, JDWPReplyPacket:new(length,id,error_code,data) + end + stdnse.print_debug(2,"JDWP error parsing reply. Wrong reply packet flag. Raw data: ", stdnse.tohex(reply_packet)) + return false, "JDWP error parsing reply." + end } @@ -189,29 +189,29 @@ JDWPReplyPacket = { --@return (status,socket) If status is false, socket is error message, otherwise socket is -- a newly created socket with initial handshake finished. function connect(host,port) - local status, result,err - local socket = nmap.new_socket("tcp") - socket:set_timeout(10000) - local status, err = socket:connect(host, port) - if not status then - stdnse.print_debug(2,"JDWP could not connect: %s",err) - return status, err - end - status, err = socket:send(JDWP_CONSTANTS.handshake) - if not status then - stdnse.print_debug(2,"JDWP could not send handshake: %s",err) - return status, err - end - status, result = socket:receive() - if not status then - stdnse.print_debug(2,"JDWP could not receive handshake: %s",result) - return status, result - end - if result == JDWP_CONSTANTS.handshake then - stdnse.print_debug("JDWP handshake successful.") - return true, socket - end - return false, "JDWP handshake unsuccessful." + local status, result,err + local socket = nmap.new_socket("tcp") + socket:set_timeout(10000) + local status, err = socket:connect(host, port) + if not status then + stdnse.print_debug(2,"JDWP could not connect: %s",err) + return status, err + end + status, err = socket:send(JDWP_CONSTANTS.handshake) + if not status then + stdnse.print_debug(2,"JDWP could not send handshake: %s",err) + return status, err + end + status, result = socket:receive() + if not status then + stdnse.print_debug(2,"JDWP could not receive handshake: %s",result) + return status, result + end + if result == JDWP_CONSTANTS.handshake then + stdnse.print_debug("JDWP handshake successful.") + return true, socket + end + return false, "JDWP handshake unsuccessful." end --- Helper function to pack regular string into UTF-8 string. @@ -219,8 +219,8 @@ end --@param data String to pack into UTF-8. --@return utf8_string UTF-8 packed string. Four bytes lenght followed by the string its self. function toUTF8(data) - local utf8_string = bin.pack(">i",#data) .. data - return utf8_string + local utf8_string = bin.pack(">i",#data) .. data + return utf8_string end --- Helper function to read all Reply packed data which might be fragmented @@ -229,20 +229,20 @@ end --@param socket Socket to receive from. --@return (status,data) If status is false, error string is returned, else data contains read ReplyPacket bytes. function receive_all(socket) - local status, result = socket:receive() - if not status then - return false,result - end - local data = result - local _, expected_length = bin.unpack(">I",result) -- first 4 bytes of packet data is the ReplyPacket length - while expected_length > #data do -- read until we get all the ReplyPacket data - status,result = socket:receive() - if not status then - return true, data -- if somethign is wrong,return partial data - end - data = data .. result - end - return true,data + local status, result = socket:receive() + if not status then + return false,result + end + local data = result + local _, expected_length = bin.unpack(">I",result) -- first 4 bytes of packet data is the ReplyPacket length + while expected_length > #data do -- read until we get all the ReplyPacket data + status,result = socket:receive() + if not status then + return true, data -- if somethign is wrong,return partial data + end + data = data .. result +end +return true,data end --- Helper function to extract ascii string from UTF-8 @@ -253,15 +253,15 @@ end --@param pos Offset into data string where to begin. --@return (pos,ascii_string) Returns position where the string extraction ended and actuall ascii string. local function extract_string(data,pos) - local string_size - if pos > #data then - stdnse.print_debug(2,"JDWP extract_string() position higher than data length, probably incomplete data received.") - return pos, nil - end - pos, string_size = bin.unpack(">I",data,pos) - local ascii_string = string.sub(data,pos,pos+string_size) - local new_pos = pos+string_size - return new_pos,ascii_string + local string_size + if pos > #data then + stdnse.print_debug(2,"JDWP extract_string() position higher than data length, probably incomplete data received.") + return pos, nil + end + pos, string_size = bin.unpack(">I",data,pos) + local ascii_string = string.sub(data,pos,pos+string_size) + local new_pos = pos+string_size + return new_pos,ascii_string end @@ -271,21 +271,21 @@ end --@param command JDWPCommandPacket to send. --@return (status,data) If status is false, data contains specified error code message. If true, data contains data from the reply. function executeCommand(socket,command) - socket:send(command:pack()) - local status, result = receive_all(socket) - if not status then - return false, "JDWP executeCommand() didn't get a reply." - end - local reply_packet - status, reply_packet = JDWPReplyPacket:parse_reply(result) - if not status then - return false, reply_packet - end - if not (reply_packet.error_code == 0) then -- we have a packet with error , error code 0 means no error occured - return false, ERROR_CODES[reply_packet.error_code] - end - local data = reply_packet.data - return true, data + socket:send(command:pack()) + local status, result = receive_all(socket) + if not status then + return false, "JDWP executeCommand() didn't get a reply." + end + local reply_packet + status, reply_packet = JDWPReplyPacket:parse_reply(result) + if not status then + return false, reply_packet + end + if not (reply_packet.error_code == 0) then -- we have a packet with error , error code 0 means no error occured + return false, ERROR_CODES[reply_packet.error_code] + end + local data = reply_packet.data + return true, data end --- VirtualMachine Command Set (1) @@ -301,33 +301,33 @@ end -- * 'jdwpMajor' Number representing major JDWP version. -- * 'jdwpMinor' Number representing minor JDWP version. -- * 'vmVersion' String representing version of the debuggee VM. --- * 'vmName' Name of the debuggee VM. +-- * 'vmName' Name of the debuggee VM. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_Version -- --@param socket Socket to use to send the command. ---@param id Packet id. +--@param id Packet id. --@return (status,version_info) If status is false, version_info is an error string, else it contains remote VM version info. function getVersion(socket,id) - local command = JDWPCommandPacket:new(id,1,1,nil) -- Version Command (1) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getVersion() error : %s",data) - return false,data - end - -- parse data - local version_info = {description = "", - jdwpMajor = 0, - jdwpMinor = 0, - vmVersion = "", - vmName = ""} - local vmVersionSize - local pos - pos, version_info.description = extract_string(data,0) - pos, version_info.jdwpMajor = bin.unpack(">i",data,pos) - pos, version_info.jdwpMinor = bin.unpack(">i",data,pos) - pos, version_info.vmVersion = extract_string(data,pos) - pos, version_info.vmName = extract_string(data,pos) - return true, version_info + local command = JDWPCommandPacket:new(id,1,1,nil) -- Version Command (1) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getVersion() error : %s",data) + return false,data + end + -- parse data + local version_info = {description = "", + jdwpMajor = 0, + jdwpMinor = 0, + vmVersion = "", + vmName = ""} + local vmVersionSize + local pos + pos, version_info.description = extract_string(data,0) + pos, version_info.jdwpMajor = bin.unpack(">i",data,pos) + pos, version_info.jdwpMinor = bin.unpack(">i",data,pos) + pos, version_info.vmVersion = extract_string(data,pos) + pos, version_info.vmName = extract_string(data,pos) + return true, version_info end --- Classes by Signature command (2) @@ -338,35 +338,35 @@ end -- * 'refTypeTag' JNI type tag -- * 'referenceTypeID' Reference type of the class -- * 'status' Current class status. --- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_ClassesBySignature +-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_ClassesBySignature -- ---@param socket Socket to use to send the command. ---@param id Packet id. +--@param socket Socket to use to send the command. +--@param id Packet id. --@param signature Signature of the class. --@return (status,classes) If status is false, classes is an error string, else it contains list of found classes. function getClassBySignature(socket,id,signature) - local command = JDWPCommandPacket:new(id,1,2,toUTF8(signature)) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getClassBySignature() error : %s",data) - return false,data - end - -- parse data - local classes = {} - local pos,number_of_classes = bin.unpack(">i",data) + local command = JDWPCommandPacket:new(id,1,2,toUTF8(signature)) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getClassBySignature() error : %s",data) + return false,data + end + -- parse data + local classes = {} + local pos,number_of_classes = bin.unpack(">i",data) - for i = 1, number_of_classes do - local class_info = { - refTypeTag = nil, - referenceTypeID = nil, - status = nil - } - pos, class_info.refTypeTag = bin.unpack("c",data,pos) - pos, class_info.referenceTypeID = bin.unpack(">L",data,pos) - pos, class_info.status = bin.unpack(">i",data,pos) - table.insert(classes,class_info) - end - return true, classes + for i = 1, number_of_classes do + local class_info = { + refTypeTag = nil, + referenceTypeID = nil, + status = nil + } + pos, class_info.refTypeTag = bin.unpack("c",data,pos) + pos, class_info.referenceTypeID = bin.unpack(">L",data,pos) + pos, class_info.status = bin.unpack(">i",data,pos) + table.insert(classes,class_info) + end + return true, classes end --- AllThreads Command (4) @@ -374,25 +374,25 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_AllThreads -- ---@param socket Socket to use to send the command. ---@param id Packet id. +--@param socket Socket to use to send the command. +--@param id Packet id. --@return (status, threads) If status is false threads contains an error string, else it conatins a list of all threads in the debuggee VM. function getAllThreads(socket,id) - local command = JDWPCommandPacket:new(id,1,4,nil) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getAllThreads() error: %s", data) - return false,data - end - -- parse data - local pos,number_of_threads = bin.unpack(">i",data) - local threads = {} - for i = 1, number_of_threads do - local thread - pos, thread = bin.unpack(">L",data,pos) - table.insert(threads,thread) - end - return true, threads + local command = JDWPCommandPacket:new(id,1,4,nil) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getAllThreads() error: %s", data) + return false,data + end + -- parse data + local pos,number_of_threads = bin.unpack(">i",data) + local threads = {} + for i = 1, number_of_threads do + local thread + pos, thread = bin.unpack(">L",data,pos) + table.insert(threads,thread) + end + return true, threads end --- Resume Command (9) @@ -400,22 +400,22 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_Resume -- ---@param socket Socket to use to send the command. ---@param id Packet id. +--@param socket Socket to use to send the command. +--@param id Packet id. --@return (status, nil) If status is false error string is returned, else it's null since this command has no data in the reply. function resumeVM(socket,id) - local command = JDWPCommandPacket:new(id,1,9,nil) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP resumeVM() error: %s", data) - return false,data - end - -- wait for event notification - status, data = receive_all(socket) - if not status then - stdnse.print_debug(2,"JDWP resumeVM() event notification failed: %s", data) - end - return true, nil + local command = JDWPCommandPacket:new(id,1,9,nil) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP resumeVM() error: %s", data) + return false,data + end + -- wait for event notification + status, data = receive_all(socket) + if not status then + stdnse.print_debug(2,"JDWP resumeVM() event notification failed: %s", data) + end + return true, nil end --- CreateString Command (11) @@ -423,63 +423,63 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_CreateString -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param ascii_string String to create. ---@return (status, stringID) If status is false error string is returned, else stringID is newly created string. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param ascii_string String to create. +--@return (status, stringID) If status is false error string is returned, else stringID is newly created string. function createString(socket,id,ascii_string) - local command = JDWPCommandPacket:new(id,1,11,toUTF8(ascii_string)) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP createString() error: %s", data) - return false,data - end - local _,stringID = bin.unpack(">L",data) - return true, stringID + local command = JDWPCommandPacket:new(id,1,11,toUTF8(ascii_string)) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP createString() error: %s", data) + return false,data + end + local _,stringID = bin.unpack(">L",data) + return true, stringID end --- AllClassesWithGeneric Command (20) -- Returns reference types and signatures for all classes currently loaded by the target VM. -- -- Returns a list of tables containing following info: --- * 'refTypeTag' Kind of following reference type. --- * 'typeID' Loaded reference type --- * 'signature' The JNI signature of the loaded reference type. --- * 'genericSignature' The generic signature of the loaded reference type or an empty string if there is none. --- * 'status' The current class status. +-- * 'refTypeTag' Kind of following reference type. +-- * 'typeID' Loaded reference type +-- * 'signature' The JNI signature of the loaded reference type. +-- * 'genericSignature' The generic signature of the loaded reference type or an empty string if there is none. +-- * 'status' The current class status. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_AllClassesWithGeneric -- ---@param socket Socket to use to send the command. ---@param id Packet id. +--@param socket Socket to use to send the command. +--@param id Packet id. --@return (status, all_classes) If status is false all_classes contains an error string, else it is a list of loaded classes information. function getAllClassesWithGeneric(socket,id) - local command = JDWPCommandPacket:new(id,1,20,nil) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getAllClassesWithGeneric() error: %s", data) - return false,data - end - -- parse data - local all_classes = {} - local pos,number_of_classes = bin.unpack(">i",data) + local command = JDWPCommandPacket:new(id,1,20,nil) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getAllClassesWithGeneric() error: %s", data) + return false,data + end + -- parse data + local all_classes = {} + local pos,number_of_classes = bin.unpack(">i",data) - for i = 0 , number_of_classes do - local class = { - refTypeTag = nil, - typeID = nil, - signature = nil, - genericSignature = nil, - status = nil - } - if pos > #data then break end - pos, class.refTypeTag = bin.unpack("C",data,pos) - pos, class.typeID = bin.unpack(">L",data,pos) - pos, class.signature = extract_string(data,pos) - pos, class.genericSignature = extract_string(data,pos) - pos, class.status = bin.unpack(">i",data,pos) - table.insert(all_classes,class) - end - return true, all_classes + for i = 0 , number_of_classes do + local class = { + refTypeTag = nil, + typeID = nil, + signature = nil, + genericSignature = nil, + status = nil + } + if pos > #data then break end + pos, class.refTypeTag = bin.unpack("C",data,pos) + pos, class.typeID = bin.unpack(">L",data,pos) + pos, class.signature = extract_string(data,pos) + pos, class.genericSignature = extract_string(data,pos) + pos, class.status = bin.unpack(">i",data,pos) + table.insert(all_classes,class) + end + return true, all_classes end --- ReferenceType Command Set (2) @@ -491,64 +491,64 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType_SignatureWithGeneric -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param classID Reference type id of the class to get the signature from. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param classID Reference type id of the class to get the signature from. --@return (status, signature) If status is false signature contains an error string, else it is class signature (like "Ljava/lang/Class"). function getSignatureWithGeneric(socket,id,classID) - local command = JDWPCommandPacket:new(id,2,13,bin.pack(">L",classID)) -- Version Command (1) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getVersion() error : %s",data) - return false,data - end - local _,signature = extract_string(data,0) - -- parse data - return true,signature + local command = JDWPCommandPacket:new(id,2,13,bin.pack(">L",classID)) -- Version Command (1) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getVersion() error : %s",data) + return false,data + end + local _,signature = extract_string(data,0) + -- parse data + return true,signature end --- MethodsWithGeneric Command (15) -- Returns information, including the generic signature if any, for each method in a reference type. -- -- Returns a list of tables containing following fields for each method: --- * 'methodID' Method ID which can be used to call the method. --- * 'name' The name of the method. --- * 'signature' The JNI signature of the method. --- * 'generic_signature' The generic signature of the method, or an empty string if there is none. --- * 'modBits' The modifier bit flags (also known as access flags) which provide additional information on the method declaration. +-- * 'methodID' Method ID which can be used to call the method. +-- * 'name' The name of the method. +-- * 'signature' The JNI signature of the method. +-- * 'generic_signature' The generic signature of the method, or an empty string if there is none. +-- * 'modBits' The modifier bit flags (also known as access flags) which provide additional information on the method declaration. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType_MethodsWithGeneric -- ---@param socket Socket to use to send the command. ---@param id Packet id. +--@param socket Socket to use to send the command. +--@param id Packet id. --@param classID Reference type id of the class to get the list of methods. --@return (status, signature) If status is false methods contains an error string, else it a list of methods information. function getMethodsWithGeneric(socket,id,classID) - local command = JDWPCommandPacket:new(id,2,15,bin.pack(">L",classID)) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getMethodsWithGeneric() error : %s",data) - return false,data - end - -- parse data - local methods = {} - local pos,number_of_methods = bin.unpack(">i",data) + local command = JDWPCommandPacket:new(id,2,15,bin.pack(">L",classID)) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getMethodsWithGeneric() error : %s",data) + return false,data + end + -- parse data + local methods = {} + local pos,number_of_methods = bin.unpack(">i",data) - for i = 1, number_of_methods do - local method_info = { - methodID = nil, - name = nil, - signature = nil, - generic_signature = nil, - modBits = nil - } - pos, method_info.methodID = bin.unpack(">i",data,pos) - pos,method_info.name = extract_string(data,pos) - pos, method_info.signature = extract_string(data,pos) - pos,method_info.generic_signature = extract_string(data,pos) - pos, method_info.modBits = bin.unpack(">i",data,pos) - table.insert(methods,method_info) - end - return true, methods + for i = 1, number_of_methods do + local method_info = { + methodID = nil, + name = nil, + signature = nil, + generic_signature = nil, + modBits = nil + } + pos, method_info.methodID = bin.unpack(">i",data,pos) + pos,method_info.name = extract_string(data,pos) + pos, method_info.signature = extract_string(data,pos) + pos,method_info.generic_signature = extract_string(data,pos) + pos, method_info.modBits = bin.unpack(">i",data,pos) + table.insert(methods,method_info) + end + return true, methods end --- ClassType Command Set (3) @@ -560,29 +560,29 @@ end -- Reply data can vary so parsing is left to the function caller. -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType_InvokeMethod -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param classID Reference type id of the class. ---@param methodID ID of the static method to call. ---@numberOfArguments Number of method arguments. ---@arguments Already packed arguments. ---@options Invocation options. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param classID Reference type id of the class. +--@param methodID ID of the static method to call. +--@numberOfArguments Number of method arguments. +--@arguments Already packed arguments. +--@options Invocation options. --@return (status, data) If status is false data contains an error string, else it contains a reply data and needs to be parsed manualy. function invokeStaticMethod(socket,id,classID,methodID,numberOfArguments,arguments,options) - local params - if numberOfArguments == 0 then - params = bin.pack(">Liii",classID,methodID,numberOfArguments,options) - else - params = bin.pack(">Lii",classID,methodID,numberOfArguments) .. arguments .. bin.pack(">i",options) - end + local params + if numberOfArguments == 0 then + params = bin.pack(">Liii",classID,methodID,numberOfArguments,options) + else + params = bin.pack(">Lii",classID,methodID,numberOfArguments) .. arguments .. bin.pack(">i",options) + end - local command = JDWPCommandPacket:new(id,3,3,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP invokeStaticMethod() error: %s", data) - return false,data - end - return true,data + local command = JDWPCommandPacket:new(id,3,3,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP invokeStaticMethod() error: %s", data) + return false,data + end + return true,data end --- NewInstance Command (4) @@ -591,61 +591,61 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType_NewInstance -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param classID Reference type id of the class. ---@param threadID The thread in which to invoke the constructor. ---@param methodID The constructor to invoke. ---@numberOfArguments Number of constructor arguments. ---@arguments Already packed arguments. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param classID Reference type id of the class. +--@param threadID The thread in which to invoke the constructor. +--@param methodID The constructor to invoke. +--@numberOfArguments Number of constructor arguments. +--@arguments Already packed arguments. --@return (status, objectID) If status is false data contains an error string, else it contains a reference ID of the newly created object. function newClassInstance(socket,id,classID,threadID,methodID,numberOfArguments,arguments) - local params - if numberOfArguments == 0 then - params = bin.pack(">LLiii",classID,threadID,methodID,numberOfArguments,0) - else - params = bin.pack(">LLii",classID,threadID,methodID,numberOfArguments) .. arguments - end + local params + if numberOfArguments == 0 then + params = bin.pack(">LLiii",classID,threadID,methodID,numberOfArguments,0) + else + params = bin.pack(">LLii",classID,threadID,methodID,numberOfArguments) .. arguments + end - local command = JDWPCommandPacket:new(id,3,4,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP newClassInstance() error: %s", data) - return false,data - end - -- parse data - stdnse.print_debug("newClassInstance data: %s",stdnse.tohex(data)) - local pos, tag = bin.unpack(">C",data) - local objectID - pos, objectID = bin.unpack(">L",data,pos) - return true,objectID + local command = JDWPCommandPacket:new(id,3,4,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP newClassInstance() error: %s", data) + return false,data + end + -- parse data + stdnse.print_debug("newClassInstance data: %s",stdnse.tohex(data)) + local pos, tag = bin.unpack(">C",data) + local objectID + pos, objectID = bin.unpack(">L",data,pos) + return true,objectID end --- ArrayType Command Set (4) -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayType --- NewInstance Command (1) --- Creates a new array object of the specified type with a given length. +-- Creates a new array object of the specified type with a given length. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayType_NewInstance -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param arrayType The array type of the new instance as per JNI (http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502). ---@param length Length of the new array. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param arrayType The array type of the new instance as per JNI (http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502). +--@param length Length of the new array. --@return (status, arrayID) If status is false data contains an error string, else it contains a reference ID of the newly created array. function newArrayInstance(socket,id,arrayType,length) - local params = bin.pack(">Li",arrayType,length) - local command = JDWPCommandPacket:new(id,4,1,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP newArrayInstance() error: %s", data) - return false,data - end - local pos,_ , tag, arrayID - pos, tag = bin.unpack("C",data) - _, arrayID = bin.unpack(">L",data,pos) - return true, arrayID + local params = bin.pack(">Li",arrayType,length) + local command = JDWPCommandPacket:new(id,4,1,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP newArrayInstance() error: %s", data) + return false,data + end + local pos,_ , tag, arrayID + pos, tag = bin.unpack("C",data) + _, arrayID = bin.unpack(">L",data,pos) + return true, arrayID end --- ObjectReference Command Set (9) @@ -656,20 +656,20 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_ReferenceType -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param objectID The ID of an object. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param objectID The ID of an object. --@return (status, runtime_type) If status is false runtime_type contains an error string, else it contains runtime type of an object. function getRuntimeType(socket,id,objectID) - local command = JDWPCommandPacket:new(id,9,1,bin.pack(">L",objectID)) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP resumeVM() error: %s", data) - return false,data - end - local _,tag,runtime_type = bin.unpack(">CL",data) - stdnse.print_debug("runtime type: %d",runtime_type) - return true,runtime_type + local command = JDWPCommandPacket:new(id,9,1,bin.pack(">L",objectID)) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP resumeVM() error: %s", data) + return false,data + end + local _,tag,runtime_type = bin.unpack(">CL",data) + stdnse.print_debug("runtime type: %d",runtime_type) + return true,runtime_type end --- InvokeMethod Command (6) @@ -677,32 +677,32 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_InvokeMethod -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param objectID The ID of an object. ---@param threadID The thread in which to invoke. ---@param classID The class type. ---@param methodID ID of the method to invoke. ---@param numberOfArguments Number of method arguments. ---@arguments Already packed arguments. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param objectID The ID of an object. +--@param threadID The thread in which to invoke. +--@param classID The class type. +--@param methodID ID of the method to invoke. +--@param numberOfArguments Number of method arguments. +--@arguments Already packed arguments. --@return (status, data) If status is false data contains an error string, else it contains a reply data and needs to be parsed manualy. function invokeObjectMethod(socket,id,objectID,threadID,classID,methodID,numberOfArguments,arguments) - local params + local params - if numberOfArguments == 0 then - params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) - else - params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) .. arguments - end + if numberOfArguments == 0 then + params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) + else + params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) .. arguments + end - local command = JDWPCommandPacket:new(id,9,6,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP invokeObjectMethod() error: %s", data) - return false,data - end - stdnse.print_debug("invoke obj method data: %s ",stdnse.tohex(data)) - return true,data + local command = JDWPCommandPacket:new(id,9,6,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP invokeObjectMethod() error: %s", data) + return false,data + end + stdnse.print_debug("invoke obj method data: %s ",stdnse.tohex(data)) + return true,data end --- StringReference Command Set (10) @@ -713,19 +713,19 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_StringReference_Value -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param stringID The ID of a string to read. ---@return (status, data) If status is false result contains an error string, else it contains read string. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param stringID The ID of a string to read. +--@return (status, data) If status is false result contains an error string, else it contains read string. function readString(socket,id,stringID) - local command = JDWPCommandPacket:new(id,10,1,bin.pack(">L",stringID)) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP readString() error: %s", data) - return false,data - end - local _,result = extract_string(data,0) - return true,result + local command = JDWPCommandPacket:new(id,10,1,bin.pack(">L",stringID)) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP readString() error: %s", data) + return false,data + end + local _,result = extract_string(data,0) + return true,result end --- ThreadReference Command Set (11) @@ -737,21 +737,21 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Name -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param threadID The ID of a thread. ---@return (status, thread_name) If status is false thread_name contains an error string, else it contains thread's name. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param threadID The ID of a thread. +--@return (status, thread_name) If status is false thread_name contains an error string, else it contains thread's name. function getThreadName(socket,id,threadID) - local params = bin.pack(">L",threadID) - local command = JDWPCommandPacket:new(id,11,1,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getThreadName() error: %s", data) - return false,data - end - -- parse data - local _,thread_name = extract_string(data,0) - return true, thread_name + local params = bin.pack(">L",threadID) + local command = JDWPCommandPacket:new(id,11,1,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getThreadName() error: %s", data) + return false,data + end + -- parse data + local _,thread_name = extract_string(data,0) + return true, thread_name end --- Suspend Command (2) @@ -759,19 +759,19 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Suspend -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param threadID The ID of a thread. ---@return (status, thread_name) If status is false an error string is returned, else it's nil. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param threadID The ID of a thread. +--@return (status, thread_name) If status is false an error string is returned, else it's nil. function suspendThread(socket,id,threadID) - local params = bin.pack(">L",threadID) - local command = JDWPCommandPacket:new(id,11,2,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP suspendThread() error: %s", data) - return false,data - end - return true, nil + local params = bin.pack(">L",threadID) + local command = JDWPCommandPacket:new(id,11,2,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP suspendThread() error: %s", data) + return false,data + end + return true, nil end --- Status Command (4) @@ -780,20 +780,20 @@ end -- Thread status is described with ThreadStatus and SuspendStatus constants (http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadStatus). -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Status -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param threadID The ID of a thread. ---@return (status, thread_name) If status is false an error string is returned, else unparsed thread status data. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param threadID The ID of a thread. +--@return (status, thread_name) If status is false an error string is returned, else unparsed thread status data. function threadStatus(socket,id,threadID) - local params = bin.pack(">L",threadID) - local command = JDWPCommandPacket:new(id,11,4,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP threadStatus() error: %s", data) - return false,data - end - stdnse.print_debug("threadStatus %s",stdnse.tohex(data)) - return true, data + local params = bin.pack(">L",threadID) + local command = JDWPCommandPacket:new(id,11,4,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP threadStatus() error: %s", data) + return false,data + end + stdnse.print_debug("threadStatus %s",stdnse.tohex(data)) + return true, data end --- ArrayReference Command Set (13) @@ -804,19 +804,19 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayReference_SetValues -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param objectID The ID of an array object. ---@return (status, data) If status is false an error string is returned, else it's nil. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param objectID The ID of an array object. +--@return (status, data) If status is false an error string is returned, else it's nil. function setArrayValues(socket,id,objectID,idx,values) - local params = bin.pack(">Lii",objectID,idx,#values) .. values - local command = JDWPCommandPacket:new(id,13,3,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP setArrayValues() error: %s", data) - return false,data - end - return true, nil + local params = bin.pack(">Lii",objectID,idx,#values) .. values + local command = JDWPCommandPacket:new(id,13,3,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP setArrayValues() error: %s", data) + return false,data + end + return true, nil end --- EventRequest Command Set (15) @@ -826,39 +826,39 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Set -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param threadID The ID of the thread. ---@return (status, requestID) If status is false an error string is returned, else it contains assigned request id. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param threadID The ID of the thread. +--@return (status, requestID) If status is false an error string is returned, else it contains assigned request id. function setThreadSinglestep(socket,id,threadID) - local params = bin.pack(">CCiCLii",1,2,1,10,threadID,0,0) -- event options see http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Set - local command = JDWPCommandPacket:new(id,15,1,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP setThreadSinglestep() error: %s", data) - return false,data - end - local _, requestID = bin.unpack(">i",data) - return true, requestID + local params = bin.pack(">CCiCLii",1,2,1,10,threadID,0,0) -- event options see http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Set + local command = JDWPCommandPacket:new(id,15,1,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP setThreadSinglestep() error: %s", data) + return false,data + end + local _, requestID = bin.unpack(">i",data) + return true, requestID end --- Uses Clear Command (2) to unset singlesteping from a thread by specified event. -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Clear -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param eventID The ID of the thread. ---@return (status, requestID) If status is false an error string is returned, else it's nil. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param eventID The ID of the thread. +--@return (status, requestID) If status is false an error string is returned, else it's nil. function clearThreadSinglestep(socket,id,eventID) - local params = bin.pack(">Ci",1,eventID) - local command = JDWPCommandPacket:new(id,15,2,params) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP clearThreadSinglestep() error: %s", data) - return false,data - end - return true,nil + local params = bin.pack(">Ci",1,eventID) + local command = JDWPCommandPacket:new(id,15,2,params) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP clearThreadSinglestep() error: %s", data) + return false,data + end + return true,nil end --- ClassObjectReference Command Set (17) @@ -870,226 +870,226 @@ end -- -- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassObjectReference_ReflectedType -- ---@param socket Socket to use to send the command. ---@param id Packet id. ---@param classObjectID The ID of the object. ---@return (status, reflected_type) If status is false an error string is returned, else reflected_type is object's reference type. +--@param socket Socket to use to send the command. +--@param id Packet id. +--@param classObjectID The ID of the object. +--@return (status, reflected_type) If status is false an error string is returned, else reflected_type is object's reference type. function getReflectedType(socket,id,classObjectID) - local _, param - local command = JDWPCommandPacket:new(id,17,1,bin.pack(">L",classObjectID)) - local status, data = executeCommand(socket,command) - if not status then - stdnse.print_debug(2,"JDWP getReflectedType() error: %s", data) - return false,data - end - local reflected_type = { - refTypeTag = nil, - typeID = nil - } - _,reflected_type.refTypeTag, reflected_type.typeID = bin.unpack(">CL",data) + local _, param + local command = JDWPCommandPacket:new(id,17,1,bin.pack(">L",classObjectID)) + local status, data = executeCommand(socket,command) + if not status then + stdnse.print_debug(2,"JDWP getReflectedType() error: %s", data) + return false,data + end + local reflected_type = { + refTypeTag = nil, + typeID = nil + } + _,reflected_type.refTypeTag, reflected_type.typeID = bin.unpack(">CL",data) - return true, reflected_type + return true, reflected_type end --- Helper function to find a method ID by its name. -- --- @param socket Socket to use for communication. --- @param class ID of the class whose method we seek. --- @param methodName Name of the method. --- @param skipFirst Skip first found method. +-- @param socket Socket to use for communication. +-- @param class ID of the class whose method we seek. +-- @param methodName Name of the method. +-- @param skipFirst Skip first found method. function findMethod(socket,class,methodName,skipFirst) - local methodID - local status, methods = getMethodsWithGeneric(socket,0,class) - if not status then - return false - end - for _, method in ipairs(methods) do -- find first constructor and first defineClass() method - stdnse.print_debug(2,"Method name: %s", method.name) - if methodID == nil then - if string.find(method.name,methodName) then - if skipFirst then - skipFirst = false - else - methodID = method.methodID - end - end - end - end - return methodID + local methodID + local status, methods = getMethodsWithGeneric(socket,0,class) + if not status then + return false + end + for _, method in ipairs(methods) do -- find first constructor and first defineClass() method + stdnse.print_debug(2,"Method name: %s", method.name) + if methodID == nil then + if string.find(method.name,methodName) then + if skipFirst then + skipFirst = false + else + methodID = method.methodID + end + end + end + end + return methodID end --- Tries to inject specified bytes as a java class and create its instance. -- -- Returns a table containing following fields: --- * 'id' Injected class reference ID. --- * 'instance' Inected calss' instance reference ID. --- * 'thread' Thread in which the class was injected and instantiated. +-- * 'id' Injected class reference ID. +-- * 'instance' Inected calss' instance reference ID. +-- * 'thread' Thread in which the class was injected and instantiated. -- --- @param socket Socket to use for communication. --- @param class_bytes String of bytes of a java class file to inject. --- @return (status,injectedClass) If status is false, an error message is returned, else returns a table with injected class info. +-- @param socket Socket to use for communication. +-- @param class_bytes String of bytes of a java class file to inject. +-- @return (status,injectedClass) If status is false, an error message is returned, else returns a table with injected class info. function injectClass(socket,class_bytes) - local classes,status - -- find byte array class id needed to create new array to load our bytecode into - status,classes = getAllClassesWithGeneric(socket,0) - if not status then - stdnse.print_debug("getAllClassesWithGeneric failed: %s", classes) - return false - end - local byteArrayID - for _,class in ipairs(classes) do - if string.find(class.signature,"%[B") then - byteArrayID = class.typeID - break - end - end - if byteArrayID == nil then - stdnse.print_debug("finding byte arrray id failed") - return false - end - stdnse.print_debug("Found byte[] id %d",byteArrayID) + local classes,status + -- find byte array class id needed to create new array to load our bytecode into + status,classes = getAllClassesWithGeneric(socket,0) + if not status then + stdnse.print_debug("getAllClassesWithGeneric failed: %s", classes) + return false + end + local byteArrayID + for _,class in ipairs(classes) do + if string.find(class.signature,"%[B") then + byteArrayID = class.typeID + break + end + end + if byteArrayID == nil then + stdnse.print_debug("finding byte arrray id failed") + return false + end + stdnse.print_debug("Found byte[] id %d",byteArrayID) - -- find SecureClassLoader id by signature - status, classes = getClassBySignature(socket,0,"Ljava/security/SecureClassLoader;") - if not status then - return false - end - local secureClassLoader = classes[1].referenceTypeID - stdnse.print_debug("Found SecureClassLoader id %d",secureClassLoader) - -- find SecureClassLoader() constructor - local constructorMethodID = findMethod(socket,secureClassLoader,"",true) - -- find ClassLoader id by signature - status, classes = getClassBySignature(socket,0,"Ljava/lang/ClassLoader;") - if not status then - return false - end - local classLoader = classes[1].referenceTypeID - stdnse.print_debug("Found ClassLoader id %d",classes[1].referenceTypeID) - -- find ClassLoader's defineClass() method - local defineClassMethodID = findMethod(socket,classLoader,"defineClass",false) - -- find ClassLoader's resolveClass() method - local resolveClassMethodID = findMethod(socket,classLoader,"resolveClass",false) - if constructorMethodID == nil or defineClassMethodID == nil or resolveClassMethodID == nil then - stdnse.print_debug("Either constructor, defineClass or resolveClass method could not be found %s,%s,%s", type(constructorMethodID), type(defineClassMethodID),type(resolveClassMethodID)) - return false - end + -- find SecureClassLoader id by signature + status, classes = getClassBySignature(socket,0,"Ljava/security/SecureClassLoader;") + if not status then + return false + end + local secureClassLoader = classes[1].referenceTypeID + stdnse.print_debug("Found SecureClassLoader id %d",secureClassLoader) + -- find SecureClassLoader() constructor + local constructorMethodID = findMethod(socket,secureClassLoader,"",true) + -- find ClassLoader id by signature + status, classes = getClassBySignature(socket,0,"Ljava/lang/ClassLoader;") + if not status then + return false + end + local classLoader = classes[1].referenceTypeID + stdnse.print_debug("Found ClassLoader id %d",classes[1].referenceTypeID) + -- find ClassLoader's defineClass() method + local defineClassMethodID = findMethod(socket,classLoader,"defineClass",false) + -- find ClassLoader's resolveClass() method + local resolveClassMethodID = findMethod(socket,classLoader,"resolveClass",false) + if constructorMethodID == nil or defineClassMethodID == nil or resolveClassMethodID == nil then + stdnse.print_debug("Either constructor, defineClass or resolveClass method could not be found %s,%s,%s", type(constructorMethodID), type(defineClassMethodID),type(resolveClassMethodID)) + return false + end - -- create array to load bytecode into - local arrayID - status, arrayID = newArrayInstance(socket,0,byteArrayID,#class_bytes) - if not status then - stdnse.print_debug("New array failed: %s", arrayID) - return false - end - stdnse.print_debug("Created new byte array of length %d",#class_bytes) - -- set array values - local temp - status, temp = setArrayValues(socket,0,arrayID,0,class_bytes) - if not status then - stdnse.print_debug("Set values failed: %s", temp) - return - end - stdnse.print_debug("Set array values to injected class bytes") + -- create array to load bytecode into + local arrayID + status, arrayID = newArrayInstance(socket,0,byteArrayID,#class_bytes) + if not status then + stdnse.print_debug("New array failed: %s", arrayID) + return false + end + stdnse.print_debug("Created new byte array of length %d",#class_bytes) + -- set array values + local temp + status, temp = setArrayValues(socket,0,arrayID,0,class_bytes) + if not status then + stdnse.print_debug("Set values failed: %s", temp) + return + end + stdnse.print_debug("Set array values to injected class bytes") - -- get main thread id - -- in order to load a new class file, thread must be suspended by an event - -- so we set it to singlestep, let it run and it get suspended right away - local threads - status,threads = getAllThreads(socket,0) - if not status then - stdnse.print_debug("get threads failed: %s", threads) - return false - end - local main_thread - local eventID - stdnse.print_debug("Looking for main thread...") - for _,thread in ipairs(threads) do - local thread_name - status, thread_name = getThreadName(socket,0,thread) - if not status then - stdnse.print_debug("getThreadName failed: %s", thread_name) - return false - end - if thread_name == "main" then - stdnse.print_debug("Setting singlesteping to main thread.") - status, eventID = setThreadSinglestep(socket,0,thread) - main_thread = thread - break - end - end - if main_thread == nil then - stdnse.print_debug("couldn't find main thread") - return false - end - -- to trigger the singlestep event, VM must be resumed - stdnse.print_debug("Resuming VM and waiting for single step event from main thread...") - local status, _ = resumeVM(socket,0) - -- clear singlestep since we need to run our code in this thread and we don't want it to stop after each instruction - clearThreadSinglestep(socket,0,eventID) - stdnse.print_debug("Cleared singlesteping from main thread.") + -- get main thread id + -- in order to load a new class file, thread must be suspended by an event + -- so we set it to singlestep, let it run and it get suspended right away + local threads + status,threads = getAllThreads(socket,0) + if not status then + stdnse.print_debug("get threads failed: %s", threads) + return false + end + local main_thread + local eventID + stdnse.print_debug("Looking for main thread...") + for _,thread in ipairs(threads) do + local thread_name + status, thread_name = getThreadName(socket,0,thread) + if not status then + stdnse.print_debug("getThreadName failed: %s", thread_name) + return false + end + if thread_name == "main" then + stdnse.print_debug("Setting singlesteping to main thread.") + status, eventID = setThreadSinglestep(socket,0,thread) + main_thread = thread + break + end + end + if main_thread == nil then + stdnse.print_debug("couldn't find main thread") + return false + end + -- to trigger the singlestep event, VM must be resumed + stdnse.print_debug("Resuming VM and waiting for single step event from main thread...") + local status, _ = resumeVM(socket,0) + -- clear singlestep since we need to run our code in this thread and we don't want it to stop after each instruction + clearThreadSinglestep(socket,0,eventID) + stdnse.print_debug("Cleared singlesteping from main thread.") - -- instantiate new class loader - local class_loader_instance - status, class_loader_instance = newClassInstance(socket,0,secureClassLoader,main_thread,constructorMethodID,0,nil) - if not status then - stdnse.print_debug("newClassInstance failed: %s", class_loader_instance) - return false - end - stdnse.print_debug("Created new instance of SecureClassLoader.") + -- instantiate new class loader + local class_loader_instance + status, class_loader_instance = newClassInstance(socket,0,secureClassLoader,main_thread,constructorMethodID,0,nil) + if not status then + stdnse.print_debug("newClassInstance failed: %s", class_loader_instance) + return false + end + stdnse.print_debug("Created new instance of SecureClassLoader.") - local injectedClass - -- invoke defineClass with byte array that contains our bytecode - local defineClassArgs = bin.pack(">CLCiCi",0x5b,arrayID,0x49,0,0x49,#class_bytes) -- argument tags taken from http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502 - stdnse.print_debug("Calling secureClassLoader.defineClass(byte[],int,int) ...") - status, injectedClass = invokeObjectMethod(socket,0,class_loader_instance,main_thread,secureClassLoader,defineClassMethodID,3,defineClassArgs) - if not status then - stdnse.print_debug("invokeObjectMethod failed: %s", injectedClass) - end - -- resolve (Java's way of saying link) loaded class - status, _ = invokeObjectMethod(socket,0,class_loader_instance,main_thread,secureClassLoader,resolveClassMethodID,1,injectedClass) -- call with injectedClass which still has a tag - if not status then - stdnse.print_debug("invokeObjectMethod failed:") - end - -- extract the injected class' ID - local tag,injectedClassID - _,tag,injectedClassID = bin.unpack(">CL",injectedClass) + local injectedClass + -- invoke defineClass with byte array that contains our bytecode + local defineClassArgs = bin.pack(">CLCiCi",0x5b,arrayID,0x49,0,0x49,#class_bytes) -- argument tags taken from http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502 + stdnse.print_debug("Calling secureClassLoader.defineClass(byte[],int,int) ...") + status, injectedClass = invokeObjectMethod(socket,0,class_loader_instance,main_thread,secureClassLoader,defineClassMethodID,3,defineClassArgs) + if not status then + stdnse.print_debug("invokeObjectMethod failed: %s", injectedClass) + end + -- resolve (Java's way of saying link) loaded class + status, _ = invokeObjectMethod(socket,0,class_loader_instance,main_thread,secureClassLoader,resolveClassMethodID,1,injectedClass) -- call with injectedClass which still has a tag + if not status then + stdnse.print_debug("invokeObjectMethod failed:") + end + -- extract the injected class' ID + local tag,injectedClassID + _,tag,injectedClassID = bin.unpack(">CL",injectedClass) - -- our class is now injected, but we need to find it's methods by calling Class.getMethods() on it - -- and for that we need its runtime_type which is Class - local runtime_type - status, runtime_type = getRuntimeType(socket,0,injectedClassID) -- should be Class - -- find the getMethods() id - local getMethodsMethod = findMethod(socket,runtime_type,"getMethods",false) - status, _ = invokeObjectMethod(socket,0,injectedClassID,main_thread,runtime_type,getMethodsMethod,0,nil) + -- our class is now injected, but we need to find it's methods by calling Class.getMethods() on it + -- and for that we need its runtime_type which is Class + local runtime_type + status, runtime_type = getRuntimeType(socket,0,injectedClassID) -- should be Class + -- find the getMethods() id + local getMethodsMethod = findMethod(socket,runtime_type,"getMethods",false) + status, _ = invokeObjectMethod(socket,0,injectedClassID,main_thread,runtime_type,getMethodsMethod,0,nil) - stdnse.print_debug("New class defined. Injected class id : %d",injectedClassID) - local sig, reflected_type - status, sig = getSignatureWithGeneric(socket,0,injectedClassID) - stdnse.print_debug("Injected class signature: %s", sig) - status, reflected_type = getReflectedType(socket,0,injectedClassID) + stdnse.print_debug("New class defined. Injected class id : %d",injectedClassID) + local sig, reflected_type + status, sig = getSignatureWithGeneric(socket,0,injectedClassID) + stdnse.print_debug("Injected class signature: %s", sig) + status, reflected_type = getReflectedType(socket,0,injectedClassID) - -- find injected class constructor - local injectedConstructor = findMethod(socket,injectedClassID,"",false) + -- find injected class constructor + local injectedConstructor = findMethod(socket,injectedClassID,"",false) - if injectedConstructor == nil then - stdnse.print_debug("Couldn't find either evil method or constructor") - return false - end + if injectedConstructor == nil then + stdnse.print_debug("Couldn't find either evil method or constructor") + return false + end - -- instantiate our evil class - local injectedClassInstance - status, injectedClassInstance = newClassInstance(socket,0,injectedClassID,main_thread,injectedConstructor,0,nil) - if not status then - return false, injectedClassInstance - end - local injected_class = { - id = injectedClassID, - instance = injectedClassInstance, - thread = main_thread - } - return true, injected_class + -- instantiate our evil class + local injectedClassInstance + status, injectedClassInstance = newClassInstance(socket,0,injectedClassID,main_thread,injectedConstructor,0,nil) + if not status then + return false, injectedClassInstance + end + local injected_class = { + id = injectedClassID, + instance = injectedClassInstance, + thread = main_thread + } + return true, injected_class end return _ENV; diff --git a/nselib/ldap.lua b/nselib/ldap.lua index 0e48af7bc..9ece48ef4 100644 --- a/nselib/ldap.lua +++ b/nselib/ldap.lua @@ -37,80 +37,80 @@ ERROR_MSG[34] = "Invalid DN" ERROR_MSG[49] = "The supplied credential is invalid." ERRORS = { - LDAP_SUCCESS = 0, - LDAP_SIZELIMIT_EXCEEDED = 4 + LDAP_SUCCESS = 0, + LDAP_SIZELIMIT_EXCEEDED = 4 } -- Application constants APPNO = { - BindRequest = 0, - BindResponse = 1, - UnbindRequest = 2, - SearchRequest = 3, - SearchResponse = 4, - SearchResDone = 5 + BindRequest = 0, + BindResponse = 1, + UnbindRequest = 2, + SearchRequest = 3, + SearchResponse = 4, + SearchResDone = 5 } -- Filter operation constants FILTER = { - _and = 0, - _or = 1, - _not = 2, - equalityMatch = 3, - substrings = 4, - greaterOrEqual = 5, - lessOrEqual = 6, - present = 7, - approxMatch = 8, - extensibleMatch = 9 + _and = 0, + _or = 1, + _not = 2, + equalityMatch = 3, + substrings = 4, + greaterOrEqual = 5, + lessOrEqual = 6, + present = 7, + approxMatch = 8, + extensibleMatch = 9 } -- Scope constants SCOPE = { - base=0, - one=1, - sub= 2, - children=3, - default = 0 + base=0, + one=1, + sub= 2, + children=3, + default = 0 } -- Deref policy constants DEREFPOLICY = { - never=0, - searching=1, - finding = 2, - always=3, - default = 0 + never=0, + searching=1, + finding = 2, + always=3, + default = 0 } -- LDAP specific tag encoders local tagEncoder = {} tagEncoder['table'] = function(self, val) - if (val._ldap == '0A') then - local ival = self.encodeInt(val[1]) - local len = self.encodeLength(#ival) - return bin.pack('HAA', '0A', len, ival) - end - if (val._ldaptype) then - local len - if val[1] == nil or #val[1] == 0 then - return bin.pack('HC', val._ldaptype, 0) - else - len = self.encodeLength(#val[1]) - return bin.pack('HAA', val._ldaptype, len, val[1]) - end - end - - local encVal = "" - for _, v in ipairs(val) do - encVal = encVal .. encode(v) -- todo: buffer? - end - local tableType = bin.pack("H", "30") - if (val["_snmp"]) then - tableType = bin.pack("H", val["_snmp"]) + if (val._ldap == '0A') then + local ival = self.encodeInt(val[1]) + local len = self.encodeLength(#ival) + return bin.pack('HAA', '0A', len, ival) + end + if (val._ldaptype) then + local len + if val[1] == nil or #val[1] == 0 then + return bin.pack('HC', val._ldaptype, 0) + else + len = self.encodeLength(#val[1]) + return bin.pack('HAA', val._ldaptype, len, val[1]) end - return bin.pack('AAA', tableType, self.encodeLength(#encVal), encVal) + end + + local encVal = "" + for _, v in ipairs(val) do + encVal = encVal .. encode(v) -- todo: buffer? + end + local tableType = bin.pack("H", "30") + if (val["_snmp"]) then + tableType = bin.pack("H", val["_snmp"]) + end + return bin.pack('AAA', tableType, self.encodeLength(#encVal), encVal) end @@ -121,17 +121,17 @@ end -- @return Encoded value. function encode(val) - local encoder = asn1.ASN1Encoder:new() - local encValue + local encoder = asn1.ASN1Encoder:new() + local encValue - encoder:registerTagEncoders(tagEncoder) - encValue = encoder:encode(val) + encoder:registerTagEncoders(tagEncoder) + encValue = encoder:encode(val) - if encValue then - return encValue - end + if encValue then + return encValue + end - return '' + return '' end @@ -139,16 +139,16 @@ end local tagDecoder = {} tagDecoder["0A"] = function( self, encStr, elen, pos ) - return self.decodeInt(encStr, elen, pos) + return self.decodeInt(encStr, elen, pos) end tagDecoder["8A"] = function( self, encStr, elen, pos ) - return bin.unpack("A" .. elen, encStr, pos) + return bin.unpack("A" .. elen, encStr, pos) end -- null decoder tagDecoder["31"] = function( self, encStr, elen, pos ) - return pos, nil + return pos, nil end @@ -160,10 +160,10 @@ end -- @return The position after decoding -- @return The decoded value(s). function decode(encStr, pos) - -- register the LDAP specific tag decoders - local decoder = asn1.ASN1Decoder:new() - decoder:registerTagDecoders( tagDecoder ) - return decoder:decode( encStr, pos ) + -- register the LDAP specific tag decoders + local decoder = asn1.ASN1Decoder:new() + decoder:registerTagDecoders( tagDecoder ) + return decoder:decode( encStr, pos ) end @@ -175,19 +175,19 @@ end -- @return The position after decoding. -- @return The decoded sequence as a table. local function decodeSeq(encStr, len, pos) - local seq = {} - local sPos = 1 - local sStr - pos, sStr = bin.unpack("A" .. len, encStr, pos) - if(sStr==nil) then - return pos,seq - end - while (sPos < len) do - local newSeq - sPos, newSeq = decode(sStr, sPos) - table.insert(seq, newSeq) - end - return pos, seq + local seq = {} + local sPos = 1 + local sStr + pos, sStr = bin.unpack("A" .. len, encStr, pos) + if(sStr==nil) then + return pos,seq + end + while (sPos < len) do + local newSeq + sPos, newSeq = decode(sStr, sPos) + table.insert(seq, newSeq) + end + return pos, seq end -- Encodes an LDAP Application operation and its data as a sequence @@ -197,11 +197,11 @@ end -- @param data string containing the LDAP operation content -- @return string containing the encoded LDAP operation function encodeLDAPOp( appno, isConstructed, data ) - local encoded_str = "" - local asn1_type = asn1.BERtoInt( asn1.BERCLASS.Application, isConstructed, appno ) + local encoded_str = "" + local asn1_type = asn1.BERtoInt( asn1.BERCLASS.Application, isConstructed, appno ) - encoded_str = encode( { _ldaptype = bin.pack("A", string.format("%X", asn1_type)), data } ) - return encoded_str + encoded_str = encode( { _ldaptype = bin.pack("A", string.format("%X", asn1_type)), data } ) + return encoded_str end --- Performs an LDAP Search request @@ -221,114 +221,114 @@ end -- @return err string containing error message function searchRequest( socket, params ) - local searchResEntries = { errorMessage="", resultCode = 0} - local catch = function() socket:close() stdnse.print_debug(string.format("SearchRequest failed")) end - local try = nmap.new_try(catch) - local attributes = params.attributes - local request = encode(params.baseObject) - local attrSeq = '' - local requestData, messageSeq, data - local maxObjects = params.maxObjects or -1 + local searchResEntries = { errorMessage="", resultCode = 0} + local catch = function() socket:close() stdnse.print_debug(string.format("SearchRequest failed")) end + local try = nmap.new_try(catch) + local attributes = params.attributes + local request = encode(params.baseObject) + local attrSeq = '' + local requestData, messageSeq, data + local maxObjects = params.maxObjects or -1 - local encoder = asn1.ASN1Encoder:new() - local decoder = asn1.ASN1Decoder:new() + local encoder = asn1.ASN1Encoder:new() + local decoder = asn1.ASN1Decoder:new() - encoder:registerTagEncoders(tagEncoder) - decoder:registerTagDecoders(tagDecoder) + encoder:registerTagEncoders(tagEncoder) + decoder:registerTagDecoders(tagDecoder) - request = request .. encode( { _ldap='0A', params.scope } )--scope - request = request .. encode( { _ldap='0A', params.derefPolicy } )--derefpolicy - request = request .. encode( params.sizeLimit or 0)--sizelimit - request = request .. encode( params.timeLimit or 0)--timelimit - request = request .. encode( params.typesOnly or false)--TypesOnly + request = request .. encode( { _ldap='0A', params.scope } )--scope + request = request .. encode( { _ldap='0A', params.derefPolicy } )--derefpolicy + request = request .. encode( params.sizeLimit or 0)--sizelimit + request = request .. encode( params.timeLimit or 0)--timelimit + request = request .. encode( params.typesOnly or false)--TypesOnly - if params.filter then - request = request .. createFilter( params.filter ) - else - request = request .. encode( { _ldaptype='87', "objectclass" } )-- filter : string, presence - end - if attributes~= nil then - for _,attr in ipairs(attributes) do - attrSeq = attrSeq .. encode(attr) - end - end + if params.filter then + request = request .. createFilter( params.filter ) + else + request = request .. encode( { _ldaptype='87', "objectclass" } )-- filter : string, presence + end + if attributes~= nil then + for _,attr in ipairs(attributes) do + attrSeq = attrSeq .. encode(attr) + end + end - request = request .. encoder:encodeSeq(attrSeq) - requestData = encodeLDAPOp(APPNO.SearchRequest, true, request) + request = request .. encoder:encodeSeq(attrSeq) + requestData = encodeLDAPOp(APPNO.SearchRequest, true, request) messageSeq = encode(ldapMessageId) - ldapMessageId = ldapMessageId +1 - messageSeq = messageSeq .. requestData - data = encoder:encodeSeq(messageSeq) - try( socket:send( data ) ) - data = "" + ldapMessageId = ldapMessageId +1 + messageSeq = messageSeq .. requestData + data = encoder:encodeSeq(messageSeq) + try( socket:send( data ) ) + data = "" - while true do - local len, pos, messageId = 0, 2, -1 - local tmp = "" - local _, objectName, attributes, ldapOp - local attributes - local searchResEntry = {} + while true do + local len, pos, messageId = 0, 2, -1 + local tmp = "" + local _, objectName, attributes, ldapOp + local attributes + local searchResEntry = {} - if ( maxObjects == 0 ) then - break - elseif ( maxObjects > 0 ) then - maxObjects = maxObjects - 1 - end + if ( maxObjects == 0 ) then + break + elseif ( maxObjects > 0 ) then + maxObjects = maxObjects - 1 + end - if data:len() > 6 then - pos, len = decoder.decodeLength( data, pos ) - else - data = data .. try( socket:receive() ) - pos, len = decoder.decodeLength( data, pos ) - end - -- pos should be at the right position regardless if length is specified in 1 or 2 bytes - while ( len + pos - 1 > data:len() ) do - data = data .. try( socket:receive() ) - end + if data:len() > 6 then + pos, len = decoder.decodeLength( data, pos ) + else + data = data .. try( socket:receive() ) + pos, len = decoder.decodeLength( data, pos ) + end + -- pos should be at the right position regardless if length is specified in 1 or 2 bytes + while ( len + pos - 1 > data:len() ) do + data = data .. try( socket:receive() ) + end - pos, messageId = decode( data, pos ) - pos, tmp = bin.unpack("C", data, pos) - pos, len = decoder.decodeLength( data, pos ) - ldapOp = asn1.intToBER( tmp ) - searchResEntry = {} + pos, messageId = decode( data, pos ) + pos, tmp = bin.unpack("C", data, pos) + pos, len = decoder.decodeLength( data, pos ) + ldapOp = asn1.intToBER( tmp ) + searchResEntry = {} - if ldapOp.number == APPNO.SearchResDone then - pos, searchResEntry.resultCode = decode( data, pos ) - -- errors may occur after a large amount of data has been received (eg. size limit exceeded) - -- we want to be able to return the data received prior to this error to the user - -- however, we also need to alert the user of the error. This is achieved through "softerrors" - -- softerrors populate the error fields of the table while returning a true status - -- this allows for the caller to output data while still being able to catch the error - if ( searchResEntry.resultCode ~= 0 ) then - local error_msg - pos, searchResEntry.matchedDN = decode( data, pos ) - pos, searchResEntry.errorMessage = decode( data, pos ) - error_msg = ERROR_MSG[searchResEntry.resultCode] - -- if the table is empty return a hard error - if #searchResEntries == 0 then - return false, string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" ) - else - searchResEntries.errorMessage = string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" ) - searchResEntries.resultCode = searchResEntry.resultCode - return true, searchResEntries - end - end - break - end + if ldapOp.number == APPNO.SearchResDone then + pos, searchResEntry.resultCode = decode( data, pos ) + -- errors may occur after a large amount of data has been received (eg. size limit exceeded) + -- we want to be able to return the data received prior to this error to the user + -- however, we also need to alert the user of the error. This is achieved through "softerrors" + -- softerrors populate the error fields of the table while returning a true status + -- this allows for the caller to output data while still being able to catch the error + if ( searchResEntry.resultCode ~= 0 ) then + local error_msg + pos, searchResEntry.matchedDN = decode( data, pos ) + pos, searchResEntry.errorMessage = decode( data, pos ) + error_msg = ERROR_MSG[searchResEntry.resultCode] + -- if the table is empty return a hard error + if #searchResEntries == 0 then + return false, string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" ) + else + searchResEntries.errorMessage = string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" ) + searchResEntries.resultCode = searchResEntry.resultCode + return true, searchResEntries + end + end + break + end - pos, searchResEntry.objectName = decode( data, pos ) - if ldapOp.number == APPNO.SearchResponse then - pos, searchResEntry.attributes = decode( data, pos ) + pos, searchResEntry.objectName = decode( data, pos ) + if ldapOp.number == APPNO.SearchResponse then + pos, searchResEntry.attributes = decode( data, pos ) - table.insert( searchResEntries, searchResEntry ) - end - if data:len() > pos then - data = data:sub(pos) - else - data = "" - end - end - return true, searchResEntries + table.insert( searchResEntries, searchResEntry ) + end + if data:len() > pos then + data = data:sub(pos) + else + data = "" + end + end + return true, searchResEntries end @@ -340,49 +340,49 @@ end -- @return err string containing error message function bindRequest( socket, params ) - local catch = function() socket:close() stdnse.print_debug(string.format("bindRequest failed")) end - local try = nmap.new_try(catch) - local ldapAuth = encode( { _ldaptype = 80, params.password } ) - local bindReq = encode( params.version ) .. encode( params.username ) .. ldapAuth - local ldapMsg = encode(ldapMessageId) .. encodeLDAPOp( APPNO.BindRequest, true, bindReq ) - local packet - local pos, packet_len, resultCode, tmp, len, _ - local response = {} + local catch = function() socket:close() stdnse.print_debug(string.format("bindRequest failed")) end + local try = nmap.new_try(catch) + local ldapAuth = encode( { _ldaptype = 80, params.password } ) + local bindReq = encode( params.version ) .. encode( params.username ) .. ldapAuth + local ldapMsg = encode(ldapMessageId) .. encodeLDAPOp( APPNO.BindRequest, true, bindReq ) + local packet + local pos, packet_len, resultCode, tmp, len, _ + local response = {} - local encoder = asn1.ASN1Encoder:new() - local decoder = asn1.ASN1Decoder:new() + local encoder = asn1.ASN1Encoder:new() + local decoder = asn1.ASN1Decoder:new() - encoder:registerTagEncoders(tagEncoder) - decoder:registerTagDecoders(tagDecoder) + encoder:registerTagEncoders(tagEncoder) + decoder:registerTagDecoders(tagDecoder) - packet = encoder:encodeSeq( ldapMsg ) - ldapMessageId = ldapMessageId +1 - try( socket:send( packet ) ) - packet = try( socket:receive() ) + packet = encoder:encodeSeq( ldapMsg ) + ldapMessageId = ldapMessageId +1 + try( socket:send( packet ) ) + packet = try( socket:receive() ) - pos, packet_len = decoder.decodeLength( packet, 2 ) - pos, response.messageID = decode( packet, pos ) - pos, tmp = bin.unpack("C", packet, pos) - pos, len = decoder.decodeLength( packet, pos ) - response.protocolOp = asn1.intToBER( tmp ) + pos, packet_len = decoder.decodeLength( packet, 2 ) + pos, response.messageID = decode( packet, pos ) + pos, tmp = bin.unpack("C", packet, pos) + pos, len = decoder.decodeLength( packet, pos ) + response.protocolOp = asn1.intToBER( tmp ) - if response.protocolOp.number ~= APPNO.BindResponse then - return false, string.format("Recieved incorrect Op in packet: %d, expected %d", response.protocolOp.number, APPNO.BindResponse) - end + if response.protocolOp.number ~= APPNO.BindResponse then + return false, string.format("Recieved incorrect Op in packet: %d, expected %d", response.protocolOp.number, APPNO.BindResponse) + end - pos, response.resultCode = decode( packet, pos ) + pos, response.resultCode = decode( packet, pos ) - if ( response.resultCode ~= 0 ) then - local error_msg - pos, response.matchedDN = decode( packet, pos ) - pos, response.errorMessage = decode( packet, pos ) - error_msg = ERROR_MSG[response.resultCode] - return false, string.format("\n Error: %s\n Details: %s", - error_msg or "Unknown error occured (code: " .. response.resultCode .. - ")", response.errorMessage or "" ) - else - return true, "Success" - end + if ( response.resultCode ~= 0 ) then + local error_msg + pos, response.matchedDN = decode( packet, pos ) + pos, response.errorMessage = decode( packet, pos ) + error_msg = ERROR_MSG[response.resultCode] + return false, string.format("\n Error: %s\n Details: %s", + error_msg or "Unknown error occured (code: " .. response.resultCode .. + ")", response.errorMessage or "" ) + else + return true, "Success" + end end --- Performs an LDAP Unbind @@ -392,19 +392,19 @@ end -- @return err string containing error message function unbindRequest( socket ) - local ldapMsg, packet - local catch = function() socket:close() stdnse.print_debug(string.format("bindRequest failed")) end - local try = nmap.new_try(catch) + local ldapMsg, packet + local catch = function() socket:close() stdnse.print_debug(string.format("bindRequest failed")) end + local try = nmap.new_try(catch) - local encoder = asn1.ASN1Encoder:new() - encoder:registerTagEncoders(tagEncoder) + local encoder = asn1.ASN1Encoder:new() + encoder:registerTagEncoders(tagEncoder) - ldapMessageId = ldapMessageId +1 - ldapMsg = encode( ldapMessageId ) - ldapMsg = ldapMsg .. encodeLDAPOp( APPNO.UnbindRequest, false, nil) - packet = encoder:encodeSeq( ldapMsg ) - try( socket:send( packet ) ) - return true, "" + ldapMessageId = ldapMessageId +1 + ldapMsg = encode( ldapMessageId ) + ldapMsg = ldapMsg .. encodeLDAPOp( APPNO.UnbindRequest, false, nil) + packet = encoder:encodeSeq( ldapMsg ) + try( socket:send( packet ) ) + return true, "" end @@ -414,103 +414,103 @@ end -- @return string containing the ASN1 byte sequence function createFilter( filter ) - local asn1_type = asn1.BERtoInt( asn1.BERCLASS.ContextSpecific, true, filter.op ) - local filter_str = "" + local asn1_type = asn1.BERtoInt( asn1.BERCLASS.ContextSpecific, true, filter.op ) + local filter_str = "" - if type(filter.val) == 'table' then - for _, v in ipairs( filter.val ) do - filter_str = filter_str .. createFilter( v ) - end - else - local obj = encode( filter.obj ) - local val = '' - if ( filter.op == FILTER['substrings'] ) then + if type(filter.val) == 'table' then + for _, v in ipairs( filter.val ) do + filter_str = filter_str .. createFilter( v ) + end + else + local obj = encode( filter.obj ) + local val = '' + if ( filter.op == FILTER['substrings'] ) then - local tmptable = stdnse.strsplit('*', filter.val) - local tmp_result = '' + local tmptable = stdnse.strsplit('*', filter.val) + local tmp_result = '' - if (#tmptable <= 1 ) then - -- 0x81 = 10000001 = 10 0 00001 - -- hex binary Context Primitive value Field: Sequence Value: 1 (any / any position in string) - tmp_result = bin.pack('HAA' , '81', string.char(#filter.val), filter.val) - else - for indexval, substr in ipairs(tmptable) do - if (indexval == 1) and (substr ~= '') then - -- 0x81 = 10000000 = 10 0 00000 - -- hex binary Context Primitive value Field: Sequence Value: 0 (initial / match at start of string) - tmp_result = bin.pack('HAA' , '80', string.char(#substr), substr) - end + if (#tmptable <= 1 ) then + -- 0x81 = 10000001 = 10 0 00001 + -- hex binary Context Primitive value Field: Sequence Value: 1 (any / any position in string) + tmp_result = bin.pack('HAA' , '81', string.char(#filter.val), filter.val) + else + for indexval, substr in ipairs(tmptable) do + if (indexval == 1) and (substr ~= '') then + -- 0x81 = 10000000 = 10 0 00000 + -- hex binary Context Primitive value Field: Sequence Value: 0 (initial / match at start of string) + tmp_result = bin.pack('HAA' , '80', string.char(#substr), substr) + end - if (indexval ~= #tmptable) and (indexval ~= 1) and (substr ~= '') then - -- 0x81 = 10000001 = 10 0 00001 - -- hex binary Context Primitive value Field: Sequence Value: 1 (any / match in any position in string) - tmp_result = tmp_result .. bin.pack('HAA' , '81', string.char(#substr), substr) - end + if (indexval ~= #tmptable) and (indexval ~= 1) and (substr ~= '') then + -- 0x81 = 10000001 = 10 0 00001 + -- hex binary Context Primitive value Field: Sequence Value: 1 (any / match in any position in string) + tmp_result = tmp_result .. bin.pack('HAA' , '81', string.char(#substr), substr) + end - if (indexval == #tmptable) and (substr ~= '') then - -- 0x82 = 10000010 = 10 0 00010 - -- hex binary Context Primitive value Field: Sequence Value: 2 (final / match at end of string) - tmp_result = tmp_result .. bin.pack('HAA' , '82', string.char(#substr), substr) - end - end - end + if (indexval == #tmptable) and (substr ~= '') then + -- 0x82 = 10000010 = 10 0 00010 + -- hex binary Context Primitive value Field: Sequence Value: 2 (final / match at end of string) + tmp_result = tmp_result .. bin.pack('HAA' , '82', string.char(#substr), substr) + end + end + end - val = asn1.ASN1Encoder:encodeSeq( tmp_result ) + val = asn1.ASN1Encoder:encodeSeq( tmp_result ) - elseif ( filter.op == FILTER['extensibleMatch'] ) then + elseif ( filter.op == FILTER['extensibleMatch'] ) then - local tmptable = stdnse.strsplit(':=', filter.val) - local tmp_result = '' - local OID, bitmask + local tmptable = stdnse.strsplit(':=', filter.val) + local tmp_result = '' + local OID, bitmask - if ( tmptable[1] ~= nil ) then - OID = tmptable[1] - else - return false, ("ERROR: Invalid extensibleMatch query format") - end + if ( tmptable[1] ~= nil ) then + OID = tmptable[1] + else + return false, ("ERROR: Invalid extensibleMatch query format") + end - if ( tmptable[2] ~= nil ) then - bitmask = tmptable[2] - else - return false, ("ERROR: Invalid extensibleMatch query format") - end + if ( tmptable[2] ~= nil ) then + bitmask = tmptable[2] + else + return false, ("ERROR: Invalid extensibleMatch query format") + end - -- Format and create matchingRule using OID - -- 0x81 = 10000001 = 10 0 00001 - -- hex binary Context Primitive value Field: matchingRule Value: 1 - tmp_result = bin.pack('HAA' , '81', string.char(#OID), OID) + -- Format and create matchingRule using OID + -- 0x81 = 10000001 = 10 0 00001 + -- hex binary Context Primitive value Field: matchingRule Value: 1 + tmp_result = bin.pack('HAA' , '81', string.char(#OID), OID) - -- Format and create type using ldap attribute - -- 0x82 = 10000010 = 10 0 00010 - -- hex binary Context Primitive value Field: Type Value: 2 - tmp_result = tmp_result .. bin.pack('HAA' , '82', string.char(#filter.obj), filter.obj) + -- Format and create type using ldap attribute + -- 0x82 = 10000010 = 10 0 00010 + -- hex binary Context Primitive value Field: Type Value: 2 + tmp_result = tmp_result .. bin.pack('HAA' , '82', string.char(#filter.obj), filter.obj) - -- Format and create matchValue using bitmask - -- 0x83 = 10000011 = 10 0 00011 - -- hex binary Context Primitive value Field: matchValue Value: 3 - tmp_result = tmp_result .. bin.pack('HAA' , '83', string.char(#bitmask), bitmask) + -- Format and create matchValue using bitmask + -- 0x83 = 10000011 = 10 0 00011 + -- hex binary Context Primitive value Field: matchValue Value: 3 + tmp_result = tmp_result .. bin.pack('HAA' , '83', string.char(#bitmask), bitmask) - -- Format and create dnAttributes, defaulting to false - -- 0x84 = 10000100 = 10 0 00100 - -- hex binary Context Primitive value Field: dnAttributes Value: 4 - -- - -- 0x01 = field length - -- 0x00 = boolean value, in this case false - tmp_result = tmp_result .. bin.pack('H' , '840100') + -- Format and create dnAttributes, defaulting to false + -- 0x84 = 10000100 = 10 0 00100 + -- hex binary Context Primitive value Field: dnAttributes Value: 4 + -- + -- 0x01 = field length + -- 0x00 = boolean value, in this case false + tmp_result = tmp_result .. bin.pack('H' , '840100') - -- Format the overall extensibleMatch block - -- 0xa9 = 10101001 = 10 1 01001 - -- hex binary Context Constructed Field: Filter Value: 9 (extensibleMatch) - return bin.pack('HAA' , 'a9', asn1.ASN1Encoder.encodeLength(#tmp_result), tmp_result) + -- Format the overall extensibleMatch block + -- 0xa9 = 10101001 = 10 1 01001 + -- hex binary Context Constructed Field: Filter Value: 9 (extensibleMatch) + return bin.pack('HAA' , 'a9', asn1.ASN1Encoder.encodeLength(#tmp_result), tmp_result) - else - val = encode( filter.val ) - end + else + val = encode( filter.val ) + end - filter_str = filter_str .. obj .. val + filter_str = filter_str .. obj .. val - end - return encode( { _ldaptype=bin.pack("A", string.format("%X", asn1_type)), filter_str } ) + end + return encode( { _ldaptype=bin.pack("A", string.format("%X", asn1_type)), filter_str } ) end --- Converts a search result as received from searchRequest to a "result" table @@ -524,39 +524,39 @@ end -- @param searchEntries table as returned from searchRequest -- @return table suitable for stdnse.format_output function searchResultToTable( searchEntries ) - local result = {} - for _, v in ipairs( searchEntries ) do - local result_part = {} - if v.objectName and v.objectName:len() > 0 then - result_part.name = string.format("dn: %s", v.objectName) - else - result_part.name = "" - end + local result = {} + for _, v in ipairs( searchEntries ) do + local result_part = {} + if v.objectName and v.objectName:len() > 0 then + result_part.name = string.format("dn: %s", v.objectName) + else + result_part.name = "" + end - local attribs = {} - if ( v.attributes ~= nil ) then - for _, attrib in ipairs( v.attributes ) do - for i=2, #attrib do - -- do some additional Windows decoding - if ( attrib[1] == "objectSid" ) then - table.insert( attribs, string.format( "%s: %d", attrib[1], decode( attrib[i] ) ) ) - elseif ( attrib[1] == "objectGUID") then - local _, o1, o2, o3, o4, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of = bin.unpack("C16", attrib[i] ) - table.insert( attribs, string.format( "%s: %x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x", attrib[1], o4, o3, o2, o1, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of ) ) - elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then - table.insert( attribs, string.format( "%s: %s", attrib[1], convertADTimeStamp(attrib[i]) ) ) - elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then - table.insert( attribs, string.format( "%s: %s", attrib[1], convertZuluTimeStamp(attrib[i]) ) ) - else - table.insert( attribs, string.format( "%s: %s", attrib[1], attrib[i] ) ) - end - end - end - table.insert( result_part, attribs ) - end - table.insert( result, result_part ) - end - return result + local attribs = {} + if ( v.attributes ~= nil ) then + for _, attrib in ipairs( v.attributes ) do + for i=2, #attrib do + -- do some additional Windows decoding + if ( attrib[1] == "objectSid" ) then + table.insert( attribs, string.format( "%s: %d", attrib[1], decode( attrib[i] ) ) ) + elseif ( attrib[1] == "objectGUID") then + local _, o1, o2, o3, o4, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of = bin.unpack("C16", attrib[i] ) + table.insert( attribs, string.format( "%s: %x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x", attrib[1], o4, o3, o2, o1, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of ) ) + elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then + table.insert( attribs, string.format( "%s: %s", attrib[1], convertADTimeStamp(attrib[i]) ) ) + elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then + table.insert( attribs, string.format( "%s: %s", attrib[1], convertZuluTimeStamp(attrib[i]) ) ) + else + table.insert( attribs, string.format( "%s: %s", attrib[1], attrib[i] ) ) + end + end + end + table.insert( result_part, attribs ) + end + table.insert( result, result_part ) + end + return result end --- Saves a search result as received from searchRequest to a file @@ -572,104 +572,104 @@ end -- @return table suitable for stdnse.format_output function searchResultToFile( searchEntries, filename ) - local f = io.open( filename, "w") + local f = io.open( filename, "w") - if ( not(f) ) then - return false, ("ERROR: Failed to open file (%s)"):format(filename) - end + if ( not(f) ) then + return false, ("ERROR: Failed to open file (%s)"):format(filename) + end - -- Build table structure. Using a multi pass approach ( build table then populate table ) - -- because the objects returned may not necessarily have the same number of attributes - -- making single pass CSV output generation problematic. - -- Unfortunately the searchEntries table passed to this function is not organized in a - -- way that make particular attributes for a given hostname directly addressable. - -- - -- At some point restructuring the searchEntries table may be a good optimization target + -- Build table structure. Using a multi pass approach ( build table then populate table ) + -- because the objects returned may not necessarily have the same number of attributes + -- making single pass CSV output generation problematic. + -- Unfortunately the searchEntries table passed to this function is not organized in a + -- way that make particular attributes for a given hostname directly addressable. + -- + -- At some point restructuring the searchEntries table may be a good optimization target - -- build table of attributes - local attrib_table = {} - for _, v in ipairs( searchEntries ) do - if ( v.attributes ~= nil ) then - for _, attrib in ipairs( v.attributes ) do - for i=2, #attrib do - if ( attrib_table[attrib[1]] == nil ) then - attrib_table[attrib[1]] = '' - end - end - end - end - end + -- build table of attributes + local attrib_table = {} + for _, v in ipairs( searchEntries ) do + if ( v.attributes ~= nil ) then + for _, attrib in ipairs( v.attributes ) do + for i=2, #attrib do + if ( attrib_table[attrib[1]] == nil ) then + attrib_table[attrib[1]] = '' + end + end + end + end + end - -- build table of hosts - local host_table = {} - for _, v in ipairs( searchEntries ) do - if v.objectName and v.objectName:len() > 0 then - local host = {} + -- build table of hosts + local host_table = {} + for _, v in ipairs( searchEntries ) do + if v.objectName and v.objectName:len() > 0 then + local host = {} - if v.objectName and v.objectName:len() > 0 then - -- use a copy of the table here, assigning attrib_table into host_table - -- links the values so setting it for one host changes the specific attribute - -- values for all hosts. - host_table[v.objectName] = {attributes = copyTable(attrib_table) } - end - end - end + if v.objectName and v.objectName:len() > 0 then + -- use a copy of the table here, assigning attrib_table into host_table + -- links the values so setting it for one host changes the specific attribute + -- values for all hosts. + host_table[v.objectName] = {attributes = copyTable(attrib_table) } + end + end + end - -- populate the host table with values for each attribute that has valid data - for _, v in ipairs( searchEntries ) do - if ( v.attributes ~= nil ) then - for _, attrib in ipairs( v.attributes ) do - for i=2, #attrib do - -- do some additional Windows decoding - if ( attrib[1] == "objectSid" ) then - host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%d", decode( attrib[i] ) ) + -- populate the host table with values for each attribute that has valid data + for _, v in ipairs( searchEntries ) do + if ( v.attributes ~= nil ) then + for _, attrib in ipairs( v.attributes ) do + for i=2, #attrib do + -- do some additional Windows decoding + if ( attrib[1] == "objectSid" ) then + host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%d", decode( attrib[i] ) ) - elseif ( attrib[1] == "objectGUID") then - local _, o1, o2, o3, o4, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of = bin.unpack("C16", attrib[i] ) - host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x", o4, o3, o2, o1, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of ) + elseif ( attrib[1] == "objectGUID") then + local _, o1, o2, o3, o4, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of = bin.unpack("C16", attrib[i] ) + host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x", o4, o3, o2, o1, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of ) - elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then - host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertADTimeStamp(attrib[i]) + elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then + host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertADTimeStamp(attrib[i]) - elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then - host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertZuluTimeStamp(attrib[i]) + elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then + host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertZuluTimeStamp(attrib[i]) - else - host_table[v.objectName].attributes[attrib[1]] = string.format( "%s", attrib[i] ) - end + else + host_table[v.objectName].attributes[attrib[1]] = string.format( "%s", attrib[i] ) + end - end - end - end - end + end + end + end + end - -- write the new, fully populuated table out to CSV + -- write the new, fully populuated table out to CSV - -- initialize header row - local output = "\"name\"" - for attribute, value in pairs(attrib_table) do - output = output .. ",\"" .. attribute .. "\"" - end - output = output .. "\n" + -- initialize header row + local output = "\"name\"" + for attribute, value in pairs(attrib_table) do + output = output .. ",\"" .. attribute .. "\"" + end + output = output .. "\n" - -- gather host data from fields, add to output. - for name, attribs in pairs(host_table) do - output = output .. "\"" .. name .. "\"" - local host_attribs = attribs.attributes - for attribute, value in pairs(attrib_table) do - output = output .. ",\"" .. host_attribs[attribute] .. "\"" - end - output = output .. "\n" - end + -- gather host data from fields, add to output. + for name, attribs in pairs(host_table) do + output = output .. "\"" .. name .. "\"" + local host_attribs = attribs.attributes + for attribute, value in pairs(attrib_table) do + output = output .. ",\"" .. host_attribs[attribute] .. "\"" + end + output = output .. "\n" + end - -- write the output to file - if ( not(f:write( output .."\n" ) ) ) then - f:close() - return false, ("ERROR: Failed to write file (%s)"):format(filename) - end + -- write the output to file + if ( not(f:write( output .."\n" ) ) ) then + f:close() + return false, ("ERROR: Failed to write file (%s)"):format(filename) + end - f:close() - return true + f:close() + return true end @@ -679,20 +679,20 @@ end -- @param attributeName string containing the attribute to extract -- @return table containing the attribute values function extractAttribute( searchEntries, attributeName ) - local attributeTbl = {} - for _, v in ipairs( searchEntries ) do - if ( v.attributes ~= nil ) then - for _, attrib in ipairs( v.attributes ) do - local attribType = attrib[1] - for i=2, #attrib do - if ( attribType:upper() == attributeName:upper() ) then - table.insert( attributeTbl, attrib[i]) - end - end - end - end - end - return ( #attributeTbl > 0 and attributeTbl or nil ) + local attributeTbl = {} + for _, v in ipairs( searchEntries ) do + if ( v.attributes ~= nil ) then + for _, attrib in ipairs( v.attributes ) do + local attribType = attrib[1] + for i=2, #attrib do + if ( attribType:upper() == attributeName:upper() ) then + table.insert( attributeTbl, attrib[i]) + end + end + end + end + end + return ( #attributeTbl > 0 and attributeTbl or nil ) end --- Convert Microsoft Active Directory timestamp format to a human readable form @@ -702,25 +702,25 @@ end -- @return string containing human readable form function convertADTimeStamp(timestamp) - local result = 0 - local base_time = tonumber(os.time({year=1601, month=1, day=1, hour=0, minute=0, sec =0})) + local result = 0 + local base_time = tonumber(os.time({year=1601, month=1, day=1, hour=0, minute=0, sec =0})) - timestamp = tonumber(timestamp) + timestamp = tonumber(timestamp) - if (timestamp and timestamp > 0) then + if (timestamp and timestamp > 0) then - -- The result value was 3036 seconds off what Microsoft says it should be. - -- I have been unable to find an explaination for this, and have resorted to - -- manually adjusting the formula. + -- The result value was 3036 seconds off what Microsoft says it should be. + -- I have been unable to find an explaination for this, and have resorted to + -- manually adjusting the formula. - result = ( timestamp / 10000000 ) - 3036 - result = result + base_time - result = os.date("%Y/%m/%d %H:%M:%S UTC", result) - else - result = 'Never' - end + result = ( timestamp / 10000000 ) - 3036 + result = result + base_time + result = os.date("%Y/%m/%d %H:%M:%S UTC", result) + else + result = 'Never' + end - return result + return result end @@ -732,21 +732,21 @@ end -- @return string containing human readable form function convertZuluTimeStamp(timestamp) - if ( type(timestamp) == 'string' and string.sub(timestamp,-3) == '.0Z' ) then + if ( type(timestamp) == 'string' and string.sub(timestamp,-3) == '.0Z' ) then - local year = string.sub(timestamp,1,4) - local month = string.sub(timestamp,5,6) - local day = string.sub(timestamp,7,8) - local hour = string.sub(timestamp,9,10) - local mins = string.sub(timestamp,11,12) - local secs = string.sub(timestamp,13,14) - local result = year .. "/" .. month .. "/" .. day .. " " .. hour .. ":" .. mins .. ":" .. secs .. " UTC" + local year = string.sub(timestamp,1,4) + local month = string.sub(timestamp,5,6) + local day = string.sub(timestamp,7,8) + local hour = string.sub(timestamp,9,10) + local mins = string.sub(timestamp,11,12) + local secs = string.sub(timestamp,13,14) + local result = year .. "/" .. month .. "/" .. day .. " " .. hour .. ":" .. mins .. ":" .. secs .. " UTC" - return result + return result - else - return 'Invalid date format' - end + else + return 'Invalid date format' + end end @@ -758,7 +758,7 @@ end function copyTable(targetTable) local temp = { } for key, val in pairs(targetTable) do - temp[key] = val + temp[key] = val end return setmetatable(temp, getmetatable(targetTable)) end diff --git a/nselib/mongodb.lua b/nselib/mongodb.lua index 0a9ddee7a..cd00ed9f1 100644 --- a/nselib/mongodb.lua +++ b/nselib/mongodb.lua @@ -28,7 +28,7 @@ local arg_DB = stdnse.get_script_args("mongodb.db") -- Some lazy shortcuts local function dbg(str,...) - stdnse.print_debug(3, "MngoDb:"..str, ...) + stdnse.print_debug(3, "MngoDb:"..str, ...) end --local dbg =stdnse.print_debug @@ -55,7 +55,7 @@ local err =stdnse.print_debug --module("bson", package.seeall) --require("bin") local function dbg_err(str,...) - stdnse.print_debug("Bson-ERR:"..str, ...) + stdnse.print_debug("Bson-ERR:"..str, ...) end --local err =stdnse.log_error @@ -63,7 +63,7 @@ end --@param input the string to pack --@return the packed nullterminated string local function make_nullterminated_string(input) - return bin.pack("z",input) + return bin.pack("z",input) end --Converts an element (key, value) into bson binary data @@ -73,35 +73,35 @@ end --@return result : the packed binary data OR error message local function _element_to_bson(key, value) - --Some constraints-checking - if type(key) ~= 'string' then - return false, "Documents must have only string keys, key was " .. type(key) - end - if key:sub(1,1) == "$" then - return false, "key must not start with $: ".. key - end - if key:find("%.") then - return false, ("key %r must not contain '.'"):format(tostring(key)) - end + --Some constraints-checking + if type(key) ~= 'string' then + return false, "Documents must have only string keys, key was " .. type(key) + end + if key:sub(1,1) == "$" then + return false, "key must not start with $: ".. key + end + if key:find("%.") then + return false, ("key %r must not contain '.'"):format(tostring(key)) + end - local name =bin.pack("z",key) -- null-terminated string - if type(value) == 'string' then - local cstring = bin.pack("z",value) -- null-terminated string - local length = bin.pack(" 4 * 1024 * 1024 then - return false, "document too large - BSON documents are limited to 4 MB" - end - dbg("Packet length is %d",length) - --Final pack - return true, bin.pack("I", length) .. elements .. bin.pack('H',"00") + if length > 4 * 1024 * 1024 then + return false, "document too large - BSON documents are limited to 4 MB" + end + dbg("Packet length is %d",length) + --Final pack + return true, bin.pack("I", length) .. elements .. bin.pack('H',"00") end -- Reads a null-terminated string. If length is supplied, it is just cut @@ -150,18 +150,18 @@ end --@return the string --@return the remaining data (*without* null-char) local function get_c_string(data,length) - if not length then - local index = data:find(string.char(0)) - if index == nil then - error({code="C-string did not contain NULL char"}) - end - length = index - end - local value = data:sub(1,length-1) + if not length then + local index = data:find(string.char(0)) + if index == nil then + error({code="C-string did not contain NULL char"}) + end + length = index + end + local value = data:sub(1,length-1) - --dbg("Found char at pos %d, data is %s c-string is %s",length, data, value) + --dbg("Found char at pos %d, data is %s c-string is %s",length, data, value) - return value, data:sub(length+1) + return value, data:sub(length+1) end -- Element parser. Parse data elements @@ -170,47 +170,47 @@ end -- @return Unpacked value -- @return error string if error occurred local function parse(code,data) - if 1 == code then -- double - return bin.unpack(" - local value = get_c_string(data:sub(5), len) - -- Count position as header (=4) + length of string (=len)+ null char (=1) - return 4+len+1,value - elseif 3 == code or 4 == code then -- table or array - local object, err + if 1 == code then -- double + return bin.unpack(" + local value = get_c_string(data:sub(5), len) + -- Count position as header (=4) + length of string (=len)+ null char (=1) + return 4+len+1,value + elseif 3 == code or 4 == code then -- table or array + local object, err - -- Need to know the length, to return later - local _,obj_size = bin.unpack(" 1 do - key, value, data = _element_to_dict(data) - dbg("Parsed (%s='%s'), data left : %d", tostring(key),tostring(value), data:len()) - if type(value) ~= 'table' then value=tostring(value) end - result[key] = value - end - return result + local result = {} + local key,value + while data and data:len() > 1 do + key, value, data = _element_to_dict(data) + dbg("Parsed (%s='%s'), data left : %d", tostring(key),tostring(value), data:len()) + if type(value) ~= 'table' then value=tostring(value) end + result[key] = value + end + return result end --Checks if enough data to parse the result is captured @@ -260,21 +260,21 @@ end --@return true if the full bson table is contained in the data, false if data is incomplete --@return required size of packet, if known, otherwise nil function isPacketComplete(data) - -- First, we check that the header is complete - if data:len() < 4 then - local err_msg = "Not enough data in buffer, at least 4 bytes header info expected" - return false - end + -- First, we check that the header is complete + if data:len() < 4 then + local err_msg = "Not enough data in buffer, at least 4 bytes header info expected" + return false + end - local _,obj_size = bin.unpack("stdnse.format_output function queryResultToTable( resultTable ) - local result = {} - for k,v in pairs( resultTable ) do - if type(v) == 'table' then - table.insert(result,k) - table.insert(result,queryResultToTable(v)) - else - table.insert(result,(("%s = %s"):format(tostring(k), tostring(v)))) - end - end - return result + local result = {} + for k,v in pairs( resultTable ) do + if type(v) == 'table' then + table.insert(result,k) + table.insert(result,queryResultToTable(v)) + else + table.insert(result,(("%s = %s"):format(tostring(k), tostring(v)))) + end + end + return result end ---------------------------------------------------------------------------------- @@ -656,30 +656,30 @@ end -- @param strData the data in a string format local function printBuffer(strData) - local out = '' - local ch - for i = 1,strData:len() do - out = out .." " - ch =strData:byte(i) - if(ch < 16) then - ch = string.format("0%x",ch) - else ch = string.format("%x",ch) - end - --if ch > 64 and ch < 123 then - -- out = out .. string.char(ch) - --else - out = out .. ch - --end - end - print(out) + local out = '' + local ch + for i = 1,strData:len() do + out = out .." " + ch =strData:byte(i) + if(ch < 16) then + ch = string.format("0%x",ch) + else ch = string.format("%x",ch) + end + --if ch > 64 and ch < 123 then + -- out = out .. string.char(ch) + --else + out = out .. ch + --end + end + print(out) end -- function test() --- local res --- res = versionQuery() --- print(type(res),res:len(),res) --- local out= bin.unpack('C'..#res,res) --- printBuffer(res) +-- local res +-- res = versionQuery() +-- print(type(res),res:len(),res) +-- local out= bin.unpack('C'..#res,res) +-- printBuffer(res) -- end --test() diff --git a/nselib/msrpc.lua b/nselib/msrpc.lua index e021f0e35..5b3ea084b 100644 --- a/nselib/msrpc.lua +++ b/nselib/msrpc.lua @@ -45,7 +45,7 @@ -- remove references to Samba. -- -- Revised 07/25/2012 - added Printer Spooler Service (spoolss) RPC functions and --- constants [Aleksandar Nikolic] +-- constants [Aleksandar Nikolic] --@author Ron Bowes --@copyright Same as Nmap--See http://nmap.org/book/man-legal.html ----------------------------------------------------------------------- @@ -74,7 +74,7 @@ SRVSVC_VERSION = 0x03 -- The path, UUID, and version for SPOOLSS SPOOLSS_PATH = "\\spoolss" -SPOOLSS_UUID = string.char(0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab) +SPOOLSS_UUID = string.char(0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0xcd, 0xab, 0xef, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab) SPOOLSS_VERSION = 0x01 -- The path, UUID, and version for LSA @@ -99,7 +99,7 @@ ATSVC_VERSION = 1 -- UUID and version for epmapper e1af8308-5d1f-11c9-91a4-08002b14a0fa v3.0 -EPMAPPER_PATH = "\\epmapper" +EPMAPPER_PATH = "\\epmapper" EPMAPPER_UUID = string.char(0x08, 0x83, 0xaf, 0xe1, 0x1f, 0x5d, 0xc9, 0x11, 0x91, 0xa4, 0x08, 0x00, 0x2b, 0x14, 0xa0, 0xfa) EPMAPPER_VERSION = 3 @@ -132,50 +132,50 @@ local LSA_MINEMPTY = 10 ---Mapping between well known MSRPC UUIDs and coresponding exe/service local UUID2EXE = { - ["1ff70682-0a51-30e8-076d-740be8cee98b"] = "mstask.exe atsvc interface (Scheduler service)", - ["3faf4738-3a21-4307-b46c-fdda9bb8c0d5"] = "AudioSrv AudioSrv interface (Windows Audio service)", - ["6bffd098-a112-3610-9833-012892020162"] = "Browser browser interface (Computer Browser service)", - ["91ae6020-9e3c-11cf-8d7c-00aa00c091be"] = "certsrv.exe ICertPassage interface (Certificate services)", - ["5ca4a760-ebb1-11cf-8611-00a0245420ed"] = "termsrv.exe winstation_rpc interface", - ["c8cb7687-e6d3-11d2-a958-00c04f682e16"] = "WebClient davclntrpc interface (WebDAV client service)", - ["50abc2a4-574d-40b3-9d66-ee4fd5fba076"] = "dns.exe DnsServer interface (DNS Server service)", - ["e1af8308-5d1f-11c9-91a4-08002b14a0fa"] = "RpcSs epmp interface (RPC endpoint mapper)", - ["82273fdc-e32a-18c3-3f78-827929dc23ea"] = "Eventlog eventlog interface (Eventlog service)", - ["3d267954-eeb7-11d1-b94e-00c04fa3080d"] = "lserver.exe Terminal Server Licensing", - ["894de0c0-0d55-11d3-a322-00c04fa321a1"] = "winlogon.exe InitShutdown interface", - ["8d0ffe72-d252-11d0-bf8f-00c04fd9126b"] = "CryptSvc IKeySvc interface (Cryptographic services)", - ["0d72a7d4-6148-11d1-b4aa-00c04fb66ea0"] = "CryptSvc ICertProtect interface (Cryptographic services)", - ["d6d70ef0-0e3b-11cb-acc3-08002b1d29c4"] = "locator.exe NsiS interface (RPC Locator service)", - ["342cfd40-3c6c-11ce-a893-08002b2e9c6d"] = "llssrv.exe llsrpc interface (Licensing Logging service)", - ["12345778-1234-abcd-ef00-0123456789ab"] = "lsass.exe lsarpc interface", - ["3919286a-b10c-11d0-9ba8-00c04fd92ef5"] = "lsass.exe dssetup interface", - ["5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc"] = "messenger msgsvcsend interface (Messenger service)", - ["2f5f3220-c126-1076-b549-074d078619da"] = "netdde.exe nddeapi interface (NetDDE service)", - ["4fc742e0-4a10-11cf-8273-00aa004ae673"] = "Dfssvc netdfs interface (Distributed File System service)", - ["12345678-1234-abcd-ef00-01234567cffb"] = "Netlogon netlogon interface (Net Logon service)", - ["8d9f4e40-a03d-11ce-8f69-08003e30051b"] = "PlugPlay pnp interface (Plug and Play service)", --- ["8d9f4e40-a03d-11ce-8f69-08003e30051b"] = "PlugPlay pnp interface (Plug and Play Windows Vista service)", - ["d335b8f6-cb31-11d0-b0f9-006097ba4e54"] = "PolicyAgent PolicyAgent interface (IPSEC Policy Agent (Windows 2000))", --- ["12345678-1234-abcd-ef00-0123456789ab"] = "PolicyAgent winipsec interface (IPsec Services)", - ["369ce4f0-0fdc-11d3-bde8-00c04f8eee78"] = "winlogon.exe pmapapi interface", - ["c9378ff1-16f7-11d0-a0b2-00aa0061426a"] = "lsass.exe IPStoreProv interface (Protected Storage)", - ["8f09f000-b7ed-11ce-bbd2-00001a181cad"] = "mprdim.dll Remote Access", - ["12345778-1234-abcd-ef00-0123456789ac"] = "lsass.exe samr interface", - ["93149ca2-973b-11d1-8c39-00c04fb984f9"] = "services.exe SceSvc", - ["12b81e99-f207-4a4c-85d3-77b42f76fd14"] = "seclogon ISeclogon interface (Secondary logon service)", - ["83da7c00-e84f-11d2-9807-00c04f8ec850"] = "winlogon.exe sfcapi interface (Windows File Protection)", --- ["12345678-1234-abcd-ef00-0123456789ab"] = "spoolsv.exe spoolss interface (Spooler service)", - ["4b324fc8-1670-01d3-1278-5a47bf6ee188"] = "services.exe (w2k) or svchost.exe (wxp and w2k3) srvsvc interface (Server service)", - ["4b112204-0e19-11d3-b42b-0000f81feb9f"] = "ssdpsrv ssdpsrv interface (SSDP service)", - ["367aeb81-9844-35f1-ad32-98f038001003"] = "services.exe svcctl interface (Services control manager)", - ["2f5f6520-ca46-1067-b319-00dd010662da"] = "Tapisrv tapsrv interface (Telephony service)", - ["300f3532-38cc-11d0-a3f0-0020af6b0add"] = "Trkwks trkwks interface (Distributed Link Tracking Client)", - ["8fb6d884-2388-11d0-8c35-00c04fda2795"] = "w32time w32time interface (Windows Time)", --- ["8fb6d884-2388-11d0-8c35-00c04fda2795"] = "w32time w32time interface (Windows Time (Windows Server 2003, Windows Vista))", - ["a002b3a0-c9b7-11d1-ae88-0080c75e4ec1"] = "winlogon.exe GetUserToken interface", - ["338cd001-2244-31f1-aaaa-900038001003"] = "RemoteRegistry winreg interface (Remote registry service)", - ["45f52c28-7f9f-101a-b52b-08002b2efabe"] = "wins.exe winsif interface (WINS service)", - ["6bffd098-a112-3610-9833-46c3f87e345a"] = "services.exe (w2k) or svchost.exe (wxp and w2k3) wkssvc interface (Workstation service)" + ["1ff70682-0a51-30e8-076d-740be8cee98b"] = "mstask.exe atsvc interface (Scheduler service)", + ["3faf4738-3a21-4307-b46c-fdda9bb8c0d5"] = "AudioSrv AudioSrv interface (Windows Audio service)", + ["6bffd098-a112-3610-9833-012892020162"] = "Browser browser interface (Computer Browser service)", + ["91ae6020-9e3c-11cf-8d7c-00aa00c091be"] = "certsrv.exe ICertPassage interface (Certificate services)", + ["5ca4a760-ebb1-11cf-8611-00a0245420ed"] = "termsrv.exe winstation_rpc interface", + ["c8cb7687-e6d3-11d2-a958-00c04f682e16"] = "WebClient davclntrpc interface (WebDAV client service)", + ["50abc2a4-574d-40b3-9d66-ee4fd5fba076"] = "dns.exe DnsServer interface (DNS Server service)", + ["e1af8308-5d1f-11c9-91a4-08002b14a0fa"] = "RpcSs epmp interface (RPC endpoint mapper)", + ["82273fdc-e32a-18c3-3f78-827929dc23ea"] = "Eventlog eventlog interface (Eventlog service)", + ["3d267954-eeb7-11d1-b94e-00c04fa3080d"] = "lserver.exe Terminal Server Licensing", + ["894de0c0-0d55-11d3-a322-00c04fa321a1"] = "winlogon.exe InitShutdown interface", + ["8d0ffe72-d252-11d0-bf8f-00c04fd9126b"] = "CryptSvc IKeySvc interface (Cryptographic services)", + ["0d72a7d4-6148-11d1-b4aa-00c04fb66ea0"] = "CryptSvc ICertProtect interface (Cryptographic services)", + ["d6d70ef0-0e3b-11cb-acc3-08002b1d29c4"] = "locator.exe NsiS interface (RPC Locator service)", + ["342cfd40-3c6c-11ce-a893-08002b2e9c6d"] = "llssrv.exe llsrpc interface (Licensing Logging service)", + ["12345778-1234-abcd-ef00-0123456789ab"] = "lsass.exe lsarpc interface", + ["3919286a-b10c-11d0-9ba8-00c04fd92ef5"] = "lsass.exe dssetup interface", + ["5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc"] = "messenger msgsvcsend interface (Messenger service)", + ["2f5f3220-c126-1076-b549-074d078619da"] = "netdde.exe nddeapi interface (NetDDE service)", + ["4fc742e0-4a10-11cf-8273-00aa004ae673"] = "Dfssvc netdfs interface (Distributed File System service)", + ["12345678-1234-abcd-ef00-01234567cffb"] = "Netlogon netlogon interface (Net Logon service)", + ["8d9f4e40-a03d-11ce-8f69-08003e30051b"] = "PlugPlay pnp interface (Plug and Play service)", + -- ["8d9f4e40-a03d-11ce-8f69-08003e30051b"] = "PlugPlay pnp interface (Plug and Play Windows Vista service)", + ["d335b8f6-cb31-11d0-b0f9-006097ba4e54"] = "PolicyAgent PolicyAgent interface (IPSEC Policy Agent (Windows 2000))", + -- ["12345678-1234-abcd-ef00-0123456789ab"] = "PolicyAgent winipsec interface (IPsec Services)", + ["369ce4f0-0fdc-11d3-bde8-00c04f8eee78"] = "winlogon.exe pmapapi interface", + ["c9378ff1-16f7-11d0-a0b2-00aa0061426a"] = "lsass.exe IPStoreProv interface (Protected Storage)", + ["8f09f000-b7ed-11ce-bbd2-00001a181cad"] = "mprdim.dll Remote Access", + ["12345778-1234-abcd-ef00-0123456789ac"] = "lsass.exe samr interface", + ["93149ca2-973b-11d1-8c39-00c04fb984f9"] = "services.exe SceSvc", + ["12b81e99-f207-4a4c-85d3-77b42f76fd14"] = "seclogon ISeclogon interface (Secondary logon service)", + ["83da7c00-e84f-11d2-9807-00c04f8ec850"] = "winlogon.exe sfcapi interface (Windows File Protection)", + -- ["12345678-1234-abcd-ef00-0123456789ab"] = "spoolsv.exe spoolss interface (Spooler service)", + ["4b324fc8-1670-01d3-1278-5a47bf6ee188"] = "services.exe (w2k) or svchost.exe (wxp and w2k3) srvsvc interface (Server service)", + ["4b112204-0e19-11d3-b42b-0000f81feb9f"] = "ssdpsrv ssdpsrv interface (SSDP service)", + ["367aeb81-9844-35f1-ad32-98f038001003"] = "services.exe svcctl interface (Services control manager)", + ["2f5f6520-ca46-1067-b319-00dd010662da"] = "Tapisrv tapsrv interface (Telephony service)", + ["300f3532-38cc-11d0-a3f0-0020af6b0add"] = "Trkwks trkwks interface (Distributed Link Tracking Client)", + ["8fb6d884-2388-11d0-8c35-00c04fda2795"] = "w32time w32time interface (Windows Time)", + -- ["8fb6d884-2388-11d0-8c35-00c04fda2795"] = "w32time w32time interface (Windows Time (Windows Server 2003, Windows Vista))", + ["a002b3a0-c9b7-11d1-ae88-0080c75e4ec1"] = "winlogon.exe GetUserToken interface", + ["338cd001-2244-31f1-aaaa-900038001003"] = "RemoteRegistry winreg interface (Remote registry service)", + ["45f52c28-7f9f-101a-b52b-08002b2efabe"] = "wins.exe winsif interface (WINS service)", + ["6bffd098-a112-3610-9833-46c3f87e345a"] = "services.exe (w2k) or svchost.exe (wxp and w2k3) wkssvc interface (Workstation service)" } @@ -194,8 +194,8 @@ local UUID2EXE = { --@return (status, smbstate) if status is false, smbstate is an error message. Otherwise, smbstate is -- required for all further calls. function start_smb(host, path, disable_extended, overrides) - overrides = overrides or {} - return smb.start_ex(host, true, true, "IPC$", path, disable_extended, overrides) + overrides = overrides or {} + return smb.start_ex(host, true, true, "IPC$", path, disable_extended, overrides) end --- A wrapper around the smb.stop function. I only created it to add symmetry, so client code @@ -203,7 +203,7 @@ end -- --@param state The SMB state table. function stop_smb(state) - smb.stop(state) + smb.stop(state) end --- Bind to a MSRPC interface. Two common interfaces are SAML and SRVSVC, and can be found as @@ -221,127 +221,127 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a -- table of values, none of which are especially useful. function bind(smbstate, interface_uuid, interface_version, transfer_syntax) - local i - local status, result - local parameters, data - local pos, align - local result + local i + local status, result + local parameters, data + local pos, align + local result - stdnse.print_debug(2, "MSRPC: Sending Bind() request") + stdnse.print_debug(2, "MSRPC: Sending Bind() request") - -- Use the only transfer_syntax value I know of. - if(transfer_syntax == nil) then - transfer_syntax = TRANSFER_SYNTAX - end + -- Use the only transfer_syntax value I know of. + if(transfer_syntax == nil) then + transfer_syntax = TRANSFER_SYNTAX + end - data = bin.pack("IIIIIIII 0 and detail_level < 2, "detail_level must be either 0 or 1") - local datadesc = ( detail_level == 0 and "B16" or "B16BBDz") - local data = bin.pack(" 0 and detail_level < 2, "detail_level must be either 0 or 1") + local datadesc = ( detail_level == 0 and "B16" or "B16BBDz") + local data = bin.pack(" 0 ) then - local comment_offset, _ - server.version = {} - pos, server.version.major, server.version.minor, - server.type, comment_offset, _ = bin.unpack(" 0 ) then + local comment_offset, _ + server.version = {} + pos, server.version.major, server.version.minor, + server.type, comment_offset, _ = bin.unpack("msrpctypes function that converts a ShareType to an english string. @@ -556,7 +556,7 @@ end --@param val The value to convert. --@return A string that can be displayed to the user. function srvsvc_ShareType_tostr(val) - return msrpctypes.srvsvc_ShareType_tostr(val) + return msrpctypes.srvsvc_ShareType_tostr(val) end ---Call the MSRPC function netshareenumall on the remote system. This function basically returns a list of all the shares @@ -567,71 +567,71 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'shares', which is a list of the system's shares. function srvsvc_netshareenumall(smbstate, server) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - local level - local ctr, referent, count, max_count + local level + local ctr, referent, count, max_count - stdnse.print_debug(2, "MSRPC: Calling NetShareEnumAll() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling NetShareEnumAll() [%s]", smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *server_unc - arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) + -- [in] [string,charset(UTF16)] uint16 *server_unc + arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) --- [in,out] uint32 level - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in,out] uint32 level + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in,out,switch_is(level)] srvsvc_NetShareCtr ctr - arguments = arguments .. msrpctypes.marshall_srvsvc_NetShareCtr(0, {array=nil}) + -- [in,out,switch_is(level)] srvsvc_NetShareCtr ctr + arguments = arguments .. msrpctypes.marshall_srvsvc_NetShareCtr(0, {array=nil}) --- [in] uint32 max_buffer, - arguments = arguments .. msrpctypes.marshall_int32(4096) + -- [in] uint32 max_buffer, + arguments = arguments .. msrpctypes.marshall_int32(4096) --- [out] uint32 totalentries --- [in,out] uint32 *resume_handle* - arguments = arguments .. msrpctypes.marshall_int32_ptr(0) + -- [out] uint32 totalentries + -- [in,out] uint32 *resume_handle* + arguments = arguments .. msrpctypes.marshall_int32_ptr(0) - -- Do the call - status, result = call_function(smbstate, 0x0F, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0F, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: NetShareEnumAll() returned successfully") + stdnse.print_debug(3, "MSRPC: NetShareEnumAll() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *server_unc --- [in,out] uint32 level - pos, result['level'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in] [string,charset(UTF16)] uint16 *server_unc + -- [in,out] uint32 level + pos, result['level'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in,out,switch_is(level)] srvsvc_NetShareCtr ctr - pos, result['ctr'] = msrpctypes.unmarshall_srvsvc_NetShareCtr(arguments, pos, level) - if(pos == nil) then - return false, "unmarshall_srvsvc_NetShareCtr() returned an error" - end + -- [in,out,switch_is(level)] srvsvc_NetShareCtr ctr + pos, result['ctr'] = msrpctypes.unmarshall_srvsvc_NetShareCtr(arguments, pos, level) + if(pos == nil) then + return false, "unmarshall_srvsvc_NetShareCtr() returned an error" + end --- [out] uint32 totalentries - pos, result['totalentries'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out] uint32 totalentries + pos, result['totalentries'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in,out] uint32 *resume_handle - pos, result['resume_handle'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) + -- [in,out] uint32 *resume_handle + pos, result['resume_handle'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) - -- The return value - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (srvsvc.netshareenumall)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (srvsvc.netshareenumall)" - end + -- The return value + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netshareenumall)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netshareenumall)" + end - return true, result + return true, result end ---Call the MSRPC function netsharegetinfo on the remote system. This function retrieves extra information about a share @@ -642,54 +642,54 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'shares', which is a list of the system's shares. function srvsvc_netsharegetinfo(smbstate, server, share, level) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align --- [in] [string,charset(UTF16)] uint16 *server_unc, - arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) + -- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) --- [in] [string,charset(UTF16)] uint16 share_name[], - arguments = arguments .. msrpctypes.marshall_unicode(share, true) + -- [in] [string,charset(UTF16)] uint16 share_name[], + arguments = arguments .. msrpctypes.marshall_unicode(share, true) --- [in] uint32 level, - arguments = arguments .. msrpctypes.marshall_int32(level) + -- [in] uint32 level, + arguments = arguments .. msrpctypes.marshall_int32(level) --- [out,switch_is(level)] srvsvc_NetShareInfo info + -- [out,switch_is(level)] srvsvc_NetShareInfo info - -- Do the call - status, result = call_function(smbstate, 0x10, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x10, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: NetShareGetInfo() returned successfully") + stdnse.print_debug(3, "MSRPC: NetShareGetInfo() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *server_unc, --- [in] [string,charset(UTF16)] uint16 share_name[], --- [in] uint32 level, --- [out,switch_is(level)] srvsvc_NetShareInfo info - pos, result['info'] = msrpctypes.unmarshall_srvsvc_NetShareInfo(arguments, pos) - if(pos == nil) then - return false, "unmarshall_srvsvc_NetShareInfo() returned an error" - end + -- [in] [string,charset(UTF16)] uint16 *server_unc, + -- [in] [string,charset(UTF16)] uint16 share_name[], + -- [in] uint32 level, + -- [out,switch_is(level)] srvsvc_NetShareInfo info + pos, result['info'] = msrpctypes.unmarshall_srvsvc_NetShareInfo(arguments, pos) + if(pos == nil) then + return false, "unmarshall_srvsvc_NetShareInfo() returned an error" + end - -- The return value - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (srvsvc.netsharegetinfo)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (srvsvc.netsharegetinfo)" - end + -- The return value + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netsharegetinfo)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netsharegetinfo)" + end - return true, result + return true, result end @@ -701,80 +701,80 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is an array of tables. -- Each table contains the elements 'user', 'client', 'active', and 'idle'. function srvsvc_netsessenum(smbstate, server) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling NetSessEnum() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling NetSessEnum() [%s]", smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *server_unc, - arguments = msrpctypes.marshall_unicode_ptr(server, true) + -- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = msrpctypes.marshall_unicode_ptr(server, true) --- [in] [string,charset(UTF16)] uint16 *client, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) + -- [in] [string,charset(UTF16)] uint16 *client, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) --- [in] [string,charset(UTF16)] uint16 *user, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) + -- [in] [string,charset(UTF16)] uint16 *user, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) --- [in,out] uint32 level, - arguments = arguments .. msrpctypes.marshall_int32(10) -- 10 seems to be the only useful one allowed anonymously + -- [in,out] uint32 level, + arguments = arguments .. msrpctypes.marshall_int32(10) -- 10 seems to be the only useful one allowed anonymously --- [in,out,switch_is(level)] srvsvc_NetSessCtr ctr, - arguments = arguments .. msrpctypes.marshall_srvsvc_NetSessCtr(10, {array=nil}) + -- [in,out,switch_is(level)] srvsvc_NetSessCtr ctr, + arguments = arguments .. msrpctypes.marshall_srvsvc_NetSessCtr(10, {array=nil}) --- [in] uint32 max_buffer, - arguments = arguments .. msrpctypes.marshall_int32(0xFFFFFFFF) + -- [in] uint32 max_buffer, + arguments = arguments .. msrpctypes.marshall_int32(0xFFFFFFFF) --- [out] uint32 totalentries, --- [in,out] uint32 *resume_handle - arguments = arguments .. msrpctypes.marshall_int32_ptr(0) + -- [out] uint32 totalentries, + -- [in,out] uint32 *resume_handle + arguments = arguments .. msrpctypes.marshall_int32_ptr(0) - -- Do the call - status, result = call_function(smbstate, 0x0C, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0C, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: NetSessEnum() returned successfully") + stdnse.print_debug(3, "MSRPC: NetSessEnum() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 - local count - local sessions = {} - local referent_id --- [in] [string,charset(UTF16)] uint16 *server_unc, --- [in] [string,charset(UTF16)] uint16 *client, --- [in] [string,charset(UTF16)] uint16 *user, --- [in,out] uint32 level, - pos, result['level'] = msrpctypes.unmarshall_int32(arguments, pos) + local count + local sessions = {} + local referent_id + -- [in] [string,charset(UTF16)] uint16 *server_unc, + -- [in] [string,charset(UTF16)] uint16 *client, + -- [in] [string,charset(UTF16)] uint16 *user, + -- [in,out] uint32 level, + pos, result['level'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in,out,switch_is(level)] srvsvc_NetSessCtr ctr, - pos, result['ctr'] = msrpctypes.unmarshall_srvsvc_NetSessCtr(arguments, pos) - if(pos == nil) then - return false, "unmarshall_srvsvc_NetSessCtr() returned an error" - end + -- [in,out,switch_is(level)] srvsvc_NetSessCtr ctr, + pos, result['ctr'] = msrpctypes.unmarshall_srvsvc_NetSessCtr(arguments, pos) + if(pos == nil) then + return false, "unmarshall_srvsvc_NetSessCtr() returned an error" + end --- [in] uint32 max_buffer, --- [out] uint32 totalentries, - pos, result['totalentries'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in] uint32 max_buffer, + -- [out] uint32 totalentries, + pos, result['totalentries'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in,out] uint32 *resume_handle - pos, result['resume_handle'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) + -- [in,out] uint32 *resume_handle + pos, result['resume_handle'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (srvsvc.netsessenum)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (srvsvc.netsessenum)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netsessenum)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netsessenum)" + end - return true, result + return true, result end --- Calls the NetServerGetStatistics function, which grabs a bunch of statistics on the server. @@ -806,58 +806,58 @@ end -- * 'reqbufneed' The number of times the server required a request buffer but failed to allocate one. This value indicates that the server parameters may need adjustment. -- * 'bigbufneed' The number of times the server required a big buffer but failed to allocate one. This value indicates that the server parameters may need adjustment. function srvsvc_netservergetstatistics(smbstate, server) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - local service = "SERVICE_SERVER" + local service = "SERVICE_SERVER" - stdnse.print_debug(2, "MSRPC: Calling NetServerGetStatistics() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling NetServerGetStatistics() [%s]", smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *server_unc, - arguments = msrpctypes.marshall_unicode_ptr(server, true) + -- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = msrpctypes.marshall_unicode_ptr(server, true) --- [in] [string,charset(UTF16)] uint16 *service, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(service, true) + -- [in] [string,charset(UTF16)] uint16 *service, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(service, true) --- [in] uint32 level, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 level, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] uint32 options, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 options, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [out] srvsvc_Statistics stat + -- [out] srvsvc_Statistics stat - -- Do the call - status, result = call_function(smbstate, 0x18, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x18, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: NetServerGetStatistics() returned successfully") + stdnse.print_debug(3, "MSRPC: NetServerGetStatistics() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *server_unc, --- [in] [string,charset(UTF16)] uint16 *service, --- [in] uint32 level, --- [in] uint32 options, --- [out] srvsvc_Statistics stat - pos, result['stat'] = msrpctypes.unmarshall_srvsvc_Statistics_ptr(arguments, pos) + -- [in] [string,charset(UTF16)] uint16 *server_unc, + -- [in] [string,charset(UTF16)] uint16 *service, + -- [in] uint32 level, + -- [in] uint32 options, + -- [out] srvsvc_Statistics stat + pos, result['stat'] = msrpctypes.unmarshall_srvsvc_Statistics_ptr(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (srvsvc.netservergetstatistics)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (srvsvc.netservergetstatistics)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netservergetstatistics)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netservergetstatistics)" + end - return true, result + return true, result end ---Call the NetPathCompare() function, which indirectly calls NetPathCanonicalize(), @@ -879,57 +879,57 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values containing -- 'return'. function srvsvc_netpathcompare(smbstate, server, path1, path2, pathtype, pathflags) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling NetPathCompare(%s, %s) [%s]", path1, path2, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling NetPathCompare(%s, %s) [%s]", path1, path2, smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *server_unc, - arguments = msrpctypes.marshall_unicode_ptr(server, true) + -- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = msrpctypes.marshall_unicode_ptr(server, true) --- [in] [string,charset(UTF16)] uint16 path1[], - arguments = arguments .. msrpctypes.marshall_unicode(path1, true) + -- [in] [string,charset(UTF16)] uint16 path1[], + arguments = arguments .. msrpctypes.marshall_unicode(path1, true) --- [in] [string,charset(UTF16)] uint16 path2[], - arguments = arguments .. msrpctypes.marshall_unicode(path2, true) + -- [in] [string,charset(UTF16)] uint16 path2[], + arguments = arguments .. msrpctypes.marshall_unicode(path2, true) --- [in] uint32 pathtype, - arguments = arguments .. msrpctypes.marshall_int32(pathtype) + -- [in] uint32 pathtype, + arguments = arguments .. msrpctypes.marshall_int32(pathtype) --- [in] uint32 pathflags - arguments = arguments .. msrpctypes.marshall_int32(pathflags) + -- [in] uint32 pathflags + arguments = arguments .. msrpctypes.marshall_int32(pathflags) - -- Do the call - status, result = call_function(smbstate, 0x20, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x20, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: NetPathCompare() returned successfully") + stdnse.print_debug(3, "MSRPC: NetPathCompare() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *server_unc, --- [in] [string,charset(UTF16)] uint16 path1[], --- [in] [string,charset(UTF16)] uint16 path2[], --- [in] uint32 pathtype, --- [in] uint32 pathflags + -- [in] [string,charset(UTF16)] uint16 *server_unc, + -- [in] [string,charset(UTF16)] uint16 path1[], + -- [in] [string,charset(UTF16)] uint16 path2[], + -- [in] uint32 pathtype, + -- [in] uint32 pathflags - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (srvsvc.netpathcompare)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcompare)" - end + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netpathcompare)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcompare)" + end - return true, result + return true, result end @@ -942,66 +942,66 @@ end --@return (status, result, error_result) If status is false, result is an error message and error_result is -- the result table. Otherwise, result is a table of values. function srvsvc_netpathcanonicalize(smbstate, server, path) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling NetPathCanonicalize(%s) [%s]", path, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling NetPathCanonicalize(%s) [%s]", path, smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *server_unc, - arguments = msrpctypes.marshall_unicode_ptr(server, true) --- [in] [string,charset(UTF16)] uint16 path[], - arguments = arguments .. msrpctypes.marshall_unicode(path, true) --- [out] [size_is(maxbuf)] uint8 can_path[], --- [in] uint32 maxbuf, - arguments = arguments .. msrpctypes.marshall_int32(2) + -- [in] [string,charset(UTF16)] uint16 *server_unc, + arguments = msrpctypes.marshall_unicode_ptr(server, true) + -- [in] [string,charset(UTF16)] uint16 path[], + arguments = arguments .. msrpctypes.marshall_unicode(path, true) + -- [out] [size_is(maxbuf)] uint8 can_path[], + -- [in] uint32 maxbuf, + arguments = arguments .. msrpctypes.marshall_int32(2) --- [in] [string,charset(UTF16)] uint16 prefix[], - arguments = arguments .. msrpctypes.marshall_unicode("\\", true) + -- [in] [string,charset(UTF16)] uint16 prefix[], + arguments = arguments .. msrpctypes.marshall_unicode("\\", true) --- [in,out] uint32 pathtype, - arguments = arguments .. msrpctypes.marshall_int32(1) --- [in] uint32 pathflags - arguments = arguments .. msrpctypes.marshall_int32(1) + -- [in,out] uint32 pathtype, + arguments = arguments .. msrpctypes.marshall_int32(1) + -- [in] uint32 pathflags + arguments = arguments .. msrpctypes.marshall_int32(1) - -- Do the call - status, result = call_function(smbstate, 0x1F, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x1F, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: NetPathCanonicalize() returned successfully") + stdnse.print_debug(3, "MSRPC: NetPathCanonicalize() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *server_unc, --- [in] [string,charset(UTF16)] uint16 path[], --- [out] [size_is(maxbuf)] uint8 can_path[],A --- [in] uint32 maxbuf, --- [in] [string,charset(UTF16)] uint16 prefix[], --- [in,out] uint32 pathtype, --- [in] uint32 pathflags + -- [in] [string,charset(UTF16)] uint16 *server_unc, + -- [in] [string,charset(UTF16)] uint16 path[], + -- [out] [size_is(maxbuf)] uint8 can_path[],A + -- [in] uint32 maxbuf, + -- [in] [string,charset(UTF16)] uint16 prefix[], + -- [in,out] uint32 pathtype, + -- [in] uint32 pathflags - -- NOTE: This isn't being done correctly.. due to Wireshark's broken parsing, - -- and Samba's possibly-broken definition, I'm not sure how this is supposed - -- to be parsed. - pos, result['max_count'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['can_path'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['type'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + -- NOTE: This isn't being done correctly.. due to Wireshark's broken parsing, + -- and Samba's possibly-broken definition, I'm not sure how this is supposed + -- to be parsed. + pos, result['max_count'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['can_path'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['type'] = msrpctypes.unmarshall_int32(arguments, pos) + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (srvsvc.netpathcanonicalize)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcanonicalize)", result - end + if(result['return'] == nil) then + return false, "Read off the end of the packet (srvsvc.netpathcanonicalize)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (srvsvc.netpathcanonicalize)", result + end - return true, result + return true, result end @@ -1013,136 +1013,136 @@ end --@param printer Printer share name --@return (status, result) If status is false, result is an error message. Otherwise, result is a printer handle. function spoolss_open_printer(smbstate,printer) - local machine = msrpctypes.marshall_unicode_ptr("",true) - local user = msrpctypes.marshall_unicode_ptr("",true) + local machine = msrpctypes.marshall_unicode_ptr("",true) + local user = msrpctypes.marshall_unicode_ptr("",true) - local arguments = msrpctypes.marshall_unicode_ptr(printer,true) - arguments = arguments .. msrpctypes.marshall_int32(0) - --devmod containter - arguments = arguments .. msrpctypes.marshall_int32(0) - arguments = arguments .. msrpctypes.marshall_int32(0) - --access we require - arguments = arguments .. msrpctypes.marshall_int32(0x02020000) - -- spool client containter - arguments = arguments .. msrpctypes.marshall_int32(1) - arguments = arguments .. msrpctypes.marshall_int32(1) - arguments = arguments .. msrpctypes.marshall_int32(12345135) + local arguments = msrpctypes.marshall_unicode_ptr(printer,true) + arguments = arguments .. msrpctypes.marshall_int32(0) + --devmod containter + arguments = arguments .. msrpctypes.marshall_int32(0) + arguments = arguments .. msrpctypes.marshall_int32(0) + --access we require + arguments = arguments .. msrpctypes.marshall_int32(0x02020000) + -- spool client containter + arguments = arguments .. msrpctypes.marshall_int32(1) + arguments = arguments .. msrpctypes.marshall_int32(1) + arguments = arguments .. msrpctypes.marshall_int32(12345135) - local arguments2 = string.sub(machine,1,4) - arguments2 = arguments2 .. string.sub(user,1,4) - arguments2 = arguments2 .. msrpctypes.marshall_int32(7600) - arguments2 = arguments2 .. msrpctypes.marshall_int32(3) - arguments2 = arguments2 .. msrpctypes.marshall_int32(0) - arguments2 = arguments2 .. msrpctypes.marshall_int32(9) - arguments2 = arguments2 .. string.sub(machine,5,#machine) - arguments2 = arguments2 .. string.sub(user,5,#user) - arguments2 = msrpctypes.marshall_int32(#arguments2+4) .. arguments2 + local arguments2 = string.sub(machine,1,4) + arguments2 = arguments2 .. string.sub(user,1,4) + arguments2 = arguments2 .. msrpctypes.marshall_int32(7600) + arguments2 = arguments2 .. msrpctypes.marshall_int32(3) + arguments2 = arguments2 .. msrpctypes.marshall_int32(0) + arguments2 = arguments2 .. msrpctypes.marshall_int32(9) + arguments2 = arguments2 .. string.sub(machine,5,#machine) + arguments2 = arguments2 .. string.sub(user,5,#user) + arguments2 = msrpctypes.marshall_int32(#arguments2+4) .. arguments2 - arguments = arguments .. arguments2 + arguments = arguments .. arguments2 - local status, result = call_function(smbstate, 69, arguments) - if not status then - stdnse.print_debug("MSRPC spoolss_open_printer(): %s ",result) - end - return status,result + local status, result = call_function(smbstate, 69, arguments) + if not status then + stdnse.print_debug("MSRPC spoolss_open_printer(): %s ",result) + end + return status,result end ---Call the RpcStartDocPrinter() function whose opnum is 17. -- -- http://msdn.microsoft.com/en-us/library/cc244828%28v=prot.10%29.aspx ---@param smbstate The SMB state table +--@param smbstate The SMB state table --@param printer_handle Printer handle returned by spoolss_open_printer() ---@param filename Name of the file to print to +--@param filename Name of the file to print to --@return (status, result) If status is false, result is an error message. Otherwise, result is a print job id. function spoolss_start_doc_printer(smbstate,printer_handle,filename) - local arguments = printer_handle - local document_name = msrpctypes.marshall_unicode_ptr("nmap_test",true) - local fname = msrpctypes.marshall_unicode_ptr(filename,true) - local dtype = msrpctypes.marshall_int32(0) - local document_container = msrpctypes.marshall_int32(1) + local arguments = printer_handle + local document_name = msrpctypes.marshall_unicode_ptr("nmap_test",true) + local fname = msrpctypes.marshall_unicode_ptr(filename,true) + local dtype = msrpctypes.marshall_int32(0) + local document_container = msrpctypes.marshall_int32(1) - arguments = arguments .. msrpctypes.marshall_int32(1) + arguments = arguments .. msrpctypes.marshall_int32(1) - document_container = document_container .. msrpctypes.marshall_int32(12332131) - document_container = document_container .. string.sub(document_name,1,4) - document_container = document_container .. string.sub(fname,1,4) - document_container = document_container .. string.sub(dtype,1,4) - document_container = document_container .. string.sub(document_name,5,#document_name) - document_container = document_container .. string.sub(fname,5,#fname) - document_container = document_container .. string.sub(dtype,5,#dtype) + document_container = document_container .. msrpctypes.marshall_int32(12332131) + document_container = document_container .. string.sub(document_name,1,4) + document_container = document_container .. string.sub(fname,1,4) + document_container = document_container .. string.sub(dtype,1,4) + document_container = document_container .. string.sub(document_name,5,#document_name) + document_container = document_container .. string.sub(fname,5,#fname) + document_container = document_container .. string.sub(dtype,5,#dtype) - arguments = arguments .. document_container + arguments = arguments .. document_container - local status, result = call_function(smbstate, 17, arguments) - if not status then - stdnse.print_debug("MSRPC spoolss_start_doc_printer(): %s",result) - end - return status,result + local status, result = call_function(smbstate, 17, arguments) + if not status then + stdnse.print_debug("MSRPC spoolss_start_doc_printer(): %s",result) + end + return status,result end ---Call the RpcWritePrinter() function whose opnum is 19. -- -- http://msdn.microsoft.com/en-us/library/cc244831%28v=prot.10%29 ---@param smbstate The SMB state table +--@param smbstate The SMB state table --@param printer_handle Printer handle returned by spoolss_open_printer() ---@param data Actuall data to write to a file +--@param data Actuall data to write to a file --@return (status, result) If status is false, result is an error message. Otherwise, result is number of bytes written. function spoolss_write_printer(smbstate,printer_handle,data) - stdnse.print_debug("len %d", #data) - local padding_len = 4 - math.fmod(#data,4) - local data_padding = nil - if not (padding_len == 4) then - data_padding = string.rep(bin.pack("H","00"),padding_len) - end - local arguments = printer_handle .. msrpctypes.marshall_int32(#data) - --arguments = arguments .. msrpctypes.marshall_int32(#data) - arguments = arguments .. data - if data_padding then arguments = arguments .. data_padding end - arguments = arguments .. msrpctypes.marshall_int32(#data) - local status,result = call_function(smbstate, 19, arguments) - if not status then - stdnse.print_debug("MSRPC spoolss_write_printer(): %s",result) - end - return status,result + stdnse.print_debug("len %d", #data) + local padding_len = 4 - math.fmod(#data,4) + local data_padding = nil + if not (padding_len == 4) then + data_padding = string.rep(bin.pack("H","00"),padding_len) + end + local arguments = printer_handle .. msrpctypes.marshall_int32(#data) + --arguments = arguments .. msrpctypes.marshall_int32(#data) + arguments = arguments .. data + if data_padding then arguments = arguments .. data_padding end + arguments = arguments .. msrpctypes.marshall_int32(#data) + local status,result = call_function(smbstate, 19, arguments) + if not status then + stdnse.print_debug("MSRPC spoolss_write_printer(): %s",result) + end + return status,result end ---Call the EndDocPrinter() function whose opnum is 23. -- -- http://msdn.microsoft.com/en-us/library/cc244783%28v=prot.10%29 ---@param smbstate The SMB state table +--@param smbstate The SMB state table --@param printer_handle Printer handle returned by spoolss_open_printer() --@return (status, result) If status is false, result is an error message. function spoolss_end_doc_printer(smbstate,printer_handle) - local status,result = call_function(smbstate,23,printer_handle) - if not status then - stdnse.print_debug("MSRPC spoolss_end_doc_printer(): %s",result) - end - return status,result + local status,result = call_function(smbstate,23,printer_handle) + if not status then + stdnse.print_debug("MSRPC spoolss_end_doc_printer(): %s",result) + end + return status,result end ---Call the RpcAbortPrinter() function whose opnum is 21. -- -- http://msdn.microsoft.com/en-us/library/cc244757%28v=prot.13%29 ---@param smbstate The SMB state table +--@param smbstate The SMB state table --@param printer_handle Printer handle returned by spoolss_open_printer() --@return (status, result) If status is false, result is an error message. function spoolss_abort_printer(smbstate,printer_handle) - local status,result = call_function(smbstate,21,printer_handle) - if not status then - stdnse.print_debug("MSRPC spoolss_abort_printer(): %s",result) - end - return status,result + local status,result = call_function(smbstate,21,printer_handle) + if not status then + stdnse.print_debug("MSRPC spoolss_abort_printer(): %s",result) + end + return status,result end ---Helper function to convert binary UUID representation to usual string. -- ---@param uuid UUID byte string +--@param uuid UUID byte string --@return UUID converted to string representation function uuid_to_string(uuid) - local pos, i1,s1,s2,c1,c2,c3,c4,c5,c6,c7,c8 = bin.unpack(" 1 then - lookup_response.annotation = string.sub(data,pos,pos+annotation_length-2) - end - local padding = (4-(annotation_length%4)) - if padding == 4 then padding = 0 end - pos = pos + annotation_length + padding - --skip lengths - pos = pos + 8 - local num_floors,floor_len,uuid, address_type,address_len,tcp_port,udp_port,ip_addr,saved_pos,ncalrpc,ncacn_np,netbios,ncacn_http - pos, num_floors = bin.unpack(" 1 then + lookup_response.annotation = string.sub(data,pos,pos+annotation_length-2) + end + local padding = (4-(annotation_length%4)) + if padding == 4 then padding = 0 end + pos = pos + annotation_length + padding + --skip lengths + pos = pos + 8 + local num_floors,floor_len,uuid, address_type,address_len,tcp_port,udp_port,ip_addr,saved_pos,ncalrpc,ncacn_np,netbios,ncacn_http + pos, num_floors = bin.unpack("S",data,pos) - elseif address_type == 0x08 then - pos,lookup_response.udp_port = bin.unpack(">S",data,pos) - elseif address_type == 0x09 then - local i1,i2,i3,i4 - pos,i1,i2,i3,i4 = bin.unpack("CCCC",data,pos) - lookup_response.ip_addr = string.format("%d.%d.%d.%d",i1,i2,i3,i4) - elseif address_type == 0x0f then - lookup_response.ncacn_np = string.sub(data,pos,pos+address_len-2) - floor_len = floor_len + address_len - 2 - elseif address_type == 0x10 then - lookup_response.ncalrpc = string.sub(data,pos,pos+address_len-2) - floor_len = floor_len + address_len - 2 - elseif address_type == 0x11 then - lookup_response.netbios = string.sub(data,pos,pos+address_len-2) - floor_len = floor_len + address_len - 2 - elseif address_type == 0x1f then - pos, lookup_response.ncacn_http = bin.unpack(">S",data,pos) - else - stdnse.print_debug("unknown address type %x",address_type) - end - end - end - pos = saved_pos + floor_len + 6 - end - return status,lookup_response + if i == 1 then + uuid = string.sub(data,pos+1,pos+16) + lookup_response.uuid = uuid_to_string(uuid) + lookup_response.exe = string_uuid_to_exe(lookup_response.uuid) + else + if not (i == 2) and not (i == 3) then -- just skip floor 2 and 3 + pos,address_type,address_len = bin.unpack("S",data,pos) + elseif address_type == 0x08 then + pos,lookup_response.udp_port = bin.unpack(">S",data,pos) + elseif address_type == 0x09 then + local i1,i2,i3,i4 + pos,i1,i2,i3,i4 = bin.unpack("CCCC",data,pos) + lookup_response.ip_addr = string.format("%d.%d.%d.%d",i1,i2,i3,i4) + elseif address_type == 0x0f then + lookup_response.ncacn_np = string.sub(data,pos,pos+address_len-2) + floor_len = floor_len + address_len - 2 + elseif address_type == 0x10 then + lookup_response.ncalrpc = string.sub(data,pos,pos+address_len-2) + floor_len = floor_len + address_len - 2 + elseif address_type == 0x11 then + lookup_response.netbios = string.sub(data,pos,pos+address_len-2) + floor_len = floor_len + address_len - 2 + elseif address_type == 0x1f then + pos, lookup_response.ncacn_http = bin.unpack(">S",data,pos) + else + stdnse.print_debug("unknown address type %x",address_type) + end + end + end + pos = saved_pos + floor_len + 6 + end + return status,lookup_response end ---A proxy to a msrpctypes function that converts a PasswordProperties to an english string. @@ -1288,7 +1288,7 @@ end --@param val The value to convert. --@return A string that can be displayed to the user. function samr_PasswordProperties_tostr(val) - return msrpctypes.samr_PasswordProperties_tostr(val) + return msrpctypes.samr_PasswordProperties_tostr(val) end ---A proxy to a msrpctypes function that converts a AcctFlags to an english string. @@ -1298,7 +1298,7 @@ end --@param val The value to convert. --@return A string that can be displayed to the user. function samr_AcctFlags_tostr(val) - return msrpctypes.samr_AcctFlags_tostr(val) + return msrpctypes.samr_AcctFlags_tostr(val) end ---Call the connect4 function, to obtain a "connect handle". This must be done before calling many @@ -1309,50 +1309,50 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'connect_handle', which is required to call other functions. function samr_connect4(smbstate, server) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling Connect4() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling Connect4() [%s]", smbstate['ip']) --- [in,string,charset(UTF16)] uint16 *system_name, - arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) + -- [in,string,charset(UTF16)] uint16 *system_name, + arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. server, true) --- [in] uint32 unknown, - arguments = arguments .. msrpctypes.marshall_int32(0x02) + -- [in] uint32 unknown, + arguments = arguments .. msrpctypes.marshall_int32(0x02) --- [in] samr_ConnectAccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_samr_ConnectAccessMask("SAMR_ACCESS_ENUM_DOMAINS|SAMR_ACCESS_OPEN_DOMAIN") --- [out,ref] policy_handle *connect_handle + -- [in] samr_ConnectAccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_samr_ConnectAccessMask("SAMR_ACCESS_ENUM_DOMAINS|SAMR_ACCESS_OPEN_DOMAIN") + -- [out,ref] policy_handle *connect_handle - -- Do the call - status, result = call_function(smbstate, 0x3E, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x3E, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: Connect4() returned successfully") + stdnse.print_debug(3, "MSRPC: Connect4() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 --- [in,string,charset(UTF16)] uint16 *system_name, --- [in] uint32 unknown, --- [in] samr_ConnectAccessMask access_mask, --- [out,ref] policy_handle *connect_handle - pos, result['connect_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 + -- [in,string,charset(UTF16)] uint16 *system_name, + -- [in] uint32 unknown, + -- [in] samr_ConnectAccessMask access_mask, + -- [out,ref] policy_handle *connect_handle + pos, result['connect_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.connect4)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.connect4)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.connect4)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.connect4)" + end - return true, result + return true, result end ---Call the enumdomains function, which returns a list of all domains in use by the system. @@ -1362,58 +1362,58 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'domains', which is a list of the domains. function samr_enumdomains(smbstate, connect_handle) - local i, j - local status, result - local arguments - local result - local pos, align + local i, j + local status, result + local arguments + local result + local pos, align - stdnse.print_debug(2, "MSRPC: Calling EnumDomains() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling EnumDomains() [%s]", smbstate['ip']) --- [in,ref] policy_handle *connect_handle, - arguments = msrpctypes.marshall_policy_handle(connect_handle) + -- [in,ref] policy_handle *connect_handle, + arguments = msrpctypes.marshall_policy_handle(connect_handle) --- [in,out,ref] uint32 *resume_handle, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in,out,ref] uint32 *resume_handle, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] uint32 buf_size, - arguments = arguments .. msrpctypes.marshall_int32(0x2000) + -- [in] uint32 buf_size, + arguments = arguments .. msrpctypes.marshall_int32(0x2000) --- [out] samr_SamArray *sam, --- [out] uint32 num_entries + -- [out] samr_SamArray *sam, + -- [out] uint32 num_entries - -- Do the call - status, result = call_function(smbstate, 0x06, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x06, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: EnumDomains() returned successfully") + stdnse.print_debug(3, "MSRPC: EnumDomains() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] + -- Make arguments easier to use + arguments = result['arguments'] --- [in,ref] policy_handle *connect_handle, --- [in,out,ref] uint32 *resume_handle, - pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in,ref] policy_handle *connect_handle, + -- [in,out,ref] uint32 *resume_handle, + pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in] uint32 buf_size, --- [out] samr_SamArray *sam, - pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) + -- [in] uint32 buf_size, + -- [out] samr_SamArray *sam, + pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) --- [out] uint32 num_entries - pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out] uint32 num_entries + pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.enumdomains)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.enumdomains)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.enumdomains)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.enumdomains)" + end - return true, result + return true, result end ---Call the LookupDomain function, which converts a domain's name into its sid, which is @@ -1425,49 +1425,49 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'sid', which is required to call other functions. function samr_lookupdomain(smbstate, connect_handle, domain) - local i, j - local status, result - local arguments - local pos, align - local referent_id + local i, j + local status, result + local arguments + local pos, align + local referent_id - stdnse.print_debug(2, "MSRPC: Calling LookupDomain(%s) [%s]", domain, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling LookupDomain(%s) [%s]", domain, smbstate['ip']) --- [in,ref] policy_handle *connect_handle, - arguments = msrpctypes.marshall_policy_handle(connect_handle) + -- [in,ref] policy_handle *connect_handle, + arguments = msrpctypes.marshall_policy_handle(connect_handle) --- [in,ref] lsa_String *domain_name, - arguments = arguments .. msrpctypes.marshall_lsa_String(domain) + -- [in,ref] lsa_String *domain_name, + arguments = arguments .. msrpctypes.marshall_lsa_String(domain) --- [out] dom_sid2 *sid + -- [out] dom_sid2 *sid - -- Do the call - status, result = call_function(smbstate, 0x05, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x05, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: LookupDomain() returned successfully") + stdnse.print_debug(3, "MSRPC: LookupDomain() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] + -- Make arguments easier to use + arguments = result['arguments'] --- [in,ref] policy_handle *connect_handle, --- [in,ref] lsa_String *domain_name, --- [out] dom_sid2 *sid - pos, result['sid'] = msrpctypes.unmarshall_dom_sid2_ptr(arguments, pos) + -- [in,ref] policy_handle *connect_handle, + -- [in,ref] lsa_String *domain_name, + -- [out] dom_sid2 *sid + pos, result['sid'] = msrpctypes.unmarshall_dom_sid2_ptr(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.lookupdomain)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.lookupdomain)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.lookupdomain)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.lookupdomain)" + end - return true, result + return true, result end ---Call OpenDomain, which returns a handle to the domain identified by the given sid. @@ -1479,52 +1479,52 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'domain_handle', which is used to call other functions. function samr_opendomain(smbstate, connect_handle, sid) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenDomain(%s) [%s]", sid, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenDomain(%s) [%s]", sid, smbstate['ip']) --- [in,ref] policy_handle *connect_handle, - arguments = msrpctypes.marshall_policy_handle(connect_handle) + -- [in,ref] policy_handle *connect_handle, + arguments = msrpctypes.marshall_policy_handle(connect_handle) --- [in] samr_DomainAccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_samr_DomainAccessMask("DOMAIN_ACCESS_LOOKUP_INFO_1|DOMAIN_ACCESS_LOOKUP_INFO_2|DOMAIN_ACCESS_ENUM_ACCOUNTS|DOMAIN_ACCESS_OPEN_ACCOUNT") + -- [in] samr_DomainAccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_samr_DomainAccessMask("DOMAIN_ACCESS_LOOKUP_INFO_1|DOMAIN_ACCESS_LOOKUP_INFO_2|DOMAIN_ACCESS_ENUM_ACCOUNTS|DOMAIN_ACCESS_OPEN_ACCOUNT") --- [in,ref] dom_sid2 *sid, - arguments = arguments .. msrpctypes.marshall_dom_sid2(sid) + -- [in,ref] dom_sid2 *sid, + arguments = arguments .. msrpctypes.marshall_dom_sid2(sid) --- [out,ref] policy_handle *domain_handle + -- [out,ref] policy_handle *domain_handle - -- Do the call - status, result = call_function(smbstate, 0x07, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x07, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenDomain() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenDomain() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *connect_handle, --- [in] samr_DomainAccessMask access_mask, --- [in,ref] dom_sid2 *sid, --- [out,ref] policy_handle *domain_handle - pos, result['domain_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,ref] policy_handle *connect_handle, + -- [in] samr_DomainAccessMask access_mask, + -- [in,ref] dom_sid2 *sid, + -- [out,ref] policy_handle *domain_handle + pos, result['domain_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.opendomain)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.opendomain)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.opendomain)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.opendomain)" + end - return true, result + return true, result end ---Call EnumDomainUsers, which returns a list of users only. To get more information about the users, the @@ -1535,62 +1535,62 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'names', which is a list of usernames in that domain. function samr_enumdomainusers(smbstate, domain_handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling EnumDomainUsers() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling EnumDomainUsers() [%s]", smbstate['ip']) --- [in,ref] policy_handle *domain_handle, - arguments = msrpctypes.marshall_policy_handle(domain_handle) + -- [in,ref] policy_handle *domain_handle, + arguments = msrpctypes.marshall_policy_handle(domain_handle) --- [in,out,ref] uint32 *resume_handle, - arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) + -- [in,out,ref] uint32 *resume_handle, + arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) --- [in] samr_AcctFlags acct_flags, - arguments = arguments .. msrpctypes.marshall_samr_AcctFlags("ACB_NONE") + -- [in] samr_AcctFlags acct_flags, + arguments = arguments .. msrpctypes.marshall_samr_AcctFlags("ACB_NONE") --- [in] uint32 max_size, - arguments = arguments .. msrpctypes.marshall_int32(0x0400) + -- [in] uint32 max_size, + arguments = arguments .. msrpctypes.marshall_int32(0x0400) --- [out] samr_SamArray *sam, --- [out] uint32 num_entries + -- [out] samr_SamArray *sam, + -- [out] uint32 num_entries - -- Do the call - status, result = call_function(smbstate, 0x0d, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0d, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: EnumDomainUsers() returned successfully") + stdnse.print_debug(3, "MSRPC: EnumDomainUsers() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *domain_handle, --- [in,out,ref] uint32 *resume_handle, - pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in,ref] policy_handle *domain_handle, + -- [in,out,ref] uint32 *resume_handle, + pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in] samr_AcctFlags acct_flags, --- [in] uint32 max_size, --- [out] samr_SamArray *sam, - pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) + -- [in] samr_AcctFlags acct_flags, + -- [in] uint32 max_size, + -- [out] samr_SamArray *sam, + pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) --- [out] uint32 num_entries - pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out] uint32 num_entries + pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.enumdomainusers)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.enumdomainusers)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.enumdomainusers)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.enumdomainusers)" + end - return true, result + return true, result end @@ -1610,77 +1610,77 @@ end -- 'name', 'fullname', and 'description' (note that any of them can be nil if the server didn't return a value). Finally, -- 'flags' is the numeric flags for the user, while 'flags_list' is an array of strings, representing the flags. function samr_querydisplayinfo(smbstate, domain_handle, index, count) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - if(count == nil) then - count = 1 - end + if(count == nil) then + count = 1 + end - -- This loop is because, in my testing, if I asked for all the results at once, it would blow up (ERR_BUFFER_OVERFLOW). So, instead, - -- I put a little loop here and grab the names individually. - stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip']) + -- This loop is because, in my testing, if I asked for all the results at once, it would blow up (ERR_BUFFER_OVERFLOW). So, instead, + -- I put a little loop here and grab the names individually. + stdnse.print_debug(2, "MSRPC: Calling QueryDisplayInfo(%d) [%s]", index, smbstate['ip']) --- [in,ref] policy_handle *domain_handle, - arguments = msrpctypes.marshall_policy_handle(domain_handle) + -- [in,ref] policy_handle *domain_handle, + arguments = msrpctypes.marshall_policy_handle(domain_handle) --- [in] uint16 level, - arguments = arguments .. msrpctypes.marshall_int16(1) -- Level (1 = users, 3 = groups, 4 = usernames only) + -- [in] uint16 level, + arguments = arguments .. msrpctypes.marshall_int16(1) -- Level (1 = users, 3 = groups, 4 = usernames only) --- [in] uint32 start_idx, - arguments = arguments .. msrpctypes.marshall_int32(index) + -- [in] uint32 start_idx, + arguments = arguments .. msrpctypes.marshall_int32(index) --- [in] uint32 max_entries, - arguments = arguments .. msrpctypes.marshall_int32(count) + -- [in] uint32 max_entries, + arguments = arguments .. msrpctypes.marshall_int32(count) --- [in] uint32 buf_size, - arguments = arguments .. msrpctypes.marshall_int32(0x7FFFFFFF) + -- [in] uint32 buf_size, + arguments = arguments .. msrpctypes.marshall_int32(0x7FFFFFFF) --- [out] uint32 total_size, --- [out] uint32 returned_size, --- [out,switch_is(level)] samr_DispInfo info + -- [out] uint32 total_size, + -- [out] uint32 returned_size, + -- [out,switch_is(level)] samr_DispInfo info - -- Do the call - status, result = call_function(smbstate, 0x28, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x28, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: QueryDisplayInfo() returned successfully", i) + stdnse.print_debug(3, "MSRPC: QueryDisplayInfo() returned successfully", i) - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *domain_handle, --- [in] uint16 level, --- [in] uint32 start_idx, --- [in] uint32 max_entries, --- [in] uint32 buf_size, --- [out] uint32 total_size, - pos, result['total_size'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in,ref] policy_handle *domain_handle, + -- [in] uint16 level, + -- [in] uint32 start_idx, + -- [in] uint32 max_entries, + -- [in] uint32 buf_size, + -- [out] uint32 total_size, + pos, result['total_size'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out] uint32 returned_size, - pos, result['returned_size'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,switch_is(level)] samr_DispInfo info - pos, result['info'] = msrpctypes.unmarshall_samr_DispInfo(arguments, pos) - if(pos == nil) then - return false, "SMB: An error occurred while calling unmarshall_samr_DispInfo" - end + -- [out] uint32 returned_size, + pos, result['returned_size'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,switch_is(level)] samr_DispInfo info + pos, result['info'] = msrpctypes.unmarshall_samr_DispInfo(arguments, pos) + if(pos == nil) then + return false, "SMB: An error occurred while calling unmarshall_samr_DispInfo" + end - -- Get the return value - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.querydisplayall)" - end - if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_MORE_ENTRIES']) then - return false, smb.get_status_name(result['return']) .. " (samr.querydisplayinfo)" - end + -- Get the return value + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.querydisplayall)" + end + if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_MORE_ENTRIES']) then + return false, smb.get_status_name(result['return']) .. " (samr.querydisplayinfo)" + end - return true, result + return true, result end ---Call QueryDomainInfo2, which grabs various data about a domain. @@ -1706,50 +1706,50 @@ end -- 'lockout_window' (in minutes) -- 'lockout_threshold' (in attempts) function samr_querydomaininfo2(smbstate, domain_handle, level) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling QueryDomainInfo2(%d) [%s]", level, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling QueryDomainInfo2(%d) [%s]", level, smbstate['ip']) --- [in,ref] policy_handle *domain_handle, - arguments = msrpctypes.marshall_policy_handle(domain_handle) + -- [in,ref] policy_handle *domain_handle, + arguments = msrpctypes.marshall_policy_handle(domain_handle) --- [in] uint16 level, - arguments = arguments .. msrpctypes.marshall_int32(level) + -- [in] uint16 level, + arguments = arguments .. msrpctypes.marshall_int32(level) --- [out,switch_is(level)] samr_DomainInfo *info + -- [out,switch_is(level)] samr_DomainInfo *info - -- Do the call - status, result = call_function(smbstate, 0x2e, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x2e, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: QueryDomainInfo2() returned successfully") + stdnse.print_debug(3, "MSRPC: QueryDomainInfo2() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *domain_handle, --- [in] uint16 level, --- [out,switch_is(level)] samr_DomainInfo *info - pos, result['info'] = msrpctypes.unmarshall_samr_DomainInfo_ptr(arguments, pos) - if(pos == nil) then - return false, "unmarshall_samr_DomainInfo_ptr() returned an error" - end + -- [in,ref] policy_handle *domain_handle, + -- [in] uint16 level, + -- [out,switch_is(level)] samr_DomainInfo *info + pos, result['info'] = msrpctypes.unmarshall_samr_DomainInfo_ptr(arguments, pos) + if(pos == nil) then + return false, "unmarshall_samr_DomainInfo_ptr() returned an error" + end - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.querydomaininfo2)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.querydomaininfo2)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.querydomaininfo2)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.querydomaininfo2)" + end - return true, result + return true, result end ---Call the EnumDomainAliases function, which retrieves a list of groups for a given domain @@ -1758,56 +1758,56 @@ end --@param domain_handle The domain_handle, returned by samr_opendomain --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_enumdomainaliases(smbstate, domain_handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - arguments = '' + arguments = '' --- [in] policy_handle *domain_handle, - arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) + -- [in] policy_handle *domain_handle, + arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) --- [in,out,ref] uint32 *resume_handle, - arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) + -- [in,out,ref] uint32 *resume_handle, + arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) --- [out,ref] samr_SamArray **sam, --- [in] uint32 max_size, (note: Wireshark says this is flags. Either way..) - arguments = arguments .. msrpctypes.marshall_int32(0x400) + -- [out,ref] samr_SamArray **sam, + -- [in] uint32 max_size, (note: Wireshark says this is flags. Either way..) + arguments = arguments .. msrpctypes.marshall_int32(0x400) --- [out,ref] uint32 *num_entries + -- [out,ref] uint32 *num_entries - -- Do the call - status, result = call_function(smbstate, 0x0f, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0f, arguments) + if(status ~= true) then + return false, result + end - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] policy_handle *domain_handle, --- [in,out,ref] uint32 *resume_handle, - pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in] policy_handle *domain_handle, + -- [in,out,ref] uint32 *resume_handle, + pos, result['resume_handle'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] samr_SamArray **sam, - pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) + -- [out,ref] samr_SamArray **sam, + pos, result['sam'] = msrpctypes.unmarshall_samr_SamArray_ptr(arguments, pos) --- [in] uint32 max_size, --- [out,ref] uint32 *num_entries - pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in] uint32 max_size, + -- [out,ref] uint32 *num_entries + pos, result['num_entries'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.enumdomainaliases)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.enumdomainaliases)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.enumdomainaliases)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.enumdomainaliases)" + end - return true, result + return true, result end ---Call the EnumDomainAliases function, which retrieves a list of groups for a given domain @@ -1816,58 +1816,58 @@ end --@param domain_handle The domain_handle, returned by samr_opendomain --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_lookupnames(smbstate, domain_handle, names) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - arguments = '' + arguments = '' --- [in,ref] policy_handle *domain_handle, - arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) + -- [in,ref] policy_handle *domain_handle, + arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) --- [in,range(0,1000)] uint32 num_names, - arguments = arguments .. msrpctypes.marshall_int32(#names) + -- [in,range(0,1000)] uint32 num_names, + arguments = arguments .. msrpctypes.marshall_int32(#names) --- [in,size_is(1000),length_is(num_names)] lsa_String names[], - arguments = arguments .. msrpctypes.marshall_lsa_String_array2(names) + -- [in,size_is(1000),length_is(num_names)] lsa_String names[], + arguments = arguments .. msrpctypes.marshall_lsa_String_array2(names) --- [out,ref] samr_Ids *rids, --- [out,ref] samr_Ids *types + -- [out,ref] samr_Ids *rids, + -- [out,ref] samr_Ids *types - -- Do the call - status, result = call_function(smbstate, 0x11, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x11, arguments) + if(status ~= true) then + return false, result + end - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *domain_handle, --- [in,range(0,1000)] uint32 num_names, --- [in,size_is(1000),length_is(num_names)] lsa_String names[], --- [out,ref] samr_Ids *rids, - pos, result['rids'] = msrpctypes.unmarshall_samr_Ids(arguments, pos) + -- [in,ref] policy_handle *domain_handle, + -- [in,range(0,1000)] uint32 num_names, + -- [in,size_is(1000),length_is(num_names)] lsa_String names[], + -- [out,ref] samr_Ids *rids, + pos, result['rids'] = msrpctypes.unmarshall_samr_Ids(arguments, pos) --- [out,ref] samr_Ids *types - pos, result['types'] = msrpctypes.unmarshall_samr_Ids(arguments, pos) + -- [out,ref] samr_Ids *types + pos, result['types'] = msrpctypes.unmarshall_samr_Ids(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.lookupnames)" - end - if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then - return false, "Couldn't find any names the host recognized" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.lookupnames)" + end + if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then + return false, "Couldn't find any names the host recognized" + end - if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then - return false, smb.get_status_name(result['return']) .. " (samr.lookupnames)" - end + if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then + return false, smb.get_status_name(result['return']) .. " (samr.lookupnames)" + end - return true, result + return true, result end ---Call the OpenAlias function, which gets a handle to a group. @@ -1877,50 +1877,50 @@ end --@param rid The RID of the alias --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_openalias(smbstate, domain_handle, rid) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - arguments = '' + arguments = '' --- [in,ref] policy_handle *domain_handle, - arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) + -- [in,ref] policy_handle *domain_handle, + arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) --- [in] samr_AliasAccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_int32(0x0002000c) -- Full read permission + -- [in] samr_AliasAccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_int32(0x0002000c) -- Full read permission --- [in] uint32 rid, - arguments = arguments .. msrpctypes.marshall_int32(rid) + -- [in] uint32 rid, + arguments = arguments .. msrpctypes.marshall_int32(rid) --- [out,ref] policy_handle *alias_handle + -- [out,ref] policy_handle *alias_handle - -- Do the call - status, result = call_function(smbstate, 0x1b, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x1b, arguments) + if(status ~= true) then + return false, result + end - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *domain_handle, --- [in] samr_AliasAccessMask access_mask, --- [in] uint32 rid, --- [out,ref] policy_handle *alias_handle - pos, result['alias_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,ref] policy_handle *domain_handle, + -- [in] samr_AliasAccessMask access_mask, + -- [in] uint32 rid, + -- [out,ref] policy_handle *alias_handle + pos, result['alias_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.openalias)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.openalias)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.openalias)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.openalias)" + end - return true, result + return true, result end ---Call the GetAliasMembership function. @@ -1928,22 +1928,22 @@ end -- --@param smbstate The SMB state table --@param alias_handle The alias_handle, already marshaled ---@param args Actuall data to send, already marshaled +--@param args Actuall data to send, already marshaled --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_getaliasmembership(smbstate, alias_handle,args) - local status, result - local arguments + local status, result + local arguments - arguments = '' + arguments = '' - arguments = arguments .. alias_handle .. args - -- Do the call - status, result = call_function(smbstate, 0x10, arguments) - if(status ~= true) then - return false, result - end + arguments = arguments .. alias_handle .. args + -- Do the call + status, result = call_function(smbstate, 0x10, arguments) + if(status ~= true) then + return false, result + end - return true, result + return true, result end ---Call the GetMembersInAlias function, which retrieves a list of users in @@ -1953,41 +1953,41 @@ end --@param alias_handle The alias_handle, returned by samr_openalias --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. function samr_getmembersinalias(smbstate, alias_handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - arguments = '' + arguments = '' --- [in,ref] policy_handle *alias_handle, - arguments = arguments .. msrpctypes.marshall_policy_handle(alias_handle) --- [out,ref] lsa_SidArray *sids + -- [in,ref] policy_handle *alias_handle, + arguments = arguments .. msrpctypes.marshall_policy_handle(alias_handle) + -- [out,ref] lsa_SidArray *sids - -- Do the call - status, result = call_function(smbstate, 0x21, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x21, arguments) + if(status ~= true) then + return false, result + end - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *alias_handle, --- [out,ref] lsa_SidArray *sids - pos, result['sids'] = msrpctypes.unmarshall_lsa_SidArray(arguments, pos) + -- [in,ref] policy_handle *alias_handle, + -- [out,ref] lsa_SidArray *sids + pos, result['sids'] = msrpctypes.unmarshall_lsa_SidArray(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.getmembersinalias)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.getmembersinalias)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)" + end - return true, result + return true, result end -- Call the LookupRids function, which converts a list of RIDs to @@ -2001,32 +2001,32 @@ end --@param rids An array of RIDs to look up --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values. --function samr_lookuprids(smbstate, domain_handle, rids) --- local i, j --- local status, result --- local arguments --- local pos, align +-- local i, j +-- local status, result +-- local arguments +-- local pos, align -- --- arguments = '' +-- arguments = '' -- ---- [in,ref] policy_handle *domain_handle, --- arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) +-- arguments = arguments .. msrpctypes.marshall_policy_handle(domain_handle) ---- [in,range(0,1000)] uint32 num_rids, --- arguments = arguments .. msrpctypes.marshall_int32(#rids) +-- arguments = arguments .. msrpctypes.marshall_int32(#rids) ---- [in,size_is(1000),length_is(num_rids)] uint32 rids[], --- arguments = arguments .. msrpctypes.marshall_int32_array(rids) +-- arguments = arguments .. msrpctypes.marshall_int32_array(rids) ---- [out,ref] lsa_Strings *names, ---- [out,ref] samr_Ids *types -- -- --- -- Do the call --- status, result = call_function(smbstate, 0x12, arguments) --- if(status ~= true) then --- return false, result --- end +-- -- Do the call +-- status, result = call_function(smbstate, 0x12, arguments) +-- if(status ~= true) then +-- return false, result +-- end -- --- -- Make arguments easier to use --- arguments = result['arguments'] --- pos = 1 +-- -- Make arguments easier to use +-- arguments = result['arguments'] +-- pos = 1 -- ---- [in,ref] policy_handle *domain_handle, ---- [in,range(0,1000)] uint32 num_rids, @@ -2035,16 +2035,16 @@ end ---- [out,ref] samr_Ids *types -- -- --- pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) +-- pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) --stdnse.print_debug("Return = %08x\n", result['return']) --- if(result['return'] == nil) then --- return false, "Read off the end of the packet (samr.getmembersinalias)" --- end --- if(result['return'] ~= 0) then --- return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)" --- end +-- if(result['return'] == nil) then +-- return false, "Read off the end of the packet (samr.getmembersinalias)" +-- end +-- if(result['return'] ~= 0) then +-- return false, smb.get_status_name(result['return']) .. " (samr.getmembersinalias)" +-- end -- --- return true, result +-- return true, result --end @@ -2055,41 +2055,41 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is potentially -- a table of values, none of which are likely to be used. function samr_close(smbstate, handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling Close() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling Close() [%s]", smbstate['ip']) --- [in,out,ref] policy_handle *handle - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,out,ref] policy_handle *handle + arguments = msrpctypes.marshall_policy_handle(handle) - -- Do the call - status, result = call_function(smbstate, 0x01, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x01, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: Close() returned successfully") + stdnse.print_debug(3, "MSRPC: Close() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (samr.close)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (samr.close)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (samr.close)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (samr.close)" + end - return true, result + return true, result end ---Call the LsarOpenPolicy2 function, to obtain a "policy handle". This must be done before calling many @@ -2100,51 +2100,51 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'policy_handle', which is required to call other functions. function lsa_openpolicy2(smbstate, server) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling LsarOpenPolicy2() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling LsarOpenPolicy2() [%s]", smbstate['ip']) --- [in,unique] [string,charset(UTF16)] uint16 *system_name, - arguments = msrpctypes.marshall_unicode_ptr(server, true) + -- [in,unique] [string,charset(UTF16)] uint16 *system_name, + arguments = msrpctypes.marshall_unicode_ptr(server, true) --- [in] lsa_ObjectAttribute *attr, - arguments = arguments .. msrpctypes.marshall_lsa_ObjectAttribute() + -- [in] lsa_ObjectAttribute *attr, + arguments = arguments .. msrpctypes.marshall_lsa_ObjectAttribute() --- [in] uint32 access_mask, - arguments = arguments .. msrpctypes.marshall_int32(0x00000800) + -- [in] uint32 access_mask, + arguments = arguments .. msrpctypes.marshall_int32(0x00000800) --- [out] policy_handle *handle + -- [out] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x2C, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x2C, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: LsarOpenPolicy2() returned successfully") + stdnse.print_debug(3, "MSRPC: LsarOpenPolicy2() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,unique] [string,charset(UTF16)] uint16 *system_name, --- [in] lsa_ObjectAttribute *attr, --- [in] uint32 access_mask, --- [out] policy_handle *handle - pos, result['policy_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in,unique] [string,charset(UTF16)] uint16 *system_name, + -- [in] lsa_ObjectAttribute *attr, + -- [in] uint32 access_mask, + -- [out] policy_handle *handle + pos, result['policy_handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (lsa.openpolicy2)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (lsa.openpolicy2)" - end + if(result['return'] == nil) then + return false, "Read off the end of the packet (lsa.openpolicy2)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (lsa.openpolicy2)" + end - return true, result + return true, result end ---Call the LsarLookupNames2 function, to convert the server's name into a sid. @@ -2157,85 +2157,85 @@ end -- domains, there is a 'name' entry, which is a string, and a 'sid' entry, which is yet another object which -- can be passed to functions that understand SIDs. function lsa_lookupnames2(smbstate, policy_handle, names) - local i, j - local status, result - local arguments - local result - local pos, align + local i, j + local status, result + local arguments + local result + local pos, align - stdnse.print_debug(2, "MSRPC: Calling LsarLookupNames2(%s) [%s]", stdnse.strjoin(", ", names), smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling LsarLookupNames2(%s) [%s]", stdnse.strjoin(", ", names), smbstate['ip']) --- [in] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(policy_handle) + -- [in] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(policy_handle) --- [in,range(0,1000)] uint32 num_names, - arguments = arguments .. msrpctypes.marshall_int32(#names) + -- [in,range(0,1000)] uint32 num_names, + arguments = arguments .. msrpctypes.marshall_int32(#names) --- [in,size_is(num_names)] lsa_String names[], - arguments = arguments .. msrpctypes.marshall_lsa_String_array(names) + -- [in,size_is(num_names)] lsa_String names[], + arguments = arguments .. msrpctypes.marshall_lsa_String_array(names) --- [out,unique] lsa_RefDomainList *domains, --- [in,out] lsa_TransSidArray2 *sids, - arguments = arguments .. msrpctypes.marshall_lsa_TransSidArray2({nil}) + -- [out,unique] lsa_RefDomainList *domains, + -- [in,out] lsa_TransSidArray2 *sids, + arguments = arguments .. msrpctypes.marshall_lsa_TransSidArray2({nil}) --- [in] lsa_LookupNamesLevel level, - arguments = arguments .. msrpctypes.marshall_lsa_LookupNamesLevel("LOOKUP_NAMES_ALL") + -- [in] lsa_LookupNamesLevel level, + arguments = arguments .. msrpctypes.marshall_lsa_LookupNamesLevel("LOOKUP_NAMES_ALL") --- [in,out] uint32 *count, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in,out] uint32 *count, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] uint32 unknown1, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 unknown1, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] uint32 unknown2 - arguments = arguments .. msrpctypes.marshall_int32(2) + -- [in] uint32 unknown2 + arguments = arguments .. msrpctypes.marshall_int32(2) - -- Do the call - status, result = call_function(smbstate, 0x3a, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x3a, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: LsarLookupNames2() returned successfully") + stdnse.print_debug(3, "MSRPC: LsarLookupNames2() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] policy_handle *handle, --- [in,range(0,1000)] uint32 num_names, --- [in,size_is(num_names)] lsa_String names[], --- [out,unique] lsa_RefDomainList *domains, - pos, result['domains'] = msrpctypes.unmarshall_lsa_RefDomainList_ptr(arguments, pos) + -- [in] policy_handle *handle, + -- [in,range(0,1000)] uint32 num_names, + -- [in,size_is(num_names)] lsa_String names[], + -- [out,unique] lsa_RefDomainList *domains, + pos, result['domains'] = msrpctypes.unmarshall_lsa_RefDomainList_ptr(arguments, pos) --- [in,out] lsa_TransSidArray2 *rids, - pos, result['rids'] = msrpctypes.unmarshall_lsa_TransSidArray2(arguments, pos) + -- [in,out] lsa_TransSidArray2 *rids, + pos, result['rids'] = msrpctypes.unmarshall_lsa_TransSidArray2(arguments, pos) --- [in] lsa_LookupNamesLevel level, --- [in,out] uint32 *count, - pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in] lsa_LookupNamesLevel level, + -- [in,out] uint32 *count, + pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in] uint32 unknown1, --- [in] uint32 unknown2 + -- [in] uint32 unknown1, + -- [in] uint32 unknown2 - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (lsa.lookupnames2)" - end - if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then - return false, "Couldn't find any names the host recognized" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (lsa.lookupnames2)" + end + if(result['return'] == smb.status_codes['NT_STATUS_NONE_MAPPED']) then + return false, "Couldn't find any names the host recognized" + end - if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then - return false, smb.get_status_name(result['return']) .. " (lsa.lookupnames2)" - end + if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED']) then + return false, smb.get_status_name(result['return']) .. " (lsa.lookupnames2)" + end - return true, result + return true, result end ---Call the LsarLookupSids2 function, to convert a list of SIDs to their names @@ -2249,72 +2249,72 @@ end -- a table containing more information about each name, even if the name wasn't found (this one is a 1:1 mapping -- with the RIDs). function lsa_lookupsids2(smbstate, policy_handle, sids) - local i, j - local status, result - local arguments - local result - local pos, align + local i, j + local status, result + local arguments + local result + local pos, align - stdnse.print_debug(2, "MSRPC: Calling LsarLookupSids2(%s) [%s]", stdnse.strjoin(", ", sids), smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling LsarLookupSids2(%s) [%s]", stdnse.strjoin(", ", sids), smbstate['ip']) --- [in] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(policy_handle) + -- [in] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(policy_handle) --- [in] lsa_SidArray *sids, - arguments = arguments .. msrpctypes.marshall_lsa_SidArray(sids) + -- [in] lsa_SidArray *sids, + arguments = arguments .. msrpctypes.marshall_lsa_SidArray(sids) --- [out,unique] lsa_RefDomainList *domains, --- [in,out] lsa_TransNameArray2 *names, - arguments = arguments .. msrpctypes.marshall_lsa_TransNameArray2(nil) + -- [out,unique] lsa_RefDomainList *domains, + -- [in,out] lsa_TransNameArray2 *names, + arguments = arguments .. msrpctypes.marshall_lsa_TransNameArray2(nil) --- [in] uint16 level, - arguments = arguments .. msrpctypes.marshall_int16(1) + -- [in] uint16 level, + arguments = arguments .. msrpctypes.marshall_int16(1) --- [in,out] uint32 *count, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in,out] uint32 *count, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] uint32 unknown1, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 unknown1, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] uint32 unknown2 - arguments = arguments .. msrpctypes.marshall_int32(2) + -- [in] uint32 unknown2 + arguments = arguments .. msrpctypes.marshall_int32(2) - -- Do the call - status, result = call_function(smbstate, 0x39, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x39, arguments) + if(status ~= true) then + return false, result + end - -- Make arguments easier to use - arguments = result['arguments'] + -- Make arguments easier to use + arguments = result['arguments'] --- [in] policy_handle *handle, --- [in] lsa_SidArray *sids, --- [out,unique] lsa_RefDomainList *domains, - pos, result['domains'] = msrpctypes.unmarshall_lsa_RefDomainList_ptr(arguments, pos) + -- [in] policy_handle *handle, + -- [in] lsa_SidArray *sids, + -- [out,unique] lsa_RefDomainList *domains, + pos, result['domains'] = msrpctypes.unmarshall_lsa_RefDomainList_ptr(arguments, pos) --- [in,out] lsa_TransNameArray2 *names, - pos, result['names'] = msrpctypes.unmarshall_lsa_TransNameArray2(arguments, pos) + -- [in,out] lsa_TransNameArray2 *names, + pos, result['names'] = msrpctypes.unmarshall_lsa_TransNameArray2(arguments, pos) --- [in] uint16 level, --- [in,out] uint32 *count, - local count - pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in] uint16 level, + -- [in,out] uint32 *count, + local count + pos, result['count'] = msrpctypes.unmarshall_int32(arguments, pos) --- [in] uint32 unknown1, --- [in] uint32 unknown2 + -- [in] uint32 unknown1, + -- [in] uint32 unknown2 - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (lsa.lookupnames2)" - end - if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED'] and result['return'] ~= smb.status_codes['NT_STATUS_NONE_MAPPED']) then - return false, smb.get_status_name(result['return']) .. " (lsa.lookupsids2)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (lsa.lookupnames2)" + end + if(result['return'] ~= 0 and result['return'] ~= smb.status_codes['NT_STATUS_SOME_NOT_MAPPED'] and result['return'] ~= smb.status_codes['NT_STATUS_NONE_MAPPED']) then + return false, smb.get_status_name(result['return']) .. " (lsa.lookupsids2)" + end - stdnse.print_debug(3, "MSRPC: LsarLookupSids2(): Returning") - return true, result + stdnse.print_debug(3, "MSRPC: LsarLookupSids2(): Returning") + return true, result end @@ -2324,39 +2324,39 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is potentially -- a table of values, none of which are likely to be used. function lsa_close(smbstate, handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling LsaClose() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling LsaClose() [%s]", smbstate['ip']) --- [in,out] policy_handle *handle - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,out] policy_handle *handle + arguments = msrpctypes.marshall_policy_handle(handle) - -- Do the call - status, result = call_function(smbstate, 0x00, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x00, arguments) + if(status ~= true) then + return false, result + end - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,out] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,out] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (lsa.close)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (lsa.close)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (lsa.close)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (lsa.close)" + end - stdnse.print_debug(3, "MSRPC: LsaClose() returned successfully") - return true, result + stdnse.print_debug(3, "MSRPC: LsaClose() returned successfully") + return true, result end ---A proxy to a msrpctypes function that converts a SidType to an english string. @@ -2366,7 +2366,7 @@ end --@param val The value to convert. --@return A string that can be displayed to the user. function lsa_SidType_tostr(val) - return msrpctypes.lsa_SidType_tostr(val) + return msrpctypes.lsa_SidType_tostr(val) end @@ -2376,47 +2376,47 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'handle', which is required to call other winreg functions. function winreg_openhku(smbstate) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenHKU() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenHKU() [%s]", smbstate['ip']) --- [in] uint16 *system_name, - arguments = msrpctypes.marshall_int16_ptr(0x1337, true) + -- [in] uint16 *system_name, + arguments = msrpctypes.marshall_int16_ptr(0x1337, true) --- [in] winreg_AccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') + -- [in] winreg_AccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x04, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x04, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenHKU() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenHKU() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] uint16 *system_name, --- [in] winreg_AccessMask access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in] uint16 *system_name, + -- [in] winreg_AccessMask access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.openhku)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.openhku)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.openhku)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.openhku)" + end - return true, result + return true, result end @@ -2426,47 +2426,47 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'handle', which is required to call other winreg functions. function winreg_openhklm(smbstate) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenHKLM() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenHKLM() [%s]", smbstate['ip']) --- [in] uint16 *system_name, - arguments = msrpctypes.marshall_int16_ptr(0x1337, true) + -- [in] uint16 *system_name, + arguments = msrpctypes.marshall_int16_ptr(0x1337, true) --- [in] winreg_AccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') + -- [in] winreg_AccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x02, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x02, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenHKLM() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenHKLM() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] uint16 *system_name, --- [in] winreg_AccessMask access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in] uint16 *system_name, + -- [in] winreg_AccessMask access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.openhklm)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.openhklm)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.openhklm)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.openhklm)" + end - return true, result + return true, result end ---Call the OpenHKPD function, to obtain a handle to the hidden HKEY_PERFORMANCE_DATA hive @@ -2475,47 +2475,47 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'handle', which is required to call other winreg functions. function winreg_openhkpd(smbstate) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenHKPD() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenHKPD() [%s]", smbstate['ip']) --- [in] uint16 *system_name, - arguments = msrpctypes.marshall_int16_ptr(0x1337, true) + -- [in] uint16 *system_name, + arguments = msrpctypes.marshall_int16_ptr(0x1337, true) --- [in] winreg_AccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') + -- [in] winreg_AccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x03, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x03, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenHKPD() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenHKPD() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] uint16 *system_name, --- [in] winreg_AccessMask access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in] uint16 *system_name, + -- [in] winreg_AccessMask access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.openhkpd)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.openhkpd)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.openhkpd)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.openhkpd)" + end - return true, result + return true, result end ---Call the OpenHKCU function, to obtain a handle to the HKEY_CURRENT_USER hive @@ -2524,47 +2524,47 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'handle', which is required to call other winreg functions. function winreg_openhkcu(smbstate) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenHKCU() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenHKCU() [%s]", smbstate['ip']) --- [in] uint16 *system_name, - arguments = msrpctypes.marshall_int16_ptr(0x1337, true) + -- [in] uint16 *system_name, + arguments = msrpctypes.marshall_int16_ptr(0x1337, true) --- [in] winreg_AccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') + -- [in] winreg_AccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x01, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x01, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenHKCU() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenHKCU() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] uint16 *system_name, --- [in] winreg_AccessMask access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in] uint16 *system_name, + -- [in] winreg_AccessMask access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.openhkcu)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.openhkcu)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.openhkcu)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.openhkcu)" + end - return true, result + return true, result end @@ -2582,65 +2582,65 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'name', which is the name of the current key function winreg_enumkey(smbstate, handle, index, name) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling EnumKey(%d) [%s]", index, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling EnumKey(%d) [%s]", index, smbstate['ip']) --- [in,ref] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] uint32 enum_index, - arguments = arguments .. msrpctypes.marshall_int32(index) + -- [in] uint32 enum_index, + arguments = arguments .. msrpctypes.marshall_int32(index) --- [in,out,ref] winreg_StringBuf *name, - -- NOTE: if the 'name' parameter here is set to 'nil', the service on a fully patched Windows 2000 system - -- may crash. - arguments = arguments .. msrpctypes.marshall_winreg_StringBuf({name=""}, 520) + -- [in,out,ref] winreg_StringBuf *name, + -- NOTE: if the 'name' parameter here is set to 'nil', the service on a fully patched Windows 2000 system + -- may crash. + arguments = arguments .. msrpctypes.marshall_winreg_StringBuf({name=""}, 520) --- [in,out,unique] winreg_StringBuf *keyclass, - arguments = arguments .. msrpctypes.marshall_winreg_StringBuf_ptr({name=nil}) + -- [in,out,unique] winreg_StringBuf *keyclass, + arguments = arguments .. msrpctypes.marshall_winreg_StringBuf_ptr({name=nil}) --- [in,out,unique] NTTIME *last_changed_time - arguments = arguments .. msrpctypes.marshall_NTTIME_ptr(0) + -- [in,out,unique] NTTIME *last_changed_time + arguments = arguments .. msrpctypes.marshall_NTTIME_ptr(0) - -- Do the call - status, result = call_function(smbstate, 0x09, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x09, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: EnumKey() returned successfully") + stdnse.print_debug(3, "MSRPC: EnumKey() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - local referent_id + -- Make arguments easier to use + arguments = result['arguments'] + local referent_id - pos = 1 + pos = 1 --- [in,ref] policy_handle *handle, --- [in] uint32 enum_index, --- [in,out,ref] winreg_StringBuf *name, - pos, result['name'] = msrpctypes.unmarshall_winreg_StringBuf(arguments, pos) + -- [in,ref] policy_handle *handle, + -- [in] uint32 enum_index, + -- [in,out,ref] winreg_StringBuf *name, + pos, result['name'] = msrpctypes.unmarshall_winreg_StringBuf(arguments, pos) --- [in,out,unique] winreg_StringBuf *keyclass, - pos, result['keyclass'] = msrpctypes.unmarshall_winreg_StringBuf_ptr(arguments, pos) + -- [in,out,unique] winreg_StringBuf *keyclass, + pos, result['keyclass'] = msrpctypes.unmarshall_winreg_StringBuf_ptr(arguments, pos) --- [in,out,unique] NTTIME *last_changed_time - pos, result['changed_time'] = msrpctypes.unmarshall_NTTIME_ptr(arguments, pos) - result['changed_date'] = os.date("%Y-%m-%d %H:%M:%S", result['changed_time']) + -- [in,out,unique] NTTIME *last_changed_time + pos, result['changed_time'] = msrpctypes.unmarshall_NTTIME_ptr(arguments, pos) + result['changed_date'] = os.date("%Y-%m-%d %H:%M:%S", result['changed_time']) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.enumkey)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.enumkey)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.enumkey)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.enumkey)" + end - return true, result + return true, result end @@ -2652,56 +2652,56 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values, the most -- useful one being 'handle', which is a handle to the newly opened key. function winreg_openkey(smbstate, handle, keyname) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenKey(%s) [%s]", keyname, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenKey(%s) [%s]", keyname, smbstate['ip']) --- [in,ref] policy_handle *parent_handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *parent_handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] winreg_String keyname, - arguments = arguments .. msrpctypes.marshall_winreg_String({name=keyname}) + -- [in] winreg_String keyname, + arguments = arguments .. msrpctypes.marshall_winreg_String({name=keyname}) --- [in] uint32 unknown, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 unknown, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] winreg_AccessMask access_mask, - arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') + -- [in] winreg_AccessMask access_mask, + arguments = arguments .. msrpctypes.marshall_winreg_AccessMask('MAXIMUM_ALLOWED_ACCESS') --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x0F, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0F, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenKey() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenKey() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *parent_handle, --- [in] winreg_String keyname, --- [in] uint32 unknown, --- [in] winreg_AccessMask access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,ref] policy_handle *parent_handle, + -- [in] winreg_String keyname, + -- [in] uint32 unknown, + -- [in] winreg_AccessMask access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.openkey)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.openkey)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.openkey)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.openkey)" + end - return true, result + return true, result end --- Calls the function QueryInfoKey, which obtains information about an opened key. @@ -2712,79 +2712,79 @@ end -- useful one, at least for me, being 'last_changed_time'/'last_changed_date', which are the date and time that the -- key was changed. function winreg_queryinfokey(smbstate, handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling QueryInfoKey() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling QueryInfoKey() [%s]", smbstate['ip']) --- [in,ref] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in,out,ref] winreg_String *classname, - arguments = arguments .. msrpctypes.marshall_winreg_String({name=""}, 2048) + -- [in,out,ref] winreg_String *classname, + arguments = arguments .. msrpctypes.marshall_winreg_String({name=""}, 2048) --- [out,ref] uint32 *num_subkeys, --- [out,ref] uint32 *max_subkeylen, --- [out,ref] uint32 *max_subkeysize, --- [out,ref] uint32 *num_values, --- [out,ref] uint32 *max_valnamelen, --- [out,ref] uint32 *max_valbufsize, --- [out,ref] uint32 *secdescsize, --- [out,ref] NTTIME *last_changed_time + -- [out,ref] uint32 *num_subkeys, + -- [out,ref] uint32 *max_subkeylen, + -- [out,ref] uint32 *max_subkeysize, + -- [out,ref] uint32 *num_values, + -- [out,ref] uint32 *max_valnamelen, + -- [out,ref] uint32 *max_valbufsize, + -- [out,ref] uint32 *secdescsize, + -- [out,ref] NTTIME *last_changed_time - -- Do the call - status, result = call_function(smbstate, 0x10, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x10, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: QueryInfoKey() returned successfully") + stdnse.print_debug(3, "MSRPC: QueryInfoKey() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *handle, --- [in,out,ref] winreg_String *classname, - pos, result['classname'] = msrpctypes.unmarshall_winreg_String(arguments, pos) + -- [in,ref] policy_handle *handle, + -- [in,out,ref] winreg_String *classname, + pos, result['classname'] = msrpctypes.unmarshall_winreg_String(arguments, pos) --- [out,ref] uint32 *num_subkeys, - pos, result['subkeys'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *num_subkeys, + pos, result['subkeys'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] uint32 *max_subkeylen, - pos, result['subkeylen'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *max_subkeylen, + pos, result['subkeylen'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] uint32 *max_subkeysize, - pos, result['subkeysize'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *max_subkeysize, + pos, result['subkeysize'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] uint32 *num_values, - pos, result['num_values'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *num_values, + pos, result['num_values'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] uint32 *max_valnamelen, - pos, result['max_valnamelen'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *max_valnamelen, + pos, result['max_valnamelen'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] uint32 *max_valbufsize, - pos, result['max_valbufsize'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *max_valbufsize, + pos, result['max_valbufsize'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] uint32 *secdescsize, - pos, result['secdescsize'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [out,ref] uint32 *secdescsize, + pos, result['secdescsize'] = msrpctypes.unmarshall_int32(arguments, pos) --- [out,ref] NTTIME *last_changed_time - pos, result['last_changed_time'] = msrpctypes.unmarshall_NTTIME(arguments, pos) - result['last_changed_date'] = os.date("%Y-%m-%d %H:%M:%S", result['last_changed_time']) + -- [out,ref] NTTIME *last_changed_time + pos, result['last_changed_time'] = msrpctypes.unmarshall_NTTIME(arguments, pos) + result['last_changed_date'] = os.date("%Y-%m-%d %H:%M:%S", result['last_changed_time']) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (winreg.queryinfokey)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (winreg.queryinfokey)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (winreg.queryinfokey)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (winreg.queryinfokey)" + end - return true, result + return true, result end @@ -2797,89 +2797,89 @@ end -- useful one, at least for me, being 'last_changed_time'/'last_changed_date', which are the date and time that the -- key was changed. function winreg_queryvalue(smbstate, handle, value) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling QueryValue(%s) [%s]", value, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling QueryValue(%s) [%s]", value, smbstate['ip']) --- [in,ref] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] winreg_String value_name, - arguments = arguments .. msrpctypes.marshall_winreg_String({name=value}) + -- [in] winreg_String value_name, + arguments = arguments .. msrpctypes.marshall_winreg_String({name=value}) --- [in,out] winreg_Type *type, - arguments = arguments .. msrpctypes.marshall_winreg_Type_ptr("REG_NONE") + -- [in,out] winreg_Type *type, + arguments = arguments .. msrpctypes.marshall_winreg_Type_ptr("REG_NONE") --- [in,out,size_is(*size),length_is(*length)] uint8 *data, - arguments = arguments .. msrpctypes.marshall_int8_array_ptr("", 1000000) + -- [in,out,size_is(*size),length_is(*length)] uint8 *data, + arguments = arguments .. msrpctypes.marshall_int8_array_ptr("", 1000000) --- [in,out] uint32 *size, - arguments = arguments .. msrpctypes.marshall_int32_ptr(1000000) + -- [in,out] uint32 *size, + arguments = arguments .. msrpctypes.marshall_int32_ptr(1000000) --- [in,out] uint32 *length - arguments = arguments .. msrpctypes.marshall_int32_ptr(0) + -- [in,out] uint32 *length + arguments = arguments .. msrpctypes.marshall_int32_ptr(0) - -- Do the call - status, result = call_function(smbstate, 0x11, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x11, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: QueryValue() returned successfully") - local length, referent_id + stdnse.print_debug(3, "MSRPC: QueryValue() returned successfully") + local length, referent_id - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *handle, --- [in] winreg_String value_name, --- [in,out] winreg_Type *type, - pos, result['type'] = msrpctypes.unmarshall_winreg_Type_ptr(arguments, pos) + -- [in,ref] policy_handle *handle, + -- [in] winreg_String value_name, + -- [in,out] winreg_Type *type, + pos, result['type'] = msrpctypes.unmarshall_winreg_Type_ptr(arguments, pos) --- [in,out,size_is(*size),length_is(*length)] uint8 *data, - pos, result['data'] = msrpctypes.unmarshall_int8_array_ptr(arguments, pos) + -- [in,out,size_is(*size),length_is(*length)] uint8 *data, + pos, result['data'] = msrpctypes.unmarshall_int8_array_ptr(arguments, pos) - -- Format the type properly and put it in "value" - if(result['data'] ~= nil) then - local _ - if(result['type'] == "REG_DWORD") then - _, result['value'] = bin.unpack("OpenSCManagerA, which gets a handle to the service manager. Should be closed with @@ -2936,52 +2936,52 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_openscmanagera(smbstate, machinename) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenSCManagerA() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenSCManagerA() [%s]", smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *MachineName, - arguments = msrpctypes.marshall_ascii_ptr("\\\\" .. machinename) + -- [in] [string,charset(UTF16)] uint16 *MachineName, + arguments = msrpctypes.marshall_ascii_ptr("\\\\" .. machinename) --- [in] [string,charset(UTF16)] uint16 *DatabaseName, - arguments = arguments .. msrpctypes.marshall_ascii_ptr(nil) + -- [in] [string,charset(UTF16)] uint16 *DatabaseName, + arguments = arguments .. msrpctypes.marshall_ascii_ptr(nil) --- [in] uint32 access_mask, --- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) - arguments = arguments .. msrpctypes.marshall_int32(0x00000002) + -- [in] uint32 access_mask, + -- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) + arguments = arguments .. msrpctypes.marshall_int32(0x00000002) --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x1b, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x1b, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenSCManagerA() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenSCManagerA() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *MachineName, --- [in] [string,charset(UTF16)] uint16 *DatabaseName, --- [in] uint32 access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in] [string,charset(UTF16)] uint16 *MachineName, + -- [in] [string,charset(UTF16)] uint16 *DatabaseName, + -- [in] uint32 access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.openscmanagera)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.openscmanagera)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.openscmanagera)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.openscmanagera)" + end - return true, result + return true, result end @@ -2993,56 +2993,56 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_openscmanagerw(smbstate, machinename) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align --- if(1 == 1) then --- return svcctl_openscmanagera(smbstate, machinename) --- end + -- if(1 == 1) then + -- return svcctl_openscmanagera(smbstate, machinename) + -- end - stdnse.print_debug(2, "MSRPC: Calling OpenSCManagerW() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenSCManagerW() [%s]", smbstate['ip']) --- [in] [string,charset(UTF16)] uint16 *MachineName, - arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. machinename, true) + -- [in] [string,charset(UTF16)] uint16 *MachineName, + arguments = msrpctypes.marshall_unicode_ptr("\\\\" .. machinename, true) --- [in] [string,charset(UTF16)] uint16 *DatabaseName, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil, true) + -- [in] [string,charset(UTF16)] uint16 *DatabaseName, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil, true) --- [in] uint32 access_mask, --- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) - arguments = arguments .. msrpctypes.marshall_int32(0x02000000) + -- [in] uint32 access_mask, + -- arguments = arguments .. msrpctypes.marshall_int32(0x000f003f) + arguments = arguments .. msrpctypes.marshall_int32(0x02000000) --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x0f, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0f, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenSCManagerW() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenSCManagerW() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in] [string,charset(UTF16)] uint16 *MachineName, --- [in] [string,charset(UTF16)] uint16 *DatabaseName, --- [in] uint32 access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in] [string,charset(UTF16)] uint16 *MachineName, + -- [in] [string,charset(UTF16)] uint16 *DatabaseName, + -- [in] uint32 access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.openscmanagerw)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.openscmanagerw)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.openscmanagerw)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.openscmanagerw)" + end - return true, result + return true, result end @@ -3053,41 +3053,41 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_closeservicehandle(smbstate, handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling CloseServiceHandle() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling CloseServiceHandle() [%s]", smbstate['ip']) --- [in,out,ref] policy_handle *handle - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,out,ref] policy_handle *handle + arguments = msrpctypes.marshall_policy_handle(handle) - -- Do the call - status, result = call_function(smbstate, 0x00, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x00, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenSCManagerA() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenSCManagerA() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.closeservicehandle)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.closeservicehandle)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.closeservicehandle)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.closeservicehandle)" + end - return true, result + return true, result end --- Calls the function CreateServiceW, which creates a service on the remote machine. This should @@ -3098,103 +3098,103 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_createservicew(smbstate, handle, service_name, display_name, path) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling CreateServiceW() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling CreateServiceW() [%s]", smbstate['ip']) --- [in,ref] policy_handle *scmanager_handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *scmanager_handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] [string,charset(UTF16)] uint16 ServiceName[], - arguments = arguments .. msrpctypes.marshall_unicode(service_name, true) + -- [in] [string,charset(UTF16)] uint16 ServiceName[], + arguments = arguments .. msrpctypes.marshall_unicode(service_name, true) --- [in] [string,charset(UTF16)] uint16 *DisplayName, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(display_name, true) + -- [in] [string,charset(UTF16)] uint16 *DisplayName, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(display_name, true) --- [in] uint32 desired_access, - arguments = arguments .. msrpctypes.marshall_int32(0x000f01ff) -- Access: Max + -- [in] uint32 desired_access, + arguments = arguments .. msrpctypes.marshall_int32(0x000f01ff) -- Access: Max --- [in] uint32 type, - arguments = arguments .. msrpctypes.marshall_int32(0x00000010) -- Type: own process + -- [in] uint32 type, + arguments = arguments .. msrpctypes.marshall_int32(0x00000010) -- Type: own process --- [in] uint32 start_type, - arguments = arguments .. msrpctypes.marshall_int32(0x00000003) -- Start: Demand + -- [in] uint32 start_type, + arguments = arguments .. msrpctypes.marshall_int32(0x00000003) -- Start: Demand --- [in] uint32 error_control, - arguments = arguments .. msrpctypes.marshall_int32(0x00000000) -- Error: Ignore + -- [in] uint32 error_control, + arguments = arguments .. msrpctypes.marshall_int32(0x00000000) -- Error: Ignore --- [in] [string,charset(UTF16)] uint16 binary_path[], - arguments = arguments .. msrpctypes.marshall_unicode(path, true) + -- [in] [string,charset(UTF16)] uint16 binary_path[], + arguments = arguments .. msrpctypes.marshall_unicode(path, true) --- [in] [string,charset(UTF16)] uint16 *LoadOrderGroupKey, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) + -- [in] [string,charset(UTF16)] uint16 *LoadOrderGroupKey, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) --- [in,out] uint32 *TagId, - arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) + -- [in,out] uint32 *TagId, + arguments = arguments .. msrpctypes.marshall_int32_ptr(nil) --- [in,size_is(dependencies_size)] uint8 *dependencies, - arguments = arguments .. msrpctypes.marshall_int8_ptr(nil) + -- [in,size_is(dependencies_size)] uint8 *dependencies, + arguments = arguments .. msrpctypes.marshall_int8_ptr(nil) --- [in] uint32 dependencies_size, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 dependencies_size, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [in] [string,charset(UTF16)] uint16 *service_start_name, - arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) + -- [in] [string,charset(UTF16)] uint16 *service_start_name, + arguments = arguments .. msrpctypes.marshall_unicode_ptr(nil) --- [in,size_is(password_size)] uint8 *password, - arguments = arguments .. msrpctypes.marshall_int8_ptr(nil) + -- [in,size_is(password_size)] uint8 *password, + arguments = arguments .. msrpctypes.marshall_int8_ptr(nil) --- [in] uint32 password_size, - arguments = arguments .. msrpctypes.marshall_int32(0) + -- [in] uint32 password_size, + arguments = arguments .. msrpctypes.marshall_int32(0) --- [out,ref] policy_handle *handle + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x0c, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x0c, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: CreateServiceW() returned successfully") + stdnse.print_debug(3, "MSRPC: CreateServiceW() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *scmanager_handle, --- [in] [string,charset(UTF16)] uint16 ServiceName[], --- [in] [string,charset(UTF16)] uint16 *DisplayName, --- [in] uint32 desired_access, --- [in] uint32 type, --- [in] uint32 start_type, --- [in] uint32 error_control, --- [in] [string,charset(UTF16)] uint16 binary_path[], --- [in] [string,charset(UTF16)] uint16 *LoadOrderGroupKey, --- [in,out] uint32 *TagId, - pos, result['TagId'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) + -- [in,ref] policy_handle *scmanager_handle, + -- [in] [string,charset(UTF16)] uint16 ServiceName[], + -- [in] [string,charset(UTF16)] uint16 *DisplayName, + -- [in] uint32 desired_access, + -- [in] uint32 type, + -- [in] uint32 start_type, + -- [in] uint32 error_control, + -- [in] [string,charset(UTF16)] uint16 binary_path[], + -- [in] [string,charset(UTF16)] uint16 *LoadOrderGroupKey, + -- [in,out] uint32 *TagId, + pos, result['TagId'] = msrpctypes.unmarshall_int32_ptr(arguments, pos) --- [in,size_is(dependencies_size)] uint8 *dependencies, --- [in] uint32 dependencies_size, --- [in] [string,charset(UTF16)] uint16 *service_start_name, --- [in,size_is(password_size)] uint8 *password, --- [in] uint32 password_size, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,size_is(dependencies_size)] uint8 *dependencies, + -- [in] uint32 dependencies_size, + -- [in] [string,charset(UTF16)] uint16 *service_start_name, + -- [in,size_is(password_size)] uint8 *password, + -- [in] uint32 password_size, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.createservicew)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.createservicew)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.createservicew)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.createservicew)" + end - return true, result + return true, result end --- Calls the function DeleteService, which deletes a service on the remote machine. This service @@ -3205,42 +3205,42 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_deleteservice(smbstate, handle) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling DeleteService() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling DeleteService() [%s]", smbstate['ip']) --- [in,ref] policy_handle *handle - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle + arguments = msrpctypes.marshall_policy_handle(handle) - -- Do the call - status, result = call_function(smbstate, 0x02, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x02, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: DeleteService() returned successfully") + stdnse.print_debug(3, "MSRPC: DeleteService() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *handle + -- [in,ref] policy_handle *handle - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.deleteservice)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.deleteservice)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.deleteservice)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.deleteservice)" + end - return true, result + return true, result end --- Calls the function OpenServiceW, which gets a handle to the service. Should be closed with @@ -3252,51 +3252,51 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_openservicew(smbstate, handle, name) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling OpenServiceW() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling OpenServiceW() [%s]", smbstate['ip']) --- [in,ref] policy_handle *scmanager_handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *scmanager_handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] [string,charset(UTF16)] uint16 ServiceName[], - arguments = arguments .. msrpctypes.marshall_unicode(name, true) + -- [in] [string,charset(UTF16)] uint16 ServiceName[], + arguments = arguments .. msrpctypes.marshall_unicode(name, true) --- [in] uint32 access_mask, - arguments = arguments .. msrpctypes.marshall_int32(0x000f01ff) --- [out,ref] policy_handle *handle + -- [in] uint32 access_mask, + arguments = arguments .. msrpctypes.marshall_int32(0x000f01ff) + -- [out,ref] policy_handle *handle - -- Do the call - status, result = call_function(smbstate, 0x10, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x10, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: OpenServiceW() returned successfully") + stdnse.print_debug(3, "MSRPC: OpenServiceW() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *scmanager_handle, --- [in] [string,charset(UTF16)] uint16 ServiceName[], --- [in] uint32 access_mask, --- [out,ref] policy_handle *handle - pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) + -- [in,ref] policy_handle *scmanager_handle, + -- [in] [string,charset(UTF16)] uint16 ServiceName[], + -- [in] uint32 access_mask, + -- [out,ref] policy_handle *handle + pos, result['handle'] = msrpctypes.unmarshall_policy_handle(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.openservicew)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.openservicew)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.openservicew)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.openservicew)" + end - return true, result + return true, result end --- Calls the function StartServiceW, which starts a service. Requires a handle @@ -3308,50 +3308,50 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_startservicew(smbstate, handle, args) - local i, j - local status, result - local arguments - local pos, align - stdnse.print_debug(2, "MSRPC: Calling StartServiceW() [%s]", smbstate['ip']) + local i, j + local status, result + local arguments + local pos, align + stdnse.print_debug(2, "MSRPC: Calling StartServiceW() [%s]", smbstate['ip']) --- [in,ref] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] uint32 NumArgs, - if(args == nil) then - arguments = arguments .. msrpctypes.marshall_int32(0) - else - arguments = arguments .. msrpctypes.marshall_int32(#args) - end + -- [in] uint32 NumArgs, + if(args == nil) then + arguments = arguments .. msrpctypes.marshall_int32(0) + else + arguments = arguments .. msrpctypes.marshall_int32(#args) + end --- [in/*FIXME:,length_is(NumArgs)*/] [string,charset(UTF16)] uint16 *Arguments - arguments = arguments .. msrpctypes.marshall_unicode_array_ptr(args, true) + -- [in/*FIXME:,length_is(NumArgs)*/] [string,charset(UTF16)] uint16 *Arguments + arguments = arguments .. msrpctypes.marshall_unicode_array_ptr(args, true) - -- Do the call - status, result = call_function(smbstate, 0x13, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x13, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: StartServiceW() returned successfully") + stdnse.print_debug(3, "MSRPC: StartServiceW() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *handle, --- [in] uint32 NumArgs, --- [in/*FIXME:,length_is(NumArgs)*/] [string,charset(UTF16)] uint16 *Arguments + -- [in,ref] policy_handle *handle, + -- [in] uint32 NumArgs, + -- [in/*FIXME:,length_is(NumArgs)*/] [string,charset(UTF16)] uint16 *Arguments - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.startservicew)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.startservicew)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.startservicew)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.startservicew)" + end - return true, result + return true, result end @@ -3363,48 +3363,48 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_controlservice(smbstate, handle, control) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling ControlService() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling ControlService() [%s]", smbstate['ip']) --- [in,ref] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [in] uint32 control, - arguments = arguments .. msrpctypes.marshall_svcctl_ControlCode(control) + -- [in] uint32 control, + arguments = arguments .. msrpctypes.marshall_svcctl_ControlCode(control) --- [out,ref] SERVICE_STATUS *service_status + -- [out,ref] SERVICE_STATUS *service_status - -- Do the call - status, result = call_function(smbstate, 0x01, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x01, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: ControlService() returned successfully") + stdnse.print_debug(3, "MSRPC: ControlService() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *handle, --- [in] uint32 control, --- [out,ref] SERVICE_STATUS *service_status - pos, result['service_status'] = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos) + -- [in,ref] policy_handle *handle, + -- [in] uint32 control, + -- [out,ref] SERVICE_STATUS *service_status + pos, result['service_status'] = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.controlservice)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.controlservice)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.controlservice)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.controlservice)" + end - return true, result + return true, result end @@ -3416,44 +3416,44 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table of values -- representing the "out" parameters. function svcctl_queryservicestatus(smbstate, handle, control) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - stdnse.print_debug(2, "MSRPC: Calling QueryServiceStatus() [%s]", smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling QueryServiceStatus() [%s]", smbstate['ip']) --- [in,ref] policy_handle *handle, - arguments = msrpctypes.marshall_policy_handle(handle) + -- [in,ref] policy_handle *handle, + arguments = msrpctypes.marshall_policy_handle(handle) --- [out,ref] SERVICE_STATUS *service_status + -- [out,ref] SERVICE_STATUS *service_status - -- Do the call - status, result = call_function(smbstate, 0x06, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x06, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: QueryServiceStatus() returned successfully") + stdnse.print_debug(3, "MSRPC: QueryServiceStatus() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,ref] policy_handle *handle, --- [out,ref] SERVICE_STATUS *service_status - pos, result['service_status'] = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos) + -- [in,ref] policy_handle *handle, + -- [out,ref] SERVICE_STATUS *service_status + pos, result['service_status'] = msrpctypes.unmarshall_SERVICE_STATUS(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (svcctl.queryservicestatus)" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (svcctl.queryservicestatus)" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (svcctl.queryservicestatus)" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (svcctl.queryservicestatus)" + end - return true, result + return true, result end ---Calls the function JobAdd, which schedules a process to be run on the remote @@ -3466,52 +3466,52 @@ end --@param time (optional) The time at which to run the command. Default: 5 seconds from -- when the user logged in. function atsvc_jobadd(smbstate, server, command, time) - local i, j - local status, result - local arguments - local pos, align + local i, j + local status, result + local arguments + local pos, align - -- Set up the time - if(time == nil) then - -- TODO - end + -- Set up the time + if(time == nil) then + -- TODO + end - stdnse.print_debug(2, "MSRPC: Calling AddJob(%s) [%s]", command, smbstate['ip']) + stdnse.print_debug(2, "MSRPC: Calling AddJob(%s) [%s]", command, smbstate['ip']) --- [in,unique,string,charset(UTF16)] uint16 *servername, - arguments = msrpctypes.marshall_unicode_ptr(server, true) + -- [in,unique,string,charset(UTF16)] uint16 *servername, + arguments = msrpctypes.marshall_unicode_ptr(server, true) --- [in] atsvc_JobInfo *job_info, - arguments = arguments .. msrpctypes.marshall_atsvc_JobInfo(command, time) --- [out,ref] uint32 *job_id + -- [in] atsvc_JobInfo *job_info, + arguments = arguments .. msrpctypes.marshall_atsvc_JobInfo(command, time) + -- [out,ref] uint32 *job_id - -- Do the call - status, result = call_function(smbstate, 0x00, arguments) - if(status ~= true) then - return false, result - end + -- Do the call + status, result = call_function(smbstate, 0x00, arguments) + if(status ~= true) then + return false, result + end - stdnse.print_debug(3, "MSRPC: AddJob() returned successfully") + stdnse.print_debug(3, "MSRPC: AddJob() returned successfully") - -- Make arguments easier to use - arguments = result['arguments'] - pos = 1 + -- Make arguments easier to use + arguments = result['arguments'] + pos = 1 --- [in,unique,string,charset(UTF16)] uint16 *servername, --- [in] atsvc_JobInfo *job_info, --- [out,ref] uint32 *job_id - pos, result['job_id'] = msrpctypes.unmarshall_int32(arguments, pos) + -- [in,unique,string,charset(UTF16)] uint16 *servername, + -- [in] atsvc_JobInfo *job_info, + -- [out,ref] uint32 *job_id + pos, result['job_id'] = msrpctypes.unmarshall_int32(arguments, pos) - pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) - if(result['return'] == nil) then - return false, "Read off the end of the packet (atsvc.addjob())" - end - if(result['return'] ~= 0) then - return false, smb.get_status_name(result['return']) .. " (atsvc.addjob())" - end + pos, result['return'] = msrpctypes.unmarshall_int32(arguments, pos) + if(result['return'] == nil) then + return false, "Read off the end of the packet (atsvc.addjob())" + end + if(result['return'] ~= 0) then + return false, smb.get_status_name(result['return']) .. " (atsvc.addjob())" + end - return true, result + return true, result end ---Attempt to enumerate users using SAMR functions. @@ -3528,324 +3528,324 @@ end -- * source -- * flags[] function samr_enum_users(host) - local i, j + local i, j - local smbstate - local bind_result, connect4_result, enumdomains_result - local connect_handle - local status, smbstate - local response = {} + local smbstate + local bind_result, connect4_result, enumdomains_result + local connect_handle + local status, smbstate + local response = {} - -- Create the SMB session - status, smbstate = start_smb(host, SAMR_PATH, true) + -- Create the SMB session + status, smbstate = start_smb(host, SAMR_PATH, true) - if(status == false) then - return false, smbstate - end + if(status == false) then + return false, smbstate + end - -- Bind to SAMR service - status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) - if(status == false) then - stop_smb(smbstate) - return false, bind_result - end + -- Bind to SAMR service + status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) + if(status == false) then + stop_smb(smbstate) + return false, bind_result + end - -- Call connect4() - status, connect4_result = samr_connect4(smbstate, host.ip) - if(status == false) then - stop_smb(smbstate) - return false, connect4_result - end + -- Call connect4() + status, connect4_result = samr_connect4(smbstate, host.ip) + if(status == false) then + stop_smb(smbstate) + return false, connect4_result + end - -- Save the connect_handle - connect_handle = connect4_result['connect_handle'] + -- Save the connect_handle + connect_handle = connect4_result['connect_handle'] - -- Call EnumDomains() - status, enumdomains_result = samr_enumdomains(smbstate, connect_handle) - if(status == false) then - stop_smb(smbstate) - return false, enumdomains_result - end + -- Call EnumDomains() + status, enumdomains_result = samr_enumdomains(smbstate, connect_handle) + if(status == false) then + stop_smb(smbstate) + return false, enumdomains_result + end - -- If no domains were returned, go back with an error - if(#enumdomains_result['sam']['entries'] == 0) then - stop_smb(smbstate) - return false, "Couldn't find any domains" - end + -- If no domains were returned, go back with an error + if(#enumdomains_result['sam']['entries'] == 0) then + stop_smb(smbstate) + return false, "Couldn't find any domains" + end - -- Now, loop through the domains and find the users - for i = 1, #enumdomains_result['sam']['entries'], 1 do + -- Now, loop through the domains and find the users + for i = 1, #enumdomains_result['sam']['entries'], 1 do - local domain = enumdomains_result['sam']['entries'][i]['name'] - -- We don't care about the 'builtin' domain, in all my tests it's empty - if(domain ~= 'Builtin') then - -- Call LookupDomain() - local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain) - if(status == false) then - stop_smb(smbstate) - return false, lookupdomain_result - end + local domain = enumdomains_result['sam']['entries'][i]['name'] + -- We don't care about the 'builtin' domain, in all my tests it's empty + if(domain ~= 'Builtin') then + -- Call LookupDomain() + local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain) + if(status == false) then + stop_smb(smbstate) + return false, lookupdomain_result + end - -- Save the sid - local sid = lookupdomain_result['sid'] + -- Save the sid + local sid = lookupdomain_result['sid'] - -- Call OpenDomain() - local status, opendomain_result = samr_opendomain(smbstate, connect_handle, sid) - if(status == false) then - stop_smb(smbstate) - return false, opendomain_result - end + -- Call OpenDomain() + local status, opendomain_result = samr_opendomain(smbstate, connect_handle, sid) + if(status == false) then + stop_smb(smbstate) + return false, opendomain_result + end - -- Save the domain handle - local domain_handle = opendomain_result['domain_handle'] + -- Save the domain handle + local domain_handle = opendomain_result['domain_handle'] - -- Loop as long as we're getting valid results - j = 0 - repeat - -- Call QueryDisplayInfo() - local status, querydisplayinfo_result = samr_querydisplayinfo(smbstate, domain_handle, j, SAMR_GROUPSIZE) - if(status == false) then - stop_smb(smbstate) - return false, querydisplayinfo_result - end + -- Loop as long as we're getting valid results + j = 0 + repeat + -- Call QueryDisplayInfo() + local status, querydisplayinfo_result = samr_querydisplayinfo(smbstate, domain_handle, j, SAMR_GROUPSIZE) + if(status == false) then + stop_smb(smbstate) + return false, querydisplayinfo_result + end - -- Save the response - if(querydisplayinfo_result['info'] ~= nil and querydisplayinfo_result['info']['entries'] ~= nil) then - local k - for k = 1, #querydisplayinfo_result['info']['entries'], 1 do - local array = {} - local l + -- Save the response + if(querydisplayinfo_result['info'] ~= nil and querydisplayinfo_result['info']['entries'] ~= nil) then + local k + for k = 1, #querydisplayinfo_result['info']['entries'], 1 do + local array = {} + local l - -- The reason these are all indexed from '1' is because we request names one at a time. - array['name'] = querydisplayinfo_result['info']['entries'][k]['account_name'] - array['fullname'] = querydisplayinfo_result['info']['entries'][k]['full_name'] - array['description'] = querydisplayinfo_result['info']['entries'][k]['description'] - array['rid'] = querydisplayinfo_result['info']['entries'][k]['rid'] - array['domain'] = domain - array['type'] = 'SID_NAME_USER' - array['typestr'] = 'User' - array['source'] = 'SAMR Enumeration' - array['flags'] = querydisplayinfo_result['info']['entries'][k]['acct_flags'] + -- The reason these are all indexed from '1' is because we request names one at a time. + array['name'] = querydisplayinfo_result['info']['entries'][k]['account_name'] + array['fullname'] = querydisplayinfo_result['info']['entries'][k]['full_name'] + array['description'] = querydisplayinfo_result['info']['entries'][k]['description'] + array['rid'] = querydisplayinfo_result['info']['entries'][k]['rid'] + array['domain'] = domain + array['type'] = 'SID_NAME_USER' + array['typestr'] = 'User' + array['source'] = 'SAMR Enumeration' + array['flags'] = querydisplayinfo_result['info']['entries'][k]['acct_flags'] - -- Convert each element in the 'flags' array into the equivalent string - for l = 1, #array['flags'], 1 do - array['flags'][l] = samr_AcctFlags_tostr(array['flags'][l]) - end + -- Convert each element in the 'flags' array into the equivalent string + for l = 1, #array['flags'], 1 do + array['flags'][l] = samr_AcctFlags_tostr(array['flags'][l]) + end - -- Add it to the array - response[#response + 1] = array - end - end - j = j + SAMR_GROUPSIZE - until querydisplayinfo_result['return'] == 0 + -- Add it to the array + response[#response + 1] = array + end + end + j = j + SAMR_GROUPSIZE + until querydisplayinfo_result['return'] == 0 - -- Close the domain handle - samr_close(smbstate, domain_handle) - end -- Checking for 'builtin' - end -- Domain loop + -- Close the domain handle + samr_close(smbstate, domain_handle) + end -- Checking for 'builtin' + end -- Domain loop - -- Close the connect handle - samr_close(smbstate, connect_handle) + -- Close the connect handle + samr_close(smbstate, connect_handle) - -- Stop the SAMR SMB - stop_smb(smbstate) + -- Stop the SAMR SMB + stop_smb(smbstate) - return true, response + return true, response end function samr_enum_groups(host) - local i, j + local i, j - stdnse.print_debug(1, "MSRPC: Attempting to enumerate groups on %s", host.ip) - -- Create the SMB session - local status, smbstate = start_smb(host, SAMR_PATH, true) + stdnse.print_debug(1, "MSRPC: Attempting to enumerate groups on %s", host.ip) + -- Create the SMB session + local status, smbstate = start_smb(host, SAMR_PATH, true) - if(status == false) then - return false, smbstate - end + if(status == false) then + return false, smbstate + end - -- Bind to SAMR service - local status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) - if(status == false) then - stop_smb(smbstate) - return false, bind_result - end + -- Bind to SAMR service + local status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) + if(status == false) then + stop_smb(smbstate) + return false, bind_result + end - -- Call connect4() - local status, connect4_result = samr_connect4(smbstate, host.ip) - if(status == false) then - stop_smb(smbstate) - return false, connect4_result - end + -- Call connect4() + local status, connect4_result = samr_connect4(smbstate, host.ip) + if(status == false) then + stop_smb(smbstate) + return false, connect4_result + end - -- Save the connect_handle - local connect_handle = connect4_result['connect_handle'] + -- Save the connect_handle + local connect_handle = connect4_result['connect_handle'] - -- Call EnumDomains() - local status, enumdomains_result = samr_enumdomains(smbstate, connect_handle) - if(status == false) then - stop_smb(smbstate) - return false, enumdomains_result - end + -- Call EnumDomains() + local status, enumdomains_result = samr_enumdomains(smbstate, connect_handle) + if(status == false) then + stop_smb(smbstate) + return false, enumdomains_result + end - -- If no domains were returned, go back with an error - if(#enumdomains_result['sam']['entries'] == 0) then - stop_smb(smbstate) - return false, "Couldn't find any domains" - end + -- If no domains were returned, go back with an error + if(#enumdomains_result['sam']['entries'] == 0) then + stop_smb(smbstate) + return false, "Couldn't find any domains" + end - -- Now, loop through the domains and find the groups - local domains = {} - for _, domain in ipairs(enumdomains_result['sam']['entries']) do - -- Get a handy domain name - domain = domain['name'] - domains[domain] = {} + -- Now, loop through the domains and find the groups + local domains = {} + for _, domain in ipairs(enumdomains_result['sam']['entries']) do + -- Get a handy domain name + domain = domain['name'] + domains[domain] = {} - -- Call LookupDomain() - local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain) - if(status == false) then - stop_smb(smbstate) - return false, lookupdomain_result - end + -- Call LookupDomain() + local status, lookupdomain_result = samr_lookupdomain(smbstate, connect_handle, domain) + if(status == false) then + stop_smb(smbstate) + return false, lookupdomain_result + end - -- Save the sid - local domain_sid = lookupdomain_result['sid'] + -- Save the sid + local domain_sid = lookupdomain_result['sid'] - -- Call OpenDomain() - local status, opendomain_result = samr_opendomain(smbstate, connect_handle, domain_sid) - if(status == false) then - stop_smb(smbstate) - return false, opendomain_result - end + -- Call OpenDomain() + local status, opendomain_result = samr_opendomain(smbstate, connect_handle, domain_sid) + if(status == false) then + stop_smb(smbstate) + return false, opendomain_result + end - -- Save the domain handle - local domain_handle = opendomain_result['domain_handle'] + -- Save the domain handle + local domain_handle = opendomain_result['domain_handle'] - -- Get a list of groups - local status, enumaliases_result = samr_enumdomainaliases(smbstate, domain_handle) - if(status == false) then - stop_smb(smbstate) - return false, "Couldn't enumerate groups: " .. enumaliases_result - end + -- Get a list of groups + local status, enumaliases_result = samr_enumdomainaliases(smbstate, domain_handle) + if(status == false) then + stop_smb(smbstate) + return false, "Couldn't enumerate groups: " .. enumaliases_result + end - -- If it returned a nil array - if(enumaliases_result['sam'] == nil or enumaliases_result['sam']['entries'] == nil) then - return false, "ERROR: No groups returned by samr_EnumDomainAliases()" - end + -- If it returned a nil array + if(enumaliases_result['sam'] == nil or enumaliases_result['sam']['entries'] == nil) then + return false, "ERROR: No groups returned by samr_EnumDomainAliases()" + end - -- Print some output - stdnse.print_debug(1, "MSRPC: Found %d groups in %s", #enumaliases_result['sam']['entries'], domain) + -- Print some output + stdnse.print_debug(1, "MSRPC: Found %d groups in %s", #enumaliases_result['sam']['entries'], domain) - -- Record the results - local group_rids = {} - for _, group in ipairs(enumaliases_result['sam']['entries']) do - -- The RID - local group_rid = group['idx'] + -- Record the results + local group_rids = {} + for _, group in ipairs(enumaliases_result['sam']['entries']) do + -- The RID + local group_rid = group['idx'] - -- Keep a list of just RIDs, for easier lookup after - table.insert(group_rids, group_rid) + -- Keep a list of just RIDs, for easier lookup after + table.insert(group_rids, group_rid) - -- Save the output, this is what will be returned - domains[domain][group_rid] = {} - domains[domain][group_rid]['name'] = group['name'] - end -- Loop over group entries + -- Save the output, this is what will be returned + domains[domain][group_rid] = {} + domains[domain][group_rid]['name'] = group['name'] + end -- Loop over group entries - for _, group_rid in ipairs(group_rids) do - -- Get a handle to the alias - local status, openalias_result = samr_openalias(smbstate, domain_handle, group_rid) - if(not(status)) then - stop_smb(smbstate) - return false, "Couldn't open handle to group: " .. openalias_result - end - local group_handle = openalias_result['alias_handle'] + for _, group_rid in ipairs(group_rids) do + -- Get a handle to the alias + local status, openalias_result = samr_openalias(smbstate, domain_handle, group_rid) + if(not(status)) then + stop_smb(smbstate) + return false, "Couldn't open handle to group: " .. openalias_result + end + local group_handle = openalias_result['alias_handle'] - -- Get the members of the group - local status, getmembers_result = samr_getmembersinalias(smbstate, group_handle) - if(not(status)) then - stop_smb(smbstate) - return false, "Couldn't get members in group: " .. getmembers_result - end + -- Get the members of the group + local status, getmembers_result = samr_getmembersinalias(smbstate, group_handle) + if(not(status)) then + stop_smb(smbstate) + return false, "Couldn't get members in group: " .. getmembers_result + end - -- Save the SIDs - local member_sids = {} - if(getmembers_result and getmembers_result.sids and getmembers_result.sids.sids) then - -- Set the list of member_sids - member_sids = getmembers_result.sids.sids - end + -- Save the SIDs + local member_sids = {} + if(getmembers_result and getmembers_result.sids and getmembers_result.sids.sids) then + -- Set the list of member_sids + member_sids = getmembers_result.sids.sids + end - -- Print some output - stdnse.print_debug(1, "MSRPC: Adding group '%s' (RID: %d) with %d members", domains[domain][group_rid]['name'], group_rid, #member_sids) + -- Print some output + stdnse.print_debug(1, "MSRPC: Adding group '%s' (RID: %d) with %d members", domains[domain][group_rid]['name'], group_rid, #member_sids) - -- Save the output - domains[domain][group_rid]['member_sids'] = member_sids + -- Save the output + domains[domain][group_rid]['member_sids'] = member_sids - -- Close the group - samr_close(smbstate, group_handle) - end -- Loop over group RIDs + -- Close the group + samr_close(smbstate, group_handle) + end -- Loop over group RIDs - -- Close the domain handle - samr_close(smbstate, domain_handle) + -- Close the domain handle + samr_close(smbstate, domain_handle) - end -- Domain loop + end -- Domain loop - -- Close the connect handle - samr_close(smbstate, connect_handle) + -- Close the connect handle + samr_close(smbstate, connect_handle) - -- Stop the SAMR SMB - stop_smb(smbstate) + -- Stop the SAMR SMB + stop_smb(smbstate) - -- Now, we need a handle to LSA (in order to convert the RIDs to users - -- Create the SMB session - local status, smbstate = start_smb(host, LSA_PATH, true) - if(status == false) then - return false, smbstate - end + -- Now, we need a handle to LSA (in order to convert the RIDs to users + -- Create the SMB session + local status, smbstate = start_smb(host, LSA_PATH, true) + if(status == false) then + return false, smbstate + end - -- Bind to LSA service - local status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil) - if(status == false) then - stop_smb(smbstate) - return false, bind_result - end + -- Bind to LSA service + local status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil) + if(status == false) then + stop_smb(smbstate) + return false, bind_result + end - -- Open the LSA policy - local status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip) - if(status == false) then - stop_smb(smbstate) - return false, openpolicy2_result - end + -- Open the LSA policy + local status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip) + if(status == false) then + stop_smb(smbstate) + return false, openpolicy2_result + end - -- Loop through the domains - for domain, domain_data in pairs(domains) do - for group_rid, group in pairs(domain_data) do - -- Look up the SIDs - local status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], group['member_sids']) - if(status == false) then - stop_smb(smbstate) - return false, "Error looking up RIDs: " .. lookupsids2_result - end + -- Loop through the domains + for domain, domain_data in pairs(domains) do + for group_rid, group in pairs(domain_data) do + -- Look up the SIDs + local status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], group['member_sids']) + if(status == false) then + stop_smb(smbstate) + return false, "Error looking up RIDs: " .. lookupsids2_result + end - if(lookupsids2_result and lookupsids2_result.names and lookupsids2_result.names.names and (#lookupsids2_result.names.names > 0)) then - local members = {} - for _, resolved_name in ipairs(lookupsids2_result.names.names) do - if(resolved_name.sid_type == "SID_NAME_USER") then - table.insert(members, resolved_name.name) - end - end - domains[domain][group_rid]['members'] = members - else - domains[domain][group_rid]['members'] = {} - end - end - end + if(lookupsids2_result and lookupsids2_result.names and lookupsids2_result.names.names and (#lookupsids2_result.names.names > 0)) then + local members = {} + for _, resolved_name in ipairs(lookupsids2_result.names.names) do + if(resolved_name.sid_type == "SID_NAME_USER") then + table.insert(members, resolved_name.name) + end + end + domains[domain][group_rid]['members'] = members + else + domains[domain][group_rid]['members'] = {} + end + end + end - -- Close the handle - lsa_close(smbstate, openpolicy2_result['policy_handle']) + -- Close the handle + lsa_close(smbstate, openpolicy2_result['policy_handle']) - stop_smb(smbstate) + stop_smb(smbstate) - return true, domains + return true, domains end ---Attempt to enumerate users using LSA functions. @@ -3860,155 +3860,155 @@ end -- * source function lsa_enum_users(host) - local smbstate - local response = {} - local status, smbstate, bind_result, openpolicy2_result, lookupnames2_result, lookupsids2_result + local smbstate + local response = {} + local status, smbstate, bind_result, openpolicy2_result, lookupnames2_result, lookupsids2_result - -- Create the SMB session - status, smbstate = start_smb(host, LSA_PATH, true) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, LSA_PATH, true) + if(status == false) then + return false, smbstate + end - -- Bind to LSA service - status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil) - if(status == false) then - stop_smb(smbstate) - return false, bind_result - end + -- Bind to LSA service + status, bind_result = bind(smbstate, LSA_UUID, LSA_VERSION, nil) + if(status == false) then + stop_smb(smbstate) + return false, bind_result + end - -- Open the LSA policy - status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip) - if(status == false) then - stop_smb(smbstate) - return false, openpolicy2_result - end + -- Open the LSA policy + status, openpolicy2_result = lsa_openpolicy2(smbstate, host.ip) + if(status == false) then + stop_smb(smbstate) + return false, openpolicy2_result + end - -- Start with some common names, as well as the name returned by the negotiate call - -- Vista doesn't like a 'null' after the server name, so fix that (TODO: the way I strip the null here feels hackish, is there a better way?) - local names = {"administrator", "guest", "test"} - -- These aren't always sent back (especially with 'extended security') - if(smbstate['domain'] ~= nil) then - names[#names + 1] = smbstate['domain'] - end - if(smbstate['server'] ~= nil) then - names[#names + 1] = string.sub(smbstate['server'], 1, #smbstate['server'] - 1) - end + -- Start with some common names, as well as the name returned by the negotiate call + -- Vista doesn't like a 'null' after the server name, so fix that (TODO: the way I strip the null here feels hackish, is there a better way?) + local names = {"administrator", "guest", "test"} + -- These aren't always sent back (especially with 'extended security') + if(smbstate['domain'] ~= nil) then + names[#names + 1] = smbstate['domain'] + end + if(smbstate['server'] ~= nil) then + names[#names + 1] = string.sub(smbstate['server'], 1, #smbstate['server'] - 1) + end - -- Get the server's name from nbstat - local result, server_name = netbios.get_server_name(host.ip) - if(result == true) then - names[#names + 1] = server_name - end + -- Get the server's name from nbstat + local result, server_name = netbios.get_server_name(host.ip) + if(result == true) then + names[#names + 1] = server_name + end - -- Get the logged in user from nbstat - local result, user_name = netbios.get_user_name(host.ip) - if(result == true) then - names[#names + 1] = user_name - end + -- Get the logged in user from nbstat + local result, user_name = netbios.get_user_name(host.ip) + if(result == true) then + names[#names + 1] = user_name + end - -- Look up the names, if any are valid than the server's SID will be returned - status, lookupnames2_result = lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names) - if(status == false) then - stop_smb(smbstate) - return false, lookupnames2_result - end - -- Loop through the domains returned and find the users in each - for i = 1, #lookupnames2_result['domains']['domains'], 1 do - local domain = lookupnames2_result['domains']['domains'][i]['name'] - local sid = lookupnames2_result['domains']['domains'][i]['sid'] - local sids = { } + -- Look up the names, if any are valid than the server's SID will be returned + status, lookupnames2_result = lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names) + if(status == false) then + stop_smb(smbstate) + return false, lookupnames2_result + end + -- Loop through the domains returned and find the users in each + for i = 1, #lookupnames2_result['domains']['domains'], 1 do + local domain = lookupnames2_result['domains']['domains'][i]['name'] + local sid = lookupnames2_result['domains']['domains'][i]['sid'] + local sids = { } - -- Start by looking up 500 and up - for j = 500, 500 + LSA_GROUPSIZE, 1 do - sids[#sids + 1] = sid .. "-" .. j - end + -- Start by looking up 500 and up + for j = 500, 500 + LSA_GROUPSIZE, 1 do + sids[#sids + 1] = sid .. "-" .. j + end - status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids) - if(status == false) then - stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result)) - else - -- Put the details for each name into an array - -- NOTE: Be sure to mirror any changes here in the next bit! - for j = 1, #lookupsids2_result['names']['names'], 1 do - if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then - local result = {} - result['name'] = lookupsids2_result['names']['names'][j]['name'] - result['rid'] = 500 + j - 1 - result['domain'] = domain - result['type'] = lookupsids2_result['names']['names'][j]['sid_type'] - result['typestr'] = lsa_SidType_tostr(result['type']) - result['source'] = "LSA Bruteforce" - table.insert(response, result) - end - end - end + status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids) + if(status == false) then + stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result)) + else + -- Put the details for each name into an array + -- NOTE: Be sure to mirror any changes here in the next bit! + for j = 1, #lookupsids2_result['names']['names'], 1 do + if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then + local result = {} + result['name'] = lookupsids2_result['names']['names'][j]['name'] + result['rid'] = 500 + j - 1 + result['domain'] = domain + result['type'] = lookupsids2_result['names']['names'][j]['sid_type'] + result['typestr'] = lsa_SidType_tostr(result['type']) + result['source'] = "LSA Bruteforce" + table.insert(response, result) + end + end + end - -- Start at RID 1000 - local start = 1000 - -- Keep track of the number of consecutive empty groups - local empty = 0 - repeat - -- Keep track of the number of names we found in this group - local used_names = 0 + -- Start at RID 1000 + local start = 1000 + -- Keep track of the number of consecutive empty groups + local empty = 0 + repeat + -- Keep track of the number of names we found in this group + local used_names = 0 - local sids = {} - for j = start, start + LSA_GROUPSIZE, 1 do - sids[#sids + 1] = sid .. "-" .. j - end + local sids = {} + for j = start, start + LSA_GROUPSIZE, 1 do + sids[#sids + 1] = sid .. "-" .. j + end - -- Try converting this group of RIDs into names - status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids) - if(status == false) then - stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result)) - else - -- Put the details for each name into an array - for j = 1, #lookupsids2_result['names']['names'], 1 do - -- Determine the RID - local name = lookupsids2_result['names']['names'][j]['name'] - local rid = start + j - 1 - local typenum = lookupsids2_result['names']['names'][j]['sid_type'] - local typestr = lsa_SidType_tostr(typenum) + -- Try converting this group of RIDs into names + status, lookupsids2_result = lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sids) + if(status == false) then + stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result)) + else + -- Put the details for each name into an array + for j = 1, #lookupsids2_result['names']['names'], 1 do + -- Determine the RID + local name = lookupsids2_result['names']['names'][j]['name'] + local rid = start + j - 1 + local typenum = lookupsids2_result['names']['names'][j]['sid_type'] + local typestr = lsa_SidType_tostr(typenum) - -- Check if the username matches the rid (one server we discovered returned every user as valid, - -- this is to prevent that infinite loop) - if(tonumber(name) ~= rid) then - if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then - local result = {} - result['name'] = name - result['rid'] = rid - result['domain'] = domain - result['type'] = typenum - result['typestr'] = typestr - result['source'] = "LSA Bruteforce" - table.insert(response, result) + -- Check if the username matches the rid (one server we discovered returned every user as valid, + -- this is to prevent that infinite loop) + if(tonumber(name) ~= rid) then + if(lookupsids2_result['names']['names'][j]['sid_type'] == "SID_NAME_USER") then + local result = {} + result['name'] = name + result['rid'] = rid + result['domain'] = domain + result['type'] = typenum + result['typestr'] = typestr + result['source'] = "LSA Bruteforce" + table.insert(response, result) - -- Increment the number of names we've found - used_names = used_names + 1 - end - end - end - end + -- Increment the number of names we've found + used_names = used_names + 1 + end + end + end + end - -- Either increment or reset the number of empty groups - if(used_names == 0) then - empty = empty + 1 - else - empty = 0 - end + -- Either increment or reset the number of empty groups + if(used_names == 0) then + empty = empty + 1 + else + empty = 0 + end - -- Go to the next set of RIDs - start = start + LSA_GROUPSIZE - until (status == false or (empty == LSA_MINEMPTY)) - end + -- Go to the next set of RIDs + start = start + LSA_GROUPSIZE + until (status == false or (empty == LSA_MINEMPTY)) + end - -- Close the handle - lsa_close(smbstate, openpolicy2_result['policy_handle']) + -- Close the handle + lsa_close(smbstate, openpolicy2_result['policy_handle']) - stop_smb(smbstate) + stop_smb(smbstate) - return true, response + return true, response end ---Gets the best possible list of user accounts on the remote system using every available method. @@ -4019,275 +4019,275 @@ end --@return (status, result, names) If status is false, result is an error message; otherwise, result -- is an array of users indexed by username and names is a sorted array of names. function get_user_list(host) - local status_samr, result_samr - local status_lsa, result_lsa - local response = {} - local names = {} - local i, v + local status_samr, result_samr + local status_lsa, result_lsa + local response = {} + local names = {} + local i, v - status_lsa, result_lsa = lsa_enum_users(host) - if(status_lsa == false) then - stdnse.print_debug("MSRPC: Failed to enumerate users through LSA: %s", result_lsa) - else - for i = 1, #result_lsa, 1 do - if(result_lsa[i]['name'] ~= nil and result_lsa[i]['type'] == "SID_NAME_USER") then - response[result_lsa[i]['domain'] .. '\\' .. result_lsa[i]['name']] = result_lsa[i] - end - end - end + status_lsa, result_lsa = lsa_enum_users(host) + if(status_lsa == false) then + stdnse.print_debug("MSRPC: Failed to enumerate users through LSA: %s", result_lsa) + else + for i = 1, #result_lsa, 1 do + if(result_lsa[i]['name'] ~= nil and result_lsa[i]['type'] == "SID_NAME_USER") then + response[result_lsa[i]['domain'] .. '\\' .. result_lsa[i]['name']] = result_lsa[i] + end + end + end - status_samr, result_samr = samr_enum_users(host) - if(status_samr == false) then - stdnse.print_debug("MSRPC: Failed to enumerate users through SAMR: %s", result_samr) - else - for i = 1, #result_samr, 1 do - if(result_samr[i]['name'] ~= nil and result_samr[i]['type'] == "SID_NAME_USER") then - response[result_samr[i]['domain'] .. '\\' .. result_samr[i]['name']] = result_samr[i] - end - end - end + status_samr, result_samr = samr_enum_users(host) + if(status_samr == false) then + stdnse.print_debug("MSRPC: Failed to enumerate users through SAMR: %s", result_samr) + else + for i = 1, #result_samr, 1 do + if(result_samr[i]['name'] ~= nil and result_samr[i]['type'] == "SID_NAME_USER") then + response[result_samr[i]['domain'] .. '\\' .. result_samr[i]['name']] = result_samr[i] + end + end + end - if(status_samr == false and status_lsa == false) then - return false, "MSRPC: Couldn't enumerate users; see debug output for more information" - end + if(status_samr == false and status_lsa == false) then + return false, "MSRPC: Couldn't enumerate users; see debug output for more information" + end - for i, v in pairs(response) do - table.insert(names, i) - end - table.sort(names, function(a,b) return a:lower() < b:lower() end ) + for i, v in pairs(response) do + table.insert(names, i) + end + table.sort(names, function(a,b) return a:lower() < b:lower() end ) - return true, response, names + return true, response, names end ---Retrieve information about a domain. This is done by three seperate calls to samr_querydomaininfo2() to get all -- possible information. smbstate has to be in the proper state for this to work. local function get_domain_info(host, domain) - local result = {} - local status, smbstate, bind_result, connect4_result, lookupdomain_result, opendomain_result, enumdomainusers_result + local result = {} + local status, smbstate, bind_result, connect4_result, lookupdomain_result, opendomain_result, enumdomainusers_result - -- Create the SMB session - status, smbstate = start_smb(host, SAMR_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SAMR_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SAMR service - status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) - if(status == false) then - stop_smb(smbstate) - return false, bind_result - end + -- Bind to SAMR service + status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) + if(status == false) then + stop_smb(smbstate) + return false, bind_result + end - -- Call connect4() - status, connect4_result = samr_connect4(smbstate, host.ip) - if(status == false) then - stop_smb(smbstate) - return false, connect4_result - end + -- Call connect4() + status, connect4_result = samr_connect4(smbstate, host.ip) + if(status == false) then + stop_smb(smbstate) + return false, connect4_result + end - -- Call LookupDomain() - status, lookupdomain_result = samr_lookupdomain(smbstate, connect4_result['connect_handle'], domain) - if(status == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, "Couldn't look up the domain: " .. lookupdomain_result - end + -- Call LookupDomain() + status, lookupdomain_result = samr_lookupdomain(smbstate, connect4_result['connect_handle'], domain) + if(status == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, "Couldn't look up the domain: " .. lookupdomain_result + end - -- Call OpenDomain() - status, opendomain_result = samr_opendomain(smbstate, connect4_result['connect_handle'], lookupdomain_result['sid']) - if(status == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, opendomain_result - end + -- Call OpenDomain() + status, opendomain_result = samr_opendomain(smbstate, connect4_result['connect_handle'], lookupdomain_result['sid']) + if(status == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, opendomain_result + end - -- Call QueryDomainInfo2() to get domain properties. We call these for three types -- 1, 8, and 12, since those return - -- the most useful information. - local status_1, querydomaininfo2_result_1 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 1) - local status_8, querydomaininfo2_result_8 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 8) - local status_12, querydomaininfo2_result_12 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 12) + -- Call QueryDomainInfo2() to get domain properties. We call these for three types -- 1, 8, and 12, since those return + -- the most useful information. + local status_1, querydomaininfo2_result_1 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 1) + local status_8, querydomaininfo2_result_8 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 8) + local status_12, querydomaininfo2_result_12 = samr_querydomaininfo2(smbstate, opendomain_result['domain_handle'], 12) - if(status_1 == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, querydomaininfo2_result_1 - end + if(status_1 == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, querydomaininfo2_result_1 + end - if(status_8 == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, querydomaininfo2_result_8 - end + if(status_8 == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, querydomaininfo2_result_8 + end - if(status_12 == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, querydomaininfo2_result_12 - end + if(status_12 == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, querydomaininfo2_result_12 + end - -- Call EnumDomainUsers() to get users - status, enumdomainusers_result = samr_enumdomainusers(smbstate, opendomain_result['domain_handle']) - if(status == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, enumdomainusers_result - end + -- Call EnumDomainUsers() to get users + status, enumdomainusers_result = samr_enumdomainusers(smbstate, opendomain_result['domain_handle']) + if(status == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, enumdomainusers_result + end - -- Call EnumDomainAliases() to get groups - local status, enumdomaingroups_result = samr_enumdomainaliases(smbstate, opendomain_result['domain_handle']) - if(status == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) - return false, enumdomaingroups_result - end + -- Call EnumDomainAliases() to get groups + local status, enumdomaingroups_result = samr_enumdomainaliases(smbstate, opendomain_result['domain_handle']) + if(status == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) + return false, enumdomaingroups_result + end - -- Close the domain handle - samr_close(smbstate, opendomain_result['domain_handle']) - -- Close the smb session - stop_smb(smbstate) + -- Close the domain handle + samr_close(smbstate, opendomain_result['domain_handle']) + -- Close the smb session + stop_smb(smbstate) - -- Create a list of groups - local groups = {} - if(enumdomaingroups_result['sam'] ~= nil and enumdomaingroups_result['sam']['entries'] ~= nil) then - for _, group in ipairs(enumdomaingroups_result['sam']['entries']) do - table.insert(groups, group.name) - end - end + -- Create a list of groups + local groups = {} + if(enumdomaingroups_result['sam'] ~= nil and enumdomaingroups_result['sam']['entries'] ~= nil) then + for _, group in ipairs(enumdomaingroups_result['sam']['entries']) do + table.insert(groups, group.name) + end + end - -- Create the list of users - local names = {} - if(enumdomainusers_result['sam'] ~= nil and enumdomainusers_result['sam']['entries'] ~= nil) then - for _, name in ipairs(enumdomainusers_result['sam']['entries']) do - table.insert(names, name.name) - end - end + -- Create the list of users + local names = {} + if(enumdomainusers_result['sam'] ~= nil and enumdomainusers_result['sam']['entries'] ~= nil) then + for _, name in ipairs(enumdomainusers_result['sam']['entries']) do + table.insert(names, name.name) + end + end - -- Our output table - local response = {} + -- Our output table + local response = {} - -- Finally, start filling in the response! - response['name'] = domain - response['sid'] = lookupdomain_result['sid'] - response['groups'] = groups - response['users'] = names - if(querydomaininfo2_result_8['info']['domain_create_time'] ~= 0) then - response['created'] = os.date("%Y-%m-%d %H:%M:%S", querydomaininfo2_result_8['info']['domain_create_time']) - else - response['created'] = "unknown" - end + -- Finally, start filling in the response! + response['name'] = domain + response['sid'] = lookupdomain_result['sid'] + response['groups'] = groups + response['users'] = names + if(querydomaininfo2_result_8['info']['domain_create_time'] ~= 0) then + response['created'] = os.date("%Y-%m-%d %H:%M:%S", querydomaininfo2_result_8['info']['domain_create_time']) + else + response['created'] = "unknown" + end - -- Password characteristics - response['min_password_length'] = querydomaininfo2_result_1['info']['min_password_length'] - response['max_password_age'] = querydomaininfo2_result_1['info']['max_password_age'] / 60 / 60 / 24 - response['min_password_age'] = querydomaininfo2_result_1['info']['min_password_age'] / 60 / 60 / 24 - response['password_history'] = querydomaininfo2_result_1['info']['password_history_length'] - response['lockout_duration'] = querydomaininfo2_result_12['info']['lockout_duration'] / 60 - response['lockout_threshold'] = querydomaininfo2_result_12['info']['lockout_threshold'] - response['lockout_window'] = querydomaininfo2_result_12['info']['lockout_window'] / 60 + -- Password characteristics + response['min_password_length'] = querydomaininfo2_result_1['info']['min_password_length'] + response['max_password_age'] = querydomaininfo2_result_1['info']['max_password_age'] / 60 / 60 / 24 + response['min_password_age'] = querydomaininfo2_result_1['info']['min_password_age'] / 60 / 60 / 24 + response['password_history'] = querydomaininfo2_result_1['info']['password_history_length'] + response['lockout_duration'] = querydomaininfo2_result_12['info']['lockout_duration'] / 60 + response['lockout_threshold'] = querydomaininfo2_result_12['info']['lockout_threshold'] + response['lockout_window'] = querydomaininfo2_result_12['info']['lockout_window'] / 60 - -- Sanity check the different values, and remove them if they don't appear to be set - if(response['min_password_length'] <= 0) then - response['min_password_length'] = nil - end + -- Sanity check the different values, and remove them if they don't appear to be set + if(response['min_password_length'] <= 0) then + response['min_password_length'] = nil + end - if(response['max_password_age'] < 0 or response['max_password_age'] > 5000) then - response['max_password_age'] = nil - end + if(response['max_password_age'] < 0 or response['max_password_age'] > 5000) then + response['max_password_age'] = nil + end - if(response['min_password_age'] <= 0) then - response['min_password_age'] = nil - end + if(response['min_password_age'] <= 0) then + response['min_password_age'] = nil + end - if(response['password_history'] <= 0) then - response['password_history'] = nil - end + if(response['password_history'] <= 0) then + response['password_history'] = nil + end - if(response['lockout_duration'] <= 0) then - response['lockout_duration'] = nil - end + if(response['lockout_duration'] <= 0) then + response['lockout_duration'] = nil + end - if(response['lockout_threshold'] <= 0) then - response['lockout_threshold'] = nil - end + if(response['lockout_threshold'] <= 0) then + response['lockout_threshold'] = nil + end - if(response['lockout_window'] <= 0) then - response['lockout_window'] = nil - end + if(response['lockout_window'] <= 0) then + response['lockout_window'] = nil + end - local password_properties = querydomaininfo2_result_1['info']['password_properties'] + local password_properties = querydomaininfo2_result_1['info']['password_properties'] - if(#password_properties > 0) then - local password_properties_response = {} - password_properties_response['name'] = "Password properties:" - for j = 1, #password_properties, 1 do - table.insert(password_properties_response, samr_PasswordProperties_tostr(password_properties[j])) - end + if(#password_properties > 0) then + local password_properties_response = {} + password_properties_response['name'] = "Password properties:" + for j = 1, #password_properties, 1 do + table.insert(password_properties_response, samr_PasswordProperties_tostr(password_properties[j])) + end - response['password_properties'] = password_properties_response - end + response['password_properties'] = password_properties_response + end - return true, response + return true, response end function get_domains(host) - local result = {} - local status, smbstate, bind_result, connect4_result, enumdomains_result - local i, j + local result = {} + local status, smbstate, bind_result, connect4_result, enumdomains_result + local i, j - -- Create the SMB session - status, smbstate = start_smb(host, SAMR_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SAMR_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SAMR service - status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) - if(status == false) then - stop_smb(smbstate) - return false, bind_result - end + -- Bind to SAMR service + status, bind_result = bind(smbstate, SAMR_UUID, SAMR_VERSION, nil) + if(status == false) then + stop_smb(smbstate) + return false, bind_result + end - -- Call connect4() - status, connect4_result = samr_connect4(smbstate, host.ip) - if(status == false) then - stop_smb(smbstate) - return false, connect4_result - end + -- Call connect4() + status, connect4_result = samr_connect4(smbstate, host.ip) + if(status == false) then + stop_smb(smbstate) + return false, connect4_result + end - -- Call EnumDomains() - status, enumdomains_result = samr_enumdomains(smbstate, connect4_result['connect_handle']) - if(status == false) then - samr_close(smbstate, connect4_result['connect_handle']) - stop_smb(smbstate) + -- Call EnumDomains() + status, enumdomains_result = samr_enumdomains(smbstate, connect4_result['connect_handle']) + if(status == false) then + samr_close(smbstate, connect4_result['connect_handle']) + stop_smb(smbstate) - return false, enumdomains_result - end + return false, enumdomains_result + end - -- Close the connect handle - samr_close(smbstate, connect4_result['connect_handle']) + -- Close the connect handle + samr_close(smbstate, connect4_result['connect_handle']) - -- Close the SMB session - stop_smb(smbstate) + -- Close the SMB session + stop_smb(smbstate) - -- If no domains were returned, return an error (not sure that this can ever happen, but who knows?) - if(#enumdomains_result['sam']['entries'] == 0) then - return false, "No domains could be found" - end + -- If no domains were returned, return an error (not sure that this can ever happen, but who knows?) + if(#enumdomains_result['sam']['entries'] == 0) then + return false, "No domains could be found" + end - local response = {} - for i = 1, #enumdomains_result['sam']['entries'], 1 do - local domain = enumdomains_result['sam']['entries'][i]['name'] - local status, domain_info = get_domain_info(host, domain) + local response = {} + for i = 1, #enumdomains_result['sam']['entries'], 1 do + local domain = enumdomains_result['sam']['entries'][i]['name'] + local status, domain_info = get_domain_info(host, domain) - if(not(status)) then - return false, "Couldn't get info for the domain: " .. domain_info - else - response[domain] = domain_info - end + if(not(status)) then + return false, "Couldn't get info for the domain: " .. domain_info + else + response[domain] = domain_info + end - end + end - return true, response + return true, response end ---Create a "service" on a remote machine. This service is linked to an executable that is already @@ -4310,55 +4310,55 @@ end --@return (status, err) If status is false, err is an error message; -- otherwise, err is undefined. function service_create(host, servicename, path) - local status, smbstate, bind_result, open_result, create_result, close_result + local status, smbstate, bind_result, open_result, create_result, close_result - stdnse.print_debug(1, "Creating service: %s (%s)", servicename, path) + stdnse.print_debug(1, "Creating service: %s (%s)", servicename, path) - -- Create the SMB session - status, smbstate = start_smb(host, SVCCTL_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SVCCTL_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SVCCTL service - status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SVCCTL service + status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Open the service manager - stdnse.print_debug(2, "Opening the remote service manager") - status, open_result = svcctl_openscmanagerw(smbstate, host.ip) - if(status == false) then - smb.stop(smbstate) - return false, open_result - end + -- Open the service manager + stdnse.print_debug(2, "Opening the remote service manager") + status, open_result = svcctl_openscmanagerw(smbstate, host.ip) + if(status == false) then + smb.stop(smbstate) + return false, open_result + end - -- Create the service - stdnse.print_debug(2, "Creating the service", servicename) - status, create_result = svcctl_createservicew(smbstate, open_result['handle'], servicename, servicename, path) - if(status == false) then - smb.stop(smbstate) - return false, create_result - end - -- Close the handle to the service - status, close_result = svcctl_closeservicehandle(smbstate, create_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Create the service + stdnse.print_debug(2, "Creating the service", servicename) + status, create_result = svcctl_createservicew(smbstate, open_result['handle'], servicename, servicename, path) + if(status == false) then + smb.stop(smbstate) + return false, create_result + end + -- Close the handle to the service + status, close_result = svcctl_closeservicehandle(smbstate, create_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - -- Close the service manager - status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the service manager + status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - smb.stop(smbstate) + smb.stop(smbstate) - return true + return true end ---Start a service on the remote machine based on its name. For example, to start the registry @@ -4374,75 +4374,75 @@ end --@return (status, err) If status is false, err is an error message; -- otherwise, err is undefined. function service_start(host, servicename, args) - local status, smbstate, bind_result, open_result, open_service_result, start_result, close_result, query_result + local status, smbstate, bind_result, open_result, open_service_result, start_result, close_result, query_result - stdnse.print_debug(1, "Starting service: %s", servicename) + stdnse.print_debug(1, "Starting service: %s", servicename) - -- Create the SMB session - status, smbstate = start_smb(host, SVCCTL_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SVCCTL_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SVCCTL service - status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SVCCTL service + status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Open the service manager - stdnse.print_debug(1, "Opening the remote service manager") - status, open_result = svcctl_openscmanagerw(smbstate, host.ip) - if(status == false) then - smb.stop(smbstate) - return false, open_result - end + -- Open the service manager + stdnse.print_debug(1, "Opening the remote service manager") + status, open_result = svcctl_openscmanagerw(smbstate, host.ip) + if(status == false) then + smb.stop(smbstate) + return false, open_result + end - -- Get a handle to the service - stdnse.print_debug(2, "Getting a handle to the service") - status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename) - if(status == false) then - smb.stop(smbstate) - return false, open_service_result - end + -- Get a handle to the service + stdnse.print_debug(2, "Getting a handle to the service") + status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename) + if(status == false) then + smb.stop(smbstate) + return false, open_service_result + end - -- Start it - stdnse.print_debug(2, "Starting the service") - status, start_result = svcctl_startservicew(smbstate, open_service_result['handle'], args) - if(status == false) then - smb.stop(smbstate) - return false, start_result - end + -- Start it + stdnse.print_debug(2, "Starting the service") + status, start_result = svcctl_startservicew(smbstate, open_service_result['handle'], args) + if(status == false) then + smb.stop(smbstate) + return false, start_result + end - -- Wait for it to start (TODO: Check the query result better) - stdnse.print_debug(1, "Waiting for the service to start") - repeat - status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, query_result - end - stdnse.sleep(.5) - until query_result['service_status']['controls_accepted'][1] == "SERVICE_CONTROL_STOP" or query_result['service_status']['state'][1] == "SERVICE_STATE_ACTIVE" + -- Wait for it to start (TODO: Check the query result better) + stdnse.print_debug(1, "Waiting for the service to start") + repeat + status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, query_result + end + stdnse.sleep(.5) + until query_result['service_status']['controls_accepted'][1] == "SERVICE_CONTROL_STOP" or query_result['service_status']['state'][1] == "SERVICE_STATE_ACTIVE" - -- Close the handle to the service - status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the handle to the service + status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - -- Close the service manager - status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the service manager + status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - smb.stop(smbstate) + smb.stop(smbstate) - return true + return true end ---Stop a service on the remote machine based on its name. For example, to stop the registry @@ -4456,75 +4456,75 @@ end --@return (status, err) If status is false, err is an error message; -- otherwise, err is undefined. function service_stop(host, servicename) - local status, smbstate, bind_result, open_result, open_service_result, control_result, close_result, query_result + local status, smbstate, bind_result, open_result, open_service_result, control_result, close_result, query_result - stdnse.print_debug(1, "Stopping service: %s", servicename) + stdnse.print_debug(1, "Stopping service: %s", servicename) - -- Create the SMB session - status, smbstate = start_smb(host, SVCCTL_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SVCCTL_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SVCCTL service - status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SVCCTL service + status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Open the service manager - stdnse.print_debug(2, "Opening the remote service manager") - status, open_result = svcctl_openscmanagerw(smbstate, host.ip) - if(status == false) then - smb.stop(smbstate) - return false, open_result - end + -- Open the service manager + stdnse.print_debug(2, "Opening the remote service manager") + status, open_result = svcctl_openscmanagerw(smbstate, host.ip) + if(status == false) then + smb.stop(smbstate) + return false, open_result + end - -- Get a handle to the service - stdnse.print_debug(2, "Getting a handle to the service") - status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename) - if(status == false) then - smb.stop(smbstate) - return false, open_service_result - end + -- Get a handle to the service + stdnse.print_debug(2, "Getting a handle to the service") + status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename) + if(status == false) then + smb.stop(smbstate) + return false, open_service_result + end - -- Stop it - stdnse.print_debug(2, "Stopping the service") - status, control_result = svcctl_controlservice(smbstate, open_service_result['handle'], "SERVICE_CONTROL_STOP") - if(status == false) then - smb.stop(smbstate) - return false, control_result - end + -- Stop it + stdnse.print_debug(2, "Stopping the service") + status, control_result = svcctl_controlservice(smbstate, open_service_result['handle'], "SERVICE_CONTROL_STOP") + if(status == false) then + smb.stop(smbstate) + return false, control_result + end - -- Wait for it to stop (TODO: Check the query result better) - stdnse.print_debug(2, "Waiting for the service to stop") - repeat - status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, query_result - end - stdnse.sleep(.5) - until query_result['service_status']['controls_accepted'][1] == nil + -- Wait for it to stop (TODO: Check the query result better) + stdnse.print_debug(2, "Waiting for the service to stop") + repeat + status, query_result = svcctl_queryservicestatus(smbstate, open_service_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, query_result + end + stdnse.sleep(.5) + until query_result['service_status']['controls_accepted'][1] == nil - -- Close the handle to the service - status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the handle to the service + status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - -- Close the service manager - status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the service manager + status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - smb.stop(smbstate) + smb.stop(smbstate) - return true + return true end ---Delete a service on the remote machine based on its name. I don't recommend deleting any services that @@ -4535,64 +4535,64 @@ end --@return (status, err) If status is false, err is an error message; -- otherwise, err is undefined. function service_delete(host, servicename) - local status, smbstate, bind_result, open_result, open_service_result, delete_result, close_result + local status, smbstate, bind_result, open_result, open_service_result, delete_result, close_result - stdnse.print_debug(1, "Deleting service: %s", servicename) + stdnse.print_debug(1, "Deleting service: %s", servicename) - -- Create the SMB session - status, smbstate = start_smb(host, SVCCTL_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SVCCTL_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SVCCTL service - status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SVCCTL service + status, bind_result = bind(smbstate, SVCCTL_UUID, SVCCTL_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Open the service manager - stdnse.print_debug(2, "Opening the remote service manager") - status, open_result = svcctl_openscmanagerw(smbstate, host.ip) - if(status == false) then - smb.stop(smbstate) - return false, open_result - end + -- Open the service manager + stdnse.print_debug(2, "Opening the remote service manager") + status, open_result = svcctl_openscmanagerw(smbstate, host.ip) + if(status == false) then + smb.stop(smbstate) + return false, open_result + end - -- Get a handle to the service - stdnse.print_debug(2, "Getting a handle to the service: %s", servicename) - status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename) - if(status == false) then - smb.stop(smbstate) - return false, open_service_result - end + -- Get a handle to the service + stdnse.print_debug(2, "Getting a handle to the service: %s", servicename) + status, open_service_result = svcctl_openservicew(smbstate, open_result['handle'], servicename) + if(status == false) then + smb.stop(smbstate) + return false, open_service_result + end - -- Delete the service - stdnse.print_debug(2, "Deleting the service") - status, delete_result = svcctl_deleteservice(smbstate, open_service_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, delete_result - end + -- Delete the service + stdnse.print_debug(2, "Deleting the service") + status, delete_result = svcctl_deleteservice(smbstate, open_service_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, delete_result + end - -- Close the handle to the service - status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the handle to the service + status, close_result = svcctl_closeservicehandle(smbstate, open_service_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - -- Close the service manager - status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) - if(status == false) then - smb.stop(smbstate) - return false, close_result - end + -- Close the service manager + status, close_result = svcctl_closeservicehandle(smbstate, open_result['handle']) + if(status == false) then + smb.stop(smbstate) + return false, close_result + end - smb.stop(smbstate) + smb.stop(smbstate) - return true + return true end ---Retrieves statistical information about the given server. This function requires administrator privileges @@ -4602,63 +4602,63 @@ end --@return (status, data) If status is false, data is an error message; otherwise, data is a table of information -- about the server. function get_server_stats(host) - local stats - local status - local smbstate + local stats + local status + local smbstate - -- Create the SMB session - status, smbstate = start_smb(host, SRVSVC_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SRVSVC_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SRVSVC service - local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SRVSVC service + local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Call netservergetstatistics for 'server' - local status, netservergetstatistics_result = srvsvc_netservergetstatistics(smbstate, host.ip) - if(status == false) then - smb.stop(smbstate) - return false, netservergetstatistics_result - end + -- Call netservergetstatistics for 'server' + local status, netservergetstatistics_result = srvsvc_netservergetstatistics(smbstate, host.ip) + if(status == false) then + smb.stop(smbstate) + return false, netservergetstatistics_result + end - -- Stop the session - smb.stop(smbstate) + -- Stop the session + smb.stop(smbstate) - -- Build the response - local stats = netservergetstatistics_result['stat'] + -- Build the response + local stats = netservergetstatistics_result['stat'] - -- Convert the date to a string - stats['start_str'] = os.date("%Y-%m-%d %H:%M:%S", stats['start']) + -- Convert the date to a string + stats['start_str'] = os.date("%Y-%m-%d %H:%M:%S", stats['start']) - -- Get the period and convert it to a proper time offset - stats['period'] = os.time() - stats['start'] - if(stats['period'] > 60 * 60 * 24) then - stats['period_str'] = string.format("%dd%dh%02dm%02ds", stats['period'] / (60*60*24), (stats['period'] % (60*60*24)) / 3600, (stats['period'] % 3600) / 60, stats['period'] % 60) - elseif(stats['period'] > 60 * 60) then - stats['period_str'] = string.format("%dh%02dm%02ds", stats['period'] / 3600, (stats['period'] % 3600) / 60, stats['period'] % 60) - else - stats['period_str'] = string.format("%02dm%02ds", stats['period'] / 60, stats['period'] % 60) - end + -- Get the period and convert it to a proper time offset + stats['period'] = os.time() - stats['start'] + if(stats['period'] > 60 * 60 * 24) then + stats['period_str'] = string.format("%dd%dh%02dm%02ds", stats['period'] / (60*60*24), (stats['period'] % (60*60*24)) / 3600, (stats['period'] % 3600) / 60, stats['period'] % 60) + elseif(stats['period'] > 60 * 60) then + stats['period_str'] = string.format("%dh%02dm%02ds", stats['period'] / 3600, (stats['period'] % 3600) / 60, stats['period'] % 60) + else + stats['period_str'] = string.format("%02dm%02ds", stats['period'] / 60, stats['period'] % 60) + end - -- Combine the 64-bit values - stats['bytessent'] = bit.bor(bit.lshift(stats['bytessent_high'], 32), stats['bytessent_low']) - stats['bytesrcvd'] = bit.bor(bit.lshift(stats['bytesrcvd_high'], 32), stats['bytesrcvd_low']) + -- Combine the 64-bit values + stats['bytessent'] = bit.bor(bit.lshift(stats['bytessent_high'], 32), stats['bytessent_low']) + stats['bytesrcvd'] = bit.bor(bit.lshift(stats['bytesrcvd_high'], 32), stats['bytesrcvd_low']) - -- Sidestep divide-by-zero errors (probabyl won't come up, but I'd rather be safe) - if(stats['period'] == 0) then - stats['period'] = 1 - end + -- Sidestep divide-by-zero errors (probabyl won't come up, but I'd rather be safe) + if(stats['period'] == 0) then + stats['period'] = 1 + end - -- Get the bytes/second values - stats['bytessentpersecond'] = stats['bytessent'] / stats['period'] - stats['bytesrcvdpersecond'] = stats['bytesrcvd'] / stats['period'] + -- Get the bytes/second values + stats['bytessentpersecond'] = stats['bytessent'] / stats['period'] + stats['bytesrcvdpersecond'] = stats['bytesrcvd'] / stats['period'] - return true, stats + return true, stats end ---Attempts to enumerate the shares on a remote system using MSRPC calls. Without a user account, @@ -4669,41 +4669,41 @@ end --@return List of shares (if status is true) or an an error string (if status is false). function enum_shares(host) - local status, smbstate - local bind_result, netshareenumall_result - local shares - local i, v + local status, smbstate + local bind_result, netshareenumall_result + local shares + local i, v - -- Create the SMB session - status, smbstate = start_smb(host, SRVSVC_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_smb(host, SRVSVC_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SRVSVC service - status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SRVSVC service + status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Call netsharenumall - status, netshareenumall_result = srvsvc_netshareenumall(smbstate, host.ip) - if(status == false) then - smb.stop(smbstate) - return false, netshareenumall_result - end + -- Call netsharenumall + status, netshareenumall_result = srvsvc_netshareenumall(smbstate, host.ip) + if(status == false) then + smb.stop(smbstate) + return false, netshareenumall_result + end - -- Stop the SMB session - smb.stop(smbstate) + -- Stop the SMB session + smb.stop(smbstate) - -- Convert the share list to an array - shares = {} - for i, v in pairs(netshareenumall_result['ctr']['array']) do - shares[#shares + 1] = v['name'] - end + -- Convert the share list to an array + shares = {} + for i, v in pairs(netshareenumall_result['ctr']['array']) do + shares[#shares + 1] = v['name'] + end - return true, shares + return true, shares end @@ -4715,54 +4715,54 @@ end --@return A table of information about the share (if status is true) or an an error string (if -- status is false). function get_share_info(host, name) - local response = {} + local response = {} - -- Create the SMB session - local status, smbstate = start_smb(host, SRVSVC_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + local status, smbstate = start_smb(host, SRVSVC_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to SRVSVC service - local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) - if(status == false) then - smb.stop(smbstate) - return false, bind_result - end + -- Bind to SRVSVC service + local status, bind_result = bind(smbstate, SRVSVC_UUID, SRVSVC_VERSION, nil) + if(status == false) then + smb.stop(smbstate) + return false, bind_result + end - -- Call NetShareGetInfo - local status, netsharegetinfo_result = srvsvc_netsharegetinfo(smbstate, host.ip, name, 2) - if(status == false) then - smb.stop(smbstate) - return false, netsharegetinfo_result - end + -- Call NetShareGetInfo + local status, netsharegetinfo_result = srvsvc_netsharegetinfo(smbstate, host.ip, name, 2) + if(status == false) then + smb.stop(smbstate) + return false, netsharegetinfo_result + end - smb.stop(smbstate) + smb.stop(smbstate) - return true, netsharegetinfo_result + return true, netsharegetinfo_result end --####################################################################-- ---# 1) RRAS RASRPC INTERFACE +--# 1) RRAS RASRPC INTERFACE --####################################################################-- ROUTER_PATH = "\\router" --also can be reached across "\\srvsvc" pipe in WinXP RASRPC_UUID = string.char(0x36, 0x00, 0x61, 0x20, 0x22, 0xfa, 0xcf, 0x11, 0x98, 0x23, 0x00, 0xa0, 0xc9, 0x11, 0xe5, 0xdf) RASRPC_VERSION = 1 --####################################################################-- ---# 2) RRAS RASRPC TYPES +--# 2) RRAS RASRPC TYPES --####################################################################-- --####################################################################-- --typedef enum _ReqTypes{ --- REQTYPE_PORTENUM = 21,//Request to enumerate all the port information on the RRAS. --- REQTYPE_GETINFO = 22,//Request to get information about a specific port on the RRAS. --- REQTYPE_GETDEVCONFIG = 73,//Request to get device information on the RRAS. --- REQTYPE_SETDEVICECONFIGINFO = 94,//Request to set device configuration information on RRAS. --- REQTYPE_GETDEVICECONFIGINFO = 95,//Request to get device configuration information on RRAS. --- REQTYPE_GETCALLEDID = 105,//Request to get CalledId information for a specific device on RRAS. --- REQTYPE_SETCALLEDID = 106,//Request to set CalledId information for a specific device on RRAS. --- REQTYPE_GETNDISWANDRIVERCAPS = 111//Request to get the encryption capabilities of the RRAS. +-- REQTYPE_PORTENUM = 21,//Request to enumerate all the port information on the RRAS. +-- REQTYPE_GETINFO = 22,//Request to get information about a specific port on the RRAS. +-- REQTYPE_GETDEVCONFIG = 73,//Request to get device information on the RRAS. +-- REQTYPE_SETDEVICECONFIGINFO = 94,//Request to set device configuration information on RRAS. +-- REQTYPE_GETDEVICECONFIGINFO = 95,//Request to get device configuration information on RRAS. +-- REQTYPE_GETCALLEDID = 105,//Request to get CalledId information for a specific device on RRAS. +-- REQTYPE_SETCALLEDID = 106,//Request to set CalledId information for a specific device on RRAS. +-- REQTYPE_GETNDISWANDRIVERCAPS = 111//Request to get the encryption capabilities of the RRAS. --} ReqTypes; --- The ReqTypes enumerations indicate the different types of message requests that can be passed in --the RB_ReqType field of RequestBuffer structure. @@ -4780,12 +4780,12 @@ RRAS_RegTypes['GETNDISWANDRIVERCAPS'] = 111 --####################################################################-- --typedef struct _RequestBuffer { --- DWORD RB_PCBIndex;//A unique identifier for the port. --- ReqTypes RB_Reqtype;//A ReqTypes enumeration value indicating the request type sent to the server. --- DWORD RB_Dummy;//MUST be set to the size of the ULONG_PTR on the client. --- DWORD RB_Done;//MBZ --- LONGLONG Alignment;//MBZ --- BYTE RB_Buffer[1];//variable size +-- DWORD RB_PCBIndex;//A unique identifier for the port. +-- ReqTypes RB_Reqtype;//A ReqTypes enumeration value indicating the request type sent to the server. +-- DWORD RB_Dummy;//MUST be set to the size of the ULONG_PTR on the client. +-- DWORD RB_Done;//MBZ +-- LONGLONG Alignment;//MBZ +-- BYTE RB_Buffer[1];//variable size --} RequestBuffer; --- The RequestBuffer is a generic information container used by the RasRpcSubmitRequest --method to set or retrieve information on RRAS server. This method performs @@ -4795,22 +4795,22 @@ RRAS_RegTypes['GETNDISWANDRIVERCAPS'] = 111 -- * [MS-RRASM] 2.2.1.2.218 RequestBuffer --####################################################################-- function RRAS_marshall_RequestBuffer(RB_PCBIndex, RB_ReqType, RB_Buffer) - local rb_blob, RB_Dummy, RB_Done, Alignment - RB_Dummy = 4 - RB_Done = 0 - Alignment = 0 - rb_blob = bin.pack("3.3.4.5 RasRpcSubmitRequest (Opnum 12) --####################################################################-- function RRAS_SubmitRequest(smbstate, pReqBuffer, dwcbBufSize) - --sanity check - if(dwcbBufSize == nil) then - dwcbBufSize = #pReqBuffer - end - --pack the request - local req_blob - --[in, out, unique, size_is(dwcbBufSize) PBYTE pReqBuffer, - req_blob = bin.pack("3.1.1.1 DNS Server Configuration Information DNSSERVER_ConfInfo = - { - DNSSERVER_IntProp = {}, - DNSSERVER_AddrArrProp = {}, - DNSSERVER_StrProp = {}, - DNSSERVER_StrLstProp = {} - } +{ + DNSSERVER_IntProp = {}, + DNSSERVER_AddrArrProp = {}, + DNSSERVER_StrProp = {}, + DNSSERVER_StrLstProp = {} +} --####################################################################-- ---# 3) DNS SERVER MANAGEMENT SERVICE OPERATIONS +--# 3) DNS SERVER MANAGEMENT SERVICE OPERATIONS --####################################################################-- local DNSSERVER_DEBUG_LVL = 2 --debug level for dnsserver operations when calling stdnse.print_debug @@ -4961,75 +4961,75 @@ LONG R_DnssrvQuery( -- * [MS-DNSP] 3.1.4.2 R_DnssrvQuery (Opnum 1) --####################################################################-- function DNSSERVER_Query(smbstate, server_name, zone, operation) - local status - --call - local req_blob, srv_name_utf16, zone_ascii, operation_ascii - --[in, unique, string] LPCWSTR pwszServerName, - local unique_ptr - unique_ptr = 0x00020000 - srv_name_utf16 = msrpctypes.string_to_unicode(server_name, true) - req_blob = bin.pack("(align-datalen%align)%align. --####################################################################-- function get_pad(data, align, pad_byte) - pad_byte = pad_byte or "\00" - return string.rep(pad_byte, (align-#data%align)%align) + pad_byte = pad_byte or "\00" + return string.rep(pad_byte, (align-#data%align)%align) end --####################################################################-- @@ -5051,13 +5051,13 @@ end --@return The random string. --####################################################################-- function random_crap(length, charset) - charset = charset or "0123456789abcdefghijklmnoprstuvzxwyABCDEFGHIJKLMNOPRSTUVZXWY" - local random_str = "" - for i = 1, length, 1 do - local random = math.random(#charset) - random_str = random_str .. string.sub(charset, random, random) - end - return random_str + charset = charset or "0123456789abcdefghijklmnoprstuvzxwyABCDEFGHIJKLMNOPRSTUVZXWY" + local random_str = "" + for i = 1, length, 1 do + local random = math.random(#charset) + random_str = random_str .. string.sub(charset, random, random) + end + return random_str end diff --git a/nselib/msrpcperformance.lua b/nselib/msrpcperformance.lua index 2325c2473..17021a4cc 100644 --- a/nselib/msrpcperformance.lua +++ b/nselib/msrpcperformance.lua @@ -31,59 +31,59 @@ _ENV = stdnse.module("msrpcperformance", stdnse.seeall) --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_title_database(data, pos) - local result = {} - local i = 1 + local result = {} + local i = 1 - repeat - local number, name - pos, number, name = bin.unpack("= #data + result[tonumber(number)] = name + i = i + 1 + until pos >= #data - return true, pos, result + return true, pos, result end ---Parses a PERF_DATA_BLOCK, which has the following definition (from "WinPerf.h" on Visual Studio 8): -- -- --- typedef struct _PERF_DATA_BLOCK { --- WCHAR Signature[4]; // Signature: Unicode "PERF" --- DWORD LittleEndian; // 0 = Big Endian, 1 = Little Endian --- DWORD Version; // Version of these data structures --- // starting at 1 --- DWORD Revision; // Revision of these data structures --- // starting at 0 for each Version --- DWORD TotalByteLength; // Total length of data block --- DWORD HeaderLength; // Length of this structure --- DWORD NumObjectTypes; // Number of types of objects --- // being reported --- LONG DefaultObject; // Object Title Index of default --- // object to display when data from --- // this system is retrieved (-1 = --- // none, but this is not expected to --- // be used) --- SYSTEMTIME SystemTime; // Time at the system under --- // measurement --- LARGE_INTEGER PerfTime; // Performance counter value --- // at the system under measurement --- LARGE_INTEGER PerfFreq; // Performance counter frequency --- // at the system under measurement --- LARGE_INTEGER PerfTime100nSec; // Performance counter time in 100 nsec --- // units at the system under measurement --- DWORD SystemNameLength; // Length of the system name --- DWORD SystemNameOffset; // Offset, from beginning of this --- // structure, to name of system --- // being measured --- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK; +-- typedef struct _PERF_DATA_BLOCK { +-- WCHAR Signature[4]; // Signature: Unicode "PERF" +-- DWORD LittleEndian; // 0 = Big Endian, 1 = Little Endian +-- DWORD Version; // Version of these data structures +-- // starting at 1 +-- DWORD Revision; // Revision of these data structures +-- // starting at 0 for each Version +-- DWORD TotalByteLength; // Total length of data block +-- DWORD HeaderLength; // Length of this structure +-- DWORD NumObjectTypes; // Number of types of objects +-- // being reported +-- LONG DefaultObject; // Object Title Index of default +-- // object to display when data from +-- // this system is retrieved (-1 = +-- // none, but this is not expected to +-- // be used) +-- SYSTEMTIME SystemTime; // Time at the system under +-- // measurement +-- LARGE_INTEGER PerfTime; // Performance counter value +-- // at the system under measurement +-- LARGE_INTEGER PerfFreq; // Performance counter frequency +-- // at the system under measurement +-- LARGE_INTEGER PerfTime100nSec; // Performance counter time in 100 nsec +-- // units at the system under measurement +-- DWORD SystemNameLength; // Length of the system name +-- DWORD SystemNameOffset; // Offset, from beginning of this +-- // structure, to name of system +-- // being measured +-- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK; -- -- --@param data The data being processed. @@ -91,47 +91,47 @@ end --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_data_block(data, pos) - local result = {} + local result = {} - pos, result['Signature'] = msrpctypes.unicode_to_string(data, pos, 4, false) - if(result['Signature'] ~= "PERF") then - return false, "MSRPC: PERF_DATA_BLOCK signature is missing or incorrect" - end + pos, result['Signature'] = msrpctypes.unicode_to_string(data, pos, 4, false) + if(result['Signature'] ~= "PERF") then + return false, "MSRPC: PERF_DATA_BLOCK signature is missing or incorrect" + end - pos, result['LittleEndian'] = msrpctypes.unmarshall_int32(data, pos) - if(result['LittleEndian'] ~= 1) then - return false, "MSRPC: PERF_DATA_BLOCK returned a non-understood endianness" - end + pos, result['LittleEndian'] = msrpctypes.unmarshall_int32(data, pos) + if(result['LittleEndian'] ~= 1) then + return false, "MSRPC: PERF_DATA_BLOCK returned a non-understood endianness" + end - -- Parse the header - pos, result['Version'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['Revision'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['NumObjectTypes'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['DefaultObject'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['SystemTime'] = msrpctypes.unmarshall_SYSTEMTIME(data, pos) - pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) - pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) - pos, result['PerfTime100nSec'] = msrpctypes.unmarshall_int64(data, pos) - pos = pos + 4 -- This value doesn't seem to line up, so add 4 + -- Parse the header + pos, result['Version'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['Revision'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['NumObjectTypes'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['DefaultObject'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['SystemTime'] = msrpctypes.unmarshall_SYSTEMTIME(data, pos) + pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) + pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) + pos, result['PerfTime100nSec'] = msrpctypes.unmarshall_int64(data, pos) + pos = pos + 4 -- This value doesn't seem to line up, so add 4 - pos, result['SystemNameLength'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['SystemNameOffset'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['SystemNameLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['SystemNameOffset'] = msrpctypes.unmarshall_int32(data, pos) - -- Ensure that the system name is directly after the header. This technically shouldn't matter, but Microsoft's documentation - -- (in WinPref.h) says that the actual object comes "after the PERF_DATA_BLOCK", so it doesn't make sense that the SystemName - -- could be anywhere else. - if(pos ~= result['SystemNameOffset'] + 1) then - return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location" - end + -- Ensure that the system name is directly after the header. This technically shouldn't matter, but Microsoft's documentation + -- (in WinPref.h) says that the actual object comes "after the PERF_DATA_BLOCK", so it doesn't make sense that the SystemName + -- could be anywhere else. + if(pos ~= result['SystemNameOffset'] + 1) then + return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location" + end - -- Read the system name from the next location (which happens to be identical to SystemNameOffset, on a proper system) - pos, result['SystemName'] = msrpctypes.unicode_to_string(data, pos, result['SystemNameLength'] / 2, true) + -- Read the system name from the next location (which happens to be identical to SystemNameOffset, on a proper system) + pos, result['SystemName'] = msrpctypes.unicode_to_string(data, pos, result['SystemNameLength'] / 2, true) - pos = pos + 4 -- Again, we end up not lined up so here we fix it + pos = pos + 4 -- Again, we end up not lined up so here we fix it - return true, pos, result + return true, pos, result end @@ -144,74 +144,74 @@ end -- // type section begins with a _PERF_OBJECT_TYPE structure. -- // -- typedef struct _PERF_OBJECT_TYPE { --- DWORD TotalByteLength; // Length of this object definition --- // including this structure, the --- // counter definitions, and the --- // instance definitions and the --- // counter blocks for each instance: --- // This is the offset from this --- // structure to the next object, if --- // any --- DWORD DefinitionLength; // Length of object definition, --- // which includes this structure --- // and the counter definition --- // structures for this object: this --- // is the offset of the first --- // instance or of the counters --- // for this object if there is --- // no instance --- DWORD HeaderLength; // Length of this structure: this --- // is the offset to the first --- // counter definition for this --- // object --- DWORD ObjectNameTitleIndex; --- // Index to name in Title Database +-- DWORD TotalByteLength; // Length of this object definition +-- // including this structure, the +-- // counter definitions, and the +-- // instance definitions and the +-- // counter blocks for each instance: +-- // This is the offset from this +-- // structure to the next object, if +-- // any +-- DWORD DefinitionLength; // Length of object definition, +-- // which includes this structure +-- // and the counter definition +-- // structures for this object: this +-- // is the offset of the first +-- // instance or of the counters +-- // for this object if there is +-- // no instance +-- DWORD HeaderLength; // Length of this structure: this +-- // is the offset to the first +-- // counter definition for this +-- // object +-- DWORD ObjectNameTitleIndex; +-- // Index to name in Title Database -- #ifdef _WIN64 --- DWORD ObjectNameTitle; // Should use this as an offset +-- DWORD ObjectNameTitle; // Should use this as an offset -- #else --- LPWSTR ObjectNameTitle; // Initially NULL, for use by --- // analysis program to point to --- // retrieved title string +-- LPWSTR ObjectNameTitle; // Initially NULL, for use by +-- // analysis program to point to +-- // retrieved title string -- #endif --- DWORD ObjectHelpTitleIndex; --- // Index to Help in Title Database +-- DWORD ObjectHelpTitleIndex; +-- // Index to Help in Title Database -- #ifdef _WIN64 --- DWORD ObjectHelpTitle; // Should use this as an offset +-- DWORD ObjectHelpTitle; // Should use this as an offset -- #else --- LPWSTR ObjectHelpTitle; // Initially NULL, for use by --- // analysis program to point to --- // retrieved title string +-- LPWSTR ObjectHelpTitle; // Initially NULL, for use by +-- // analysis program to point to +-- // retrieved title string -- #endif --- DWORD DetailLevel; // Object level of detail (for --- // controlling display complexity); --- // will be min of detail levels --- // for all this object's counters --- DWORD NumCounters; // Number of counters in each --- // counter block (one counter --- // block per instance) --- LONG DefaultCounter; // Default counter to display when --- // this object is selected, index --- // starting at 0 (-1 = none, but --- // this is not expected to be used) --- LONG NumInstances; // Number of object instances --- // for which counters are being --- // returned from the system under --- // measurement. If the object defined --- // will never have any instance data --- // structures (PERF_INSTANCE_DEFINITION) --- // then this value should be -1, if the --- // object can have 0 or more instances, --- // but has none present, then this --- // should be 0, otherwise this field --- // contains the number of instances of --- // this counter. --- DWORD CodePage; // 0 if instance strings are in --- // UNICODE, else the Code Page of --- // the instance names --- LARGE_INTEGER PerfTime; // Sample Time in "Object" units --- // --- LARGE_INTEGER PerfFreq; // Frequency of "Object" units in --- // counts per second. +-- DWORD DetailLevel; // Object level of detail (for +-- // controlling display complexity); +-- // will be min of detail levels +-- // for all this object's counters +-- DWORD NumCounters; // Number of counters in each +-- // counter block (one counter +-- // block per instance) +-- LONG DefaultCounter; // Default counter to display when +-- // this object is selected, index +-- // starting at 0 (-1 = none, but +-- // this is not expected to be used) +-- LONG NumInstances; // Number of object instances +-- // for which counters are being +-- // returned from the system under +-- // measurement. If the object defined +-- // will never have any instance data +-- // structures (PERF_INSTANCE_DEFINITION) +-- // then this value should be -1, if the +-- // object can have 0 or more instances, +-- // but has none present, then this +-- // should be 0, otherwise this field +-- // contains the number of instances of +-- // this counter. +-- DWORD CodePage; // 0 if instance strings are in +-- // UNICODE, else the Code Page of +-- // the instance names +-- LARGE_INTEGER PerfTime; // Sample Time in "Object" units +-- // +-- LARGE_INTEGER PerfFreq; // Frequency of "Object" units in +-- // counts per second. -- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE; -- -- @@ -220,69 +220,69 @@ end --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_object_type(data, pos) - local result = {} + local result = {} - pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the next object - pos, result['DefinitionLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first instance (or counter, if no instances) - pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first counter definition - pos, result['ObjectNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Title Database - pos, result['ObjectNameTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this work with 64-bit? - pos, result['ObjectHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Help Database - pos, result['ObjectHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this workw ith 64-bit? - pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['NumCounters'] = msrpctypes.unmarshall_int32(data, pos) -- The number of counters in each counter block - pos, result['DefaultCounter'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['NumInstances'] = msrpctypes.unmarshall_int32(data, pos) -- Numer of object instances for which counters are being returned - pos, result['CodePage'] = msrpctypes.unmarshall_int32(data, pos) -- 0 if strings are in UNICODE, otherwise the Code Page --- if(result['CodePage'] ~= 0) then --- return false, string.format("Unknown Code Page for data: %d\n", result['CodePage']) --- end - pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) -- Sample time in "Object" units - pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) -- Frequency of "Object" units in counts/second + pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the next object + pos, result['DefinitionLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first instance (or counter, if no instances) + pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) -- Offset to the first counter definition + pos, result['ObjectNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Title Database + pos, result['ObjectNameTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this work with 64-bit? + pos, result['ObjectHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) -- Index in the Help Database + pos, result['ObjectHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this workw ith 64-bit? + pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['NumCounters'] = msrpctypes.unmarshall_int32(data, pos) -- The number of counters in each counter block + pos, result['DefaultCounter'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['NumInstances'] = msrpctypes.unmarshall_int32(data, pos) -- Numer of object instances for which counters are being returned + pos, result['CodePage'] = msrpctypes.unmarshall_int32(data, pos) -- 0 if strings are in UNICODE, otherwise the Code Page + -- if(result['CodePage'] ~= 0) then + -- return false, string.format("Unknown Code Page for data: %d\n", result['CodePage']) + -- end + pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) -- Sample time in "Object" units + pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) -- Frequency of "Object" units in counts/second - return true, pos, result + return true, pos, result end ---Parse a PERF_COUNTER_DEFINITION structure. From Microsoft's documentation: -- -- --- // There is one of the following for each of the --- // PERF_OBJECT_TYPE.NumCounters. The Unicode names in this structure MUST --- // come from a message file. --- typedef struct _PERF_COUNTER_DEFINITION { --- DWORD ByteLength; // Length in bytes of this structure --- DWORD CounterNameTitleIndex; --- // Index of Counter name into --- // Title Database --- #ifdef _WIN64 --- DWORD CounterNameTitle; --- #else --- LPWSTR CounterNameTitle; // Initially NULL, for use by --- // analysis program to point to --- // retrieved title string --- #endif --- DWORD CounterHelpTitleIndex; --- // Index of Counter Help into --- // Title Database --- #ifdef _WIN64 --- DWORD CounterHelpTitle; --- #else --- LPWSTR CounterHelpTitle; // Initially NULL, for use by --- // analysis program to point to --- // retrieved title string --- #endif --- LONG DefaultScale; // Power of 10 by which to scale --- // chart line if vertical axis is 100 --- // 0 ==> 1, 1 ==> 10, -1 ==>1/10, etc. --- DWORD DetailLevel; // Counter level of detail (for --- // controlling display complexity) --- DWORD CounterType; // Type of counter --- DWORD CounterSize; // Size of counter in bytes --- DWORD CounterOffset; // Offset from the start of the --- // PERF_COUNTER_BLOCK to the first --- // byte of this counter --- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION; +-- // There is one of the following for each of the +-- // PERF_OBJECT_TYPE.NumCounters. The Unicode names in this structure MUST +-- // come from a message file. +-- typedef struct _PERF_COUNTER_DEFINITION { +-- DWORD ByteLength; // Length in bytes of this structure +-- DWORD CounterNameTitleIndex; +-- // Index of Counter name into +-- // Title Database +-- #ifdef _WIN64 +-- DWORD CounterNameTitle; +-- #else +-- LPWSTR CounterNameTitle; // Initially NULL, for use by +-- // analysis program to point to +-- // retrieved title string +-- #endif +-- DWORD CounterHelpTitleIndex; +-- // Index of Counter Help into +-- // Title Database +-- #ifdef _WIN64 +-- DWORD CounterHelpTitle; +-- #else +-- LPWSTR CounterHelpTitle; // Initially NULL, for use by +-- // analysis program to point to +-- // retrieved title string +-- #endif +-- LONG DefaultScale; // Power of 10 by which to scale +-- // chart line if vertical axis is 100 +-- // 0 ==> 1, 1 ==> 10, -1 ==>1/10, etc. +-- DWORD DetailLevel; // Counter level of detail (for +-- // controlling display complexity) +-- DWORD CounterType; // Type of counter +-- DWORD CounterSize; // Size of counter in bytes +-- DWORD CounterOffset; // Offset from the start of the +-- // PERF_COUNTER_BLOCK to the first +-- // byte of this counter +-- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION; -- -- --@param data The data being processed. @@ -290,23 +290,23 @@ end --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_counter_definition(data, pos) - local result = {} - local initial_pos = pos + local result = {} + local initial_pos = pos - pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterNameTitle'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['DefaultScale'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterType'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterSize'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['CounterOffset'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterNameTitle'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['DefaultScale'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterType'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterSize'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['CounterOffset'] = msrpctypes.unmarshall_int32(data, pos) - pos = initial_pos + result['ByteLength'] + pos = initial_pos + result['ByteLength'] - return true, pos, result + return true, pos, result end ---Parse the actual counter value. This is a fairly simple function, it takes a counter @@ -321,57 +321,57 @@ end --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_counter(data, pos, counter_definition) - local result + local result - if(counter_definition['CounterSize'] == 4) then - pos, result = msrpctypes.unmarshall_int32(data, pos) - elseif(counter_definition['CounterSize'] == 8) then - pos, result = msrpctypes.unmarshall_int64(data, pos) --- pos, result = bin.unpack(" --- // If (PERF_DATA_BLOCK.NumInstances >= 0) then there will be --- // PERF_DATA_BLOCK.NumInstances of a (PERF_INSTANCE_DEFINITION --- // followed by a PERF_COUNTER_BLOCK followed by the counter data fields) --- // for each instance. --- // --- // If (PERF_DATA_BLOCK.NumInstances < 0) then the counter definition --- // strucutre above will be followed by only a PERF_COUNTER_BLOCK and the --- // counter data for that COUNTER. --- typedef struct _PERF_INSTANCE_DEFINITION { --- DWORD ByteLength; // Length in bytes of this structure, --- // including the subsequent name --- DWORD ParentObjectTitleIndex; --- // Title Index to name of "parent" --- // object (e.g., if thread, then --- // process is parent object type); --- // if logical drive, the physical --- // drive is parent object type --- DWORD ParentObjectInstance; --- // Index to instance of parent object --- // type which is the parent of this --- // instance. --- LONG UniqueID; // A unique ID used instead of --- // matching the name to identify --- // this instance, -1 = none --- DWORD NameOffset; // Offset from beginning of --- // this struct to the Unicode name --- // of this instance --- DWORD NameLength; // Length in bytes of name; 0 = none --- // this length includes the characters --- // in the string plus the size of the --- // terminating NULL char. It does not --- // include any additional pad bytes to --- // correct structure alignment --- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION; +-- // If (PERF_DATA_BLOCK.NumInstances >= 0) then there will be +-- // PERF_DATA_BLOCK.NumInstances of a (PERF_INSTANCE_DEFINITION +-- // followed by a PERF_COUNTER_BLOCK followed by the counter data fields) +-- // for each instance. +-- // +-- // If (PERF_DATA_BLOCK.NumInstances < 0) then the counter definition +-- // strucutre above will be followed by only a PERF_COUNTER_BLOCK and the +-- // counter data for that COUNTER. +-- typedef struct _PERF_INSTANCE_DEFINITION { +-- DWORD ByteLength; // Length in bytes of this structure, +-- // including the subsequent name +-- DWORD ParentObjectTitleIndex; +-- // Title Index to name of "parent" +-- // object (e.g., if thread, then +-- // process is parent object type); +-- // if logical drive, the physical +-- // drive is parent object type +-- DWORD ParentObjectInstance; +-- // Index to instance of parent object +-- // type which is the parent of this +-- // instance. +-- LONG UniqueID; // A unique ID used instead of +-- // matching the name to identify +-- // this instance, -1 = none +-- DWORD NameOffset; // Offset from beginning of +-- // this struct to the Unicode name +-- // of this instance +-- DWORD NameLength; // Length in bytes of name; 0 = none +-- // this length includes the characters +-- // in the string plus the size of the +-- // terminating NULL char. It does not +-- // include any additional pad bytes to +-- // correct structure alignment +-- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION; -- -- --@param data The data being processed. @@ -379,33 +379,33 @@ end --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_instance_definition(data, pos) - local result = {} + local result = {} - -- Remember where we started. I noticed that where the counter part starts can move around, so we have to - -- determine it by adding ByteLength to the initial position - local initial_pos = pos + -- Remember where we started. I noticed that where the counter part starts can move around, so we have to + -- determine it by adding ByteLength to the initial position + local initial_pos = pos - pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['ParentObjectTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['ParentObjectInstance'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['UniqueID'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['NameOffset'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['NameLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['ParentObjectTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['ParentObjectInstance'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['UniqueID'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['NameOffset'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['NameLength'] = msrpctypes.unmarshall_int32(data, pos) - pos, result['InstanceName'] = msrpctypes.unicode_to_string(data, pos, result['NameLength'] / 2, true) + pos, result['InstanceName'] = msrpctypes.unicode_to_string(data, pos, result['NameLength'] / 2, true) - pos = initial_pos + result['ByteLength'] + pos = initial_pos + result['ByteLength'] - return true, pos, result + return true, pos, result end ---Parse a PERF_COUNTER_BLOCK structure. From Microsoft's documentation: -- -- --- typedef struct _PERF_COUNTER_BLOCK { --- DWORD ByteLength; // Length in bytes of this structure, --- // including the following counters --- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK; +-- typedef struct _PERF_COUNTER_BLOCK { +-- DWORD ByteLength; // Length in bytes of this structure, +-- // including the following counters +-- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK; -- -- -- @@ -414,11 +414,11 @@ end --@return (status, pos, result) The status (true if successful), the new position in data (or an error -- message), and a table representing the datatype, if any. local function parse_perf_counter_block(data, pos) - local result = {} + local result = {} - pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) + pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) - return true, pos, result + return true, pos, result end ---Retrieve the parsed performance data from the given host for the requested object values. To get a list of possible @@ -430,186 +430,186 @@ end --@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database. function get_performance_data(host, objects) - -- Create the SMB session - local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH) + if(status == false) then + return false, smbstate + end - -- Bind to WINREG service - local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, bind_result - end + -- Bind to WINREG service + local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, bind_result + end - -- Open HKEY_PERFORMANCE_DATA - local status, openhkpd_result = msrpc.winreg_openhkpd(smbstate) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, openhkpd_result - end + -- Open HKEY_PERFORMANCE_DATA + local status, openhkpd_result = msrpc.winreg_openhkpd(smbstate) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, openhkpd_result + end - local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], "Counter 009") - if(status == false) then - msrpc.stop_smb(smbstate) - return false, queryvalue_result - end + local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], "Counter 009") + if(status == false) then + msrpc.stop_smb(smbstate) + return false, queryvalue_result + end - -- Parse the title database - local pos = 1 - local status - local result = {} - status, pos, result['title_database'] = parse_perf_title_database(queryvalue_result['value'], pos) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end - result['title_database'][0] = "" + -- Parse the title database + local pos = 1 + local status + local result = {} + status, pos, result['title_database'] = parse_perf_title_database(queryvalue_result['value'], pos) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end + result['title_database'][0] = "" - if(objects ~= nil and #objects > 0) then - -- Query for the objects - local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], objects) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, queryvalue_result - end + if(objects ~= nil and #objects > 0) then + -- Query for the objects + local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], objects) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, queryvalue_result + end - -- Parse the header - pos = 1 - local status, data_block - status, pos, data_block = parse_perf_data_block(queryvalue_result['value'], pos) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end + -- Parse the header + pos = 1 + local status, data_block + status, pos, data_block = parse_perf_data_block(queryvalue_result['value'], pos) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end - -- Move past the header - pos = 1 + data_block['HeaderLength'] + -- Move past the header + pos = 1 + data_block['HeaderLength'] - -- Parse the data sections - for i = 1, data_block['NumObjectTypes'], 1 do - local object_start = pos + -- Parse the data sections + for i = 1, data_block['NumObjectTypes'], 1 do + local object_start = pos - local counter_definitions = {} - local object_instances = {} - local counter_definitions = {} + local counter_definitions = {} + local object_instances = {} + local counter_definitions = {} - -- Get the type of the object (this is basically the class definition -- info about the object instances) - local status, object_type - status, pos, object_type = parse_perf_object_type(queryvalue_result['value'], pos) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end + -- Get the type of the object (this is basically the class definition -- info about the object instances) + local status, object_type + status, pos, object_type = parse_perf_object_type(queryvalue_result['value'], pos) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end - -- Start setting up the result object ---stdnse.print_debug("Index = %d\n", object_type['ObjectNameTitleIndex']) - local object_name = result['title_database'][object_type['ObjectNameTitleIndex']] - result[object_name] = {} + -- Start setting up the result object + --stdnse.print_debug("Index = %d\n", object_type['ObjectNameTitleIndex']) + local object_name = result['title_database'][object_type['ObjectNameTitleIndex']] + result[object_name] = {} ---stdnse.print_debug("\n\nOBJECT: %s\n", object_name) ---stdnse.print_debug(" Counters: %d\n", object_type['NumCounters']) ---stdnse.print_debug(" Instances: %d\n", object_type['NumInstances']) ---stdnse.print_debug("-----------------\n") + --stdnse.print_debug("\n\nOBJECT: %s\n", object_name) + --stdnse.print_debug(" Counters: %d\n", object_type['NumCounters']) + --stdnse.print_debug(" Instances: %d\n", object_type['NumInstances']) + --stdnse.print_debug("-----------------\n") - -- Bring the position to the beginning of the counter definitions - pos = object_start + object_type['HeaderLength'] + -- Bring the position to the beginning of the counter definitions + pos = object_start + object_type['HeaderLength'] - -- Parse the counter definitions - for j = 1, object_type['NumCounters'], 1 do - status, pos, counter_definitions[j] = parse_perf_counter_definition(queryvalue_result['value'], pos) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end ---stdnse.print_debug(" Counter definition #%2d: [%d bytes] %s\n", j, counter_definitions[j]['CounterSize'], result['title_database'][counter_definitions[j]['CounterNameTitleIndex']]) - end + -- Parse the counter definitions + for j = 1, object_type['NumCounters'], 1 do + status, pos, counter_definitions[j] = parse_perf_counter_definition(queryvalue_result['value'], pos) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end + --stdnse.print_debug(" Counter definition #%2d: [%d bytes] %s\n", j, counter_definitions[j]['CounterSize'], result['title_database'][counter_definitions[j]['CounterNameTitleIndex']]) + end - -- Bring the position to the beginning of the instances (or counters) - pos = object_start + object_type['DefinitionLength'] + -- Bring the position to the beginning of the instances (or counters) + pos = object_start + object_type['DefinitionLength'] - -- Check if we have any instances (sometimes we don't -- if we don't, the value returned is a negative) - if(bit.band(object_type['NumInstances'], 0x80000000) == 0) then - -- Parse the object instances and counters - for j = 1, object_type['NumInstances'], 1 do - local instance_start = pos + -- Check if we have any instances (sometimes we don't -- if we don't, the value returned is a negative) + if(bit.band(object_type['NumInstances'], 0x80000000) == 0) then + -- Parse the object instances and counters + for j = 1, object_type['NumInstances'], 1 do + local instance_start = pos - -- Instance definition - local status - status, pos, object_instances[j] = parse_perf_instance_definition(queryvalue_result['value'], pos) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end + -- Instance definition + local status + status, pos, object_instances[j] = parse_perf_instance_definition(queryvalue_result['value'], pos) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end - -- Set up the instance array - local instance_name = object_instances[j]['InstanceName'] - result[object_name][instance_name] = {} + -- Set up the instance array + local instance_name = object_instances[j]['InstanceName'] + result[object_name][instance_name] = {} - -- Bring the pos to the start of the counter block - pos = instance_start + object_instances[j]['ByteLength'] + -- Bring the pos to the start of the counter block + pos = instance_start + object_instances[j]['ByteLength'] ---stdnse.print_debug("\n INSTANCE: %s\n", instance_name) ---stdnse.print_debug(" Length: %d\n", object_instances[j]['ByteLength']) ---stdnse.print_debug(" NameOffset: %d\n", object_instances[j]['NameOffset']) ---stdnse.print_debug(" NameLength: %d\n", object_instances[j]['NameLength']) ---stdnse.print_debug(" --------------\n") + --stdnse.print_debug("\n INSTANCE: %s\n", instance_name) + --stdnse.print_debug(" Length: %d\n", object_instances[j]['ByteLength']) + --stdnse.print_debug(" NameOffset: %d\n", object_instances[j]['NameOffset']) + --stdnse.print_debug(" NameLength: %d\n", object_instances[j]['NameLength']) + --stdnse.print_debug(" --------------\n") - -- The counter block - local status, counter_block - status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end + -- The counter block + local status, counter_block + status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end - for k = 1, object_type['NumCounters'], 1 do - -- Each individual counter - local status, counter_result - status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k]) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end + for k = 1, object_type['NumCounters'], 1 do + -- Each individual counter + local status, counter_result + status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k]) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end - local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']] ---stdnse.print_debug(" %s: %s\n", counter_name, counter_result) + local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']] + --stdnse.print_debug(" %s: %s\n", counter_name, counter_result) - -- Save it in the result - result[object_name][instance_name][counter_name] = counter_result - end + -- Save it in the result + result[object_name][instance_name][counter_name] = counter_result + end - -- Bring the pos to the end of the next section - pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength'] - end - else - for k = 1, object_type['NumCounters'], 1 do - -- Each individual counter - local status, counter_result - status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k]) - if(status == false) then - msrpc.stop_smb(smbstate) - return false, pos - end + -- Bring the pos to the end of the next section + pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength'] + end + else + for k = 1, object_type['NumCounters'], 1 do + -- Each individual counter + local status, counter_result + status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k]) + if(status == false) then + msrpc.stop_smb(smbstate) + return false, pos + end - local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']] ---stdnse.print_debug(" %s: %s\n", counter_name, counter_result) + local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']] + --stdnse.print_debug(" %s: %s\n", counter_name, counter_result) - -- Save it in the result - result[object_name][counter_name] = counter_result - end - end - end + -- Save it in the result + result[object_name][counter_name] = counter_result + end + end + end - -- Blank out the database - result['title_database'] = nil - end + -- Blank out the database + result['title_database'] = nil + end - msrpc.stop_smb(smbstate) + msrpc.stop_smb(smbstate) - return true, result + return true, result end diff --git a/nselib/msrpctypes.lua b/nselib/msrpctypes.lua index 3c7f5a015..8005d67cd 100644 --- a/nselib/msrpctypes.lua +++ b/nselib/msrpctypes.lua @@ -124,43 +124,43 @@ local ALL = 'ALL' --@param do_null [optional] Add a null-terminator to the unicode string. Default false. --@return The unicode version of the string. function string_to_unicode(string, do_null) - local i - local result = "" + local i + local result = "" - stdnse.print_debug(4, "MSRPC: Entering string_to_unicode(string = %s)", string) + stdnse.print_debug(4, "MSRPC: Entering string_to_unicode(string = %s)", string) - if(do_null == nil) then - do_null = false - end + if(do_null == nil) then + do_null = false + end - -- Try converting the value to a string - if(type(string) ~= 'string') then - string = tostring(string) - end + -- Try converting the value to a string + if(type(string) ~= 'string') then + string = tostring(string) + end - if(string == nil) then - stdnse.print_debug(1, "MSRPC: WARNING: couldn't convert value to string in string_to_unicode()") - end + if(string == nil) then + stdnse.print_debug(1, "MSRPC: WARNING: couldn't convert value to string in string_to_unicode()") + end - -- Loop through the string, adding each character followed by a char(0) - for i = 1, #string, 1 do - result = result .. string.sub(string, i, i) .. string.char(0) - end + -- Loop through the string, adding each character followed by a char(0) + for i = 1, #string, 1 do + result = result .. string.sub(string, i, i) .. string.char(0) + end - -- Add a null, if the caller requestd it - if(do_null == true) then - result = result .. string.char(0) .. string.char(0) - end + -- Add a null, if the caller requestd it + if(do_null == true) then + result = result .. string.char(0) .. string.char(0) + end - -- Align it to a multiple of 4, if necessary - if(#result % 4 ~= 0) then - result = result .. string.char(0) .. string.char(0) - end + -- Align it to a multiple of 4, if necessary + if(#result % 4 ~= 0) then + result = result .. string.char(0) .. string.char(0) + end - stdnse.print_debug(4, "MSRPC: Leaving string_to_unicode()") + stdnse.print_debug(4, "MSRPC: Leaving string_to_unicode()") - return result + return result end --- Read a unicode string from a buffer, similar to how bin.unpack would, optionally eat the null terminator, @@ -171,50 +171,50 @@ end --@param length The number of ascii characters that will be read (including the null, if do_null is set). --@param do_null [optional] Remove a null terminator from the string as the last character. Default false. --@return (pos, string) The new position and the string read, again imitating bin.unpack. If there was an --- attempt to read off the end of the string, then 'nil' is returned for both parameters. +-- attempt to read off the end of the string, then 'nil' is returned for both parameters. function unicode_to_string(buffer, pos, length, do_null) - local i, j, ch, dummy - local string = "" + local i, j, ch, dummy + local string = "" - stdnse.print_debug(4, string.format("MSRPC: Entering unicode_to_string(pos = %d, length = %d)", pos, length)) + stdnse.print_debug(4, string.format("MSRPC: Entering unicode_to_string(pos = %d, length = %d)", pos, length)) - if(do_null == nil) then - do_null = false - end + if(do_null == nil) then + do_null = false + end - if(do_null == true and length > 0) then - length = length - 1 - end + if(do_null == true and length > 0) then + length = length - 1 + end - for j = 1, length, 1 do + for j = 1, length, 1 do - pos, ch, dummy = bin.unpack("false for null pointers. For BODY or ALL, the result is -- nil for null pointers, or the data for valid pointers. local function unmarshall_ptr(location, data, pos, func, args, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_ptr()")) - if(args == nil) then - args = {} - end - -- If we're unmarshalling the header, then pull off a referent_id. - if(location == HEAD or location == ALL) then - local referent_id - pos, referent_id = bin.unpack("marshall_ptr, except that this marshalls a type that isn't a pointer. @@ -371,18 +371,18 @@ end --@param args An array of arguments that will be directly passed to the function func --@return A string representing the marshalled data. local function marshall_basetype(location, func, args) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_basetype()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_basetype()")) - if(location == HEAD or location == ALL) then - result = bin.pack("unmarshall_array. This allows the same @@ -508,19 +508,19 @@ end --@param args Arbitrary arguments to pass to the function. --@return (pos, result) The new position and the result of unmarshalling this value. local function unmarshall_struct(data, pos, func, args) - local result + local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_struct()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_struct()")) - if(args == nil) then - args = {} - end + if(args == nil) then + args = {} + end - pos, result = func(ALL, data, pos, nil, args) + pos, result = func(ALL, data, pos, nil, args) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_struct()")) + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_struct()")) - return pos, result + return pos, result end ------------------------------------- @@ -541,35 +541,35 @@ end -- is in characters, not bytes. --@return A string representing the marshalled data. function marshall_unicode(str, do_null, max_length) - local buffer_length - local result + local buffer_length + local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_unicode()")) + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_unicode()")) - if(do_null == nil) then - do_null = false - end + if(do_null == nil) then + do_null = false + end - if(do_null) then - buffer_length = #str + 1 - else - buffer_length = #str - end + if(do_null) then + buffer_length = #str + 1 + else + buffer_length = #str + end - if(max_length == nil) then - max_length = buffer_length - end + if(max_length == nil) then + max_length = buffer_length + end - result = bin.pack("marshall_unicode_array @@ -711,24 +711,24 @@ end --@param do_null [optional] Appends a null to the end of the string. Default false. --@return A string representing the marshalled data. function marshall_unicode_array_ptr(strings, do_null) - local result + local result - result = marshall_ptr(ALL, marshall_unicode_array, {strings, do_null}, strings) + result = marshall_ptr(ALL, marshall_unicode_array, {strings, do_null}, strings) - return result + return result end --- Marshall an int64. This is simply an 8-byte integer inserted into the buffer, nothing fancy. --@param int64 The integer to insert --@return A string representing the marshalled data. function marshall_int64(int64) - local result + local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int64()")) - result = bin.pack("marshall_int64 for more information. @@ -817,16 +817,16 @@ end --@param pos The position within data. --@return (pos, int64) The new position, and the value. function unmarshall_int64(data, pos) - local value + local value - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int64()")) - pos, value = bin.unpack("marshall_int32 for more information. @@ -835,14 +835,14 @@ end --@param pos The position within data. --@return (pos, int32) The new position, and the value. function unmarshall_int32(data, pos) - local value + local value - pos, value = bin.unpack("marshall_int16 for more information. @@ -852,22 +852,22 @@ end --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true --@return (pos, int16) The new position, and the value. function unmarshall_int16(data, pos, pad) - local value + local value - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16()")) - pos, value = bin.unpack("marshall_int8 for more information. @@ -877,22 +877,22 @@ end --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true --@return (pos, int8) The new position, and the value. function unmarshall_int8(data, pos, pad) - local value + local value - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8()")) - pos, value = bin.unpack("marshall_int32_ptr for more information. @@ -969,13 +969,13 @@ end --@param pos The position within the data. --@return (pos, int32) The new position, and the value. function unmarshall_int32_ptr(data, pos) - local result + local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int32_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int32, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int32_ptr()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int32_ptr()")) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int32, {}) + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int32_ptr()")) - return pos, result + return pos, result end --- Unmarshall a pointer to an int16. See marshall_int16_ptr for more information. @@ -985,13 +985,13 @@ end --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true --@return (pos, int16) The new position, and the value. function unmarshall_int16_ptr(data, pos, pad) - local result + local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int16, {pad}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int16_ptr()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int16_ptr()")) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int16, {pad}) + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int16_ptr()")) - return pos, result + return pos, result end --- Unmarshall a pointer to an int8. See marshall_int8_ptr for more information. @@ -1001,13 +1001,13 @@ end --@param pad [optional] If set, will remove extra bytes to align the packet, Default: true --@return (pos, int8) The new position, and the value. function unmarshall_int8_ptr(data, pos, pad) - local result + local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int8, {pad}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int8_ptr()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_ptr()")) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_int8, {pad}) + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int8_ptr()")) - return pos, result + return pos, result end --- Marshall an array of int8s, with an optional max_length set. @@ -1017,19 +1017,19 @@ end -- data. --@return A string representing the marshalled data. function marshall_int8_array(data, max_length) - local result = "" + local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array()")) + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array()")) - if(max_length == nil) then - max_length = #data - end + if(max_length == nil) then + max_length = #data + end - result = result .. bin.pack("data. --@return A string representing the marshalled data. function marshall_int8_array_ptr(data, max_length) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_int8_array_ptr()")) - result = marshall_ptr(ALL, marshall_int8_array, {data, max_length}, data) + result = marshall_ptr(ALL, marshall_int8_array, {data, max_length}, data) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_int8_array_ptr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_int8_array_ptr()")) + return result end --- Unmarshall a pointer to an array of int8s. By default, aligns the result to 4-byte @@ -1092,13 +1092,13 @@ end -- true. --@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int8_array_ptr(data, pos, pad) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_array_ptr()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_int8_array_ptr()")) - pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_int8_array, {pad}) + pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_int8_array, {pad}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int8_array_ptr()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_int8_array_ptr()")) + return pos, str end --- Unmarshall an array of int32s. @@ -1107,16 +1107,16 @@ end --@param pos The position within the data. --@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int32_array(data, pos, count) - local maxcount - local result = {} + local maxcount + local result = {} - pos, maxcount = unmarshall_int32(data, pos) + pos, maxcount = unmarshall_int32(data, pos) - for i = 1, count, 1 do - pos, result[i] = unmarshall_int32(data, pos) - end + for i = 1, count, 1 do + pos, result[i] = unmarshall_int32(data, pos) + end - return pos, result + return pos, result end --- Unmarshall a pointer to an array of int32s. @@ -1125,12 +1125,12 @@ end --@param pos The position within the data. --@return (pos, str) The position, and the resulting string, which cannot be nil. function unmarshall_int32_array_ptr(data, pos) - local count, array + local count, array - pos, count = unmarshall_int32(data, pos) - pos, array = unmarshall_ptr(ALL, data, pos, unmarshall_int32_array, {count}) + pos, count = unmarshall_int32(data, pos) + pos, array = unmarshall_ptr(ALL, data, pos, unmarshall_int32_array, {count}) - return pos, array + return pos, array end ---Marshalls an NTTIME. This is sent as the number of 1/10 microseconds since 1601; however @@ -1141,17 +1141,17 @@ end --@param time The time, in seconds since 1970. --@return A string representing the marshalled data. function marshall_NTTIME(time) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_NTTIME()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_NTTIME()")) - if(time == 0) then - result = bin.pack("marshall_NTTIME for more information. @@ -1160,20 +1160,20 @@ end --@param pos The position within the data. --@return (pos, time) The new position, and the time in seconds since 1970. function unmarshall_NTTIME(data, pos) - local time - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME()")) + local time + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME()")) - pos, time = bin.unpack("NTTIME*. @@ -1196,44 +1196,44 @@ end --@param pos The position within the data. --@return (pos, time) The new position, and the time in seconds since 1970. function unmarshall_NTTIME_ptr(data, pos) - local time - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME_ptr()")) + local time + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_NTTIME_ptr()")) - pos, time = unmarshall_ptr(ALL, data, pos, unmarshall_NTTIME, {}) + pos, time = unmarshall_ptr(ALL, data, pos, unmarshall_NTTIME, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_NTTIME_ptr()")) - return pos, time + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_NTTIME_ptr()")) + return pos, time end ---Unmarshall a SYSTEMTIME structure, converting it to a standard representation. The structure is a -- follows: -- -- --- typedef struct _SYSTEMTIME { --- WORD wYear; --- WORD wMonth; --- WORD wDayOfWeek; --- WORD wDay; --- WORD wHour; --- WORD wMinute; --- WORD wSecond; --- WORD wMilliseconds; --- } SYSTEMTIME +-- typedef struct _SYSTEMTIME { +-- WORD wYear; +-- WORD wMonth; +-- WORD wDayOfWeek; +-- WORD wDay; +-- WORD wHour; +-- WORD wMinute; +-- WORD wSecond; +-- WORD wMilliseconds; +-- } SYSTEMTIME -- -- --@param data The data packet. --@param pos The position within the data. --@return (pos, time) The new position, and the time in seconds since 1970. function unmarshall_SYSTEMTIME(data, pos) - local date = {} - local _ + local date = {} + local _ - pos, date['year'], date['month'], _, date['day'], date['hour'], date['min'], date['sec'], _ = bin.unpack("hyper. I have no idea what a hyper is, just that it seems @@ -1244,14 +1244,14 @@ end --@param pos The position within the data. --@return (pos, val) The new position, and the result in seconds. function unmarshall_hyper(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_hyper()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_hyper()")) - pos, result = unmarshall_int64(data, pos) - result = result / -10000000 + pos, result = unmarshall_int64(data, pos) + result = result / -10000000 - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_hyper()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_hyper()")) + return pos, result end ---Marshall an entry in a table. Basically, converts the string to a number based on the entries in @@ -1263,20 +1263,20 @@ end -- the numbers. --@return A string representing the marshalled data. local function marshall_Enum32(val, table) - local result = 0 - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum32()")) + local result = 0 + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum32()")) - local vals = stdnse.strsplit("|", val) - local i + local vals = stdnse.strsplit("|", val) + local i - for i = 1, #vals, 1 do - result = bit.bor(result, table[vals[i]]) - end + for i = 1, #vals, 1 do + result = bit.bor(result, table[vals[i]]) + end - result = marshall_int32(result) + result = marshall_int32(result) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum32()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum32()")) + return result end ---Unmarshall an entry in a table. Basically, converts the next int32 in the buffer to a string @@ -1289,22 +1289,22 @@ end --@param default The default value to return if the lookup was unsuccessful. --@return (pos, policy_handle) The new position, and a table representing the policy_handle. local function unmarshall_Enum32(data, pos, table, default) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32()")) - if(default == nil) then - default = "" - end + if(default == nil) then + default = "" + end - local pos, val = unmarshall_int32(data, pos) + local pos, val = unmarshall_int32(data, pos) - for i, v in pairs(table) do - if(v == val) then - return pos, i - end - end + for i, v in pairs(table) do + if(v == val) then + return pos, i + end + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum32()")) - return pos, default + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum32()")) + return pos, default end ---Unmarshall an entry in a table. Basically, converts the next int16 in the buffer to a string @@ -1318,22 +1318,22 @@ end --@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true. --@return (pos, policy_handle) The new position, and a table representing the policy_handle. local function unmarshall_Enum16(data, pos, table, default, pad) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum16()")) + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum16()")) - if(default == nil) then - default = "" - end + if(default == nil) then + default = "" + end - local pos, val = unmarshall_int16(data, pos, pad) + local pos, val = unmarshall_int16(data, pos, pad) - for i, v in pairs(table) do - if(v == val) then - return pos, i - end - end + for i, v in pairs(table) do + if(v == val) then + return pos, i + end + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum16()")) - return pos, default + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum16()")) + return pos, default end ---Marshall an entry in a table. Basically, converts the string to a number based on the entries in @@ -1346,20 +1346,20 @@ end --@param pad [optional] If set, will ensure that we end up on an even multiple of 4. Default: true. --@return A string representing the marshalled data. local function marshall_Enum8(val, table, pad) - local result = 0 - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum8()")) + local result = 0 + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_Enum8()")) - local vals = stdnse.strsplit("|", val) - local i + local vals = stdnse.strsplit("|", val) + local i - for i = 1, #vals, 1 do - result = bit.bor(result, table[vals[i]]) - end + for i = 1, #vals, 1 do + result = bit.bor(result, table[vals[i]]) + end - result = marshall_int8(result, pad) + result = marshall_int8(result, pad) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum8()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_Enum8()")) + return result end @@ -1372,21 +1372,21 @@ end -- the numbers. --@return (pos, array) The new position, and a table representing the enumeration values. local function unmarshall_Enum32_array(data, pos, table) - local array = {} - local i, v - local val - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32_array()")) + local array = {} + local i, v + local val + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_Enum32_array()")) - pos, val = unmarshall_int32(data, pos) + pos, val = unmarshall_int32(data, pos) - for i, v in pairs(table) do - if(bit.band(v, val) ~= 0) then - array[#array + 1] = i - end - end + for i, v in pairs(table) do + if(bit.band(v, val) ~= 0) then + array[#array + 1] = i + end + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum32_array()")) - return pos, array + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_Enum32_array()")) + return pos, array end ---Unmarshall raw data. @@ -1395,16 +1395,16 @@ end --@param length The number of bytes to unmarshall. --@return (pos, data) The new position in the packet, and a string representing the raw data. function unmarshall_raw(data, pos, length) - local val - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_raw()")) + local val + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_raw()")) - pos, val = bin.unpack(string.format("A%d", length), data, pos) - if(val == nil) then - stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_raw(). Please report!") - end + pos, val = bin.unpack(string.format("A%d", length), data, pos) + if(val == nil) then + stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_raw(). Please report!") + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_raw()")) - return pos, val + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_raw()")) + return pos, val end @@ -1416,25 +1416,25 @@ end ---Marshalls a GUID, which looks like this: -- -- --- typedef [public,noprint,gensize,noejs] struct { --- uint32 time_low; --- uint16 time_mid; --- uint16 time_hi_and_version; --- uint8 clock_seq[2]; --- uint8 node[6]; --- } GUID; +-- typedef [public,noprint,gensize,noejs] struct { +-- uint32 time_low; +-- uint16 time_mid; +-- uint16 time_hi_and_version; +-- uint8 clock_seq[2]; +-- uint8 node[6]; +-- } GUID; -- -- --@param guid A table representing the GUID. --@return A string representing the marshalled data. local function marshall_guid(guid) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_guid()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_guid()")) - result = bin.pack("marshall_guid for the structure. @@ -1443,37 +1443,37 @@ end --@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_guid(data, pos) - local guid = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_guid()")) + local guid = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_guid()")) - pos, guid['time_low'], guid['time_high'], guid['time_hi_and_version'], guid['clock_seq'], guid['node'] = bin.unpack(" --- typedef struct { --- uint32 handle_type; --- GUID uuid; --- } policy_handle; +-- typedef struct { +-- uint32 handle_type; +-- GUID uuid; +-- } policy_handle; -- -- --@param policy_handle The policy_handle to marshall. --@return A string representing the marshalled data. function marshall_policy_handle(policy_handle) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_policy_handle()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_policy_handle()")) - result = bin.pack("marshall_policy_handle for the structure. @@ -1482,14 +1482,14 @@ end --@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_policy_handle(data, pos) - local policy_handle = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_policy_handle()")) + local policy_handle = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_policy_handle()")) - pos, policy_handle['handle_type'] = unmarshall_int32(data, pos) - pos, policy_handle['uuid'] = unmarshall_guid(data, pos) + pos, policy_handle['handle_type'] = unmarshall_int32(data, pos) + pos, policy_handle['uuid'] = unmarshall_guid(data, pos) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_policy_handle()")) - return pos, policy_handle + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_policy_handle()")) + return pos, policy_handle end ---------------------------------- @@ -1512,33 +1512,33 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_dom_sid2(data, pos) - local i + local i - -- Read the SID from the packet - local sid = {} - pos, sid['count'] = unmarshall_int32(data, pos) - pos, sid['sid_rev_num'] = unmarshall_int8(data, pos, false) - pos, sid['num_auths'] = unmarshall_int8(data, pos, false) + -- Read the SID from the packet + local sid = {} + pos, sid['count'] = unmarshall_int32(data, pos) + pos, sid['sid_rev_num'] = unmarshall_int8(data, pos, false) + pos, sid['num_auths'] = unmarshall_int8(data, pos, false) - -- Note that authority is big endian (I guess it's an array, not really an integer like we're handling it) - pos, sid['authority_high'], sid['authority_low'] = bin.unpack(">SI", data, pos) - if(sid['authority_low'] == nil) then - stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_dom_sid2(). Please report!") - end - sid['authority'] = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority_low']) + -- Note that authority is big endian (I guess it's an array, not really an integer like we're handling it) + pos, sid['authority_high'], sid['authority_low'] = bin.unpack(">SI", data, pos) + if(sid['authority_low'] == nil) then + stdnse.print_debug(1, "MSRPC: ERROR: Ran off the end of a packet in unmarshall_dom_sid2(). Please report!") + end + sid['authority'] = bit.bor(bit.lshift(sid['authority_high'], 32), sid['authority_low']) - sid['sub_auths'] = {} - for i = 1, sid['num_auths'], 1 do - pos, sid['sub_auths'][i] = unmarshall_int32(data, pos) - end + sid['sub_auths'] = {} + for i = 1, sid['num_auths'], 1 do + pos, sid['sub_auths'][i] = unmarshall_int32(data, pos) + end - -- Convert the SID to a string - local result = string.format("S-%u-%u", sid['sid_rev_num'], sid['authority']) - for i = 1, sid['num_auths'], 1 do - result = result .. string.format("-%u", sid['sub_auths'][i]) - end + -- Convert the SID to a string + local result = string.format("S-%u-%u", sid['sid_rev_num'], sid['authority']) + for i = 1, sid['num_auths'], 1 do + result = result .. string.format("-%u", sid['sub_auths'][i]) + end - return pos, result + return pos, result end ---Unmarshall a pointer to a dom_sid2 struct. See the unmarshall_dom_sid2 function @@ -1548,7 +1548,7 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_dom_sid2_ptr(data, pos) - return unmarshall_ptr(ALL, data, pos, unmarshall_dom_sid2, {}) + return unmarshall_ptr(ALL, data, pos, unmarshall_dom_sid2, {}) end ---Marshall a struct with the following definition: @@ -1564,54 +1564,54 @@ end -- --@return A string representing the marshalled data. function marshall_dom_sid2(sid) - local i - local pos_next - local sid_array = {} - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_dom_sid2()")) + local i + local pos_next + local sid_array = {} + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_dom_sid2()")) - if(string.find(sid, "^S%-") == nil) then - stdnse.print_debug(1, "MSRPC: ERROR: Invalid SID encountered: %s\n", sid) - return nil - end - if(string.find(sid, "%-%d+$") == nil) then - stdnse.print_debug(1, "MSRPC: ERROR: Invalid SID encountered: %s\n", sid) - return nil - end + if(string.find(sid, "^S%-") == nil) then + stdnse.print_debug(1, "MSRPC: ERROR: Invalid SID encountered: %s\n", sid) + return nil + end + if(string.find(sid, "%-%d+$") == nil) then + stdnse.print_debug(1, "MSRPC: ERROR: Invalid SID encountered: %s\n", sid) + return nil + end - local pos = 3 + local pos = 3 - pos_next = string.find(sid, "-", pos) - sid_array['sid_rev_num'] = string.sub(sid, pos, pos_next - 1) + pos_next = string.find(sid, "-", pos) + sid_array['sid_rev_num'] = string.sub(sid, pos, pos_next - 1) - pos = pos_next + 1 - pos_next = string.find(sid, "-", pos) - sid_array['authority_high'] = bit.rshift(string.sub(sid, pos, pos_next - 1), 32) - sid_array['authority_low'] = bit.band(string.sub(sid, pos, pos_next - 1), 0xFFFFFFFF) + pos = pos_next + 1 + pos_next = string.find(sid, "-", pos) + sid_array['authority_high'] = bit.rshift(string.sub(sid, pos, pos_next - 1), 32) + sid_array['authority_low'] = bit.band(string.sub(sid, pos, pos_next - 1), 0xFFFFFFFF) - sid_array['sub_auths'] = {} - i = 1 - repeat - pos = pos_next + 1 - pos_next = string.find(sid, "-", pos) - if(pos_next == nil) then - sid_array['sub_auths'][i] = string.sub(sid, pos) - else - sid_array['sub_auths'][i] = string.sub(sid, pos, pos_next - 1) - end - i = i + 1 - until pos_next == nil - sid_array['num_auths'] = i - 1 + sid_array['sub_auths'] = {} + i = 1 + repeat + pos = pos_next + 1 + pos_next = string.find(sid, "-", pos) + if(pos_next == nil) then + sid_array['sub_auths'][i] = string.sub(sid, pos) + else + sid_array['sub_auths'][i] = string.sub(sid, pos, pos_next - 1) + end + i = i + 1 + until pos_next == nil + sid_array['num_auths'] = i - 1 - result = bin.pack("SI", sid_array['sid_rev_num'], sid_array['num_auths'], sid_array['authority_high'], sid_array['authority_low']) - for i = 1, sid_array['num_auths'], 1 do - result = result .. bin.pack("SI", sid_array['sid_rev_num'], sid_array['num_auths'], sid_array['authority_high'], sid_array['authority_low']) + for i = 1, sid_array['num_auths'], 1 do + result = result .. bin.pack("lsa_String value. See marshall_lsa_String_internal for more information. @@ -1696,23 +1696,23 @@ end -- anything. --@return (pos, str) The new position, and the unmarshalled string. local function unmarshall_lsa_String_internal(location, data, pos, result) - local length, size - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_String_internal()")) + local length, size + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_String_internal()")) - if(location == HEAD or location == ALL) then - pos, length = unmarshall_int16(data, pos, false) - pos, size = unmarshall_int16(data, pos, false) + if(location == HEAD or location == ALL) then + pos, length = unmarshall_int16(data, pos, false) + pos, size = unmarshall_int16(data, pos, false) - pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false}) - end + pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false}) + end - if(location == BODY or location == ALL) then - pos, str = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {false}, result) - end + if(location == BODY or location == ALL) then + pos, str = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {false}, result) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_String_internal()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_String_internal()")) + return pos, str end ---Public version of marshall_lsa_String_internal -- see that function on that for more information. @@ -1723,13 +1723,13 @@ end -- Defaults to the length of the string, including the null. --@return A string representing the marshalled data. function marshall_lsa_String(str, max_length) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String()")) - result = marshall_lsa_String_internal(ALL, str, max_length) + result = marshall_lsa_String_internal(ALL, str, max_length) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_String()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_String()")) + return result end ---Marshall an array of lsa_String objects. This is a perfect demonstration of how to use @@ -1738,72 +1738,72 @@ end --@param strings The array of strings to marshall --@return A string representing the marshalled data. function marshall_lsa_String_array(strings) - local array = {} - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String_array()")) + local array = {} + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_String_array()")) - for i = 1, #strings, 1 do - array[i] = {} - array[i]['func'] = marshall_lsa_String_internal - array[i]['args'] = {strings[i]} - end + for i = 1, #strings, 1 do + array[i] = {} + array[i]['func'] = marshall_lsa_String_internal + array[i]['args'] = {strings[i]} + end - result = marshall_array(array) + result = marshall_array(array) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_String_array()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_String_array()")) + return result end ---Basically the same as marshall_lsa_String_array, except it has a different structure -- --@param strings The array of strings to marshall function marshall_lsa_String_array2(strings) - local array = {} - local result + local array = {} + local result - for i = 1, #strings, 1 do - array[i] = {} - array[i]['func'] = marshall_lsa_String_internal - array[i]['args'] = {strings[i], nil, nil, false} - end + for i = 1, #strings, 1 do + array[i] = {} + array[i]['func'] = marshall_lsa_String_internal + array[i]['args'] = {strings[i], nil, nil, false} + end - result = marshall_int32(1000) -- Max length - result = result .. marshall_int32(0) -- Offset - result = result .. marshall_array(array) + result = marshall_int32(1000) -- Max length + result = result .. marshall_int32(0) -- Offset + result = result .. marshall_array(array) ---require 'nsedebug' ---nsedebug.print_hex(result) ---os.exit() - return result + --require 'nsedebug' + --nsedebug.print_hex(result) + --os.exit() + return result end ---Table of SID types. local lsa_SidType = { - SID_NAME_USE_NONE = 0, -- NOTUSED - SID_NAME_USER = 1, -- user - SID_NAME_DOM_GRP = 2, -- domain group - SID_NAME_DOMAIN = 3, -- domain: don't know what this is - SID_NAME_ALIAS = 4, -- local group - SID_NAME_WKN_GRP = 5, -- well-known group - SID_NAME_DELETED = 6, -- deleted account: needed for c2 rating - SID_NAME_INVALID = 7, -- invalid account - SID_NAME_UNKNOWN = 8, -- oops. - SID_NAME_COMPUTER = 9 -- machine + SID_NAME_USE_NONE = 0, -- NOTUSED + SID_NAME_USER = 1, -- user + SID_NAME_DOM_GRP = 2, -- domain group + SID_NAME_DOMAIN = 3, -- domain: don't know what this is + SID_NAME_ALIAS = 4, -- local group + SID_NAME_WKN_GRP = 5, -- well-known group + SID_NAME_DELETED = 6, -- deleted account: needed for c2 rating + SID_NAME_INVALID = 7, -- invalid account + SID_NAME_UNKNOWN = 8, -- oops. + SID_NAME_COMPUTER = 9 -- machine } ---String versions of SID types local lsa_SidType_str = { - SID_NAME_USE_NONE = "n/a", - SID_NAME_USER = "User", - SID_NAME_DOM_GRP = "Domain group", - SID_NAME_DOMAIN = "Domain", - SID_NAME_ALIAS = "Local group", - SID_NAME_WKN_GRP = "Well known group", - SID_NAME_DELETED = "Deleted account", - SID_NAME_INVALID = "Invalid account", - SID_NAME_UNKNOWN = "Unknown account", - SID_NAME_COMPUTER = "Machine" + SID_NAME_USE_NONE = "n/a", + SID_NAME_USER = "User", + SID_NAME_DOM_GRP = "Domain group", + SID_NAME_DOMAIN = "Domain", + SID_NAME_ALIAS = "Local group", + SID_NAME_WKN_GRP = "Well known group", + SID_NAME_DELETED = "Deleted account", + SID_NAME_INVALID = "Invalid account", + SID_NAME_UNKNOWN = "Unknown account", + SID_NAME_COMPUTER = "Machine" } ---Marshall a lsa_SidType. This datatype is tied to the table above with that -- name. @@ -1812,13 +1812,13 @@ local lsa_SidType_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_lsa_SidType(sid_type) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidType()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidType()")) - result = marshall_Enum32(sid_type, lsa_SidType) + result = marshall_Enum32(sid_type, lsa_SidType) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidType()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidType()")) + return result end ---Unmarshall a lsa_SidType. This datatype is tied to the table with that name. @@ -1827,13 +1827,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_lsa_SidType(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_SidType()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_SidType()")) - pos, str = unmarshall_Enum16(data, pos, lsa_SidType) + pos, str = unmarshall_Enum16(data, pos, lsa_SidType) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_SidType()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_SidType()")) + return pos, str end ---Convert a lsa_SidType value to a string that can be shown to the user. This is @@ -1842,34 +1842,34 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function lsa_SidType_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering lsa_SidType_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering lsa_SidType_tostr()")) - result = lsa_SidType_str[val] + result = lsa_SidType_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving lsa_SidType_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving lsa_SidType_tostr()")) + return result end ---LSA name levels. local lsa_LookupNamesLevel = { - LOOKUP_NAMES_ALL = 1, - LOOKUP_NAMES_DOMAINS_ONLY = 2, - LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY = 3, - LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY = 4, - LOOKUP_NAMES_FOREST_TRUSTS_ONLY = 5, - LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = 6 + LOOKUP_NAMES_ALL = 1, + LOOKUP_NAMES_DOMAINS_ONLY = 2, + LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY = 3, + LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY = 4, + LOOKUP_NAMES_FOREST_TRUSTS_ONLY = 5, + LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = 6 } ---LSA name level strings. local lsa_LookupNamesLevel_str = { - LOOKUP_NAMES_ALL = "All", - LOOKUP_NAMES_DOMAINS_ONLY = "Domains only", - LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY = "Primary domains only", - LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY = "Uplevel trusted domains only", - LOOKUP_NAMES_FOREST_TRUSTS_ONLY = "Forest trusted domains only", - LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = "Uplevel trusted domains only (2)" + LOOKUP_NAMES_ALL = "All", + LOOKUP_NAMES_DOMAINS_ONLY = "Domains only", + LOOKUP_NAMES_PRIMARY_DOMAIN_ONLY = "Primary domains only", + LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY = "Uplevel trusted domains only", + LOOKUP_NAMES_FOREST_TRUSTS_ONLY = "Forest trusted domains only", + LOOKUP_NAMES_UPLEVEL_TRUSTS_ONLY2 = "Uplevel trusted domains only (2)" } ---Marshall a lsa_LookupNamesLevel. This datatype is tied to the table above with that -- name. @@ -1878,13 +1878,13 @@ local lsa_LookupNamesLevel_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_lsa_LookupNamesLevel(names_level) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_LookupNamesLevel()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_LookupNamesLevel()")) - result = marshall_Enum32(names_level, lsa_LookupNamesLevel) + result = marshall_Enum32(names_level, lsa_LookupNamesLevel) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_LookupNamesLevel()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_LookupNamesLevel()")) + return result end ---Unmarshall a lsa_LookupNamesLevel. This datatype is tied to the table with that name. @@ -1893,13 +1893,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_lsa_LookupNamesLevel(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_LookupNamesLevel()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_LookupNamesLevel()")) - pos, str = unmarshall_Enum32(data, pos, lsa_LookupNamesLevel) + pos, str = unmarshall_Enum32(data, pos, lsa_LookupNamesLevel) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_LookupNamesLevel()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_LookupNamesLevel()")) + return pos, str end ---Convert a lsa_LookupNamesLevel value to a string that can be shown to the user. This is @@ -1908,13 +1908,13 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function lsa_LookupNamesLevel_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering lsa_LookupNamesLevel_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering lsa_LookupNamesLevel_tostr()")) - result = lsa_LookupNamesLevel_str[val] + result = lsa_LookupNamesLevel_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving lsa_LookupNamesLevel_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving lsa_LookupNamesLevel_tostr()")) + return result end ---Marshall a struct with the following definition: @@ -1938,27 +1938,27 @@ end --@param unknown An unknown value (is normaly 0). --@return A string representing the marshalled data. local function marshall_lsa_TranslatedSid2(location, sid_type, rid, sid_index, unknown) - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedSid2()")) + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedSid2()")) - -- Set some default values - if(sid_type == nil) then sid_type = "SID_NAME_USE_NONE" end - if(rid == nil) then rid = 0 end - if(sid_index == nil) then sid_index = 0 end - if(unknown == nil) then unknown = 0 end + -- Set some default values + if(sid_type == nil) then sid_type = "SID_NAME_USE_NONE" end + if(rid == nil) then rid = 0 end + if(sid_index == nil) then sid_index = 0 end + if(unknown == nil) then unknown = 0 end - if(location == HEAD or location == ALL) then - result = result .. marshall_lsa_SidType(sid_type) - result = result .. marshall_int32(rid) - result = result .. marshall_int32(sid_index) - result = result .. marshall_int32(unknown) - end + if(location == HEAD or location == ALL) then + result = result .. marshall_lsa_SidType(sid_type) + result = result .. marshall_int32(rid) + result = result .. marshall_int32(sid_index) + result = result .. marshall_int32(unknown) + end - if(location == BODY or location == ALL) then - end + if(location == BODY or location == ALL) then + end - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TranslatedSid2()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TranslatedSid2()")) + return result end ---Unmarshall a struct with the following definition: @@ -1984,22 +1984,22 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_lsa_TranslatedSid2(location, data, pos, result) - if(result == nil) then - result = {} - end + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['sid_type'] = unmarshall_lsa_SidType(data, pos) - pos, result['rid'] = unmarshall_int32(data, pos) - pos, result['sid_index'] = unmarshall_int32(data, pos) - pos, result['unknown'] = unmarshall_int32(data, pos) - end + if(location == HEAD or location == ALL) then + pos, result['sid_type'] = unmarshall_lsa_SidType(data, pos) + pos, result['rid'] = unmarshall_int32(data, pos) + pos, result['sid_index'] = unmarshall_int32(data, pos) + pos, result['unknown'] = unmarshall_int32(data, pos) + end - if(location == BODY or location == ALL) then - end + if(location == BODY or location == ALL) then + end - return pos, result + return pos, result end ---Marshall a struct with the following definition: @@ -2023,28 +2023,28 @@ end --@param unknown An unknown value, normally 0 --@return A string representing the marshalled data. local function marshall_lsa_TranslatedName2(location, sid_type, name, sid_index, unknown) - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedName2()")) + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TranslatedName2()")) - -- Set some default values - if(sid_type == nil) then sid_type = "SID_NAME_USE_NONE" end - if(name == nil) then name = "" end - if(sid_index == nil) then sid_index = 0 end - if(unknown == nil) then unknown = 0 end + -- Set some default values + if(sid_type == nil) then sid_type = "SID_NAME_USE_NONE" end + if(name == nil) then name = "" end + if(sid_index == nil) then sid_index = 0 end + if(unknown == nil) then unknown = 0 end - if(location == HEAD or location == ALL) then - result = result .. marshall_lsa_SidType(sid_type) - result = result .. marshall_lsa_String_internal(HEAD, name) - result = result .. marshall_int32(sid_index) - result = result .. marshall_int32(unknown) - end + if(location == HEAD or location == ALL) then + result = result .. marshall_lsa_SidType(sid_type) + result = result .. marshall_lsa_String_internal(HEAD, name) + result = result .. marshall_int32(sid_index) + result = result .. marshall_int32(unknown) + end - if(location == BODY or location == ALL) then - result = result .. marshall_lsa_String_internal(BODY, name) - end + if(location == BODY or location == ALL) then + result = result .. marshall_lsa_String_internal(BODY, name) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TranslatedName2()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TranslatedName2()")) + return result end --@param location The part of the pointer wanted, either HEAD (for the data itself), BODY @@ -2059,25 +2059,25 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_lsa_TranslatedName2(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TranslatedName2()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TranslatedName2()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['sid_type'] = unmarshall_lsa_SidType(data, pos) - pos, result['name'] = unmarshall_lsa_String_internal(HEAD, data, pos) - pos, result['sid_index'] = unmarshall_int32(data, pos) - pos, result['unknown'] = unmarshall_int32(data, pos) - end + if(location == HEAD or location == ALL) then + pos, result['sid_type'] = unmarshall_lsa_SidType(data, pos) + pos, result['name'] = unmarshall_lsa_String_internal(HEAD, data, pos) + pos, result['sid_index'] = unmarshall_int32(data, pos) + pos, result['unknown'] = unmarshall_int32(data, pos) + end - if(location == BODY or location == ALL) then - pos, result['name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['name']) - end + if(location == BODY or location == ALL) then + pos, result['name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['name']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TranslatedName2()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TranslatedName2()")) + return pos, result end @@ -2093,22 +2093,22 @@ end --@param sids An array of SIDs to translate (as strings) --@return A string representing the marshalled data. function marshall_lsa_TransSidArray2(sids) - local result = "" - local array = {} - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TransSidArray2()")) + local result = "" + local array = {} + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TransSidArray2()")) - result = result .. marshall_int32(#sids) + result = result .. marshall_int32(#sids) - for i = 1, #sids, 1 do - array[i] = {} - array[i]['func'] = marshall_lsa_TranslatedSid2 - array[i]['args'] = {sids[i]['sid_type'], sids[i]['rid'], sids[i]['sid_index'], sids[i]['unknown']} - end + for i = 1, #sids, 1 do + array[i] = {} + array[i]['func'] = marshall_lsa_TranslatedSid2 + array[i]['args'] = {sids[i]['sid_type'], sids[i]['rid'], sids[i]['sid_index'], sids[i]['unknown']} + end - result = result .. marshall_ptr(ALL, marshall_array, {array}, array) + result = result .. marshall_ptr(ALL, marshall_array, {array}, array) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TransSidArray2()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TransSidArray2()")) + return result end ---Marshall a struct with the following definition: @@ -2133,23 +2133,23 @@ end -- anything. --@return (pos, result) The new position in data, and the string value. local function unmarshall_lsa_StringLarge(location, data, pos, result) - local length, size - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_StringLarge()")) + local length, size + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_StringLarge()")) - if(location == HEAD or location == ALL) then - pos, length = unmarshall_int16(data, pos, false) - pos, size = unmarshall_int16(data, pos, false) + if(location == HEAD or location == ALL) then + pos, length = unmarshall_int16(data, pos, false) + pos, size = unmarshall_int16(data, pos, false) - pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false}) - end + pos, str = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {false}) + end - if(location == BODY or location == ALL) then - pos, str = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {false}, result) - end + if(location == BODY or location == ALL) then + pos, str = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {false}, result) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_StringLarge()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_StringLarge()")) + return pos, str end ---Unmarshall a struct with the following definition: @@ -2173,23 +2173,23 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_lsa_DomainInfo(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_DomainInfo()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_DomainInfo()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['name'] = unmarshall_lsa_StringLarge(HEAD, data, pos) - pos, result['sid'] = unmarshall_ptr(HEAD, data, pos, unmarshall_dom_sid2) - end + if(location == HEAD or location == ALL) then + pos, result['name'] = unmarshall_lsa_StringLarge(HEAD, data, pos) + pos, result['sid'] = unmarshall_ptr(HEAD, data, pos, unmarshall_dom_sid2) + end - if(location == BODY or location == ALL) then - pos, result['name'] = unmarshall_lsa_StringLarge(BODY, data, pos, result['name']) - pos, result['sid'] = unmarshall_ptr(BODY, data, pos, unmarshall_dom_sid2, {}, result['sid']) - end + if(location == BODY or location == ALL) then + pos, result['name'] = unmarshall_lsa_StringLarge(BODY, data, pos, result['name']) + pos, result['sid'] = unmarshall_ptr(BODY, data, pos, unmarshall_dom_sid2, {}, result['sid']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_DomainInfo()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_DomainInfo()")) + return pos, result end ---Unmarshall a struct with the following definition: @@ -2206,19 +2206,19 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_RefDomainList(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_RefDomainList()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_RefDomainList()")) - -- Head - pos, result['count'] = unmarshall_int32(data, pos) - pos, result['domains'] = unmarshall_ptr(HEAD, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_DomainInfo, {}}) - pos, result['max_size'] = unmarshall_int32(data, pos) + -- Head + pos, result['count'] = unmarshall_int32(data, pos) + pos, result['domains'] = unmarshall_ptr(HEAD, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_DomainInfo, {}}) + pos, result['max_size'] = unmarshall_int32(data, pos) - -- Body - pos, result['domains'] = unmarshall_ptr(BODY, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_DomainInfo, {}}, result['domains']) + -- Body + pos, result['domains'] = unmarshall_ptr(BODY, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_DomainInfo, {}}, result['domains']) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_RefDomainList()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_RefDomainList()")) + return pos, result end ---Unmarshall a pointer to a lsa_RefDomainList. See the unmarshall_lsa_RefDomainList function @@ -2228,13 +2228,13 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_RefDomainList_ptr(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_RefDomainList_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_RefDomainList_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_lsa_RefDomainList, nil) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_lsa_RefDomainList, nil) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_RefDomainList_ptr()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_RefDomainList_ptr()")) + return pos, result end ---Unmarshall a struct with the following definition: @@ -2250,14 +2250,14 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_TransSidArray2(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TransSidArray2()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TransSidArray2()")) - pos, result['count'] = unmarshall_int32(data, pos) - pos, result['sid'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_TranslatedSid2, {}}) + pos, result['count'] = unmarshall_int32(data, pos) + pos, result['sid'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_TranslatedSid2, {}}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TransSidArray2()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TransSidArray2()")) + return pos, result end ---Marshall a struct with the following definition: @@ -2276,16 +2276,16 @@ end -- --@return A string representing the marshalled data. function marshall_lsa_QosInfo() - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_QosInfo()")) + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_QosInfo()")) - result = result .. marshall_int32(12) - result = result .. marshall_int16(2, false) - result = result .. marshall_int8(1, false) - result = result .. marshall_int8(0, false) + result = result .. marshall_int32(12) + result = result .. marshall_int16(2, false) + result = result .. marshall_int8(1, false) + result = result .. marshall_int8(0, false) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_QosInfo()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_QosInfo()")) + return result end ---Marshall a struct with the following definition: @@ -2306,18 +2306,18 @@ end -- --@return A string representing the marshalled data. function marshall_lsa_ObjectAttribute() - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_ObjectAttribute()")) + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_ObjectAttribute()")) - result = result .. marshall_int32(24) - result = result .. marshall_int32(0) -- Null'ing out these pointers for now. Maybe we'll need them in the future... - result = result .. marshall_int32(0) - result = result .. marshall_int32(0) - result = result .. marshall_int32(0) - result = result .. marshall_ptr(ALL, marshall_lsa_QosInfo, {}) + result = result .. marshall_int32(24) + result = result .. marshall_int32(0) -- Null'ing out these pointers for now. Maybe we'll need them in the future... + result = result .. marshall_int32(0) + result = result .. marshall_int32(0) + result = result .. marshall_int32(0) + result = result .. marshall_ptr(ALL, marshall_lsa_QosInfo, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_ObjectAttribute()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_ObjectAttribute()")) + return result end ---Marshall a struct with the following definition: @@ -2335,13 +2335,13 @@ end --@param sid The SID to marshall (as a string). --@return A string representing the marshalled data. local function marshall_lsa_SidPtr(location, sid) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidPtr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_SidPtr()")) - result = marshall_ptr(location, marshall_dom_sid2, {sid}, sid) + result = marshall_ptr(location, marshall_dom_sid2, {sid}, sid) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidPtr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_SidPtr()")) + return result end ---Marshall a struct with the following definition: @@ -2356,20 +2356,20 @@ end --@param sids The array of SIDs to marshall (as strings). --@return A string representing the marshalled data. function marshall_lsa_SidArray(sids) - local result = "" - local array = {} + local result = "" + local array = {} - result = result .. marshall_int32(#sids) + result = result .. marshall_int32(#sids) - for i = 1, #sids, 1 do - array[i] = {} - array[i]['func'] = marshall_lsa_SidPtr - array[i]['args'] = {sids[i]} - end + for i = 1, #sids, 1 do + array[i] = {} + array[i]['func'] = marshall_lsa_SidPtr + array[i]['args'] = {sids[i]} + end - result = result .. marshall_ptr(ALL, marshall_array, {array}, array) + result = result .. marshall_ptr(ALL, marshall_array, {array}, array) - return result + return result end ---Unmarshall a struct with the following definition: @@ -2389,7 +2389,7 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_SidPtr(location, data, pos, result) - return unmarshall_ptr(location, data, pos, unmarshall_dom_sid2, {}, result) + return unmarshall_ptr(location, data, pos, unmarshall_dom_sid2, {}, result) end ---Unmarshall a struct with the following definition: @@ -2403,12 +2403,12 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_SidArray(data, pos) - local sidarray = {} + local sidarray = {} - pos, sidarray['count'] = unmarshall_int32(data, pos) - pos, sidarray['sids'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {sidarray['count'], unmarshall_lsa_SidPtr, {}}) + pos, sidarray['count'] = unmarshall_int32(data, pos) + pos, sidarray['sids'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {sidarray['count'], unmarshall_lsa_SidPtr, {}}) - return pos, sidarray + return pos, sidarray end ---Marshall a struct with the following definition: @@ -2423,27 +2423,27 @@ end --@param names An array of names to translate. --@return A string representing the marshalled data. function marshall_lsa_TransNameArray2(names) - local result = "" - local array = {} - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TransNameArray2()")) + local result = "" + local array = {} + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_lsa_TransNameArray2()")) - if(names == nil) then - result = result .. marshall_int32(0) - array = nil - else - result = result .. marshall_int32(#names) + if(names == nil) then + result = result .. marshall_int32(0) + array = nil + else + result = result .. marshall_int32(#names) - for i = 1, #names, 1 do - array[i] = {} - array[i]['func'] = marshall_lsa_TranslatedName2 - array[i]['args'] = {names[i]['sid_type'], names[i]['name'], names[i]['sid_index'], names[i]['unknown']} - end - end + for i = 1, #names, 1 do + array[i] = {} + array[i]['func'] = marshall_lsa_TranslatedName2 + array[i]['args'] = {names[i]['sid_type'], names[i]['name'], names[i]['sid_index'], names[i]['unknown']} + end + end - result = result .. marshall_ptr(ALL, marshall_array, {array}, array) + result = result .. marshall_ptr(ALL, marshall_array, {array}, array) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TransNameArray2()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_lsa_TransNameArray2()")) + return result end ---Unmarshall a lsa_TransNameArray2 structure. See the marshall_lsa_TransNameArray2 for more @@ -2453,14 +2453,14 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_lsa_TransNameArray2(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TransNameArray2()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_lsa_TransNameArray2()")) - pos, result['count'] = unmarshall_int32(data, pos) - pos, result['names'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_TranslatedName2, {}}) + pos, result['count'] = unmarshall_int32(data, pos) + pos, result['names'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_lsa_TranslatedName2, {}}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TransNameArray2()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_lsa_TransNameArray2()")) + return pos, result end @@ -2472,34 +2472,34 @@ end --- Access masks for Windows registry calls local winreg_AccessMask = { - DELETE_ACCESS = 0x00010000, - READ_CONTROL_ACCESS = 0x00020000, - WRITE_DAC_ACCESS = 0x00040000, - WRITE_OWNER_ACCESS = 0x00080000, - SYNCHRONIZE_ACCESS = 0x00100000, - ACCESS_SACL_ACCESS = 0x00800000, - SYSTEM_SECURITY_ACCESS = 0x01000000, - MAXIMUM_ALLOWED_ACCESS = 0x02000000, - GENERIC_ALL_ACCESS = 0x10000000, - GENERIC_EXECUTE_ACCESS = 0x20000000, - GENERIC_WRITE_ACCESS = 0x40000000, - GENERIC_READ_ACCESS = 0x80000000 + DELETE_ACCESS = 0x00010000, + READ_CONTROL_ACCESS = 0x00020000, + WRITE_DAC_ACCESS = 0x00040000, + WRITE_OWNER_ACCESS = 0x00080000, + SYNCHRONIZE_ACCESS = 0x00100000, + ACCESS_SACL_ACCESS = 0x00800000, + SYSTEM_SECURITY_ACCESS = 0x01000000, + MAXIMUM_ALLOWED_ACCESS = 0x02000000, + GENERIC_ALL_ACCESS = 0x10000000, + GENERIC_EXECUTE_ACCESS = 0x20000000, + GENERIC_WRITE_ACCESS = 0x40000000, + GENERIC_READ_ACCESS = 0x80000000 } --- String versions of access masks for Windows registry calls local winreg_AccessMask_str = { - DELETE_ACCESS = "Delete", - READ_CONTROL_ACCESS = "Read", - WRITE_DAC_ACCESS = "Write", - WRITE_OWNER_ACCESS = "Write (owner)", - SYNCHRONIZE_ACCESS = "Synchronize", - ACCESS_SACL_ACCESS = "Access SACL", - SYSTEM_SECURITY_ACCESS = "System security", - MAXIMUM_ALLOWED_ACCESS = "Maximum allowed access", - GENERIC_ALL_ACCESS = "All access", - GENERIC_EXECUTE_ACCESS = "Execute access", - GENERIC_WRITE_ACCESS = "Write access", - GENERIC_READ_ACCESS = "Read access" + DELETE_ACCESS = "Delete", + READ_CONTROL_ACCESS = "Read", + WRITE_DAC_ACCESS = "Write", + WRITE_OWNER_ACCESS = "Write (owner)", + SYNCHRONIZE_ACCESS = "Synchronize", + ACCESS_SACL_ACCESS = "Access SACL", + SYSTEM_SECURITY_ACCESS = "System security", + MAXIMUM_ALLOWED_ACCESS = "Maximum allowed access", + GENERIC_ALL_ACCESS = "All access", + GENERIC_EXECUTE_ACCESS = "Execute access", + GENERIC_WRITE_ACCESS = "Write access", + GENERIC_READ_ACCESS = "Read access" } ---Marshall a winreg_AccessMask. @@ -2508,13 +2508,13 @@ local winreg_AccessMask_str = -- table) --@return A string representing the marshalled data. function marshall_winreg_AccessMask(accessmask) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_AccessMask()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_AccessMask()")) - result = marshall_Enum32(accessmask, winreg_AccessMask) + result = marshall_Enum32(accessmask, winreg_AccessMask) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_AccessMask()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_AccessMask()")) + return result end ---Unmarshall a winreg_AccessMask. This datatype is tied to the table with that name. @@ -2523,13 +2523,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_winreg_AccessMask(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_AccessMask()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_AccessMask()")) - pos, str = unmarshall_Enum32(data, pos, winreg_AccessMask) + pos, str = unmarshall_Enum32(data, pos, winreg_AccessMask) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_AccessMask()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_AccessMask()")) + return pos, str end ---Convert a winreg_AccessMask value to a string that can be shown to the user. This is @@ -2538,47 +2538,47 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function winreg_AccessMask_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering winreg_AccessMask_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering winreg_AccessMask_tostr()")) - result = winreg_AccessMask_str[val] + result = winreg_AccessMask_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving winreg_AccessMask_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving winreg_AccessMask_tostr()")) + return result end ---Registry types winreg_Type = { - REG_NONE = 0, - REG_SZ = 1, - REG_EXPAND_SZ = 2, - REG_BINARY = 3, - REG_DWORD = 4, - REG_DWORD_BIG_ENDIAN = 5, - REG_LINK = 6, - REG_MULTI_SZ = 7, - REG_RESOURCE_LIST = 8, - REG_FULL_RESOURCE_DESCRIPTOR = 9, - REG_RESOURCE_REQUIREMENTS_LIST = 10, - REG_QWORD = 11 + REG_NONE = 0, + REG_SZ = 1, + REG_EXPAND_SZ = 2, + REG_BINARY = 3, + REG_DWORD = 4, + REG_DWORD_BIG_ENDIAN = 5, + REG_LINK = 6, + REG_MULTI_SZ = 7, + REG_RESOURCE_LIST = 8, + REG_FULL_RESOURCE_DESCRIPTOR = 9, + REG_RESOURCE_REQUIREMENTS_LIST = 10, + REG_QWORD = 11 } ---Registry type strings winreg_Type_str = { - REG_NONE = "None", - REG_SZ = "String", - REG_EXPAND_SZ = "String (expanded)", - REG_BINARY = "Binary", - REG_DWORD = "Dword", - REG_DWORD_BIG_ENDIAN = "Dword (big endian)", - REG_LINK = "Link", - REG_MULTI_SZ = "String (multi)", - REG_RESOURCE_LIST = "Resource list", - REG_FULL_RESOURCE_DESCRIPTOR = "Full resource descriptor", - REG_RESOURCE_REQUIREMENTS_LIST = "Resource requirements list", - REG_QWORD = "Qword" + REG_NONE = "None", + REG_SZ = "String", + REG_EXPAND_SZ = "String (expanded)", + REG_BINARY = "Binary", + REG_DWORD = "Dword", + REG_DWORD_BIG_ENDIAN = "Dword (big endian)", + REG_LINK = "Link", + REG_MULTI_SZ = "String (multi)", + REG_RESOURCE_LIST = "Resource list", + REG_FULL_RESOURCE_DESCRIPTOR = "Full resource descriptor", + REG_RESOURCE_REQUIREMENTS_LIST = "Resource requirements list", + REG_QWORD = "Qword" } ---Marshall a winreg_Type. This datatype is tied to the table above with that @@ -2588,13 +2588,13 @@ winreg_Type_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_winreg_Type(winregtype) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type()")) - result = marshall_Enum32(winregtype, winreg_Type) + result = marshall_Enum32(winregtype, winreg_Type) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_Type()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_Type()")) + return result end ---Unmarshall a winreg_Type. This datatype is tied to the table with that name. @@ -2603,13 +2603,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_winreg_Type(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type()")) - pos, str = unmarshall_Enum32(data, pos, winreg_Type) + pos, str = unmarshall_Enum32(data, pos, winreg_Type) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_Type()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_Type()")) + return pos, str end ---Marshall a pointer to a winreg_Type. This datatype is tied to the table above with that @@ -2619,13 +2619,13 @@ end --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_winreg_Type_ptr(winreg_type) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_Type_ptr()")) - result = marshall_ptr(ALL, marshall_winreg_Type, {winreg_type}, winreg_type) + result = marshall_ptr(ALL, marshall_winreg_Type, {winreg_type}, winreg_type) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_Type_ptr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_Type_ptr()")) + return result end ---Unmarshall a pointer to a winreg_Type. This datatype is tied to the table with that name. @@ -2634,13 +2634,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_winreg_Type_ptr(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type_ptr()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_Type_ptr()")) - pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_winreg_Type, {}) + pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_winreg_Type, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_Type_ptr()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_Type_ptr()")) + return pos, str end ---Convert a winreg_Type value to a string that can be shown to the user. This is @@ -2649,13 +2649,13 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function winreg_Type_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering winreg_Type_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering winreg_Type_tostr()")) - result = winreg_Type_str[val] + result = winreg_Type_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving winreg_Type_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving winreg_Type_tostr()")) + return result end --- A winreg_stringbuf is a buffer that holds a null-terminated string. It can have a max size that's different @@ -2664,11 +2664,11 @@ end -- This is the format: -- -- --- typedef struct { --- [value(strlen_m_term(name)*2)] uint16 length; --- uint16 size; --- [size_is(size/2),length_is(length/2),charset(UTF16)] uint16 *name; --- } winreg_StringBuf; +-- typedef struct { +-- [value(strlen_m_term(name)*2)] uint16 length; +-- uint16 size; +-- [size_is(size/2),length_is(length/2),charset(UTF16)] uint16 *name; +-- } winreg_StringBuf; -- -- --@param table The table to marshall. Will probably contain just the 'name' entry. @@ -2676,37 +2676,37 @@ end -- Defaults to the length of the string, including the null. --@return A string representing the marshalled data. function marshall_winreg_StringBuf(table, max_length) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf()")) - local name = table['name'] - local length + local name = table['name'] + local length - -- Handle default max lengths - if(max_length == nil) then - if(name == nil) then - max_length = 0 - else - max_length = #name + 1 - end - end + -- Handle default max lengths + if(max_length == nil) then + if(name == nil) then + max_length = 0 + else + max_length = #name + 1 + end + end - -- For some reason, 0-length strings are handled differently (no null terminator)... - if(name == "") then - length = 0 - result = bin.pack("marshall_winreg_StringBuf, except @@ -2735,13 +2735,13 @@ end --@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null. --@return A string representing the marshalled data. function marshall_winreg_StringBuf_ptr(table, max_length) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_StringBuf_ptr()")) - result = marshall_ptr(ALL, marshall_winreg_StringBuf, {table, max_length}, table) + result = marshall_ptr(ALL, marshall_winreg_StringBuf, {table, max_length}, table) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_StringBuf_ptr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_StringBuf_ptr()")) + return result end ---Unmarshall a winreg_StringBuffer pointer @@ -2750,13 +2750,13 @@ end --@param pos The position in the data buffer. --@return (pos, str) The new position and the string. function unmarshall_winreg_StringBuf_ptr(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_StringBuf_ptr()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_StringBuf_ptr()")) - pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_winreg_StringBuf, {}) + pos, str = unmarshall_ptr(ALL, data, pos, unmarshall_winreg_StringBuf, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_StringBuf_ptr()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_StringBuf_ptr()")) + return pos, str end @@ -2766,13 +2766,13 @@ end --@param max_length [optional] The maximum size of the buffer, in characters. Defaults to the length of the string, including the null. --@return A string representing the marshalled data. function marshall_winreg_String(table, max_length) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_String()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_winreg_String()")) - result = marshall_winreg_StringBuf(table, max_length) + result = marshall_winreg_StringBuf(table, max_length) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_String()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_winreg_String()")) + return result end ---Unmarshall a winreg_String. Since ti has the same makup as winreg_StringBuf, delegate to that. @@ -2781,13 +2781,13 @@ end --@param pos The position in the data buffer. --@return (pos, str) The new position and the string. function unmarshall_winreg_String(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_String()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_winreg_String()")) - pos, str = unmarshall_winreg_StringBuf(data, pos) + pos, str = unmarshall_winreg_StringBuf(data, pos) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_String()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_winreg_String()")) + return pos, str end @@ -2798,34 +2798,34 @@ end ---Share types local srvsvc_ShareType = { - STYPE_DISKTREE = 0x00000000, - STYPE_DISKTREE_TEMPORARY = 0x40000000, - STYPE_DISKTREE_HIDDEN = 0x80000000, - STYPE_PRINTQ = 0x00000001, - STYPE_PRINTQ_TEMPORARY = 0x40000001, - STYPE_PRINTQ_HIDDEN = 0x80000001, - STYPE_DEVICE = 0x00000002, -- Serial device - STYPE_DEVICE_TEMPORARY = 0x40000002, - STYPE_DEVICE_HIDDEN = 0x80000002, - STYPE_IPC = 0x00000003, -- Interprocess communication (IPC) - STYPE_IPC_TEMPORARY = 0x40000003, - STYPE_IPC_HIDDEN = 0x80000003 + STYPE_DISKTREE = 0x00000000, + STYPE_DISKTREE_TEMPORARY = 0x40000000, + STYPE_DISKTREE_HIDDEN = 0x80000000, + STYPE_PRINTQ = 0x00000001, + STYPE_PRINTQ_TEMPORARY = 0x40000001, + STYPE_PRINTQ_HIDDEN = 0x80000001, + STYPE_DEVICE = 0x00000002, -- Serial device + STYPE_DEVICE_TEMPORARY = 0x40000002, + STYPE_DEVICE_HIDDEN = 0x80000002, + STYPE_IPC = 0x00000003, -- Interprocess communication (IPC) + STYPE_IPC_TEMPORARY = 0x40000003, + STYPE_IPC_HIDDEN = 0x80000003 } ---Share type strings local srvsvc_ShareType_str = { - STYPE_DISKTREE = "Disk", - STYPE_DISKTREE_TEMPORARY = "Disk (temporary)", - STYPE_DISKTREE_HIDDEN = "Disk (hidden)", - STYPE_PRINTQ = "Print queue", - STYPE_PRINTQ_TEMPORARY = "Print queue (temporary)", - STYPE_PRINTQ_HIDDEN = "Print queue (hidden)", - STYPE_DEVICE = "Serial device", - STYPE_DEVICE_TEMPORARY = "Serial device (temporary)", - STYPE_DEVICE_HIDDEN = "Serial device (hidden)", - STYPE_IPC = "Interprocess Communication", - STYPE_IPC_TEMPORARY = "Interprocess Communication (temporary)", - STYPE_IPC_HIDDEN = "Interprocess Communication (hidden)" + STYPE_DISKTREE = "Disk", + STYPE_DISKTREE_TEMPORARY = "Disk (temporary)", + STYPE_DISKTREE_HIDDEN = "Disk (hidden)", + STYPE_PRINTQ = "Print queue", + STYPE_PRINTQ_TEMPORARY = "Print queue (temporary)", + STYPE_PRINTQ_HIDDEN = "Print queue (hidden)", + STYPE_DEVICE = "Serial device", + STYPE_DEVICE_TEMPORARY = "Serial device (temporary)", + STYPE_DEVICE_HIDDEN = "Serial device (hidden)", + STYPE_IPC = "Interprocess Communication", + STYPE_IPC_TEMPORARY = "Interprocess Communication (temporary)", + STYPE_IPC_HIDDEN = "Interprocess Communication (hidden)" } ---Marshall a srvsvc_ShareType. This datatype is tied to the table above with that @@ -2835,13 +2835,13 @@ local srvsvc_ShareType_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_srvsvc_ShareType(sharetype) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_ShareType()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_ShareType()")) - result = marshall_Enum32(sharetype, srvsvc_ShareType) + result = marshall_Enum32(sharetype, srvsvc_ShareType) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_ShareType()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_ShareType()")) + return result end ---Unmarshall a srvsvc_ShareType. This datatype is tied to the table with that name. @@ -2850,13 +2850,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_srvsvc_ShareType(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_ShareType()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_ShareType()")) - pos, str = unmarshall_Enum32(data, pos, srvsvc_ShareType) + pos, str = unmarshall_Enum32(data, pos, srvsvc_ShareType) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_ShareType()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_ShareType()")) + return pos, str end ---Convert a srvsvc_ShareType value to a string that can be shown to the user. This is @@ -2865,13 +2865,13 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function srvsvc_ShareType_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering srvsvc_ShareType_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering srvsvc_ShareType_tostr()")) - result = srvsvc_ShareType_str[val] + result = srvsvc_ShareType_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving srvsvc_ShareType_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving srvsvc_ShareType_tostr()")) + return result end ---Marshall a NetShareInfo type 0, which is just a name. @@ -2889,13 +2889,13 @@ end --@param name The name to marshall. --@return A string representing the marshalled data. local function marshall_srvsvc_NetShareInfo0(location, name) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo0()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo0()")) - result = marshall_ptr(location, marshall_unicode, {name, true}, name) + result = marshall_ptr(location, marshall_unicode, {name, true}, name) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_NetShareInfo0()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_NetShareInfo0()")) + return result end ---Unmarshall a NetShareInfo type 0, which is just a name. See the marshall function for more information. @@ -2912,21 +2912,21 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_srvsvc_NetShareInfo0(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo0()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo0()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - end + if(location == HEAD or location == ALL) then + pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + end - if(location == BODY or location == ALL) then - pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name']) - end + if(location == BODY or location == ALL) then + pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo0()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo0()")) + return pos, result end ---Marshall a NetShareInfo type 1, which is the name and a few other things. @@ -2948,16 +2948,16 @@ end --@param comment The comment to marshall. --@return A string representing the marshalled data. local function marshall_srvsvc_NetShareInfo1(location, name, sharetype, comment) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo1()")) - local name = marshall_ptr(location, marshall_unicode, {name, true}, name) - local sharetype = marshall_basetype(location, marshall_srvsvc_ShareType, {sharetype}) - local comment = marshall_ptr(location, marshall_unicode, {comment, true}, comment) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo1()")) + local name = marshall_ptr(location, marshall_unicode, {name, true}, name) + local sharetype = marshall_basetype(location, marshall_srvsvc_ShareType, {sharetype}) + local comment = marshall_ptr(location, marshall_unicode, {comment, true}, comment) - result = bin.pack("data, and a table representing the datatype. local function unmarshall_srvsvc_NetShareInfo1(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo1()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo1()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - pos, result['sharetype'] = unmarshall_srvsvc_ShareType(data, pos) - pos, result['comment'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - end + if(location == HEAD or location == ALL) then + pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + pos, result['sharetype'] = unmarshall_srvsvc_ShareType(data, pos) + pos, result['comment'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + end - if(location == BODY or location == ALL) then - pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name']) - pos, result['comment'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['comment']) - end + if(location == BODY or location == ALL) then + pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name']) + pos, result['comment'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['comment']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo1()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo1()")) + return pos, result end @@ -3025,21 +3025,21 @@ end --@param password The share-level password, a string (never used on Windows). --@return A string representing the marshalled data. local function marshall_srvsvc_NetShareInfo2(location, name, sharetype, comment, permissions, max_users, current_users, path, password) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo2()")) - local name = marshall_ptr(location, marshall_unicode, {name, true}, name) - local sharetype = marshall_basetype(location, marshall_srvsvc_ShareType, {sharetype}) - local comment = marshall_ptr(location, marshall_unicode, {comment, true}, comment) - local permissions = marshall_basetype(location, marshall_int32, {permissions}) - local max_users = marshall_basetype(location, marshall_int32, {max_users}) - local current_users = marshall_basetype(location, marshall_int32, {current_users}) - local path = marshall_ptr(location, marshall_unicode, {path, true}, path) - local password = marshall_ptr(location, marshall_unicode, {password, true}, password) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo2()")) + local name = marshall_ptr(location, marshall_unicode, {name, true}, name) + local sharetype = marshall_basetype(location, marshall_srvsvc_ShareType, {sharetype}) + local comment = marshall_ptr(location, marshall_unicode, {comment, true}, comment) + local permissions = marshall_basetype(location, marshall_int32, {permissions}) + local max_users = marshall_basetype(location, marshall_int32, {max_users}) + local current_users = marshall_basetype(location, marshall_int32, {current_users}) + local path = marshall_ptr(location, marshall_unicode, {path, true}, path) + local password = marshall_ptr(location, marshall_unicode, {password, true}, password) - result = name .. sharetype .. comment .. permissions .. max_users .. current_users .. path .. password + result = name .. sharetype .. comment .. permissions .. max_users .. current_users .. path .. password - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_NetShareInfo2()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_srvsvc_NetShareInfo2()")) + return result end ---Unmarshall a NetShareInfo type 2, which is a name and a few other things. See the marshall @@ -3057,31 +3057,31 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_srvsvc_NetShareInfo2(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo2()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo2()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - pos, result['sharetype'] = unmarshall_srvsvc_ShareType(data, pos) - pos, result['comment'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - pos, result['permissions'] = unmarshall_int32(data, pos) - pos, result['max_users'] = unmarshall_int32(data, pos) - pos, result['current_users'] = unmarshall_int32(data, pos) - pos, result['path'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - pos, result['password'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - end + if(location == HEAD or location == ALL) then + pos, result['name'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + pos, result['sharetype'] = unmarshall_srvsvc_ShareType(data, pos) + pos, result['comment'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + pos, result['permissions'] = unmarshall_int32(data, pos) + pos, result['max_users'] = unmarshall_int32(data, pos) + pos, result['current_users'] = unmarshall_int32(data, pos) + pos, result['path'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + pos, result['password'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + end - if(location == BODY or location == ALL) then - pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name']) - pos, result['comment'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['comment']) - pos, result['path'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['path']) - pos, result['password'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['password']) - end + if(location == BODY or location == ALL) then + pos, result['name'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['name']) + pos, result['comment'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['comment']) + pos, result['path'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['path']) + pos, result['password'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['password']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo2()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo2()")) + return pos, result end ---Marshall a NetShareCtr (container) type 0. It is a simple array with the following definition: @@ -3096,35 +3096,35 @@ end --@param NetShareCtr0 A table representing the structure. --@return A string representing the marshalled data. function marshall_srvsvc_NetShareCtr0(NetShareCtr0) - local i - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr0()")) + local i + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr0()")) - if(NetShareCtr0 == nil) then - result = result .. bin.pack("data, and a table representing the datatype. function unmarshall_srvsvc_NetShareCtr0(data, pos) - local count - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareCtr0()")) + local count + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareCtr0()")) - pos, count = unmarshall_int32(data, pos) + pos, count = unmarshall_int32(data, pos) - pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetShareInfo0, {}}) + pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetShareInfo0, {}}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareCtr0()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareCtr0()")) + return pos, result end ---Marshall a NetShareCtr (container) type 1. It is a simple array with the following definition: @@ -3157,35 +3157,35 @@ end --@param NetShareCtr1 A table representing the structure. --@return A string representing the marshalled data. function marshall_srvsvc_NetShareCtr1(NetShareCtr1) - local i - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr1()")) + local i + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr1()")) - if(NetShareCtr1 == nil) then - result = result .. bin.pack("data, and a table representing the datatype. -- The result may be nil if there's an error. function unmarshall_srvsvc_NetShareCtr(data, pos) - local level - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srv_NetShareCtr()")) + local level + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srv_NetShareCtr()")) - pos, level = unmarshall_int32(data, pos) + pos, level = unmarshall_int32(data, pos) - if(level == 0) then - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_NetShareCtr0, {}) - else - stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for srvsvc_NetShareCtr: %d", level) - pos, result = nil, nil - end + if(level == 0) then + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_NetShareCtr0, {}) + else + stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for srvsvc_NetShareCtr: %d", level) + pos, result = nil, nil + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srv_NetShareCtr()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srv_NetShareCtr()")) + return pos, result end ---Unmarshall the top-level NetShareInfo. This is a union of a bunch of different structs: @@ -3329,24 +3329,24 @@ end --@return (pos, result) The new position in data, and a table representing the datatype. This may be -- nil if there was an error. function unmarshall_srvsvc_NetShareInfo(data, pos) - local level - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo()")) - pos, level = unmarshall_int32(data, pos) + local level + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetShareInfo()")) + pos, level = unmarshall_int32(data, pos) - if(level == 0) then - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo0, {}}) - elseif(level == 1) then - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo1, {}}) - elseif(level == 2) then - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo2, {}}) - else - stdnse.print_debug(1, "MSRPC: ERROR: Invalid level returned by NetShareInfo: %d\n", level) - pos, result = nil, nil - end + if(level == 0) then + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo0, {}}) + elseif(level == 1) then + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo1, {}}) + elseif(level == 2) then + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_struct, {unmarshall_srvsvc_NetShareInfo2, {}}) + else + stdnse.print_debug(1, "MSRPC: ERROR: Invalid level returned by NetShareInfo: %d\n", level) + pos, result = nil, nil + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetShareInfo()")) + return pos, result end ---Marshall a NetSessInfo type 10. @@ -3370,17 +3370,17 @@ end --@param idle_time The number of seconds that the user's been idle. --@return A string representing the marshalled data. local function marshall_srvsvc_NetSessInfo10(location, client, user, time, idle_time) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo10()")) - local client = marshall_ptr(location, marshall_unicode, {client, true}, client) - local user = marshall_ptr(location, marshall_unicode, {user, true}, user) - local time = marshall_basetype(location, marshall_int32, {time}) - local idle_time = marshall_basetype(location, marshall_int32, {idle_time}) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareInfo10()")) + local client = marshall_ptr(location, marshall_unicode, {client, true}, client) + local user = marshall_ptr(location, marshall_unicode, {user, true}, user) + local time = marshall_basetype(location, marshall_int32, {time}) + local idle_time = marshall_basetype(location, marshall_int32, {idle_time}) - result = bin.pack("data, and a table representing the datatype. local function unmarshall_srvsvc_NetSessInfo10(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessInfo10()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessInfo10()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['client'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - pos, result['user'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) - pos, result['time'] = unmarshall_int32(data, pos) - pos, result['idle_time'] = unmarshall_int32(data, pos) - end + if(location == HEAD or location == ALL) then + pos, result['client'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + pos, result['user'] = unmarshall_ptr(HEAD, data, pos, unmarshall_unicode, {true}) + pos, result['time'] = unmarshall_int32(data, pos) + pos, result['idle_time'] = unmarshall_int32(data, pos) + end - if(location == BODY or location == ALL) then - pos, result['client'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['client']) - pos, result['user'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['user']) - end + if(location == BODY or location == ALL) then + pos, result['client'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['client']) + pos, result['user'] = unmarshall_ptr(BODY, data, pos, unmarshall_unicode, {true}, result['user']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetSessInfo10()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetSessInfo10()")) + return pos, result end ---Marshall a NetSessCtr (session container) type 10. It is a simple array with the following definition: @@ -3430,35 +3430,35 @@ end --@param NetSessCtr10 A table representing the structure. --@return A string representing the marshalled data. function marshall_srvsvc_NetSessCtr10(NetSessCtr10) - local i - local result = "" - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetSessCtr10()")) + local i + local result = "" + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetSessCtr10()")) - if(NetSessCtr10 == nil) then - result = result .. bin.pack("data, and a table representing the datatype. function unmarshall_srvsvc_NetSessCtr10(data, pos) - local count - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr10()")) + local count + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr10()")) - pos, count = unmarshall_int32(data, pos) + pos, count = unmarshall_int32(data, pos) - pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetSessInfo10, {}}) + pos, result['array'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {count, unmarshall_srvsvc_NetSessInfo10, {}}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetSessCtr10()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_NetSessCtr10()")) + return pos, result end ---Marshall the top-level NetShareCtr. This is a union of a bunch of different containers: @@ -3500,18 +3500,18 @@ end --@param data The data to populate the array with. Depending on the level, this data will be different. --@return A string representing the marshalled data. function marshall_srvsvc_NetSessCtr(level, data) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_srvsvc_NetShareCtr()")) - if(level == 10) then - result = bin.pack("data, and a table representing the datatype. Can be -- nil if there's an error. function unmarshall_srvsvc_NetSessCtr(data, pos) - local level - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr()")) + local level + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_NetSessCtr()")) - pos, level = bin.unpack("data --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_srvsvc_Statistics(data, pos) - local response = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_Statistics()")) + local response = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_Statistics()")) - pos, response['start'] = unmarshall_int32(data, pos) - pos, response['fopens'] = unmarshall_int32(data, pos) - pos, response['devopens'] = unmarshall_int32(data, pos) - pos, response['jobsqueued'] = unmarshall_int32(data, pos) - pos, response['sopens'] = unmarshall_int32(data, pos) - pos, response['stimeouts'] = unmarshall_int32(data, pos) - pos, response['serrorout'] = unmarshall_int32(data, pos) - pos, response['pwerrors'] = unmarshall_int32(data, pos) - pos, response['permerrors'] = unmarshall_int32(data, pos) - pos, response['syserrors'] = unmarshall_int32(data, pos) - pos, response['bytessent_low'] = unmarshall_int32(data, pos) - pos, response['bytessent_high'] = unmarshall_int32(data, pos) - pos, response['bytesrcvd_low'] = unmarshall_int32(data, pos) - pos, response['bytesrcvd_high'] = unmarshall_int32(data, pos) - pos, response['avresponse'] = unmarshall_int32(data, pos) - pos, response['reqbufneed'] = unmarshall_int32(data, pos) - pos, response['bigbufneed'] = unmarshall_int32(data, pos) + pos, response['start'] = unmarshall_int32(data, pos) + pos, response['fopens'] = unmarshall_int32(data, pos) + pos, response['devopens'] = unmarshall_int32(data, pos) + pos, response['jobsqueued'] = unmarshall_int32(data, pos) + pos, response['sopens'] = unmarshall_int32(data, pos) + pos, response['stimeouts'] = unmarshall_int32(data, pos) + pos, response['serrorout'] = unmarshall_int32(data, pos) + pos, response['pwerrors'] = unmarshall_int32(data, pos) + pos, response['permerrors'] = unmarshall_int32(data, pos) + pos, response['syserrors'] = unmarshall_int32(data, pos) + pos, response['bytessent_low'] = unmarshall_int32(data, pos) + pos, response['bytessent_high'] = unmarshall_int32(data, pos) + pos, response['bytesrcvd_low'] = unmarshall_int32(data, pos) + pos, response['bytesrcvd_high'] = unmarshall_int32(data, pos) + pos, response['avresponse'] = unmarshall_int32(data, pos) + pos, response['reqbufneed'] = unmarshall_int32(data, pos) + pos, response['bigbufneed'] = unmarshall_int32(data, pos) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_Statistics()")) - return pos, response + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_Statistics()")) + return pos, response end ---Unmarshalls a srvsvc_Statistics as a pointer. Wireshark fails to do this, and ends @@ -3603,13 +3603,13 @@ end --@param pos The position within data --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_srvsvc_Statistics_ptr(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_Statistics_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_srvsvc_Statistics_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_Statistics, {}) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_srvsvc_Statistics, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_Statistics_ptr()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_srvsvc_Statistics_ptr()")) + return pos, result end @@ -3621,21 +3621,21 @@ end local samr_ConnectAccessMask = { - SAMR_ACCESS_CONNECT_TO_SERVER = 0x00000001, - SAMR_ACCESS_SHUTDOWN_SERVER = 0x00000002, - SAMR_ACCESS_INITIALIZE_SERVER = 0x00000004, - SAMR_ACCESS_CREATE_DOMAIN = 0x00000008, - SAMR_ACCESS_ENUM_DOMAINS = 0x00000010, - SAMR_ACCESS_OPEN_DOMAIN = 0x00000020 + SAMR_ACCESS_CONNECT_TO_SERVER = 0x00000001, + SAMR_ACCESS_SHUTDOWN_SERVER = 0x00000002, + SAMR_ACCESS_INITIALIZE_SERVER = 0x00000004, + SAMR_ACCESS_CREATE_DOMAIN = 0x00000008, + SAMR_ACCESS_ENUM_DOMAINS = 0x00000010, + SAMR_ACCESS_OPEN_DOMAIN = 0x00000020 } local samr_ConnectAccessMask_str = { - SAMR_ACCESS_CONNECT_TO_SERVER = "Connect to server", - SAMR_ACCESS_SHUTDOWN_SERVER = "Shutdown server", - SAMR_ACCESS_INITIALIZE_SERVER = "Initialize server", - SAMR_ACCESS_CREATE_DOMAIN = "Create domain", - SAMR_ACCESS_ENUM_DOMAINS = "Enum domains", - SAMR_ACCESS_OPEN_DOMAIN = "Open domain" + SAMR_ACCESS_CONNECT_TO_SERVER = "Connect to server", + SAMR_ACCESS_SHUTDOWN_SERVER = "Shutdown server", + SAMR_ACCESS_INITIALIZE_SERVER = "Initialize server", + SAMR_ACCESS_CREATE_DOMAIN = "Create domain", + SAMR_ACCESS_ENUM_DOMAINS = "Enum domains", + SAMR_ACCESS_OPEN_DOMAIN = "Open domain" } ---Marshall a samr_ConnectAccessMask. This datatype is tied to the table above with that @@ -3645,13 +3645,13 @@ local samr_ConnectAccessMask_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_samr_ConnectAccessMask(accessmask) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_ConnectAccessMask()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_ConnectAccessMask()")) - result = marshall_Enum32(accessmask, samr_ConnectAccessMask) + result = marshall_Enum32(accessmask, samr_ConnectAccessMask) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_ConnectAccessMask()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_ConnectAccessMask()")) + return result end ---Unmarshall a samr_ConnectAccessMask. This datatype is tied to the table with that name. @@ -3660,13 +3660,13 @@ end --@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_ConnectAccessMask(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_ConnectAccessMask()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_ConnectAccessMask()")) - pos, result = unmarshall_Enum32(data, pos, samr_ConnectAccessMask) + pos, result = unmarshall_Enum32(data, pos, samr_ConnectAccessMask) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_ConnectAccessMask()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_ConnectAccessMask()")) + return pos, result end ---Convert a samr_ConnectAccessMask value to a string that can be shown to the user. This is @@ -3675,42 +3675,42 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_ConnectAccessMask_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering samr_ConnectAccessMask_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering samr_ConnectAccessMask_tostr()")) - result = samr_ConnectAccessMask_str[val] + result = samr_ConnectAccessMask_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving samr_ConnectAccessMask_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving samr_ConnectAccessMask_tostr()")) + return result end local samr_DomainAccessMask = { - DOMAIN_ACCESS_LOOKUP_INFO_1 = 0x00000001, - DOMAIN_ACCESS_SET_INFO_1 = 0x00000002, - DOMAIN_ACCESS_LOOKUP_INFO_2 = 0x00000004, - DOMAIN_ACCESS_SET_INFO_2 = 0x00000008, - DOMAIN_ACCESS_CREATE_USER = 0x00000010, - DOMAIN_ACCESS_CREATE_GROUP = 0x00000020, - DOMAIN_ACCESS_CREATE_ALIAS = 0x00000040, - DOMAIN_ACCESS_LOOKUP_ALIAS = 0x00000080, - DOMAIN_ACCESS_ENUM_ACCOUNTS = 0x00000100, - DOMAIN_ACCESS_OPEN_ACCOUNT = 0x00000200, - DOMAIN_ACCESS_SET_INFO_3 = 0x00000400 + DOMAIN_ACCESS_LOOKUP_INFO_1 = 0x00000001, + DOMAIN_ACCESS_SET_INFO_1 = 0x00000002, + DOMAIN_ACCESS_LOOKUP_INFO_2 = 0x00000004, + DOMAIN_ACCESS_SET_INFO_2 = 0x00000008, + DOMAIN_ACCESS_CREATE_USER = 0x00000010, + DOMAIN_ACCESS_CREATE_GROUP = 0x00000020, + DOMAIN_ACCESS_CREATE_ALIAS = 0x00000040, + DOMAIN_ACCESS_LOOKUP_ALIAS = 0x00000080, + DOMAIN_ACCESS_ENUM_ACCOUNTS = 0x00000100, + DOMAIN_ACCESS_OPEN_ACCOUNT = 0x00000200, + DOMAIN_ACCESS_SET_INFO_3 = 0x00000400 } local samr_DomainAccessMask_str = { - DOMAIN_ACCESS_LOOKUP_INFO_1 = "Lookup info (1)", - DOMAIN_ACCESS_SET_INFO_1 = "Set info (1)", - DOMAIN_ACCESS_LOOKUP_INFO_2 = "Lookup info (2)", - DOMAIN_ACCESS_SET_INFO_2 = "Set info (2)", - DOMAIN_ACCESS_CREATE_USER = "Create user", - DOMAIN_ACCESS_CREATE_GROUP = "Create group", - DOMAIN_ACCESS_CREATE_ALIAS = "Create alias", - DOMAIN_ACCESS_LOOKUP_ALIAS = "Lookup alias", - DOMAIN_ACCESS_ENUM_ACCOUNTS = "Enum accounts", - DOMAIN_ACCESS_OPEN_ACCOUNT = "Open account", - DOMAIN_ACCESS_SET_INFO_3 = "Set info (3)" + DOMAIN_ACCESS_LOOKUP_INFO_1 = "Lookup info (1)", + DOMAIN_ACCESS_SET_INFO_1 = "Set info (1)", + DOMAIN_ACCESS_LOOKUP_INFO_2 = "Lookup info (2)", + DOMAIN_ACCESS_SET_INFO_2 = "Set info (2)", + DOMAIN_ACCESS_CREATE_USER = "Create user", + DOMAIN_ACCESS_CREATE_GROUP = "Create group", + DOMAIN_ACCESS_CREATE_ALIAS = "Create alias", + DOMAIN_ACCESS_LOOKUP_ALIAS = "Lookup alias", + DOMAIN_ACCESS_ENUM_ACCOUNTS = "Enum accounts", + DOMAIN_ACCESS_OPEN_ACCOUNT = "Open account", + DOMAIN_ACCESS_SET_INFO_3 = "Set info (3)" } ---Marshall a samr_DomainAccessMask. This datatype is tied to the table above with that @@ -3720,13 +3720,13 @@ local samr_DomainAccessMask_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_samr_DomainAccessMask(accessmask) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_DomainAccessMask()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_DomainAccessMask()")) - result = marshall_Enum32(accessmask, samr_DomainAccessMask) + result = marshall_Enum32(accessmask, samr_DomainAccessMask) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_DomainAccessMask()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_DomainAccessMask()")) + return result end ---Unmarshall a samr_DomainAccessMask. This datatype is tied to the table with that name. @@ -3735,13 +3735,13 @@ end --@param pos The position within the data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomainAccessMask(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainAccessMask()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainAccessMask()")) - pos, result = unmarshall_Enum32(data, pos, samr_DomainAccessMask) + pos, result = unmarshall_Enum32(data, pos, samr_DomainAccessMask) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainAccessMask()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainAccessMask()")) + return pos, result end ---Convert a samr_DomainAccessMask value to a string that can be shown to the user. This is @@ -3750,60 +3750,60 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_DomainAccessMask_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering samr_DomainAccessMask_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering samr_DomainAccessMask_tostr()")) - result = samr_DomainAccessMask_str[val] + result = samr_DomainAccessMask_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving samr_DomainAccessMask_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving samr_DomainAccessMask_tostr()")) + return result end local samr_AcctFlags = { - ACB_NONE = 0x0000000, - ACB_DISABLED = 0x00000001, -- User account disabled - ACB_HOMDIRREQ = 0x00000002, -- Home directory required - ACB_PWNOTREQ = 0x00000004, -- User password not required - ACB_TEMPDUP = 0x00000008, -- Temporary duplicate account - ACB_NORMAL = 0x00000010, -- Normal user account - ACB_MNS = 0x00000020, -- MNS logon user account - ACB_DOMTRUST = 0x00000040, -- Interdomain trust account - ACB_WSTRUST = 0x00000080, -- Workstation trust account - ACB_SVRTRUST = 0x00000100, -- Server trust account - ACB_PWNOEXP = 0x00000200, -- User password does not expire - ACB_AUTOLOCK = 0x00000400, -- Account auto locked - ACB_ENC_TXT_PWD_ALLOWED = 0x00000800, -- Encryped text password is allowed - ACB_SMARTCARD_REQUIRED = 0x00001000, -- Smart Card required - ACB_TRUSTED_FOR_DELEGATION = 0x00002000, -- Trusted for Delegation - ACB_NOT_DELEGATED = 0x00004000, -- Not delegated - ACB_USE_DES_KEY_ONLY = 0x00008000, -- Use DES key only - ACB_DONT_REQUIRE_PREAUTH = 0x00010000, -- Preauth not required - ACB_PW_EXPIRED = 0x00020000, -- Password Expired - ACB_NO_AUTH_DATA_REQD = 0x00080000 -- No authorization data required + ACB_NONE = 0x0000000, + ACB_DISABLED = 0x00000001, -- User account disabled + ACB_HOMDIRREQ = 0x00000002, -- Home directory required + ACB_PWNOTREQ = 0x00000004, -- User password not required + ACB_TEMPDUP = 0x00000008, -- Temporary duplicate account + ACB_NORMAL = 0x00000010, -- Normal user account + ACB_MNS = 0x00000020, -- MNS logon user account + ACB_DOMTRUST = 0x00000040, -- Interdomain trust account + ACB_WSTRUST = 0x00000080, -- Workstation trust account + ACB_SVRTRUST = 0x00000100, -- Server trust account + ACB_PWNOEXP = 0x00000200, -- User password does not expire + ACB_AUTOLOCK = 0x00000400, -- Account auto locked + ACB_ENC_TXT_PWD_ALLOWED = 0x00000800, -- Encryped text password is allowed + ACB_SMARTCARD_REQUIRED = 0x00001000, -- Smart Card required + ACB_TRUSTED_FOR_DELEGATION = 0x00002000, -- Trusted for Delegation + ACB_NOT_DELEGATED = 0x00004000, -- Not delegated + ACB_USE_DES_KEY_ONLY = 0x00008000, -- Use DES key only + ACB_DONT_REQUIRE_PREAUTH = 0x00010000, -- Preauth not required + ACB_PW_EXPIRED = 0x00020000, -- Password Expired + ACB_NO_AUTH_DATA_REQD = 0x00080000 -- No authorization data required } local samr_AcctFlags_str = { - ACB_NONE = "n/a", - ACB_DISABLED = "Account disabled", - ACB_HOMDIRREQ = "Home directory required", - ACB_PWNOTREQ = "Password not required", - ACB_TEMPDUP = "Temporary duplicate account", - ACB_NORMAL = "Normal user account", - ACB_MNS = "MNS logon user account", - ACB_DOMTRUST = "Interdomain trust account", - ACB_WSTRUST = "Workstation trust account", - ACB_SVRTRUST = "Server trust account", - ACB_PWNOEXP = "Password does not expire", - ACB_AUTOLOCK = "Auto locked", - ACB_ENC_TXT_PWD_ALLOWED = "Encryped text password is allowed", - ACB_SMARTCARD_REQUIRED = "Smart Card required", - ACB_TRUSTED_FOR_DELEGATION = "Trusted for Delegation", - ACB_NOT_DELEGATED = "Not delegated", - ACB_USE_DES_KEY_ONLY = "Use DES key only", - ACB_DONT_REQUIRE_PREAUTH = "Preauth not required", - ACB_PW_EXPIRED = "Password Expired", - ACB_NO_AUTH_DATA_REQD = "No authorization data required" + ACB_NONE = "n/a", + ACB_DISABLED = "Account disabled", + ACB_HOMDIRREQ = "Home directory required", + ACB_PWNOTREQ = "Password not required", + ACB_TEMPDUP = "Temporary duplicate account", + ACB_NORMAL = "Normal user account", + ACB_MNS = "MNS logon user account", + ACB_DOMTRUST = "Interdomain trust account", + ACB_WSTRUST = "Workstation trust account", + ACB_SVRTRUST = "Server trust account", + ACB_PWNOEXP = "Password does not expire", + ACB_AUTOLOCK = "Auto locked", + ACB_ENC_TXT_PWD_ALLOWED = "Encryped text password is allowed", + ACB_SMARTCARD_REQUIRED = "Smart Card required", + ACB_TRUSTED_FOR_DELEGATION = "Trusted for Delegation", + ACB_NOT_DELEGATED = "Not delegated", + ACB_USE_DES_KEY_ONLY = "Use DES key only", + ACB_DONT_REQUIRE_PREAUTH = "Preauth not required", + ACB_PW_EXPIRED = "Password Expired", + ACB_NO_AUTH_DATA_REQD = "No authorization data required" } ---Marshall a samr_AcctFlags. This datatype is tied to the table above with that @@ -3813,13 +3813,13 @@ local samr_AcctFlags_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_samr_AcctFlags(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_AcctFlags()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_AcctFlags()")) - result = marshall_Enum32(flags, samr_AcctFlags) + result = marshall_Enum32(flags, samr_AcctFlags) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_AcctFlags()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_AcctFlags()")) + return result end ---Unmarshall a samr_AcctFlags. This datatype is tied to the table with that name. @@ -3828,13 +3828,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_samr_AcctFlags(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_AcctFlags()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_AcctFlags()")) - pos, str = unmarshall_Enum32_array(data, pos, samr_AcctFlags) + pos, str = unmarshall_Enum32_array(data, pos, samr_AcctFlags) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_AcctFlags()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_AcctFlags()")) + return pos, str end ---Convert a samr_AcctFlags value to a string that can be shown to the user. This is @@ -3843,32 +3843,32 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_AcctFlags_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering samr_AcctFlags_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering samr_AcctFlags_tostr()")) - result = samr_AcctFlags_str[val] + result = samr_AcctFlags_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving samr_AcctFlags_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving samr_AcctFlags_tostr()")) + return result end local samr_PasswordProperties = { - DOMAIN_PASSWORD_COMPLEX = 0x00000001, - DOMAIN_PASSWORD_NO_ANON_CHANGE = 0x00000002, - DOMAIN_PASSWORD_NO_CLEAR_CHANGE = 0x00000004, - DOMAIN_PASSWORD_LOCKOUT_ADMINS = 0x00000008, - DOMAIN_PASSWORD_STORE_CLEARTEXT = 0x00000010, - DOMAIN_REFUSE_PASSWORD_CHANGE = 0x00000020 + DOMAIN_PASSWORD_COMPLEX = 0x00000001, + DOMAIN_PASSWORD_NO_ANON_CHANGE = 0x00000002, + DOMAIN_PASSWORD_NO_CLEAR_CHANGE = 0x00000004, + DOMAIN_PASSWORD_LOCKOUT_ADMINS = 0x00000008, + DOMAIN_PASSWORD_STORE_CLEARTEXT = 0x00000010, + DOMAIN_REFUSE_PASSWORD_CHANGE = 0x00000020 } local samr_PasswordProperties_str = { - DOMAIN_PASSWORD_COMPLEX = "Complexity requirements exist", - DOMAIN_PASSWORD_NO_ANON_CHANGE = "Must be logged in to change password", - DOMAIN_PASSWORD_NO_CLEAR_CHANGE = "Cannot change passwords in cleartext", - DOMAIN_PASSWORD_LOCKOUT_ADMINS = "Admin account can be locked out", - DOMAIN_PASSWORD_STORE_CLEARTEXT = "Cleartext passwords can be stored", - DOMAIN_REFUSE_PASSWORD_CHANGE = "Passwords cannot be changed" + DOMAIN_PASSWORD_COMPLEX = "Complexity requirements exist", + DOMAIN_PASSWORD_NO_ANON_CHANGE = "Must be logged in to change password", + DOMAIN_PASSWORD_NO_CLEAR_CHANGE = "Cannot change passwords in cleartext", + DOMAIN_PASSWORD_LOCKOUT_ADMINS = "Admin account can be locked out", + DOMAIN_PASSWORD_STORE_CLEARTEXT = "Cleartext passwords can be stored", + DOMAIN_REFUSE_PASSWORD_CHANGE = "Passwords cannot be changed" } ---Marshall a samr_PasswordProperties. This datatype is tied to the table above with that @@ -3878,13 +3878,13 @@ local samr_PasswordProperties_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_samr_PasswordProperties(properties) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_PasswordProperties()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_samr_PasswordProperties()")) - result = marshall_Enum32(properties, samr_PasswordProperties) + result = marshall_Enum32(properties, samr_PasswordProperties) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_PasswordProperties()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_samr_PasswordProperties()")) + return result end ---Unmarshall a samr_PasswordProperties. This datatype is tied to the table with that name. @@ -3893,13 +3893,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_samr_PasswordProperties(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_PasswordProperties()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_PasswordProperties()")) - pos, str = unmarshall_Enum32_array(data, pos, samr_PasswordProperties) + pos, str = unmarshall_Enum32_array(data, pos, samr_PasswordProperties) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_PasswordProperties()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_PasswordProperties()")) + return pos, str end ---Convert a samr_PasswordProperties value to a string that can be shown to the user. This is @@ -3908,13 +3908,13 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function samr_PasswordProperties_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering samr_PasswordProperties_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering samr_PasswordProperties_tostr()")) - result = samr_PasswordProperties_str[val] + result = samr_PasswordProperties_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving samr_PasswordProperties_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving samr_PasswordProperties_tostr()")) + return result end @@ -3939,23 +3939,23 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_samr_SamEntry(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamEntry()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamEntry()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['idx'] = unmarshall_int32(data, pos) - pos, result['name'] = unmarshall_lsa_String_internal(HEAD, data, pos) - end + if(location == HEAD or location == ALL) then + pos, result['idx'] = unmarshall_int32(data, pos) + pos, result['name'] = unmarshall_lsa_String_internal(HEAD, data, pos) + end - if(location == BODY or location == ALL) then - pos, result['name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['name']) - end + if(location == BODY or location == ALL) then + pos, result['name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['name']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamEntry()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamEntry()")) + return pos, result end ---Unmarshall a struct with the following definition: @@ -3971,14 +3971,14 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_SamArray(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamArray()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamArray()")) - pos, result['count'] = unmarshall_int32(data, pos) - pos, result['entries'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_samr_SamEntry, {}}) + pos, result['count'] = unmarshall_int32(data, pos) + pos, result['entries'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_samr_SamEntry, {}}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamArray()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamArray()")) + return pos, result end ---Unmarshall a pointer to a samr_SamArray type. See unmarshall_samr_SamArray for @@ -3988,13 +3988,13 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_SamArray_ptr(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamArray_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_SamArray_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_samr_SamArray, {}) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_samr_SamArray, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamArray_ptr()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_SamArray_ptr()")) + return pos, result end ---Unmarshall a struct with the following definition: @@ -4022,29 +4022,29 @@ end -- anything. --@return (pos, result) The new position in data, and a table representing the datatype. local function unmarshall_samr_DispEntryGeneral(location, data, pos, result) - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispEntryGeneral()")) - if(result == nil) then - result = {} - end + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispEntryGeneral()")) + if(result == nil) then + result = {} + end - if(location == HEAD or location == ALL) then - pos, result['idx'] = unmarshall_int32(data, pos) - pos, result['rid'] = unmarshall_int32(data, pos) - pos, result['acct_flags'] = unmarshall_samr_AcctFlags(data, pos) - pos, result['account_name'] = unmarshall_lsa_String_internal(HEAD, data, pos) - pos, result['description'] = unmarshall_lsa_String_internal(HEAD, data, pos) - pos, result['full_name'] = unmarshall_lsa_String_internal(HEAD, data, pos) - end + if(location == HEAD or location == ALL) then + pos, result['idx'] = unmarshall_int32(data, pos) + pos, result['rid'] = unmarshall_int32(data, pos) + pos, result['acct_flags'] = unmarshall_samr_AcctFlags(data, pos) + pos, result['account_name'] = unmarshall_lsa_String_internal(HEAD, data, pos) + pos, result['description'] = unmarshall_lsa_String_internal(HEAD, data, pos) + pos, result['full_name'] = unmarshall_lsa_String_internal(HEAD, data, pos) + end - if(location == BODY or location == ALL) then - pos, result['account_name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['account_name']) - pos, result['description'] = unmarshall_lsa_String_internal(BODY, data, pos, result['description']) - pos, result['full_name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['full_name']) - end + if(location == BODY or location == ALL) then + pos, result['account_name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['account_name']) + pos, result['description'] = unmarshall_lsa_String_internal(BODY, data, pos, result['description']) + pos, result['full_name'] = unmarshall_lsa_String_internal(BODY, data, pos, result['full_name']) + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispEntryGeneral()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispEntryGeneral()")) + return pos, result end ---Unmarshall a struct with the following definition: @@ -4060,14 +4060,14 @@ end --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DispInfoGeneral(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispInfoGeneral()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispInfoGeneral()")) - pos, result['count'] = unmarshall_int32(data, pos) - pos, result['entries'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_samr_DispEntryGeneral, {}}) + pos, result['count'] = unmarshall_int32(data, pos) + pos, result['entries'] = unmarshall_ptr(ALL, data, pos, unmarshall_array, {result['count'], unmarshall_samr_DispEntryGeneral, {}}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispInfoGeneral()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispInfoGeneral()")) + return pos, result end @@ -4088,118 +4088,118 @@ end --@return (pos, result) The new position in data, and a table representing the datatype. It may also return -- nil, if there was an error. function unmarshall_samr_DispInfo(data, pos) - local level - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispInfo()")) + local level + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DispInfo()")) - pos, level = unmarshall_int16(data, pos) + pos, level = unmarshall_int16(data, pos) - if(level == 1) then - pos, result = unmarshall_samr_DispInfoGeneral(data, pos) - else - stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for samr_DispInfo: %d", level) - pos, result = nil, nil - end + if(level == 1) then + pos, result = unmarshall_samr_DispInfoGeneral(data, pos) + else + stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for samr_DispInfo: %d", level) + pos, result = nil, nil + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispInfo()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DispInfo()")) + return pos, result end ---Unmarshall a struct with the following definition: -- -- --- typedef struct { --- uint16 min_password_length; --- uint16 password_history_length; --- samr_PasswordProperties password_properties; --- /* yes, these are signed. They are in negative 100ns */ --- dlong max_password_age; --- dlong min_password_age; --- } samr_DomInfo1; +-- typedef struct { +-- uint16 min_password_length; +-- uint16 password_history_length; +-- samr_PasswordProperties password_properties; +-- /* yes, these are signed. They are in negative 100ns */ +-- dlong max_password_age; +-- dlong min_password_age; +-- } samr_DomInfo1; -- -- --@param data The data being processed. --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomInfo1(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo1()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo1()")) - pos, result['min_password_length'] = unmarshall_int16(data, pos, false) - pos, result['password_history_length'] = unmarshall_int16(data, pos, false) - pos, result['password_properties'] = unmarshall_samr_PasswordProperties(data, pos) - pos, result['max_password_age'] = unmarshall_hyper(data, pos) - pos, result['min_password_age'] = unmarshall_hyper(data, pos) + pos, result['min_password_length'] = unmarshall_int16(data, pos, false) + pos, result['password_history_length'] = unmarshall_int16(data, pos, false) + pos, result['password_properties'] = unmarshall_samr_PasswordProperties(data, pos) + pos, result['max_password_age'] = unmarshall_hyper(data, pos) + pos, result['min_password_age'] = unmarshall_hyper(data, pos) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo1()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo1()")) + return pos, result end ---Unmarshall a struct with the following definition: -- -- --- typedef struct { --- hyper sequence_num; --- NTTIME domain_create_time; --- } samr_DomInfo8; +-- typedef struct { +-- hyper sequence_num; +-- NTTIME domain_create_time; +-- } samr_DomInfo8; -- -- --@param data The data being processed. --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomInfo8(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo8()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo8()")) - pos, result['sequence_num'] = unmarshall_hyper(data, pos) - pos, result['domain_create_time'] = unmarshall_NTTIME(data, pos) + pos, result['sequence_num'] = unmarshall_hyper(data, pos) + pos, result['domain_create_time'] = unmarshall_NTTIME(data, pos) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo8()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo8()")) + return pos, result end ---Unmarshall a struct with the following definition: -- -- --- typedef struct { --- hyper lockout_duration; --- hyper lockout_window; --- uint16 lockout_threshold; --- } samr_DomInfo12; +-- typedef struct { +-- hyper lockout_duration; +-- hyper lockout_window; +-- uint16 lockout_threshold; +-- } samr_DomInfo12; -- -- --@param data The data being processed. --@param pos The position within data. --@return (pos, result) The new position in data, and a table representing the datatype. function unmarshall_samr_DomInfo12(data, pos) - local result = {} - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo12()")) + local result = {} + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomInfo12()")) - pos, result['lockout_duration'] = unmarshall_hyper(data, pos) - pos, result['lockout_window'] = unmarshall_hyper(data, pos) - pos, result['lockout_threshold'] = unmarshall_int16(data, pos) + pos, result['lockout_duration'] = unmarshall_hyper(data, pos) + pos, result['lockout_window'] = unmarshall_hyper(data, pos) + pos, result['lockout_threshold'] = unmarshall_int16(data, pos) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo12()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomInfo12()")) + return pos, result end ---Unmarshall a union with the following definition: -- -- --- typedef [switch_type(uint16)] union { --- [case(1)] samr_DomInfo1 info1; --- [case(2)] samr_DomInfo2 info2; --- [case(3)] samr_DomInfo3 info3; --- [case(4)] samr_DomInfo4 info4; --- [case(5)] samr_DomInfo5 info5; --- [case(6)] samr_DomInfo6 info6; --- [case(7)] samr_DomInfo7 info7; --- [case(8)] samr_DomInfo8 info8; --- [case(9)] samr_DomInfo9 info9; --- [case(11)] samr_DomInfo11 info11; --- [case(12)] samr_DomInfo12 info12; --- [case(13)] samr_DomInfo13 info13; --- } samr_DomainInfo; +-- typedef [switch_type(uint16)] union { +-- [case(1)] samr_DomInfo1 info1; +-- [case(2)] samr_DomInfo2 info2; +-- [case(3)] samr_DomInfo3 info3; +-- [case(4)] samr_DomInfo4 info4; +-- [case(5)] samr_DomInfo5 info5; +-- [case(6)] samr_DomInfo6 info6; +-- [case(7)] samr_DomInfo7 info7; +-- [case(8)] samr_DomInfo8 info8; +-- [case(9)] samr_DomInfo9 info9; +-- [case(11)] samr_DomInfo11 info11; +-- [case(12)] samr_DomInfo12 info12; +-- [case(13)] samr_DomInfo13 info13; +-- } samr_DomainInfo; -- -- --@param data The data being processed. @@ -4207,25 +4207,25 @@ end --@return (pos, result) The new position in data, and a table representing the datatype. May return -- nil if there was an error. function unmarshall_samr_DomainInfo(data, pos) - local level - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo()")) + local level + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo()")) - pos, level = unmarshall_int16(data, pos) + pos, level = unmarshall_int16(data, pos) - if(level == 1) then - pos, result = unmarshall_samr_DomInfo1(data, pos) - elseif(level == 8) then - pos, result = unmarshall_samr_DomInfo8(data, pos) - elseif(level == 12) then - pos, result = unmarshall_samr_DomInfo12(data, pos) - else - stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for samr_DomainInfo: %d", level) - pos, result = nil, nil - end + if(level == 1) then + pos, result = unmarshall_samr_DomInfo1(data, pos) + elseif(level == 8) then + pos, result = unmarshall_samr_DomInfo8(data, pos) + elseif(level == 12) then + pos, result = unmarshall_samr_DomInfo12(data, pos) + else + stdnse.print_debug(1, "MSRPC: ERROR: Server returned an unknown level for samr_DomainInfo: %d", level) + pos, result = nil, nil + end - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainInfo()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainInfo()")) + return pos, result end ---Unmarshall a pointer to a samr_DomainInfo. See unmarshall_samr_DomainInfo for @@ -4236,13 +4236,13 @@ end --@return (pos, result) The new position in data, and a table representing the datatype. May return -- nil if there was an error. function unmarshall_samr_DomainInfo_ptr(data, pos) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo_ptr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_samr_DomainInfo_ptr()")) - pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_samr_DomainInfo, {}) + pos, result = unmarshall_ptr(ALL, data, pos, unmarshall_samr_DomainInfo, {}) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainInfo_ptr()")) - return pos, result + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_samr_DomainInfo_ptr()")) + return pos, result end ---Unmarshall a structure with the following definition: @@ -4259,11 +4259,11 @@ end --@return (pos, result) The new position in data, and a table representing the datatype. May return -- nil if there was an error. function unmarshall_samr_Ids(data, pos) - local array + local array - pos, array = unmarshall_int32_array_ptr(data, pos) + pos, array = unmarshall_int32_array_ptr(data, pos) - return pos, array + return pos, array end ---------------------------------- @@ -4273,27 +4273,27 @@ end local svcctl_ControlCode = { - SERVICE_CONTROL_CONTINUE = 0x00000003, - SERVICE_CONTROL_INTERROGATE = 0x00000004, - SERVICE_CONTROL_NETBINDADD = 0x00000007, - SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A, - SERVICE_CONTROL_NETBINDENABLE = 0x00000009, - SERVICE_CONTROL_NETBINDREMOVE = 0x00000008, - SERVICE_CONTROL_PARAMCHANGE = 0x00000006, - SERVICE_CONTROL_PAUSE = 0x00000002, - SERVICE_CONTROL_STOP = 0x00000001, + SERVICE_CONTROL_CONTINUE = 0x00000003, + SERVICE_CONTROL_INTERROGATE = 0x00000004, + SERVICE_CONTROL_NETBINDADD = 0x00000007, + SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A, + SERVICE_CONTROL_NETBINDENABLE = 0x00000009, + SERVICE_CONTROL_NETBINDREMOVE = 0x00000008, + SERVICE_CONTROL_PARAMCHANGE = 0x00000006, + SERVICE_CONTROL_PAUSE = 0x00000002, + SERVICE_CONTROL_STOP = 0x00000001, } local svcctl_ControlCode_str = { - SERVICE_CONTROL_CONTINUE = "Notifies a paused service that it should resume.", - SERVICE_CONTROL_INTERROGATE = "Notifies a service that it should report its current status information to the service control manager.", - SERVICE_CONTROL_NETBINDADD = "Notifies a network service that there is a new component for binding. Deprecated.", - SERVICE_CONTROL_NETBINDDISABLE = "Notifies a network service that one of its bindings has been disabled. Deprecated.", - SERVICE_CONTROL_NETBINDENABLE = "Notifies a network service that a disabled binding has been enabled. Deprecated", - SERVICE_CONTROL_NETBINDREMOVE = "Notifies a network service that a component for binding has been removed. Deprecated", - SERVICE_CONTROL_PARAMCHANGE = "Notifies a service that its startup parameters have changed.", - SERVICE_CONTROL_PAUSE = "Notifies a service that it should pause.", - SERVICE_CONTROL_STOP = "Notifies a service that it should stop." + SERVICE_CONTROL_CONTINUE = "Notifies a paused service that it should resume.", + SERVICE_CONTROL_INTERROGATE = "Notifies a service that it should report its current status information to the service control manager.", + SERVICE_CONTROL_NETBINDADD = "Notifies a network service that there is a new component for binding. Deprecated.", + SERVICE_CONTROL_NETBINDDISABLE = "Notifies a network service that one of its bindings has been disabled. Deprecated.", + SERVICE_CONTROL_NETBINDENABLE = "Notifies a network service that a disabled binding has been enabled. Deprecated", + SERVICE_CONTROL_NETBINDREMOVE = "Notifies a network service that a component for binding has been removed. Deprecated", + SERVICE_CONTROL_PARAMCHANGE = "Notifies a service that its startup parameters have changed.", + SERVICE_CONTROL_PAUSE = "Notifies a service that it should pause.", + SERVICE_CONTROL_STOP = "Notifies a service that it should stop." } @@ -4304,13 +4304,13 @@ local svcctl_ControlCode_str = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_svcctl_ControlCode(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_ControlCode()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_ControlCode()")) - result = marshall_Enum32(flags, svcctl_ControlCode) + result = marshall_Enum32(flags, svcctl_ControlCode) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_svcctl_ControlCode()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_svcctl_ControlCode()")) + return result end ---Unmarshall a svcctl_ControlCode. This datatype is tied to the table with that name. @@ -4319,13 +4319,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_svcctl_ControlCode(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_ControlCode()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_ControlCode()")) - pos, str = unmarshall_Enum32_array(data, pos, svcctl_ControlCode) + pos, str = unmarshall_Enum32_array(data, pos, svcctl_ControlCode) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_svcctl_ControlCode()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_svcctl_ControlCode()")) + return pos, str end ---Convert a svcctl_ControlCode value to a string that can be shown to the user. This is @@ -4334,25 +4334,25 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function svcctl_ControlCode_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_ControlCode_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_ControlCode_tostr()")) - result = svcctl_ControlCode_str[val] + result = svcctl_ControlCode_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving svcctl_ControlCode_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving svcctl_ControlCode_tostr()")) + return result end local svcctl_Type = { - SERVICE_TYPE_KERNEL_DRIVER = 0x01, - SERVICE_TYPE_FS_DRIVER = 0x02, - SERVICE_TYPE_ADAPTER = 0x04, - SERVICE_TYPE_RECOGNIZER_DRIVER = 0x08, - SERVICE_TYPE_DRIVER = 0x0B, - SERVICE_TYPE_WIN32_OWN_PROCESS = 0x10, - SERVICE_TYPE_WIN32_SHARE_PROCESS = 0x20, - SERVICE_TYPE_WIN32 = 0x30 + SERVICE_TYPE_KERNEL_DRIVER = 0x01, + SERVICE_TYPE_FS_DRIVER = 0x02, + SERVICE_TYPE_ADAPTER = 0x04, + SERVICE_TYPE_RECOGNIZER_DRIVER = 0x08, + SERVICE_TYPE_DRIVER = 0x0B, + SERVICE_TYPE_WIN32_OWN_PROCESS = 0x10, + SERVICE_TYPE_WIN32_SHARE_PROCESS = 0x20, + SERVICE_TYPE_WIN32 = 0x30 } ---Marshall a svcctl_Type. This datatype is tied to the table above with that @@ -4362,13 +4362,13 @@ local svcctl_Type = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_svcctl_Type(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_Type()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_Type()")) - result = marshall_Enum32(flags, svcctl_Type) + result = marshall_Enum32(flags, svcctl_Type) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_svcctl_Type()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_svcctl_Type()")) + return result end ---Unmarshall a svcctl_Type. This datatype is tied to the table with that name. @@ -4377,13 +4377,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_svcctl_Type(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_Type()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_Type()")) - pos, str = unmarshall_Enum32_array(data, pos, svcctl_Type) + pos, str = unmarshall_Enum32_array(data, pos, svcctl_Type) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_svcctl_Type()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_svcctl_Type()")) + return pos, str end --[[Convert a svcctl_Type value to a string that can be shown to the user. This is @@ -4392,22 +4392,22 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function svcctl_Type_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_Type_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_Type_tostr()")) - result = svcctl_Type_str[val] + result = svcctl_Type_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving svcctl_Type_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving svcctl_Type_tostr()")) + return result end]]-- local svcctl_State = { - SERVICE_STATE_ACTIVE = 0x01, - SERVICE_STATE_INACTIVE = 0x02, - SERVICE_STATE_ALL = 0x03 + SERVICE_STATE_ACTIVE = 0x01, + SERVICE_STATE_INACTIVE = 0x02, + SERVICE_STATE_ALL = 0x03 } ---Marshall a svcctl_State. This datatype is tied to the table above with that -- name. @@ -4416,13 +4416,13 @@ local svcctl_State = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_svcctl_State(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_State()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_svcctl_State()")) - result = marshall_Enum32(flags, svcctl_State) + result = marshall_Enum32(flags, svcctl_State) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_svcctl_State()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_svcctl_State()")) + return result end ---Unmarshall a svcctl_State. This datatype is tied to the table with that name. @@ -4431,13 +4431,13 @@ end --@param pos The position within the data. --@return (pos, str) The new position, and the string representing the datatype. function unmarshall_svcctl_State(data, pos) - local str - stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_State()")) + local str + stdnse.print_debug(4, string.format("MSRPC: Entering unmarshall_svcctl_State()")) - pos, str = unmarshall_Enum32_array(data, pos, svcctl_State) + pos, str = unmarshall_Enum32_array(data, pos, svcctl_State) - stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_svcctl_State()")) - return pos, str + stdnse.print_debug(4, string.format("MSRPC: Leaving unmarshall_svcctl_State()")) + return pos, str end --[[Convert a svcctl_State value to a string that can be shown to the user. This is @@ -4446,13 +4446,13 @@ end --@param val The string value (returned by the unmarshall_ function) to convert. --@return A string suitable for displaying to the user, or nil if it wasn't found. function svcctl_State_tostr(val) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_State_tostr()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering svcctl_State_tostr()")) - result = svcctl_State_str[val] + result = svcctl_State_str[val] - stdnse.print_debug(4, string.format("MSRPC: Leaving svcctl_State_tostr()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving svcctl_State_tostr()")) + return result end]]-- @@ -4475,54 +4475,54 @@ end]]-- --@param pos The position within the data. --@return (pos, table) The new position, and the table of values. function unmarshall_SERVICE_STATUS(data, pos) - local result = {} + local result = {} - pos, result['type'] = unmarshall_svcctl_Type(data, pos) - pos, result['state'] = unmarshall_svcctl_State(data, pos) - pos, result['controls_accepted'] = unmarshall_svcctl_ControlCode(data, pos) - pos, result['win32_exit_code'] = unmarshall_int32(data, pos) - pos, result['service_exit_code'] = unmarshall_int32(data, pos) - pos, result['check_point'] = unmarshall_int32(data, pos) - pos, result['wait_hint'] = unmarshall_int32(data, pos) + pos, result['type'] = unmarshall_svcctl_Type(data, pos) + pos, result['state'] = unmarshall_svcctl_State(data, pos) + pos, result['controls_accepted'] = unmarshall_svcctl_ControlCode(data, pos) + pos, result['win32_exit_code'] = unmarshall_int32(data, pos) + pos, result['service_exit_code'] = unmarshall_int32(data, pos) + pos, result['check_point'] = unmarshall_int32(data, pos) + pos, result['wait_hint'] = unmarshall_int32(data, pos) - return pos, result + return pos, result end local atsvc_DaysOfMonth = { - First = 0x00000001, - Second = 0x00000002, - Third = 0x00000004, - Fourth = 0x00000008, - Fifth = 0x00000010, - Sixth = 0x00000020, - Seventh = 0x00000040, - Eight = 0x00000080, - Ninth = 0x00000100, - Tenth = 0x00000200, - Eleventh = 0x00000400, - Twelfth = 0x00000800, - Thitteenth = 0x00001000, - Fourteenth = 0x00002000, - Fifteenth = 0x00004000, - Sixteenth = 0x00008000, - Seventeenth = 0x00010000, - Eighteenth = 0x00020000, - Ninteenth = 0x00040000, - Twentyth = 0x00080000, - Twentyfirst = 0x00100000, - Twentysecond = 0x00200000, - Twentythird = 0x00400000, - Twentyfourth = 0x00800000, - Twentyfifth = 0x01000000, - Twentysixth = 0x02000000, - Twentyseventh = 0x04000000, - Twentyeighth = 0x08000000, - Twentyninth = 0x10000000, - Thirtieth = 0x20000000, - Thirtyfirst = 0x40000000 + First = 0x00000001, + Second = 0x00000002, + Third = 0x00000004, + Fourth = 0x00000008, + Fifth = 0x00000010, + Sixth = 0x00000020, + Seventh = 0x00000040, + Eight = 0x00000080, + Ninth = 0x00000100, + Tenth = 0x00000200, + Eleventh = 0x00000400, + Twelfth = 0x00000800, + Thitteenth = 0x00001000, + Fourteenth = 0x00002000, + Fifteenth = 0x00004000, + Sixteenth = 0x00008000, + Seventeenth = 0x00010000, + Eighteenth = 0x00020000, + Ninteenth = 0x00040000, + Twentyth = 0x00080000, + Twentyfirst = 0x00100000, + Twentysecond = 0x00200000, + Twentythird = 0x00400000, + Twentyfourth = 0x00800000, + Twentyfifth = 0x01000000, + Twentysixth = 0x02000000, + Twentyseventh = 0x04000000, + Twentyeighth = 0x08000000, + Twentyninth = 0x10000000, + Thirtieth = 0x20000000, + Thirtyfirst = 0x40000000 } ---Marshall a atsvc_DaysOfMonth. This datatype is tied to the table above with that @@ -4532,23 +4532,23 @@ local atsvc_DaysOfMonth = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_atsvc_DaysOfMonth(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_DaysOfMonth()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_DaysOfMonth()")) - result = marshall_Enum32(flags, atsvc_DaysOfMonth) + result = marshall_Enum32(flags, atsvc_DaysOfMonth) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_atsvc_DaysOfMonth()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_atsvc_DaysOfMonth()")) + return result end local atsvc_Flags = { - JOB_RUN_PERIODICALLY = 0x01, - JOB_EXEC_ERROR = 0x02, - JOB_RUNS_TODAY = 0x04, - JOB_ADD_CURRENT_DATE = 0x08, - JOB_NONINTERACTIVE = 0x10 + JOB_RUN_PERIODICALLY = 0x01, + JOB_EXEC_ERROR = 0x02, + JOB_RUNS_TODAY = 0x04, + JOB_ADD_CURRENT_DATE = 0x08, + JOB_NONINTERACTIVE = 0x10 } ---Marshall a atsvc_Flags. This datatype is tied to the table above with that -- name. @@ -4557,25 +4557,25 @@ local atsvc_Flags = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_atsvc_Flags(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_Flags()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_Flags()")) - result = marshall_Enum8(flags, atsvc_Flags, false) + result = marshall_Enum8(flags, atsvc_Flags, false) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_atsvc_Flags()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_atsvc_Flags()")) + return result end local atsvc_DaysOfWeek = { - DAYSOFWEEK_MONDAY = 0x01, - DAYSOFWEEK_TUESDAY = 0x02, - DAYSOFWEEK_WEDNESDAY = 0x04, - DAYSOFWEEK_THURSDAY = 0x08, - DAYSOFWEEK_FRIDAY = 0x10, - DAYSOFWEEK_SATURDAY = 0x20, - DAYSOFWEEK_SUNDAY = 0x40 + DAYSOFWEEK_MONDAY = 0x01, + DAYSOFWEEK_TUESDAY = 0x02, + DAYSOFWEEK_WEDNESDAY = 0x04, + DAYSOFWEEK_THURSDAY = 0x08, + DAYSOFWEEK_FRIDAY = 0x10, + DAYSOFWEEK_SATURDAY = 0x20, + DAYSOFWEEK_SUNDAY = 0x40 } ---Marshall a atsvc_DaysOfWeek. This datatype is tied to the table above with that -- name. @@ -4584,13 +4584,13 @@ local atsvc_DaysOfWeek = --@return The marshalled integer representing the given value, or nil if it wasn't -- found. function marshall_atsvc_DaysOfWeek(flags) - local result - stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_DaysOfWeek()")) + local result + stdnse.print_debug(4, string.format("MSRPC: Entering marshall_atsvc_DaysOfWeek()")) - result = marshall_Enum8(flags, atsvc_DaysOfWeek, false) + result = marshall_Enum8(flags, atsvc_DaysOfWeek, false) - stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_atsvc_DaysOfWeek()")) - return result + stdnse.print_debug(4, string.format("MSRPC: Leaving marshall_atsvc_DaysOfWeek()")) + return result end ---Marshall a JobInfo struct. The structure is as follows: @@ -4610,16 +4610,16 @@ end -- file. --@param time The time at which to run the job, in milliseconds from midnight. function marshall_atsvc_JobInfo(command, time) - local result = "" + local result = "" - result = result .. marshall_int32(time) -- Job time - result = result .. marshall_int32(0) -- Day of month - result = result .. marshall_int8(0, false) -- Day of week - result = result .. marshall_atsvc_Flags("JOB_NONINTERACTIVE") -- Flags - result = result .. marshall_int16(0, false) -- Padding - result = result .. marshall_unicode_ptr(command, true) -- Command + result = result .. marshall_int32(time) -- Job time + result = result .. marshall_int32(0) -- Day of month + result = result .. marshall_int8(0, false) -- Day of week + result = result .. marshall_atsvc_Flags("JOB_NONINTERACTIVE") -- Flags + result = result .. marshall_int16(0, false) -- Padding + result = result .. marshall_unicode_ptr(command, true) -- Command - return result + return result end diff --git a/nselib/mssql.lua b/nselib/mssql.lua index 7918e311e..1369d6275 100644 --- a/nselib/mssql.lua +++ b/nselib/mssql.lua @@ -63,15 +63,15 @@ -- @author "Patrik Karlsson , Chris Woodbury" -- -- @args mssql.username The username to use to connect to SQL Server instances. --- This username is used by scripts taking actions that require --- authentication (e.g. ms-sql-query) This username (and its --- associated password) takes precedence over any credentials discovered --- by the ms-sql-brute and ms-sql-empty-password --- scripts. +-- This username is used by scripts taking actions that require +-- authentication (e.g. ms-sql-query) This username (and its +-- associated password) takes precedence over any credentials discovered +-- by the ms-sql-brute and ms-sql-empty-password +-- scripts. -- -- @args mssql.password The password for mssql.username. If this --- argument is not given but mssql.username, a blank password --- is used. +-- argument is not given but mssql.username, a blank password +-- is used. -- -- @args mssql.instance-name The name of the instance to connect to. -- @@ -89,18 +89,18 @@ -- TCP. -- -- @args mssql.timeout How long to wait for SQL responses. This is a number --- followed by ms for milliseconds, s for --- seconds, m for minutes, or h for hours. --- Default: 30s. +-- followed by ms for milliseconds, s for +-- seconds, m for minutes, or h for hours. +-- Default: 30s. -- -- @args mssql.scanned-ports-only If set, the script will only connect --- to ports that were included in the Nmap scan. This may result in --- instances not being discovered, particularly if UDP port 1434 is not --- included. Additionally, instances that are found to be running on --- ports that were not scanned (e.g. if 1434/udp is in the scan and the --- SQL Server Browser service on that port reports an instance --- listening on 43210/tcp, which was not scanned) will be reported but --- will not be stored for use by other ms-sql-* scripts. +-- to ports that were included in the Nmap scan. This may result in +-- instances not being discovered, particularly if UDP port 1434 is not +-- included. Additionally, instances that are found to be running on +-- ports that were not scanned (e.g. if 1434/udp is in the scan and the +-- SQL Server Browser service on that port reports an instance +-- listening on 43210/tcp, which was not scanned) will be reported but +-- will not be stored for use by other ms-sql-* scripts. local bin = require "bin" local bit = require "bit" @@ -120,21 +120,21 @@ _ENV = stdnse.module("mssql", stdnse.seeall) -- Created 01/17/2010 - v0.1 - created by Patrik Karlsson -- Revised 03/28/2010 - v0.2 - fixed incorrect token types. added 30 seconds timeout -- Revised 01/23/2011 - v0.3 - fixed parsing error in discovery code with patch --- from Chris Woodbury +-- from Chris Woodbury -- Revised 02/01/2011 - v0.4 - numerous changes and additions to support new --- functionality in ms-sql- scripts and to be more --- robust in parsing and handling data. (Chris Woodbury) +-- functionality in ms-sql- scripts and to be more +-- robust in parsing and handling data. (Chris Woodbury) -- Revised 02/19/2011 - v0.5 - numerous changes in script, library behaviour --- * huge improvements in version detection --- * added support for named pipes --- * added support for integrated NTLMv1 authentication +-- * huge improvements in version detection +-- * added support for named pipes +-- * added support for integrated NTLMv1 authentication -- --- (Patrik Karlsson, Chris Woodbury) +-- (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 +-- * added detection and handling of null values when processing query responses from the server +-- * added DoneProc response token support -- --- (Tom Sellers) +-- (Tom Sellers) -- Updated 10/01/2012 - v0.7 - added support for 2012 and later service packs for 2005, 2008 and 2008 R2 (Rob Nicholls) local HAVE_SSL, openssl = pcall(require, "openssl") @@ -151,7 +151,7 @@ do SCANNED_PORTS_ONLY = false if ( stdnse.get_script_args( "mssql.scanned-ports-only" ) ) then - SCANNED_PORTS_ONLY = true + SCANNED_PORTS_ONLY = true end end @@ -163,333 +163,333 @@ end --- SqlServerInstanceInfo class SqlServerInstanceInfo = { - instanceName = nil, - version = nil, - serverName = nil, - isClustered = nil, - host = nil, - port = nil, - pipeName = nil, + instanceName = nil, + version = nil, + serverName = nil, + isClustered = nil, + host = nil, + port = nil, + pipeName = nil, - new = function(self,o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self,o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o + end, - -- Compares two SqlServerInstanceInfo objects and determines whether they - -- refer to the same SQL Server instance, judging by a combination of host, - -- port, named pipe information and instance name. - __eq = function( self, other ) - local areEqual - if ( not (self.host and other.host) ) then - -- if they don't both have host information, we certainly can't say - -- whether they're the same - areEqual = false - else - areEqual = (self.host.ip == other.host.ip) - end + -- Compares two SqlServerInstanceInfo objects and determines whether they + -- refer to the same SQL Server instance, judging by a combination of host, + -- port, named pipe information and instance name. + __eq = function( self, other ) + local areEqual + if ( not (self.host and other.host) ) then + -- if they don't both have host information, we certainly can't say + -- whether they're the same + areEqual = false + else + areEqual = (self.host.ip == other.host.ip) + end - if (self.port and other.port) then - areEqual = areEqual and ( other.port.number == self.port.number and - other.port.protocol == self.port.protocol ) - elseif (self.pipeName and other.pipeName) then - areEqual = areEqual and (self.pipeName == other.pipeName) - elseif (self.instanceName and other.instanceName) then - areEqual = areEqual and (self.instanceName == other.instanceName) - else - -- if we have neither port nor named pipe info nor instance names, - -- we can't say whether they're the same - areEqual = false - end + if (self.port and other.port) then + areEqual = areEqual and ( other.port.number == self.port.number and + other.port.protocol == self.port.protocol ) + elseif (self.pipeName and other.pipeName) then + areEqual = areEqual and (self.pipeName == other.pipeName) + elseif (self.instanceName and other.instanceName) then + areEqual = areEqual and (self.instanceName == other.instanceName) + else + -- if we have neither port nor named pipe info nor instance names, + -- we can't say whether they're the same + areEqual = false + end - return areEqual - end, + return areEqual + end, - --- Merges the data from one SqlServerInstanceInfo object into another. Each - -- field in the first object is populated with the data from that field in - -- second object if the first object's field is nil OR if overwrite - -- is set to true. A special case is made for the version field, - -- which is only overwritten in the second object has more reliable version - -- information. The second object is not modified. - Merge = function( self, other, overwrite ) - local mergeFields = { "host", "port", "instanceName", "version", "isClustered", "pipeName" } - for _, fieldname in ipairs( mergeFields ) do - -- Add values from other only if self doesn't have a value, or if overwrite is true - if ( other[ fieldname ] ~= nil and (overwrite or self[ fieldname ] == nil) ) then - self[ fieldname ] = other[ fieldname ] - end - end - if (self.version and self.version.source == "SSRP" and - other.version and other.version.Source == "SSNetLib") then - self.version = other.version - end - end, + --- Merges the data from one SqlServerInstanceInfo object into another. Each + -- field in the first object is populated with the data from that field in + -- second object if the first object's field is nil OR if overwrite + -- is set to true. A special case is made for the version field, + -- which is only overwritten in the second object has more reliable version + -- information. The second object is not modified. + Merge = function( self, other, overwrite ) + local mergeFields = { "host", "port", "instanceName", "version", "isClustered", "pipeName" } + for _, fieldname in ipairs( mergeFields ) do + -- Add values from other only if self doesn't have a value, or if overwrite is true + if ( other[ fieldname ] ~= nil and (overwrite or self[ fieldname ] == nil) ) then + self[ fieldname ] = other[ fieldname ] + end + end + if (self.version and self.version.source == "SSRP" and + other.version and other.version.Source == "SSNetLib") then + self.version = other.version + end + end, - --- Returns a name for the instance, based on the available information. This - -- may take one of the following forms: - -- * HOST\INSTANCENAME - -- * PIPENAME - -- * HOST:PORT - GetName = function( self ) - if (self.instanceName) then - return string.format( "%s\\%s", self.host.ip or self.serverName or "[nil]", self.instanceName or "[nil]" ) - elseif (self.pipeName) then - return string.format( "%s", self.pipeName ) - else - return string.format( "%s:%s", self.host.ip or self.serverName or "[nil]", (self.port and self.port.number) or "[nil]" ) - end - end, + --- Returns a name for the instance, based on the available information. This + -- may take one of the following forms: + -- * HOST\INSTANCENAME + -- * PIPENAME + -- * HOST:PORT + GetName = function( self ) + if (self.instanceName) then + return string.format( "%s\\%s", self.host.ip or self.serverName or "[nil]", self.instanceName or "[nil]" ) + elseif (self.pipeName) then + return string.format( "%s", self.pipeName ) + else + return string.format( "%s:%s", self.host.ip or self.serverName or "[nil]", (self.port and self.port.number) or "[nil]" ) + end + end, - --- Sets whether the instance is in a cluster - -- - -- @param self - -- @param isClustered Boolean true or the string "Yes" are interpreted as true; - -- all other values are interpreted as false. - SetIsClustered = function( self, isClustered ) - self.isClustered = (isClustered == true) or (isClustered == "Yes") - end, + --- Sets whether the instance is in a cluster + -- + -- @param self + -- @param isClustered Boolean true or the string "Yes" are interpreted as true; + -- all other values are interpreted as false. + SetIsClustered = function( self, isClustered ) + self.isClustered = (isClustered == true) or (isClustered == "Yes") + end, - --- Indicates whether this instance has networking protocols enabled, such - -- that scripts could attempt to connect to it. - HasNetworkProtocols = function( self ) - return (self.pipeName ~= nil) or (self.port and self.port.number) - end, + --- Indicates whether this instance has networking protocols enabled, such + -- that scripts could attempt to connect to it. + HasNetworkProtocols = function( self ) + return (self.pipeName ~= nil) or (self.port and self.port.number) + end, } --- SqlServerVersionInfo class SqlServerVersionInfo = { - versionNumber = "", -- The full version string (e.g. "9.00.2047.00") - major = nil, -- The major version (e.g. 9) - minor = nil, -- The minor version (e.g. 0) - build = nil, -- The build number (e.g. 2047) - subBuild = nil, -- The sub-build number (e.g. 0) - productName = nil, -- The prodcut name (e.g. "SQL Server 2005") - brandedVersion = nil, -- The branded version of the product (e.g. "2005") - servicePackLevel = nil, -- The service pack leve (e.g. "SP1") - patched = nil, -- Whether patches have been applied since SP installation (true/false/nil) - source = nil, -- The source of the version info (e.g. "SSRP", "SSNetLib") + versionNumber = "", -- The full version string (e.g. "9.00.2047.00") + major = nil, -- The major version (e.g. 9) + minor = nil, -- The minor version (e.g. 0) + build = nil, -- The build number (e.g. 2047) + subBuild = nil, -- The sub-build number (e.g. 0) + productName = nil, -- The prodcut name (e.g. "SQL Server 2005") + brandedVersion = nil, -- The branded version of the product (e.g. "2005") + servicePackLevel = nil, -- The service pack leve (e.g. "SP1") + patched = nil, -- Whether patches have been applied since SP installation (true/false/nil) + source = nil, -- The source of the version info (e.g. "SSRP", "SSNetLib") - new = function(self,o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self,o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Sets the version using a version number string. - -- - -- @param versionNumber a version number string (e.g. "9.00.1399.00") - -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib") - SetVersionNumber = function(self, versionNumber, source) - local major, minor, revision, subBuild - if versionNumber:match( "^%d+%.%d+%.%d+.%d+" ) then - major, minor, revision, subBuild = versionNumber:match( "^(%d+)%.(%d+)%.(%d+)" ) - elseif versionNumber:match( "^%d+%.%d+%.%d+" ) then - major, minor, revision = versionNumber:match( "^(%d+)%.(%d+)%.(%d+)" ) - else - stdnse.print_debug( 1, "%s: SetVersionNumber: versionNumber is not in correct format: %s", "MSSQL", versionNumber or "nil" ) - end + --- Sets the version using a version number string. + -- + -- @param versionNumber a version number string (e.g. "9.00.1399.00") + -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib") + SetVersionNumber = function(self, versionNumber, source) + local major, minor, revision, subBuild + if versionNumber:match( "^%d+%.%d+%.%d+.%d+" ) then + major, minor, revision, subBuild = versionNumber:match( "^(%d+)%.(%d+)%.(%d+)" ) + elseif versionNumber:match( "^%d+%.%d+%.%d+" ) then + major, minor, revision = versionNumber:match( "^(%d+)%.(%d+)%.(%d+)" ) + else + stdnse.print_debug( 1, "%s: SetVersionNumber: versionNumber is not in correct format: %s", "MSSQL", versionNumber or "nil" ) + end - self:SetVersion( major, minor, revision, subBuild, source ) - end, + self:SetVersion( major, minor, revision, subBuild, source ) + end, - --- Sets the version using the individual numeric components of the version - -- number. - -- - -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib") - SetVersion = function(self, major, minor, build, subBuild, source) - self.source = source - -- make sure our version numbers all end up as valid numbers - self.major, self.minor, self.build, self.subBuild = - tonumber( major or 0 ), tonumber( minor or 0 ), tonumber( build or 0 ), tonumber( subBuild or 0 ) + --- Sets the version using the individual numeric components of the version + -- number. + -- + -- @param source a string indicating the source of the version info (e.g. "SSRP", "SSNetLib") + SetVersion = function(self, major, minor, build, subBuild, source) + self.source = source + -- make sure our version numbers all end up as valid numbers + self.major, self.minor, self.build, self.subBuild = + tonumber( major or 0 ), tonumber( minor or 0 ), tonumber( build or 0 ), tonumber( subBuild or 0 ) - self.versionNumber = string.format( "%u.%02u.%u.%02u", self.major, self.minor, self.build, self.subBuild ) + self.versionNumber = string.format( "%u.%02u.%u.%02u", self.major, self.minor, self.build, self.subBuild ) - self:_ParseVersionInfo() - end, + self:_ParseVersionInfo() + end, - --- Using the version number, determines the product version - _InferProductVersion = function(self) + --- Using the version number, determines the product version + _InferProductVersion = function(self) - local VERSION_LOOKUP_TABLE = { - ["^6%.0"] = "6.0", ["^6%.5"] = "6.5", ["^7%.0"] = "7.0", - ["^8%.0"] = "2000", ["^9%.0"] = "2005", ["^10%.0"] = "2008", - ["^10%.50"] = "2008 R2", ["^11%.0"] = "2012", - } + local VERSION_LOOKUP_TABLE = { + ["^6%.0"] = "6.0", ["^6%.5"] = "6.5", ["^7%.0"] = "7.0", + ["^8%.0"] = "2000", ["^9%.0"] = "2005", ["^10%.0"] = "2008", + ["^10%.50"] = "2008 R2", ["^11%.0"] = "2012", + } - local product = "" + local product = "" - for m, v in pairs(VERSION_LOOKUP_TABLE) do - if ( self.versionNumber:match(m) ) then - product = v - self.brandedVersion = product - break - end - end + for m, v in pairs(VERSION_LOOKUP_TABLE) do + if ( self.versionNumber:match(m) ) then + product = v + self.brandedVersion = product + break + end + end - self.productName = ("Microsoft SQL Server %s"):format(product) + self.productName = ("Microsoft SQL Server %s"):format(product) - end, + end, - --- Returns a lookup table that maps revision numbers to service pack levels for - -- the applicable SQL Server version (e.g. { {1600, "RTM"}, {2531, "SP1"} }). - _GetSpLookupTable = function(self) + --- Returns a lookup table that maps revision numbers to service pack levels for + -- the applicable SQL Server version (e.g. { {1600, "RTM"}, {2531, "SP1"} }). + _GetSpLookupTable = function(self) - -- Service pack lookup tables: - -- For instances where a revised service pack was released (e.g. 2000 SP3a), we will include the - -- build number for the original SP and the build number for the revision. However, leaving it - -- like this would make it appear that subsequent builds were a patched version of the revision - -- (e.g. a patch applied to 2000 SP3 that increased the build number to 780 would get displayed - -- as "SP3a+", when it was actually SP3+). To avoid this, we will include an additional fake build - -- number that combines the two. - local SP_LOOKUP_TABLE_6_5 = { {201, "RTM"}, {213, "SP1"}, {240, "SP2"}, {258, "SP3"}, {281, "SP4"}, - {415, "SP5"}, {416, "SP5a"}, {417, "SP5/SP5a"}, } + -- Service pack lookup tables: + -- For instances where a revised service pack was released (e.g. 2000 SP3a), we will include the + -- build number for the original SP and the build number for the revision. However, leaving it + -- like this would make it appear that subsequent builds were a patched version of the revision + -- (e.g. a patch applied to 2000 SP3 that increased the build number to 780 would get displayed + -- as "SP3a+", when it was actually SP3+). To avoid this, we will include an additional fake build + -- number that combines the two. + local SP_LOOKUP_TABLE_6_5 = { {201, "RTM"}, {213, "SP1"}, {240, "SP2"}, {258, "SP3"}, {281, "SP4"}, + {415, "SP5"}, {416, "SP5a"}, {417, "SP5/SP5a"}, } - local SP_LOOKUP_TABLE_7 = { {623, "RTM"}, {699, "SP1"}, {842, "SP2"}, {961, "SP3"}, {1063, "SP4"}, } + local SP_LOOKUP_TABLE_7 = { {623, "RTM"}, {699, "SP1"}, {842, "SP2"}, {961, "SP3"}, {1063, "SP4"}, } - local SP_LOOKUP_TABLE_2000 = { {194, "RTM"}, {384, "SP1"}, {532, "SP2"}, {534, "SP2"}, {760, "SP3"}, - {766, "SP3a"}, {767, "SP3/SP3a"}, {2039, "SP4"}, } + local SP_LOOKUP_TABLE_2000 = { {194, "RTM"}, {384, "SP1"}, {532, "SP2"}, {534, "SP2"}, {760, "SP3"}, + {766, "SP3a"}, {767, "SP3/SP3a"}, {2039, "SP4"}, } - local SP_LOOKUP_TABLE_2005 = { {1399, "RTM"}, {2047, "SP1"}, {3042, "SP2"}, {4035, "SP3"}, {5000, "SP4"}, } + local SP_LOOKUP_TABLE_2005 = { {1399, "RTM"}, {2047, "SP1"}, {3042, "SP2"}, {4035, "SP3"}, {5000, "SP4"}, } - local SP_LOOKUP_TABLE_2008 = { {1600, "RTM"}, {2531, "SP1"}, {4000, "SP2"}, {5500, "SP3"}, } + local SP_LOOKUP_TABLE_2008 = { {1600, "RTM"}, {2531, "SP1"}, {4000, "SP2"}, {5500, "SP3"}, } - local SP_LOOKUP_TABLE_2008R2 = { {1600, "RTM"}, {2500, "SP1"}, {4000, "SP2"}, } + local SP_LOOKUP_TABLE_2008R2 = { {1600, "RTM"}, {2500, "SP1"}, {4000, "SP2"}, } - local SP_LOOKUP_TABLE_2012 = { {2100, "RTM"}, } + local SP_LOOKUP_TABLE_2012 = { {2100, "RTM"}, } - if ( not self.brandedVersion ) then - self:_InferProductVersion() - end + if ( not self.brandedVersion ) then + self:_InferProductVersion() + end - local spLookupTable - if self.brandedVersion == "6.5" then spLookupTable = SP_LOOKUP_TABLE_6_5 - elseif self.brandedVersion == "7.0" then spLookupTable = SP_LOOKUP_TABLE_7 - elseif self.brandedVersion == "2000" then spLookupTable = SP_LOOKUP_TABLE_2000 - elseif self.brandedVersion == "2005" then spLookupTable = SP_LOOKUP_TABLE_2005 - elseif self.brandedVersion == "2008" then spLookupTable = SP_LOOKUP_TABLE_2008 - elseif self.brandedVersion == "2008 R2" then spLookupTable = SP_LOOKUP_TABLE_2008R2 - elseif self.brandedVersion == "2012" then spLookupTable = SP_LOOKUP_TABLE_2012 - end + local spLookupTable + if self.brandedVersion == "6.5" then spLookupTable = SP_LOOKUP_TABLE_6_5 + elseif self.brandedVersion == "7.0" then spLookupTable = SP_LOOKUP_TABLE_7 + elseif self.brandedVersion == "2000" then spLookupTable = SP_LOOKUP_TABLE_2000 + elseif self.brandedVersion == "2005" then spLookupTable = SP_LOOKUP_TABLE_2005 + elseif self.brandedVersion == "2008" then spLookupTable = SP_LOOKUP_TABLE_2008 + elseif self.brandedVersion == "2008 R2" then spLookupTable = SP_LOOKUP_TABLE_2008R2 + elseif self.brandedVersion == "2012" then spLookupTable = SP_LOOKUP_TABLE_2012 + end - return spLookupTable + return spLookupTable - end, + end, - --- Processes version data to determine (if possible) the product version, - -- service pack level and patch status. - _ParseVersionInfo = function(self) + --- Processes version data to determine (if possible) the product version, + -- service pack level and patch status. + _ParseVersionInfo = function(self) - local spLookupTable = self:_GetSpLookupTable() + local spLookupTable = self:_GetSpLookupTable() - if spLookupTable then + if spLookupTable then - local spLookupItr = 0 - -- Loop through the service pack levels until we find one whose revision - -- number is the same as or lower than our revision number. - while spLookupItr < #spLookupTable do - spLookupItr = spLookupItr + 1 + local spLookupItr = 0 + -- Loop through the service pack levels until we find one whose revision + -- number is the same as or lower than our revision number. + while spLookupItr < #spLookupTable do + spLookupItr = spLookupItr + 1 - if (spLookupTable[ spLookupItr ][1] == self.build ) then - spLookupItr = spLookupItr - break - elseif (spLookupTable[ spLookupItr ][1] > self.build ) then - -- The target revision number is lower than the first release - if spLookupItr == 1 then - self.servicePackLevel = "Pre-RTM" - else - -- we went too far - it's the previous SP, but with patches applied - spLookupItr = spLookupItr - 1 - end - break - end - end + if (spLookupTable[ spLookupItr ][1] == self.build ) then + spLookupItr = spLookupItr + break + elseif (spLookupTable[ spLookupItr ][1] > self.build ) then + -- The target revision number is lower than the first release + if spLookupItr == 1 then + self.servicePackLevel = "Pre-RTM" + else + -- we went too far - it's the previous SP, but with patches applied + spLookupItr = spLookupItr - 1 + end + break + end + end - -- Now that we've identified the proper service pack level: - if self.servicePackLevel ~= "Pre-RTM" then - self.servicePackLevel = spLookupTable[ spLookupItr ][2] + -- Now that we've identified the proper service pack level: + if self.servicePackLevel ~= "Pre-RTM" then + self.servicePackLevel = spLookupTable[ spLookupItr ][2] - if ( spLookupTable[ spLookupItr ][1] == self.build ) then - self.patched = false - else - self.patched = true - end - end + if ( spLookupTable[ spLookupItr ][1] == self.build ) then + self.patched = false + else + self.patched = true + end + end - -- Clean up some of our inferences. If the source of our revision number - -- was the SSRP (SQL Server Browser) response, we need to recognize its - -- limitations: - -- * Versions of SQL Server prior to 2005 are reported with the RTM build - -- number, regardless of the actual version (e.g. SQL Server 2000 is - -- always 8.00.194). - -- * Versions of SQL Server starting with 2005 (and going through at least - -- 2008) do better but are still only reported with the build number as - -- of the last service pack (e.g. SQL Server 2005 SP3 with patches is - -- still reported as 9.00.4035.00). - if ( self.source == "SSRP" ) then - self.patched = nil + -- Clean up some of our inferences. If the source of our revision number + -- was the SSRP (SQL Server Browser) response, we need to recognize its + -- limitations: + -- * Versions of SQL Server prior to 2005 are reported with the RTM build + -- number, regardless of the actual version (e.g. SQL Server 2000 is + -- always 8.00.194). + -- * Versions of SQL Server starting with 2005 (and going through at least + -- 2008) do better but are still only reported with the build number as + -- of the last service pack (e.g. SQL Server 2005 SP3 with patches is + -- still reported as 9.00.4035.00). + if ( self.source == "SSRP" ) then + self.patched = nil - if ( self.major <= 8 ) then - self.servicePackLevel = nil - end - end - end + if ( self.major <= 8 ) then + self.servicePackLevel = nil + end + end + end - return true - end, + return true + end, - --- - ToString = function(self) - local friendlyVersion = strbuf.new() - if self.productName then - friendlyVersion:concatbuf( self.productName ) - if self.servicePackLevel then - friendlyVersion:concatbuf( " " ) - friendlyVersion:concatbuf( self.servicePackLevel ) - end - if self.patched then - friendlyVersion:concatbuf( "+" ) - end - end + --- + ToString = function(self) + local friendlyVersion = strbuf.new() + if self.productName then + friendlyVersion:concatbuf( self.productName ) + if self.servicePackLevel then + friendlyVersion:concatbuf( " " ) + friendlyVersion:concatbuf( self.servicePackLevel ) + end + if self.patched then + friendlyVersion:concatbuf( "+" ) + end + end - return friendlyVersion:dump() - end, + return friendlyVersion:dump() + end, - --- Uses the information in this SqlServerVersionInformation object to - -- populate the version information in an Nmap port table for a SQL Server - -- TCP listener. - -- - -- @param self A SqlServerVersionInformation object - -- @param port An Nmap port table corresponding to the instance - PopulateNmapPortVersion = function(self, port) + --- Uses the information in this SqlServerVersionInformation object to + -- populate the version information in an Nmap port table for a SQL Server + -- TCP listener. + -- + -- @param self A SqlServerVersionInformation object + -- @param port An Nmap port table corresponding to the instance + PopulateNmapPortVersion = function(self, port) - port.service = "ms-sql-s" - port.version = port.version or {} - port.version.name = "ms-sql-s" - port.version.product = self.productName + port.service = "ms-sql-s" + port.version = port.version or {} + port.version.name = "ms-sql-s" + port.version.product = self.productName - local versionString = strbuf.new() - if self.source ~= "SSRP" then - versionString:concatbuf( self.versionNumber ) - if self.servicePackLevel then - versionString:concatbuf( "; " ) - versionString:concatbuf( self.servicePackLevel ) - end - if self.patched then - versionString:concatbuf( "+" ) - end - port.version.version = versionString:dump() - end + local versionString = strbuf.new() + if self.source ~= "SSRP" then + versionString:concatbuf( self.versionNumber ) + if self.servicePackLevel then + versionString:concatbuf( "; " ) + versionString:concatbuf( self.servicePackLevel ) + end + if self.patched then + versionString:concatbuf( "+" ) + end + port.version.version = versionString:dump() + end - return port - end, + return port + end, } @@ -498,170 +498,170 @@ SqlServerVersionInfo = -- ************************************* SSRP = { - PORT = { number = 1434, protocol = "udp" }, - DEBUG_ID = "MSSQL-SSRP", + PORT = { number = 1434, protocol = "udp" }, + DEBUG_ID = "MSSQL-SSRP", - MESSAGE_TYPE = - { - ClientBroadcast = 0x02, - ClientUnicast = 0x03, - ClientUnicastInstance = 0x04, - ClientUnicastDAC = 0x0F, - ServerResponse = 0x05, - }, + MESSAGE_TYPE = + { + ClientBroadcast = 0x02, + ClientUnicast = 0x03, + ClientUnicastInstance = 0x04, + ClientUnicastDAC = 0x0F, + ServerResponse = 0x05, + }, - --- Parses an SSRP string and returns a table containing one or more - -- SqlServerInstanceInfo objects created from the parsed string. - _ParseSsrpString = function( host, ssrpString ) - -- It would seem easier to just capture (.-;;) repeateadly, since - -- each instance ends with ";;", but ";;" can also occur within the - -- data, signifying an empty field (e.g. "...bv;;@COMPNAME;;tcp;1433;;..."). - -- So, instead, we'll split up the string ahead of time. - -- See the SSRP specification for more details. + --- Parses an SSRP string and returns a table containing one or more + -- SqlServerInstanceInfo objects created from the parsed string. + _ParseSsrpString = function( host, ssrpString ) + -- It would seem easier to just capture (.-;;) repeateadly, since + -- each instance ends with ";;", but ";;" can also occur within the + -- data, signifying an empty field (e.g. "...bv;;@COMPNAME;;tcp;1433;;..."). + -- So, instead, we'll split up the string ahead of time. + -- See the SSRP specification for more details. - local instanceStrings = {} - local firstInstanceEnd, instanceString - repeat - firstInstanceEnd = ssrpString:find( ";ServerName;(.-);InstanceName;(.-);IsClustered;(.-);" ) - if firstInstanceEnd then - instanceString = ssrpString:sub( 1, firstInstanceEnd ) - ssrpString = ssrpString:sub( firstInstanceEnd + 1 ) - else - instanceString = ssrpString - end + local instanceStrings = {} + local firstInstanceEnd, instanceString + repeat + firstInstanceEnd = ssrpString:find( ";ServerName;(.-);InstanceName;(.-);IsClustered;(.-);" ) + if firstInstanceEnd then + instanceString = ssrpString:sub( 1, firstInstanceEnd ) + ssrpString = ssrpString:sub( firstInstanceEnd + 1 ) + else + instanceString = ssrpString + end - table.insert( instanceStrings, instanceString ) - until (not firstInstanceEnd) - stdnse.print_debug( 2, "%s: SSRP Substrings:\n %s", SSRP.DEBUG_ID, stdnse.strjoin( "\n ", instanceStrings ) ) + table.insert( instanceStrings, instanceString ) + until (not firstInstanceEnd) + stdnse.print_debug( 2, "%s: SSRP Substrings:\n %s", SSRP.DEBUG_ID, stdnse.strjoin( "\n ", instanceStrings ) ) - local instances = {} - for _, instanceString in ipairs( instanceStrings ) do - local instance = SqlServerInstanceInfo:new() - local version = SqlServerVersionInfo:new() - instance.version = version + local instances = {} + for _, instanceString in ipairs( instanceStrings ) do + local instance = SqlServerInstanceInfo:new() + local version = SqlServerVersionInfo:new() + instance.version = version - instance.host = host - instance.serverName = instanceString:match( "ServerName;(.-);") - instance.instanceName = instanceString:match( "InstanceName;(.-);") - instance:SetIsClustered( instanceString:match( "IsClustered;(.-);") ) - version:SetVersionNumber( instanceString:match( "Version;(.-);"), "SSRP" ) + instance.host = host + instance.serverName = instanceString:match( "ServerName;(.-);") + instance.instanceName = instanceString:match( "InstanceName;(.-);") + instance:SetIsClustered( instanceString:match( "IsClustered;(.-);") ) + version:SetVersionNumber( instanceString:match( "Version;(.-);"), "SSRP" ) - local tcpPort = tonumber( instanceString:match( ";tcp;(.-);") ) - if tcpPort then instance.port = {number = tcpPort, protocol = "tcp"} end + local tcpPort = tonumber( instanceString:match( ";tcp;(.-);") ) + if tcpPort then instance.port = {number = tcpPort, protocol = "tcp"} end - local pipeName = instanceString:match( ";np;(.-);") - local status, pipeSubPath = namedpipes.get_pipe_subpath( pipeName ) - if status then - pipeName = namedpipes.make_pipe_name( host.ip, pipeSubPath ) - elseif pipeName ~= nil then - stdnse.print_debug( 1, "%s: Invalid pipe name:\n%s", SSRP.DEBUG_ID, pipeName ) - end - instance.pipeName = pipeName + local pipeName = instanceString:match( ";np;(.-);") + local status, pipeSubPath = namedpipes.get_pipe_subpath( pipeName ) + if status then + pipeName = namedpipes.make_pipe_name( host.ip, pipeSubPath ) + elseif pipeName ~= nil then + stdnse.print_debug( 1, "%s: Invalid pipe name:\n%s", SSRP.DEBUG_ID, pipeName ) + end + instance.pipeName = pipeName - table.insert( instances, instance ) - end + table.insert( instances, instance ) + end - return instances - end, + return instances + end, - --- - _ProcessResponse = function( host, responseData ) - local instances + --- + _ProcessResponse = function( host, responseData ) + local instances - local pos, messageType, dataLength = 1, nil, nil - pos, messageType, dataLength = bin.unpack("CSS", optionType, offset, optionLength ) - offset = offset + optionLength + optionType = PreLoginPacket.OPTION_TYPE.Version + optionLength = OPTION_LENGTH_CLIENT[ optionType ] + data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) + offset = offset + optionLength - optionType = PreLoginPacket.OPTION_TYPE.Encryption - optionLength = OPTION_LENGTH_CLIENT[ optionType ] - data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) - offset = offset + optionLength + optionType = PreLoginPacket.OPTION_TYPE.Encryption + optionLength = OPTION_LENGTH_CLIENT[ optionType ] + data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) + offset = offset + optionLength - optionType = PreLoginPacket.OPTION_TYPE.InstOpt - optionLength = #self._instanceName + 1 --(string length + null-terminator) - data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) - offset = offset + optionLength + optionType = PreLoginPacket.OPTION_TYPE.InstOpt + optionLength = #self._instanceName + 1 --(string length + null-terminator) + data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) + offset = offset + optionLength - optionType = PreLoginPacket.OPTION_TYPE.ThreadId - optionLength = OPTION_LENGTH_CLIENT[ optionType ] - data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) - offset = offset + optionLength + optionType = PreLoginPacket.OPTION_TYPE.ThreadId + optionLength = OPTION_LENGTH_CLIENT[ optionType ] + data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) + offset = offset + optionLength - if self.requestMars then - optionType = PreLoginPacket.OPTION_TYPE.MARS - optionLength = OPTION_LENGTH_CLIENT[ optionType ] - data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) - offset = offset + optionLength - end + if self.requestMars then + optionType = PreLoginPacket.OPTION_TYPE.MARS + optionLength = OPTION_LENGTH_CLIENT[ optionType ] + data = data .. bin.pack( ">CSS", optionType, offset, optionLength ) + offset = offset + optionLength + end - data = data .. bin.pack( "C", PreLoginPacket.OPTION_TYPE.Terminator ) + data = data .. bin.pack( "C", PreLoginPacket.OPTION_TYPE.Terminator ) - -- Now that the pre-login headers are done, write the data - data = data .. bin.pack( ">CCSS", self.versionInfo.major, self.versionInfo.minor, - self.versionInfo.build, self.versionInfo.subBuild ) - data = data .. bin.pack( "C", self._requestEncryption ) - data = data .. bin.pack( "z", self._instanceName ) - data = data .. bin.pack( "CCSS", self.versionInfo.major, self.versionInfo.minor, + self.versionInfo.build, self.versionInfo.subBuild ) + data = data .. bin.pack( "C", self._requestEncryption ) + data = data .. bin.pack( "z", self._instanceName ) + data = data .. bin.pack( "SS", bytes, pos) - if not (optionPos and optionLength) then - stdnse.print_debug( 2, "%s: Could not unpack optionPos and optionLength.", "MSSQL" ) - return false, "Invalid pre-login response" - end + pos, optionPos, optionLength = bin.unpack(">SS", bytes, pos) + if not (optionPos and optionLength) then + stdnse.print_debug( 2, "%s: Could not unpack optionPos and optionLength.", "MSSQL" ) + return false, "Invalid pre-login response" + end - optionPos = optionPos + 1 -- convert from 0-based index to 1-based index - if ( (optionPos + optionLength) > (#bytes + 1) ) then - stdnse.print_debug( 2, "%s: Pre-login response: pos+len for option type %s is beyond end of data.", "MSSQL", optionType ) - stdnse.print_debug( 2, "%s: (optionPos: %s) (optionLength: %s)", "MSSQL", optionPos, optionLength ) - return false, "Invalid pre-login response" - end + optionPos = optionPos + 1 -- convert from 0-based index to 1-based index + if ( (optionPos + optionLength) > (#bytes + 1) ) then + stdnse.print_debug( 2, "%s: Pre-login response: pos+len for option type %s is beyond end of data.", "MSSQL", optionType ) + stdnse.print_debug( 2, "%s: (optionPos: %s) (optionLength: %s)", "MSSQL", optionPos, optionLength ) + return false, "Invalid pre-login response" + end - if ( optionLength ~= expectedOptionLength and expectedOptionLength ~= -1 ) then - stdnse.print_debug( 2, "%s: Option data is incorrect size in pre-login response. ", "MSSQL" ) - stdnse.print_debug( 2, "%s: (optionType: %s) (optionLength: %s)", "MSSQL", optionType, optionLength ) - return false, "Invalid pre-login response" - end - optionData = bytes:sub( optionPos, optionPos + optionLength - 1 ) - if #optionData ~= optionLength then - stdnse.print_debug( 2, "%s: Could not read sufficient bytes from version data.", "MSSQL" ) - return false, "Invalid pre-login response" - end + if ( optionLength ~= expectedOptionLength and expectedOptionLength ~= -1 ) then + stdnse.print_debug( 2, "%s: Option data is incorrect size in pre-login response. ", "MSSQL" ) + stdnse.print_debug( 2, "%s: (optionType: %s) (optionLength: %s)", "MSSQL", optionType, optionLength ) + return false, "Invalid pre-login response" + end + optionData = bytes:sub( optionPos, optionPos + optionLength - 1 ) + if #optionData ~= optionLength then + stdnse.print_debug( 2, "%s: Could not read sufficient bytes from version data.", "MSSQL" ) + return false, "Invalid pre-login response" + end - if ( optionType == PreLoginPacket.OPTION_TYPE.Version ) then - local major, minor, build, subBuild, version - major = string.byte( optionData:sub( 1, 1 ) ) - minor = string.byte( optionData:sub( 2, 2 ) ) - build = (string.byte( optionData:sub( 3, 3 ) ) * 256) + string.byte( optionData:sub( 4, 4 ) ) - subBuild = (string.byte( optionData:sub( 5, 5 ) ) * 256) + string.byte( optionData:sub( 6, 6 ) ) + if ( optionType == PreLoginPacket.OPTION_TYPE.Version ) then + local major, minor, build, subBuild, version + major = string.byte( optionData:sub( 1, 1 ) ) + minor = string.byte( optionData:sub( 2, 2 ) ) + build = (string.byte( optionData:sub( 3, 3 ) ) * 256) + string.byte( optionData:sub( 4, 4 ) ) + subBuild = (string.byte( optionData:sub( 5, 5 ) ) * 256) + string.byte( optionData:sub( 6, 6 ) ) - version = SqlServerVersionInfo:new() - version:SetVersion( major, minor, build, subBuild, "SSNetLib" ) - preLoginPacket.versionInfo = version - elseif ( optionType == PreLoginPacket.OPTION_TYPE.Encryption ) then - preLoginPacket:SetRequestEncryption( bin.unpack( "C", optionData ) ) - elseif ( optionType == PreLoginPacket.OPTION_TYPE.InstOpt ) then - preLoginPacket:SetInstanceName( bin.unpack( "z", optionData ) ) - elseif ( optionType == PreLoginPacket.OPTION_TYPE.ThreadId ) then - -- Do nothing. According to the TDS spec, this option is empty when sent from the server - elseif ( optionType == PreLoginPacket.OPTION_TYPE.MARS ) then - preLoginPacket:SetRequestMars( bin.unpack( "C", optionData ) ) - end - end + version = SqlServerVersionInfo:new() + version:SetVersion( major, minor, build, subBuild, "SSNetLib" ) + preLoginPacket.versionInfo = version + elseif ( optionType == PreLoginPacket.OPTION_TYPE.Encryption ) then + preLoginPacket:SetRequestEncryption( bin.unpack( "C", optionData ) ) + elseif ( optionType == PreLoginPacket.OPTION_TYPE.InstOpt ) then + preLoginPacket:SetInstanceName( bin.unpack( "z", optionData ) ) + elseif ( optionType == PreLoginPacket.OPTION_TYPE.ThreadId ) then + -- Do nothing. According to the TDS spec, this option is empty when sent from the server + elseif ( optionType == PreLoginPacket.OPTION_TYPE.MARS ) then + preLoginPacket:SetRequestMars( bin.unpack( "C", optionData ) ) + end + end - return status, preLoginPacket - end, + return status, preLoginPacket + end, } @@ -1693,1481 +1693,1481 @@ PreLoginPacket = LoginPacket = { - -- options_1 possible values - -- 0x80 enable warning messages if SET LANGUAGE issued - -- 0x40 change to initial database must succeed - -- 0x20 enable warning messages if USE issued - -- 0x10 enable BCP + -- options_1 possible values + -- 0x80 enable warning messages if SET LANGUAGE issued + -- 0x40 change to initial database must succeed + -- 0x20 enable warning messages if USE issued + -- 0x10 enable BCP - -- options_2 possible values - -- 0x80 enable domain login security - -- 0x40 "USER_SERVER - reserved" - -- 0x20 user type is "DQ login" - -- 0x10 user type is "replication login" - -- 0x08 "fCacheConnect" - -- 0x04 "fTranBoundary" - -- 0x02 client is an ODBC driver - -- 0x01 change to initial language must succeed - length = 0, - version = 0x71000001, -- Version 7.1 - size = 0, - cli_version = 7, -- From jTDS JDBC driver - cli_pid = 0, -- Dummy value - conn_id = 0, - options_1 = 0xa0, - options_2 = 0x03, - sqltype_flag = 0, - reserved_flag= 0, - time_zone = 0, - collation = 0, + -- options_2 possible values + -- 0x80 enable domain login security + -- 0x40 "USER_SERVER - reserved" + -- 0x20 user type is "DQ login" + -- 0x10 user type is "replication login" + -- 0x08 "fCacheConnect" + -- 0x04 "fTranBoundary" + -- 0x02 client is an ODBC driver + -- 0x01 change to initial language must succeed + length = 0, + version = 0x71000001, -- Version 7.1 + size = 0, + cli_version = 7, -- From jTDS JDBC driver + cli_pid = 0, -- Dummy value + conn_id = 0, + options_1 = 0xa0, + options_2 = 0x03, + sqltype_flag = 0, + reserved_flag= 0, + time_zone = 0, + collation = 0, - -- Strings - client = "Nmap", - username = nil, - password = nil, - app = "Nmap NSE", - server = nil, - library = "mssql.lua", - locale = "", - database = "master", --nil, - MAC = string.char(0x00,0x00,0x00,0x00,0x00,0x00), -- should contain client MAC, jTDS uses all zeroes + -- Strings + client = "Nmap", + username = nil, + password = nil, + app = "Nmap NSE", + server = nil, + library = "mssql.lua", + locale = "", + database = "master", --nil, + MAC = string.char(0x00,0x00,0x00,0x00,0x00,0x00), -- should contain client MAC, jTDS uses all zeroes - new = function(self,o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self,o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Sets the username used for authentication - -- - -- @param username string containing the username to user for authentication - SetUsername = function(self, username) - self.username = username - end, + --- Sets the username used for authentication + -- + -- @param username string containing the username to user for authentication + SetUsername = function(self, username) + self.username = username + end, - --- Sets the password used for authentication - -- - -- @param password string containing the password to user for authentication - SetPassword = function(self, password) - self.password = password - end, + --- Sets the password used for authentication + -- + -- @param password string containing the password to user for authentication + SetPassword = function(self, password) + self.password = password + end, - --- Sets the database used in authentication - -- - -- @param database string containing the database name - SetDatabase = function(self, database) - self.database = database - end, + --- Sets the database used in authentication + -- + -- @param database string containing the database name + SetDatabase = function(self, database) + self.database = database + end, - --- Sets the server's name used in authentication - -- - -- @param server string containing the name or ip of the server - SetServer = function(self, server) - self.server = server - end, + --- Sets the server's name used in authentication + -- + -- @param server string containing the name or ip of the server + SetServer = function(self, server) + self.server = server + end, - SetDomain = function(self, domain) - self.domain = domain - end, + SetDomain = function(self, domain) + self.domain = domain + end, - --- Returns the authentication packet as string - -- - -- @return string containing the authentication packet - ToString = function(self) - local data - local offset = 86 - local ntlmAuth = not(not(self.domain)) - local authLen = 0 + --- Returns the authentication packet as string + -- + -- @return string containing the authentication packet + ToString = function(self) + local data + local offset = 86 + local ntlmAuth = not(not(self.domain)) + local authLen = 0 - self.cli_pid = math.random(100000) + self.cli_pid = math.random(100000) - self.length = offset + 2 * ( self.client:len() + self.app:len() + self.server:len() + self.library:len() + self.database:len() ) + self.length = offset + 2 * ( self.client:len() + self.app:len() + self.server:len() + self.library:len() + self.database:len() ) - if ( ntlmAuth ) then - authLen = 32 + #self.domain - self.length = self.length + authLen - self.options_2 = self.options_2 + 0x80 - else - self.length = self.length + 2 * (self.username:len() + self.password:len()) - end + if ( ntlmAuth ) then + authLen = 32 + #self.domain + self.length = self.length + authLen + self.options_2 = self.options_2 + 0x80 + else + self.length = self.length + 2 * (self.username:len() + self.password:len()) + end - data = bin.pack("smb - -- library (for use with named pipes). - ConnectEx = function( self, instanceInfo, connectionPreference, smbOverrides ) - if ( self._socket ) then return false, "Already connected via TCP" end - if ( self._pipe ) then return false, "Already connected via named pipes" end - connectionPreference = connectionPreference or stdnse.get_script_args('mssql.protocol') or { "TCP", "Named Pipes" } - if ( connectionPreference and 'string' == type(connectionPreference) ) then - connectionPreference = { connectionPreference } - end + --- Establishes a connection to the SQL server. + -- + -- @param self A mssql.Helper object + -- @param instanceInfo A SqlServerInstanceInfo object for the instance to + -- connect to. + -- @param connectionPreference (Optional) A list containing one or both of + -- the strings "TCP" and "Named Pipes", indicating which transport + -- methods to try and in what order. + -- @param smbOverrides (Optional) An overrides table for calls to the smb + -- library (for use with named pipes). + ConnectEx = function( self, instanceInfo, connectionPreference, smbOverrides ) + if ( self._socket ) then return false, "Already connected via TCP" end + if ( self._pipe ) then return false, "Already connected via named pipes" end + connectionPreference = connectionPreference or stdnse.get_script_args('mssql.protocol') or { "TCP", "Named Pipes" } + if ( connectionPreference and 'string' == type(connectionPreference) ) then + connectionPreference = { connectionPreference } + end - local status, result, connectionType, errorMessage - stdnse.print_debug( 3, "%s: Connection preferences for %s: %s", - "MSSQL", instanceInfo:GetName(), stdnse.strjoin( ", ", connectionPreference ) ) + local status, result, connectionType, errorMessage + stdnse.print_debug( 3, "%s: Connection preferences for %s: %s", + "MSSQL", instanceInfo:GetName(), stdnse.strjoin( ", ", connectionPreference ) ) - for _, connectionType in ipairs( connectionPreference ) do - if connectionType == "TCP" then + for _, connectionType in ipairs( connectionPreference ) do + if connectionType == "TCP" then - if not ( instanceInfo.port ) then - stdnse.print_debug( 3, "%s: Cannot connect to %s via TCP because port table is not set.", - "MSSQL", instanceInfo:GetName() ) - result = "No TCP port for this instance" - else - status, result = self:Connect( instanceInfo.host, instanceInfo.port ) - if status then return true end - end + if not ( instanceInfo.port ) then + stdnse.print_debug( 3, "%s: Cannot connect to %s via TCP because port table is not set.", + "MSSQL", instanceInfo:GetName() ) + result = "No TCP port for this instance" + else + status, result = self:Connect( instanceInfo.host, instanceInfo.port ) + if status then return true end + end - elseif connectionType == "Named Pipes" or connectionType == "NP" then + elseif connectionType == "Named Pipes" or connectionType == "NP" then - if not ( instanceInfo.pipeName ) then - stdnse.print_debug( 3, "%s: Cannot connect to %s via named pipes because pipe name is not set.", - "MSSQL", instanceInfo:GetName() ) - result = "No named pipe for this instance" - else - status, result = self:ConnectToNamedPipe( instanceInfo.host, instanceInfo.pipeName, smbOverrides ) - if status then return true end - end + if not ( instanceInfo.pipeName ) then + stdnse.print_debug( 3, "%s: Cannot connect to %s via named pipes because pipe name is not set.", + "MSSQL", instanceInfo:GetName() ) + result = "No named pipe for this instance" + else + status, result = self:ConnectToNamedPipe( instanceInfo.host, instanceInfo.pipeName, smbOverrides ) + if status then return true end + end - else - stdnse.print_debug( 1, "%s: Unknown connection preference: %s", "MSSQL", connectionType ) - return false, ("ERROR: Unknown connection preference: %s"):format(connectionType) - end + else + stdnse.print_debug( 1, "%s: Unknown connection preference: %s", "MSSQL", connectionType ) + return false, ("ERROR: Unknown connection preference: %s"):format(connectionType) + end - -- Handle any error messages - if not status then - if errorMessage then - errorMessage = string.format( "%s, %s: %s", errorMessage, connectionType, result or "nil" ) - else - errorMessage = string.format( "%s: %s", connectionType, result or "nil" ) - end - end - end + -- Handle any error messages + if not status then + if errorMessage then + errorMessage = string.format( "%s, %s: %s", errorMessage, connectionType, result or "nil" ) + else + errorMessage = string.format( "%s: %s", connectionType, result or "nil" ) + end + end + end - if not errorMessage then - errorMessage = string.format( "%s: None of the preferred connection types are available for %s\\%s", - "MSSQL", instanceInfo:GetName() ) - end + if not errorMessage then + errorMessage = string.format( "%s: None of the preferred connection types are available for %s\\%s", + "MSSQL", instanceInfo:GetName() ) + end - return false, errorMessage - end, + return false, errorMessage + end, - --- Establishes a connection to the SQL server - -- - -- @param host A host table for the target host - -- @param pipePath The path to the named pipe of the target SQL Server - -- (e.g. "\MSSQL$SQLEXPRESS\sql\query"). If nil, "\sql\query\" is used. - -- @param smbOverrides (Optional) An overrides table for calls to the smb - -- library (for use with named pipes). - -- @return status: true on success, false on failure - -- @return error_message: an error message, or nil - ConnectToNamedPipe = function( self, host, pipePath, overrides ) - if ( self._socket ) then return false, "Already connected via TCP" end + --- Establishes a connection to the SQL server + -- + -- @param host A host table for the target host + -- @param pipePath The path to the named pipe of the target SQL Server + -- (e.g. "\MSSQL$SQLEXPRESS\sql\query"). If nil, "\sql\query\" is used. + -- @param smbOverrides (Optional) An overrides table for calls to the smb + -- library (for use with named pipes). + -- @return status: true on success, false on failure + -- @return error_message: an error message, or nil + ConnectToNamedPipe = function( self, host, pipePath, overrides ) + if ( self._socket ) then return false, "Already connected via TCP" end - if ( SCANNED_PORTS_ONLY and smb.get_port( host ) == nil ) then - stdnse.print_debug( 2, "%s: Connection disallowed: scanned-ports-only is set and no SMB port is available", "MSSQL" ) - return false, "Connection disallowed: scanned-ports-only" - end + if ( SCANNED_PORTS_ONLY and smb.get_port( host ) == nil ) then + stdnse.print_debug( 2, "%s: Connection disallowed: scanned-ports-only is set and no SMB port is available", "MSSQL" ) + return false, "Connection disallowed: scanned-ports-only" + end - pipePath = pipePath or "\\sql\\query" + pipePath = pipePath or "\\sql\\query" - self._pipe = namedpipes.named_pipe:new() - local status, result = self._pipe:connect( host, pipePath, overrides ) - if ( status ) then - self._name = self._pipe.pipe - else - self._pipe = nil - end + self._pipe = namedpipes.named_pipe:new() + local status, result = self._pipe:connect( host, pipePath, overrides ) + if ( status ) then + self._name = self._pipe.pipe + else + self._pipe = nil + end - return status, result - end, + return status, result + end, - --- Establishes a connection to the SQL server - -- - -- @param host table containing host information - -- @param port table containing port information - -- @return status true on success, false on failure - -- @return result containing error message on failure - Connect = function( self, host, port ) - if ( self._pipe ) then return false, "Already connected via named pipes" end + --- Establishes a connection to the SQL server + -- + -- @param host table containing host information + -- @param port table containing port information + -- @return status true on success, false on failure + -- @return result containing error message on failure + Connect = function( self, host, port ) + if ( self._pipe ) then return false, "Already connected via named pipes" end - if ( SCANNED_PORTS_ONLY and nmap.get_port_state( host, port ) == nil ) then - stdnse.print_debug( 2, "%s: Connection disallowed: scanned-ports-only is set and port %d was not scanned", "MSSQL", port.number ) - return false, "Connection disallowed: scanned-ports-only" - end + if ( SCANNED_PORTS_ONLY and nmap.get_port_state( host, port ) == nil ) then + stdnse.print_debug( 2, "%s: Connection disallowed: scanned-ports-only is set and port %d was not scanned", "MSSQL", port.number ) + return false, "Connection disallowed: scanned-ports-only" + end - local status, result, lport, _ + local status, result, lport, _ - self._socket = nmap.new_socket() + self._socket = nmap.new_socket() - -- Set the timeout to something realistic for connects - self._socket:set_timeout( 5000 ) - status, result = self._socket:connect(host, port) + -- Set the timeout to something realistic for connects + self._socket:set_timeout( 5000 ) + status, result = self._socket:connect(host, port) - if ( status ) then - -- Sometimes a Query can take a long time to respond, so we set - -- the timeout to 30 seconds. This shouldn't be a problem as the - -- library attempt to decode the protocol and avoid reading past - -- the end of the input buffer. So the only time the timeout is - -- triggered is when waiting for a response to a query. - self._socket:set_timeout( MSSQL_TIMEOUT * 1000 ) + if ( status ) then + -- Sometimes a Query can take a long time to respond, so we set + -- the timeout to 30 seconds. This shouldn't be a problem as the + -- library attempt to decode the protocol and avoid reading past + -- the end of the input buffer. So the only time the timeout is + -- triggered is when waiting for a response to a query. + self._socket:set_timeout( MSSQL_TIMEOUT * 1000 ) - status, _, lport, _, _ = self._socket:get_info() - end + status, _, lport, _, _ = self._socket:get_info() + end - if ( not(status) ) then - self._socket = nil - stdnse.print_debug( 2, "%s: Socket connection failed on %s:%s", "MSSQL", host.ip, port.number ) - return false, "Socket connection failed" - end - self._name = string.format( "%s:%s", host.ip, port.number ) + if ( not(status) ) then + self._socket = nil + stdnse.print_debug( 2, "%s: Socket connection failed on %s:%s", "MSSQL", host.ip, port.number ) + return false, "Socket connection failed" + end + self._name = string.format( "%s:%s", host.ip, port.number ) - return status, result - end, + return status, result + end, - --- Disconnects from the SQL Server - -- - -- @return status true on success, false on failure - -- @return result containing error message on failure - Disconnect = function( self ) - if ( self._socket ) then - local status, result = self._socket:close() - self._socket = nil - return status, result - elseif ( self._pipe ) then - local status, result = self._pipe:disconnect() - self._pipe = nil - return status, result - else - return false, "Not connected" - end - end, + --- Disconnects from the SQL Server + -- + -- @return status true on success, false on failure + -- @return result containing error message on failure + Disconnect = function( self ) + if ( self._socket ) then + local status, result = self._socket:close() + self._socket = nil + return status, result + elseif ( self._pipe ) then + local status, result = self._pipe:disconnect() + self._pipe = nil + return status, result + else + return false, "Not connected" + end + end, - --- Sets the timeout for communication over the socket - -- - -- @param timeout number containing the new socket timeout in ms - SetTimeout = function( self, timeout ) - if ( self._socket ) then - self._socket:set_timeout(timeout) - else - return false, "Not connected" - end - end, + --- Sets the timeout for communication over the socket + -- + -- @param timeout number containing the new socket timeout in ms + SetTimeout = function( self, timeout ) + if ( self._socket ) then + self._socket:set_timeout(timeout) + else + return false, "Not connected" + end + end, - --- Gets the name of the name pipe, or nil - GetNamedPipeName = function( self ) - if ( self._pipe ) then - return self._pipe.name - else - return nil - end - end, + --- Gets the name of the name pipe, or nil + GetNamedPipeName = function( self ) + if ( self._pipe ) then + return self._pipe.name + else + return nil + end + end, - --- Send a TDS request to the server - -- - -- @param packetType A PacketType, indicating the type of TDS - -- packet being sent. - -- @param packetData A string containing the raw data to send to the server - -- @return status true on success, false on failure - -- @return result containing error message on failure - Send = function( self, packetType, packetData ) - local packetLength = packetData:len() + 8 -- +8 for TDS header - local messageStatus, spid, window = 1, 0, 0 + --- Send a TDS request to the server + -- + -- @param packetType A PacketType, indicating the type of TDS + -- packet being sent. + -- @param packetData A string containing the raw data to send to the server + -- @return status true on success, false on failure + -- @return result containing error message on failure + Send = function( self, packetType, packetData ) + local packetLength = packetData:len() + 8 -- +8 for TDS header + local messageStatus, spid, window = 1, 0, 0 - if ( packetType ~= PacketType.NTAuthentication ) then self._packetId = self._packetId + 1 end - local assembledPacket = bin.pack(">CCSSCCA", packetType, messageStatus, packetLength, spid, self._packetId, window, packetData ) + if ( packetType ~= PacketType.NTAuthentication ) then self._packetId = self._packetId + 1 end + local assembledPacket = bin.pack(">CCSSCCA", packetType, messageStatus, packetLength, spid, self._packetId, window, packetData ) - if ( self._socket ) then - return self._socket:send( assembledPacket ) - elseif ( self._pipe ) then - return self._pipe:send( assembledPacket ) - else - return false, "Not connected" - end - end, + if ( self._socket ) then + return self._socket:send( assembledPacket ) + elseif ( self._pipe ) then + return self._pipe:send( assembledPacket ) + else + return false, "Not connected" + end + end, - --- Recieves responses from SQL Server - -- The function continues to read and assemble a response until the server - -- responds with the last response flag set - -- - -- @return status true on success, false on failure - -- @return result containing raw data contents or error message on failure - -- @return errorDetail nil, or additional information about an error. In - -- the case of named pipes, this will be an SMB error name (e.g. NT_STATUS_PIPE_DISCONNECTED) - Receive = function( self ) - local status, result, errorDetail - local combinedData, readBuffer = "", "" -- the buffer is solely for the benefit of TCP connections - local tdsPacketAvailable = true + --- Recieves responses from SQL Server + -- The function continues to read and assemble a response until the server + -- responds with the last response flag set + -- + -- @return status true on success, false on failure + -- @return result containing raw data contents or error message on failure + -- @return errorDetail nil, or additional information about an error. In + -- the case of named pipes, this will be an SMB error name (e.g. NT_STATUS_PIPE_DISCONNECTED) + Receive = function( self ) + local status, result, errorDetail + local combinedData, readBuffer = "", "" -- the buffer is solely for the benefit of TCP connections + local tdsPacketAvailable = true - if not ( self._socket or self._pipe ) then - return false, "Not connected" - end + if not ( self._socket or self._pipe ) then + return false, "Not connected" + end - -- Large messages (e.g. result sets) can be split across multiple TDS - -- packets from the server (which could themselves each be split across - -- multiple TCP packets or SMB messages). - while ( tdsPacketAvailable ) do - local packetType, messageStatus, packetLength, spid, window - local pos = 1 + -- Large messages (e.g. result sets) can be split across multiple TDS + -- packets from the server (which could themselves each be split across + -- multiple TCP packets or SMB messages). + while ( tdsPacketAvailable ) do + local packetType, messageStatus, packetLength, spid, window + local pos = 1 - if ( self._socket ) then - -- If there is existing data in the readBuffer, see if there's - -- enough to read the TDS headers for the next packet. If not, - -- do another read so we have something to work with. - if ( readBuffer:len() < 8 ) then - status, result = self._socket:receive_bytes(8 - readBuffer:len()) - readBuffer = readBuffer .. result - end - elseif ( self._pipe ) then - -- The named pipe takes care of all of its reassembly. We don't - -- have to mess with buffers and repeatedly reading until we get - -- the whole packet. We'll still write to readBuffer, though, so - -- that the common logic can be reused. - status, result, errorDetail = self._pipe:receive() - readBuffer = result - end + if ( self._socket ) then + -- If there is existing data in the readBuffer, see if there's + -- enough to read the TDS headers for the next packet. If not, + -- do another read so we have something to work with. + if ( readBuffer:len() < 8 ) then + status, result = self._socket:receive_bytes(8 - readBuffer:len()) + readBuffer = readBuffer .. result + end + elseif ( self._pipe ) then + -- The named pipe takes care of all of its reassembly. We don't + -- have to mess with buffers and repeatedly reading until we get + -- the whole packet. We'll still write to readBuffer, though, so + -- that the common logic can be reused. + status, result, errorDetail = self._pipe:receive() + readBuffer = result + end - if not ( status and readBuffer ) then return false, result, errorDetail end + if not ( status and readBuffer ) then return false, result, errorDetail end - -- TDS packet validity check: packet at least as long as the TDS header - if ( readBuffer:len() < 8 ) then - stdnse.print_debug( 2, "%s: Receiving (%s): packet is invalid length", "MSSQL", self._name ) - return false, "Server returned invalid packet" - end + -- TDS packet validity check: packet at least as long as the TDS header + if ( readBuffer:len() < 8 ) then + stdnse.print_debug( 2, "%s: Receiving (%s): packet is invalid length", "MSSQL", self._name ) + return false, "Server returned invalid packet" + end - -- read in the TDS headers - pos, packetType, messageStatus, packetLength = bin.unpack(">CCS", readBuffer, pos ) - pos, spid, self._packetId, window = bin.unpack(">SCC", readBuffer, pos ) + -- read in the TDS headers + pos, packetType, messageStatus, packetLength = bin.unpack(">CCS", readBuffer, pos ) + pos, spid, self._packetId, window = bin.unpack(">SCC", readBuffer, pos ) - -- TDS packet validity check: packet type is Response (0x4) - if ( packetType ~= PacketType.Response ) then - stdnse.print_debug( 2, "%s: Receiving (%s): Expected type 0x4 (response), but received type 0x%x", - "MSSQL", self._name, packetType ) - return false, "Server returned invalid packet" - end + -- TDS packet validity check: packet type is Response (0x4) + if ( packetType ~= PacketType.Response ) then + stdnse.print_debug( 2, "%s: Receiving (%s): Expected type 0x4 (response), but received type 0x%x", + "MSSQL", self._name, packetType ) + return false, "Server returned invalid packet" + end - if ( self._socket ) then - -- If we didn't previously read in enough data to complete this - -- TDS packet, let's do so. - while ( packetLength - readBuffer:len() > 0 ) do - status, result = self._socket:receive() - if not ( status and result ) then return false, result end - readBuffer = readBuffer .. result - end - end + if ( self._socket ) then + -- If we didn't previously read in enough data to complete this + -- TDS packet, let's do so. + while ( packetLength - readBuffer:len() > 0 ) do + status, result = self._socket:receive() + if not ( status and result ) then return false, result end + readBuffer = readBuffer .. result + end + end - -- We've read in an apparently valid TDS packet - local thisPacketData = readBuffer:sub( pos, packetLength ) - -- Append its data to that of any previous TDS packets - combinedData = combinedData .. thisPacketData - if ( self._socket ) then - -- If we read in data beyond the end of this TDS packet, save it - -- so that we can use it in the next loop. - readBuffer = readBuffer:sub( packetLength + 1 ) - end + -- We've read in an apparently valid TDS packet + local thisPacketData = readBuffer:sub( pos, packetLength ) + -- Append its data to that of any previous TDS packets + combinedData = combinedData .. thisPacketData + if ( self._socket ) then + -- If we read in data beyond the end of this TDS packet, save it + -- so that we can use it in the next loop. + readBuffer = readBuffer:sub( packetLength + 1 ) + end - -- TDS packet validity check: packet length matches length from header - if ( packetLength ~= (thisPacketData:len() + 8) ) then - stdnse.print_debug( 2, "%s: Receiving (%s): Header reports length %d, actual length is %d", - "MSSQL", self._name, packetLength, thisPacketData:len() ) - return false, "Server returned invalid packet" - end + -- TDS packet validity check: packet length matches length from header + if ( packetLength ~= (thisPacketData:len() + 8) ) then + stdnse.print_debug( 2, "%s: Receiving (%s): Header reports length %d, actual length is %d", + "MSSQL", self._name, packetLength, thisPacketData:len() ) + return false, "Server returned invalid packet" + end - -- Check the status flags in the TDS packet to see if the message is - -- continued in another TDS packet. - tdsPacketAvailable = (bit.band( messageStatus, TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) ~= - TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) - end + -- Check the status flags in the TDS packet to see if the message is + -- continued in another TDS packet. + tdsPacketAvailable = (bit.band( messageStatus, TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) ~= + TDSStream.MESSAGE_STATUS_FLAGS.EndOfMessage) + end - -- return only the data section ie. without the headers - return status, combinedData - end, + -- return only the data section ie. without the headers + return status, combinedData + end, } --- Helper class Helper = { - new = function(self,o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o - end, - - --- Establishes a connection to the SQL server - -- - -- @param host table containing host information - -- @param port table containing port information - -- @return status true on success, false on failure - -- @return result containing error message on failure - ConnectEx = function( self, instanceInfo ) - local status, result - self.stream = TDSStream:new() - status, result = self.stream:ConnectEx( instanceInfo ) - if ( not(status) ) then - return false, result - end - - return true - end, - - --- Establishes a connection to the SQL server - -- - -- @param host table containing host information - -- @param port table containing port information - -- @return status true on success, false on failure - -- @return result containing error message on failure - Connect = function( self, host, port ) - local status, result - self.stream = TDSStream:new() - status, result = self.stream:Connect(host, port) - if ( not(status) ) then - return false, result - end - - return true - end, - - --- Returns true if discovery has been performed to detect - -- SQL Server instances on the given host - WasDiscoveryPerformed = function( host ) - local mutex = nmap.mutex( "discovery_performed for " .. host.ip ) - mutex( "lock" ) - nmap.registry.mssql = nmap.registry.mssql or {} - nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {} - - local wasPerformed = nmap.registry.mssql.discovery_performed[ host.ip ] or false - mutex( "done" ) - - return wasPerformed - end, - - --- Adds an instance to the list of instances kept in the Nmap registry for - -- shared use by SQL Server scripts. If the registry already contains the - -- instance, any new information is merged into the existing instance info. - -- This may happen, for example, when an instance is discovered via named - -- pipes, but the same instance has already been discovered via SSRP; this - -- will prevent duplicates, where possible. - AddOrMergeInstance = function( newInstance ) - local instanceExists - - nmap.registry.mssql = nmap.registry.mssql or {} - nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} - nmap.registry.mssql.instances[ newInstance.host.ip ] = nmap.registry.mssql.instances[ newInstance.host.ip ] or {} - - for _, existingInstance in ipairs( nmap.registry.mssql.instances[ newInstance.host.ip ] ) do - if existingInstance == newInstance then - existingInstance:Merge( newInstance ) - instanceExists = true - break - end - end - - if not instanceExists then - table.insert( nmap.registry.mssql.instances[ newInstance.host.ip ], newInstance ) - end - end, - - --- Gets a table containing SqlServerInstanceInfo objects discovered on - -- the specified host (and port, if specified). - -- - -- @param host A host table for the target host - -- @param port (Optional) If omitted, all of the instances for the host - -- will be returned. - -- @return A table containing SqlServerInstanceInfo objects, or nil - GetDiscoveredInstances = function( host, port ) - nmap.registry.mssql = nmap.registry.mssql or {} - nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} - nmap.registry.mssql.instances[ host.ip ] = nmap.registry.mssql.instances[ host.ip ] or {} - - if ( not port ) then - local instances = nmap.registry.mssql.instances[ host.ip ] - if ( instances and #instances == 0 ) then instances = nil end - return instances - else - for _, instance in ipairs( nmap.registry.mssql.instances[ host.ip ] ) do - if ( instance.port and instance.port.number == port.number and - instance.port.protocol == port.protocol ) then - return { instance } - end - end - - return nil - end - end, - - --- Attempts to discover SQL Server instances using SSRP to query one or - -- more (if broadcast is used) SQL Server Browser services. - -- Any discovered instances are returned, as well as being stored for use - -- by other scripts (see mssql.Helper.GetDiscoveredInstances()). - -- - -- @param host A host table for the target. - -- @param port (Optional) A port table for the target port. If this is nil, - -- the default SSRP port (UDP 1434) is used. - -- @param broadcast If true, this will be done with an SSRP broadcast, and - -- host should contain the broadcast specification (e.g. - -- ip = "255.255.255.255"). - -- @return (status, result) If status is true, result is a table of - -- tables containing SqlServerInstanceInfo objects. The top-level table - -- is indexed by IP address. If status is false, result is an - -- error message. - DiscoverBySsrp = function( host, port, broadcast ) - - if broadcast then - local status, result = SSRP.DiscoverInstances_Broadcast( host, port ) - - if not status then - return status, result - else - for ipAddress, host in pairs( result ) do - for _, instance in ipairs( host ) do - Helper.AddOrMergeInstance( instance ) - -- Give some version info back to Nmap - if ( instance.port and instance.version ) then - instance.version:PopulateNmapPortVersion( instance.port ) - --nmap.set_port_version( instance.host, instance.port) - end - end - end - - return true, result - end - else - local status, result = SSRP.DiscoverInstances( host, port ) - - if not status then - return status, result - else - for _, instance in ipairs( result ) do - Helper.AddOrMergeInstance( instance ) - -- Give some version info back to Nmap - if ( instance.port and instance.version ) then - instance.version:PopulateNmapPortVersion( instance.port ) - nmap.set_port_version( host, instance.port) - end - end - - local instances_all = {} - instances_all[ host.ip ] = result - return true, instances_all - end - end - end, - - --- Attempts to discover a SQL Server instance listening on the specified - -- port. If an instance is discovered, it is returned, as well as being - -- stored for use by other scripts (see mssql.Helper.GetDiscoveredInstances()). - -- - -- @param host A host table for the target. - -- @param port A port table for the target port. - -- @return (status, result) If status is true, result is a table of - -- SqlServerInstanceInfo objects. If status is false, result is an - -- error message or nil. - DiscoverByTcp = function( host, port ) - local version, instance, status - -- Check to see if we've already discovered an instance on this port - instance = Helper.GetDiscoveredInstances( host, port ) - if ( not instance ) then - instance = SqlServerInstanceInfo:new() - instance.host = host - instance.port = port - - status, version = Helper.GetInstanceVersion( instance ) - if ( status ) then - Helper.AddOrMergeInstance( instance ) - -- The point of this wasn't to get the version, just to use the - -- pre-login packet to determine whether there was a SQL Server on - -- the port. However, since we have the version now, we'll store it. - instance.version = version - -- Give some version info back to Nmap - if ( instance.port and instance.version ) then - instance.version:PopulateNmapPortVersion( instance.port ) - nmap.set_port_version( host, instance.port) - end - end - end - - return (instance ~= nil), { instance } - end, - - --- Attempts to discover SQL Server instances listening on default named - -- pipes. Any discovered instances are returned, as well as being stored - -- for use by other scripts (see mssql.Helper.GetDiscoveredInstances()). - -- - -- @param host A host table for the target. - -- @param port A port table for the port to connect on for SMB - -- @return (status, result) If status is true, result is a table of - -- SqlServerInstanceInfo objects. If status is false, result is an - -- error message or nil. - DiscoverBySmb = function( host, port ) - local defaultPipes = { - "\\sql\\query", - "\\MSSQL$SQLEXPRESS\\sql\\query", - "\\MSSQL$SQLSERVER\\sql\\query", - } - local tdsStream = TDSStream:new() - local status, result, instances_host - - for _, pipeSubPath in ipairs( defaultPipes ) do - status, result = tdsStream:ConnectToNamedPipe( host, pipeSubPath, nil ) - - if status then - instances_host = {} - local instance = SqlServerInstanceInfo:new() - instance.pipeName = tdsStream:GetNamedPipeName() - tdsStream:Disconnect() - instance.host = host - - Helper.AddOrMergeInstance( instance ) - table.insert( instances_host, instance ) - else - stdnse.print_debug( 3, "DiscoverBySmb \n pipe: %s\n result: %s", pipeSubPath, tostring( result ) ) - end - end - - return (instances_host ~= nil), instances_host - end, - - --- Attempts to discover SQL Server instances by a variety of means. - -- This function calls the three DiscoverBy functions, which perform the - -- actual discovery. Any discovered instances can be retrieved using - -- mssql.Helper.GetDiscoveredInstances(). - -- - -- @param host Host table as received by the script action function - Discover = function( host ) - nmap.registry.mssql = nmap.registry.mssql or {} - nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {} - nmap.registry.mssql.discovery_performed[ host.ip ] = false - - local mutex = nmap.mutex( "discovery_performed for " .. host.ip ) - mutex( "lock" ) - - local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} ) or {number = 1433, protocol = "tcp"} - local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) or {number = 1434, protocol = "udp"} - local smbPort - -- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open - local smbPortNumber = smb.get_port( host ) - if ( smbPortNumber ) then - smbPort = nmap.get_port_state( host, {number = smbPortNumber, protocol = "tcp"} ) - -- There's no use in manually setting an SMB port; if no SMB port was - -- scanned and found open, the SMB library won't work - end - -- if the user has specified ports, we'll check those too - local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) - - if ( sqlBrowserPort and sqlBrowserPort.state ~= "closed" ) then - Helper.DiscoverBySsrp( host, sqlBrowserPort ) - end - if ( sqlDefaultPort and sqlDefaultPort.state ~= "closed" ) then - Helper.DiscoverByTcp( host, sqlDefaultPort ) - end - if ( smbPort ) then - Helper.DiscoverBySmb( host, smbPort ) - end - if ( targetInstancePorts ) then - if ( type( targetInstancePorts ) == "string" ) then - targetInstancePorts = { targetInstancePorts } - end - for _, portNumber in ipairs( targetInstancePorts ) do - portNumber = tonumber( portNumber ) - Helper.DiscoverByTcp( host, {number = portNumber, protocol = "tcp"} ) - end - end - - nmap.registry.mssql.discovery_performed[ host.ip ] = true - mutex( "done" ) - end, - - --- Returns all of the credentials available for the target instance, - -- including any set by the mssql.username and mssql.password - -- script arguments. - -- - -- @param instanceInfo A SqlServerInstanceInfo object for the target instance - -- @return A table of usernames mapped to passwords (i.e. creds[ username ] = password) - GetLoginCredentials_All = function( instanceInfo ) - local credentials = instanceInfo.credentials or {} - local credsExist = false - for _, _ in pairs( credentials ) do - credsExist = true - break - end - if ( not credsExist ) then credentials = nil end - - if ( stdnse.get_script_args( "mssql.username" ) ) then - credentials = credentials or {} - local usernameArg = stdnse.get_script_args( "mssql.username" ) - local passwordArg = stdnse.get_script_args( "mssql.password" ) or "" - credentials[ usernameArg ] = passwordArg - end - - return credentials - end, - - --- Returns a username-password set according to the following rules of - -- precedence: - -- * If the mssql.username and mssql.password - -- script arguments were set, their values are used. (If the username - -- argument was specified without the password argument, a blank - -- password is used.) - -- * If the password for the "sa" account has been discovered (e.g. by the - -- ms-sql-empty-password or ms-sql-brute - -- scripts), these credentials are used. - -- * If other credentials have been discovered, the first of these in the - -- table are used. - -- * Otherwise, nil is returned. - -- - -- @param instanceInfo A SqlServerInstanceInfo object for the target instance - -- @return (username, password) - GetLoginCredentials = function( instanceInfo ) - - -- First preference goes to any user-specified credentials - local username = stdnse.get_script_args( "mssql.username" ) - local password = stdnse.get_script_args( "mssql.password" ) or "" - - -- Otherwise, use any valid credentials that have been discovered (e.g. by ms-sql-brute) - if ( not(username) and instanceInfo.credentials ) then - -- Second preference goes to the "sa" account - if ( instanceInfo.credentials.sa ) then - username = "sa" - password = instanceInfo.credentials.sa - else - -- ok were stuck with some n00b account, just get the first one - for user, pass in pairs( instanceInfo.credentials ) do - username = user - password = pass - break - end - end - end - - return username, password - end, - - --- Disconnects from the SQL Server - -- - -- @return status true on success, false on failure - -- @return result containing error message on failure - Disconnect = function( self ) - if ( not(self.stream) ) then - return false, "Not connected to server" - end - - self.stream:Disconnect() - self.stream = nil - - return true - end, - - --- Authenticates to SQL Server. If login fails, one of the following error - -- messages will be returned: - -- * "Password is expired" - -- * "Must change password at next logon" - -- * "Account is locked out" - -- * "Login Failed" - -- - -- @param username string containing the username for authentication - -- @param password string containing the password for authentication - -- @param database string containing the database to access - -- @param servername string containing the name or ip of the remote server - -- @return status true on success, false on failure - -- @return result containing error message on failure - -- @return errorDetail nil or a LoginErrorType value, if available - Login = function( self, username, password, database, servername ) - local loginPacket = LoginPacket:new() - local status, result, data, errorDetail, token - local servername = servername or "DUMMY" - local pos = 1 - local ntlmAuth = false - - if ( not self.stream ) then - return false, "Not connected to server" - end - - loginPacket:SetUsername(username) - loginPacket:SetPassword(password) - loginPacket:SetDatabase(database) - loginPacket:SetServer(servername) - - local domain = stdnse.get_script_args("mssql.domain") - if (domain) then - if ( not(HAVE_SSL) ) then return false, "mssql: OpenSSL not present" end - ntlmAuth = true - -- if the domain was specified without an argument, set a default domain of "." - if (domain == 1 or domain == true ) then - domain = "." - end - loginPacket:SetDomain(domain) - end - - status, result = self.stream:Send( loginPacket:ToString() ) - if ( not(status) ) then - return false, result - end - - status, data, errorDetail = self.stream:Receive() - if ( not(status) ) then - -- When logging in via named pipes, SQL Server will sometimes - -- disconnect the pipe if the login attempt failed (this only seems - -- to happen with non-"sa") accounts. At this point, having - -- successfully connected and sent a message, we can be reasonably - -- comfortable that a disconnected pipe indicates a failed login. - if ( errorDetail == "NT_STATUS_PIPE_DISCONNECTED" ) then - return false, "Bad username or password", LoginErrorType.InvalidUsernameOrPassword - end - return false, data - end - - if ( ntlmAuth ) then - local pos, nonce = Token.ParseToken( data, pos ) - local authpacket = NTAuthenticationPacket:new( username, password, domain, nonce ) - status, result = self.stream:Send( authpacket:ToString() ) - status, data = self.stream:Receive() - if ( not(status) ) then - return false, data - end - end - - while( pos < data:len() ) do - pos, token = Token.ParseToken( data, pos ) - if ( -1 == pos ) then - return false, token - end - - if ( token.type == TokenType.ErrorMessage ) then - local errorMessageLookup = { - [LoginErrorType.AccountLockedOut] = "Account is locked out", - [LoginErrorType.NotAssociatedWithTrustedConnection] = "User is not associated with a trusted connection (instance may allow Windows authentication only)", - [LoginErrorType.InvalidUsernameOrPassword] = "Bad username or password", - [LoginErrorType.PasswordExpired] = "Password is expired", - [LoginErrorType.PasswordMustChange] = "Must change password at next logon", - } - local errorMessage = errorMessageLookup[ token.errno ] or string.format( "Login Failed (%s)", tostring(token.errno) ) - - return false, errorMessage, token.errno - elseif ( token.type == TokenType.LoginAcknowledgement ) then - return true, "Login Success" - end - end - - return false, "Failed to process login response" - end, - - --- Authenticates to SQL Server, using the credentials returned by - -- Helper.GetLoginCredentials(). If the login is rejected by the server, - -- the error code will be returned, as a number in the form of a - -- mssql.LoginErrorType (for which error messages can be - -- looked up in mssql.LoginErrorMessage). - -- - -- @param instanceInfo a SqlServerInstanceInfo object for the instance to log into - -- @param database string containing the database to access - -- @param servername string containing the name or ip of the remote server - -- @return status true on success, false on failure - -- @return result containing error code or error message - LoginEx = function( self, instanceInfo, database, servername ) - local servername = servername or instanceInfo.host.ip - local username, password = Helper.GetLoginCredentials( instanceInfo ) - if ( not username ) then - return false, "No login credentials" - end - - return self:Login( username, password, database, servername ) - end, - - --- Performs a SQL query and parses the response - -- - -- @param query string containing the SQL query - -- @return status true on success, false on failure - -- @return table containing a table of columns for each row - -- or error message on failure - Query = function( self, query ) - - local queryPacket = QueryPacket:new() - local status, result, data, token, colinfo, rows - local pos = 1 - - if ( nil == self.stream ) then - return false, "Not connected to server" - end - - queryPacket:SetQuery( query ) - status, result = self.stream:Send( queryPacket:ToString() ) - if ( not(status) ) then - return false, result - end - - status, data = self.stream:Receive() - if ( not(status) ) then - return false, data - end - - -- Iterate over tokens until we get to a rowtag - while( pos < data:len() ) do - local rowtag = select(2, bin.unpack("C", data, pos)) - - if ( rowtag == TokenType.Row ) then - break - end - - pos, token = Token.ParseToken( data, pos ) - if ( -1 == pos ) then - return false, token - end - if ( token.type == TokenType.ErrorMessage ) then - return false, token.error - elseif ( token.type == TokenType.TDS7Results ) then - colinfo = token.colinfo - end - end - - - rows = {} - - while(true) do - local rowtag - pos, rowtag = bin.unpack("C", data, pos ) - - if ( rowtag ~= TokenType.Row ) then - break - end - - if ( rowtag == TokenType.Row and colinfo and #colinfo > 0 ) then - local columns = {} - - for i=1, #colinfo do - 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 - table.insert(columns, val) - else - return false, ("unknown datatype=0x%X"):format(colinfo[i].type) - end - end - table.insert(rows, columns) - end - end - - result = {} - result.rows = rows - result.colinfo = colinfo - - return true, result - end, - - --- Attempts to connect to a SQL Server instance listening on a TCP port in - -- order to determine the version of the SSNetLib DLL, which is an - -- authoritative version number for the SQL Server instance itself. - -- - -- @param instanceInfo An instance of SqlServerInstanceInfo - -- @return status true on success, false on failure - -- @return versionInfo an instance of mssql.SqlServerVersionInfo, or nil - GetInstanceVersion = function( instanceInfo ) - - if ( not instanceInfo.host or not (instanceInfo:HasNetworkProtocols()) ) then return false, nil end - - local status, response, version - local tdsStream = TDSStream:new() - - status, response = tdsStream:ConnectEx( instanceInfo ) - - if ( not status ) then - stdnse.print_debug( 2, "%s: Connection to %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" ) - return false, "Connect failed" - end - - local preLoginRequest = PreLoginPacket:new() - preLoginRequest:SetInstanceName( instanceInfo.instanceName ) - - tdsStream:SetTimeout( 5000 ) - tdsStream:Send( preLoginRequest:ToBytes() ) - - -- read in any response we might get - status, response = tdsStream:Receive() - tdsStream:Disconnect() - - if status then - local preLoginResponse - status, preLoginResponse = PreLoginPacket.FromBytes( response ) - if status then - version = preLoginResponse.versionInfo - else - stdnse.print_debug( 2, "%s: Parsing of pre-login packet from %s failed: %s", - "MSSQL", instanceInfo:GetName(), preLoginResponse or "" ) - return false, "Parsing failed" - end - else - stdnse.print_debug( 2, "%s: Receive for %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" ) - return false, "Receive failed" - end - - return status, version - end, - - --- Gets a table containing SqlServerInstanceInfo objects for the instances - -- that should be run against, based on the script-args (e.g. mssql.instance) - -- - -- @param host Host table as received by the script action function - -- @param port (Optional) Port table as received by the script action function - -- @return status True on success, false on failure - -- @return instances If status is true, this will be a table with one or - -- more SqlServerInstanceInfo objects. If status is false, this will be - -- an error message. - GetTargetInstances = function( host, port ) - if ( port ) then - local status = true - local instance = Helper.GetDiscoveredInstances( host, port ) - - if ( not instance ) then - status, instance = Helper.DiscoverByTcp( host, port ) - end - if ( instance ) then - return true, instance - else - return false, "No SQL Server instance detected on this port" - end - else - local targetInstanceNames = stdnse.get_script_args( "mssql.instance-name" ) - local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) - local targetAllInstances = stdnse.get_script_args( "mssql.instance-all" ) - - if ( targetInstanceNames and targetInstancePorts ) then - return false, "Connections can be made either by instance name or port." - end - - if ( targetAllInstances and ( targetInstanceNames or targetInstancePorts ) ) then - return false, "All instances cannot be specified together with an instance name or port." - end - - if ( not (targetInstanceNames or targetInstancePorts or targetAllInstances) ) then - return false, "No instance(s) specified." - end - - if ( not Helper.WasDiscoveryPerformed( host ) ) then - stdnse.print_debug( 2, "%s: Discovery has not been performed prior to GetTargetInstances() call. Performing discovery now.", "MSSQL" ) - Helper.Discover( host ) - end - - local instanceList = Helper.GetDiscoveredInstances( host ) - if ( not instanceList ) then - return false, "No instances found on target host" - end - - local targetInstances = {} - if ( targetAllInstances ) then - targetInstances = instanceList - else - -- We want an easy way to look up whether an instance's name was - -- in our target list. So, we'll make a table of { instanceName = true, ... } - local temp = {} - if ( targetInstanceNames ) then - if ( type( targetInstanceNames ) == "string" ) then - targetInstanceNames = { targetInstanceNames } - end - for _, instanceName in ipairs( targetInstanceNames ) do - temp[ string.upper( instanceName ) ] = true - end - end - targetInstanceNames = temp - - -- Do the same for the target ports - temp = {} - if ( targetInstancePorts ) then - if ( type( targetInstancePorts ) == "string" ) then - targetInstancePorts = { targetInstancePorts } - end - for _, portNumber in ipairs( targetInstancePorts ) do - portNumber = tonumber( portNumber ) - temp[portNumber] = true - end - end - targetInstancePorts = temp - - for _, instance in ipairs( instanceList ) do - if ( instance.instanceName and targetInstanceNames[ string.upper( instance.instanceName ) ] ) then - table.insert( targetInstances, instance ) - elseif ( instance.port and targetInstancePorts[ tonumber( instance.port.number ) ] ) then - table.insert( targetInstances, instance ) - end - end - end - - if ( #targetInstances > 0 ) then - return true, targetInstances - else - return false, "Specified instance(s) not found on target host" - end - end - end, - - --- Queries the SQL Browser service for the DAC port of the specified instance - -- The DAC (Dedicated Admin Connection) port allows DBA's to connect to - -- the database when normal connection attempts fail, for example, when - -- the server is hanging, out of memory or other bad states. - -- - -- @param host Host table as received by the script action function - -- @param instanceName the instance name to probe for a DAC port - -- @return number containing the DAC port on success or nil on failure - DiscoverDACPort = function(host, instanceName) - local socket = nmap.new_socket() - socket:set_timeout(5000) - - if ( not(socket:connect(host, 1434, "udp")) ) then - return false, "Failed to connect to sqlbrowser service" - end - - if ( not(socket:send(bin.pack("Hz", "0F01", instanceName))) ) then - socket:close() - return false, "Failed to send request to sqlbrowser service" - end - - local status, data = socket:receive_buf(match.numbytes(6), true) - if ( not(status) ) then - socket:close() - return nil - end - socket:close() - - if ( #data < 6 ) then - return nil - end - return select(2, bin.unpack("mssql.instance - -- script argument. However, if a previous script has failed to find any - -- SQL Server instances on the host, the hostrule function will return - -- false to keep further scripts from running unnecessarily on that host. - -- - -- @return A hostrule function (use as hostrule = mssql.GetHostrule_Standard()) - GetHostrule_Standard = function() - return function( host ) - if ( stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil ) then - if ( Helper.WasDiscoveryPerformed( host ) ) then - return Helper.GetDiscoveredInstances( host ) ~= nil - else - return true - end - else - return false - end - end - end, - - - --- Returns a portrule for standard SQL Server scripts, which will run return - -- true if BOTH of the following conditions are met: - -- * The port has been identified as "ms-sql-s" - -- * The mssql.instance script argument has NOT been used - -- - -- @return A portrule function (use as portrule = mssql.GetPortrule_Standard()) - GetPortrule_Standard = function() - return function( host, port ) - return ( shortport.service( "ms-sql-s" )(host, port) and - stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) == nil) - end - end, + new = function(self,o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o + end, + + --- Establishes a connection to the SQL server + -- + -- @param host table containing host information + -- @param port table containing port information + -- @return status true on success, false on failure + -- @return result containing error message on failure + ConnectEx = function( self, instanceInfo ) + local status, result + self.stream = TDSStream:new() + status, result = self.stream:ConnectEx( instanceInfo ) + if ( not(status) ) then + return false, result + end + + return true + end, + + --- Establishes a connection to the SQL server + -- + -- @param host table containing host information + -- @param port table containing port information + -- @return status true on success, false on failure + -- @return result containing error message on failure + Connect = function( self, host, port ) + local status, result + self.stream = TDSStream:new() + status, result = self.stream:Connect(host, port) + if ( not(status) ) then + return false, result + end + + return true + end, + + --- Returns true if discovery has been performed to detect + -- SQL Server instances on the given host + WasDiscoveryPerformed = function( host ) + local mutex = nmap.mutex( "discovery_performed for " .. host.ip ) + mutex( "lock" ) + nmap.registry.mssql = nmap.registry.mssql or {} + nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {} + + local wasPerformed = nmap.registry.mssql.discovery_performed[ host.ip ] or false + mutex( "done" ) + + return wasPerformed + end, + + --- Adds an instance to the list of instances kept in the Nmap registry for + -- shared use by SQL Server scripts. If the registry already contains the + -- instance, any new information is merged into the existing instance info. + -- This may happen, for example, when an instance is discovered via named + -- pipes, but the same instance has already been discovered via SSRP; this + -- will prevent duplicates, where possible. + AddOrMergeInstance = function( newInstance ) + local instanceExists + + nmap.registry.mssql = nmap.registry.mssql or {} + nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} + nmap.registry.mssql.instances[ newInstance.host.ip ] = nmap.registry.mssql.instances[ newInstance.host.ip ] or {} + + for _, existingInstance in ipairs( nmap.registry.mssql.instances[ newInstance.host.ip ] ) do + if existingInstance == newInstance then + existingInstance:Merge( newInstance ) + instanceExists = true + break + end + end + + if not instanceExists then + table.insert( nmap.registry.mssql.instances[ newInstance.host.ip ], newInstance ) + end + end, + + --- Gets a table containing SqlServerInstanceInfo objects discovered on + -- the specified host (and port, if specified). + -- + -- @param host A host table for the target host + -- @param port (Optional) If omitted, all of the instances for the host + -- will be returned. + -- @return A table containing SqlServerInstanceInfo objects, or nil + GetDiscoveredInstances = function( host, port ) + nmap.registry.mssql = nmap.registry.mssql or {} + nmap.registry.mssql.instances = nmap.registry.mssql.instances or {} + nmap.registry.mssql.instances[ host.ip ] = nmap.registry.mssql.instances[ host.ip ] or {} + + if ( not port ) then + local instances = nmap.registry.mssql.instances[ host.ip ] + if ( instances and #instances == 0 ) then instances = nil end + return instances + else + for _, instance in ipairs( nmap.registry.mssql.instances[ host.ip ] ) do + if ( instance.port and instance.port.number == port.number and + instance.port.protocol == port.protocol ) then + return { instance } + end + end + + return nil + end + end, + + --- Attempts to discover SQL Server instances using SSRP to query one or + -- more (if broadcast is used) SQL Server Browser services. + -- Any discovered instances are returned, as well as being stored for use + -- by other scripts (see mssql.Helper.GetDiscoveredInstances()). + -- + -- @param host A host table for the target. + -- @param port (Optional) A port table for the target port. If this is nil, + -- the default SSRP port (UDP 1434) is used. + -- @param broadcast If true, this will be done with an SSRP broadcast, and + -- host should contain the broadcast specification (e.g. + -- ip = "255.255.255.255"). + -- @return (status, result) If status is true, result is a table of + -- tables containing SqlServerInstanceInfo objects. The top-level table + -- is indexed by IP address. If status is false, result is an + -- error message. + DiscoverBySsrp = function( host, port, broadcast ) + + if broadcast then + local status, result = SSRP.DiscoverInstances_Broadcast( host, port ) + + if not status then + return status, result + else + for ipAddress, host in pairs( result ) do + for _, instance in ipairs( host ) do + Helper.AddOrMergeInstance( instance ) + -- Give some version info back to Nmap + if ( instance.port and instance.version ) then + instance.version:PopulateNmapPortVersion( instance.port ) + --nmap.set_port_version( instance.host, instance.port) + end + end + end + + return true, result + end + else + local status, result = SSRP.DiscoverInstances( host, port ) + + if not status then + return status, result + else + for _, instance in ipairs( result ) do + Helper.AddOrMergeInstance( instance ) + -- Give some version info back to Nmap + if ( instance.port and instance.version ) then + instance.version:PopulateNmapPortVersion( instance.port ) + nmap.set_port_version( host, instance.port) + end + end + + local instances_all = {} + instances_all[ host.ip ] = result + return true, instances_all + end + end + end, + + --- Attempts to discover a SQL Server instance listening on the specified + -- port. If an instance is discovered, it is returned, as well as being + -- stored for use by other scripts (see mssql.Helper.GetDiscoveredInstances()). + -- + -- @param host A host table for the target. + -- @param port A port table for the target port. + -- @return (status, result) If status is true, result is a table of + -- SqlServerInstanceInfo objects. If status is false, result is an + -- error message or nil. + DiscoverByTcp = function( host, port ) + local version, instance, status + -- Check to see if we've already discovered an instance on this port + instance = Helper.GetDiscoveredInstances( host, port ) + if ( not instance ) then + instance = SqlServerInstanceInfo:new() + instance.host = host + instance.port = port + + status, version = Helper.GetInstanceVersion( instance ) + if ( status ) then + Helper.AddOrMergeInstance( instance ) + -- The point of this wasn't to get the version, just to use the + -- pre-login packet to determine whether there was a SQL Server on + -- the port. However, since we have the version now, we'll store it. + instance.version = version + -- Give some version info back to Nmap + if ( instance.port and instance.version ) then + instance.version:PopulateNmapPortVersion( instance.port ) + nmap.set_port_version( host, instance.port) + end + end + end + + return (instance ~= nil), { instance } + end, + + --- Attempts to discover SQL Server instances listening on default named + -- pipes. Any discovered instances are returned, as well as being stored + -- for use by other scripts (see mssql.Helper.GetDiscoveredInstances()). + -- + -- @param host A host table for the target. + -- @param port A port table for the port to connect on for SMB + -- @return (status, result) If status is true, result is a table of + -- SqlServerInstanceInfo objects. If status is false, result is an + -- error message or nil. + DiscoverBySmb = function( host, port ) + local defaultPipes = { + "\\sql\\query", + "\\MSSQL$SQLEXPRESS\\sql\\query", + "\\MSSQL$SQLSERVER\\sql\\query", + } + local tdsStream = TDSStream:new() + local status, result, instances_host + + for _, pipeSubPath in ipairs( defaultPipes ) do + status, result = tdsStream:ConnectToNamedPipe( host, pipeSubPath, nil ) + + if status then + instances_host = {} + local instance = SqlServerInstanceInfo:new() + instance.pipeName = tdsStream:GetNamedPipeName() + tdsStream:Disconnect() + instance.host = host + + Helper.AddOrMergeInstance( instance ) + table.insert( instances_host, instance ) + else + stdnse.print_debug( 3, "DiscoverBySmb \n pipe: %s\n result: %s", pipeSubPath, tostring( result ) ) + end + end + + return (instances_host ~= nil), instances_host + end, + + --- Attempts to discover SQL Server instances by a variety of means. + -- This function calls the three DiscoverBy functions, which perform the + -- actual discovery. Any discovered instances can be retrieved using + -- mssql.Helper.GetDiscoveredInstances(). + -- + -- @param host Host table as received by the script action function + Discover = function( host ) + nmap.registry.mssql = nmap.registry.mssql or {} + nmap.registry.mssql.discovery_performed = nmap.registry.mssql.discovery_performed or {} + nmap.registry.mssql.discovery_performed[ host.ip ] = false + + local mutex = nmap.mutex( "discovery_performed for " .. host.ip ) + mutex( "lock" ) + + local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} ) or {number = 1433, protocol = "tcp"} + local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} ) or {number = 1434, protocol = "udp"} + local smbPort + -- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open + local smbPortNumber = smb.get_port( host ) + if ( smbPortNumber ) then + smbPort = nmap.get_port_state( host, {number = smbPortNumber, protocol = "tcp"} ) + -- There's no use in manually setting an SMB port; if no SMB port was + -- scanned and found open, the SMB library won't work + end + -- if the user has specified ports, we'll check those too + local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) + + if ( sqlBrowserPort and sqlBrowserPort.state ~= "closed" ) then + Helper.DiscoverBySsrp( host, sqlBrowserPort ) + end + if ( sqlDefaultPort and sqlDefaultPort.state ~= "closed" ) then + Helper.DiscoverByTcp( host, sqlDefaultPort ) + end + if ( smbPort ) then + Helper.DiscoverBySmb( host, smbPort ) + end + if ( targetInstancePorts ) then + if ( type( targetInstancePorts ) == "string" ) then + targetInstancePorts = { targetInstancePorts } + end + for _, portNumber in ipairs( targetInstancePorts ) do + portNumber = tonumber( portNumber ) + Helper.DiscoverByTcp( host, {number = portNumber, protocol = "tcp"} ) + end + end + + nmap.registry.mssql.discovery_performed[ host.ip ] = true + mutex( "done" ) + end, + + --- Returns all of the credentials available for the target instance, + -- including any set by the mssql.username and mssql.password + -- script arguments. + -- + -- @param instanceInfo A SqlServerInstanceInfo object for the target instance + -- @return A table of usernames mapped to passwords (i.e. creds[ username ] = password) + GetLoginCredentials_All = function( instanceInfo ) + local credentials = instanceInfo.credentials or {} + local credsExist = false + for _, _ in pairs( credentials ) do + credsExist = true + break + end + if ( not credsExist ) then credentials = nil end + + if ( stdnse.get_script_args( "mssql.username" ) ) then + credentials = credentials or {} + local usernameArg = stdnse.get_script_args( "mssql.username" ) + local passwordArg = stdnse.get_script_args( "mssql.password" ) or "" + credentials[ usernameArg ] = passwordArg + end + + return credentials + end, + + --- Returns a username-password set according to the following rules of + -- precedence: + -- * If the mssql.username and mssql.password + -- script arguments were set, their values are used. (If the username + -- argument was specified without the password argument, a blank + -- password is used.) + -- * If the password for the "sa" account has been discovered (e.g. by the + -- ms-sql-empty-password or ms-sql-brute + -- scripts), these credentials are used. + -- * If other credentials have been discovered, the first of these in the + -- table are used. + -- * Otherwise, nil is returned. + -- + -- @param instanceInfo A SqlServerInstanceInfo object for the target instance + -- @return (username, password) + GetLoginCredentials = function( instanceInfo ) + + -- First preference goes to any user-specified credentials + local username = stdnse.get_script_args( "mssql.username" ) + local password = stdnse.get_script_args( "mssql.password" ) or "" + + -- Otherwise, use any valid credentials that have been discovered (e.g. by ms-sql-brute) + if ( not(username) and instanceInfo.credentials ) then + -- Second preference goes to the "sa" account + if ( instanceInfo.credentials.sa ) then + username = "sa" + password = instanceInfo.credentials.sa + else + -- ok were stuck with some n00b account, just get the first one + for user, pass in pairs( instanceInfo.credentials ) do + username = user + password = pass + break + end + end + end + + return username, password + end, + + --- Disconnects from the SQL Server + -- + -- @return status true on success, false on failure + -- @return result containing error message on failure + Disconnect = function( self ) + if ( not(self.stream) ) then + return false, "Not connected to server" + end + + self.stream:Disconnect() + self.stream = nil + + return true + end, + + --- Authenticates to SQL Server. If login fails, one of the following error + -- messages will be returned: + -- * "Password is expired" + -- * "Must change password at next logon" + -- * "Account is locked out" + -- * "Login Failed" + -- + -- @param username string containing the username for authentication + -- @param password string containing the password for authentication + -- @param database string containing the database to access + -- @param servername string containing the name or ip of the remote server + -- @return status true on success, false on failure + -- @return result containing error message on failure + -- @return errorDetail nil or a LoginErrorType value, if available + Login = function( self, username, password, database, servername ) + local loginPacket = LoginPacket:new() + local status, result, data, errorDetail, token + local servername = servername or "DUMMY" + local pos = 1 + local ntlmAuth = false + + if ( not self.stream ) then + return false, "Not connected to server" + end + + loginPacket:SetUsername(username) + loginPacket:SetPassword(password) + loginPacket:SetDatabase(database) + loginPacket:SetServer(servername) + + local domain = stdnse.get_script_args("mssql.domain") + if (domain) then + if ( not(HAVE_SSL) ) then return false, "mssql: OpenSSL not present" end + ntlmAuth = true + -- if the domain was specified without an argument, set a default domain of "." + if (domain == 1 or domain == true ) then + domain = "." + end + loginPacket:SetDomain(domain) + end + + status, result = self.stream:Send( loginPacket:ToString() ) + if ( not(status) ) then + return false, result + end + + status, data, errorDetail = self.stream:Receive() + if ( not(status) ) then + -- When logging in via named pipes, SQL Server will sometimes + -- disconnect the pipe if the login attempt failed (this only seems + -- to happen with non-"sa") accounts. At this point, having + -- successfully connected and sent a message, we can be reasonably + -- comfortable that a disconnected pipe indicates a failed login. + if ( errorDetail == "NT_STATUS_PIPE_DISCONNECTED" ) then + return false, "Bad username or password", LoginErrorType.InvalidUsernameOrPassword + end + return false, data + end + + if ( ntlmAuth ) then + local pos, nonce = Token.ParseToken( data, pos ) + local authpacket = NTAuthenticationPacket:new( username, password, domain, nonce ) + status, result = self.stream:Send( authpacket:ToString() ) + status, data = self.stream:Receive() + if ( not(status) ) then + return false, data + end + end + + while( pos < data:len() ) do + pos, token = Token.ParseToken( data, pos ) + if ( -1 == pos ) then + return false, token + end + + if ( token.type == TokenType.ErrorMessage ) then + local errorMessageLookup = { + [LoginErrorType.AccountLockedOut] = "Account is locked out", + [LoginErrorType.NotAssociatedWithTrustedConnection] = "User is not associated with a trusted connection (instance may allow Windows authentication only)", + [LoginErrorType.InvalidUsernameOrPassword] = "Bad username or password", + [LoginErrorType.PasswordExpired] = "Password is expired", + [LoginErrorType.PasswordMustChange] = "Must change password at next logon", + } + local errorMessage = errorMessageLookup[ token.errno ] or string.format( "Login Failed (%s)", tostring(token.errno) ) + + return false, errorMessage, token.errno + elseif ( token.type == TokenType.LoginAcknowledgement ) then + return true, "Login Success" + end + end + + return false, "Failed to process login response" + end, + + --- Authenticates to SQL Server, using the credentials returned by + -- Helper.GetLoginCredentials(). If the login is rejected by the server, + -- the error code will be returned, as a number in the form of a + -- mssql.LoginErrorType (for which error messages can be + -- looked up in mssql.LoginErrorMessage). + -- + -- @param instanceInfo a SqlServerInstanceInfo object for the instance to log into + -- @param database string containing the database to access + -- @param servername string containing the name or ip of the remote server + -- @return status true on success, false on failure + -- @return result containing error code or error message + LoginEx = function( self, instanceInfo, database, servername ) + local servername = servername or instanceInfo.host.ip + local username, password = Helper.GetLoginCredentials( instanceInfo ) + if ( not username ) then + return false, "No login credentials" + end + + return self:Login( username, password, database, servername ) + end, + + --- Performs a SQL query and parses the response + -- + -- @param query string containing the SQL query + -- @return status true on success, false on failure + -- @return table containing a table of columns for each row + -- or error message on failure + Query = function( self, query ) + + local queryPacket = QueryPacket:new() + local status, result, data, token, colinfo, rows + local pos = 1 + + if ( nil == self.stream ) then + return false, "Not connected to server" + end + + queryPacket:SetQuery( query ) + status, result = self.stream:Send( queryPacket:ToString() ) + if ( not(status) ) then + return false, result + end + + status, data = self.stream:Receive() + if ( not(status) ) then + return false, data + end + + -- Iterate over tokens until we get to a rowtag + while( pos < data:len() ) do + local rowtag = select(2, bin.unpack("C", data, pos)) + + if ( rowtag == TokenType.Row ) then + break + end + + pos, token = Token.ParseToken( data, pos ) + if ( -1 == pos ) then + return false, token + end + if ( token.type == TokenType.ErrorMessage ) then + return false, token.error + elseif ( token.type == TokenType.TDS7Results ) then + colinfo = token.colinfo + end + end + + + rows = {} + + while(true) do + local rowtag + pos, rowtag = bin.unpack("C", data, pos ) + + if ( rowtag ~= TokenType.Row ) then + break + end + + if ( rowtag == TokenType.Row and colinfo and #colinfo > 0 ) then + local columns = {} + + for i=1, #colinfo do + 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 + table.insert(columns, val) + else + return false, ("unknown datatype=0x%X"):format(colinfo[i].type) + end + end + table.insert(rows, columns) + end + end + + result = {} + result.rows = rows + result.colinfo = colinfo + + return true, result + end, + + --- Attempts to connect to a SQL Server instance listening on a TCP port in + -- order to determine the version of the SSNetLib DLL, which is an + -- authoritative version number for the SQL Server instance itself. + -- + -- @param instanceInfo An instance of SqlServerInstanceInfo + -- @return status true on success, false on failure + -- @return versionInfo an instance of mssql.SqlServerVersionInfo, or nil + GetInstanceVersion = function( instanceInfo ) + + if ( not instanceInfo.host or not (instanceInfo:HasNetworkProtocols()) ) then return false, nil end + + local status, response, version + local tdsStream = TDSStream:new() + + status, response = tdsStream:ConnectEx( instanceInfo ) + + if ( not status ) then + stdnse.print_debug( 2, "%s: Connection to %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" ) + return false, "Connect failed" + end + + local preLoginRequest = PreLoginPacket:new() + preLoginRequest:SetInstanceName( instanceInfo.instanceName ) + + tdsStream:SetTimeout( 5000 ) + tdsStream:Send( preLoginRequest:ToBytes() ) + + -- read in any response we might get + status, response = tdsStream:Receive() + tdsStream:Disconnect() + + if status then + local preLoginResponse + status, preLoginResponse = PreLoginPacket.FromBytes( response ) + if status then + version = preLoginResponse.versionInfo + else + stdnse.print_debug( 2, "%s: Parsing of pre-login packet from %s failed: %s", + "MSSQL", instanceInfo:GetName(), preLoginResponse or "" ) + return false, "Parsing failed" + end + else + stdnse.print_debug( 2, "%s: Receive for %s failed: %s", "MSSQL", instanceInfo:GetName(), response or "" ) + return false, "Receive failed" + end + + return status, version + end, + + --- Gets a table containing SqlServerInstanceInfo objects for the instances + -- that should be run against, based on the script-args (e.g. mssql.instance) + -- + -- @param host Host table as received by the script action function + -- @param port (Optional) Port table as received by the script action function + -- @return status True on success, false on failure + -- @return instances If status is true, this will be a table with one or + -- more SqlServerInstanceInfo objects. If status is false, this will be + -- an error message. + GetTargetInstances = function( host, port ) + if ( port ) then + local status = true + local instance = Helper.GetDiscoveredInstances( host, port ) + + if ( not instance ) then + status, instance = Helper.DiscoverByTcp( host, port ) + end + if ( instance ) then + return true, instance + else + return false, "No SQL Server instance detected on this port" + end + else + local targetInstanceNames = stdnse.get_script_args( "mssql.instance-name" ) + local targetInstancePorts = stdnse.get_script_args( "mssql.instance-port" ) + local targetAllInstances = stdnse.get_script_args( "mssql.instance-all" ) + + if ( targetInstanceNames and targetInstancePorts ) then + return false, "Connections can be made either by instance name or port." + end + + if ( targetAllInstances and ( targetInstanceNames or targetInstancePorts ) ) then + return false, "All instances cannot be specified together with an instance name or port." + end + + if ( not (targetInstanceNames or targetInstancePorts or targetAllInstances) ) then + return false, "No instance(s) specified." + end + + if ( not Helper.WasDiscoveryPerformed( host ) ) then + stdnse.print_debug( 2, "%s: Discovery has not been performed prior to GetTargetInstances() call. Performing discovery now.", "MSSQL" ) + Helper.Discover( host ) + end + + local instanceList = Helper.GetDiscoveredInstances( host ) + if ( not instanceList ) then + return false, "No instances found on target host" + end + + local targetInstances = {} + if ( targetAllInstances ) then + targetInstances = instanceList + else + -- We want an easy way to look up whether an instance's name was + -- in our target list. So, we'll make a table of { instanceName = true, ... } + local temp = {} + if ( targetInstanceNames ) then + if ( type( targetInstanceNames ) == "string" ) then + targetInstanceNames = { targetInstanceNames } + end + for _, instanceName in ipairs( targetInstanceNames ) do + temp[ string.upper( instanceName ) ] = true + end + end + targetInstanceNames = temp + + -- Do the same for the target ports + temp = {} + if ( targetInstancePorts ) then + if ( type( targetInstancePorts ) == "string" ) then + targetInstancePorts = { targetInstancePorts } + end + for _, portNumber in ipairs( targetInstancePorts ) do + portNumber = tonumber( portNumber ) + temp[portNumber] = true + end + end + targetInstancePorts = temp + + for _, instance in ipairs( instanceList ) do + if ( instance.instanceName and targetInstanceNames[ string.upper( instance.instanceName ) ] ) then + table.insert( targetInstances, instance ) + elseif ( instance.port and targetInstancePorts[ tonumber( instance.port.number ) ] ) then + table.insert( targetInstances, instance ) + end + end + end + + if ( #targetInstances > 0 ) then + return true, targetInstances + else + return false, "Specified instance(s) not found on target host" + end + end + end, + + --- Queries the SQL Browser service for the DAC port of the specified instance + -- The DAC (Dedicated Admin Connection) port allows DBA's to connect to + -- the database when normal connection attempts fail, for example, when + -- the server is hanging, out of memory or other bad states. + -- + -- @param host Host table as received by the script action function + -- @param instanceName the instance name to probe for a DAC port + -- @return number containing the DAC port on success or nil on failure + DiscoverDACPort = function(host, instanceName) + local socket = nmap.new_socket() + socket:set_timeout(5000) + + if ( not(socket:connect(host, 1434, "udp")) ) then + return false, "Failed to connect to sqlbrowser service" + end + + if ( not(socket:send(bin.pack("Hz", "0F01", instanceName))) ) then + socket:close() + return false, "Failed to send request to sqlbrowser service" + end + + local status, data = socket:receive_buf(match.numbytes(6), true) + if ( not(status) ) then + socket:close() + return nil + end + socket:close() + + if ( #data < 6 ) then + return nil + end + return select(2, bin.unpack("mssql.instance + -- script argument. However, if a previous script has failed to find any + -- SQL Server instances on the host, the hostrule function will return + -- false to keep further scripts from running unnecessarily on that host. + -- + -- @return A hostrule function (use as hostrule = mssql.GetHostrule_Standard()) + GetHostrule_Standard = function() + return function( host ) + if ( stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil ) then + if ( Helper.WasDiscoveryPerformed( host ) ) then + return Helper.GetDiscoveredInstances( host ) ~= nil + else + return true + end + else + return false + end + end + end, + + + --- Returns a portrule for standard SQL Server scripts, which will run return + -- true if BOTH of the following conditions are met: + -- * The port has been identified as "ms-sql-s" + -- * The mssql.instance script argument has NOT been used + -- + -- @return A portrule function (use as portrule = mssql.GetPortrule_Standard()) + GetPortrule_Standard = function() + return function( host, port ) + return ( shortport.service( "ms-sql-s" )(host, port) and + stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) == nil) + end + end, } Auth = { - --- Encrypts a password using the TDS7 *ultra secure* XOR encryption - -- - -- @param password string containing the password to encrypt - -- @return string containing the encrypted password - TDS7CryptPass = function(password) - local xormask = 0x5a5a - local result = "" + --- Encrypts a password using the TDS7 *ultra secure* XOR encryption + -- + -- @param password string containing the password to encrypt + -- @return string containing the encrypted password + TDS7CryptPass = function(password) + local xormask = 0x5a5a + local result = "" - for i=1, password:len() do - local c = bit.bxor( string.byte( password:sub( i, i ) ), xormask ) - local m1= bit.band( bit.rshift( c, 4 ), 0x0F0F ) - local m2= bit.band( bit.lshift( c, 4 ), 0xF0F0 ) - result = result .. bin.pack("S", bit.bor( m1, m2 ) ) - end - return result - end, + for i=1, password:len() do + local c = bit.bxor( string.byte( password:sub( i, i ) ), xormask ) + local m1= bit.band( bit.rshift( c, 4 ), 0x0F0F ) + local m2= bit.band( bit.lshift( c, 4 ), 0xF0F0 ) + result = result .. bin.pack("S", bit.bor( m1, m2 ) ) + end + return result + end, - LmResponse = function( password, nonce ) + LmResponse = function( password, nonce ) - if ( not(HAVE_SSL) ) then - stdnse.print_debug("ERROR: Nmap is missing OpenSSL") - return - end + if ( not(HAVE_SSL) ) then + stdnse.print_debug("ERROR: Nmap is missing OpenSSL") + return + end - if(#password < 14) then - password = password .. string.rep(string.char(0), 14 - #password) - end + if(#password < 14) then + password = password .. string.rep(string.char(0), 14 - #password) + end - password = password:upper() + password = password:upper() - -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated) - local str1 = string.sub(password, 1, 7) - local str2 = string.sub(password, 8, 14) + -- Take the first and second half of the password (note that if it's longer than 14 characters, it's truncated) + local str1 = string.sub(password, 1, 7) + local str2 = string.sub(password, 8, 14) - -- Generate the keys - local key1 = openssl.DES_string_to_key(str1) - local key2 = openssl.DES_string_to_key(str2) + -- Generate the keys + local key1 = openssl.DES_string_to_key(str1) + local key2 = openssl.DES_string_to_key(str2) - local result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce) + local result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce) - if(#result < 21) then - result = result .. string.rep(string.char(0), 21 - #result) - end + if(#result < 21) then + result = result .. string.rep(string.char(0), 21 - #result) + end - str1 = string.sub(result, 1, 7) - str2 = string.sub(result, 8, 14) - local str3 = string.sub(result, 15, 21) + str1 = string.sub(result, 1, 7) + str2 = string.sub(result, 8, 14) + local str3 = string.sub(result, 15, 21) - key1 = openssl.DES_string_to_key(str1) - key2 = openssl.DES_string_to_key(str2) - local key3 = openssl.DES_string_to_key(str3) + key1 = openssl.DES_string_to_key(str1) + key2 = openssl.DES_string_to_key(str2) + local key3 = openssl.DES_string_to_key(str3) - result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce) .. openssl.encrypt("DES", key3, nil, nonce) - return result - end, + result = openssl.encrypt("DES", key1, nil, nonce) .. openssl.encrypt("DES", key2, nil, nonce) .. openssl.encrypt("DES", key3, nil, nonce) + return result + end, - NtlmResponse = function( password, nonce ) - local lm_response, ntlm_response, mac_key = smbauth.get_password_response(nil, - nil, - nil, - password, - nil, - "v1", - nonce, - false - ) - return ntlm_response - end, + NtlmResponse = function( password, nonce ) + local lm_response, ntlm_response, mac_key = smbauth.get_password_response(nil, + nil, + nil, + password, + nil, + "v1", + nonce, + false + ) + return ntlm_response + end, } --- "static" Utility class containing mostly conversion functions Util = { - --- Converts a string to a wide string - -- - -- @param str string to be converted - -- @return string containing a two byte representation of str where a zero - -- byte character has been tagged on to each character. - ToWideChar = function( str ) - return str:gsub("(.)", "%1" .. string.char(0x00) ) - end, + --- Converts a string to a wide string + -- + -- @param str string to be converted + -- @return string containing a two byte representation of str where a zero + -- byte character has been tagged on to each character. + ToWideChar = function( str ) + return str:gsub("(.)", "%1" .. string.char(0x00) ) + end, - --- Concerts a wide string to string - -- - -- @param wstr containing the wide string to convert - -- @return string with every other character removed - FromWideChar = function( wstr ) - local str = "" - if ( nil == wstr ) then - return nil - end - for i=1, wstr:len(), 2 do - str = str .. wstr:sub(i, i) - end - return str - end, + --- Concerts a wide string to string + -- + -- @param wstr containing the wide string to convert + -- @return string with every other character removed + FromWideChar = function( wstr ) + local str = "" + if ( nil == wstr ) then + return nil + end + for i=1, wstr:len(), 2 do + str = str .. wstr:sub(i, i) + end + return str + end, - --- Takes a table as returned by Query and does some fancy formatting - -- better suitable for stdnse.output_result - -- - -- @param tbl as recieved by Helper.Query - -- @param with_headers boolean true if output should contain column headers - -- @return table suitable for stdnse.output_result - FormatOutputTable = function ( tbl, with_headers ) - local new_tbl = {} - local col_names = {} + --- Takes a table as returned by Query and does some fancy formatting + -- better suitable for stdnse.output_result + -- + -- @param tbl as recieved by Helper.Query + -- @param with_headers boolean true if output should contain column headers + -- @return table suitable for stdnse.output_result + FormatOutputTable = function ( tbl, with_headers ) + local new_tbl = {} + local col_names = {} - if ( not(tbl) ) then - return - end + if ( not(tbl) ) then + return + end - if ( with_headers and tbl.rows and #tbl.rows > 0 ) then - local headers - for k, v in pairs( tbl.colinfo ) do - table.insert( col_names, v.text) - end - headers = stdnse.strjoin("\t", col_names) - table.insert( new_tbl, headers) - headers = headers:gsub("[^%s]", "=") - table.insert( new_tbl, headers ) - end + if ( with_headers and tbl.rows and #tbl.rows > 0 ) then + local headers + for k, v in pairs( tbl.colinfo ) do + table.insert( col_names, v.text) + end + headers = stdnse.strjoin("\t", col_names) + table.insert( new_tbl, headers) + headers = headers:gsub("[^%s]", "=") + table.insert( new_tbl, headers ) + end - for _, v in ipairs( tbl.rows ) do - table.insert( new_tbl, stdnse.strjoin("\t", v) ) - end + for _, v in ipairs( tbl.rows ) do + table.insert( new_tbl, stdnse.strjoin("\t", v) ) + end - return new_tbl - end, + return new_tbl + end, } return _ENV; diff --git a/nselib/mysql.lua b/nselib/mysql.lua index 94b9ba650..1c56740dc 100644 --- a/nselib/mysql.lua +++ b/nselib/mysql.lua @@ -29,52 +29,52 @@ local HAVE_SSL, openssl = pcall(require,'openssl') Capabilities = { - LongPassword = 0x1, - FoundRows = 0x2, - LongColumnFlag = 0x4, - ConnectWithDatabase = 0x8, - DontAllowDatabaseTableColumn = 0x10, - SupportsCompression = 0x20, - ODBCClient = 0x40, - SupportsLoadDataLocal = 0x80, - IgnoreSpaceBeforeParenthesis = 0x100, - Speaks41ProtocolNew = 0x200, - InteractiveClient = 0x400, - SwitchToSSLAfterHandshake = 0x800, - IgnoreSigpipes = 0x1000, - SupportsTransactions = 0x2000, - Speaks41ProtocolOld = 0x4000, - Support41Auth = 0x8000 + LongPassword = 0x1, + FoundRows = 0x2, + LongColumnFlag = 0x4, + ConnectWithDatabase = 0x8, + DontAllowDatabaseTableColumn = 0x10, + SupportsCompression = 0x20, + ODBCClient = 0x40, + SupportsLoadDataLocal = 0x80, + IgnoreSpaceBeforeParenthesis = 0x100, + Speaks41ProtocolNew = 0x200, + InteractiveClient = 0x400, + SwitchToSSLAfterHandshake = 0x800, + IgnoreSigpipes = 0x1000, + SupportsTransactions = 0x2000, + Speaks41ProtocolOld = 0x4000, + Support41Auth = 0x8000 } ExtCapabilities = { - SupportsMultipleStatments = 0x1, - SupportsMultipleResults = 0x2 + SupportsMultipleStatments = 0x1, + SupportsMultipleResults = 0x2 } Charset = { - latin1_COLLATE_latin1_swedish_ci = 0x8 + latin1_COLLATE_latin1_swedish_ci = 0x8 } ServerStatus = { - InTransaction = 0x1, - AutoCommit = 0x2, - MoreResults = 0x4, - MultiQuery = 0x8, - BadIndexUsed = 0x10, - NoIndexUsed = 0x20, - CursorExists = 0x40, - LastRowSebd = 0x80, - DatabaseDropped = 0x100, - NoBackslashEscapes = 0x200 + InTransaction = 0x1, + AutoCommit = 0x2, + MoreResults = 0x4, + MultiQuery = 0x8, + BadIndexUsed = 0x10, + NoIndexUsed = 0x20, + CursorExists = 0x40, + LastRowSebd = 0x80, + DatabaseDropped = 0x100, + NoBackslashEscapes = 0x200 } Command = { - Query = 3 + Query = 3 } local MAXPACKET = 16777216 @@ -87,14 +87,14 @@ local HEADER_SIZE = 4 -- @return response table containing the fields len and packetno local function decodeHeader( data, pos ) - local response = {} - local pos, tmp = pos or 1, 0 + local response = {} + local pos, tmp = pos or 1, 0 - pos, tmp = bin.unpack( "I", data, pos ) - response.len = bit.band( tmp,255 ) - response.number = bit.rshift( tmp, 24 ) + pos, tmp = bin.unpack( "I", data, pos ) + response.len = bit.band( tmp,255 ) + response.number = bit.rshift( tmp, 24 ) - return pos, response + return pos, response end --- Recieves the server greeting upon intial connection @@ -106,43 +106,43 @@ end -- status or error message on failure (status == false) function receiveGreeting( socket ) - local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end - local try = nmap.new_try(catch) - local data = try( socket:receive_bytes(HEADER_SIZE) ) - local pos, response, tmp, _ + local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end + local try = nmap.new_try(catch) + local data = try( socket:receive_bytes(HEADER_SIZE) ) + local pos, response, tmp, _ - pos, response = decodeHeader( data, 1 ) + pos, response = decodeHeader( data, 1 ) - -- do we need to read the remainder - if ( #data - HEADER_SIZE < response.len ) then - local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) ) - data = data .. tmp - end + -- do we need to read the remainder + if ( #data - HEADER_SIZE < response.len ) then + local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) ) + data = data .. tmp + end - local is_error - pos, is_error = bin.unpack( "C", data, pos ) + local is_error + pos, is_error = bin.unpack( "C", data, pos ) - if ( is_error == 0xff ) then - pos, response.errorcode = bin.unpack( "S", data, pos ) - pos, response.errormsg = bin.unpack("A" .. (#data - pos + 1), data, pos ) + if ( is_error == 0xff ) then + pos, response.errorcode = bin.unpack( "S", data, pos ) + pos, response.errormsg = bin.unpack("A" .. (#data - pos + 1), data, pos ) - return false, response.errormsg - end + return false, response.errormsg + end - pos, response.proto = bin.unpack( "C", data, pos ) - pos, response.version = bin.unpack( "z", data, pos ) - pos, response.threadid = bin.unpack( "I", data, pos ) - pos, response.salt, _ = bin.unpack( "A8C", data, pos ) - pos, response.capabilities = bin.unpack( "S", data, pos ) - pos, response.charset = bin.unpack( "C", data, pos ) - pos, response.status = bin.unpack( "S", data, pos ) - pos, _ = bin.unpack( "A13", data, pos ) - pos, tmp = bin.unpack( "A12", data, pos ) + pos, response.proto = bin.unpack( "C", data, pos ) + pos, response.version = bin.unpack( "z", data, pos ) + pos, response.threadid = bin.unpack( "I", data, pos ) + pos, response.salt, _ = bin.unpack( "A8C", data, pos ) + pos, response.capabilities = bin.unpack( "S", data, pos ) + pos, response.charset = bin.unpack( "C", data, pos ) + pos, response.status = bin.unpack( "S", data, pos ) + pos, _ = bin.unpack( "A13", data, pos ) + pos, tmp = bin.unpack( "A12", data, pos ) - response.salt = response.salt .. tmp - response.errorcode = 0 + response.salt = response.salt .. tmp + response.errorcode = 0 - return true, response + return true, response end @@ -153,28 +153,28 @@ end -- @param salt string containing the servers salt as obtained from receiveGreeting -- @return reply string containing the raw hashed value local function createLoginHash(pass, salt) - local hash_stage1 - local hash_stage2 - local hash_stage3 - local reply = "" - local pos, b1, b2, b3, _ = 1, 0, 0, 0 + local hash_stage1 + local hash_stage2 + local hash_stage3 + local reply = "" + local pos, b1, b2, b3, _ = 1, 0, 0, 0 - if ( not(HAVE_SSL) ) then - return nil - end + if ( not(HAVE_SSL) ) then + return nil + end - hash_stage1 = openssl.sha1( pass ) - hash_stage2 = openssl.sha1( hash_stage1 ) - hash_stage3 = openssl.sha1( salt .. hash_stage2 ) + hash_stage1 = openssl.sha1( pass ) + hash_stage2 = openssl.sha1( hash_stage1 ) + hash_stage3 = openssl.sha1( salt .. hash_stage2 ) - for pos=1, hash_stage1:len() do - _, b1 = bin.unpack( "C", hash_stage1, pos ) - _, b2 = bin.unpack( "C", hash_stage3, pos ) + for pos=1, hash_stage1:len() do + _, b1 = bin.unpack( "C", hash_stage1, pos ) + _, b2 = bin.unpack( "C", hash_stage3, pos ) - reply = reply .. string.char( bit.bxor( b2, b1 ) ) - end + reply = reply .. string.char( bit.bxor( b2, b1 ) ) + end - return reply + return reply end @@ -183,9 +183,9 @@ end -- -- @param socket already connected to the remote server -- @param params table with additional options to the loginrequest --- current supported fields are charset and authversion --- authversion is either "pre41" or "post41" (default is post41) --- currently only post41 authentication is supported +-- current supported fields are charset and authversion +-- authversion is either "pre41" or "post41" (default is post41) +-- currently only post41 authentication is supported -- @param username string containing the username of the user that is authenticating -- @param password string containing the users password or nil if empty -- @param salt string containing the servers salt as recieved from receiveGreeting @@ -193,84 +193,84 @@ end -- @return response table or error message on failure function loginRequest( socket, params, username, password, salt ) - local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end - local try = nmap.new_try(catch) - local packetno = 1 - local authversion = params.authversion or "post41" - local username = username or "" + local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end + local try = nmap.new_try(catch) + local packetno = 1 + local authversion = params.authversion or "post41" + local username = username or "" - if not(HAVE_SSL) then - return false, "No OpenSSL" - end + if not(HAVE_SSL) then + return false, "No OpenSSL" + end - if authversion ~= "post41" then - return false, "Unsupported authentication version: " .. authversion - end + if authversion ~= "post41" then + return false, "Unsupported authentication version: " .. authversion + end - local clicap = Capabilities.LongPassword - clicap = clicap + Capabilities.LongColumnFlag - clicap = clicap + Capabilities.SupportsLoadDataLocal - clicap = clicap + Capabilities.Speaks41ProtocolNew - clicap = clicap + Capabilities.InteractiveClient - clicap = clicap + Capabilities.SupportsTransactions - clicap = clicap + Capabilities.Support41Auth + local clicap = Capabilities.LongPassword + clicap = clicap + Capabilities.LongColumnFlag + clicap = clicap + Capabilities.SupportsLoadDataLocal + clicap = clicap + Capabilities.Speaks41ProtocolNew + clicap = clicap + Capabilities.InteractiveClient + clicap = clicap + Capabilities.SupportsTransactions + clicap = clicap + Capabilities.Support41Auth - local extcapabilities = ExtCapabilities.SupportsMultipleStatments - extcapabilities = extcapabilities + ExtCapabilities.SupportsMultipleResults + local extcapabilities = ExtCapabilities.SupportsMultipleStatments + extcapabilities = extcapabilities + ExtCapabilities.SupportsMultipleResults - local packet = bin.pack( "S", clicap ) - packet = packet .. bin.pack( "S", extcapabilities ) - packet = packet .. bin.pack( "I", MAXPACKET ) - packet = packet .. bin.pack( "C", Charset.latin1_COLLATE_latin1_swedish_ci ) - packet = packet .. bin.pack( "A", string.char(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ) - packet = packet .. bin.pack( "z", username ) + local packet = bin.pack( "S", clicap ) + packet = packet .. bin.pack( "S", extcapabilities ) + packet = packet .. bin.pack( "I", MAXPACKET ) + packet = packet .. bin.pack( "C", Charset.latin1_COLLATE_latin1_swedish_ci ) + packet = packet .. bin.pack( "A", string.char(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) ) + packet = packet .. bin.pack( "z", username ) - if ( password ~= nil and password:len() > 0 ) then - local hash = createLoginHash( password, salt ) - packet = packet .. bin.pack( "A", string.char( 0x14 ) .. hash ) - else - packet = packet .. bin.pack( "C", 0 ) - end + if ( password ~= nil and password:len() > 0 ) then + local hash = createLoginHash( password, salt ) + packet = packet .. bin.pack( "A", string.char( 0x14 ) .. hash ) + else + packet = packet .. bin.pack( "C", 0 ) + end - local tmp = packet:len() + bit.lshift( packetno, 24 ) + local tmp = packet:len() + bit.lshift( packetno, 24 ) - packet = bin.pack( "I", tmp ) .. packet + packet = bin.pack( "I", tmp ) .. packet - try( socket:send(packet) ) - packet = try( socket:receive_bytes(HEADER_SIZE) ) - local pos, response = decodeHeader( packet ) + try( socket:send(packet) ) + packet = try( socket:receive_bytes(HEADER_SIZE) ) + local pos, response = decodeHeader( packet ) - -- do we need to read the remainder - if ( #packet - HEADER_SIZE < response.len ) then - local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) ) - packet = packet .. tmp - end + -- do we need to read the remainder + if ( #packet - HEADER_SIZE < response.len ) then + local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) ) + packet = packet .. tmp + end - local is_error + local is_error - pos, is_error = bin.unpack( "C", packet, pos ) + pos, is_error = bin.unpack( "C", packet, pos ) - if is_error > 0 then - pos, response.errorcode = bin.unpack( "S", packet, pos ) + if is_error > 0 then + pos, response.errorcode = bin.unpack( "S", packet, pos ) - local has_sqlstate - pos, has_sqlstate = bin.unpack( "C", packet, pos ) + local has_sqlstate + pos, has_sqlstate = bin.unpack( "C", packet, pos ) - if has_sqlstate == 35 then - pos, response.sqlstate = bin.unpack( "A5", packet, pos ) - end + if has_sqlstate == 35 then + pos, response.sqlstate = bin.unpack( "A5", packet, pos ) + end - pos, response.errormessage = bin.unpack( "z", packet, pos ) + pos, response.errormessage = bin.unpack( "z", packet, pos ) - return false, response.errormessage - else - response.errorcode = 0 - pos, response.affectedrows = bin.unpack( "C", packet, pos ) - pos, response.serverstatus = bin.unpack( "S", packet, pos ) - pos, response.warnings = bin.unpack( "S", packet, pos ) - end + return false, response.errormessage + else + response.errorcode = 0 + pos, response.affectedrows = bin.unpack( "C", packet, pos ) + pos, response.serverstatus = bin.unpack( "S", packet, pos ) + pos, response.warnings = bin.unpack( "S", packet, pos ) + end - return true, response + return true, response end @@ -283,42 +283,42 @@ end -- the position should point to the data in this buffer (ie. after the header) -- @return pos number containing the position after the field was decoded -- @return field table containing catalog, database, table, --- origt_table, name, orig_name, --- length and type +-- origt_table, name, orig_name, +-- length and type function decodeField( data, pos ) - local header, len - local def, _ - local field = {} + local header, len + local def, _ + local field = {} - pos, len = bin.unpack( "C", data, pos ) - pos, field.catalog = bin.unpack( "A" .. len, data, pos ) + pos, len = bin.unpack( "C", data, pos ) + pos, field.catalog = bin.unpack( "A" .. len, data, pos ) - pos, len = bin.unpack( "C", data, pos ) - pos, field.database = bin.unpack( "A" .. len, data, pos ) + pos, len = bin.unpack( "C", data, pos ) + pos, field.database = bin.unpack( "A" .. len, data, pos ) - pos, len = bin.unpack( "C", data, pos ) - pos, field.table = bin.unpack( "A" .. len, data, pos ) + pos, len = bin.unpack( "C", data, pos ) + pos, field.table = bin.unpack( "A" .. len, data, pos ) - pos, len = bin.unpack( "C", data, pos ) - pos, field.orig_table = bin.unpack( "A" .. len, data, pos ) + pos, len = bin.unpack( "C", data, pos ) + pos, field.orig_table = bin.unpack( "A" .. len, data, pos ) - pos, len = bin.unpack( "C", data, pos ) - pos, field.name = bin.unpack( "A" .. len, data, pos ) + pos, len = bin.unpack( "C", data, pos ) + pos, field.name = bin.unpack( "A" .. len, data, pos ) - pos, len = bin.unpack( "C", data, pos ) - pos, field.orig_name = bin.unpack( "A" .. len, data, pos ) + pos, len = bin.unpack( "C", data, pos ) + pos, field.orig_name = bin.unpack( "A" .. len, data, pos ) - -- should be 0x0C - pos, _ = bin.unpack( "C", data, pos ) + -- should be 0x0C + pos, _ = bin.unpack( "C", data, pos ) - -- charset, in my case 0x0800 - pos, _ = bin.unpack( "S", data, pos ) + -- charset, in my case 0x0800 + pos, _ = bin.unpack( "S", data, pos ) - pos, field.length = bin.unpack( "I", data, pos ) - pos, field.type = bin.unpack( "A6", data, pos ) + pos, field.length = bin.unpack( "I", data, pos ) + pos, field.type = bin.unpack( "A6", data, pos ) - return pos, field + return pos, field end @@ -330,81 +330,81 @@ end -- @return table containing the following header, fields and data function decodeQueryResponse( socket ) - local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end - local try = nmap.new_try(catch) - local data, header, pos - local rs, blocks = {}, {} - local block_start, block_end - local EOF_MARKER = 254 + local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end + local try = nmap.new_try(catch) + local data, header, pos + local rs, blocks = {}, {} + local block_start, block_end + local EOF_MARKER = 254 - data = try( socket:receive_bytes(HEADER_SIZE) ) - pos, header = decodeHeader( data, pos ) + data = try( socket:receive_bytes(HEADER_SIZE) ) + pos, header = decodeHeader( data, pos ) - -- - -- First, Let's attempt to read the "Result Set Header Packet" - -- - if data:len() < header.len then - data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) ) - end + -- + -- First, Let's attempt to read the "Result Set Header Packet" + -- + if data:len() < header.len then + data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) ) + end - rs.header = data:sub( 1, HEADER_SIZE + header.len ) + rs.header = data:sub( 1, HEADER_SIZE + header.len ) - -- abort on MySQL error - if rs.header:sub(HEADER_SIZE + 1, HEADER_SIZE + 1) == string.char(0xFF) then - -- is this a 4.0 or 4.1 error message - if rs.header:find("#") then - return false, rs.header:sub(HEADER_SIZE+10) - else - return false, rs.header:sub(HEADER_SIZE+4) - end - end + -- abort on MySQL error + if rs.header:sub(HEADER_SIZE + 1, HEADER_SIZE + 1) == string.char(0xFF) then + -- is this a 4.0 or 4.1 error message + if rs.header:find("#") then + return false, rs.header:sub(HEADER_SIZE+10) + else + return false, rs.header:sub(HEADER_SIZE+4) + end + end - pos = HEADER_SIZE + header.len + 1 + pos = HEADER_SIZE + header.len + 1 - -- Second, Let's attempt to read the "Field Packets" and "Row Data Packets" - -- They're separated by an "EOF Packet" - for i=1,2 do + -- Second, Let's attempt to read the "Field Packets" and "Row Data Packets" + -- They're separated by an "EOF Packet" + for i=1,2 do - -- marks the start of our block - block_start = pos + -- marks the start of our block + block_start = pos - while true do + while true do - if data:len() - pos < HEADER_SIZE then - data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) ) - end + if data:len() - pos < HEADER_SIZE then + data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) ) + end - pos, header = decodeHeader( data, pos ) + pos, header = decodeHeader( data, pos ) - if data:len() - pos < header.len - 1 then - data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) ) - end + if data:len() - pos < header.len - 1 then + data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) ) + end - if header.len > 0 then - local _, b = bin.unpack("C", data, pos ) + if header.len > 0 then + local _, b = bin.unpack("C", data, pos ) - -- Is this the EOF packet? - if b == EOF_MARKER then - -- we don't want the EOF Packet included - block_end = pos - HEADER_SIZE - pos = pos + header.len - break - end - end + -- Is this the EOF packet? + if b == EOF_MARKER then + -- we don't want the EOF Packet included + block_end = pos - HEADER_SIZE + pos = pos + header.len + break + end + end - pos = pos + header.len + pos = pos + header.len - end + end - blocks[i] = data:sub( block_start, block_end ) + blocks[i] = data:sub( block_start, block_end ) - end + end - rs.fields = blocks[1] - rs.data = blocks[2] + rs.fields = blocks[1] + rs.data = blocks[2] - return true, rs + return true, rs end @@ -419,20 +419,20 @@ end -- or string containing error message if status is false function decodeFieldPackets( data, count ) - local pos, header - local field, fields = {}, {} + local pos, header + local field, fields = {}, {} - if count < 1 then - return false, "Field count was less than one, aborting" - end + if count < 1 then + return false, "Field count was less than one, aborting" + end - for i=1, count do - pos, header = decodeHeader( data, pos ) - pos, field = decodeField( data, pos ) - table.insert( fields, field ) - end + for i=1, count do + pos, header = decodeHeader( data, pos ) + pos, field = decodeField( data, pos ) + table.insert( fields, field ) + end - return true, fields + return true, fields end -- Decodes the result set header @@ -443,15 +443,15 @@ end -- @return number containing the amount of fields function decodeResultSetHeader( data ) - local _, fields + local _, fields - if data:len() ~= HEADER_SIZE + 1 then - return false, "Result set header was incorrect" - end + if data:len() ~= HEADER_SIZE + 1 then + return false, "Result set header was incorrect" + end - _, fields = bin.unpack( "C", data, HEADER_SIZE + 1 ) + _, fields = bin.unpack( "C", data, HEADER_SIZE + 1 ) - return true, fields + return true, fields end --- Decodes the row data @@ -464,23 +464,23 @@ end -- @return rows table containing row tables function decodeDataPackets( data, count ) - local len, pos = 0, 1, 1 - local header, row, rows = {}, {}, {} + local len, pos = 0, 1, 1 + local header, row, rows = {}, {}, {} - while pos < data:len() do - row = {} - pos, header = decodeHeader( data, pos ) + while pos < data:len() do + row = {} + pos, header = decodeHeader( data, pos ) - for i=1, count do - pos, len = bin.unpack("C", data, pos ) - pos, row[i] = bin.unpack("A" .. len, data, pos) - end + for i=1, count do + pos, len = bin.unpack("C", data, pos ) + pos, row[i] = bin.unpack("A" .. len, data, pos) + end - table.insert( rows, row ) + table.insert( rows, row ) - end + end - return true, rows + return true, rows end @@ -492,54 +492,54 @@ end -- @return rows table containing row tabels as decoded by decodeDataPackets function sqlQuery( socket, query ) - local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end - local try = nmap.new_try(catch) - local packetno = 0 - local querylen = query:len() + 1 - local packet, packet_len, pos, header - local status, fields, field_count, rows, rs + local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end + local try = nmap.new_try(catch) + local packetno = 0 + local querylen = query:len() + 1 + local packet, packet_len, pos, header + local status, fields, field_count, rows, rs - packet = bin.pack("ICA", querylen, Command.Query, query ) + packet = bin.pack("ICA", querylen, Command.Query, query ) - -- - -- http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Result_Set_Header_Packet - -- - -- (Result Set Header Packet) the number of columns - -- (Field Packets) column descriptors - -- (EOF Packet) marker: end of Field Packets - -- (Row Data Packets) row contents - -- (EOF Packet) marker: end of Data Packets + -- + -- http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Result_Set_Header_Packet + -- + -- (Result Set Header Packet) the number of columns + -- (Field Packets) column descriptors + -- (EOF Packet) marker: end of Field Packets + -- (Row Data Packets) row contents + -- (EOF Packet) marker: end of Data Packets - try( socket:send(packet) ) + try( socket:send(packet) ) - -- - -- Let's read all the data into a table - -- This way we avoid the hustle with reading from the socket - status, rs = decodeQueryResponse( socket ) + -- + -- Let's read all the data into a table + -- This way we avoid the hustle with reading from the socket + status, rs = decodeQueryResponse( socket ) - if not status then - return false, rs - end + if not status then + return false, rs + end - status, field_count = decodeResultSetHeader(rs.header) + status, field_count = decodeResultSetHeader(rs.header) - if not status then - return false, field_count - end + if not status then + return false, field_count + end - status, fields = decodeFieldPackets(rs.fields, field_count) + status, fields = decodeFieldPackets(rs.fields, field_count) - if not status then - return false, fields - end + if not status then + return false, fields + end - status, rows = decodeDataPackets(rs.data, field_count) + status, rows = decodeDataPackets(rs.data, field_count) - if not status then - return false, rows - end + if not status then + return false, rows + end - return true, { cols = fields, rows = rows } + return true, { cols = fields, rows = rows } end --- @@ -550,24 +550,24 @@ end -- - noheaders - does not include column names in result -- @return string containing the formated resultset table function formatResultset(rs, options) - options = options or {} - if ( not(rs) or not(rs.cols) or not(rs.rows) ) then - return - end + options = options or {} + if ( not(rs) or not(rs.cols) or not(rs.rows) ) then + return + end - local restab = tab.new(#rs.cols) - local colnames = {} + local restab = tab.new(#rs.cols) + local colnames = {} - if ( not(options.noheaders) ) then - for _, col in ipairs(rs.cols) do table.insert(colnames, col.name) end - tab.addrow(restab, table.unpack(colnames)) - end + if ( not(options.noheaders) ) then + for _, col in ipairs(rs.cols) do table.insert(colnames, col.name) end + tab.addrow(restab, table.unpack(colnames)) + end - for _, row in ipairs(rs.rows) do - tab.addrow(restab, table.unpack(row)) - end + for _, row in ipairs(rs.rows) do + tab.addrow(restab, table.unpack(row)) + end - return tab.dump(restab) + return tab.dump(restab) end return _ENV; diff --git a/nselib/ncp.lua b/nselib/ncp.lua index 06b938d17..2133b9aec 100644 --- a/nselib/ncp.lua +++ b/nselib/ncp.lua @@ -12,38 +12,38 @@ -- The following classes exist: -- -- * Packet --- - Implements functions for creating and serializing a NCP packet +-- - Implements functions for creating and serializing a NCP packet -- -- * ResponseParser --- - A static class containing a bunch of functions to decode server --- responses +-- - A static class containing a bunch of functions to decode server +-- responses -- -- * Response --- - Class responsible for decoding NCP responses +-- - Class responsible for decoding NCP responses -- -- * NCP --- - Contains the "native" NCP functions sending the actual request to the --- server. +-- - Contains the "native" NCP functions sending the actual request to the +-- server. -- -- * Socket --- - A buffered socket implementation +-- - A buffered socket implementation -- -- * Helper --- - The prefered script interface to the library containing functions --- that wrap functions from the NCP class using more descriptive names --- and easier interface. +-- - The prefered script interface to the library containing functions +-- that wrap functions from the NCP class using more descriptive names +-- and easier interface. -- -- * Util --- - A class containing mostly decoding and helper functions +-- - A class containing mostly decoding and helper functions -- -- The following example code illustrates how to use the Helper class from a -- script. The example queries the server for all User objects from the root. -- -- --- local helper = ncp.Helper:new(host,port) --- local status, resp = helper:connect() --- status, resp = helper:search("[Root]", "User", "*") --- status = helper:close() +-- local helper = ncp.Helper:new(host,port) +-- local status, resp = helper:connect() +-- status, resp = helper:search("[Root]", "User", "*") +-- status = helper:close() -- -- @@ -64,499 +64,499 @@ _ENV = stdnse.module("ncp", stdnse.seeall) NCPType = { - CreateConnection = 0x1111, - ServiceRequest = 0x2222, - ServiceReply = 0x3333, - DestroyConnection = 0x5555, + CreateConnection = 0x1111, + ServiceRequest = 0x2222, + ServiceReply = 0x3333, + DestroyConnection = 0x5555, } Status = { - CONNECTION_OK = 0, - COMPLETION_OK = 0, + CONNECTION_OK = 0, + COMPLETION_OK = 0, } NCPFunction = { - GetMountVolumeList = 0x16, - GetFileServerInfo = 0x17, - Ping = 0x68, - EnumerateNetworkAddress = 0x7b, - SendFragmentedRequest = 0x68, + GetMountVolumeList = 0x16, + GetFileServerInfo = 0x17, + Ping = 0x68, + EnumerateNetworkAddress = 0x7b, + SendFragmentedRequest = 0x68, } NCPVerb = { - Resolve = 1, - List = 5, - Search = 6, + Resolve = 1, + List = 5, + Search = 6, } -- The NCP Packet Packet = { - --- Creates a new instance of Packet - -- @return o instance of Packet - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - o.ncp_ip = { signature = "DmdT", replybuf = 0, version = 1 } - o.task = 1 - o.func = 0 - return o - end, + --- Creates a new instance of Packet + -- @return o instance of Packet + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + o.ncp_ip = { signature = "DmdT", replybuf = 0, version = 1 } + o.task = 1 + o.func = 0 + return o + end, - --- Sets the NCP Reply buffer size - -- @param n number containing the buffer size - setNCPReplyBuf = function(self, n) self.ncp_ip.replybuf = n end, + --- Sets the NCP Reply buffer size + -- @param n number containing the buffer size + setNCPReplyBuf = function(self, n) self.ncp_ip.replybuf = n end, - --- Sets the NCP packet length - -- @param n number containing the length - setNCPLength = function(self, n) self.ncp_ip.length = n end, + --- Sets the NCP packet length + -- @param n number containing the length + setNCPLength = function(self, n) self.ncp_ip.length = n end, - --- Gets the NCP packet length - -- @return n number containing the NCP length - getNCPLength = function(self) return self.ncp_ip.length end, + --- Gets the NCP packet length + -- @return n number containing the NCP length + getNCPLength = function(self) return self.ncp_ip.length end, - --- Sets the NCP packet type - -- @param t number containing the NCP packet type - setType = function(self, t) self.type = t end, + --- Sets the NCP packet type + -- @param t number containing the NCP packet type + setType = function(self, t) self.type = t end, - --- Gets the NCP packet type - -- @return type number containing the NCP packet type - getType = function(self) return self.type end, + --- Gets the NCP packet type + -- @return type number containing the NCP packet type + getType = function(self) return self.type end, - --- Sets the NCP packet function - -- @param t number containing the NCP function - setFunc = function(self, f) self.func = f end, + --- Sets the NCP packet function + -- @param t number containing the NCP function + setFunc = function(self, f) self.func = f end, - --- Gets the NCP packet function - -- @return func number containing the NCP packet function - getFunc = function(self) return self.func end, + --- Gets the NCP packet function + -- @return func number containing the NCP packet function + getFunc = function(self) return self.func end, - --- Sets the NCP sequence number - -- @param seqno number containing the sequence number - setSeqNo = function(self, n) self.seqno = n end, + --- Sets the NCP sequence number + -- @param seqno number containing the sequence number + setSeqNo = function(self, n) self.seqno = n end, - --- Sets the NCP connection number - -- @param conn number containing the connection number - setConnNo = function(self, n) self.conn = n end, + --- Sets the NCP connection number + -- @param conn number containing the connection number + setConnNo = function(self, n) self.conn = n end, - --- Gets the NCP connection number - -- @return conn number containing the connection number - getConnNo = function(self) return self.conn end, + --- Gets the NCP connection number + -- @return conn number containing the connection number + getConnNo = function(self) return self.conn end, - --- Sets the NCP sub function - -- @param subfunc number containing the subfunction - setSubFunc = function(self, n) self.subfunc = n end, + --- Sets the NCP sub function + -- @param subfunc number containing the subfunction + setSubFunc = function(self, n) self.subfunc = n end, - --- Gets the NCP sub function - -- @return subfunc number containing the subfunction - getSubFunc = function(self) return self.subfunc end, + --- Gets the NCP sub function + -- @return subfunc number containing the subfunction + getSubFunc = function(self) return self.subfunc end, - --- Gets the Sequence number - -- @return seqno number containing the sequence number - getSeqNo = function(self) return self.seqno end, + --- Gets the Sequence number + -- @return seqno number containing the sequence number + getSeqNo = function(self) return self.seqno end, - --- Sets the packet length - -- @param len number containing the packet length - setLength = function(self, n) self.length = n end, + --- Sets the packet length + -- @param len number containing the packet length + setLength = function(self, n) self.length = n end, - --- Sets the packet data - -- @param data string containing the packet data - setData = function(self, data) self.data = data end, + --- Sets the packet data + -- @param data string containing the packet data + setData = function(self, data) self.data = data end, - --- Gets the packet data - -- @return data string containing the packet data - getData = function(self) return self.data end, + --- Gets the packet data + -- @return data string containing the packet data + getData = function(self) return self.data end, - --- Sets the packet task - -- @param task number containing the packet number - setTask = function(self, task) self.task = task end, + --- Sets the packet task + -- @param task number containing the packet number + setTask = function(self, task) self.task = task end, - --- "Serializes" the packet to a string - __tostring = function(self) - local UNKNOWN = 0 - local data = bin.pack(">AIIISCCCCC", self.ncp_ip.signature, - self.ncp_ip.length or 0, self.ncp_ip.version, - self.ncp_ip.replybuf, self.type, self.seqno, - self.conn, self.task, UNKNOWN, self.func ) + --- "Serializes" the packet to a string + __tostring = function(self) + local UNKNOWN = 0 + local data = bin.pack(">AIIISCCCCC", self.ncp_ip.signature, + self.ncp_ip.length or 0, self.ncp_ip.version, + self.ncp_ip.replybuf, self.type, self.seqno, + self.conn, self.task, UNKNOWN, self.func ) - if ( self.length ) then data = data .. bin.pack(">S", self.length) end - if ( self.subfunc ) then data = data .. bin.pack("C", self.subfunc) end - if ( self.data ) then data = data .. bin.pack("A", self.data) end + if ( self.length ) then data = data .. bin.pack(">S", self.length) end + if ( self.subfunc ) then data = data .. bin.pack("C", self.subfunc) end + if ( self.data ) then data = data .. bin.pack("A", self.data) end - return data - end, + return data + end, } -- Parses different responses into suitable tables ResponseParser = { - --- Determines what parser to call based on the contents of the client - -- request and server response. - -- @param req instance of Packet containing the request to the server - -- @param resp instance of Response containing the server response - -- @return status true on success, false on failure - -- @return resp table (on success) containing the decoded response - -- @return err string (on failure) containing the error message - parse = function(req, resp) - local func, subfunc, typ = req:getFunc(), req:getSubFunc(), req:getType() + --- Determines what parser to call based on the contents of the client + -- request and server response. + -- @param req instance of Packet containing the request to the server + -- @param resp instance of Response containing the server response + -- @return status true on success, false on failure + -- @return resp table (on success) containing the decoded response + -- @return err string (on failure) containing the error message + parse = function(req, resp) + local func, subfunc, typ = req:getFunc(), req:getSubFunc(), req:getType() - if ( ResponseParser[func] ) then - return ResponseParser[func](resp) - elseif ( NCPFunction.SendFragmentedRequest == func ) then - if ( 1 == subfunc ) then - return ResponseParser.Ping(resp) - elseif ( 2 == subfunc ) then - local data = req:getData() - if ( #data < 21 ) then - return false, "Invalid NCP request, could not parse" - end - local pos, verb = bin.unpack("srvname - -- os_major - -- os_minor - -- conns_supported - -- conns_inuse - -- vols_supported - -- os_rev - -- sft_support - -- tts_level - -- conns_max_use - -- acct_version - -- vap_version - -- qms_version - -- print_version - -- internet_bridge_ver - -- mixed_mode_path - -- local_login_info - -- product_major - -- product_minor - -- product_rev - -- os_lang_id - -- support_64_bit - -- @return error message (if status is false) - [NCPFunction.GetFileServerInfo] = function(resp) - local data = resp:getData() - local len = #data + --- Decodes a GetFileServerInfo response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- srvname + -- os_major + -- os_minor + -- conns_supported + -- conns_inuse + -- vols_supported + -- os_rev + -- sft_support + -- tts_level + -- conns_max_use + -- acct_version + -- vap_version + -- qms_version + -- print_version + -- internet_bridge_ver + -- mixed_mode_path + -- local_login_info + -- product_major + -- product_minor + -- product_rev + -- os_lang_id + -- support_64_bit + -- @return error message (if status is false) + [NCPFunction.GetFileServerInfo] = function(resp) + local data = resp:getData() + local len = #data - if ( len < 78 ) then - return false, "Failed to decode GetFileServerInfo" - end + if ( len < 78 ) then + return false, "Failed to decode GetFileServerInfo" + end - local result = {} - local pos + local result = {} + local pos - pos, result.srvname, result.os_major, result.os_minor, - result.conns_supported, result.conns_inuse, - result.vols_supported, result.os_rev, result.sft_support, - result.tts_level, result.conns_max_use, result.acct_version, - result.vap_version, result.qms_version, result.print_version, - result.virt_console_ver, result.sec_restrict_ver, - result.internet_bridge_ver, result.mixed_mode_path, - result.local_login_info, result.product_major, - result.product_minor, result.product_rev, result.os_lang_id, - result.support_64_bit = bin.unpack(">A48CCSSSCCCSCCCCCCCCCSSSCC", data) + pos, result.srvname, result.os_major, result.os_minor, + result.conns_supported, result.conns_inuse, + result.vols_supported, result.os_rev, result.sft_support, + result.tts_level, result.conns_max_use, result.acct_version, + result.vap_version, result.qms_version, result.print_version, + result.virt_console_ver, result.sec_restrict_ver, + result.internet_bridge_ver, result.mixed_mode_path, + result.local_login_info, result.product_major, + result.product_minor, result.product_rev, result.os_lang_id, + result.support_64_bit = bin.unpack(">A48CCSSSCCCSCCCCCCCCCSSSCC", data) - return true, result - end, + return true, result + end, - --- Decodes a GetMountVolumeList response - -- @param resp string containing the response as received from the server - -- @return status true on success, false on failure - -- @return response table of vol entries (if status is true) - -- Each vol entry is a table containing the following fields: - -- vol_no and vol_name - -- @return error message (if status is false) - [NCPFunction.GetMountVolumeList] = function(resp) - local data = resp:getData() - local len = #data + --- Decodes a GetMountVolumeList response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return response table of vol entries (if status is true) + -- Each vol entry is a table containing the following fields: + -- vol_no and vol_name + -- @return error message (if status is false) + [NCPFunction.GetMountVolumeList] = function(resp) + local data = resp:getData() + local len = #data - local pos, items, next_vol_no = bin.unpack("tree_name - -- @return error message (if status is false) - Ping = function(resp) - local data = resp:getData() - local len = #data - local pos - local result = {} + --- Decodes a Ping response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- tree_name + -- @return error message (if status is false) + Ping = function(resp) + local data = resp:getData() + local len = #data + local pos + local result = {} - if ( len < 40 ) then return false, "NCP Ping result too short" end + if ( len < 40 ) then return false, "NCP Ping result too short" end - pos, result.nds_version = bin.unpack("C", data) - -- move to the offset of the - pos = pos + 7 - pos, result.tree_name = bin.unpack("A32", data, pos) + pos, result.nds_version = bin.unpack("C", data) + -- move to the offset of the + pos = pos + 7 + pos, result.tree_name = bin.unpack("A32", data, pos) - result.tree_name = (result.tree_name:match("^([^_]*)_*$")) + result.tree_name = (result.tree_name:match("^([^_]*)_*$")) - return true, result - end, + return true, result + end, - --- Decodes a EnumerateNetworkAddress response - -- @param resp string containing the response as received from the server - -- @return status true on success, false on failure - -- @return response table (if status is true) containing: - -- ip, port and proto - -- @return error message (if status is false) - [NCPFunction.EnumerateNetworkAddress] = function(resp) - local pos, result = 1, {} - local items - local data = resp:getData() - local len = #data + --- Decodes a EnumerateNetworkAddress response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- ip, port and proto + -- @return error message (if status is false) + [NCPFunction.EnumerateNetworkAddress] = function(resp) + local pos, result = 1, {} + local items + local data = resp:getData() + local len = #data - pos, result.time_since_boot, result.console_version, result.console_revision, - result.srvinfo_flags, result.guid, result.next_search, - items = bin.unpack("CCISSCCISS len ) then - return false, "EnumerateNetworkAddress packet too short" - end + if ( ( pos - 1 ) + (items * 14 ) > len ) then + return false, "EnumerateNetworkAddress packet too short" + end - result.addr = {} - for i=1, items do - local addr = {} - pos, addr = DecodeAddress(data, pos) - table.insert(result.addr, addr ) - end - return true, result - end, + result.addr = {} + for i=1, items do + local addr = {} + pos, addr = DecodeAddress(data, pos) + table.insert(result.addr, addr ) + end + return true, result + end, - --- Decodes a Resolve response - -- @param resp string containing the response as received from the server - -- @return status true on success, false on failure - -- @return response table (if status is true) containing: - -- tag and id - -- @return error message (if status is false) - Resolve = function(resp) - local data = resp:getData() - local len = #data + --- Decodes a Resolve response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- tag and id + -- @return error message (if status is false) + Resolve = function(resp) + local data = resp:getData() + local len = #data - if ( len < 12 ) then - return false, "ResponseParser: NCP Resolve, packet too short" - end + if ( len < 12 ) then + return false, "ResponseParser: NCP Resolve, packet too short" + end - local pos, frag_size, frag_handle, comp_code = bin.unpack("EntryDecoder - -- @return error message (if status is false) - Search = function(resp) - local data = resp:getData() - local len = #data - local entries = {} + --- Decodes a Search response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return entries table (if status is true) as return by: + -- EntryDecoder + -- @return error message (if status is false) + Search = function(resp) + local data = resp:getData() + local len = #data + local entries = {} - if ( len < 12 ) then - return false, "ResponseParser: NCP Resolve, packet too short" - end + if ( len < 12 ) then + return false, "ResponseParser: NCP Resolve, packet too short" + end - local pos, frag_size, frag_handle, comp_code, iter_handle = bin.unpack("flags - -- mod_time - -- sub_count - -- baseclass - -- rdn - -- name - EntryDecoder = function(data, pos) + --- The EntryDecoder is used by the Search and List function, for decoding + -- the returned entries. + -- @param data containing the response as returned by the server + -- @param pos number containing the offset into data to start decoding + -- @return pos number containing the new offset after decoding + -- @return entry table containing the decoded entry, currently it contains + -- one or more of the following fields: + -- flags + -- mod_time + -- sub_count + -- baseclass + -- rdn + -- name + EntryDecoder = function(data, pos) - -- The InfoFlags class takes a numeric value and facilitates - -- bit decoding into InfoFlag fields, the current supported fields - -- are: - -- Output - -- Entry - -- Count - -- ModTime - -- BaseClass - -- RelDN - -- DN - local InfoFlags = { - -- Creates a new instance - -- @param val number containing the numeric representation of flags - -- @return a new instance of InfoFlags - new = function(self, val) - local o = {} - setmetatable(o, self) - self.__index = self - o.val = val - o:parse() - return o - end, + -- The InfoFlags class takes a numeric value and facilitates + -- bit decoding into InfoFlag fields, the current supported fields + -- are: + -- Output + -- Entry + -- Count + -- ModTime + -- BaseClass + -- RelDN + -- DN + local InfoFlags = { + -- Creates a new instance + -- @param val number containing the numeric representation of flags + -- @return a new instance of InfoFlags + new = function(self, val) + local o = {} + setmetatable(o, self) + self.__index = self + o.val = val + o:parse() + return o + end, - -- Parses the numeric value and creates a number of class fields - parse = function(self) - local fields = { "Output", "_u1", "Entry", "Count", "ModTime", - "_u2", "_u3", "_u4", "_u5", "_u6", "_u7", "BaseClass", - "RelDN", "DN" } - local bits = 1 - for _, field in ipairs(fields) do - self[field] = ( bit.band(self.val, bits) == bits ) - bits = bits * 2 - end - end - } + -- Parses the numeric value and creates a number of class fields + parse = function(self) + local fields = { "Output", "_u1", "Entry", "Count", "ModTime", + "_u2", "_u3", "_u4", "_u5", "_u6", "_u7", "BaseClass", + "RelDN", "DN" } + local bits = 1 + for _, field in ipairs(fields) do + self[field] = ( bit.band(self.val, bits) == bits ) + bits = bits * 2 + end + end + } - local entry = {} - local f, len - pos, f = bin.unpack("EntryDecoder - -- @return error message (if status is false) - List = function(resp) - local data = resp:getData() - local len = #data + --- Decodes a List response + -- @param resp string containing the response as received from the server + -- @return status true on success, false on failure + -- @return entries table (if status is true) as return by: + -- EntryDecoder + -- @return error message (if status is false) + List = function(resp) + local data = resp:getData() + local len = #data - if ( len < 12 ) then - return false, "ResponseParser: NCP Resolve, packet too short" - end + if ( len < 12 ) then + return false, "ResponseParser: NCP Resolve, packet too short" + end - local pos, frag_size, frag_handle, comp_code, iter_handle = bin.unpack("IISCCSCC", self.header) + pos, self.signature, self.length, self.type, + self.seqno, self.conn, _, self.compl_code, + self.status_code = bin.unpack(">IISCCSCC", self.header) - if ( self.data ) then - local len = #self.data - pos - if ( ( #self.data - pos ) ~= ( self.length - 33 ) ) then - stdnse.print_debug("NCP packet length mismatched") - return - end - end - end, + if ( self.data ) then + local len = #self.data - pos + if ( ( #self.data - pos ) ~= ( self.length - 33 ) ) then + stdnse.print_debug("NCP packet length mismatched") + return + end + end + end, - --- Gets the sequence number - -- @return seqno number - getSeqNo = function(self) return self.seqno end, + --- Gets the sequence number + -- @return seqno number + getSeqNo = function(self) return self.seqno end, - --- Gets the connection number - -- @return conn number - getConnNo = function(self) return self.conn end, + --- Gets the connection number + -- @return conn number + getConnNo = function(self) return self.conn end, - --- Gets the data portion of the response - -- @return data string - getData = function(self) return self.data end, + --- Gets the data portion of the response + -- @return data string + getData = function(self) return self.data end, - --- Gets the header portion of the response - getHeader = function(self) return self.header end, + --- Gets the header portion of the response + getHeader = function(self) return self.header end, - --- Returns true if there are any errors - -- @return error true if the response error code is anything else than OK - hasErrors = function(self) - return not( ( self.compl_code == Status.COMPLETION_OK ) and - ( self.status_code == Status.CONNECTION_OK ) ) + --- Returns true if there are any errors + -- @return error true if the response error code is anything else than OK + hasErrors = function(self) + return not( ( self.compl_code == Status.COMPLETION_OK ) and + ( self.status_code == Status.CONNECTION_OK ) ) - end, + end, - --- Creates a Response instance from the data read of the socket - -- @param socket socket connected to server and ready to receive data - -- @return Response containing a new Response instance - fromSocket = function(socket) - local status, header = socket:recv(16) - if ( not(status) ) then return false, "Failed to receive data" end + --- Creates a Response instance from the data read of the socket + -- @param socket socket connected to server and ready to receive data + -- @return Response containing a new Response instance + fromSocket = function(socket) + local status, header = socket:recv(16) + if ( not(status) ) then return false, "Failed to receive data" end - local pos, sig, len = bin.unpack(">II", header) - if ( len < 8 ) then return false, "NCP packet too short" end + local pos, sig, len = bin.unpack(">II", header) + if ( len < 8 ) then return false, "NCP packet too short" end - local data + local data - if ( 0 < len - 16 ) then - status, data = socket:recv(len - 16) - if ( not(status) ) then return false, "Failed to receive data" end - end - return true, Response:new(header, data) - end, + if ( 0 < len - 16 ) then + status, data = socket:recv(len - 16) + if ( not(status) ) then return false, "Failed to receive data" end + end + return true, Response:new(header, data) + end, - --- "Serializes" the Response instance to a string - __tostring = function(self) - return bin.pack("AA", self.header, self.data) - end, + --- "Serializes" the Response instance to a string + __tostring = function(self) + return bin.pack("AA", self.header, self.data) + end, } -- The NCP class NCP = { - --- Creates a new NCP instance - -- @param socket containing a socket connected to the NCP server - -- @return o instance of NCP - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.socket = socket - o.seqno = -1 - o.conn = 0 - return o - end, + --- Creates a new NCP instance + -- @param socket containing a socket connected to the NCP server + -- @return o instance of NCP + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.socket = socket + o.seqno = -1 + o.conn = 0 + return o + end, - --- Handles sending and receiving a NCP message - -- @param p Packet containing the request to send to the server - -- @return status true on success false on failure - -- @return response table (if status is true) containing the parsed - -- response - -- @return error string (if status is false) containing the error - Exch = function(self, p) - local status, err = self:SendPacket(p) - if ( not(status) ) then return status, err end + --- Handles sending and receiving a NCP message + -- @param p Packet containing the request to send to the server + -- @return status true on success false on failure + -- @return response table (if status is true) containing the parsed + -- response + -- @return error string (if status is false) containing the error + Exch = function(self, p) + local status, err = self:SendPacket(p) + if ( not(status) ) then return status, err end - local status, resp = Response.fromSocket(self.socket) - if ( not(status) or resp:hasErrors() ) then return false, resp end + local status, resp = Response.fromSocket(self.socket) + if ( not(status) or resp:hasErrors() ) then return false, resp end - self.seqno = resp:getSeqNo() - self.conn = resp:getConnNo() + self.seqno = resp:getSeqNo() + self.conn = resp:getConnNo() - return ResponseParser.parse(p, resp) - end, + return ResponseParser.parse(p, resp) + end, - --- Sends a packet to the server - -- @param p Packet to be sent to the server - -- @return status true on success, false on failure - -- @return err string containing the error message on failure - SendPacket = function(self, p) - if ( not(p:getSeqNo() ) ) then p:setSeqNo(self.seqno + 1) end - if ( not(p:getConnNo() ) ) then p:setConnNo(self.conn) end + --- Sends a packet to the server + -- @param p Packet to be sent to the server + -- @return status true on success, false on failure + -- @return err string containing the error message on failure + SendPacket = function(self, p) + if ( not(p:getSeqNo() ) ) then p:setSeqNo(self.seqno + 1) end + if ( not(p:getConnNo() ) ) then p:setConnNo(self.conn) end - if ( not(p:getNCPLength()) ) then - local len = #(tostring(p)) - p:setNCPLength(len) - end + if ( not(p:getNCPLength()) ) then + local len = #(tostring(p)) + p:setNCPLength(len) + end - local status, err = self.socket:send(tostring(p)) - if ( not(status) ) then return status, "Failed to send data" end + local status, err = self.socket:send(tostring(p)) + if ( not(status) ) then return status, "Failed to send data" end - return true - end, + return true + end, - --- Creates a connection to the NCP server - -- @return status true on success, false on failure - CreateConnect = function(self) - local p = Packet:new() - p:setType(NCPType.CreateConnection) + --- Creates a connection to the NCP server + -- @return status true on success, false on failure + CreateConnect = function(self) + local p = Packet:new() + p:setType(NCPType.CreateConnection) - local resp = self:Exch( p ) - return true - end, + local resp = self:Exch( p ) + return true + end, - --- Destroys a connection established with the NCP server - -- @return status true on success, false on failure - DestroyConnect = function(self) - local p = Packet:new() - p:setType(NCPType.DestroyConnection) + --- Destroys a connection established with the NCP server + -- @return status true on success, false on failure + DestroyConnect = function(self) + local p = Packet:new() + p:setType(NCPType.DestroyConnection) - local resp = self:Exch( p ) - return true - end, + local resp = self:Exch( p ) + return true + end, - --- Gets file server information - -- @return status true on success, false on failure - -- @return response table (if status is true) containing: - -- srvname - -- os_major - -- os_minor - -- conns_supported - -- conns_inuse - -- vols_supported - -- os_rev - -- sft_support - -- tts_level - -- conns_max_use - -- acct_version - -- vap_version - -- qms_version - -- print_version - -- internet_bridge_ver - -- mixed_mode_path - -- local_login_info - -- product_major - -- product_minor - -- product_rev - -- os_lang_id - -- support_64_bit - -- @return error message (if status is false) - GetFileServerInfo = function(self) - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.GetFileServerInfo) - p:setNCPReplyBuf(128) - p:setLength(1) - p:setSubFunc(17) - return self:Exch( p ) - end, + --- Gets file server information + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- srvname + -- os_major + -- os_minor + -- conns_supported + -- conns_inuse + -- vols_supported + -- os_rev + -- sft_support + -- tts_level + -- conns_max_use + -- acct_version + -- vap_version + -- qms_version + -- print_version + -- internet_bridge_ver + -- mixed_mode_path + -- local_login_info + -- product_major + -- product_minor + -- product_rev + -- os_lang_id + -- support_64_bit + -- @return error message (if status is false) + GetFileServerInfo = function(self) + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.GetFileServerInfo) + p:setNCPReplyBuf(128) + p:setLength(1) + p:setSubFunc(17) + return self:Exch( p ) + end, - -- NEEDS authentication, disabled for now - -- - -- Get the logged on user for the specified connection - -- @param conn_no number containing the connection number - -- GetStationLoggedInfo = function(self, conn_no) - -- local p = Packet:new() - -- p:setType(NCPType.ServiceRequest) - -- p:setFunc(NCPFunction.GetFileServerInfo) - -- p:setNCPReplyBuf(62) - -- p:setLength(5) - -- p:setSubFunc(28) - -- p:setTask(4) - -- - -- local data = bin.pack("tree_name - -- @return error message (if status is false) - Ping = function(self) - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.Ping) - p:setSubFunc(1) - p:setNCPReplyBuf(45) - p:setData("\0\0\0") + --- Sends a PING to the server which responds with the tree name + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- tree_name + -- @return error message (if status is false) + Ping = function(self) + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.Ping) + p:setSubFunc(1) + p:setNCPReplyBuf(45) + p:setData("\0\0\0") - return self:Exch( p ) - end, + return self:Exch( p ) + end, - --- Enumerates the IP addresses associated with the server - -- @return status true on success, false on failure - -- @return response table (if status is true) containing: - -- ip, port and proto - -- @return error message (if status is false) - EnumerateNetworkAddress = function(self) - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.EnumerateNetworkAddress) - p:setSubFunc(17) - p:setNCPReplyBuf(4096) - p:setData("\0\0\0\0") - p:setLength(5) - return self:Exch( p ) - end, + --- Enumerates the IP addresses associated with the server + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- ip, port and proto + -- @return error message (if status is false) + EnumerateNetworkAddress = function(self) + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.EnumerateNetworkAddress) + p:setSubFunc(17) + p:setNCPReplyBuf(4096) + p:setData("\0\0\0\0") + p:setLength(5) + return self:Exch( p ) + end, - --- Resolves an directory entry id from a name - -- @param name string containing the name to resolve - -- @return status true on success, false on failure - -- @return response table (if status is true) containing: - -- tag and id - -- @return error message (if status is false) - ResolveName = function(self, name) - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.SendFragmentedRequest) - p:setSubFunc(2) - p:setNCPReplyBuf(4108) + --- Resolves an directory entry id from a name + -- @param name string containing the name to resolve + -- @return status true on success, false on failure + -- @return response table (if status is true) containing: + -- tag and id + -- @return error message (if status is false) + ResolveName = function(self, name) + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.SendFragmentedRequest) + p:setSubFunc(2) + p:setNCPReplyBuf(4108) - local pad = (4 - ( #name % 4 ) ) - name = Util.ZeroPad(name, #name + pad) + local pad = (4 - ( #name % 4 ) ) + name = Util.ZeroPad(name, #name + pad) - local w_name = Util.ToWideChar(name) - local frag_handle, frag_size = 0xffffffff, 64176 - local msg_size, unknown, proto_flags, nds_verb = 44 + #w_name, 0, 0, 1 - local nds_reply_buf, version, flags, scope = 4096, 1, 0x2062, 0 - local unknown2 = 0x0e - local ZERO = 0 + local w_name = Util.ToWideChar(name) + local frag_handle, frag_size = 0xffffffff, 64176 + local msg_size, unknown, proto_flags, nds_verb = 44 + #w_name, 0, 0, 1 + local nds_reply_buf, version, flags, scope = 4096, 1, 0x2062, 0 + local unknown2 = 0x0e + local ZERO = 0 - local data = bin.pack("vol_no and vol_name - -- @return error message (if status is false) - GetMountVolumeList = function(self) - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.GetMountVolumeList) - p:setSubFunc(52) - p:setNCPReplyBuf(538) - p:setTask(4) - p:setLength(12) + --- Gets a list of volumes from the server + -- @return status true on success, false on failure + -- @return response table of vol entries (if status is true) + -- Each vol entry is a table containing the following fields: + -- vol_no and vol_name + -- @return error message (if status is false) + GetMountVolumeList = function(self) + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.GetMountVolumeList) + p:setSubFunc(52) + p:setNCPReplyBuf(538) + p:setTask(4) + p:setLength(12) - local start_vol = 0 - local vol_req_flags = 1 - local src_name_space = 0 + local start_vol = 0 + local vol_req_flags = 1 + local src_name_space = 0 - local data = bin.pack("Resolve - -- @param class string containing a class name (or * wildcard) - -- @param name string containing a entry name (or * wildcard) - -- @param options table containing one or more of the following - -- numobjs - -- @return status true on success false on failure - -- @return entries table (if status is true) as return by: - -- ResponseDecoder.EntryDecoder - -- @return error string (if status is false) containing the error - Search = function(self, base, class, name, options) - assert( ( base and base.id ), "No base entry was specified") + --- Searches the directory + -- @param base entry as resolved by Resolve + -- @param class string containing a class name (or * wildcard) + -- @param name string containing a entry name (or * wildcard) + -- @param options table containing one or more of the following + -- numobjs + -- @return status true on success false on failure + -- @return entries table (if status is true) as return by: + -- ResponseDecoder.EntryDecoder + -- @return error string (if status is false) containing the error + Search = function(self, base, class, name, options) + assert( ( base and base.id ), "No base entry was specified") - local class = class and class .. '\0' or '*\0' - local name = name and name .. '\0' or '*\0' - local w_name = Util.ToWideChar(name) - local w_class = Util.ToWideChar(class) - local options = options or {} - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.SendFragmentedRequest) - p:setSubFunc(2) - p:setNCPReplyBuf(64520) - p:setTask(5) + local class = class and class .. '\0' or '*\0' + local name = name and name .. '\0' or '*\0' + local w_name = Util.ToWideChar(name) + local w_class = Util.ToWideChar(class) + local options = options or {} + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.SendFragmentedRequest) + p:setSubFunc(2) + p:setNCPReplyBuf(64520) + p:setTask(5) - local frag_handle, frag_size, msg_size = 0xffffffff, 64176, 98 - local unknown, proto_flags, nds_verb, version, flags = 0, 0, 6, 3, 0 - local nds_reply_buf = 64520 - local iter_handle = 0xffffffff - local repl_type = 2 -- base and all subordinates - local numobjs = options.numobjs or 0 - local info_types = 1 -- Names - local info_flags = 0x0000381d - -- a bunch of unknowns - local u2, u3, u4, u5, u6, u7, u8, u9 = 0, 0, 2, 2, 0, 0x10, 0, 0x11 + local frag_handle, frag_size, msg_size = 0xffffffff, 64176, 98 + local unknown, proto_flags, nds_verb, version, flags = 0, 0, 6, 3, 0 + local nds_reply_buf = 64520 + local iter_handle = 0xffffffff + local repl_type = 2 -- base and all subordinates + local numobjs = options.numobjs or 0 + local info_types = 1 -- Names + local info_flags = 0x0000381d + -- a bunch of unknowns + local u2, u3, u4, u5, u6, u7, u8, u9 = 0, 0, 2, 2, 0, 0x10, 0, 0x11 - local data = bin.pack("Resolve - -- @return status true on success false on failure - -- @return entries table (if status is true) as return by: - -- ResponseDecoder.EntryDecoder - -- @return error string (if status is false) containing the error - List = function(self, entry) - local p = Packet:new() - p:setType(NCPType.ServiceRequest) - p:setFunc(NCPFunction.SendFragmentedRequest) - p:setSubFunc(2) - p:setNCPReplyBuf(4112) - p:setTask(2) + --- Lists the contents of entry + -- @param entry entry as resolved by Resolve + -- @return status true on success false on failure + -- @return entries table (if status is true) as return by: + -- ResponseDecoder.EntryDecoder + -- @return error string (if status is false) containing the error + List = function(self, entry) + local p = Packet:new() + p:setType(NCPType.ServiceRequest) + p:setFunc(NCPFunction.SendFragmentedRequest) + p:setSubFunc(2) + p:setNCPReplyBuf(4112) + p:setTask(2) - local frag_handle, frag_size = 0xffffffff, 64176 - local msg_size, unknown, proto_flags, nds_verb = 40, 0, 0, 5 - local nds_reply_buf, version, flags = 4100, 1, 0x0001 - local iter_handle = 0xffffffff - local unknown2 = 0x0e - local ZERO = 0 - local info_flags = 0x0000381d + local frag_handle, frag_size = 0xffffffff, 64176 + local msg_size, unknown, proto_flags, nds_verb = 40, 0, 0, 5 + local nds_reply_buf, version, flags = 4100, 1, 0x0001 + local iter_handle = 0xffffffff + local unknown2 = 0x0e + local ZERO = 0 + local info_flags = 0x0000381d - local data = bin.pack("numobjs - number of objects to limit the search to - search = function(self, base, class, name, options) - local base = base or "[Root]" - local status, entry = self.ncp:ResolveName(base) + --- Performs a directory search + -- @param base string containing the name of the base to search + -- @param class string containing the type of class to search + -- @param name string containing the name of the object to find + -- @param options table containing on or more of the following + -- numobjs - number of objects to limit the search to + search = function(self, base, class, name, options) + local base = base or "[Root]" + local status, entry = self.ncp:ResolveName(base) - if ( not(status) ) then - return false, "Search failed, base could not be resolved" - end + if ( not(status) ) then + return false, "Search failed, base could not be resolved" + end - local status, result = self.ncp:Search(entry, class, name, options) - if (not(status)) then return false, result end + local status, result = self.ncp:Search(entry, class, name, options) + if (not(status)) then return false, result end - return status, result - end, + return status, result + end, - --- Retrieves some information from the server using the following NCP - -- functions: - -- GetFileServerInfo - -- Ping - -- EnumerateNetworkAddress - -- GetMountVolumeList - -- The result contains the Tree name, product versions and mounts - getServerInfo = function(self) - local status, srv_info = self.ncp:GetFileServerInfo() - if ( not(status) ) then return false, srv_info end + --- Retrieves some information from the server using the following NCP + -- functions: + -- GetFileServerInfo + -- Ping + -- EnumerateNetworkAddress + -- GetMountVolumeList + -- The result contains the Tree name, product versions and mounts + getServerInfo = function(self) + local status, srv_info = self.ncp:GetFileServerInfo() + if ( not(status) ) then return false, srv_info end - local status, ping_info = self.ncp:Ping() - if ( not(status) ) then return false, ping_info end + local status, ping_info = self.ncp:Ping() + if ( not(status) ) then return false, ping_info end - local status, net_info = self.ncp:EnumerateNetworkAddress() - if ( not(status) ) then return false, net_info end + local status, net_info = self.ncp:EnumerateNetworkAddress() + if ( not(status) ) then return false, net_info end - local status, mnt_list = self.ncp:GetMountVolumeList() - if ( not(status) ) then return false, mnt_list end + local status, mnt_list = self.ncp:GetMountVolumeList() + if ( not(status) ) then return false, mnt_list end - local output = {} - table.insert(output, ("Server name: %s"):format(srv_info.srvname)) - table.insert(output, ("Tree Name: %s"):format(ping_info.tree_name)) - table.insert(output, - ("OS Version: %d.%d (rev %d)"):format(srv_info.os_major, - srv_info.os_minor, srv_info.os_rev)) - table.insert(output, - ("Product version: %d.%d (rev %d)"):format(srv_info.product_major, - srv_info.product_minor, srv_info.product_rev)) - table.insert(output, ("OS Language ID: %d"):format(srv_info.os_lang_id)) + local output = {} + table.insert(output, ("Server name: %s"):format(srv_info.srvname)) + table.insert(output, ("Tree Name: %s"):format(ping_info.tree_name)) + table.insert(output, + ("OS Version: %d.%d (rev %d)"):format(srv_info.os_major, + srv_info.os_minor, srv_info.os_rev)) + table.insert(output, + ("Product version: %d.%d (rev %d)"):format(srv_info.product_major, + srv_info.product_minor, srv_info.product_rev)) + table.insert(output, ("OS Language ID: %d"):format(srv_info.os_lang_id)) - local niceaddr = {} - for _, addr in ipairs(net_info.addr) do - table.insert(niceaddr, ("%s %d/%s"):format(addr.ip,addr.port, - addr.proto)) - end + local niceaddr = {} + for _, addr in ipairs(net_info.addr) do + table.insert(niceaddr, ("%s %d/%s"):format(addr.ip,addr.port, + addr.proto)) + end - niceaddr.name = "Addresses" - table.insert(output, niceaddr) + niceaddr.name = "Addresses" + table.insert(output, niceaddr) - local mounts = {} - for _, mount in ipairs(mnt_list) do - table.insert(mounts, mount.vol_name) - end + local mounts = {} + for _, mount in ipairs(mnt_list) do + table.insert(mounts, mount.vol_name) + end - mounts.name = "Mounts" - table.insert(output, mounts) + mounts.name = "Mounts" + table.insert(output, mounts) - if ( nmap.debugging() > 0 ) then - table.insert(output, ("Acct version: %d"):format(srv_info.acct_version)) - table.insert(output, ("VAP version: %d"):format(srv_info.vap_version)) - table.insert(output, ("QMS version: %d"):format(srv_info.qms_version)) - table.insert(output, - ("Print server version: %d"):format(srv_info.print_version)) - table.insert(output, - ("Virtual console version: %d"):format(srv_info.virt_console_ver)) - table.insert(output, - ("Security Restriction Version: %d"):format(srv_info.sec_restrict_ver)) - table.insert(output, - ("Internet Bridge Version: %d"):format(srv_info.internet_bridge_ver)) - end + if ( nmap.debugging() > 0 ) then + table.insert(output, ("Acct version: %d"):format(srv_info.acct_version)) + table.insert(output, ("VAP version: %d"):format(srv_info.vap_version)) + table.insert(output, ("QMS version: %d"):format(srv_info.qms_version)) + table.insert(output, + ("Print server version: %d"):format(srv_info.print_version)) + table.insert(output, + ("Virtual console version: %d"):format(srv_info.virt_console_ver)) + table.insert(output, + ("Security Restriction Version: %d"):format(srv_info.sec_restrict_ver)) + table.insert(output, + ("Internet Bridge Version: %d"):format(srv_info.internet_bridge_ver)) + end - return true, output - end, + return true, output + end, } Socket = { - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - o.Socket = nmap.new_socket() - o.Buffer = nil - return o - end, + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + o.Socket = nmap.new_socket() + o.Buffer = nil + return o + end, - --- Sets the socket timeout (@see nmap.set_timeout) - -- @param tm number containing the socket timeout in ms - set_timeout = function(self, tm) self.Socket:set_timeout(tm) end, + --- Sets the socket timeout (@see nmap.set_timeout) + -- @param tm number containing the socket timeout in ms + set_timeout = function(self, tm) self.Socket:set_timeout(tm) end, - --- Establishes a connection. - -- - -- @param hostid Hostname or IP address. - -- @param port Port number. - -- @param protocol "tcp", "udp", or - -- @return Status (true or false). - -- @return Error code (if status is false). - connect = function( self, hostid, port, protocol ) - self.Socket:set_timeout(5000) - return self.Socket:connect( hostid, port, protocol ) - end, + --- Establishes a connection. + -- + -- @param hostid Hostname or IP address. + -- @param port Port number. + -- @param protocol "tcp", "udp", or + -- @return Status (true or false). + -- @return Error code (if status is false). + connect = function( self, hostid, port, protocol ) + self.Socket:set_timeout(5000) + return self.Socket:connect( hostid, port, protocol ) + end, - --- Closes an open connection. - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - close = function( self ) - return self.Socket:close() - end, + --- Closes an open connection. + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + close = function( self ) + return self.Socket:close() + end, - --- Opposed to the socket:receive_bytes function, that returns - -- at least x bytes, this function returns the amount of bytes requested. - -- - -- @param count of bytes to read - -- @return true on success, false on failure - -- @return data containing bytes read from the socket - -- err containing error message if status is false - recv = function( self, count ) - local status, data + --- Opposed to the socket:receive_bytes function, that returns + -- at least x bytes, this function returns the amount of bytes requested. + -- + -- @param count of bytes to read + -- @return true on success, false on failure + -- @return data containing bytes read from the socket + -- err containing error message if status is false + recv = function( self, count ) + local status, data - self.Buffer = self.Buffer or "" + self.Buffer = self.Buffer or "" - if ( #self.Buffer < count ) then - status, data = self.Socket:receive_bytes( count - #self.Buffer ) - if ( not(status) or #data < count - #self.Buffer ) then - return false, data - end - self.Buffer = self.Buffer .. data - end + if ( #self.Buffer < count ) then + status, data = self.Socket:receive_bytes( count - #self.Buffer ) + if ( not(status) or #data < count - #self.Buffer ) then + return false, data + end + self.Buffer = self.Buffer .. data + end - data = self.Buffer:sub( 1, count ) - self.Buffer = self.Buffer:sub( count + 1) + data = self.Buffer:sub( 1, count ) + self.Buffer = self.Buffer:sub( count + 1) - return true, data - end, + return true, data + end, - --- Sends data over the socket - -- - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, data ) - return self.Socket:send( data ) - end, + --- Sends data over the socket + -- + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, data ) + return self.Socket:send( data ) + end, } --- "static" Utility class containing mostly conversion functions Util = { - --- Converts a string to a wide string - -- - -- @param str string to be converted - -- @return string containing a two byte representation of str where a zero - -- byte character has been tagged on to each character. - ToWideChar = function( str ) - return str:gsub("(.)", "%1" .. string.char(0x00) ) - end, + --- Converts a string to a wide string + -- + -- @param str string to be converted + -- @return string containing a two byte representation of str where a zero + -- byte character has been tagged on to each character. + ToWideChar = function( str ) + return str:gsub("(.)", "%1" .. string.char(0x00) ) + end, - --- Concerts a wide string to string - -- - -- @param wstr containing the wide string to convert - -- @return string with every other character removed - FromWideChar = function( wstr ) - local str = "" - if ( nil == wstr ) then return nil end - for i=1, wstr:len(), 2 do str = str .. wstr:sub(i, i) end - return str - end, + --- Concerts a wide string to string + -- + -- @param wstr containing the wide string to convert + -- @return string with every other character removed + FromWideChar = function( wstr ) + local str = "" + if ( nil == wstr ) then return nil end + for i=1, wstr:len(), 2 do str = str .. wstr:sub(i, i) end + return str + end, - --- Pads a string with zeroes - -- - -- @param str string containing the string to be padded - -- @param len number containing the length of the new string - -- @return str string containing the new string - ZeroPad = function( str, len ) - if len < str:len() then return end - for i=1, len - str:len() do str = str .. string.char(0) end - return str - end, + --- Pads a string with zeroes + -- + -- @param str string containing the string to be padded + -- @param len number containing the length of the new string + -- @return str string containing the new string + ZeroPad = function( str, len ) + if len < str:len() then return end + for i=1, len - str:len() do str = str .. string.char(0) end + return str + end, - -- Removes trailing nulls - -- - -- @param str containing the string - -- @return ret the string with any trailing nulls removed - CToLuaString = function( str ) - local ret + -- Removes trailing nulls + -- + -- @param str containing the string + -- @return ret the string with any trailing nulls removed + CToLuaString = function( str ) + local ret - if ( not(str) ) then return "" end - if ( str:sub(-1, -1 ) ~= "\0" ) then return str end + if ( not(str) ) then return "" end + if ( str:sub(-1, -1 ) ~= "\0" ) then return str end - for i=1, #str do - if ( str:sub(-i,-i) == "\0" ) then - ret = str:sub(1, -i - 1) - else - break - end - end - return ret - end, + for i=1, #str do + if ( str:sub(-i,-i) == "\0" ) then + ret = str:sub(1, -i - 1) + else + break + end + end + return ret + end, } diff --git a/nselib/packet.lua b/nselib/packet.lua index b00c43279..7d926dbb9 100644 --- a/nselib/packet.lua +++ b/nselib/packet.lua @@ -18,28 +18,28 @@ _ENV = stdnse.module("packet", stdnse.seeall) -- @param i Offset. -- @return An 8-bit integer. function u8(b, i) - return string.byte(b, i+1) + return string.byte(b, i+1) end --- Get a 16-bit integer at a 0-based byte offset in a byte string. -- @param b A byte string. -- @param i Offset. -- @return A 16-bit integer. function u16(b, i) - local b1,b2 - b1, b2 = string.byte(b, i+1), string.byte(b, i+2) - -- 2^8 2^0 - return b1*256 + b2 + local b1,b2 + b1, b2 = string.byte(b, i+1), string.byte(b, i+2) + -- 2^8 2^0 + return b1*256 + b2 end --- Get a 32-bit integer at a 0-based byte offset in a byte string. -- @param b A byte string. -- @param i Offset. -- @return A 32-bit integer. function u32(b,i) - local b1,b2,b3,b4 - b1, b2 = string.byte(b, i+1), string.byte(b, i+2) - b3, b4 = string.byte(b, i+3), string.byte(b, i+4) - -- 2^24 2^16 2^8 2^0 - return b1*16777216 + b2*65536 + b3*256 + b4 + local b1,b2,b3,b4 + b1, b2 = string.byte(b, i+1), string.byte(b, i+2) + b3, b4 = string.byte(b, i+3), string.byte(b, i+4) + -- 2^24 2^16 2^8 2^0 + return b1*16777216 + b2*65536 + b3*256 + b4 end --- Set an 8-bit integer at a 0-based byte offset in a byte string @@ -48,8 +48,8 @@ end -- @param i Offset. -- @param num Integer to store. function set_u8(b, i, num) - local s = string.char(bit.band(num, 0xff)) - return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+1) + local s = string.char(bit.band(num, 0xff)) + return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+1) end --- Set a 16-bit integer at a 0-based byte offset in a byte string -- (big-endian). @@ -57,8 +57,8 @@ end -- @param i Offset. -- @param num Integer to store. function set_u16(b, i, num) - local s = string.char(bit.band(bit.rshift(num, 8), 0xff)) .. string.char(bit.band(num, 0xff)) - return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+2) + local s = string.char(bit.band(bit.rshift(num, 8), 0xff)) .. string.char(bit.band(num, 0xff)) + return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+2) end --- Set a 32-bit integer at a 0-based byte offset in a byte string -- (big-endian). @@ -66,128 +66,128 @@ end -- @param i Offset. -- @param num Integer to store. function set_u32(b,i, num) - local s = string.char(bit.band(bit.rshift(num,24), 0xff)) .. - string.char(bit.band(bit.rshift(num,16), 0xff)) .. - string.char(bit.band(bit.rshift(num,8), 0xff)) .. - string.char(bit.band(num, 0xff)) - return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+4) + local s = string.char(bit.band(bit.rshift(num,24), 0xff)) .. + string.char(bit.band(bit.rshift(num,16), 0xff)) .. + string.char(bit.band(bit.rshift(num,8), 0xff)) .. + string.char(bit.band(num, 0xff)) + return b:sub(0+1, i+1-1) .. s .. b:sub(i+1+4) end --- Get a 1-byte string from a number. -- @param num A number. function numtostr8(num) - return string.char(num) + return string.char(num) end --- Get a 2-byte string from a number. -- (big-endian) -- @param num A number. function numtostr16(num) - return set_u16("..", 0, num) + return set_u16("..", 0, num) end --- Get a 4-byte string from a number. -- (big-endian) -- @param num A number. function numtostr32(num) - return set_u32("....", 0, num) + return set_u32("....", 0, num) end --- Calculate a standard Internet checksum. -- @param b Data to checksum. -- @return Checksum. function in_cksum(b) - local sum = 0 - local i + local sum = 0 + local i - -- Note we are using 0-based indexes here. - i = 0 - while i < b:len() - 1 do - sum = sum + u16(b, i) - i = i + 2 - end - if i < b:len() then - sum = sum + u8(b, i) * 256 - end + -- Note we are using 0-based indexes here. + i = 0 + while i < b:len() - 1 do + sum = sum + u16(b, i) + i = i + 2 + end + if i < b:len() then + sum = sum + u8(b, i) * 256 + end - sum = bit.rshift(sum, 16) + bit.band(sum, 0xffff) - sum = sum + bit.rshift(sum, 16) - sum = bit.bnot(sum) - sum = bit.band(sum, 0xffff) -- trunctate to 16 bits - return sum + sum = bit.rshift(sum, 16) + bit.band(sum, 0xffff) + sum = sum + bit.rshift(sum, 16) + sum = bit.bnot(sum) + sum = bit.band(sum, 0xffff) -- trunctate to 16 bits + return sum end -- ip protocol field -IPPROTO_IP = 0 -- Dummy protocol for TCP -IPPROTO_HOPOPTS = 0 -- IPv6 hop-by-hop options -IPPROTO_ICMP = 1 -- Internet Control Message Protocol -IPPROTO_IGMP = 2 -- Internet Group Management Protocol -IPPROTO_IPIP = 4 -- IPIP tunnels (older KA9Q tunnels use 94) -IPPROTO_TCP = 6 -- Transmission Control Protocol -IPPROTO_EGP = 8 -- Exterior Gateway Protocol -IPPROTO_PUP = 12 -- PUP protocol -IPPROTO_UDP = 17 -- User Datagram Protocol -IPPROTO_IDP = 22 -- XNS IDP protocol -IPPROTO_DCCP = 33 -- Datagram Congestion Control Protocol -IPPROTO_RSVP = 46 -- RSVP protocol -IPPROTO_GRE = 47 -- Cisco GRE tunnels (rfc 1701,1702) -IPPROTO_IPV6 = 41 -- IPv6-in-IPv4 tunnelling +IPPROTO_IP = 0 -- Dummy protocol for TCP +IPPROTO_HOPOPTS = 0 -- IPv6 hop-by-hop options +IPPROTO_ICMP = 1 -- Internet Control Message Protocol +IPPROTO_IGMP = 2 -- Internet Group Management Protocol +IPPROTO_IPIP = 4 -- IPIP tunnels (older KA9Q tunnels use 94) +IPPROTO_TCP = 6 -- Transmission Control Protocol +IPPROTO_EGP = 8 -- Exterior Gateway Protocol +IPPROTO_PUP = 12 -- PUP protocol +IPPROTO_UDP = 17 -- User Datagram Protocol +IPPROTO_IDP = 22 -- XNS IDP protocol +IPPROTO_DCCP = 33 -- Datagram Congestion Control Protocol +IPPROTO_RSVP = 46 -- RSVP protocol +IPPROTO_GRE = 47 -- Cisco GRE tunnels (rfc 1701,1702) +IPPROTO_IPV6 = 41 -- IPv6-in-IPv4 tunnelling -IPPROTO_ROUTING = 43 -- IPv6 routing header -IPPROTO_FRAGMENT= 44 -- IPv6 fragmentation header -IPPROTO_ESP = 50 -- Encapsulation Security Payload protocol -IPPROTO_AH = 51 -- Authentication Header protocol -IPPROTO_ICMPV6 = 58 -- ICMP for IPv6 -IPPROTO_DSTOPTS = 60 -- IPv6 destination options -IPPROTO_BEETPH = 94 -- IP option pseudo header for BEET -IPPROTO_PIM = 103 -- Protocol Independent Multicast +IPPROTO_ROUTING = 43 -- IPv6 routing header +IPPROTO_FRAGMENT= 44 -- IPv6 fragmentation header +IPPROTO_ESP = 50 -- Encapsulation Security Payload protocol +IPPROTO_AH = 51 -- Authentication Header protocol +IPPROTO_ICMPV6 = 58 -- ICMP for IPv6 +IPPROTO_DSTOPTS = 60 -- IPv6 destination options +IPPROTO_BEETPH = 94 -- IP option pseudo header for BEET +IPPROTO_PIM = 103 -- Protocol Independent Multicast -IPPROTO_COMP = 108 -- Compression Header protocol -IPPROTO_SCTP = 132 -- Stream Control Transport Protocol -IPPROTO_UDPLITE = 136 -- UDP-Lite (RFC 3828) +IPPROTO_COMP = 108 -- Compression Header protocol +IPPROTO_SCTP = 132 -- Stream Control Transport Protocol +IPPROTO_UDPLITE = 136 -- UDP-Lite (RFC 3828) ICMP_ECHO_REQUEST = 8 ICMP_ECHO_REPLY = 0 -ICMP6_ECHO_REQUEST = 128 -ICMP6_ECHO_REPLY = 129 -MLD_LISTENER_QUERY = 130 -MLD_LISTENER_REPORT = 131 -MLD_LISTENER_REDUCTION = 132 -ND_ROUTER_SOLICIT = 133 -ND_ROUTER_ADVERT = 134 -ND_NEIGHBOR_SOLICIT = 135 -ND_NEIGHBOR_ADVERT = 136 -ND_REDIRECT = 137 +ICMP6_ECHO_REQUEST = 128 +ICMP6_ECHO_REPLY = 129 +MLD_LISTENER_QUERY = 130 +MLD_LISTENER_REPORT = 131 +MLD_LISTENER_REDUCTION = 132 +ND_ROUTER_SOLICIT = 133 +ND_ROUTER_ADVERT = 134 +ND_NEIGHBOR_SOLICIT = 135 +ND_NEIGHBOR_ADVERT = 136 +ND_REDIRECT = 137 MLDV2_LISTENER_REPORT = 143 -ND_OPT_SOURCE_LINKADDR = 1 -ND_OPT_TARGET_LINKADDR = 2 -ND_OPT_PREFIX_INFORMATION = 3 -ND_OPT_REDIRECTED_HEADER = 4 -ND_OPT_MTU = 5 -ND_OPT_RTR_ADV_INTERVAL = 7 -ND_OPT_HOME_AGENT_INFO = 8 +ND_OPT_SOURCE_LINKADDR = 1 +ND_OPT_TARGET_LINKADDR = 2 +ND_OPT_PREFIX_INFORMATION = 3 +ND_OPT_REDIRECTED_HEADER = 4 +ND_OPT_MTU = 5 +ND_OPT_RTR_ADV_INTERVAL = 7 +ND_OPT_HOME_AGENT_INFO = 8 -ETHER_TYPE_IPV4 = string.char(0x08, 0x00) -ETHER_TYPE_IPV6 = string.char(0x86, 0xdd) +ETHER_TYPE_IPV4 = string.char(0x08, 0x00) +ETHER_TYPE_IPV6 = string.char(0x86, 0xdd) ---------------------------------------------------------------------------------------------------------------- -- Frame is a class Frame = {} function Frame:new(frame, force_continue) - local packet = nil - local packet_len = 0 - if frame and #frame > 14 then - packet = string.sub(frame, 15, -1) - packet_len = #frame - 14 - end - local o = Packet:new(packet, packet_len, force_continue) + local packet = nil + local packet_len = 0 + if frame and #frame > 14 then + packet = string.sub(frame, 15, -1) + packet_len = #frame - 14 + end + local o = Packet:new(packet, packet_len, force_continue) - o.build_ether_frame = self.build_ether_frame - o.ether_parse = self.ether_parse - o.frame_buf = frame - o:ether_parse() - return o + o.build_ether_frame = self.build_ether_frame + o.ether_parse = self.ether_parse + o.frame_buf = frame + o:ether_parse() + return o end --- Build an Ethernet frame. -- @param mac_dst six-byte string of the destination MAC address. @@ -196,14 +196,14 @@ end -- @param packet string of the payload. -- @return frame string of the Ether frame. function Frame:build_ether_frame(mac_dst, mac_src, ether_type, packet) - self.mac_dst = mac_dst or self.mac_dst - self.mac_src = mac_src or self.mac_src - self.ether_type = ether_type or self.ether_type - self.buf = packet or self.buf - if not self.ether_type then - return nil, "Unknown packet type." - end - self.frame_buf = self.mac_dst..self.mac_src..self.ether_type..self.buf + self.mac_dst = mac_dst or self.mac_dst + self.mac_src = mac_src or self.mac_src + self.ether_type = ether_type or self.ether_type + self.buf = packet or self.buf + if not self.ether_type then + return nil, "Unknown packet type." + end + self.frame_buf = self.mac_dst..self.mac_src..self.ether_type..self.buf end --- Parse an Ethernet frame. -- @param frame string of the Ether frame. @@ -211,12 +211,12 @@ end -- @return mac_src six-byte string of the source MAC address. -- @return packet string of the payload. function Frame:ether_parse() - if not self.frame_buf or #self.frame_buf < 14 then -- too short - return false - end - self.mac_dst = string.sub(self.frame_buf, 1, 6) - self.mac_src = string.sub(self.frame_buf, 7, 12) - self.ether_type = u16(self.frame_buf, 12) + if not self.frame_buf or #self.frame_buf < 14 then -- too short + return false + end + self.mac_dst = string.sub(self.frame_buf, 1, 6) + self.mac_src = string.sub(self.frame_buf, 7, 12) + self.ether_type = u16(self.frame_buf, 12) end ---------------------------------------------------------------------------------------------------------------- @@ -233,57 +233,57 @@ Packet = {} -- normally would fail because the TCP header is too small. -- @return A new Packet. function Packet:new(packet, packet_len, force_continue) - local o = setmetatable({}, {__index = Packet}) - if not packet then - return o - end - o.buf = packet - o.packet_len = packet_len - o.ip_v = bit.rshift(string.byte(o.buf), 4) - if o.ip_v == 4 and not o:ip_parse(force_continue) then - return nil - elseif o.ip_v == 6 and not o:ip6_parse(force_continue) then - return nil - end + local o = setmetatable({}, {__index = Packet}) + if not packet then + return o + end + o.buf = packet + o.packet_len = packet_len + o.ip_v = bit.rshift(string.byte(o.buf), 4) + if o.ip_v == 4 and not o:ip_parse(force_continue) then + return nil + elseif o.ip_v == 6 and not o:ip6_parse(force_continue) then + return nil + end - if o.ip_v == 6 then - while o:ipv6_is_extension_header() do - if not o:ipv6_ext_header_parse(force_continue) or o.ip6_data_offset >= o.packet_len then - stdnse.print_debug("Error while parsing IPv6 extension headers.") - return o - end - end - o.ip_p = o.ip6_nhdr - end + if o.ip_v == 6 then + while o:ipv6_is_extension_header() do + if not o:ipv6_ext_header_parse(force_continue) or o.ip6_data_offset >= o.packet_len then + stdnse.print_debug("Error while parsing IPv6 extension headers.") + return o + end + end + o.ip_p = o.ip6_nhdr + end - if o.ip_p == IPPROTO_TCP then - if not o:tcp_parse(force_continue) then - stdnse.print_debug("Error while parsing TCP packet\n") - end - elseif o.ip_p == IPPROTO_UDP then - if not o:udp_parse(force_continue) then - stdnse.print_debug("Error while parsing UDP packet\n") - end - elseif o.ip_p == IPPROTO_ICMP then - if not o:icmp_parse(force_continue) then - stdnse.print_debug("Error while parsing ICMP packet\n") - end - elseif o.ip_p == IPPROTO_ICMPV6 then - if not o:icmpv6_parse(force_continue) then - stdnse.print_debug("Error while parsing ICMPv6 packet\n") - end - end - return o + if o.ip_p == IPPROTO_TCP then + if not o:tcp_parse(force_continue) then + stdnse.print_debug("Error while parsing TCP packet\n") + end + elseif o.ip_p == IPPROTO_UDP then + if not o:udp_parse(force_continue) then + stdnse.print_debug("Error while parsing UDP packet\n") + end + elseif o.ip_p == IPPROTO_ICMP then + if not o:icmp_parse(force_continue) then + stdnse.print_debug("Error while parsing ICMP packet\n") + end + elseif o.ip_p == IPPROTO_ICMPV6 then + if not o:icmpv6_parse(force_continue) then + stdnse.print_debug("Error while parsing ICMPv6 packet\n") + end + end + return o end --- Convert Version, Traffic Class and Flow Label to a 4-byte string. -- @param ip6_tc Number stands for Traffic Class. -- @param ip6_fl Number stands for Flow Label. -- @return The first four-byte string of an IPv6 header. function ipv6_hdr_pack_tc_fl(ip6_tc, ip6_fl) - local ver_tc_fl = bit.lshift(6, 28) + - bit.lshift(bit.band(ip6_tc, 0xFF), 20) + - bit.band(ip6_fl, 0xFFFFF) - return numtostr32(ver_tc_fl) + local ver_tc_fl = bit.lshift(6, 28) + + bit.lshift(bit.band(ip6_tc, 0xFF), 20) + + bit.band(ip6_fl, 0xFFFFF) + return numtostr32(ver_tc_fl) end --- Build an IPv6 packet. -- @param src 16-byte string of the source IPv6 address. @@ -293,48 +293,48 @@ end -- @param t_class integer that represents traffic class. -- @param f_label integer that represents flow label. function Packet:build_ipv6_packet(src, dst, nx_hdr, payload, h_limit, t_class, f_label) - self.ether_type = ETHER_TYPE_IPV6 - self.ip_v = 6 - self.ip_bin_src = src or self.ip_bin_src - self.ip_bin_dst = dst or self.ip_bin_dst - self.ip6_nhdr = nx_hdr or self.ip6_nhdr - self.l4_packet = payload or self.l4_packet - self.ip6_tc = t_class or self.ip6_tc or 1 - self.ip6_fl = f_label or self.ip6_fl or 1 - self.ip6_hlimit = h_limit or self.ip6_hlimit or 255 - self.ip6_plen = #(self.exheader or "")+#(self.l4_packet or "") - self.buf = - ipv6_hdr_pack_tc_fl(self.ip6_tc, self.ip6_fl) .. - numtostr16(self.ip6_plen) .. --payload length - string.char(self.ip6_nhdr) .. --next header - string.char(self.ip6_hlimit) .. --hop limit - self.ip_bin_src .. --Source - self.ip_bin_dst ..--dest - (self.exheader or "").. - (self.l4_packet or "") + self.ether_type = ETHER_TYPE_IPV6 + self.ip_v = 6 + self.ip_bin_src = src or self.ip_bin_src + self.ip_bin_dst = dst or self.ip_bin_dst + self.ip6_nhdr = nx_hdr or self.ip6_nhdr + self.l4_packet = payload or self.l4_packet + self.ip6_tc = t_class or self.ip6_tc or 1 + self.ip6_fl = f_label or self.ip6_fl or 1 + self.ip6_hlimit = h_limit or self.ip6_hlimit or 255 + self.ip6_plen = #(self.exheader or "")+#(self.l4_packet or "") + self.buf = + ipv6_hdr_pack_tc_fl(self.ip6_tc, self.ip6_fl) .. + numtostr16(self.ip6_plen) .. --payload length + string.char(self.ip6_nhdr) .. --next header + string.char(self.ip6_hlimit) .. --hop limit + self.ip_bin_src .. --Source + self.ip_bin_dst ..--dest + (self.exheader or "").. + (self.l4_packet or "") end --- Return true if and only if the next header is an known extension header. -- @param nhdr Next header. function Packet:ipv6_is_extension_header(nhdr) - self.ip6_nhdr = nhdr or self.ip6_nhdr - if self.ip6_nhdr == IPPROTO_HOPOPTS or - self.ip6_nhdr == IPPROTO_DSTOPTS or - self.ip6_nhdr == IPPROTO_ROUTING or - self.ip6_nhdr == IPPROTO_FRAGMENT then - return true - end - return nil + self.ip6_nhdr = nhdr or self.ip6_nhdr + if self.ip6_nhdr == IPPROTO_HOPOPTS or + self.ip6_nhdr == IPPROTO_DSTOPTS or + self.ip6_nhdr == IPPROTO_ROUTING or + self.ip6_nhdr == IPPROTO_FRAGMENT then + return true + end + return nil end --- Count IPv6 checksum. -- @return the checksum. function Packet:count_ipv6_pseudoheader_cksum() - local pseudoheader = self.ip_bin_src .. self.ip_bin_dst .. numtostr16(#self.l4_packet) .. string.char(0x0,0x0,0x0) .. string.char(self.ip6_nhdr) - local ck_content = pseudoheader .. self.l4_packet - return in_cksum(ck_content) + local pseudoheader = self.ip_bin_src .. self.ip_bin_dst .. numtostr16(#self.l4_packet) .. string.char(0x0,0x0,0x0) .. string.char(self.ip6_nhdr) + local ck_content = pseudoheader .. self.l4_packet + return in_cksum(ck_content) end --- Set ICMPv6 checksum. function Packet:set_icmp6_cksum(check_sum) - self.l4_packet = set_u16(self.l4_packet, 2, check_sum) + self.l4_packet = set_u16(self.l4_packet, 2, check_sum) end --- Build an ICMPv6 header. -- @param icmpv6_type integer that represent ICMPv6 type. @@ -343,19 +343,19 @@ end -- @param ip_bin_src 16-byte string of the source IPv6 address. -- @param ip_bin_dst 16-byte string of the destination IPv6 address. function Packet:build_icmpv6_header(icmpv6_type, icmpv6_code, icmpv6_payload, ip_bin_src, ip_bin_dst) - self.ip6_nhdr = IPPROTO_ICMPV6 - self.icmpv6_type = icmpv6_type or self.icmpv6_type - self.icmpv6_code = icmpv6_code or self.icmpv6_code - self.icmpv6_payload = icmpv6_payload or self.icmpv6_payload - self.ip_bin_src = ip_bin_src or self.ip_bin_src - self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst + self.ip6_nhdr = IPPROTO_ICMPV6 + self.icmpv6_type = icmpv6_type or self.icmpv6_type + self.icmpv6_code = icmpv6_code or self.icmpv6_code + self.icmpv6_payload = icmpv6_payload or self.icmpv6_payload + self.ip_bin_src = ip_bin_src or self.ip_bin_src + self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst - self.l4_packet = - string.char(self.icmpv6_type,self.icmpv6_code) .. - string.char(0x00,0x00) .. --checksum - (self.icmpv6_payload or "") - local check_sum = self:count_ipv6_pseudoheader_cksum() - self:set_icmp6_cksum(check_sum) + self.l4_packet = + string.char(self.icmpv6_type,self.icmpv6_code) .. + string.char(0x00,0x00) .. --checksum + (self.icmpv6_payload or "") + local check_sum = self:count_ipv6_pseudoheader_cksum() + self:set_icmp6_cksum(check_sum) end --- Build an ICMPv6 Echo Request frame. -- @param mac_src six-byte string of source MAC address. @@ -369,27 +369,27 @@ end -- @param fl integer that represents flow label of IPv6 packet. -- @param hop-limit integer that represents hop limit of IPv6 packet. function Packet:build_icmpv6_echo_request(id, sequence, data, mac_src, mac_dst, ip_bin_src, ip_bin_dst, tc, fl, hop_limit) - self.mac_src = mac_src or self.mac_src - self.mac_dst = mac_dst or self.mac_dst + self.mac_src = mac_src or self.mac_src + self.mac_dst = mac_dst or self.mac_dst - self.ip_bin_src = ip_bin_src or self.ip_bin_src - self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst - self.traffic_class = tc or 1 - self.flow_label = fl or 1 - self.ip6_hlimit = hop_limit or 255 + self.ip_bin_src = ip_bin_src or self.ip_bin_src + self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst + self.traffic_class = tc or 1 + self.flow_label = fl or 1 + self.ip6_hlimit = hop_limit or 255 - self.icmpv6_type = ICMP6_ECHO_REQUEST - self.icmpv6_code = 0 + self.icmpv6_type = ICMP6_ECHO_REQUEST + self.icmpv6_code = 0 - self.echo_id = id or self.echo_id or 0xdead - self.echo_seq = sequence or self.echo_seq or 0xbeef - self.echo_data = data or self.echo_data or "" + self.echo_id = id or self.echo_id or 0xdead + self.echo_seq = sequence or self.echo_seq or 0xbeef + self.echo_data = data or self.echo_data or "" - self.icmpv6_payload = numtostr16(self.echo_id) .. numtostr16(self.echo_seq) .. self.echo_data + self.icmpv6_payload = numtostr16(self.echo_id) .. numtostr16(self.echo_seq) .. self.echo_data end --- Set an ICMPv6 option message. function Packet:set_icmpv6_option(opt_type,msg) - return string.char(opt_type, (#msg+2)/8) .. msg + return string.char(opt_type, (#msg+2)/8) .. msg end --- Build an IPv4 packet. @@ -403,32 +403,32 @@ end -- @param ttl integer that represent the IP time to live -- @param proto integer that represents the IP protocol function Packet:build_ip_packet(src, dst, payload, dsf, id, flags, off, ttl, proto) - self.ether_type = ETHER_TYPE_IPV4 - self.ip_v = 4 - self.ip_bin_src = src or self.ip_bin_src - self.ip_bin_dst = dst or self.ip_bin_dst - self.l3_packet = payload or self.l3_packet - self.ip_dsf = dsf or self.ip_dsf or 0 - self.ip_p = proto or self.ip_p - self.flags = flags or self.flags or 0 -- should be split into ip_rd, ip_df, ip_mv - self.ip_id = id or self.ip_id or 0xbeef - self.ip_off = off or self.ip_off or 0 - self.ip_ttl = ttl or self.ip_ttl or 255 - self.buf = - numtostr8(bit.lshift(self.ip_v,4) + 20 / 4) .. -- version and header length - numtostr8(self.ip_dsf) .. - numtostr16(#self.l3_packet + 20) .. - numtostr16(self.ip_id) .. - numtostr8(self.flags) .. - numtostr8(self.ip_off) .. - numtostr8(self.ip_ttl) .. - numtostr8(self.ip_p) .. - numtostr16(0) .. -- checksum - self.ip_bin_src .. --Source - self.ip_bin_dst --dest + self.ether_type = ETHER_TYPE_IPV4 + self.ip_v = 4 + self.ip_bin_src = src or self.ip_bin_src + self.ip_bin_dst = dst or self.ip_bin_dst + self.l3_packet = payload or self.l3_packet + self.ip_dsf = dsf or self.ip_dsf or 0 + self.ip_p = proto or self.ip_p + self.flags = flags or self.flags or 0 -- should be split into ip_rd, ip_df, ip_mv + self.ip_id = id or self.ip_id or 0xbeef + self.ip_off = off or self.ip_off or 0 + self.ip_ttl = ttl or self.ip_ttl or 255 + self.buf = + numtostr8(bit.lshift(self.ip_v,4) + 20 / 4) .. -- version and header length + numtostr8(self.ip_dsf) .. + numtostr16(#self.l3_packet + 20) .. + numtostr16(self.ip_id) .. + numtostr8(self.flags) .. + numtostr8(self.ip_off) .. + numtostr8(self.ip_ttl) .. + numtostr8(self.ip_p) .. + numtostr16(0) .. -- checksum + self.ip_bin_src .. --Source + self.ip_bin_dst --dest - self.buf = set_u16(self.buf, 10, in_cksum(self.buf)) - self.buf = self.buf .. self.l3_packet + self.buf = set_u16(self.buf, 10, in_cksum(self.buf)) + self.buf = self.buf .. self.l3_packet end --- Build an ICMP header. -- @param icmp_type integer that represent ICMPv6 type. @@ -437,17 +437,17 @@ end -- @param ip_bin_src 16-byte string of the source IPv6 address. -- @param ip_bin_dst 16-byte string of the destination IPv6 address. function Packet:build_icmp_header(icmp_type, icmp_code, icmp_payload, ip_bin_src, ip_bin_dst) - self.icmp_type = icmp_type or self.icmp_type - self.icmp_code = icmp_code or self.icmp_code - self.icmp_payload = icmp_payload or self.icmp_payload - self.ip_bin_src = ip_bin_src or self.ip_bin_src - self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst + self.icmp_type = icmp_type or self.icmp_type + self.icmp_code = icmp_code or self.icmp_code + self.icmp_payload = icmp_payload or self.icmp_payload + self.ip_bin_src = ip_bin_src or self.ip_bin_src + self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst - self.l3_packet = - string.char(self.icmp_type,self.icmp_code) .. - string.char(0x00,0x00) .. --checksum - (self.icmp_payload or "") - self.l3_packet = set_u16(self.l3_packet, 2, in_cksum(self.l3_packet)) + self.l3_packet = + string.char(self.icmp_type,self.icmp_code) .. + string.char(0x00,0x00) .. --checksum + (self.icmp_payload or "") + self.l3_packet = set_u16(self.l3_packet, 2, in_cksum(self.l3_packet)) end --- Build an ICMP Echo Request frame. -- @param mac_src six-byte string of source MAC address. @@ -459,21 +459,21 @@ end -- @param data string of Echo data. -- @param dsf integer that represents differentiated services field. function Packet:build_icmp_echo_request(id, seq, data, mac_src, mac_dst, ip_bin_src, ip_bin_dst) - self.mac_src = mac_src or self.mac_src - self.mac_dst = mac_dst or self.mac_dst + self.mac_src = mac_src or self.mac_src + self.mac_dst = mac_dst or self.mac_dst - self.ip_p = IPPROTO_ICMP - self.ip_bin_src = ip_bin_src or self.ip_bin_src - self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst + self.ip_p = IPPROTO_ICMP + self.ip_bin_src = ip_bin_src or self.ip_bin_src + self.ip_bin_dst = ip_bin_dst or self.ip_bin_dst - self.icmp_type = ICMP_ECHO_REQUEST - self.icmp_code = 0 + self.icmp_type = ICMP_ECHO_REQUEST + self.icmp_code = 0 - self.echo_id = id or self.echo_id or 0xdead - self.echo_seq = seq or self.echo_seq or 0xbeef - self.echo_data = data or self.echo_data or "" + self.echo_id = id or self.echo_id or 0xdead + self.echo_seq = seq or self.echo_seq or 0xbeef + self.echo_data = data or self.echo_data or "" - self.icmp_payload = numtostr16(self.echo_id) .. numtostr16(self.echo_seq) .. self.echo_data + self.icmp_payload = numtostr16(self.echo_id) .. numtostr16(self.echo_seq) .. self.echo_data end @@ -485,144 +485,144 @@ end -- @param str IP address string. -- @return Four-byte string. function iptobin(str) - local ret = "" - for c in string.gmatch(str, "[0-9]+") do - ret = ret .. string.char(c+0) -- automatic conversion to int - end - return ret + local ret = "" + for c in string.gmatch(str, "[0-9]+") do + ret = ret .. string.char(c+0) -- automatic conversion to int + end + return ret end --- Convert an IPv6 address string (like "fe80:21::1") to a raw -- string 16 bytes long (big-endian). -- @param str IPv6 address string. -- @return 16-byte string. function ip6tobin(str) - if not str then - return nil - end - -- Handle IPv4-compatible IPv6 address. - local ipv6_size = 8 -- An IPv6 address is 8*16bits long. But for IPv4-compatible address, the IPv6-style part is 6*16bits long. - local ip4_bin = "" - local dot_count = stdnse.strsplit("%.", str) - if #dot_count == 4 then -- It might be IPv4-compatible IPv6 address. - local ip64 = stdnse.strsplit(":", str) - local ip4_str = ip64[#ip64] -- Get the embeded IPv4 address string. - ip4_bin = iptobin(ip4_str) - if not ip4_bin then - return nil - end - ipv6_size = 6 - str = string.sub(str, 1, -#ip4_str-1) - elseif #dot_count ~= 1 then - return nil - end - -- Handle the left IPv6-style part. - local sides = stdnse.strsplit("::", str) - if #sides > 2 then - return nil - end - local head = stdnse.strsplit(":", sides[1]) - if #sides > 1 then - local tail = stdnse.strsplit(":", sides[2]) - if tail[#tail] == "" then - table.remove(tail, #tail) - end - local missing = ipv6_size - #head - #tail - while missing > 0 do - table.insert(head, "0") - missing = missing - 1 - end - for _, e in ipairs(tail) do - table.insert(head, e) - end - end - if #head ~= ipv6_size then - return nil - end - -- Transfer the 16-bit units to raw string. - local unit16 - local addr_hex = "" - for _, unit16 in ipairs(head) do - local h8 = string.sub(unit16,-4,-3) - local l8 = string.sub(unit16,-2,-1) - local unit8 - for _,unit8 in pairs({h8,l8}) do - if (unit8 == "") then - addr_hex = addr_hex .. string.char(0x00) - else - addr_hex = addr_hex .. string.char("0x"..unit8) - end - end - end + if not str then + return nil + end + -- Handle IPv4-compatible IPv6 address. + local ipv6_size = 8 -- An IPv6 address is 8*16bits long. But for IPv4-compatible address, the IPv6-style part is 6*16bits long. + local ip4_bin = "" + local dot_count = stdnse.strsplit("%.", str) + if #dot_count == 4 then -- It might be IPv4-compatible IPv6 address. + local ip64 = stdnse.strsplit(":", str) + local ip4_str = ip64[#ip64] -- Get the embeded IPv4 address string. + ip4_bin = iptobin(ip4_str) + if not ip4_bin then + return nil + end + ipv6_size = 6 + str = string.sub(str, 1, -#ip4_str-1) + elseif #dot_count ~= 1 then + return nil + end + -- Handle the left IPv6-style part. + local sides = stdnse.strsplit("::", str) + if #sides > 2 then + return nil + end + local head = stdnse.strsplit(":", sides[1]) + if #sides > 1 then + local tail = stdnse.strsplit(":", sides[2]) + if tail[#tail] == "" then + table.remove(tail, #tail) + end + local missing = ipv6_size - #head - #tail + while missing > 0 do + table.insert(head, "0") + missing = missing - 1 + end + for _, e in ipairs(tail) do + table.insert(head, e) + end + end + if #head ~= ipv6_size then + return nil + end + -- Transfer the 16-bit units to raw string. + local unit16 + local addr_hex = "" + for _, unit16 in ipairs(head) do + local h8 = string.sub(unit16,-4,-3) + local l8 = string.sub(unit16,-2,-1) + local unit8 + for _,unit8 in pairs({h8,l8}) do + if (unit8 == "") then + addr_hex = addr_hex .. string.char(0x00) + else + addr_hex = addr_hex .. string.char("0x"..unit8) + end + end + end - return addr_hex .. ip4_bin + return addr_hex .. ip4_bin end --- Convert a MAC address string (like "00:23:ae:5d:3b:10") to -- a raw six-byte long. -- @param str MAC address string. -- @return Six-byte string. function mactobin(str) - if not str then - return nil, "MAC was not specified." - end - local unit8 - local addr_hex = "" - for unit8 in string.gmatch(str,"%x+") do - addr_hex = addr_hex .. string.char("0x"..unit8) - end - return addr_hex + if not str then + return nil, "MAC was not specified." + end + local unit8 + local addr_hex = "" + for unit8 in string.gmatch(str,"%x+") do + addr_hex = addr_hex .. string.char("0x"..unit8) + end + return addr_hex end --- Convert a four-byte raw string to a dotted-quad IP address string. -- @param raw_ip_addr Four-byte string. -- @return IP address string. function toip(raw_ip_addr) - if not raw_ip_addr then - return "?.?.?.?" - end - return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4)) + if not raw_ip_addr then + return "?.?.?.?" + end + return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4)) end --- Convert a 16-byte raw string to an IPv6 address string. -- @param raw_ipv6_addr 16-byte string. -- @return IPv6 address string. function toipv6(raw_ipv6_addr) - local long_addr_str - local status, addrs + local long_addr_str + local status, addrs - if not raw_ipv6_addr then - return nil, "IPv6 address was not specified." - end - long_addr_str = stdnse.tohex(raw_ipv6_addr, {separator=":", group=4}) - status, addrs = nmap.resolve(long_addr_str, "inet6") + if not raw_ipv6_addr then + return nil, "IPv6 address was not specified." + end + long_addr_str = stdnse.tohex(raw_ipv6_addr, {separator=":", group=4}) + status, addrs = nmap.resolve(long_addr_str, "inet6") - return (status and addrs[1]) or long_addr_str + return (status and addrs[1]) or long_addr_str end --- Generate the link-local IPv6 address from the MAC address. -- @param mac MAC address string. -- @return Link-local IPv6 address string. function mac_to_lladdr(mac) - if not mac then - return nil, "MAC was not specified." - end - local interfier = string.char(bit.bor(string.byte(mac,1),0x02))..string.sub(mac,2,3)..string.char(0xff,0xfe)..string.sub(mac,4,6) - local ll_prefix = ip6tobin("fe80::") - return string.sub(ll_prefix,1,8)..interfier + if not mac then + return nil, "MAC was not specified." + end + local interfier = string.char(bit.bor(string.byte(mac,1),0x02))..string.sub(mac,2,3)..string.char(0xff,0xfe)..string.sub(mac,4,6) + local ll_prefix = ip6tobin("fe80::") + return string.sub(ll_prefix,1,8)..interfier end --- Get an 8-bit integer at a 0-based byte offset in the packet. -- @param index Offset. -- @return An 8-bit integer. function Packet:u8(index) - return u8(self.buf, index) + return u8(self.buf, index) end --- Get a 16-bit integer at a 0-based byte offset in the packet. -- @param index Offset. -- @return A 16-bit integer. function Packet:u16(index) - return u16(self.buf, index) + return u16(self.buf, index) end --- Get a 32-bit integer at a 0-based byte offset in the packet. -- @param index Offset. -- @return An 32-bit integer. function Packet:u32(index) - return u32(self.buf, index) + return u32(self.buf, index) end --- Return part of the packet contents as a byte string. -- @param index The beginning of the part of the packet to extract. The index @@ -631,9 +631,9 @@ end -- the remaining contents from index to the end of the string are returned. -- @return A string. function Packet:raw(index, length) - if not index then index = 0 end - if not length then length = #self.buf-index end - return string.char(string.byte(self.buf, index+1, index+1+length-1)) + if not index then index = 0 end + if not length then length = #self.buf-index end + return string.char(string.byte(self.buf, index+1, index+1+length-1)) end --- Set an 8-bit integer at a 0-based byte offset in the packet. @@ -641,180 +641,180 @@ end -- @param index Offset. -- @param num Integer to store. function Packet:set_u8(index, num) - self.buf = set_u8(self.buf, index, num) - return self.buf + self.buf = set_u8(self.buf, index, num) + return self.buf end --- Set a 16-bit integer at a 0-based byte offset in the packet. -- (big-endian). -- @param index Offset. -- @param num Integer to store. function Packet:set_u16(index, num) - self.buf = set_u16(self.buf, index, num) - return self.buf + self.buf = set_u16(self.buf, index, num) + return self.buf end --- Set a 32-bit integer at a 0-based byte offset in the packet. -- (big-endian). -- @param index Offset. -- @param num Integer to store. function Packet:set_u32(index, num) - self.buf = set_u32(self.buf, index, num) - return self.buf + self.buf = set_u32(self.buf, index, num) + return self.buf end --- Parse an IP packet header. -- @param force_continue Ignored. -- @return Whether the parsing succeeded. function Packet:ip_parse(force_continue) - self.ip_offset = 0 - if #self.buf < 20 then -- too short - print("too short") - return false - end - self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4) - self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset - if self.ip_v ~= 4 then -- not ip - print("not v4") - return false - end - self.ip = true - self.ip_tos = self:u8(self.ip_offset + 1) - self.ip_len = self:u16(self.ip_offset + 2) - self.ip_id = self:u16(self.ip_offset + 4) - self.ip_off = self:u16(self.ip_offset + 6) - self.ip_rf = bit.band(self.ip_off, 0x8000)~=0 -- true/false - self.ip_df = bit.band(self.ip_off, 0x4000)~=0 - self.ip_mf = bit.band(self.ip_off, 0x2000)~=0 - self.ip_off = bit.band(self.ip_off, 0x1FFF) -- fragment offset - self.ip_ttl = self:u8(self.ip_offset + 8) - self.ip_p = self:u8(self.ip_offset + 9) - self.ip_sum = self:u16(self.ip_offset + 10) - self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string - self.ip_bin_dst = self:raw(self.ip_offset + 16,4) - self.ip_src = toip(self.ip_bin_src) -- formatted string - self.ip_dst = toip(self.ip_bin_dst) - self.ip_opt_offset = self.ip_offset + 20 - self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20)) - self.ip_data_offset = self.ip_offset + self.ip_hl*4 - return true + self.ip_offset = 0 + if #self.buf < 20 then -- too short + print("too short") + return false + end + self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4) + self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset + if self.ip_v ~= 4 then -- not ip + print("not v4") + return false + end + self.ip = true + self.ip_tos = self:u8(self.ip_offset + 1) + self.ip_len = self:u16(self.ip_offset + 2) + self.ip_id = self:u16(self.ip_offset + 4) + self.ip_off = self:u16(self.ip_offset + 6) + self.ip_rf = bit.band(self.ip_off, 0x8000)~=0 -- true/false + self.ip_df = bit.band(self.ip_off, 0x4000)~=0 + self.ip_mf = bit.band(self.ip_off, 0x2000)~=0 + self.ip_off = bit.band(self.ip_off, 0x1FFF) -- fragment offset + self.ip_ttl = self:u8(self.ip_offset + 8) + self.ip_p = self:u8(self.ip_offset + 9) + self.ip_sum = self:u16(self.ip_offset + 10) + self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string + self.ip_bin_dst = self:raw(self.ip_offset + 16,4) + self.ip_src = toip(self.ip_bin_src) -- formatted string + self.ip_dst = toip(self.ip_bin_dst) + self.ip_opt_offset = self.ip_offset + 20 + self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20)) + self.ip_data_offset = self.ip_offset + self.ip_hl*4 + return true end --- Parse an IPv6 packet header. -- @param force_continue Ignored. -- @return Whether the parsing succeeded. function Packet:ip6_parse(force_continue) - self.ip6_offset = 0 - if #self.buf < 40 then -- too short - return false - end - self.ip_v = bit.rshift(bit.band(self:u8(self.ip6_offset + 0), 0xF0), 4) - if self.ip_v ~= 6 then -- not ipv6 - return false - end - self.ip6 = true - self.ip6_tc = bit.rshift(bit.band(self:u16(self.ip6_offset + 0), 0x0FF0), 4) - self.ip6_fl = bit.band(self:u8(self.ip6_offset + 1), 0x0F)*65536 + self:u16(self.ip6_offset + 2) - self.ip6_plen = self:u16(self.ip6_offset + 4) - self.ip6_nhdr = self:u8(self.ip6_offset + 6) - self.ip6_hlimt = self:u8(self.ip6_offset + 7) - self.ip_bin_src = self:raw(self.ip6_offset + 8, 16) - self.ip_bin_dst = self:raw(self.ip6_offset + 24, 16) - self.ip_src = toipv6(self.ip_bin_src) - self.ip_dst = toipv6(self.ip_bin_dst) - self.ip6_data_offset = 40 - return true + self.ip6_offset = 0 + if #self.buf < 40 then -- too short + return false + end + self.ip_v = bit.rshift(bit.band(self:u8(self.ip6_offset + 0), 0xF0), 4) + if self.ip_v ~= 6 then -- not ipv6 + return false + end + self.ip6 = true + self.ip6_tc = bit.rshift(bit.band(self:u16(self.ip6_offset + 0), 0x0FF0), 4) + self.ip6_fl = bit.band(self:u8(self.ip6_offset + 1), 0x0F)*65536 + self:u16(self.ip6_offset + 2) + self.ip6_plen = self:u16(self.ip6_offset + 4) + self.ip6_nhdr = self:u8(self.ip6_offset + 6) + self.ip6_hlimt = self:u8(self.ip6_offset + 7) + self.ip_bin_src = self:raw(self.ip6_offset + 8, 16) + self.ip_bin_dst = self:raw(self.ip6_offset + 24, 16) + self.ip_src = toipv6(self.ip_bin_src) + self.ip_dst = toipv6(self.ip_bin_dst) + self.ip6_data_offset = 40 + return true end --- Pare an IPv6 extension header. Just jump over it at the moment. -- @param force_continue Ignored. -- @return Whether the parsing succeeded. function Packet:ipv6_ext_header_parse(force_continue) - local ext_hdr_len = self:u8(self.ip6_data_offset + 1) - ext_hdr_len = ext_hdr_len*8 + 8 - self.ip6_data_offset = self.ip6_data_offset + ext_hdr_len - self.ip6_nhdr = self:u8(self.ip6_data_offset) + local ext_hdr_len = self:u8(self.ip6_data_offset + 1) + ext_hdr_len = ext_hdr_len*8 + 8 + self.ip6_data_offset = self.ip6_data_offset + ext_hdr_len + self.ip6_nhdr = self:u8(self.ip6_data_offset) end --- Set the payload length field. -- @param plen Payload length. function Packet:ip6_set_plen(plen) - self:set_u16(self.ip6_offset + 4, plen) - self.ip6_plen = plen + self:set_u16(self.ip6_offset + 4, plen) + self.ip6_plen = plen end --- Set the header length field. function Packet:ip_set_hl(len) - self:set_u8(self.ip_offset + 0, bit.bor(bit.lshift(self.ip_v, 4), bit.band(len, 0x0F))) - self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4) - self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset + self:set_u8(self.ip_offset + 0, bit.bor(bit.lshift(self.ip_v, 4), bit.band(len, 0x0F))) + self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4) + self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset end --- Set the packet length field. -- @param len Packet length. function Packet:ip_set_len(len) - self:set_u16(self.ip_offset + 2, len) - self.ip_len = len + self:set_u16(self.ip_offset + 2, len) + self.ip_len = len end --- Set the packet identification field. -- @param id packet ID. function Packet:ip_set_id(id) - self:set_u16(self.ip_offset + 4, id) - self.ip_id = id + self:set_u16(self.ip_offset + 4, id) + self.ip_id = id end --- Set the TTL. -- @param ttl TTL. function Packet:ip_set_ttl(ttl) - self:set_u8(self.ip_offset + 8, ttl) - self.ip_ttl = ttl + self:set_u8(self.ip_offset + 8, ttl) + self.ip_ttl = ttl end --- Set the checksum. -- @param checksum Checksum. function Packet:ip_set_checksum(checksum) - self:set_u16(self.ip_offset + 10, checksum) - self.ip_sum = checksum + self:set_u16(self.ip_offset + 10, checksum) + self.ip_sum = checksum end --- Count checksum for packet and save it. function Packet:ip_count_checksum() - self:ip_set_checksum(0) - local csum = in_cksum( self.buf:sub(0, self.ip_offset + self.ip_hl*4) ) - self:ip_set_checksum(csum) + self:ip_set_checksum(0) + local csum = in_cksum( self.buf:sub(0, self.ip_offset + self.ip_hl*4) ) + self:ip_set_checksum(csum) end --- Set the source IP address. -- @param binip The source IP address as a byte string. function Packet:ip_set_bin_src(binip) - local nrip = u32(binip, 0) - self:set_u32(self.ip_offset + 12, nrip) - self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string + local nrip = u32(binip, 0) + self:set_u32(self.ip_offset + 12, nrip) + self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string end --- Set the destination IP address. -- @param binip The destination IP address as a byte string. function Packet:ip_set_bin_dst(binip) - local nrip = u32(binip, 0) - self:set_u32(self.ip_offset + 16, nrip) - self.ip_bin_dst = self:raw(self.ip_offset + 16,4) + local nrip = u32(binip, 0) + self:set_u32(self.ip_offset + 16, nrip) + self.ip_bin_dst = self:raw(self.ip_offset + 16,4) end --- Set the IP options field (and move the data, count new length, -- etc.). -- @param ipoptions IP options. function Packet:ip_set_options(ipoptions) - -- packet = + ipoptions + - local buf = self.buf:sub(0+1,self.ip_offset + 20) .. ipoptions .. self.buf:sub(self.ip_data_offset+1) - self.buf = buf - -- set ip_len - self:ip_set_len(self.buf:len()) - -- set ip_hl - self:ip_set_hl(5 + ipoptions:len()/4) - -- set data offset correctly - self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20)) - self.ip_data_offset = self.ip_offset + self.ip_hl*4 - if self.tcp then - self.tcp_offset = self.ip_data_offset - elseif self.icmp then - self.icmp_offset = self.ip_data_offset - end + -- packet = + ipoptions + + local buf = self.buf:sub(0+1,self.ip_offset + 20) .. ipoptions .. self.buf:sub(self.ip_data_offset+1) + self.buf = buf + -- set ip_len + self:ip_set_len(self.buf:len()) + -- set ip_hl + self:ip_set_hl(5 + ipoptions:len()/4) + -- set data offset correctly + self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20)) + self.ip_data_offset = self.ip_offset + self.ip_hl*4 + if self.tcp then + self.tcp_offset = self.ip_data_offset + elseif self.icmp then + self.icmp_offset = self.ip_data_offset + end end --- Get a short string representation of the IP header. -- @return A string representation of the IP header. function Packet:ip_tostring() - return string.format( - "IP %s -> %s", - self.ip_src, - self.ip_dst) + return string.format( + "IP %s -> %s", + self.ip_src, + self.ip_dst) end --- Parse IP/TCP options into a table. @@ -822,45 +822,45 @@ end -- @param length Length of options. -- @return Table of options. function Packet:parse_options(offset, length) - local options = {} - local op = 1 - local opt_ptr = 0 - while opt_ptr < length do - local t, l, d - options[op] = {} + local options = {} + local op = 1 + local opt_ptr = 0 + while opt_ptr < length do + local t, l, d + options[op] = {} - t = self:u8(offset + opt_ptr) - options[op].type = t - if t==0 or t==1 then - l = 1 - d = nil - else - l = self:u8(offset + opt_ptr + 1) - if l > 2 then - d = self:raw(offset + opt_ptr + 2, l-2) - end - end - options[op].len = l - options[op].data = d - opt_ptr = opt_ptr + l - op = op + 1 - end - return options + t = self:u8(offset + opt_ptr) + options[op].type = t + if t==0 or t==1 then + l = 1 + d = nil + else + l = self:u8(offset + opt_ptr + 1) + if l > 2 then + d = self:raw(offset + opt_ptr + 2, l-2) + end + end + options[op].len = l + options[op].data = d + opt_ptr = opt_ptr + l + op = op + 1 + end + return options end --- Get a short string representation of the packet. -- @return A string representation of the packet. function Packet:tostring() - if self.tcp then - return self:tcp_tostring() - elseif self.udp then - return self:udp_tostring() - elseif self.icmp then - return self:icmp_tostring() - elseif self.ip then - return self:ip_tostring() - end - return "" + if self.tcp then + return self:tcp_tostring() + elseif self.udp then + return self:udp_tostring() + elseif self.icmp then + return self:icmp_tostring() + elseif self.ip then + return self:ip_tostring() + end + return "" end ---------------------------------------------------------------------------------------------------------------- @@ -868,30 +868,30 @@ end -- @param force_continue Ignored. -- @return Whether the parsing succeeded. function Packet:icmp_parse(force_continue) - self.icmp_offset = self.ip_data_offset - if #self.buf < self.icmp_offset + 8 then -- let's say 8 bytes minimum - return false - end - self.icmp = true - self.icmp_type = self:u8(self.icmp_offset + 0) - self.icmp_code = self:u8(self.icmp_offset + 1) - self.icmp_sum = self:u16(self.icmp_offset + 2) + self.icmp_offset = self.ip_data_offset + if #self.buf < self.icmp_offset + 8 then -- let's say 8 bytes minimum + return false + end + self.icmp = true + self.icmp_type = self:u8(self.icmp_offset + 0) + self.icmp_code = self:u8(self.icmp_offset + 1) + self.icmp_sum = self:u16(self.icmp_offset + 2) - if self.icmp_type == 3 or self.icmp_type == 4 or self.icmp_type == 11 or self.icmp_type == 12 then - self.icmp_payload = true - self.icmp_r0 = self:u32(self.icmp_offset + 4) - self.icmp_payload_offset = self.icmp_offset + 8 - if #self.buf < self.icmp_payload_offset + 24 then - return false - end - self.icmp_payload = Packet:new(self.buf:sub(self.icmp_payload_offset+1), self.packet_len - self.icmp_payload_offset, true) - end - return true + if self.icmp_type == 3 or self.icmp_type == 4 or self.icmp_type == 11 or self.icmp_type == 12 then + self.icmp_payload = true + self.icmp_r0 = self:u32(self.icmp_offset + 4) + self.icmp_payload_offset = self.icmp_offset + 8 + if #self.buf < self.icmp_payload_offset + 24 then + return false + end + self.icmp_payload = Packet:new(self.buf:sub(self.icmp_payload_offset+1), self.packet_len - self.icmp_payload_offset, true) + end + return true end --- Get a short string representation of the ICMP header. -- @return A string representation of the ICMP header. function Packet:icmp_tostring() - return self:ip_tostring() .. " ICMP(" .. self.icmp_payload:tostring() .. ")" + return self:ip_tostring() .. " ICMP(" .. self.icmp_payload:tostring() .. ")" end ---------------------------------------------------------------------------------------------------------------- @@ -899,18 +899,18 @@ end -- @param force_continue Ignored. -- @return Whether the parsing succeeded. function Packet:icmpv6_parse(force_continue) - self.icmpv6_offset = self.ip6_data_offset - if #self.buf < self.icmpv6_offset + 8 then -- let's say 8 bytes minimum - return false - end - self.icmpv6 = true - self.icmpv6_type = self:u8(self.icmpv6_offset + 0) - self.icmpv6_code = self:u8(self.icmpv6_offset + 1) + self.icmpv6_offset = self.ip6_data_offset + if #self.buf < self.icmpv6_offset + 8 then -- let's say 8 bytes minimum + return false + end + self.icmpv6 = true + self.icmpv6_type = self:u8(self.icmpv6_offset + 0) + self.icmpv6_code = self:u8(self.icmpv6_offset + 1) - if self.icmpv6_type == ND_NEIGHBOR_SOLICIT then - self.ns_target = self:raw(self.icmpv6_offset + 8, 16) - end - return true + if self.icmpv6_type == ND_NEIGHBOR_SOLICIT then + self.ns_target = self:raw(self.icmpv6_offset + 8, 16) + end + return true end ---------------------------------------------------------------------------------------------------------------- @@ -918,190 +918,190 @@ end -- @param force_continue Whether a short packet causes parsing to fail. -- @return Whether the parsing succeeded. function Packet:tcp_parse(force_continue) - self.tcp = true - self.tcp_offset = self.ip_data_offset or self.ip6_data_offset - if #self.buf < self.tcp_offset + 4 then - return false - end - self.tcp_sport = self:u16(self.tcp_offset + 0) - self.tcp_dport = self:u16(self.tcp_offset + 2) - if #self.buf < self.tcp_offset + 20 then - if force_continue then - return true - else - return false - end - end - self.tcp_seq = self:u32(self.tcp_offset + 4) - self.tcp_ack = self:u32(self.tcp_offset + 8) - self.tcp_hl = bit.rshift(bit.band(self:u8(self.tcp_offset+12), 0xF0), 4) -- header_length or data_offset - self.tcp_x2 = bit.band(self:u8(self.tcp_offset+12), 0x0F) - self.tcp_flags = self:u8(self.tcp_offset + 13) - self.tcp_th_fin = bit.band(self.tcp_flags, 0x01)~=0 -- true/false - self.tcp_th_syn = bit.band(self.tcp_flags, 0x02)~=0 - self.tcp_th_rst = bit.band(self.tcp_flags, 0x04)~=0 - self.tcp_th_push = bit.band(self.tcp_flags, 0x08)~=0 - self.tcp_th_ack = bit.band(self.tcp_flags, 0x10)~=0 - self.tcp_th_urg = bit.band(self.tcp_flags, 0x20)~=0 - self.tcp_th_ece = bit.band(self.tcp_flags, 0x40)~=0 - self.tcp_th_cwr = bit.band(self.tcp_flags, 0x80)~=0 - self.tcp_win = self:u16(self.tcp_offset + 14) - self.tcp_sum = self:u16(self.tcp_offset + 16) - self.tcp_urp = self:u16(self.tcp_offset + 18) - self.tcp_opt_offset = self.tcp_offset + 20 - self.tcp_options = self:parse_options(self.tcp_opt_offset, ((self.tcp_hl*4)-20)) - self.tcp_data_offset = self.tcp_offset + self.tcp_hl*4 + self.tcp = true + self.tcp_offset = self.ip_data_offset or self.ip6_data_offset + if #self.buf < self.tcp_offset + 4 then + return false + end + self.tcp_sport = self:u16(self.tcp_offset + 0) + self.tcp_dport = self:u16(self.tcp_offset + 2) + if #self.buf < self.tcp_offset + 20 then + if force_continue then + return true + else + return false + end + end + self.tcp_seq = self:u32(self.tcp_offset + 4) + self.tcp_ack = self:u32(self.tcp_offset + 8) + self.tcp_hl = bit.rshift(bit.band(self:u8(self.tcp_offset+12), 0xF0), 4) -- header_length or data_offset + self.tcp_x2 = bit.band(self:u8(self.tcp_offset+12), 0x0F) + self.tcp_flags = self:u8(self.tcp_offset + 13) + self.tcp_th_fin = bit.band(self.tcp_flags, 0x01)~=0 -- true/false + self.tcp_th_syn = bit.band(self.tcp_flags, 0x02)~=0 + self.tcp_th_rst = bit.band(self.tcp_flags, 0x04)~=0 + self.tcp_th_push = bit.band(self.tcp_flags, 0x08)~=0 + self.tcp_th_ack = bit.band(self.tcp_flags, 0x10)~=0 + self.tcp_th_urg = bit.band(self.tcp_flags, 0x20)~=0 + self.tcp_th_ece = bit.band(self.tcp_flags, 0x40)~=0 + self.tcp_th_cwr = bit.band(self.tcp_flags, 0x80)~=0 + self.tcp_win = self:u16(self.tcp_offset + 14) + self.tcp_sum = self:u16(self.tcp_offset + 16) + self.tcp_urp = self:u16(self.tcp_offset + 18) + self.tcp_opt_offset = self.tcp_offset + 20 + self.tcp_options = self:parse_options(self.tcp_opt_offset, ((self.tcp_hl*4)-20)) + self.tcp_data_offset = self.tcp_offset + self.tcp_hl*4 - if self.ip_len then - self.tcp_data_length = self.ip_len - self.tcp_offset - self.tcp_hl*4 - else - self.tcp_data_length = self.ip6_plen - self.tcp_hl*4 - end - self:tcp_parse_options() - return true + if self.ip_len then + self.tcp_data_length = self.ip_len - self.tcp_offset - self.tcp_hl*4 + else + self.tcp_data_length = self.ip6_plen - self.tcp_hl*4 + end + self:tcp_parse_options() + return true end --- Get a short string representation of the TCP packet. -- @return A string representation of the TCP header. function Packet:tcp_tostring() - return string.format( - "TCP %s:%i -> %s:%i", - self.ip_src, self.tcp_sport, - self.ip_dst, self.tcp_dport - ) + return string.format( + "TCP %s:%i -> %s:%i", + self.ip_src, self.tcp_sport, + self.ip_dst, self.tcp_dport + ) end --- Parse options for TCP header. function Packet:tcp_parse_options() - local eoo = false - for _,opt in ipairs(self.tcp_options) do - if eoo then - self.tcp_opt_after_eol = true - end + local eoo = false + for _,opt in ipairs(self.tcp_options) do + if eoo then + self.tcp_opt_after_eol = true + end - if opt.type == 0 then -- end of options - eoo = true - elseif opt.type == 2 then -- MSS - self.tcp_opt_mss = u16(opt.data, 0) - self.tcp_opt_mtu = self.tcp_opt_mss + 40 - elseif opt.type == 3 then -- widow scaling - self.tcp_opt_ws = u8(opt.data, 0) - elseif opt.type == 8 then -- timestamp - self.tcp_opt_t1 = u32(opt.data, 0) - self.tcp_opt_t2 = u32(opt.data, 4) - end - end + if opt.type == 0 then -- end of options + eoo = true + elseif opt.type == 2 then -- MSS + self.tcp_opt_mss = u16(opt.data, 0) + self.tcp_opt_mtu = self.tcp_opt_mss + 40 + elseif opt.type == 3 then -- widow scaling + self.tcp_opt_ws = u8(opt.data, 0) + elseif opt.type == 8 then -- timestamp + self.tcp_opt_t1 = u32(opt.data, 0) + self.tcp_opt_t2 = u32(opt.data, 4) + end + end end --- Set the TCP source port. -- @param port Source port. function Packet:tcp_set_sport(port) - self:set_u16(self.tcp_offset + 0, port) - self.tcp_sport = port + self:set_u16(self.tcp_offset + 0, port) + self.tcp_sport = port end --- Set the TCP destination port. -- @param port Destination port. function Packet:tcp_set_dport(port) - self:set_u16(self.tcp_offset + 2, port) - self.tcp_dport = port + self:set_u16(self.tcp_offset + 2, port) + self.tcp_dport = port end --- Set the TCP sequence field. -- @param new_seq Sequence. function Packet:tcp_set_seq(new_seq) - self:set_u32(self.tcp_offset + 4, new_seq) - self.tcp_seq = new_seq + self:set_u32(self.tcp_offset + 4, new_seq) + self.tcp_seq = new_seq end --- Set the TCP flags field (like SYN, ACK, RST). -- @param new_flags Flags, represented as an 8-bit number. function Packet:tcp_set_flags(new_flags) - self:set_u8(self.tcp_offset + 13, new_flags) - self.tcp_flags = new_flags + self:set_u8(self.tcp_offset + 13, new_flags) + self.tcp_flags = new_flags end --- Set the urgent pointer field. -- @param urg_ptr Urgent pointer. function Packet:tcp_set_urp(urg_ptr) - self:set_u16(self.tcp_offset + 18, urg_ptr) - self.tcp_urp = urg_ptr + self:set_u16(self.tcp_offset + 18, urg_ptr) + self.tcp_urp = urg_ptr end --- Set the TCP checksum field. -- @param checksum Checksum. function Packet:tcp_set_checksum(checksum) - self:set_u16(self.tcp_offset + 16, checksum) - self.tcp_sum = checksum + self:set_u16(self.tcp_offset + 16, checksum) + self.tcp_sum = checksum end --- Count and save the TCP checksum field. function Packet:tcp_count_checksum() - self:tcp_set_checksum(0) - local proto = self.ip_p - local length = self.buf:len() - self.tcp_offset - local b = self.ip_bin_src .. - self.ip_bin_dst .. - string.char(0) .. - string.char(proto) .. - set_u16("..", 0, length) .. - self.buf:sub(self.tcp_offset+1) + self:tcp_set_checksum(0) + local proto = self.ip_p + local length = self.buf:len() - self.tcp_offset + local b = self.ip_bin_src .. + self.ip_bin_dst .. + string.char(0) .. + string.char(proto) .. + set_u16("..", 0, length) .. + self.buf:sub(self.tcp_offset+1) - self:tcp_set_checksum(in_cksum(b)) + self:tcp_set_checksum(in_cksum(b)) end --- Map an MTU to a link type string. Stolen from p0f. -- @return A string describing the link type. function Packet:tcp_lookup_link() - local mtu_def = { - {["mtu"]=256, ["txt"]= "radio modem"}, - {["mtu"]=386, ["txt"]= "ethernut"}, - {["mtu"]=552, ["txt"]= "SLIP line / encap ppp"}, - {["mtu"]=576, ["txt"]= "sometimes modem"}, - {["mtu"]=1280, ["txt"]= "gif tunnel"}, - {["mtu"]=1300, ["txt"]= "PIX, SMC, sometimes wireless"}, - {["mtu"]=1362, ["txt"]= "sometimes DSL (1)"}, - {["mtu"]=1372, ["txt"]= "cable modem"}, - {["mtu"]=1400, ["txt"]= "(Google/AOL)"}, - {["mtu"]=1415, ["txt"]= "sometimes wireless"}, - {["mtu"]=1420, ["txt"]= "GPRS, T1, FreeS/WAN"}, - {["mtu"]=1423, ["txt"]= "sometimes cable"}, - {["mtu"]=1440, ["txt"]= "sometimes DSL (2)"}, - {["mtu"]=1442, ["txt"]= "IPIP tunnel"}, - {["mtu"]=1450, ["txt"]= "vtun"}, - {["mtu"]=1452, ["txt"]= "sometimes DSL (3)"}, - {["mtu"]=1454, ["txt"]= "sometimes DSL (4)"}, - {["mtu"]=1456, ["txt"]= "ISDN ppp"}, - {["mtu"]=1458, ["txt"]= "BT DSL (?)"}, - {["mtu"]=1462, ["txt"]= "sometimes DSL (5)"}, - {["mtu"]=1470, ["txt"]= "(Google 2)"}, - {["mtu"]=1476, ["txt"]= "IPSec/GRE"}, - {["mtu"]=1480, ["txt"]= "IPv6/IPIP"}, - {["mtu"]=1492, ["txt"]= "pppoe (DSL)"}, - {["mtu"]=1496, ["txt"]= "vLAN"}, - {["mtu"]=1500, ["txt"]= "ethernet/modem"}, - {["mtu"]=1656, ["txt"]= "Ericsson HIS"}, - {["mtu"]=2024, ["txt"]= "wireless/IrDA"}, - {["mtu"]=2048, ["txt"]= "Cyclom X.25 WAN"}, - {["mtu"]=2250, ["txt"]= "AiroNet wireless"}, - {["mtu"]=3924, ["txt"]= "loopback"}, - {["mtu"]=4056, ["txt"]= "token ring (1)"}, - {["mtu"]=4096, ["txt"]= "Sangoma X.25 WAN"}, - {["mtu"]=4352, ["txt"]= "FDDI"}, - {["mtu"]=4500, ["txt"]= "token ring (2)"}, - {["mtu"]=9180, ["txt"]= "FORE ATM"}, - {["mtu"]=16384, ["txt"]= "sometimes loopback (1)"}, - {["mtu"]=16436, ["txt"]= "sometimes loopback (2)"}, - {["mtu"]=18000, ["txt"]= "token ring x4"}, - } - if not self.tcp_opt_mss or self.tcp_opt_mss==0 then - return "unspecified" - end - for _,x in ipairs(mtu_def) do - local mtu = x["mtu"] - local txt = x["txt"] - if self.tcp_opt_mtu == mtu then - return txt - end - if self.tcp_opt_mtu < mtu then - return string.format("unknown-%i", self.tcp_opt_mtu) - end - end - return string.format("unknown-%i", self.tcp_opt_mtu) + local mtu_def = { + {["mtu"]=256, ["txt"]= "radio modem"}, + {["mtu"]=386, ["txt"]= "ethernut"}, + {["mtu"]=552, ["txt"]= "SLIP line / encap ppp"}, + {["mtu"]=576, ["txt"]= "sometimes modem"}, + {["mtu"]=1280, ["txt"]= "gif tunnel"}, + {["mtu"]=1300, ["txt"]= "PIX, SMC, sometimes wireless"}, + {["mtu"]=1362, ["txt"]= "sometimes DSL (1)"}, + {["mtu"]=1372, ["txt"]= "cable modem"}, + {["mtu"]=1400, ["txt"]= "(Google/AOL)"}, + {["mtu"]=1415, ["txt"]= "sometimes wireless"}, + {["mtu"]=1420, ["txt"]= "GPRS, T1, FreeS/WAN"}, + {["mtu"]=1423, ["txt"]= "sometimes cable"}, + {["mtu"]=1440, ["txt"]= "sometimes DSL (2)"}, + {["mtu"]=1442, ["txt"]= "IPIP tunnel"}, + {["mtu"]=1450, ["txt"]= "vtun"}, + {["mtu"]=1452, ["txt"]= "sometimes DSL (3)"}, + {["mtu"]=1454, ["txt"]= "sometimes DSL (4)"}, + {["mtu"]=1456, ["txt"]= "ISDN ppp"}, + {["mtu"]=1458, ["txt"]= "BT DSL (?)"}, + {["mtu"]=1462, ["txt"]= "sometimes DSL (5)"}, + {["mtu"]=1470, ["txt"]= "(Google 2)"}, + {["mtu"]=1476, ["txt"]= "IPSec/GRE"}, + {["mtu"]=1480, ["txt"]= "IPv6/IPIP"}, + {["mtu"]=1492, ["txt"]= "pppoe (DSL)"}, + {["mtu"]=1496, ["txt"]= "vLAN"}, + {["mtu"]=1500, ["txt"]= "ethernet/modem"}, + {["mtu"]=1656, ["txt"]= "Ericsson HIS"}, + {["mtu"]=2024, ["txt"]= "wireless/IrDA"}, + {["mtu"]=2048, ["txt"]= "Cyclom X.25 WAN"}, + {["mtu"]=2250, ["txt"]= "AiroNet wireless"}, + {["mtu"]=3924, ["txt"]= "loopback"}, + {["mtu"]=4056, ["txt"]= "token ring (1)"}, + {["mtu"]=4096, ["txt"]= "Sangoma X.25 WAN"}, + {["mtu"]=4352, ["txt"]= "FDDI"}, + {["mtu"]=4500, ["txt"]= "token ring (2)"}, + {["mtu"]=9180, ["txt"]= "FORE ATM"}, + {["mtu"]=16384, ["txt"]= "sometimes loopback (1)"}, + {["mtu"]=16436, ["txt"]= "sometimes loopback (2)"}, + {["mtu"]=18000, ["txt"]= "token ring x4"}, + } + if not self.tcp_opt_mss or self.tcp_opt_mss==0 then + return "unspecified" + end + for _,x in ipairs(mtu_def) do + local mtu = x["mtu"] + local txt = x["txt"] + if self.tcp_opt_mtu == mtu then + return txt + end + if self.tcp_opt_mtu < mtu then + return string.format("unknown-%i", self.tcp_opt_mtu) + end + end + return string.format("unknown-%i", self.tcp_opt_mtu) end ---------------------------------------------------------------------------------------------------------------- @@ -1109,78 +1109,78 @@ end -- @param force_continue Whether a short packet causes parsing to fail. -- @return Whether the parsing succeeded. function Packet:udp_parse(force_continue) - self.udp = true - self.udp_offset = self.ip_data_offset or self.ip6_data_offset - if #self.buf < self.udp_offset + 4 then - return false - end - self.udp_sport = self:u16(self.udp_offset + 0) - self.udp_dport = self:u16(self.udp_offset + 2) - if #self.buf < self.udp_offset + 8 then - if force_continue then - return true - else - return false - end - end - self.udp_len = self:u16(self.udp_offset + 4) - self.udp_sum = self:u16(self.udp_offset + 6) + self.udp = true + self.udp_offset = self.ip_data_offset or self.ip6_data_offset + if #self.buf < self.udp_offset + 4 then + return false + end + self.udp_sport = self:u16(self.udp_offset + 0) + self.udp_dport = self:u16(self.udp_offset + 2) + if #self.buf < self.udp_offset + 8 then + if force_continue then + return true + else + return false + end + end + self.udp_len = self:u16(self.udp_offset + 4) + self.udp_sum = self:u16(self.udp_offset + 6) - return true + return true end --- Get a short string representation of the UDP packet. -- @return A string representation of the UDP header. function Packet:udp_tostring() - return string.format( - "UDP %s:%i -> %s:%i", - self.ip_src, self.udp_sport, - self.ip_dst, self.udp_dport - ) + return string.format( + "UDP %s:%i -> %s:%i", + self.ip_src, self.udp_sport, + self.ip_dst, self.udp_dport + ) end --- -- Set the UDP source port. -- @param port Source port. function Packet:udp_set_sport(port) - self:set_u16(self.udp_offset + 0, port) - self.udp_sport = port + self:set_u16(self.udp_offset + 0, port) + self.udp_sport = port end --- -- Set the UDP destination port. -- @param port Destination port. function Packet:udp_set_dport(port) - self:set_u16(self.udp_offset + 2, port) - self.udp_dport = port + self:set_u16(self.udp_offset + 2, port) + self.udp_dport = port end --- -- Set the UDP payload length. -- @param len UDP payload length. function Packet:udp_set_length(len) - self:set_u16(self.udp_offset + 4, len) - self.udp_len = len + self:set_u16(self.udp_offset + 4, len) + self.udp_len = len end --- -- Set the UDP checksum field. -- @param checksum Checksum. function Packet:udp_set_checksum(checksum) - self:set_u16(self.udp_offset + 6, checksum) - self.udp_sum = checksum + self:set_u16(self.udp_offset + 6, checksum) + self.udp_sum = checksum end --- -- Count and save the UDP checksum field. function Packet:udp_count_checksum() - self:udp_set_checksum(0) - local proto = self.ip_p - local length = self.buf:len() - self.udp_offset - local b = self.ip_bin_src .. - self.ip_bin_dst .. - string.char(0) .. - string.char(proto) .. - set_u16("..", 0, length) .. - self.buf:sub(self.udp_offset+1) + self:udp_set_checksum(0) + local proto = self.ip_p + local length = self.buf:len() - self.udp_offset + local b = self.ip_bin_src .. + self.ip_bin_dst .. + string.char(0) .. + string.char(proto) .. + set_u16("..", 0, length) .. + self.buf:sub(self.udp_offset+1) - self:udp_set_checksum(in_cksum(b)) + self:udp_set_checksum(in_cksum(b)) end diff --git a/nselib/pgsql.lua b/nselib/pgsql.lua index ac6181755..183c8b024 100644 --- a/nselib/pgsql.lua +++ b/nselib/pgsql.lua @@ -28,533 +28,533 @@ _ENV = stdnse.module("pgsql", stdnse.seeall) --- Supported pgsql message types MessageType = { - Error = 0x45, - BackendKeyData = 0x4b, - AuthRequest=0x52, - ParameterStatus = 0x53, - ReadyForQuery = 0x5a, - PasswordMessage = 0x70, + Error = 0x45, + BackendKeyData = 0x4b, + AuthRequest=0x52, + ParameterStatus = 0x53, + ReadyForQuery = 0x5a, + PasswordMessage = 0x70, } --- Supported authentication types AuthenticationType = { - Success = 0x00, - Plain = 0x03, - MD5 = 0x05 + Success = 0x00, + Plain = 0x03, + MD5 = 0x05 } -- Version 2 of the protocol v2 = { - --- Pad a string with zeroes - -- - -- @param str string containing the string to be padded - -- @param len number containing the wanted length - -- @return string containing the padded string value - zeroPad = function(str, len) - local padding = len - str:len() + --- Pad a string with zeroes + -- + -- @param str string containing the string to be padded + -- @param len number containing the wanted length + -- @return string containing the padded string value + zeroPad = function(str, len) + local padding = len - str:len() - if ( padding < 0 ) then - return str - end - for i=1,padding do - str = str .. string.char(0x00) - end - return str - end, + if ( padding < 0 ) then + return str + end + for i=1,padding do + str = str .. string.char(0x00) + end + return str + end, - messageDecoder = { + messageDecoder = { - --- Decodes an Auth Request packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding, -1 on error - -- @return response table containing zero or more of the following salt and success - -- error string containing error message if pos is -1 - [MessageType.AuthRequest] = function( data, len, pos ) - local _, authtype - local response = {} - pos, authtype = bin.unpack(">I", data, pos) + --- Decodes an Auth Request packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding, -1 on error + -- @return response table containing zero or more of the following salt and success + -- error string containing error message if pos is -1 + [MessageType.AuthRequest] = function( data, len, pos ) + local _, authtype + local response = {} + pos, authtype = bin.unpack(">I", data, pos) - if ( authtype == AuthenticationType.MD5 ) then - if ( len - pos + 1 ) < 3 then - return -1, "ERROR: Malformed AuthRequest received" - end - pos, response.salt = bin.unpack("A4", data, pos) - elseif ( authtype == AuthenticationType.Plain ) then - --do nothing - elseif ( authtype == 0 ) then - response.success = true - else - stdnse.print_debug( ("unknown auth type: %d"):format(authtype) ) - end + if ( authtype == AuthenticationType.MD5 ) then + if ( len - pos + 1 ) < 3 then + return -1, "ERROR: Malformed AuthRequest received" + end + pos, response.salt = bin.unpack("A4", data, pos) + elseif ( authtype == AuthenticationType.Plain ) then + --do nothing + elseif ( authtype == 0 ) then + response.success = true + else + stdnse.print_debug( ("unknown auth type: %d"):format(authtype) ) + end - response.authtype = authtype - return pos, response - end, + response.authtype = authtype + return pos, response + end, - --- Decodes an Error packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding - -- @return response table containing zero or more of the following error.severity, - -- error.code, error.message, error.file, - -- error.line and error.routine - [MessageType.Error] = function( data, len, pos ) - local tmp = data:sub(pos, pos + len - 4) - local response = {} - local pos_end = pos + len + --- Decodes an Error packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding + -- @return response table containing zero or more of the following error.severity, + -- error.code, error.message, error.file, + -- error.line and error.routine + [MessageType.Error] = function( data, len, pos ) + local tmp = data:sub(pos, pos + len - 4) + local response = {} + local pos_end = pos + len - response.error = {} - pos, response.error.message = bin.unpack("z", data, pos) - return pos, response - end, + response.error = {} + pos, response.error.message = bin.unpack("z", data, pos) + return pos, response + end, - }, + }, - --- Process the server response - -- - -- @param data string containing the server response - -- @param pos number containing the offset into the data buffer - processResponse = function(data, pos) - local ptype, len, status, response - local pos = pos or 1 + --- Process the server response + -- + -- @param data string containing the server response + -- @param pos number containing the offset into the data buffer + processResponse = function(data, pos) + local ptype, len, status, response + local pos = pos or 1 - pos, ptype = bin.unpack("C", data, pos) - len = data:len() - 1 + pos, ptype = bin.unpack("C", data, pos) + len = data:len() - 1 - if v2.messageDecoder[ptype] then - pos, response = v2.messageDecoder[ptype](data, len, pos) + if v2.messageDecoder[ptype] then + pos, response = v2.messageDecoder[ptype](data, len, pos) - if pos ~= -1 then - response.type = ptype - return pos, response - end - else - stdnse.print_debug( ("Missing decoder for %d"):format(ptype) ) - return -1, ("Missing decoder for %d"):format(ptype) - end - return -1, "Decoding failed" - end, + if pos ~= -1 then + response.type = ptype + return pos, response + end + else + stdnse.print_debug( ("Missing decoder for %d"):format(ptype) ) + return -1, ("Missing decoder for %d"):format(ptype) + end + return -1, "Decoding failed" + end, - --- Reads a packet and handles additional socket reads to retrieve remaining data - -- - -- @param socket socket already connected to the pgsql server - -- @param data string containing any data already retrieved from the socket - -- @param pos number containing the offset into the data buffer - -- @return data string containing the initial and any additional data - readPacket=function(socket, data, pos) + --- Reads a packet and handles additional socket reads to retrieve remaining data + -- + -- @param socket socket already connected to the pgsql server + -- @param data string containing any data already retrieved from the socket + -- @param pos number containing the offset into the data buffer + -- @return data string containing the initial and any additional data + readPacket=function(socket, data, pos) - local pos = pos or 1 - local data = data or "" - local status = true - local tmp = "" - local ptype, len + local pos = pos or 1 + local data = data or "" + local status = true + local tmp = "" + local ptype, len - local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end - local try = nmap.new_try(catch) + local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end + local try = nmap.new_try(catch) - if ( data == nil or data:len() == 0 ) then - data = try(socket:receive()) - end - return data - end, + if ( data == nil or data:len() == 0 ) then + data = try(socket:receive()) + end + return data + end, - --- Sends a startup message to the server containing the username and database to connect to - -- - -- @param socket socket already connected to the pgsql server - -- @param user string containing the name of the user - -- @param database string containing the name of the database - -- @return status true on success, false on failure - -- @return table containing a processed response from processResponse - -- string containing error message if status is false - sendStartup=function(socket, user, database) - local data, response, status, pos - local proto_ver, ptype, _, tmp + --- Sends a startup message to the server containing the username and database to connect to + -- + -- @param socket socket already connected to the pgsql server + -- @param user string containing the name of the user + -- @param database string containing the name of the database + -- @return status true on success, false on failure + -- @return table containing a processed response from processResponse + -- string containing error message if status is false + sendStartup=function(socket, user, database) + local data, response, status, pos + local proto_ver, ptype, _, tmp - local tty, unused, args = "", "", "" - proto_ver = 0x0020000 - user = v2.zeroPad(user, 32) - database = v2.zeroPad(database, 64) - data = bin.pack(">I>IAAAAA", 296, proto_ver, database, user, v2.zeroPad(args, 64), v2.zeroPad(unused, 64), v2.zeroPad(tty,64) ) + local tty, unused, args = "", "", "" + proto_ver = 0x0020000 + user = v2.zeroPad(user, 32) + database = v2.zeroPad(database, 64) + data = bin.pack(">I>IAAAAA", 296, proto_ver, database, user, v2.zeroPad(args, 64), v2.zeroPad(unused, 64), v2.zeroPad(tty,64) ) - socket:send( data ) + socket:send( data ) - -- attempt to verify version - status, data = socket:receive_bytes( 1 ) + -- attempt to verify version + status, data = socket:receive_bytes( 1 ) - if ( not(status) ) then - return false, "sendStartup failed" - end + if ( not(status) ) then + return false, "sendStartup failed" + end - data = v2.readPacket(socket, data ) - pos, response = v2.processResponse( data ) + data = v2.readPacket(socket, data ) + pos, response = v2.processResponse( data ) - if ( pos < 0 or response.type == MessageType.Error) then - return false, response.error.message or "unknown error" - end + if ( pos < 0 or response.type == MessageType.Error) then + return false, response.error.message or "unknown error" + end - return true, response - end, + return true, response + end, - --- Attempts to authenticate to the pgsql server - -- Supports plain-text and MD5 authentication - -- - -- @param socket socket already connected to the pgsql server - -- @param params table containing any additional parameters authtype, version - -- @param username string containing the username to use for authentication - -- @param password string containing the password to use for authentication - -- @param salt string containing the crypthographic salt value - -- @return status true on success, false on failure - -- @return result table containing parameter status information, - -- result string containing an error message if login fails - loginRequest = function ( socket, params, username, password, salt ) + --- Attempts to authenticate to the pgsql server + -- Supports plain-text and MD5 authentication + -- + -- @param socket socket already connected to the pgsql server + -- @param params table containing any additional parameters authtype, version + -- @param username string containing the username to use for authentication + -- @param password string containing the password to use for authentication + -- @param salt string containing the crypthographic salt value + -- @return status true on success, false on failure + -- @return result table containing parameter status information, + -- result string containing an error message if login fails + loginRequest = function ( socket, params, username, password, salt ) - local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end - local try = nmap.new_try(catch) - local response = {} - local status, data, len, pos, tmp + local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end + local try = nmap.new_try(catch) + local response = {} + local status, data, len, pos, tmp - if ( params.authtype == AuthenticationType.MD5 ) then - local hash = createMD5LoginHash(username,password,salt) - data = bin.pack( ">Iz", 40, hash) - try( socket:send( data ) ) - elseif ( params.authtype == AuthenticationType.Plain ) then - local data - data = bin.pack(">Iz", password:len() + 4, password) - try( socket:send( data ) ) - elseif ( params.authtype == AuthenticationType.Success ) then - return true, nil - end + if ( params.authtype == AuthenticationType.MD5 ) then + local hash = createMD5LoginHash(username,password,salt) + data = bin.pack( ">Iz", 40, hash) + try( socket:send( data ) ) + elseif ( params.authtype == AuthenticationType.Plain ) then + local data + data = bin.pack(">Iz", password:len() + 4, password) + try( socket:send( data ) ) + elseif ( params.authtype == AuthenticationType.Success ) then + return true, nil + end - data, response.params = "", {} + data, response.params = "", {} - data = v2.readPacket(socket, data, 1) - pos, tmp = v2.processResponse(data, 1) + data = v2.readPacket(socket, data, 1) + pos, tmp = v2.processResponse(data, 1) - -- this should contain the AuthRequest packet - if tmp.type ~= MessageType.AuthRequest then - return false, "Expected AuthRequest got something else" - end + -- this should contain the AuthRequest packet + if tmp.type ~= MessageType.AuthRequest then + return false, "Expected AuthRequest got something else" + end - if not tmp.success then - return false, "Login failure" - end + if not tmp.success then + return false, "Login failure" + end - return true, response - end, + return true, response + end, } -- Version 3 of the protocol v3 = { - messageDecoder = { + messageDecoder = { - --- Decodes an Auth Request packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding, -1 on error - -- @return response table containing zero or more of the following salt and success - -- error string containing error message if pos is -1 - [MessageType.AuthRequest] = function( data, len, pos ) - local _, authtype - local response = {} + --- Decodes an Auth Request packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding, -1 on error + -- @return response table containing zero or more of the following salt and success + -- error string containing error message if pos is -1 + [MessageType.AuthRequest] = function( data, len, pos ) + local _, authtype + local response = {} - pos, authtype = bin.unpack(">I", data, pos) + pos, authtype = bin.unpack(">I", data, pos) - if ( authtype == AuthenticationType.MD5 ) then - if ( len - pos + 1 ) < 3 then - return -1, "ERROR: Malformed AuthRequest received" - end - pos, response.salt = bin.unpack("A4", data, pos) - elseif ( authtype == AuthenticationType.Plain ) then - --do nothing - elseif ( authtype == 0 ) then - response.success = true - else - stdnse.print_debug( "unknown auth type: %d", authtype ) - end + if ( authtype == AuthenticationType.MD5 ) then + if ( len - pos + 1 ) < 3 then + return -1, "ERROR: Malformed AuthRequest received" + end + pos, response.salt = bin.unpack("A4", data, pos) + elseif ( authtype == AuthenticationType.Plain ) then + --do nothing + elseif ( authtype == 0 ) then + response.success = true + else + stdnse.print_debug( "unknown auth type: %d", authtype ) + end - response.authtype = authtype - return pos, response - end, + response.authtype = authtype + return pos, response + end, - --- Decodes an ParameterStatus packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding - -- @return response table containing zero or more of the following key and value - [MessageType.ParameterStatus] = function( data, len, pos ) - local tmp, _ - local response = {} + --- Decodes an ParameterStatus packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding + -- @return response table containing zero or more of the following key and value + [MessageType.ParameterStatus] = function( data, len, pos ) + local tmp, _ + local response = {} - tmp = data:sub(pos, pos + len - 4) - _, response.key, response.value = bin.unpack("zz", tmp) - return pos + len - 4, response - end, + tmp = data:sub(pos, pos + len - 4) + _, response.key, response.value = bin.unpack("zz", tmp) + return pos + len - 4, response + end, - --- Decodes an Error packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding - -- @return response table containing zero or more of the following error.severity, - -- error.code, error.message, error.file, - -- error.line and error.routine - [MessageType.Error] = function( data, len, pos ) - local tmp = data:sub(pos, pos + len - 4) - local _, value, prefix - local response = {} - local pos_end = pos + len + --- Decodes an Error packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding + -- @return response table containing zero or more of the following error.severity, + -- error.code, error.message, error.file, + -- error.line and error.routine + [MessageType.Error] = function( data, len, pos ) + local tmp = data:sub(pos, pos + len - 4) + local _, value, prefix + local response = {} + local pos_end = pos + len - response.error = {} + response.error = {} - while ( pos < pos_end - 5 ) do - pos, prefix, value = bin.unpack("Az", data, pos) + while ( pos < pos_end - 5 ) do + pos, prefix, value = bin.unpack("Az", data, pos) - if prefix == 'S' then - response.error.severity = value - elseif prefix == 'C' then - response.error.code = value - elseif prefix == 'M' then - response.error.message = value - elseif prefix == 'F' then - response.error.file = value - elseif prefix == 'L' then - response.error.line = value - elseif prefix == 'R' then - response.error.routine = value - end - end - return pos, response - end, + if prefix == 'S' then + response.error.severity = value + elseif prefix == 'C' then + response.error.code = value + elseif prefix == 'M' then + response.error.message = value + elseif prefix == 'F' then + response.error.file = value + elseif prefix == 'L' then + response.error.line = value + elseif prefix == 'R' then + response.error.routine = value + end + end + return pos, response + end, - --- Decodes the BackendKeyData packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding, -1 on error - -- @return response table containing zero or more of the following pid and key - -- error string containing error message if pos is -1 - [MessageType.BackendKeyData] = function( data, len, pos ) - local response = {} + --- Decodes the BackendKeyData packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding, -1 on error + -- @return response table containing zero or more of the following pid and key + -- error string containing error message if pos is -1 + [MessageType.BackendKeyData] = function( data, len, pos ) + local response = {} - if len ~= 12 then - return -1, "ERROR: Invalid BackendKeyData packet" - end + if len ~= 12 then + return -1, "ERROR: Invalid BackendKeyData packet" + end - pos, response.pid, response.key = bin.unpack(">I>I", data, pos) - return pos, response - end, + pos, response.pid, response.key = bin.unpack(">I>I", data, pos) + return pos, response + end, - --- Decodes an ReadyForQuery packet - -- - -- @param data string containing raw data recieved from socket - -- @param len number containing the length as retrieved from the header - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding, -1 on error - -- @return response table containing zero or more of the following status - -- error string containing error message if pos is -1 - [MessageType.ReadyForQuery] = function( data, len, pos ) - local response = {} + --- Decodes an ReadyForQuery packet + -- + -- @param data string containing raw data recieved from socket + -- @param len number containing the length as retrieved from the header + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding, -1 on error + -- @return response table containing zero or more of the following status + -- error string containing error message if pos is -1 + [MessageType.ReadyForQuery] = function( data, len, pos ) + local response = {} - if len ~= 5 then - return -1, "ERROR: Invalid ReadyForQuery packet" - end + if len ~= 5 then + return -1, "ERROR: Invalid ReadyForQuery packet" + end - pos, response.status = bin.unpack("C", data, pos ) - return pos, response - end, - }, + pos, response.status = bin.unpack("C", data, pos ) + return pos, response + end, + }, - --- Reads a packet and handles additional socket reads to retrieve remaining data - -- - -- @param socket socket already connected to the pgsql server - -- @param data string containing any data already retrieved from the socket - -- @param pos number containing the offset into the data buffer - -- @return data string containing the initial and any additional data - readPacket = function(socket, data, pos) + --- Reads a packet and handles additional socket reads to retrieve remaining data + -- + -- @param socket socket already connected to the pgsql server + -- @param data string containing any data already retrieved from the socket + -- @param pos number containing the offset into the data buffer + -- @return data string containing the initial and any additional data + readPacket = function(socket, data, pos) - local pos = pos or 1 - local data = data or "" - local status = true - local tmp = "" - local ptype, len - local header + local pos = pos or 1 + local data = data or "" + local status = true + local tmp = "" + local ptype, len + local header - local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end - local try = nmap.new_try(catch) + local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end + local try = nmap.new_try(catch) - if ( data:len() - pos < 5 ) then - status, tmp = socket:receive_bytes( 5 - ( data:len() - pos ) ) - end + if ( data:len() - pos < 5 ) then + status, tmp = socket:receive_bytes( 5 - ( data:len() - pos ) ) + end - if not status then - return nil, "Failed to read packet" - end + if not status then + return nil, "Failed to read packet" + end - if tmp:len() ~= 0 then - data = data .. tmp - end + if tmp:len() ~= 0 then + data = data .. tmp + end - pos, header = v3.decodeHeader(data,pos) + pos, header = v3.decodeHeader(data,pos) - while data:len() < header.len do - data = data .. try(socket:receive_bytes( ( header.len + 1 ) - data:len() )) - end - return data - end, + while data:len() < header.len do + data = data .. try(socket:receive_bytes( ( header.len + 1 ) - data:len() )) + end + return data + end, - --- Decodes the postgres header - -- - -- @param data string containing the server response - -- @param pos number containing the offset into the data buffer - -- @return pos number containing the offset after decoding - -- @return header table containing type and len - decodeHeader = function(data, pos) - local ptype, len + --- Decodes the postgres header + -- + -- @param data string containing the server response + -- @param pos number containing the offset into the data buffer + -- @return pos number containing the offset after decoding + -- @return header table containing type and len + decodeHeader = function(data, pos) + local ptype, len - pos, ptype, len = bin.unpack("C>I", data, pos) - return pos, { ['type'] = ptype, ['len'] = len } - end, + pos, ptype, len = bin.unpack("C>I", data, pos) + return pos, { ['type'] = ptype, ['len'] = len } + end, - --- Process the server response - -- - -- @param data string containing the server response - -- @param pos number containing the offset into the data buffer - -- @return pos number containing offset after decoding - -- @return response string containing decoded data - -- error message if pos is -1 - processResponse = function(data, pos) - local ptype, len, status, response - local pos = pos or 1 - local header + --- Process the server response + -- + -- @param data string containing the server response + -- @param pos number containing the offset into the data buffer + -- @return pos number containing offset after decoding + -- @return response string containing decoded data + -- error message if pos is -1 + processResponse = function(data, pos) + local ptype, len, status, response + local pos = pos or 1 + local header - pos, header = v3.decodeHeader( data, pos ) + pos, header = v3.decodeHeader( data, pos ) - if v3.messageDecoder[header.type] then - pos, response = v3.messageDecoder[header.type](data, header.len, pos) + if v3.messageDecoder[header.type] then + pos, response = v3.messageDecoder[header.type](data, header.len, pos) - if pos ~= -1 then - response.type = header.type - return pos, response - end - else - stdnse.print_debug( "Missing decoder for %d", header.type ) - return -1, ("Missing decoder for %d"):format(header.type) - end - return -1, "Decoding failed" - end, + if pos ~= -1 then + response.type = header.type + return pos, response + end + else + stdnse.print_debug( "Missing decoder for %d", header.type ) + return -1, ("Missing decoder for %d"):format(header.type) + end + return -1, "Decoding failed" + end, - --- Attempts to authenticate to the pgsql server - -- Supports plain-text and MD5 authentication - -- - -- @param socket socket already connected to the pgsql server - -- @param params table containing any additional parameters authtype, version - -- @param username string containing the username to use for authentication - -- @param password string containing the password to use for authentication - -- @param salt string containing the crypthographic salt value - -- @return status true on success, false on failure - -- @return result table containing parameter status information, - -- result string containing an error message if login fails - loginRequest = function ( socket, params, username, password, salt ) + --- Attempts to authenticate to the pgsql server + -- Supports plain-text and MD5 authentication + -- + -- @param socket socket already connected to the pgsql server + -- @param params table containing any additional parameters authtype, version + -- @param username string containing the username to use for authentication + -- @param password string containing the password to use for authentication + -- @param salt string containing the crypthographic salt value + -- @return status true on success, false on failure + -- @return result table containing parameter status information, + -- result string containing an error message if login fails + loginRequest = function ( socket, params, username, password, salt ) - local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end - local try = nmap.new_try(catch) - local response, header = {}, {} - local status, data, len, tmp, _ - local pos = 1 + local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end + local try = nmap.new_try(catch) + local response, header = {}, {} + local status, data, len, tmp, _ + local pos = 1 - if ( params.authtype == AuthenticationType.MD5 ) then - local hash = createMD5LoginHash(username, password, salt) - data = bin.pack( "C>Iz", MessageType.PasswordMessage, 40, hash ) - try( socket:send( data ) ) - elseif ( params.authtype == AuthenticationType.Plain ) then - local data - data = bin.pack("C>Iz", MessageType.PasswordMessage, password:len() + 4, password) - try( socket:send( data ) ) - elseif ( params.authtype == AuthenticationType.Success ) then - return true, nil - end + if ( params.authtype == AuthenticationType.MD5 ) then + local hash = createMD5LoginHash(username, password, salt) + data = bin.pack( "C>Iz", MessageType.PasswordMessage, 40, hash ) + try( socket:send( data ) ) + elseif ( params.authtype == AuthenticationType.Plain ) then + local data + data = bin.pack("C>Iz", MessageType.PasswordMessage, password:len() + 4, password) + try( socket:send( data ) ) + elseif ( params.authtype == AuthenticationType.Success ) then + return true, nil + end - data, response.params = "", {} + data, response.params = "", {} - data = v3.readPacket(socket, data, 1) - pos, tmp = v3.processResponse(data, 1) + data = v3.readPacket(socket, data, 1) + pos, tmp = v3.processResponse(data, 1) - -- this should contain the AuthRequest packet - if tmp.type ~= MessageType.AuthRequest then - return false, "Expected AuthRequest got something else" - end + -- this should contain the AuthRequest packet + if tmp.type ~= MessageType.AuthRequest then + return false, "Expected AuthRequest got something else" + end - if not tmp.success then - return false, "Login failure" - end + if not tmp.success then + return false, "Login failure" + end - repeat - data = v3.readPacket(socket, data, pos) - pos, tmp = v3.processResponse(data, pos) - if ( tmp.type == MessageType.ParameterStatus ) then - table.insert(response.params, {name=tmp.key, value=tmp.value}) - end - until pos >= data:len() or pos == -1 + repeat + data = v3.readPacket(socket, data, pos) + pos, tmp = v3.processResponse(data, pos) + if ( tmp.type == MessageType.ParameterStatus ) then + table.insert(response.params, {name=tmp.key, value=tmp.value}) + end + until pos >= data:len() or pos == -1 - return true, response - end, + return true, response + end, - --- Sends a startup message to the server containing the username and database to connect to - -- - -- @param socket socket already connected to the pgsql server - -- @param user string containing the name of the user - -- @param database string containing the name of the database - -- @return status true on success, false on failure - -- @return table containing a processed response from processResponse - -- string containing error message if status is false - sendStartup = function(socket, user, database ) - local data, response, status, pos - local proto_ver, ptype, _, tmp + --- Sends a startup message to the server containing the username and database to connect to + -- + -- @param socket socket already connected to the pgsql server + -- @param user string containing the name of the user + -- @param database string containing the name of the database + -- @return status true on success, false on failure + -- @return table containing a processed response from processResponse + -- string containing error message if status is false + sendStartup = function(socket, user, database ) + local data, response, status, pos + local proto_ver, ptype, _, tmp - proto_ver = 0x0030000 - data = bin.pack(">IzzzzH", proto_ver, "user", user, "database", database, 0) - data = bin.pack(">I", data:len() + 4) .. data + proto_ver = 0x0030000 + data = bin.pack(">IzzzzH", proto_ver, "user", user, "database", database, 0) + data = bin.pack(">I", data:len() + 4) .. data - socket:send( data ) + socket:send( data ) - -- attempt to verify version - status, data = socket:receive_bytes( 2 ) + -- attempt to verify version + status, data = socket:receive_bytes( 2 ) - if ( not(status) ) then - return false, "sendStartup failed" - end + if ( not(status) ) then + return false, "sendStartup failed" + end - if ( not(status) or data:match("^EF") ) then - return false, "Incorrect version" - end + if ( not(status) or data:match("^EF") ) then + return false, "Incorrect version" + end - data = v3.readPacket(socket, data ) - pos, response = v3.processResponse( data ) + data = v3.readPacket(socket, data ) + pos, response = v3.processResponse( data ) - if ( pos < 0 or response.type == MessageType.Error) then - return false, response.error.message or "unknown error" - end + if ( pos < 0 or response.type == MessageType.Error) then + return false, response.error.message or "unknown error" + end - return true, response - end + return true, response + end } @@ -563,23 +563,23 @@ v3 = -- @param socket socket already connected to the pgsql server -- @return boolean true if request was accepted, false if request was denied function requestSSL(socket) - -- SSLRequest - local ssl_req_code = 80877103 - local data = bin.pack( ">I>I", 8, ssl_req_code) - local status, response + -- SSLRequest + local ssl_req_code = 80877103 + local data = bin.pack( ">I>I", 8, ssl_req_code) + local status, response - socket:send(data) - status, response = socket:receive_bytes(1) + socket:send(data) + status, response = socket:receive_bytes(1) - if ( not(status) ) then - return false - end + if ( not(status) ) then + return false + end - if ( response == 'S' ) then - return true - end + if ( response == 'S' ) then + return true + end - return false + return false end --- Creates a cryptographic hash to be used for login @@ -589,20 +589,20 @@ end -- @param salt salt -- @return string suitable for login request function createMD5LoginHash(username, password, salt) - local md5_1 = select( 2, bin.unpack( "H16", openssl.md5(password..username) ) ):lower() - return "md5" .. select( 2, bin.unpack("H16", openssl.md5( md5_1 .. salt ) ) ):lower() + local md5_1 = select( 2, bin.unpack( "H16", openssl.md5(password..username) ) ):lower() + return "md5" .. select( 2, bin.unpack("H16", openssl.md5( md5_1 .. salt ) ) ):lower() end --- Prints the contents of the error table returned from the Error message decoder -- -- @param dberror table containing the error function printErrorMessage( dberror ) - if not dberror then - return - end - for k, v in pairs(dberror) do - stdnse.print_debug( ("%s=%s"):format(k, v) ) - end + if not dberror then + return + end + for k, v in pairs(dberror) do + stdnse.print_debug( ("%s=%s"):format(k, v) ) + end end --- Attempts to determine if the server supports v3 or v2 of the protocol @@ -611,18 +611,18 @@ end -- @param port table -- @return class v2 or v3 function detectVersion(host, port) - local status, response - local socket = nmap.new_socket() + local status, response + local socket = nmap.new_socket() - socket:connect(host, port) - status, response = v3.sendStartup(socket, "versionprobe", "versionprobe") - socket:close() + socket:connect(host, port) + status, response = v3.sendStartup(socket, "versionprobe", "versionprobe") + socket:close() - if ( not(status) and response == 'Incorrect version' ) then - return v2 - end + if ( not(status) and response == 'Incorrect version' ) then + return v2 + end - return v3 + return v3 end return _ENV; diff --git a/nselib/pppoe.lua b/nselib/pppoe.lua index de38ed864..11aae5aee 100644 --- a/nselib/pppoe.lua +++ b/nselib/pppoe.lua @@ -32,600 +32,600 @@ _ENV = stdnse.module("pppoe", stdnse.seeall) EtherType = { - PPPOE_DISCOVERY = 0x8863, - PPPOE_SESSION = 0x8864, + PPPOE_DISCOVERY = 0x8863, + PPPOE_SESSION = 0x8864, } -- A Class to handle the Link Control Protocol LCP LCP = { - ConfigOption = { + ConfigOption = { - RESERVED = 0, - MRU = 1, - AUTH_PROTO = 3, - QUAL_PROTO = 4, - MAGIC_NUMBER= 5, - PROTO_COMPR = 7, - ACFC = 8, + RESERVED = 0, + MRU = 1, + AUTH_PROTO = 3, + QUAL_PROTO = 4, + MAGIC_NUMBER = 5, + PROTO_COMPR = 7, + ACFC = 8, - -- Value has already been encoded, treat it as a byte stream - RAW = -1, + -- Value has already been encoded, treat it as a byte stream + RAW = -1, - -- Creates a new config option - -- @param option number containing the configuration option - -- @param value containing the configuration option value - -- @param raw string containing the configuration options raw value - -- @return o new instance of ConfigOption - new = function(self, option, value, raw) - local o = { - option = option, - value = value, - raw = raw, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new config option + -- @param option number containing the configuration option + -- @param value containing the configuration option value + -- @param raw string containing the configuration options raw value + -- @return o new instance of ConfigOption + new = function(self, option, value, raw) + local o = { + option = option, + value = value, + raw = raw, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the ConfigOption - -- class - -- @param data string containing raw bytes to parse - -- @return o instance of ConfigOption - parse = function(data) - local opt, pos, len = {}, 1, 0 - pos, opt.option, len = bin.unpack("CC", data, pos) - pos, opt.raw = bin.unpack("A" .. ( len - 2 ), data, pos) + -- Parses a byte stream and builds a new instance of the ConfigOption + -- class + -- @param data string containing raw bytes to parse + -- @return o instance of ConfigOption + parse = function(data) + local opt, pos, len = {}, 1, 0 + pos, opt.option, len = bin.unpack("CC", data, pos) + pos, opt.raw = bin.unpack("A" .. ( len - 2 ), data, pos) - -- MRU - if ( 1 == opt.option ) then - opt.value = select(2, bin.unpack(">S", opt.raw)) - end - return LCP.ConfigOption:new(opt.option, opt.value, opt.raw) - end, + -- MRU + if ( 1 == opt.option ) then + opt.value = select(2, bin.unpack(">S", opt.raw)) + end + return LCP.ConfigOption:new(opt.option, opt.value, opt.raw) + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - -- MRU - if ( self.raw ) then - return bin.pack(">CCA", self.option, #self.raw + 2, self.raw ) - elseif( 1 == self.option ) then - return bin.pack(">CCS", 1, 4, self.value) - else - error( ("Unsupported configuration option %d"):format(self.option) ) - end - end, - }, + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + -- MRU + if ( self.raw ) then + return bin.pack(">CCA", self.option, #self.raw + 2, self.raw ) + elseif( 1 == self.option ) then + return bin.pack(">CCS", 1, 4, self.value) + else + error( ("Unsupported configuration option %d"):format(self.option) ) + end + end, + }, - -- A class to hold multiple ordered config options - ConfigOptions = { + -- A class to hold multiple ordered config options + ConfigOptions = { - new = function(self, options) - local o = { - options = options or {}, - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self, options) + local o = { + options = options or {}, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Adds a new config option to the table - -- @param option instance of ConfigOption - add = function(self, option) - table.insert(self.options, option) - end, + -- Adds a new config option to the table + -- @param option instance of ConfigOption + add = function(self, option) + table.insert(self.options, option) + end, - -- Gets a config option by ID - -- @param opt number containing the configuration option to retrieve - -- @return v instance of ConfigOption - getById = function(self, opt) - for _, v in ipairs(self.options) do - if ( v.option == opt ) then - return v - end - end - end, + -- Gets a config option by ID + -- @param opt number containing the configuration option to retrieve + -- @return v instance of ConfigOption + getById = function(self, opt) + for _, v in ipairs(self.options) do + if ( v.option == opt ) then + return v + end + end + end, - -- Returns all config options in an ordered table - -- @return tab table containing all configuration options - getTable = function(self) - local tab = {} - for _, v in ipairs(self.options) do - table.insert(tab, v) - end - return tab - end, + -- Returns all config options in an ordered table + -- @return tab table containing all configuration options + getTable = function(self) + local tab = {} + for _, v in ipairs(self.options) do + table.insert(tab, v) + end + return tab + end, - -- Parses a byte stream and builds a new instance of the ConfigOptions - -- class - -- @param data string containing raw bytes to parse - -- @return o instance of ConfigOption - parse = function(data) - local options = LCP.ConfigOptions:new() - local pos, opt, opt_val, len + -- Parses a byte stream and builds a new instance of the ConfigOptions + -- class + -- @param data string containing raw bytes to parse + -- @return o instance of ConfigOption + parse = function(data) + local options = LCP.ConfigOptions:new() + local pos, opt, opt_val, len - repeat - pos, opt, len = bin.unpack(">CC", data, pos) - if ( 0 == opt ) then break end - pos, opt_val = bin.unpack("A"..len, data, (pos - 2)) - options:add(LCP.ConfigOption.parse(opt_val)) - until( pos == #data ) - return options - end, + repeat + pos, opt, len = bin.unpack(">CC", data, pos) + if ( 0 == opt ) then break end + pos, opt_val = bin.unpack("A"..len, data, (pos - 2)) + options:add(LCP.ConfigOption.parse(opt_val)) + until( pos == #data ) + return options + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - local str = "" - for _, v in ipairs(self.options) do - str = str .. tostring(v) - end - return str - end, + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + local str = "" + for _, v in ipairs(self.options) do + str = str .. tostring(v) + end + return str + end, - }, + }, - ConfigOptionName = { - [0] = "Reserved", - [1] = "Maximum receive unit", - [3] = "Authentication protocol", - [4] = "Quality protocol", - [5] = "Magic number", - [7] = "Protocol field compression", - [8] = "Address and control field compression", - }, + ConfigOptionName = { + [0] = "Reserved", + [1] = "Maximum receive unit", + [3] = "Authentication protocol", + [4] = "Quality protocol", + [5] = "Magic number", + [7] = "Protocol field compression", + [8] = "Address and control field compression", + }, - Code = { - CONFIG_REQUEST = 1, - CONFIG_ACK = 2, - CONFIG_NAK = 3, - TERMINATE_REQUEST = 5, - TERMINATE_ACK = 6, - }, + Code = { + CONFIG_REQUEST = 1, + CONFIG_ACK = 2, + CONFIG_NAK = 3, + TERMINATE_REQUEST = 5, + TERMINATE_ACK = 6, + }, - -- The LCP Header - Header = { + -- The LCP Header + Header = { - -- Creates a new instance of the LCP header - -- @param code number containing the LCP code of the request - -- @param identifier number containing the LCP identifier - new = function(self, code, identifier) - local o = { - code = code, - identifier = identifier or 1, - length = 0, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the LCP header + -- @param code number containing the LCP code of the request + -- @param identifier number containing the LCP identifier + new = function(self, code, identifier) + local o = { + code = code, + identifier = identifier or 1, + length = 0, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the Header class - -- @param data string containing raw bytes to parse - -- @return o instance of ConfigOption - parse = function(data) - local header = LCP.Header:new() - local pos - pos, header.code, header.identifier, header.length = bin.unpack(">CCS", data) - return header - end, + -- Parses a byte stream and builds a new instance of the Header class + -- @param data string containing raw bytes to parse + -- @return o instance of ConfigOption + parse = function(data) + local header = LCP.Header:new() + local pos + pos, header.code, header.identifier, header.length = bin.unpack(">CCS", data) + return header + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - return bin.pack(">CCS", self.code, self.identifier, self.length) - end, + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + return bin.pack(">CCS", self.code, self.identifier, self.length) + end, - }, + }, - ConfigRequest = { + ConfigRequest = { - -- Creates a new instance of the ConfigRequest class - -- @param identifier number containing the LCP identifier - -- @param options table of LCP.ConfigOption options - -- @return o instance of ConfigRequest - new = function(self, identifier, options) - local o = { - header = LCP.Header:new(LCP.Code.CONFIG_REQUEST, identifier), - options = LCP.ConfigOptions:new(options) - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the ConfigRequest class + -- @param identifier number containing the LCP identifier + -- @param options table of LCP.ConfigOption options + -- @return o instance of ConfigRequest + new = function(self, identifier, options) + local o = { + header = LCP.Header:new(LCP.Code.CONFIG_REQUEST, identifier), + options = LCP.ConfigOptions:new(options) + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the ConfigRequest - -- class - -- @param data string containing raw bytes to parse - -- @return o instance of ConfigRequest - parse = function(data) - local req = LCP.ConfigRequest:new() - req.header = LCP.Header.parse(data) - req.options = LCP.ConfigOptions.parse(data:sub(#tostring(req.header) + 1)) - return req - end, + -- Parses a byte stream and builds a new instance of the ConfigRequest + -- class + -- @param data string containing raw bytes to parse + -- @return o instance of ConfigRequest + parse = function(data) + local req = LCP.ConfigRequest:new() + req.header = LCP.Header.parse(data) + req.options = LCP.ConfigOptions.parse(data:sub(#tostring(req.header) + 1)) + return req + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - self.header.length = 4 + #tostring(self.options) - return tostring(self.header) .. tostring(self.options) - end, - }, + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + self.header.length = 4 + #tostring(self.options) + return tostring(self.header) .. tostring(self.options) + end, + }, - ConfigNak = { + ConfigNak = { - -- Creates a new instance of the ConfigNak class - -- @param identifier number containing the LCP identifier - -- @param options table of LCP.ConfigOption options - -- @return o instance of ConfigNak - new = function(self, identifier, options) - local o = { - header = LCP.Header:new(LCP.Code.CONFIG_NAK, identifier), - options = LCP.ConfigOptions:new(options), - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the ConfigNak class + -- @param identifier number containing the LCP identifier + -- @param options table of LCP.ConfigOption options + -- @return o instance of ConfigNak + new = function(self, identifier, options) + local o = { + header = LCP.Header:new(LCP.Code.CONFIG_NAK, identifier), + options = LCP.ConfigOptions:new(options), + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - self.header.length = 4 + #tostring(self.options) - return tostring(self.header) .. tostring(self.options) - end, - }, + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + self.header.length = 4 + #tostring(self.options) + return tostring(self.header) .. tostring(self.options) + end, + }, - ConfigAck = { + ConfigAck = { - -- Creates a new instance of the ConfigAck class - -- @param identifier number containing the LCP identifier - -- @param options table of LCP.ConfigOption options - -- @return o instance of ConfigNak - new = function(self, identifier, options) - local o = { - header = LCP.Header:new(LCP.Code.CONFIG_ACK, identifier), - options = LCP.ConfigOptions:new(options), - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the ConfigAck class + -- @param identifier number containing the LCP identifier + -- @param options table of LCP.ConfigOption options + -- @return o instance of ConfigNak + new = function(self, identifier, options) + local o = { + header = LCP.Header:new(LCP.Code.CONFIG_ACK, identifier), + options = LCP.ConfigOptions:new(options), + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the ConfigAck class - -- @param data string containing raw bytes to parse - -- @return o instance of ConfigRequest - parse = function(data) - local ack = LCP.ConfigAck:new() - ack.header = LCP.Header.parse(data) - ack.options = LCP.ConfigOptions.parse(data:sub(#tostring(ack.header) + 1)) - return ack - end, + -- Parses a byte stream and builds a new instance of the ConfigAck class + -- @param data string containing raw bytes to parse + -- @return o instance of ConfigRequest + parse = function(data) + local ack = LCP.ConfigAck:new() + ack.header = LCP.Header.parse(data) + ack.options = LCP.ConfigOptions.parse(data:sub(#tostring(ack.header) + 1)) + return ack + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - self.header.length = 4 + #tostring(self.options) - return tostring(self.header) .. tostring(self.options) - end, + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + self.header.length = 4 + #tostring(self.options) + return tostring(self.header) .. tostring(self.options) + end, - }, + }, - TerminateRequest = { + TerminateRequest = { - -- Creates a new instance of the TerminateRequest class - -- @param identifier number containing the LCP identifier - -- @return o instance of ConfigNak - new = function(self, identifier, data) - local o = { - header = LCP.Header:new(LCP.Code.TERMINATE_REQUEST, identifier), - data = data or "", - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the TerminateRequest class + -- @param identifier number containing the LCP identifier + -- @return o instance of ConfigNak + new = function(self, identifier, data) + local o = { + header = LCP.Header:new(LCP.Code.TERMINATE_REQUEST, identifier), + data = data or "", + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts the class instance to string - -- @return string containing the raw config option - __tostring = function(self) - self.header.length = 4 + #self.data - return tostring(self.header) .. self.data - end, - } + -- Converts the class instance to string + -- @return string containing the raw config option + __tostring = function(self) + self.header.length = 4 + #self.data + return tostring(self.header) .. self.data + end, + } } -- The PPPoE class PPPoE = { - -- Supported PPPoE codes (requests/responses) - Code = { - SESSION_DATA = 0x00, - PADO = 0x07, - PADI = 0x09, - PADR = 0x19, - PADS = 0x65, - PADT = 0xa7, - }, + -- Supported PPPoE codes (requests/responses) + Code = { + SESSION_DATA = 0x00, + PADO = 0x07, + PADI = 0x09, + PADR = 0x19, + PADS = 0x65, + PADT = 0xa7, + }, - -- Support PPPoE Tag types - TagType = { - SERVICE_NAME = 0x0101, - AC_NAME = 0x0102, - HOST_UNIQUE = 0x0103, - AC_COOKIE = 0x0104, - }, + -- Support PPPoE Tag types + TagType = { + SERVICE_NAME = 0x0101, + AC_NAME = 0x0102, + HOST_UNIQUE = 0x0103, + AC_COOKIE = 0x0104, + }, - -- Table used to convert table IDs to Names - TagName = { - [0x0101] = "Service-Name", - [0x0102] = "AC-Name", - [0x0103] = "Host-Uniq", - [0x0104] = "AC-Cookie", - }, + -- Table used to convert table IDs to Names + TagName = { + [0x0101] = "Service-Name", + [0x0102] = "AC-Name", + [0x0103] = "Host-Uniq", + [0x0104] = "AC-Cookie", + }, - Header = { + Header = { - -- Creates a new instance of the PPPoE header class - -- @param code number containing the PPPoE code - -- @param session number containing the PPPoE session - -- @return o instance of Header - new = function(self, code, session) - local o = { - version = 1, - type = 1, - code = code, - session = session or 0, - length = 0, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the PPPoE header class + -- @param code number containing the PPPoE code + -- @param session number containing the PPPoE session + -- @return o instance of Header + new = function(self, code, session) + local o = { + version = 1, + type = 1, + code = code, + session = session or 0, + length = 0, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the class - -- @param data string containing raw bytes to parse - -- @return o instance of Header - parse = function(data) - local pos, vertyp - local header = PPPoE.Header:new() - pos, vertyp, header.code, header.session, header.length = bin.unpack(">CCSS", data) - header.version = bit.rshift(vertyp,4) - header.type = bit.band(vertyp, 0x0F) - return header - end, + -- Parses a byte stream and builds a new instance of the class + -- @param data string containing raw bytes to parse + -- @return o instance of Header + parse = function(data) + local pos, vertyp + local header = PPPoE.Header:new() + pos, vertyp, header.code, header.session, header.length = bin.unpack(">CCSS", data) + header.version = bit.rshift(vertyp,4) + header.type = bit.band(vertyp, 0x0F) + return header + end, - -- Converts the instance to string - -- @return string containing the raw config option - __tostring = function(self) - local vertype = bit.lshift(self.version, 4) + self.type - return bin.pack(">CCSS", vertype, self.code, self.session, self.length) - end, + -- Converts the instance to string + -- @return string containing the raw config option + __tostring = function(self) + local vertype = bit.lshift(self.version, 4) + self.type + return bin.pack(">CCSS", vertype, self.code, self.session, self.length) + end, - }, + }, - -- The TAG NVP Class - Tag = { + -- The TAG NVP Class + Tag = { - -- Creates a new instance of the Tag class - -- @param tag number containing the tag type - -- @param value string/number containing the tag value - -- @return o instance of Tag - new = function(self, tag, value) - local o = { tag = tag, value = value or "" } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the Tag class + -- @param tag number containing the tag type + -- @param value string/number containing the tag value + -- @return o instance of Tag + new = function(self, tag, value) + local o = { tag = tag, value = value or "" } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts the instance to string - -- @return string containing the raw config option - __tostring = function(self) - return bin.pack(">SSA", self.tag, #self.value, self.value) - end, - }, + -- Converts the instance to string + -- @return string containing the raw config option + __tostring = function(self) + return bin.pack(">SSA", self.tag, #self.value, self.value) + end, + }, - PADI = { + PADI = { - -- Creates a new instance of the PADI class - -- @param tags table of PPPoE.Tag instances - -- @param value string/number containing the tag value - -- @return o instance of ConfigNak - new = function(self, tags) - local c = "" - for i=1, 4 do - c = c .. math.random(255) - end + -- Creates a new instance of the PADI class + -- @param tags table of PPPoE.Tag instances + -- @param value string/number containing the tag value + -- @return o instance of ConfigNak + new = function(self, tags) + local c = "" + for i=1, 4 do + c = c .. math.random(255) + end - local o = { - header = PPPoE.Header:new(PPPoE.Code.PADI), - tags = tags or { - PPPoE.Tag:new(PPPoE.TagType.SERVICE_NAME), - PPPoE.Tag:new(PPPoE.TagType.HOST_UNIQUE, bin.pack("A", c)) - } - } - setmetatable(o, self) - self.__index = self - return o - end, + local o = { + header = PPPoE.Header:new(PPPoE.Code.PADI), + tags = tags or { + PPPoE.Tag:new(PPPoE.TagType.SERVICE_NAME), + PPPoE.Tag:new(PPPoE.TagType.HOST_UNIQUE, bin.pack("A", c)) + } + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts the instance to string - -- @return string containing the raw config option - __tostring = function(self) - local tags = "" - for _, tag in ipairs(self.tags) do - tags = tags .. tostring(tag) - end - self.header.length = #tags - return tostring(self.header) .. tags - end, + -- Converts the instance to string + -- @return string containing the raw config option + __tostring = function(self) + local tags = "" + for _, tag in ipairs(self.tags) do + tags = tags .. tostring(tag) + end + self.header.length = #tags + return tostring(self.header) .. tags + end, - }, + }, - PADO = { + PADO = { - -- Creates a new instance of the PADO class - -- @return o instance of PADO - new = function(self) - local o = { tags = {} } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the PADO class + -- @return o instance of PADO + new = function(self) + local o = { tags = {} } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the class - -- @param data string containing raw bytes to parse - -- @return o instance of PADO - parse = function(data) - local pado = PPPoE.PADO:new() - pado.header = PPPoE.Header.parse(data) - local pos = #tostring(pado.header) + 1 - pado.data = data:sub(pos) + -- Parses a byte stream and builds a new instance of the class + -- @param data string containing raw bytes to parse + -- @return o instance of PADO + parse = function(data) + local pado = PPPoE.PADO:new() + pado.header = PPPoE.Header.parse(data) + local pos = #tostring(pado.header) + 1 + pado.data = data:sub(pos) - repeat - local tag, len, decoded, raw - pos, tag, len = bin.unpack(">SS", data, pos) - raw = select(2, bin.unpack("A" .. len, data, pos)) - if ( PPPoE.TagDecoder[tag] ) then - pos, decoded = PPPoE.TagDecoder[tag](data, pos, len) - else - stdnse.print_debug("PPPoE: Unsupported tag (%d)", tag) - pos = pos + len - end - local t = PPPoE.Tag:new(tag, raw) - t.decoded = decoded - table.insert(pado.tags, t) - until( pos >= #data ) + repeat + local tag, len, decoded, raw + pos, tag, len = bin.unpack(">SS", data, pos) + raw = select(2, bin.unpack("A" .. len, data, pos)) + if ( PPPoE.TagDecoder[tag] ) then + pos, decoded = PPPoE.TagDecoder[tag](data, pos, len) + else + stdnse.print_debug("PPPoE: Unsupported tag (%d)", tag) + pos = pos + len + end + local t = PPPoE.Tag:new(tag, raw) + t.decoded = decoded + table.insert(pado.tags, t) + until( pos >= #data ) - return pado - end, - }, + return pado + end, + }, - PADR = { + PADR = { - -- Creates a new instance of the PADR class - -- @param tags table of PPPoE.Tag instances - -- @return o instance of PADR - new = function(self, tags) - local o = { - tags = tags or {}, - header = PPPoE.Header:new(PPPoE.Code.PADR) - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the PADR class + -- @param tags table of PPPoE.Tag instances + -- @return o instance of PADR + new = function(self, tags) + local o = { + tags = tags or {}, + header = PPPoE.Header:new(PPPoE.Code.PADR) + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Converts the instance to string - -- @return string containing the raw config option - __tostring = function(self) - local tags = "" - for _, tag in ipairs(self.tags) do - tags = tags .. tostring(tag) - end - self.header.length = #tags - return tostring(self.header) .. tags - end, + -- Converts the instance to string + -- @return string containing the raw config option + __tostring = function(self) + local tags = "" + for _, tag in ipairs(self.tags) do + tags = tags .. tostring(tag) + end + self.header.length = #tags + return tostring(self.header) .. tags + end, - }, + }, - PADS = { + PADS = { - -- Creates a new instance of the PADS class - -- @return o instance of PADS - new = function(self) - local o = { tags = {} } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the PADS class + -- @return o instance of PADS + new = function(self) + local o = { tags = {} } + setmetatable(o, self) + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the class - -- @param data string containing raw bytes to parse - -- @return o instance of PADS - parse = function(data) - local pads = PPPoE.PADS:new() - pads.header = PPPoE.Header.parse(data) - local pos = #tostring(pads.header) + 1 - pads.data = data:sub(pos) - return pads - end, + -- Parses a byte stream and builds a new instance of the class + -- @param data string containing raw bytes to parse + -- @return o instance of PADS + parse = function(data) + local pads = PPPoE.PADS:new() + pads.header = PPPoE.Header.parse(data) + local pos = #tostring(pads.header) + 1 + pads.data = data:sub(pos) + return pads + end, - }, + }, - PADT = { + PADT = { - -- Creates a new instance of the PADT class - -- @param session number containing the PPPoE session - -- @return o instance of PADT - new = function(self, session) - local o = { header = PPPoE.Header:new(PPPoE.Code.PADT) } - setmetatable(o, self) - o.header.session = session - self.__index = self - return o - end, + -- Creates a new instance of the PADT class + -- @param session number containing the PPPoE session + -- @return o instance of PADT + new = function(self, session) + local o = { header = PPPoE.Header:new(PPPoE.Code.PADT) } + setmetatable(o, self) + o.header.session = session + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the class - -- @param data string containing raw bytes to parse - -- @return o instance of PADI - parse = function(data) - local padt = PPPoE.PADT:new() - padt.header = PPPoE.Header.parse(data) - return padt - end, + -- Parses a byte stream and builds a new instance of the class + -- @param data string containing raw bytes to parse + -- @return o instance of PADI + parse = function(data) + local padt = PPPoE.PADT:new() + padt.header = PPPoE.Header.parse(data) + return padt + end, - -- Converts the instance to string - -- @return string containing the raw config option - __tostring = function(self) - return tostring(self.header) - end, - }, + -- Converts the instance to string + -- @return string containing the raw config option + __tostring = function(self) + return tostring(self.header) + end, + }, - SessionData = { + SessionData = { - -- Creates a new instance of the SessionData class - -- @param session number containing the PPPoE session - -- @param data string containing the LCP data to send - -- @return o instance of ConfigNak - new = function(self, session, data) - local o = { - data = data or "", - header = PPPoE.Header:new(PPPoE.Code.SESSION_DATA) - } - setmetatable(o, self) - o.header.session = session - self.__index = self - return o - end, + -- Creates a new instance of the SessionData class + -- @param session number containing the PPPoE session + -- @param data string containing the LCP data to send + -- @return o instance of ConfigNak + new = function(self, session, data) + local o = { + data = data or "", + header = PPPoE.Header:new(PPPoE.Code.SESSION_DATA) + } + setmetatable(o, self) + o.header.session = session + self.__index = self + return o + end, - -- Parses a byte stream and builds a new instance of the class - -- @param data string containing raw bytes to parse - -- @return o instance of SessionData - parse = function(data) - local sess = PPPoE.SessionData:new() - sess.header = PPPoE.Header.parse(data) - local pos = #tostring(sess.header) + 1 + 2 - sess.data = data:sub(pos) - return sess - end, + -- Parses a byte stream and builds a new instance of the class + -- @param data string containing raw bytes to parse + -- @return o instance of SessionData + parse = function(data) + local sess = PPPoE.SessionData:new() + sess.header = PPPoE.Header.parse(data) + local pos = #tostring(sess.header) + 1 + 2 + sess.data = data:sub(pos) + return sess + end, - -- Converts the instance to string - -- @return string containing the raw config option - __tostring = function(self) - -- 2 for the encapsulation - self.header.length = 2 + 4 + #self.data - return tostring(self.header) .. bin.pack(">S", 0xC021) .. self.data - end, + -- Converts the instance to string + -- @return string containing the raw config option + __tostring = function(self) + -- 2 for the encapsulation + self.header.length = 2 + 4 + #self.data + return tostring(self.header) .. bin.pack(">S", 0xC021) .. self.data + end, - } + } } @@ -634,386 +634,386 @@ PPPoE = { PPPoE.TagDecoder = {} PPPoE.TagDecoder.decodeHex = function(data, pos, len) return pos + len, stdnse.tohex(data:sub(pos, pos+len)) end PPPoE.TagDecoder.decodeStr = function(data, pos, len) return pos + len, data:sub(pos, pos + len - 1) end -PPPoE.TagDecoder[PPPoE.TagType.SERVICE_NAME]= PPPoE.TagDecoder.decodeStr -PPPoE.TagDecoder[PPPoE.TagType.AC_NAME] = PPPoE.TagDecoder.decodeStr -PPPoE.TagDecoder[PPPoE.TagType.AC_COOKIE] = PPPoE.TagDecoder.decodeHex +PPPoE.TagDecoder[PPPoE.TagType.SERVICE_NAME] = PPPoE.TagDecoder.decodeStr +PPPoE.TagDecoder[PPPoE.TagType.AC_NAME] = PPPoE.TagDecoder.decodeStr +PPPoE.TagDecoder[PPPoE.TagType.AC_COOKIE] = PPPoE.TagDecoder.decodeHex PPPoE.TagDecoder[PPPoE.TagType.HOST_UNIQUE] = PPPoE.TagDecoder.decodeHex -- The Comm class responsible for communication with the PPPoE server Comm = { - -- Creates a new instance of the Comm class - -- @param iface string containing the interface name - -- @param src_mac string containing the source MAC address - -- @param dst_mac string containing the destination MAC address - -- @return o new instance of Comm - new = function(self, iface, src_mac, dst_mac) - local o = { - iface = iface, - src_mac = src_mac, - dst_mac = dst_mac, - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the Comm class + -- @param iface string containing the interface name + -- @param src_mac string containing the source MAC address + -- @param dst_mac string containing the destination MAC address + -- @return o new instance of Comm + new = function(self, iface, src_mac, dst_mac) + local o = { + iface = iface, + src_mac = src_mac, + dst_mac = dst_mac, + } + setmetatable(o, self) + self.__index = self + return o + end, - -- Sets up the pcap receiving socket - -- @return status true on success - connect = function(self) - self.socket = nmap.new_socket() - self.socket:set_timeout(10000) + -- Sets up the pcap receiving socket + -- @return status true on success + connect = function(self) + self.socket = nmap.new_socket() + self.socket:set_timeout(10000) - -- there's probably a more elegant way of doing this - local mac = {} - for i=1, #self.src_mac do table.insert(mac, select(2,bin.unpack("H", self.src_mac, i))) end - mac = stdnse.strjoin(":", mac) + -- there's probably a more elegant way of doing this + local mac = {} + for i=1, #self.src_mac do table.insert(mac, select(2,bin.unpack("H", self.src_mac, i))) end + mac = stdnse.strjoin(":", mac) - -- let's set a filter on PPPoE we can then check what packet is ours, - -- based on the HOST_UNIQUE tag, if we need to - self.socket:pcap_open(self.iface, 1500, false, "ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 and ether dst " .. mac) - return true - end, + -- let's set a filter on PPPoE we can then check what packet is ours, + -- based on the HOST_UNIQUE tag, if we need to + self.socket:pcap_open(self.iface, 1500, false, "ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 and ether dst " .. mac) + return true + end, - -- Sends a packet - -- @param data class containing the request to send - -- @return status true on success, false on failure - send = function(self, data) - local eth_type = ( data.header.code == PPPoE.Code.SESSION_DATA ) and 0x8864 or 0x8863 - local ether = bin.pack(">AAS", self.dst_mac, self.src_mac, eth_type) - local p = packet.Frame:new(ether .. tostring(data)) + -- Sends a packet + -- @param data class containing the request to send + -- @return status true on success, false on failure + send = function(self, data) + local eth_type = ( data.header.code == PPPoE.Code.SESSION_DATA ) and 0x8864 or 0x8863 + local ether = bin.pack(">AAS", self.dst_mac, self.src_mac, eth_type) + local p = packet.Frame:new(ether .. tostring(data)) - local sock = nmap.new_dnet() - if ( not(sock) ) then - return false, "Failed to create raw socket" - end + local sock = nmap.new_dnet() + if ( not(sock) ) then + return false, "Failed to create raw socket" + end - local status = sock:ethernet_open(self.iface) - -- we don't actually need to do this as the script simply crashes - -- if we don't have the right permissions at this point - if ( not(status) ) then - return false, "Failed to open raw socket" - end + local status = sock:ethernet_open(self.iface) + -- we don't actually need to do this as the script simply crashes + -- if we don't have the right permissions at this point + if ( not(status) ) then + return false, "Failed to open raw socket" + end - status = sock:ethernet_send(p.frame_buf) - if ( not(status) ) then - return false, "Failed to send data" - end - sock:ethernet_close() - return true - end, + status = sock:ethernet_send(p.frame_buf) + if ( not(status) ) then + return false, "Failed to send data" + end + sock:ethernet_close() + return true + end, - -- Receive a response from the server - -- @return status true on success, false on failure - -- @return response class containing the response or - -- err string on error - recv = function(self) - local status, _, l2, l3 = self.socket:pcap_receive() - -- if we got no response, just return false as there's - -- probably not really an error - if ( not(status) ) then - return false - end + -- Receive a response from the server + -- @return status true on success, false on failure + -- @return response class containing the response or + -- err string on error + recv = function(self) + local status, _, l2, l3 = self.socket:pcap_receive() + -- if we got no response, just return false as there's + -- probably not really an error + if ( not(status) ) then + return false + end - local header = PPPoE.Header.parse(l3) - local p = packet.Frame:new(l2..l3) + local header = PPPoE.Header.parse(l3) + local p = packet.Frame:new(l2..l3) - -- there's probably a more elegant way of doing this - if ( EtherType.PPPOE_DISCOVERY == p.ether_type ) then - if ( header.code == PPPoE.Code.PADO ) then - local pado = PPPoE.PADO.parse(l3) - pado.mac_srv = p.mac_src - return true, pado - elseif ( header.code == PPPoE.Code.PADS ) then - local pads = PPPoE.PADS.parse(l3) - return true, pads - elseif ( header.code == PPPoE.Code.PADT ) then - local pads = PPPoE.PADT.parse(l3) - return true, pads - end - elseif ( EtherType.PPPOE_SESSION == p.ether_type ) then - return true, PPPoE.SessionData.parse(l3) - end - return false, ("Received unsupported response, can't decode code (%d)"):format(header.code) - end, + -- there's probably a more elegant way of doing this + if ( EtherType.PPPOE_DISCOVERY == p.ether_type ) then + if ( header.code == PPPoE.Code.PADO ) then + local pado = PPPoE.PADO.parse(l3) + pado.mac_srv = p.mac_src + return true, pado + elseif ( header.code == PPPoE.Code.PADS ) then + local pads = PPPoE.PADS.parse(l3) + return true, pads + elseif ( header.code == PPPoE.Code.PADT ) then + local pads = PPPoE.PADT.parse(l3) + return true, pads + end + elseif ( EtherType.PPPOE_SESSION == p.ether_type ) then + return true, PPPoE.SessionData.parse(l3) + end + return false, ("Received unsupported response, can't decode code (%d)"):format(header.code) + end, - -- Does an "exchange", ie, sends a request and waits for a response - -- @param data class containing the request to send - -- @return status true on success, false on failure - -- @return response class containing the response or - -- err string on error - exch = function(self, data) - local status, err = self:send(data) - if ( not(status) ) then - return false, err - end - local retries, resp = 3, nil + -- Does an "exchange", ie, sends a request and waits for a response + -- @param data class containing the request to send + -- @return status true on success, false on failure + -- @return response class containing the response or + -- err string on error + exch = function(self, data) + local status, err = self:send(data) + if ( not(status) ) then + return false, err + end + local retries, resp = 3, nil - repeat - status, resp = self:recv() - if ( data.header and 0 == data.header.session ) then - return true, resp - elseif ( data.header and data.header.session == resp.header.session ) then - return true, resp - end - retries = retries - 1 - until(retries == 0) + repeat + status, resp = self:recv() + if ( data.header and 0 == data.header.session ) then + return true, resp + elseif ( data.header and data.header.session == resp.header.session ) then + return true, resp + end + retries = retries - 1 + until(retries == 0) - return false, "Failed to retrieve proper PPPoE response" - end, + return false, "Failed to retrieve proper PPPoE response" + end, } -- The Helper class is the main script interface Helper = { - -- Creates a new instance of Helper - -- @param iface string containing the name of the interface to use - -- @return o new instance on success, nil on failure - new = function(self, iface) - local o = { - iface = iface, + -- Creates a new instance of Helper + -- @param iface string containing the name of the interface to use + -- @return o new instance on success, nil on failure + new = function(self, iface) + local o = { + iface = iface, - -- set the LCP identifier to 0 - identifier = 0, - } - setmetatable(o, self) - self.__index = self + -- set the LCP identifier to 0 + identifier = 0, + } + setmetatable(o, self) + self.__index = self - if ( not(nmap.is_privileged()) ) then - return nil, "The PPPoE library requires Nmap to be run in privileged mode" - end + if ( not(nmap.is_privileged()) ) then + return nil, "The PPPoE library requires Nmap to be run in privileged mode" + end - -- get src_mac - local info = nmap.get_interface_info(iface) - if ( not(info) or not(info.mac) ) then - return nil, "Failed to get source MAC address" - end - o.comm = Comm:new(iface, info.mac) - return o - end, + -- get src_mac + local info = nmap.get_interface_info(iface) + if ( not(info) or not(info.mac) ) then + return nil, "Failed to get source MAC address" + end + o.comm = Comm:new(iface, info.mac) + return o + end, - -- Sets up the pcap socket for listening and does some other preparations - -- @return status true on success, false on failure - connect = function(self) - return self.comm:connect() - end, + -- Sets up the pcap socket for listening and does some other preparations + -- @return status true on success, false on failure + connect = function(self) + return self.comm:connect() + end, - -- Performs a PPPoE discovery initiation by sending a PADI request to the - -- ethernet broadcast address - -- @return status true on success, false on failure - -- @return pado instance of PADO on success, err string on failure - discoverInit = function(self) - local padi = PPPoE.PADI:new() - self.comm.dst_mac = bin.pack("H", "FF FF FF FF FF FF") - local status, err = self.comm:send(padi) - if ( not(status) ) then - return false, err - end - -- wait for a pado - local pado, retries = nil, 3 + -- Performs a PPPoE discovery initiation by sending a PADI request to the + -- ethernet broadcast address + -- @return status true on success, false on failure + -- @return pado instance of PADO on success, err string on failure + discoverInit = function(self) + local padi = PPPoE.PADI:new() + self.comm.dst_mac = bin.pack("H", "FF FF FF FF FF FF") + local status, err = self.comm:send(padi) + if ( not(status) ) then + return false, err + end + -- wait for a pado + local pado, retries = nil, 3 - repeat - status, pado = self.comm:recv() - if ( not(status) ) then - return status, pado - end - retries = retries - 1 - until( pado.tags or retries == 0 ) - if ( not(pado.tags) ) then - return false, "PADO response containined no tags" - end + repeat + status, pado = self.comm:recv() + if ( not(status) ) then + return status, pado + end + retries = retries - 1 + until( pado.tags or retries == 0 ) + if ( not(pado.tags) ) then + return false, "PADO response containined no tags" + end - local pado_host_unique - for _, tag in ipairs(pado.tags) do - if ( PPPoE.TagType.HOST_UNIQUE == tag.tag ) then - pado_host_unique = tag.raw - end - end + local pado_host_unique + for _, tag in ipairs(pado.tags) do + if ( PPPoE.TagType.HOST_UNIQUE == tag.tag ) then + pado_host_unique = tag.raw + end + end - -- store the tags for later use - self.tags = pado.tags - self.comm.dst_mac = pado.mac_srv + -- store the tags for later use + self.tags = pado.tags + self.comm.dst_mac = pado.mac_srv - if ( pado_host_unique and - pado_host_unique ~= padi.tags[PPPoE.TagType.HOST_UNIQUE] ) then - -- currently, we don't handle this, we probably should - -- in order to do so, we need to split the function exch - -- to recv and send - return false, "Got incorrect answer" - end + if ( pado_host_unique and + pado_host_unique ~= padi.tags[PPPoE.TagType.HOST_UNIQUE] ) then + -- currently, we don't handle this, we probably should + -- in order to do so, we need to split the function exch + -- to recv and send + return false, "Got incorrect answer" + end - return true, pado - end, + return true, pado + end, - -- Performs a Discovery Request by sending PADR to the PPPoE ethernet - -- address - -- @return status true on success, false on failure - -- @return pads instance of PADS on success - discoverRequest = function(self) + -- Performs a Discovery Request by sending PADR to the PPPoE ethernet + -- address + -- @return status true on success, false on failure + -- @return pads instance of PADS on success + discoverRequest = function(self) - -- remove the AC-Name tag if there is one - local function getTag(tag) - for _, t in ipairs(self.tags) do - if ( tag == t.tag ) then - return t - end - end - end + -- remove the AC-Name tag if there is one + local function getTag(tag) + for _, t in ipairs(self.tags) do + if ( tag == t.tag ) then + return t + end + end + end - local taglist = { - PPPoE.TagType.SERVICE_NAME, - PPPoE.TagType.HOST_UNIQUE, - PPPoE.TagType.AC_COOKIE - } + local taglist = { + PPPoE.TagType.SERVICE_NAME, + PPPoE.TagType.HOST_UNIQUE, + PPPoE.TagType.AC_COOKIE + } - local tags = {} - for _, t in ipairs(taglist) do - if ( getTag(t) ) then - table.insert(tags, getTag(t)) - end - end + local tags = {} + for _, t in ipairs(taglist) do + if ( getTag(t) ) then + table.insert(tags, getTag(t)) + end + end - local padr = PPPoE.PADR:new(tags) - local status, pads = self.comm:exch(padr) + local padr = PPPoE.PADR:new(tags) + local status, pads = self.comm:exch(padr) - if ( status ) then - self.session = pads.header.session - end + if ( status ) then + self.session = pads.header.session + end - return status, pads - end, + return status, pads + end, - -- Attempts to specify a method for authentication - -- If the server responds with another method it's NAK:ed and we try to set - -- our requested method instead. If this fails, we return a failure - -- @param method string containing one of the following methods: - -- MSCHAPv1, MSCHAPv2 or PAP - -- @return status true on success, false on failure - -- err string containing error message on failure - setAuthMethod = function(self, method) + -- Attempts to specify a method for authentication + -- If the server responds with another method it's NAK:ed and we try to set + -- our requested method instead. If this fails, we return a failure + -- @param method string containing one of the following methods: + -- MSCHAPv1, MSCHAPv2 or PAP + -- @return status true on success, false on failure + -- err string containing error message on failure + setAuthMethod = function(self, method) - local AuthMethod = { - methods = { - { name = "EAP", value = bin.pack("H", "C227") }, - { name = "MSCHAPv1", value = bin.pack("H", "C22380") }, - { name = "MSCHAPv2", value = bin.pack("H", "C22381") }, - { name = "PAP", value = bin.pack("H", "C023") }, - } - } + local AuthMethod = { + methods = { + { name = "EAP", value = bin.pack("H", "C227") }, + { name = "MSCHAPv1", value = bin.pack("H", "C22380") }, + { name = "MSCHAPv2", value = bin.pack("H", "C22381") }, + { name = "PAP", value = bin.pack("H", "C023") }, + } + } - AuthMethod.byName = function(name) - for _, m in ipairs(AuthMethod.methods) do - if ( m.name == name ) then - return m - end - end - end + AuthMethod.byName = function(name) + for _, m in ipairs(AuthMethod.methods) do + if ( m.name == name ) then + return m + end + end + end - AuthMethod.byValue = function(value) - for _, m in ipairs(AuthMethod.methods) do - if ( m.value == value ) then - return m - end - end - end + AuthMethod.byValue = function(value) + for _, m in ipairs(AuthMethod.methods) do + if ( m.value == value ) then + return m + end + end + end - local auth_data = ( AuthMethod.byName(method) and AuthMethod.byName(method).value ) - if ( not(auth_data) ) then - return false, ("Unsupported authentication mode (%s)"):format(method) - end + local auth_data = ( AuthMethod.byName(method) and AuthMethod.byName(method).value ) + if ( not(auth_data) ) then + return false, ("Unsupported authentication mode (%s)"):format(method) + end - self.identifier = self.identifier + 1 + self.identifier = self.identifier + 1 - -- First do a Configuration Request - local options = { LCP.ConfigOption:new(LCP.ConfigOption.MRU, 1492) } - local lcp_req = LCP.ConfigRequest:new(self.identifier, options) - local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) - local status, resp = self.comm:exch(sess_req) + -- First do a Configuration Request + local options = { LCP.ConfigOption:new(LCP.ConfigOption.MRU, 1492) } + local lcp_req = LCP.ConfigRequest:new(self.identifier, options) + local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) + local status, resp = self.comm:exch(sess_req) - if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then - return false, "Unexpected packet type was received" - end + if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then + return false, "Unexpected packet type was received" + end - -- Make sure we got a Configuration Request in return - local lcp_header = LCP.Header.parse(resp.data) - if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then - return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code) - end + -- Make sure we got a Configuration Request in return + local lcp_header = LCP.Header.parse(resp.data) + if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then + return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code) + end - local config_req = LCP.ConfigRequest.parse(resp.data) - if ( not(config_req.options) ) then - return false, "Failed to retrieve any options from response" - end + local config_req = LCP.ConfigRequest.parse(resp.data) + if ( not(config_req.options) ) then + return false, "Failed to retrieve any options from response" + end - local auth_proposed = config_req.options:getById(LCP.ConfigOption.AUTH_PROTO) + local auth_proposed = config_req.options:getById(LCP.ConfigOption.AUTH_PROTO) - if ( auth_proposed.raw ~= auth_data ) then - local options = { LCP.ConfigOption:new(LCP.ConfigOption.AUTH_PROTO, nil, bin.pack("A", auth_data)) } - local lcp_req = LCP.ConfigNak:new(self.identifier, options) - local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) - local status, resp = self.comm:exch(sess_req) + if ( auth_proposed.raw ~= auth_data ) then + local options = { LCP.ConfigOption:new(LCP.ConfigOption.AUTH_PROTO, nil, bin.pack("A", auth_data)) } + local lcp_req = LCP.ConfigNak:new(self.identifier, options) + local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) + local status, resp = self.comm:exch(sess_req) - if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then - return false, "Unexpected packet type was received" - end + if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then + return false, "Unexpected packet type was received" + end - -- Make sure we got a Configuration Request in return - local lcp_header = LCP.Header.parse(resp.data) - if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then - return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code) - end + -- Make sure we got a Configuration Request in return + local lcp_header = LCP.Header.parse(resp.data) + if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then + return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code) + end - config_req = LCP.ConfigRequest.parse(resp.data) + config_req = LCP.ConfigRequest.parse(resp.data) - -- if the authentication methods match, send an ACK - if ( config_req.options:getById(LCP.ConfigOption.AUTH_PROTO).raw == auth_data ) then - -- The ACK is essential the Config Request, only with a different code - -- Do a dirty attempt to just replace the code and send the request back as an ack - self.identifier = self.identifier + 1 + -- if the authentication methods match, send an ACK + if ( config_req.options:getById(LCP.ConfigOption.AUTH_PROTO).raw == auth_data ) then + -- The ACK is essential the Config Request, only with a different code + -- Do a dirty attempt to just replace the code and send the request back as an ack + self.identifier = self.identifier + 1 - local lcp_req = LCP.ConfigAck:new(config_req.header.identifier, config_req.options:getTable()) - local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) - local status, resp = self.comm:send(sess_req) + local lcp_req = LCP.ConfigAck:new(config_req.header.identifier, config_req.options:getTable()) + local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) + local status, resp = self.comm:send(sess_req) - return true - end + return true + end - return false, "Authentication method was not accepted" - end + return false, "Authentication method was not accepted" + end - return false, "Failed to negotiate authentication mechanism" - end, + return false, "Failed to negotiate authentication mechanism" + end, - -- Sends a LCP Terminate Request and waits for an ACK - -- Attempts to do so 10 times before aborting - -- @return status true on success false on failure - close = function(self) - local tries = 10 - repeat - if ( 0 == self.session ) then - break - end - local lcp_req = LCP.TerminateRequest:new(self.identifier) - local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) - local status, resp = self.comm:exch(sess_req) - if ( status and resp.header and resp.header.code ) then - if ( PPPoE.Code.SESSION_DATA == resp.header.code ) then - local lcp_header = LCP.Header.parse(resp.data) - if ( LCP.Code.TERMINATE_ACK == lcp_header.code ) then - break - end - end - end - tries = tries - 1 - until( tries == 0 ) + -- Sends a LCP Terminate Request and waits for an ACK + -- Attempts to do so 10 times before aborting + -- @return status true on success false on failure + close = function(self) + local tries = 10 + repeat + if ( 0 == self.session ) then + break + end + local lcp_req = LCP.TerminateRequest:new(self.identifier) + local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req)) + local status, resp = self.comm:exch(sess_req) + if ( status and resp.header and resp.header.code ) then + if ( PPPoE.Code.SESSION_DATA == resp.header.code ) then + local lcp_header = LCP.Header.parse(resp.data) + if ( LCP.Code.TERMINATE_ACK == lcp_header.code ) then + break + end + end + end + tries = tries - 1 + until( tries == 0 ) - self.comm:exch(PPPoE.PADT:new(self.session)) + self.comm:exch(PPPoE.PADT:new(self.session)) - return true - end, + return true + end, } diff --git a/nselib/rmi.lua b/nselib/rmi.lua index d3638cd16..356f4148f 100644 --- a/nselib/rmi.lua +++ b/nselib/rmi.lua @@ -16,18 +16,18 @@ -- functionality by lifting over more stuff from the jdk. -- -- The interesting classes in OpenJDK are: --- java.io.ObjectStreamConstants --- java.io.ObjectStreamClass --- java.io.ObjectInputStream --- sun.rmi.transport.StreamRemoteCall --- and a few more. +-- java.io.ObjectStreamConstants +-- java.io.ObjectStreamClass +-- java.io.ObjectInputStream +-- sun.rmi.transport.StreamRemoteCall +-- and a few more. -- -- If you want to add calls to classes you know of, you can use e.g Jode to decompile the -- stub-class or skeleton class and find out the details that are needed to perform an -- RMI method invokation. Those are --- Class hashcode --- Method number (each method gets a number) --- Arguments f +-- Class hashcode +-- Method number (each method gets a number) +-- Arguments f -- You also need the object id (so the remote server knows what instance you are talking to). That can be -- fetched from the registry (afaik) but not currently implemented. Some object ids are static : the registry is always 0 -- @@ -52,18 +52,18 @@ _ENV = stdnse.module("rmi", stdnse.seeall) -- Some lazy shortcuts local function dbg(str,...) - local arg={...} - stdnse.print_debug(3,"RMI:"..str, table.unpack(arg)) + local arg={...} + stdnse.print_debug(3,"RMI:"..str, table.unpack(arg)) end -- Convenience function to both print an error message and return -- Example usage : -- if foo ~= "gazonk" then --- return doh("Foo should be gazonk but was %s", foo) +-- return doh("Foo should be gazonk but was %s", foo) -- end local function doh(str,...) - local arg={...} - stdnse.print_debug("RMI-ERR:"..tostring(str), table.unpack(arg)) - return false, str + local arg={...} + stdnse.print_debug("RMI-ERR:"..tostring(str), table.unpack(arg)) + return false, str end --- @@ -74,56 +74,56 @@ end -- until flush is called. When flush is called, it either sends the data to the socket OR -- returns the data, if no socket has been set. --@usage: --- local bWriter = BufferedWriter:new(socket) --- local breader= BufferedReader:new(socket) +-- local bWriter = BufferedWriter:new(socket) +-- local breader= BufferedReader:new(socket) -- --- bWriter.pack('>i', integer) --- bWrier.flush() -- sends the data +-- bWriter.pack('>i', integer) +-- bWrier.flush() -- sends the data -- --- if bsocket:canRead(4) then -- Waits until four bytes can be read --- local packetLength = bsocket:unpack('i') -- Read the four bytess --- if bsocket:canRead(packetLength) then --- -- ...continue reading packet values +-- if bsocket:canRead(4) then -- Waits until four bytes can be read +-- local packetLength = bsocket:unpack('i') -- Read the four bytess +-- if bsocket:canRead(packetLength) then +-- -- ...continue reading packet values BufferedWriter = { - new = function(self, socket) - local o = {} - setmetatable(o, self) - self.__index = self - o.writeBuffer = '' - o.pos = 1 - o.socket = socket - return o - end, + new = function(self, socket) + local o = {} + setmetatable(o, self) + self.__index = self + o.writeBuffer = '' + o.pos = 1 + o.socket = socket + return o + end, - -- Sends data over the socket - -- (Actually, just buffers until flushed) - -- @return Status (true or false). - -- @return Error code (if status is false). - send = function( self, data ) - self.writeBuffer = self.writeBuffer .. data - end, - -- Convenience function, wraps bin - pack = function(self, fmt, ... ) - local arg={...} - self.writeBuffer = self.writeBuffer .. bin.pack( fmt, table.unpack(arg)) - end, + -- Sends data over the socket + -- (Actually, just buffers until flushed) + -- @return Status (true or false). + -- @return Error code (if status is false). + send = function( self, data ) + self.writeBuffer = self.writeBuffer .. data + end, + -- Convenience function, wraps bin + pack = function(self, fmt, ... ) + local arg={...} + self.writeBuffer = self.writeBuffer .. bin.pack( fmt, table.unpack(arg)) + end, - -- This function flushes the buffer contents, thereby emptying - -- the buffer. If a socket has been supplied, that's where it will be sent - -- otherwise the buffer contents are returned - --@return status - --@return content of buffer, in case no socket was used - flush = function(self) + -- This function flushes the buffer contents, thereby emptying + -- the buffer. If a socket has been supplied, that's where it will be sent + -- otherwise the buffer contents are returned + --@return status + --@return content of buffer, in case no socket was used + flush = function(self) - local content = self.writeBuffer - self.writeBuffer = '' + local content = self.writeBuffer + self.writeBuffer = '' - if not self.socket then - return true, content - end - return self.socket:send(content) - end, + if not self.socket then + return true, content + end + return self.socket:send(content) + end, } --- @@ -138,102 +138,102 @@ BufferedWriter = { -- data to read. Since this class does not parse arguments to unpack, it does not -- know how much data to read ahead on those calls. --@usage: --- local bWriter = BufferedWriter:new(socket) --- local breader= BufferedReader:new(socket) +-- local bWriter = BufferedWriter:new(socket) +-- local breader= BufferedReader:new(socket) -- --- bWriter.pack('>i', integer) --- bWrier.flush() -- sends the data +-- bWriter.pack('>i', integer) +-- bWrier.flush() -- sends the data -- --- if bsocket:canRead(4) then -- Waits until four bytes can be read --- local packetLength = bsocket:unpack('i') -- Read the four bytess --- if bsocket:canRead(packetLength) then --- -- ...continue reading packet values +-- if bsocket:canRead(4) then -- Waits until four bytes can be read +-- local packetLength = bsocket:unpack('i') -- Read the four bytess +-- if bsocket:canRead(packetLength) then +-- -- ...continue reading packet values BufferedReader = { - new = function(self, socket, readBuffer) - local o = {} - setmetatable(o, self) - self.__index = self - o.readBuffer = readBuffer -- May be nil - o.pos = 1 - o.socket = socket -- May also be nil - return o - end, - --- - -- This method blocks until the specified number of bytes - -- have been read from the socket and are avaiable for - -- the caller to read, e.g via the unpack function - canRead= function(self,count) - local status, data - self.readBuffer = self.readBuffer or "" - local missing = self.pos + count - #self.readBuffer -1 - if ( missing > 0) then - if self.socket == nil then - return doh("Not enough data in static buffer") - end + new = function(self, socket, readBuffer) + local o = {} + setmetatable(o, self) + self.__index = self + o.readBuffer = readBuffer -- May be nil + o.pos = 1 + o.socket = socket -- May also be nil + return o + end, + --- + -- This method blocks until the specified number of bytes + -- have been read from the socket and are avaiable for + -- the caller to read, e.g via the unpack function + canRead= function(self,count) + local status, data + self.readBuffer = self.readBuffer or "" + local missing = self.pos + count - #self.readBuffer -1 + if ( missing > 0) then + if self.socket == nil then + return doh("Not enough data in static buffer") + end - status, data = self.socket:receive_bytes( missing ) - if ( not(status) ) then - return false, data - end - self.readBuffer = self.readBuffer .. data - end - -- Now and then, we flush the buffer - if ( self.pos > 1024) then - self.readBuffer = self.readBuffer:sub( self.pos ) - self.pos = 1 - end - return true - end, - --- - --@return Returns the number of bytes already available for reading - bufferSize = function(self) - return #self.readBuffer +1 -self.pos - end, - --- - -- This function works just like bin.unpack (in fact, it is - -- merely a wrapper around it. However, it uses the data - -- already read into the buffer, and the internal position - --@param format - see bin - --@return the unpacked value (NOT the index) - unpack = function(self,format) - local ret = {bin.unpack(format, self.readBuffer, self.pos)} - self.pos = ret[1] - return table.unpack(ret,2) - end, - --- - -- This function works just like bin.unpack (in fact, it is - -- merely a wrapper around it. However, it uses the data - -- already read into the buffer, and the internal position. - -- This method does not update the current position, and the - -- data can be read again - --@param format - see bin - --@return the unpacked value (NOT the index) - peekUnpack = function(self,format) - local ret = {bin.unpack(format, self.readBuffer, self.pos)} - return table.unpack(ret,2) - end, - --- - -- Tries to read a byte, without consuming it. - --@return status - --@return bytevalue - peekByte = function(self) - if self:canRead(1) then - return true, self:peekUnpack('C') - end - return false - end, - --- - -- Skips a number of bytes - --@param len the number of bytes to skip - skip = function(self, len) - if(#self.readBuffer < len + self.pos) then - return doh("ERROR: reading too far ahead") - end - local skipped = self.readBuffer:sub(self.pos, self.pos+len-1) - self.pos = self.pos + len - return true, skipped - end, + status, data = self.socket:receive_bytes( missing ) + if ( not(status) ) then + return false, data + end + self.readBuffer = self.readBuffer .. data + end + -- Now and then, we flush the buffer + if ( self.pos > 1024) then + self.readBuffer = self.readBuffer:sub( self.pos ) + self.pos = 1 + end + return true + end, + --- + --@return Returns the number of bytes already available for reading + bufferSize = function(self) + return #self.readBuffer +1 -self.pos + end, + --- + -- This function works just like bin.unpack (in fact, it is + -- merely a wrapper around it. However, it uses the data + -- already read into the buffer, and the internal position + --@param format - see bin + --@return the unpacked value (NOT the index) + unpack = function(self,format) + local ret = {bin.unpack(format, self.readBuffer, self.pos)} + self.pos = ret[1] + return table.unpack(ret,2) + end, + --- + -- This function works just like bin.unpack (in fact, it is + -- merely a wrapper around it. However, it uses the data + -- already read into the buffer, and the internal position. + -- This method does not update the current position, and the + -- data can be read again + --@param format - see bin + --@return the unpacked value (NOT the index) + peekUnpack = function(self,format) + local ret = {bin.unpack(format, self.readBuffer, self.pos)} + return table.unpack(ret,2) + end, + --- + -- Tries to read a byte, without consuming it. + --@return status + --@return bytevalue + peekByte = function(self) + if self:canRead(1) then + return true, self:peekUnpack('C') + end + return false + end, + --- + -- Skips a number of bytes + --@param len the number of bytes to skip + skip = function(self, len) + if(#self.readBuffer < len + self.pos) then + return doh("ERROR: reading too far ahead") + end + local skipped = self.readBuffer:sub(self.pos, self.pos+len-1) + self.pos = self.pos + len + return true, skipped + end, } @@ -245,13 +245,13 @@ BufferedReader = { -- writeInt(self, value) and readInt() respectively local JavaTypes = { - {name = 'Int', expr = '>i', len= 4}, - {name = 'UnsignedInt', expr = '>I', len= 4}, - {name = 'Short', expr = '>s', len= 2}, - {name = 'UnsignedShort', expr = '>S', len= 2}, - {name = 'Long', expr = '>l', len= 8}, - {name = 'UnsignedLong', expr = '>L', len= 8}, - {name = 'Byte', expr = '>C', len= 1}, + {name = 'Int', expr = '>i', len= 4}, + {name = 'UnsignedInt', expr = '>I', len= 4}, + {name = 'Short', expr = '>s', len= 2}, + {name = 'UnsignedShort', expr = '>S', len= 2}, + {name = 'Long', expr = '>l', len= 8}, + {name = 'UnsignedLong', expr = '>L', len= 8}, + {name = 'Byte', expr = '>C', len= 1}, } --- @@ -263,49 +263,49 @@ local JavaTypes = { -- checks transparently, i.e the caller does not have to check if enough data is available -- -- @usage: --- local dos = JavaDOS:new(BufferedWriter:new(socket)) --- local dos = JavaDIS:new(BufferedReader:new(socket)) --- dos:writeUTF("Hello world") --- dos:writeInt(3) --- dos:writeLong(3) --- dos:flush() -- send data --- local answer = dis:readUTF() --- local int = dis:readInt() --- local long = dis:readLong() +-- local dos = JavaDOS:new(BufferedWriter:new(socket)) +-- local dos = JavaDIS:new(BufferedReader:new(socket)) +-- dos:writeUTF("Hello world") +-- dos:writeInt(3) +-- dos:writeLong(3) +-- dos:flush() -- send data +-- local answer = dis:readUTF() +-- local int = dis:readInt() +-- local long = dis:readLong() JavaDOS = { - new = function (self,bWriter) - local o = {} -- create new object if user does not provide one - setmetatable(o, self) - self.__index = self -- DIY inheritance - o.bWriter = bWriter - return o - end, - -- This closure method generates all writer methods on the fly - -- according to the definitions in JavaTypes - _generateWriterFunc = function(self, javatype) - local functionName = 'write'..javatype.name - local newFunc = function(_self, value) - --dbg(functionName .."(%s) called" ,tostring(value)) - return _self:pack(javatype.expr, value) - end - self[functionName] = newFunc - end, + new = function (self,bWriter) + local o = {} -- create new object if user does not provide one + setmetatable(o, self) + self.__index = self -- DIY inheritance + o.bWriter = bWriter + return o + end, + -- This closure method generates all writer methods on the fly + -- according to the definitions in JavaTypes + _generateWriterFunc = function(self, javatype) + local functionName = 'write'..javatype.name + local newFunc = function(_self, value) + --dbg(functionName .."(%s) called" ,tostring(value)) + return _self:pack(javatype.expr, value) + end + self[functionName] = newFunc + end, - writeUTF = function(self, text) - -- TODO: Make utf-8 of it - return self:pack('>P', text) - end, - pack = function(self, ...) - local arg={...} - return self.bWriter:pack(table.unpack(arg)) - end, - write = function(self, data) - return self.bWriter:send(data) - end, - flush = function(self) - return self.bWriter:flush() - end, + writeUTF = function(self, text) + -- TODO: Make utf-8 of it + return self:pack('>P', text) + end, + pack = function(self, ...) + local arg={...} + return self.bWriter:pack(table.unpack(arg)) + end, + write = function(self, data) + return self.bWriter:send(data) + end, + flush = function(self) + return self.bWriter:flush() + end, } --- @@ -317,296 +317,296 @@ JavaDOS = { -- checks transparently, i.e the caller does not have to check if enough data is available -- -- @usage: --- local dos = JavaDOS:new(BufferedWriter:new(socket)) --- local dos = JavaDIS:new(BufferedReader:new(socket)) --- dos:writeUTF("Hello world") --- dos:writeInt(3) --- dos:writeLong(3) --- dos:flush() -- send data --- local answer = dis:readUTF() --- local int = dis:readInt() --- local long = dis:readLong() +-- local dos = JavaDOS:new(BufferedWriter:new(socket)) +-- local dos = JavaDIS:new(BufferedReader:new(socket)) +-- dos:writeUTF("Hello world") +-- dos:writeInt(3) +-- dos:writeLong(3) +-- dos:flush() -- send data +-- local answer = dis:readUTF() +-- local int = dis:readInt() +-- local long = dis:readLong() JavaDIS = { - new = function (self,bReader) - local o = {} -- create new object if user does not provide one - setmetatable(o, self) - self.__index = self -- DIY inheritance - o.bReader = bReader - return o - end, + new = function (self,bReader) + local o = {} -- create new object if user does not provide one + setmetatable(o, self) + self.__index = self -- DIY inheritance + o.bReader = bReader + return o + end, - -- This closure method generates all reader methods (except unstandard ones) on the fly - -- according to the definitions in JavaTypes. - _generateReaderFunc = function(self, javatype) - local functionName = 'read'..javatype.name - local newFunc = function(_self) - --dbg(functionName .."() called" ) - if not _self.bReader:canRead(javatype.len) then - local err = ("Not enough data in buffer (%d required by %s)"):format(javatype.len, functionName) - return doh(err) - end - return true, _self.bReader:unpack(javatype.expr) - end - self[functionName] = newFunc - end, - -- This is a bit special, since we do not know beforehand how many bytes must be read. Therfore - -- this cannot be generated on the fly like the others. - readUTF = function(self, text) - -- First, we need to read the length, 2 bytes - if not self.bReader:canRead(2) then-- Length of the string is two bytes - return false, "Not enough data in buffer [0]" - end - -- We do it as a 'peek', so bin can reuse the data to unpack with 'P' - local len = self.bReader:peekUnpack('>S') - --dbg("Reading utf, len %d" , len) - -- Check that we have data - if not self.bReader:canRead(len) then - return false, "Not enough data in buffer [1]" - end - -- For some reason, the 'P' switch does not work for me. - -- Probably some idiot thing. This is a hack: - local val = self.bReader.readBuffer:sub(self.bReader.pos+2, self.bReader.pos+len+2-1) - self.bReader.pos = self.bReader.pos+len+2 - -- Someone smarter than me can maybe get this working instead: - --local val = self.bReader:unpack('P') - --dbg("Read UTF: %s", val) - return true, val - end, - readLongAsHexString = function(self) - if not self.bReader:canRead(8) then-- Length of the string is two bytes - return false, "Not enough data in buffer [3]" - end - return true, self.bReader:unpack('H8') + -- This closure method generates all reader methods (except unstandard ones) on the fly + -- according to the definitions in JavaTypes. + _generateReaderFunc = function(self, javatype) + local functionName = 'read'..javatype.name + local newFunc = function(_self) + --dbg(functionName .."() called" ) + if not _self.bReader:canRead(javatype.len) then + local err = ("Not enough data in buffer (%d required by %s)"):format(javatype.len, functionName) + return doh(err) + end + return true, _self.bReader:unpack(javatype.expr) + end + self[functionName] = newFunc + end, + -- This is a bit special, since we do not know beforehand how many bytes must be read. Therfore + -- this cannot be generated on the fly like the others. + readUTF = function(self, text) + -- First, we need to read the length, 2 bytes + if not self.bReader:canRead(2) then-- Length of the string is two bytes + return false, "Not enough data in buffer [0]" + end + -- We do it as a 'peek', so bin can reuse the data to unpack with 'P' + local len = self.bReader:peekUnpack('>S') + --dbg("Reading utf, len %d" , len) + -- Check that we have data + if not self.bReader:canRead(len) then + return false, "Not enough data in buffer [1]" + end + -- For some reason, the 'P' switch does not work for me. + -- Probably some idiot thing. This is a hack: + local val = self.bReader.readBuffer:sub(self.bReader.pos+2, self.bReader.pos+len+2-1) + self.bReader.pos = self.bReader.pos+len+2 + -- Someone smarter than me can maybe get this working instead: + --local val = self.bReader:unpack('P') + --dbg("Read UTF: %s", val) + return true, val + end, + readLongAsHexString = function(self) + if not self.bReader:canRead(8) then-- Length of the string is two bytes + return false, "Not enough data in buffer [3]" + end + return true, self.bReader:unpack('H8') - end, - skip = function(self, len) - return self.bReader:skip(len) - end, - canRead = function(self, len) - return self.bReader:canRead(len) - end, + end, + skip = function(self, len) + return self.bReader:skip(len) + end, + canRead = function(self, len) + return self.bReader:canRead(len) + end, } -- Generate writer-functions on the JavaDOS/JavaDIS classes on the fly for _,x in ipairs(JavaTypes) do - JavaDOS._generateWriterFunc(JavaDOS, x) - JavaDIS._generateReaderFunc(JavaDIS, x) + JavaDOS._generateWriterFunc(JavaDOS, x) + JavaDIS._generateReaderFunc(JavaDIS, x) end --- -- This class represents a java class and is what is returned by the library -- when invoking a remote function. Therefore, this can also represent a java -- object instance. JavaClass = { - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - customDataFormatter = nil, + customDataFormatter = nil, - setName = function( self, name ) - dbg("Setting class name to %s", name) - self.name = name - end, - setSerialID = function( self, serial ) self.serial = serial end, - setFlags = function( self, flags ) - self.flags = RMIUtils.flagsToString(flags) - self._binaryflags = flags - end, + setName = function( self, name ) + dbg("Setting class name to %s", name) + self.name = name + end, + setSerialID = function( self, serial ) self.serial = serial end, + setFlags = function( self, flags ) + self.flags = RMIUtils.flagsToString(flags) + self._binaryflags = flags + end, - isExternalizable = function(self) - if self._binaryFlags == nil then return false end + isExternalizable = function(self) + if self._binaryFlags == nil then return false end - return bit.band(self._binaryflags, RMIUtils.SC_EXTERNALIZABLE) - end, + return bit.band(self._binaryflags, RMIUtils.SC_EXTERNALIZABLE) + end, - addField = function( self, field ) - if self.fields == nil then self.fields = {} end - table.insert( self.fields, field ) - --self[field.name] = field - end, - setSuperClass = function(self,super) self.superClass = super end, + addField = function( self, field ) + if self.fields == nil then self.fields = {} end + table.insert( self.fields, field ) + --self[field.name] = field + end, + setSuperClass = function(self,super) self.superClass = super end, - setCustomData = function(self, data) self.customData = data end, - getCustomData = function(self) return self.customData end, + setCustomData = function(self, data) self.customData = data end, + getCustomData = function(self) return self.customData end, - setInterfaces = function(self,ifaces) self.ifaces = ifaces end, - getName = function( self ) return self.name end, - getSuperClass = function(self) return self.superClass end, - getSerialID = function( self ) return self.serial end, - getFlags = function( self ) return self.flags end, - getFields = function( self ) return self.fields end, - getFieldByName = function( self, name ) - if self.fields == nil then return end - for i=1, #self.fields do - if ( self.fields[i].name == name ) then - return self.fields[i] - end - end - end, + setInterfaces = function(self,ifaces) self.ifaces = ifaces end, + getName = function( self ) return self.name end, + getSuperClass = function(self) return self.superClass end, + getSerialID = function( self ) return self.serial end, + getFlags = function( self ) return self.flags end, + getFields = function( self ) return self.fields end, + getFieldByName = function( self, name ) + if self.fields == nil then return end + for i=1, #self.fields do + if ( self.fields[i].name == name ) then + return self.fields[i] + end + end + end, - __tostring = function( self ) - local data - if self.name ~=nil then - data = ("%s "):format(self.name) - else - data = "???" - end - if self.superClass~=nil then - data = data .. " extends ".. tostring( self.superClass) - end - if self.ifaces ~= nil then - data = data .. " implements " .. self.ifaces - end - if self.fields ~=nil then - for i=1, #self.fields do - if i == 1 then - data = data .. "[" - end - data = data .. tostring(self.fields[i]) - if ( i < #self.fields ) then - data = data .. ";" - else - data = data .. "]" - end + __tostring = function( self ) + local data + if self.name ~=nil then + data = ("%s "):format(self.name) + else + data = "???" + end + if self.superClass~=nil then + data = data .. " extends ".. tostring( self.superClass) + end + if self.ifaces ~= nil then + data = data .. " implements " .. self.ifaces + end + if self.fields ~=nil then + for i=1, #self.fields do + if i == 1 then + data = data .. "[" + end + data = data .. tostring(self.fields[i]) + if ( i < #self.fields ) then + data = data .. ";" + else + data = data .. "]" + end - end - end - return data - end, - toTable = function(self, customDataFormatter) - local data = {self.name} + end + end + return data + end, + toTable = function(self, customDataFormatter) + local data = {self.name} - if self.externalData ~=nil then - table.insert(data, tostring(self.externalData)) - end + if self.externalData ~=nil then + table.insert(data, tostring(self.externalData)) + end - --if self.name ~=nil then - -- data.class = self.name - --end - if self.ifaces ~= nil then - table.insert(data, " implements " .. self.ifaces) - end + --if self.name ~=nil then + -- data.class = self.name + --end + if self.ifaces ~= nil then + table.insert(data, " implements " .. self.ifaces) + end - if self.superClass~=nil then - local extends = self.superClass:toTable() - table.insert(data ,"extends") - table.insert(data, extends) - --data.extends = self.superClass:toTable() - end - if self.fields ~=nil then - table.insert(data, "fields") - local f = {} - for i=1, #self.fields do - table.insert(f, self.fields[i]:toTable()) - end - table.insert(data, f) - end + if self.superClass~=nil then + local extends = self.superClass:toTable() + table.insert(data ,"extends") + table.insert(data, extends) + --data.extends = self.superClass:toTable() + end + if self.fields ~=nil then + table.insert(data, "fields") + local f = {} + for i=1, #self.fields do + table.insert(f, self.fields[i]:toTable()) + end + table.insert(data, f) + end - if self.customData ~=nil then - local formatter = JavaClass['customDataFormatter'] - if formatter ~= nil then - local title, cdata = formatter(self.name, self.customData) - table.insert(data, title) - table.insert(data, cdata) - else - table.insert(data, "Custom data") - table.insert(data, self.customData) - end - end + if self.customData ~=nil then + local formatter = JavaClass['customDataFormatter'] + if formatter ~= nil then + local title, cdata = formatter(self.name, self.customData) + table.insert(data, title) + table.insert(data, cdata) + else + table.insert(data, "Custom data") + table.insert(data, self.customData) + end + end - return data + return data - end, + end, } --- Represents a field in an object, i.e an object member JavaField = { - new = function(self, name, typ ) - local o = {} - setmetatable(o, self) - self.__index = self - o.name = name - o.type = typ - return o - end, + new = function(self, name, typ ) + local o = {} + setmetatable(o, self) + self.__index = self + o.name = name + o.type = typ + return o + end, - setType = function( self, typ ) self.type = typ end, - setSignature = function( self, sig ) self.signature = sig end, - setName = function( self, name ) self.name = name end, - setObjectType = function( self, ot ) self.object_type = ot end, - setReference = function( self, ref ) self.ref = ref end, - setValue = function (self, val) - dbg("Setting field value to %s", tostring(val)) - self.value = val + setType = function( self, typ ) self.type = typ end, + setSignature = function( self, sig ) self.signature = sig end, + setName = function( self, name ) self.name = name end, + setObjectType = function( self, ot ) self.object_type = ot end, + setReference = function( self, ref ) self.ref = ref end, + setValue = function (self, val) + dbg("Setting field value to %s", tostring(val)) + self.value = val - end, + end, - getType = function( self ) return self.type end, - getSignature = function( self ) return self.signature end, - getName = function( self ) return self.name end, - getObjectType = function( self ) return self.object_type end, - getReference = function( self ) return self.ref end, - getValue = function( self ) return self.value end, + getType = function( self ) return self.type end, + getSignature = function( self ) return self.signature end, + getName = function( self ) return self.name end, + getObjectType = function( self ) return self.object_type end, + getReference = function( self ) return self.ref end, + getValue = function( self ) return self.value end, - __tostring = function( self ) - local data = tostring(self.type) .. " " .. tostring(self.name) - if self.value ~= nil then - data = data .." = " .. tostring(self.value) - end + __tostring = function( self ) + local data = tostring(self.type) .. " " .. tostring(self.name) + if self.value ~= nil then + data = data .." = " .. tostring(self.value) + end - return data - end, - toTable = function(self) - local data = {tostring(self.type) .. " " .. tostring(self.name)} - --print("FIELD VALUE:", self.value) - if self.value ~= nil then - if type(self.value) == 'table' then - if self.value.toTable ~=nil then - table.insert(data, self.value:toTable()) - else - table.insert(data, self.value) - end - else - data = data .." = " .. tostring(self.value) - end - end - return data - end, + return data + end, + toTable = function(self) + local data = {tostring(self.type) .. " " .. tostring(self.name)} + --print("FIELD VALUE:", self.value) + if self.value ~= nil then + if type(self.value) == 'table' then + if self.value.toTable ~=nil then + table.insert(data, self.value:toTable()) + else + table.insert(data, self.value) + end + else + data = data .." = " .. tostring(self.value) + end + end + return data + end, } --- -- Represents a java array. Internally, this is a lua list of JavaClass-instances JavaArray = { - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - o.values = {} - return o - end, - setClass = function( self, class ) self.class = class end, - setLength = function( self, length ) self.length = length end, - setValue = function(self, index, object) self.values[index] = object end, - __tostring=function(self) - local data = ("Array: %s [%d] = {"):format(tostring(self.class), self.length) + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + o.values = {} + return o + end, + setClass = function( self, class ) self.class = class end, + setLength = function( self, length ) self.length = length end, + setValue = function(self, index, object) self.values[index] = object end, + __tostring=function(self) + local data = ("Array: %s [%d] = {"):format(tostring(self.class), self.length) - for i=1, #self.values do - data = data .. self.values[i].."," - end - data = data .."}" - return data - end, - toTable = function(self) - local title = ("Array: %s [%d] = {"):format(tostring(self.class), self.length) - local t = {title = self.values} - return t - end, + for i=1, #self.values do + data = data .. self.values[i].."," + end + data = data .."}" + return data + end, + toTable = function(self) + local title = ("Array: %s [%d] = {"):format(tostring(self.class), self.length) + local t = {title = self.values} + return t + end, - getValues = function(self) return self.values end + getValues = function(self) return self.values end } @@ -614,45 +614,45 @@ JavaArray = { TC = { - TC_NULL = 0x70, - TC_REFERENCE = 0x71, - TC_CLASSDESC = 0x72, - TC_OBJECT = 0x73, - TC_STRING = 0x74, - TC_ARRAY = 0x75, - TC_CLASS = 0x76, - TC_BLOCKDATA = 0x77, - TC_ENDBLOCKDATA = 0x78, - TC_RESET = 0x79, - TC_BLOCKDATALONG = 0x7A, - TC_EXCEPTION = 0x7B, - TC_LONGSTRING = 0x7C, - TC_PROXYCLASSDESC = 0x7D, - TC_ENUM = 0x7E, + TC_NULL = 0x70, + TC_REFERENCE = 0x71, + TC_CLASSDESC = 0x72, + TC_OBJECT = 0x73, + TC_STRING = 0x74, + TC_ARRAY = 0x75, + TC_CLASS = 0x76, + TC_BLOCKDATA = 0x77, + TC_ENDBLOCKDATA = 0x78, + TC_RESET = 0x79, + TC_BLOCKDATALONG = 0x7A, + TC_EXCEPTION = 0x7B, + TC_LONGSTRING = 0x7C, + TC_PROXYCLASSDESC = 0x7D, + TC_ENUM = 0x7E, - Integer = 0x49, - Object = 0x4c, + Integer = 0x49, + Object = 0x4c, - Strings = { - [0x49] = "Integer", - [0x4c] = "Object", - [0x71] = "TC_REFERENCE", - [0x70] = "TC_NULL", - [0x71] = "TC_REFERENCE", - [0x72] = "TC_CLASSDESC", - [0x73] = "TC_OBJECT", - [0x74] = "TC_STRING", - [0x75] = "TC_ARRAY", - [0x76] = "TC_CLASS", - [0x77] = "TC_BLOCKDATA", - [0x78] = "TC_ENDBLOCKDATA", - [0x79] = "TC_RESET", - [0x7A] = "TC_BLOCKDATALONG", - [0x7B] = "TC_EXCEPTION", - [0x7C] = "TC_LONGSTRING", - [0x7D] = "TC_PROXYCLASSDESC", - [0x7E] = "TC_ENUM", - }, + Strings = { + [0x49] = "Integer", + [0x4c] = "Object", + [0x71] = "TC_REFERENCE", + [0x70] = "TC_NULL", + [0x71] = "TC_REFERENCE", + [0x72] = "TC_CLASSDESC", + [0x73] = "TC_OBJECT", + [0x74] = "TC_STRING", + [0x75] = "TC_ARRAY", + [0x76] = "TC_CLASS", + [0x77] = "TC_BLOCKDATA", + [0x78] = "TC_ENDBLOCKDATA", + [0x79] = "TC_RESET", + [0x7A] = "TC_BLOCKDATALONG", + [0x7B] = "TC_EXCEPTION", + [0x7C] = "TC_LONGSTRING", + [0x7D] = "TC_PROXYCLASSDESC", + [0x7E] = "TC_ENUM", + }, } @@ -668,85 +668,85 @@ local Proto= {Stream=0x4b, SingleOp=0x4c, Multiplex=0x4d} -- RmiDataStream = { - new = function (self,o) - o = o or {} -- create object if user does not provide one - setmetatable(o, self) - self.__index = self -- DIY inheritance - return o - end, + new = function (self,o) + o = o or {} -- create object if user does not provide one + setmetatable(o, self) + self.__index = self -- DIY inheritance + return o + end, } -- An output stream in RMI consists of transport Header information followed by a sequence of Messages. -- Out: --- Header Messages --- HttpMessage +-- Header Messages +-- HttpMessage -- Header: --- 0x4a 0x52 0x4d 0x49 Version Protocol +-- 0x4a 0x52 0x4d 0x49 Version Protocol -- (4a 52 4d 49 === JRMI) -- Version: --- 0x00 0x01 +-- 0x00 0x01 -- Protocol: --- StreamProtocol --- SingleOpProtocol --- MultiplexProtocol +-- StreamProtocol +-- SingleOpProtocol +-- MultiplexProtocol -- StreamProtocol: --- 0x4b +-- 0x4b -- SingleOpProtocol: --- 0x4c +-- 0x4c -- MultiplexProtocol: --- 0x4d +-- 0x4d -- Messages: --- Message --- Messages Message +-- Message +-- Messages Message ---- -- Connects to a remote service. The connection process creates a -- socket and does some handshaking. If this is successfull, -- we are definitely talking to an RMI service. function RmiDataStream:connect(host, port) - local status, err + local status, err - local socket = nmap.new_socket() - socket:set_timeout(5000) + local socket = nmap.new_socket() + socket:set_timeout(5000) --- local bsocket = BufferedSocket:new() - socket:connect(host,port, "tcp") + -- local bsocket = BufferedSocket:new() + socket:connect(host,port, "tcp") - -- Output and input - local dos = JavaDOS:new(BufferedWriter:new(socket)) - local dis = JavaDIS:new(BufferedReader:new(socket)) + -- Output and input + local dos = JavaDOS:new(BufferedWriter:new(socket)) + local dis = JavaDIS:new(BufferedReader:new(socket)) - -- Start sending a message -- - -- Add Header, Version and Protocol + -- Start sending a message -- + -- Add Header, Version and Protocol - --dos:write('JRMI' .. bin.pack('H', Version .. Proto.Stream)) - dos:writeInt(1246907721) -- == JRMI - dos:writeShort(Version) - dos:writeByte(Proto.Stream) - status = dos:flush() - if not status then - return doh(err) - end + --dos:write('JRMI' .. bin.pack('H', Version .. Proto.Stream)) + dos:writeInt(1246907721) -- == JRMI + dos:writeShort(Version) + dos:writeByte(Proto.Stream) + status = dos:flush() + if not status then + return doh(err) + end - -- For the StreamProtocol and the MultiplexProtocol, the server must respond with a a byte 0x4e - -- acknowledging support for the protocol, and an EndpointIdentifier that contains the host name - -- and port number that the server can see is being used by the client. - -- The client can use this information to determine its host name if it is otherwise unable to do that for security reasons. + -- For the StreamProtocol and the MultiplexProtocol, the server must respond with a a byte 0x4e + -- acknowledging support for the protocol, and an EndpointIdentifier that contains the host name + -- and port number that the server can see is being used by the client. + -- The client can use this information to determine its host name if it is otherwise unable to do that for security reasons. - -- Read ack - status, err = self:readAck(dis) - if not status then - return doh("No ack received from server:" .. tostring(err)) - end + -- Read ack + status, err = self:readAck(dis) + if not status then + return doh("No ack received from server:" .. tostring(err)) + end - -- The client must then respond with another EndpointIdentifier that contains the clients - -- default endpoint for accepting connections. This can be used by a server in the MultiplexProtocol case to identify the client. + -- The client must then respond with another EndpointIdentifier that contains the clients + -- default endpoint for accepting connections. This can be used by a server in the MultiplexProtocol case to identify the client. - dos:writeUTF("127.0.0.1") -- TODO, write our own ip instead (perhaps not necessary, since we are not using MultiplexProtocol - dos:writeInt(0) -- Port ( 0 works fine) - dos:flush() - self.dos = dos - self.dis =dis - return true + dos:writeUTF("127.0.0.1") -- TODO, write our own ip instead (perhaps not necessary, since we are not using MultiplexProtocol + dos:writeInt(0) -- Port ( 0 works fine) + dos:flush() + self.dos = dos + self.dis =dis + return true end -- Reads a DgcAck message, which is sent during conection handshake @@ -754,20 +754,20 @@ end --@return status --@return error message function RmiDataStream:readAck(dis) - local status, ack = dis:readByte() + local status, ack = dis:readByte() - if not status then return doh( "Could not read data") end + if not status then return doh( "Could not read data") end - if ack ~= 78 then - return doh("No ack received: ".. tostring(ack)) - end - local status, host = dis:readUTF() - if not status then return false, "Could not read data" end - local status, port = dis:readUnsignedInt() - if not status then return false, "Could not read data" end + if ack ~= 78 then + return doh("No ack received: ".. tostring(ack)) + end + local status, host = dis:readUTF() + if not status then return false, "Could not read data" end + local status, port = dis:readUnsignedInt() + if not status then return false, "Could not read data" end - dbg("RMI-Ack received (host %s, port: %d) " , host, port) - return true + dbg("RMI-Ack received (host %s, port: %d) " , host, port) + return true end -- Sends an RMI method call @@ -776,51 +776,51 @@ end --@param hash - the hashcode for the class that is invoked --@param op - the operation number (method) invoked --@param arguments - optional, if arguments are needed to this method. Should be an Arguments table --- or something else which has a getData() function to get binary data +-- or something else which has a getData() function to get binary data function RmiDataStream:writeMethodCall(out,objNum, hash, op, arguments) - dbg("Invoking object %s, hash %s, opNum %s, args %s", tostring(objNum), tostring(hash), tostring(op), tostring(arguments)) - local dos = self.dos - local dis = self.dis + dbg("Invoking object %s, hash %s, opNum %s, args %s", tostring(objNum), tostring(hash), tostring(op), tostring(arguments)) + local dos = self.dos + local dis = self.dis - -- Send Call: - dos:writeByte(0x50) - -- Send Magic 0xaced - dos:writeShort(0xACED) - -- Send version 0x0005 - dos:writeShort(0x0005) - -- Send TC_BLOKDATA - dos:writeByte(0x77) + -- Send Call: + dos:writeByte(0x50) + -- Send Magic 0xaced + dos:writeShort(0xACED) + -- Send version 0x0005 + dos:writeShort(0x0005) + -- Send TC_BLOKDATA + dos:writeByte(0x77) - -- send length (byte) - dos:writeByte(0x22) + -- send length (byte) + dos:writeByte(0x22) - -- From sun.rmi.transport.StreamRemoteCall : - -- // write out remote call header info... - -- // call header, part 1 (read by Transport) - -- conn.getOutputStream().write(TransportConstants.Call); - -- getOutputStream(); // creates a MarshalOutputStream - -- id.write(out); // object id (target of call) - -- // call header, part 2 (read by Dispatcher) - -- out.writeInt(op); // method number (operation index) - -- out.writeLong(hash); // stub/skeleton hash - -- Send rest of the call + -- From sun.rmi.transport.StreamRemoteCall : + -- // write out remote call header info... + -- // call header, part 1 (read by Transport) + -- conn.getOutputStream().write(TransportConstants.Call); + -- getOutputStream(); // creates a MarshalOutputStream + -- id.write(out); // object id (target of call) + -- // call header, part 2 (read by Dispatcher) + -- out.writeInt(op); // method number (operation index) + -- out.writeLong(hash); // stub/skeleton hash + -- Send rest of the call - local unique, time, count =0,0,0 + local unique, time, count =0,0,0 - dos:writeLong(objNum);-- id objNum - dos:writeInt(unique); -- space - dos:writeLong(time); - dos:writeShort(count); - dos:writeInt(op) - dos:pack('H',hash) + dos:writeLong(objNum);-- id objNum + dos:writeInt(unique); -- space + dos:writeLong(time); + dos:writeShort(count); + dos:writeInt(op) + dos:pack('H',hash) - -- And now, the arguments - if arguments ~= nil then - dos:write(arguments:getData()) - end + -- And now, the arguments + if arguments ~= nil then + dos:write(arguments:getData()) + end - dos:flush() + dos:flush() end --- @@ -830,23 +830,23 @@ end --@param hash - the hashcode for the class that is invoked --@param op - the operation number (method) invoked --@param arguments - optional, if arguments are needed to this method. Should be an Arguments table --- or something else which has a getData() function to get binary data +-- or something else which has a getData() function to get binary data --@return status --@return a JavaClass instance function RmiDataStream:invoke(objNum, hash, op, arguments) - local status, data - local out = self.out - local dis = self.dis - self:writeMethodCall(out,objNum,hash, op, arguments) - local status, retByte = dis:readByte() - if not status then return false, "No return data received from server" end + local status, data + local out = self.out + local dis = self.dis + self:writeMethodCall(out,objNum,hash, op, arguments) + local status, retByte = dis:readByte() + if not status then return false, "No return data received from server" end - if 0x51 ~= retByte then -- 0x51 : Returndata - return false, "No return data received from server" - end + if 0x51 ~= retByte then -- 0x51 : Returndata + return false, "No return data received from server" + end - status, data = self:readReturnData(dis) - return status, data + status, data = self:readReturnData(dis) + return status, data end --- @@ -854,164 +854,164 @@ end --@param dis a JavaDIS inputstream function RmiDataStream:readReturnData(dis) - --[[ - From -http://turtle.ee.ncku.edu.tw/docs/java/jdk1.2.2/guide/rmi/spec/rmi-protocol.doc3.html : - A ReturnValue of an RMI call consists of a return code to indicate either a normal or - exceptional return, a UniqueIdentifier to tag the return value (used to send a DGCAck if necessary) - followed by the return result: either the Value returned or the Exception thrown. + --[[ + From -http://turtle.ee.ncku.edu.tw/docs/java/jdk1.2.2/guide/rmi/spec/rmi-protocol.doc3.html : + A ReturnValue of an RMI call consists of a return code to indicate either a normal or + exceptional return, a UniqueIdentifier to tag the return value (used to send a DGCAck if necessary) + followed by the return result: either the Value returned or the Exception thrown. - CallData: ObjectIdentifier Operation Hash (Arguments) - ReturnValue: - 0x01 UniqueIdentifier (Value) - 0x02 UniqueIdentifier Exception + CallData: ObjectIdentifier Operation Hash (Arguments) + ReturnValue: + 0x01 UniqueIdentifier (Value) + 0x02 UniqueIdentifier Exception - ObjectIdentifier: ObjectNumber UniqueIdentifier - UniqueIdentifier: Number Time Count - Arguments: Value Arguments Value - Value: Object Primitive + ObjectIdentifier: ObjectNumber UniqueIdentifier + UniqueIdentifier: Number Time Count + Arguments: Value Arguments Value + Value: Object Primitive - Example: [ac ed][00 05][77][0f][01][25 14 95 21][00 00 01 2b 16 9a 62 5a 80 0b] - [magc][ver ][BL][L ][Ok][ --------------- not interesting atm ----------------------] + Example: [ac ed][00 05][77][0f][01][25 14 95 21][00 00 01 2b 16 9a 62 5a 80 0b] + [magc][ver ][BL][L ][Ok][ --------------- not interesting atm ----------------------] - --]] + --]] - -- We need to be able to read at least 7 bytes - -- If that is doable, we can ignore the status on the following readbyte operations - if not dis:canRead(7) then - return doh("Not enough data received") - end + -- We need to be able to read at least 7 bytes + -- If that is doable, we can ignore the status on the following readbyte operations + if not dis:canRead(7) then + return doh("Not enough data received") + end - local status, magic = dis:readShort() -- read magic - local status, version = dis:readShort() -- read version + local status, magic = dis:readShort() -- read magic + local status, version = dis:readShort() -- read version - local status, typ = dis:readByte() - if typ ~= TC.TC_BLOCKDATA then - return doh("Expected block data when reading return data") - end - local status, len = dis:readByte() -- packet length - --dis:setReadLimit(len) - local status, ex = dis:readByte() -- 1=ok, 2=exception thrown - if ex ~= 1 then - return doh("Remote call threw exception") - end + local status, typ = dis:readByte() + if typ ~= TC.TC_BLOCKDATA then + return doh("Expected block data when reading return data") + end + local status, len = dis:readByte() -- packet length + --dis:setReadLimit(len) + local status, ex = dis:readByte() -- 1=ok, 2=exception thrown + if ex ~= 1 then + return doh("Remote call threw exception") + end - -- We can skip the rest of this block - dis:skip(len -1) + -- We can skip the rest of this block + dis:skip(len -1) - -- Now, the return value object: - local status, x = readObject0(dis) - dbg("Read object, got %d left in buffer", dis.bReader:bufferSize()) + -- Now, the return value object: + local status, x = readObject0(dis) + dbg("Read object, got %d left in buffer", dis.bReader:bufferSize()) - if(dis.bReader:bufferSize() > 0) then - local content = dis.bReader:unpack('H'..tostring(dis.bReader:bufferSize())) - dbg("Buffer content: %s" ,content) - end - return status, x + if(dis.bReader:bufferSize() > 0) then + local content = dis.bReader:unpack('H'..tostring(dis.bReader:bufferSize())) + dbg("Buffer content: %s" ,content) + end + return status, x end --- -- Deserializes a serialized java object function readObject0(dis) - local finished = false - local data, status, responseType + local finished = false + local data, status, responseType - status, responseType = dis:readByte() - if not status then - return doh("Not enough data received") - end + status, responseType = dis:readByte() + if not status then + return doh("Not enough data received") + end - dbg("Reading object of type : %s" , RMIUtils.tcString(responseType)) - local decoder = TypeDecoders[responseType] - if decoder ~= nil then - status, data = decoder(dis) - if not status then return doh("readObject0: Could not read data %s", tostring(data)) end - dbg("Read: %s", tostring(data)) - return true, data - else - return doh("No decoder found for responsetype: %s" , RMIUtils.tcString(responseType)) - end + dbg("Reading object of type : %s" , RMIUtils.tcString(responseType)) + local decoder = TypeDecoders[responseType] + if decoder ~= nil then + status, data = decoder(dis) + if not status then return doh("readObject0: Could not read data %s", tostring(data)) end + dbg("Read: %s", tostring(data)) + return true, data + else + return doh("No decoder found for responsetype: %s" , RMIUtils.tcString(responseType)) + end end function readString(dis) - return dis:readUTF() + return dis:readUTF() end -- Reads return type array function readArray(dis) - local array = JavaArray:new() - dbg("Reading array class description") - local status, classDesc = readClassDesc(dis) - array:setClass(classDesc) - dbg("Reading array length") - local status, len = dis:readInt() + local array = JavaArray:new() + dbg("Reading array class description") + local status, classDesc = readClassDesc(dis) + array:setClass(classDesc) + dbg("Reading array length") + local status, len = dis:readInt() - if not status then - return doh("Could not read data") - end + if not status then + return doh("Could not read data") + end - array:setLength(len) - dbg("Reading array of length is %X", len) - for i =1, len, 1 do - local status, object = readObject0(dis) - array:setValue(i,object) - end - return true, array + array:setLength(len) + dbg("Reading array of length is %X", len) + for i =1, len, 1 do + local status, object = readObject0(dis) + array:setValue(i,object) + end + return true, array end function readClassDesc(dis) - local status, p = dis:readByte() - if not status then return doh( "Could not read data" ) end + local status, p = dis:readByte() + if not status then return doh( "Could not read data" ) end - dbg("reading classdesc: %s" , RMIUtils.tcString(p)) + dbg("reading classdesc: %s" , RMIUtils.tcString(p)) - local val + local val - if p == TC.TC_CLASSDESC then - dbg("Reading TC_CLASSDESC") - status, val = readNonProxyDesc(dis) - elseif p == TC.TC_NULL then - dbg("Reading TC_NULL") - status, val = true, nil - elseif p == TC.TC_PROXYCLASSDESC then - dbg("Reading TC_PROXYCLASSDESC") - status, val = readProxyDesc(dis) - else - return doh("TC_classdesc is other %d", p) - end + if p == TC.TC_CLASSDESC then + dbg("Reading TC_CLASSDESC") + status, val = readNonProxyDesc(dis) + elseif p == TC.TC_NULL then + dbg("Reading TC_NULL") + status, val = true, nil + elseif p == TC.TC_PROXYCLASSDESC then + dbg("Reading TC_PROXYCLASSDESC") + status, val = readProxyDesc(dis) + else + return doh("TC_classdesc is other %d", p) + end - if not status then - return doh("Error reading class description") - end - return status, val + if not status then + return doh("Error reading class description") + end + return status, val end function readOrdinaryObject(dis) - local status, desc = readClassDesc(dis) - if not status then - return doh("Error reading ordinary object") - end + local status, desc = readClassDesc(dis) + if not status then + return doh("Error reading ordinary object") + end - if desc:isExternalizable() then - dbg("External content") - local status, extdata = readExternalData(dis) - if status then - desc["externalData"] = extdata - end - else - dbg("Serial content") - local status, serdata = readExternalData(dis) - if status then - desc["externalData"] = serdata - local status, data =parseExternalData(desc) - if status then - desc['externalData'] = data - end - end - end - return status, desc + if desc:isExternalizable() then + dbg("External content") + local status, extdata = readExternalData(dis) + if status then + desc["externalData"] = extdata + end + else + dbg("Serial content") + local status, serdata = readExternalData(dis) + if status then + desc["externalData"] = serdata + local status, data =parseExternalData(desc) + if status then + desc['externalData'] = data + end + end + end + return status, desc end @@ -1019,33 +1019,33 @@ end -- header. This method returns the external data in 'raw' form, -- since it is up to each class to define an readExternal method function readExternalData(dis) - local data = {} - while dis.bReader:bufferSize() > 0 do - local status, tc= dis:readByte() - if not status then - return doh("Could not read external data") - end - dbg("readExternalData: %s", RMIUtils.tcString(tc)) - local status, len, content - if tc == TC.TC_BLOCKDATA then - status, len = dis:readByte() - status, content = dis.bReader:skip(len) - --print(bin.unpack("H"..tostring(#content),content)) - --print(makeStringReadable(content)) - dbg("Read external data (%d bytes): %s " ,len, content) - --local object = ExternalClassParsers['java.rmi.server.RemoteObject'](dis) - --print(object) - return status, content - elseif tc == TC.TC_BLOCKDATALONG then - status, len = dis:readUnsignedInt() - status, content = dis.bReader:skip(len) - return status, content - elseif tc == TC.TC_ENDBLOCKDATA then - --noop - else - return doh("Got unexpected field in readExternalData: %s ", RMIUtils.tcString(tc)) - end - end + local data = {} + while dis.bReader:bufferSize() > 0 do + local status, tc= dis:readByte() + if not status then + return doh("Could not read external data") + end + dbg("readExternalData: %s", RMIUtils.tcString(tc)) + local status, len, content + if tc == TC.TC_BLOCKDATA then + status, len = dis:readByte() + status, content = dis.bReader:skip(len) + --print(bin.unpack("H"..tostring(#content),content)) + --print(makeStringReadable(content)) + dbg("Read external data (%d bytes): %s " ,len, content) + --local object = ExternalClassParsers['java.rmi.server.RemoteObject'](dis) + --print(object) + return status, content + elseif tc == TC.TC_BLOCKDATALONG then + status, len = dis:readUnsignedInt() + status, content = dis.bReader:skip(len) + return status, content + elseif tc == TC.TC_ENDBLOCKDATA then + --noop + else + return doh("Got unexpected field in readExternalData: %s ", RMIUtils.tcString(tc)) + end + end end ---- @@ -1058,205 +1058,205 @@ end -- 'goodies' of e.g UnicastRef, which contain important information about -- where another RMI-socket is listening and waiting for someone to connect. ExternalClassParsers = { - --- - --@see sun.rmi.transport.tcp.TCPEndpoint - --@see sun.rmi.server.UnicastRef - --@see sun.rmi.server.UnicastRef2 - UnicastRef = function(dis) - local stat, host = dis:readUTF(); - if not stat then return doh("Parsing external data, could not read host (UTF)") end - local status, port = dis:readUnsignedInt(); - if not stat then return doh("Parsing external data, could not read port (int)") end + --- + --@see sun.rmi.transport.tcp.TCPEndpoint + --@see sun.rmi.server.UnicastRef + --@see sun.rmi.server.UnicastRef2 + UnicastRef = function(dis) + local stat, host = dis:readUTF(); + if not stat then return doh("Parsing external data, could not read host (UTF)") end + local status, port = dis:readUnsignedInt(); + if not stat then return doh("Parsing external data, could not read port (int)") end - dbg("a host: %s, port %d", host, port) - return true, ("@%s:%d"):format(host,port) - end, - --- - --@see sun.rmi.transport.tcp.TCPEndpoint - --@see sun.rmi.server.UnicastRef - --@see sun.rmi.server.UnicastRef2 - UnicastRef2 = function(dis) - local stat, form = dis:readByte(); - if not stat then return doh("Parsing external data, could not read byte") end - if form == 0 or form == 1 then-- FORMAT_HOST_PORT or FORMAT_HOST_PORT_FACTORY - local stat, host = dis:readUTF(); - if not stat then return doh("Parsing external data, could not read host (UTF)") end - local status, port = dis:readUnsignedInt(); - if not stat then return doh("Parsing external data, could not read port (int)") end - dbg("b host: %s, port %d", host, port) - if form ==0 then - return true, ("@%s:%d"):format(host,port) - end - -- for FORMAT_HOST_PORT_FACTORY, there's an object left to read - local status, object = readObject0(dis) - return true, ("@%s:%d"):format(host,port) - --return true, {host = host, port = port, factory = object} - else - return doh("Invalid endpoint format") - end - end + dbg("a host: %s, port %d", host, port) + return true, ("@%s:%d"):format(host,port) + end, + --- + --@see sun.rmi.transport.tcp.TCPEndpoint + --@see sun.rmi.server.UnicastRef + --@see sun.rmi.server.UnicastRef2 + UnicastRef2 = function(dis) + local stat, form = dis:readByte(); + if not stat then return doh("Parsing external data, could not read byte") end + if form == 0 or form == 1 then-- FORMAT_HOST_PORT or FORMAT_HOST_PORT_FACTORY + local stat, host = dis:readUTF(); + if not stat then return doh("Parsing external data, could not read host (UTF)") end + local status, port = dis:readUnsignedInt(); + if not stat then return doh("Parsing external data, could not read port (int)") end + dbg("b host: %s, port %d", host, port) + if form ==0 then + return true, ("@%s:%d"):format(host,port) + end + -- for FORMAT_HOST_PORT_FACTORY, there's an object left to read + local status, object = readObject0(dis) + return true, ("@%s:%d"):format(host,port) + --return true, {host = host, port = port, factory = object} + else + return doh("Invalid endpoint format") + end + end } --@see java.rmi.server.RemoteObject:readObject() ExternalClassParsers['java.rmi.server.RemoteObject'] = function(dis) - local status, refClassName = dis:readUTF() - if not status then return doh("Parsing external data, could not read classname (UTF)") end - if #refClassName == 0 then - local status, ref = readObject0(dis) - return status, ref - end - dbg("Ref class name: %s ", refClassName) - local parser = ExternalClassParsers[refClassName] + local status, refClassName = dis:readUTF() + if not status then return doh("Parsing external data, could not read classname (UTF)") end + if #refClassName == 0 then + local status, ref = readObject0(dis) + return status, ref + end + dbg("Ref class name: %s ", refClassName) + local parser = ExternalClassParsers[refClassName] - if parser == nil then - return doh("No external class reader for %s" , refClassName) - end + if parser == nil then + return doh("No external class reader for %s" , refClassName) + end - local status, object = parser(dis) - return status, object + local status, object = parser(dis) + return status, object end -- Attempts to parse the externalized data of an object. --@return status, the object data function parseExternalData(j_object) - if j_object == nil then - return doh("parseExternalData got nil object") - end + if j_object == nil then + return doh("parseExternalData got nil object") + end - local className = j_object:getName() + local className = j_object:getName() - -- Find parser for the object, move up the hierarchy - local obj = j_object - local parser = nil - while(className ~= nil) do - parser = ExternalClassParsers[className] - if parser ~= nil then break end + -- Find parser for the object, move up the hierarchy + local obj = j_object + local parser = nil + while(className ~= nil) do + parser = ExternalClassParsers[className] + if parser ~= nil then break end - obj = obj:getSuperClass() - if obj== nil then break end-- No more super classes - className = obj:getName() - end + obj = obj:getSuperClass() + if obj== nil then break end-- No more super classes + className = obj:getName() + end - if parser == nil then - return doh("External reader for class %s is not implemented", tostring(className)) - end - -- Read the actual object, start by creating a new dis based on the data-string - local dis = JavaDIS:new(BufferedReader:new(nil,j_object.externalData)) - local status, object = parser(dis) - if not status then - return doh("Could not parse external data") - end - return true, object + if parser == nil then + return doh("External reader for class %s is not implemented", tostring(className)) + end + -- Read the actual object, start by creating a new dis based on the data-string + local dis = JavaDIS:new(BufferedReader:new(nil,j_object.externalData)) + local status, object = parser(dis) + if not status then + return doh("Could not parse external data") + end + return true, object end -- Helper function to display data -- returns the string with all non-printable chars -- coded as hex function makeStringReadable(data) - local r = "" - for i=1,#data,1 do - local x = data:byte(i) - if x > 31 and x <127 then - r = r .. data:sub(i,i) - else - r = r .. ("\\x%x"):format(x) - end - end - return r + local r = "" + for i=1,#data,1 do + local x = data:byte(i) + if x > 31 and x <127 then + r = r .. data:sub(i,i) + else + r = r .. ("\\x%x"):format(x) + end + end + return r end function readNonProxyDesc(dis) - dbg("-- entering readNonProxyDesc--") - local j_class = JavaClass:new() - local status, classname = dis:readUTF() - if not status then return doh( "Could not read data" ) end - j_class:setName(classname) + dbg("-- entering readNonProxyDesc--") + local j_class = JavaClass:new() + local status, classname = dis:readUTF() + if not status then return doh( "Could not read data" ) end + j_class:setName(classname) - local status, serialID = dis:readLongAsHexString() - if not status then return doh("Could not read data") end - j_class:setSerialID(serialID) + local status, serialID = dis:readLongAsHexString() + if not status then return doh("Could not read data") end + j_class:setSerialID(serialID) - dbg("Set serial ID to %s", tostring(serialID)) + dbg("Set serial ID to %s", tostring(serialID)) - local status, flags = dis:readByte() - if not status then return doh("Could not read data") end - j_class:setFlags(flags) + local status, flags = dis:readByte() + if not status then return doh("Could not read data") end + j_class:setFlags(flags) - local status, fieldCount = dis:readShort() - if not status then return doh( "Could not read data") end + local status, fieldCount = dis:readShort() + if not status then return doh( "Could not read data") end - dbg("Fieldcount %d", fieldCount) + dbg("Fieldcount %d", fieldCount) - local fields = {} - for i =0, fieldCount-1,1 do - local status, fieldDesc = readFieldDesc(dis) - j_class:addField(fieldDesc) - -- Need to store in list, the field values need to be read - -- after we have finished reading the class description - -- hierarchy - table.insert(fields,fieldDesc) - end - local status, customStrings = skipCustomData(dis) - if status and customStrings ~= nil and #customStrings > 0 then - j_class:setCustomData(customStrings) - end + local fields = {} + for i =0, fieldCount-1,1 do + local status, fieldDesc = readFieldDesc(dis) + j_class:addField(fieldDesc) + -- Need to store in list, the field values need to be read + -- after we have finished reading the class description + -- hierarchy + table.insert(fields,fieldDesc) + end + local status, customStrings = skipCustomData(dis) + if status and customStrings ~= nil and #customStrings > 0 then + j_class:setCustomData(customStrings) + end - local _,superDescriptor = readClassDesc(dis) + local _,superDescriptor = readClassDesc(dis) - j_class:setSuperClass(superDescriptor) - dbg("Superclass read, now reading %i field values", #fields) - --Read field values - for i=1, #fields, 1 do - local status, fieldType = dis:readByte() - local value = nil - if ( TypeDecoders[fieldType] ) then - status, value= TypeDecoders[fieldType](dis) - else - dbg("ellol reading".. RMIUtils.tcString(fieldType)) - return - end - dbg("Read fieldvalue ".. tostring(value) .. " for field ".. tostring(fields[i])) - fields[i]:setValue(value) - end - dbg("-- leaving readNonProxyDesc--") - return true, j_class + j_class:setSuperClass(superDescriptor) + dbg("Superclass read, now reading %i field values", #fields) + --Read field values + for i=1, #fields, 1 do + local status, fieldType = dis:readByte() + local value = nil + if ( TypeDecoders[fieldType] ) then + status, value= TypeDecoders[fieldType](dis) + else + dbg("ellol reading".. RMIUtils.tcString(fieldType)) + return + end + dbg("Read fieldvalue ".. tostring(value) .. " for field ".. tostring(fields[i])) + fields[i]:setValue(value) + end + dbg("-- leaving readNonProxyDesc--") + return true, j_class end function readProxyDesc(dis) - dbg("-- in readProxyDesc--") - local interfaces = '' - local superclass = nil - local status, ifaceNum= dis:readInt() - if not status then return doh("Could not read data") end - --dbg("# interfaces: %d" , ifaceNum) - while ifaceNum > 0 do - local status, iface = dis:readUTF() - if not status then return doh( "Could not read data") end - --table.insert(interfaces, iface) - interfaces = interfaces .. iface ..', ' - dbg("Interface: %s " ,iface) - ifaceNum = ifaceNum-1 - end + dbg("-- in readProxyDesc--") + local interfaces = '' + local superclass = nil + local status, ifaceNum= dis:readInt() + if not status then return doh("Could not read data") end + --dbg("# interfaces: %d" , ifaceNum) + while ifaceNum > 0 do + local status, iface = dis:readUTF() + if not status then return doh( "Could not read data") end + --table.insert(interfaces, iface) + interfaces = interfaces .. iface ..', ' + dbg("Interface: %s " ,iface) + ifaceNum = ifaceNum-1 + end - local j_class = JavaClass:new() + local j_class = JavaClass:new() - local status, customStrings = skipCustomData(dis) - if status and customStrings ~= nil and #customStrings > 0 then - j_class:setCustomData(customStrings) - end + local status, customStrings = skipCustomData(dis) + if status and customStrings ~= nil and #customStrings > 0 then + j_class:setCustomData(customStrings) + end - local _,superDescriptor = readClassDesc(dis) + local _,superDescriptor = readClassDesc(dis) - --print ("superdescriptor", superDescriptor) - j_class:setSuperClass(superDescriptor) - j_class:setInterfaces(interfaces) + --print ("superdescriptor", superDescriptor) + j_class:setSuperClass(superDescriptor) + j_class:setInterfaces(interfaces) - dbg("-- leaving readProxyDesc--") - return true, j_class + dbg("-- leaving readProxyDesc--") + return true, j_class end -- @@ -1266,180 +1266,180 @@ end --@return status --@return any strings found while searching function skipCustomData(dis) - -- If we come across something interesting, just put it into - -- the returnData list - local returnData = {} - while true do - local status, p = dis:readByte() - if not status then - return doh("Could not read data") - end + -- If we come across something interesting, just put it into + -- the returnData list + local returnData = {} + while true do + local status, p = dis:readByte() + if not status then + return doh("Could not read data") + end - if not status then return doh("Could not read data") end - dbg("skipCustomData read %s", RMIUtils.tcString(p)) + if not status then return doh("Could not read data") end + dbg("skipCustomData read %s", RMIUtils.tcString(p)) - if p == TC.TC_BLOCKDATA or p == TC.TC_BLOCKDATALONG then - dbg("continuing") - --return - elseif p == TC.TC_ENDBLOCKDATA then - return true, returnData - else - -- In the java impl, this is a function called readObject0. We just - -- use the read null, otherwise error - if p == TC.TC_NULL then - -- No op, already read the byte, continue reading - elseif p == TC.TC_STRING then - --dbg("A string is coming!") - local status, str = dis:readUTF() - if not status then - return doh("Could not read data") - end - dbg("Got a string, but don't know what to do with it! : %s",str) - -- Object serialization is a bit messy. I have seen the - -- classpath being sent over a customdata-field, so it is - -- definitely interesting. Quick fix to get it showing - -- is to just stick it onto the object we are currently at. - -- So, just put the string into the returnData and continue - table.insert(returnData, str) - else - return doh("Not implemented in skipcustomData:: %s", RMIUtils.tcString(p)) - end - end - end + if p == TC.TC_BLOCKDATA or p == TC.TC_BLOCKDATALONG then + dbg("continuing") + --return + elseif p == TC.TC_ENDBLOCKDATA then + return true, returnData + else + -- In the java impl, this is a function called readObject0. We just + -- use the read null, otherwise error + if p == TC.TC_NULL then + -- No op, already read the byte, continue reading + elseif p == TC.TC_STRING then + --dbg("A string is coming!") + local status, str = dis:readUTF() + if not status then + return doh("Could not read data") + end + dbg("Got a string, but don't know what to do with it! : %s",str) + -- Object serialization is a bit messy. I have seen the + -- classpath being sent over a customdata-field, so it is + -- definitely interesting. Quick fix to get it showing + -- is to just stick it onto the object we are currently at. + -- So, just put the string into the returnData and continue + table.insert(returnData, str) + else + return doh("Not implemented in skipcustomData:: %s", RMIUtils.tcString(p)) + end + end + end end function readFieldDesc(dis) - -- fieldDesc: - -- primitiveDesc - -- objectDesc - -- primitiveDesc: - -- prim_typecode fieldName - -- objectDesc: - -- obj_typecode fieldName className1 - -- prim_typecode: - -- `B' // byte - -- `C' // char - -- `D' // double - -- `F' // float - -- `I' // integer - -- `J' // long - -- `S' // short - -- `Z' // boolean - -- obj_typecode: - -- `[` // array - -- `L' // object - local j_field = JavaField:new() + -- fieldDesc: + -- primitiveDesc + -- objectDesc + -- primitiveDesc: + -- prim_typecode fieldName + -- objectDesc: + -- obj_typecode fieldName className1 + -- prim_typecode: + -- `B' // byte + -- `C' // char + -- `D' // double + -- `F' // float + -- `I' // integer + -- `J' // long + -- `S' // short + -- `Z' // boolean + -- obj_typecode: + -- `[` // array + -- `L' // object + local j_field = JavaField:new() - local status, c = dis:readByte() - if not status then return doh("Could not read data") end + local status, c = dis:readByte() + if not status then return doh("Could not read data") end - local char = string.char(c) + local char = string.char(c) - local status, name = dis:readUTF() - if not status then return doh("Could not read data") end + local status, name = dis:readUTF() + if not status then return doh("Could not read data") end - local fieldType = ('primitive type: (%s) '):format(char) - dbg("Fieldtype, char = %s, %s", tostring(fieldType), tostring(char)) - if char == 'L' or char == '[' then - -- These also have classname which tells the type - -- on the field - local status, fieldclassname = readTypeString(dis) - if not status then return doh("Could not read data") end - if char == '[s' then - fieldType = fieldclassname .. " []" - else - fieldType = fieldclassname - end - end + local fieldType = ('primitive type: (%s) '):format(char) + dbg("Fieldtype, char = %s, %s", tostring(fieldType), tostring(char)) + if char == 'L' or char == '[' then + -- These also have classname which tells the type + -- on the field + local status, fieldclassname = readTypeString(dis) + if not status then return doh("Could not read data") end + if char == '[s' then + fieldType = fieldclassname .. " []" + else + fieldType = fieldclassname + end + end - if not status then - return false, fieldType - end + if not status then + return false, fieldType + end - dbg("Field description: name: %s, type: %s", tostring(name), tostring(fieldType)) + dbg("Field description: name: %s, type: %s", tostring(name), tostring(fieldType)) - j_field:setType(fieldType) - j_field:setName(name) --- setType = function( self, typ ) self.type = typ end, --- setSignature = function( self, sig ) self.signature = sig end, --- setName = function( self, name ) self.name = name end, --- setObjectType = function( self, ot ) self.object_type = ot end, --- setReference = function( self, ref ) self.ref = ref end, + j_field:setType(fieldType) + j_field:setName(name) + -- setType = function( self, typ ) self.type = typ end, + -- setSignature = function( self, sig ) self.signature = sig end, + -- setName = function( self, name ) self.name = name end, + -- setObjectType = function( self, ot ) self.object_type = ot end, + -- setReference = function( self, ref ) self.ref = ref end, - dbg("Created java field:".. tostring(j_field)) + dbg("Created java field:".. tostring(j_field)) - return true, j_field + return true, j_field end function readTypeString(dis) - local status, tc = dis:readByte() - if not status then return doh("Could not read data") end - if tc == TC.TC_NULL then - return true, nil - elseif tc== TC.TC_REFERENCE then - return doh("Not implemented, readTypeString(TC_REFERENCE)"); - elseif tc == TC.TC_STRING then - return dis:readUTF() - elseif tc == TC.TC_LONGSTRING then - --TODO, add this (will throw error as is) - return dis:readLongUTF() - end + local status, tc = dis:readByte() + if not status then return doh("Could not read data") end + if tc == TC.TC_NULL then + return true, nil + elseif tc== TC.TC_REFERENCE then + return doh("Not implemented, readTypeString(TC_REFERENCE)"); + elseif tc == TC.TC_STRING then + return dis:readUTF() + elseif tc == TC.TC_LONGSTRING then + --TODO, add this (will throw error as is) + return dis:readLongUTF() + end end TypeDecoders = { - [TC.TC_ARRAY] = readArray, - [TC.TC_CLASSDESC] = readClassDesc, - [TC.TC_STRING] = readString, - [TC.TC_OBJECT] = readOrdinaryObject, + [TC.TC_ARRAY] = readArray, + [TC.TC_CLASSDESC] = readClassDesc, + [TC.TC_STRING] = readString, + [TC.TC_OBJECT] = readOrdinaryObject, } --- -- Registry -- Class to represent the RMI Registry. --@usage: --- registry = rmi.Registry:new() --- status, data = registry:list() +-- registry = rmi.Registry:new() +-- status, data = registry:list() Registry ={ - new = function (self,host, port) - local o ={} -- create object - setmetatable(o, self) - self.__index = self -- DIY inheritance - -- Hash code for sun.rmi.registry.RegistryImpl_Stub, which we are invoking : - -- hex: 0x44154dc9d4e63bdf , dec: 4905912898345647071 - self.hash = '44154dc9d4e63bdf' - -- RmiRegistry object id is 0 - self.objId = 0 - o.host = host - o.port = port - return o - end + new = function (self,host, port) + local o ={} -- create object + setmetatable(o, self) + self.__index = self -- DIY inheritance + -- Hash code for sun.rmi.registry.RegistryImpl_Stub, which we are invoking : + -- hex: 0x44154dc9d4e63bdf, dec: 4905912898345647071 + self.hash = '44154dc9d4e63bdf' + -- RmiRegistry object id is 0 + self.objId = 0 + o.host = host + o.port = port + return o + end } -- Connect to the remote registry. --@return status --@return error message function Registry:_handshake() - local out = RmiDataStream:new() - local status, err = out:connect(self.host,self.port) + local out = RmiDataStream:new() + local status, err = out:connect(self.host,self.port) - if not status then - return doh("Registry connection failed: %s", tostring(err)) - end - dbg("Registry connection OK "..tostring(out.bsocket) ) - self.out = out - return true + if not status then + return doh("Registry connection failed: %s", tostring(err)) + end + dbg("Registry connection OK "..tostring(out.bsocket) ) + self.out = out + return true end --- -- List the named objects in the remote RMI registry --@return status --@return a table of strings , or error message function Registry:list() - if not self:_handshake() then - return doh("Handshake failed") - end - -- Method list() is op number 1 - return self.out:invoke(self.objId, self.hash,1) + if not self:_handshake() then + return doh("Handshake failed") + end + -- Method list() is op number 1 + return self.out:invoke(self.objId, self.hash,1) end --- -- Perform a lookup on an object in the Registry, @@ -1448,12 +1448,12 @@ end --@return status --@return JavaClass-object function Registry:lookup(name) - self:_handshake() - -- Method lookup() is op number 2 - -- Takes a string as arguments - local a = Arguments:new() - a:addString(name) - return self.out:invoke(self.objId, self.hash,2, a) + self:_handshake() + -- Method lookup() is op number 2 + -- Takes a string as arguments + local a = Arguments:new() + a:addString(name) + return self.out:invoke(self.objId, self.hash,2, a) end ---- -- Arguments class @@ -1463,32 +1463,32 @@ end -- currently --@usage: When invoking a remote method -- use this class in this manner: --- Arguments a = Arguments:new() --- a:addString("foo") --- datastream:invoke{objNum=oid, hash=hash, opNum = opid, arguments=a} --- ... +-- Arguments a = Arguments:new() +-- a:addString("foo") +-- datastream:invoke{objNum=oid, hash=hash, opNum = opid, arguments=a} +-- ... -- Arguments = { - new = function (self,o) - o = o or {} -- create object if user does not provide one - setmetatable(o, self) - self.__index = self -- DIY inheritance - -- We use a buffered socket just to be able to use a javaDOS for writing - self.dos = JavaDOS:new(BufferedWriter:new()) - return o - end, - addString = function(self, str) - self.dos:writeByte(TC.TC_STRING) - self.dos:writeUTF(str) - end, - addRaw = function(self, str) - self.dos:write(str) - end, - getData = function(self) - local _, res = self.dos:flush() - return res - end + new = function (self,o) + o = o or {} -- create object if user does not provide one + setmetatable(o, self) + self.__index = self -- DIY inheritance + -- We use a buffered socket just to be able to use a javaDOS for writing + self.dos = JavaDOS:new(BufferedWriter:new()) + return o + end, + addString = function(self, str) + self.dos:writeByte(TC.TC_STRING) + self.dos:writeUTF(str) + end, + addRaw = function(self, str) + self.dos:write(str) + end, + getData = function(self) + local _, res = self.dos:flush() + return res + end } @@ -1503,48 +1503,48 @@ Arguments = { RMIUtils = { - -- Indicates a Serializable class defines its own writeObject method. - SC_WRITE_METHOD = 0x01, - -- Indicates Externalizable data written in Block Data mode. - SC_BLOCK_DATA = 0x08, - -- Bit mask for ObjectStreamClass flag. Indicates class is Serializable. - SC_SERIALIZABLE = 0x02, - --Bit mask for ObjectStreamClass flag. Indicates class is Externalizable. - SC_EXTERNALIZABLE = 0x04, - --Bit mask for ObjectStreamClass flag. Indicates class is an enum type. - SC_ENUM = 0x10, + -- Indicates a Serializable class defines its own writeObject method. + SC_WRITE_METHOD = 0x01, + -- Indicates Externalizable data written in Block Data mode. + SC_BLOCK_DATA = 0x08, + -- Bit mask for ObjectStreamClass flag. Indicates class is Serializable. + SC_SERIALIZABLE = 0x02, + --Bit mask for ObjectStreamClass flag. Indicates class is Externalizable. + SC_EXTERNALIZABLE = 0x04, + --Bit mask for ObjectStreamClass flag. Indicates class is an enum type. + SC_ENUM = 0x10, - flagsToString = function(flags) - local retval = '' - if ( bit.band(flags, RMIUtils.SC_WRITE_METHOD) ~= 0) then - retval = retval .. " WRITE_METHOD" - end - if ( bit.band(flags, RMIUtils.SC_BLOCK_DATA) ~= 0) then - retval = retval .. " BLOCK_DATA" - end - if ( bit.band(flags, RMIUtils.SC_EXTERNALIZABLE) ~= 0) then - retval = retval .. " EXTERNALIZABLE" - end - if ( bit.band(flags, RMIUtils.SC_SERIALIZABLE) ~= 0) then - retval = retval .. " SC_SERIALIZABLE" - end - if ( bit.band(flags, RMIUtils.SC_ENUM) ~= 0) then - retval = retval .. " SC_ENUM" - end - return retval - end, - tcString = function (constant) - local x = TC.Strings[constant] or "Unknown code" - return ("%s (0x%x)"):format(x,tostring(constant)) + flagsToString = function(flags) + local retval = '' + if ( bit.band(flags, RMIUtils.SC_WRITE_METHOD) ~= 0) then + retval = retval .. " WRITE_METHOD" + end + if ( bit.band(flags, RMIUtils.SC_BLOCK_DATA) ~= 0) then + retval = retval .. " BLOCK_DATA" + end + if ( bit.band(flags, RMIUtils.SC_EXTERNALIZABLE) ~= 0) then + retval = retval .. " EXTERNALIZABLE" + end + if ( bit.band(flags, RMIUtils.SC_SERIALIZABLE) ~= 0) then + retval = retval .. " SC_SERIALIZABLE" + end + if ( bit.band(flags, RMIUtils.SC_ENUM) ~= 0) then + retval = retval .. " SC_ENUM" + end + return retval + end, + tcString = function (constant) + local x = TC.Strings[constant] or "Unknown code" + return ("%s (0x%x)"):format(x,tostring(constant)) - end, + end, } local RMIMessage = { - Call = 0x50, - Ping = 0x52, - DgcAck= 0x54, + Call = 0x50, + Ping = 0x52, + DgcAck= 0x54, } STREAM_MAGIC = 0xaced STREAM_VERSION = 5 diff --git a/nselib/rpc.lua b/nselib/rpc.lua index ce66e927c..7d797a56a 100644 --- a/nselib/rpc.lua +++ b/nselib/rpc.lua @@ -254,30 +254,30 @@ Comm = { -- -- @return status boolean true SetVersion = function(self, version) - if self.checkprogver then - if (RPC_version[self.program] and RPC_args[self.program] and - nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then - self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver]) - elseif (not(self.version) and version) then - self.version = version - end - else - self.version = version - end - return true, nil + if self.checkprogver then + if (RPC_version[self.program] and RPC_args[self.program] and + nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then + self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver]) + elseif (not(self.version) and version) then + self.version = version + end + else + self.version = version + end + return true, nil end, --- Sets the verification of the specified program and version support -- before trying to connecting. -- @param check boolean to enable or disable checking of program and version support. SetCheckProgVer = function(self, check) - self.checkprogver = check + self.checkprogver = check end, --- Sets the RPC program ID to use. -- @param progid number Program ID to set. SetProgID = function(self, progid) - self.program_id = progid + self.program_id = progid end, --- Checks if data contains enough bytes to read the needed amount diff --git a/nselib/sip.lua b/nselib/sip.lua index 9eef2535f..800922246 100644 --- a/nselib/sip.lua +++ b/nselib/sip.lua @@ -1,36 +1,36 @@ --- A SIP library supporting a limited subset of SIP commands and methods -- -- The library currently supports the following methods: --- * REGISTER, INVITE & OPTIONS +-- * REGISTER, INVITE & OPTIONS -- -- Overview -- -------- -- The library consists of the following classes: -- -- o SessionData --- - Holds session data for the SIP session +-- - Holds session data for the SIP session -- -- o Session --- - Contains application functionality related to the implemented --- SIP methods. +-- - Contains application functionality related to the implemented +-- SIP methods. -- -- o Connection --- - A class containing code related to socket communication. +-- - A class containing code related to socket communication. -- -- o Response --- - A class containing code for handling SIP responses +-- - A class containing code for handling SIP responses -- -- o Request --- - A class containing code for handling SIP requests +-- - A class containing code for handling SIP requests -- -- o Util --- - A class containing static utility functions +-- - A class containing static utility functions -- -- o SIPAuth --- - A class containing code related to SIP Authentication +-- - A class containing code related to SIP Authentication -- -- o Helper --- - A class containing code used as a primary interface by scripts +-- - A class containing code used as a primary interface by scripts -- -- -- @author "Patrik Karlsson " @@ -53,358 +53,358 @@ _ENV = stdnse.module("sip", stdnse.seeall) -- Method constants Method = { - ACK = "ACK", - INVITE = "INVITE", - OPTIONS = "OPTIONS", - REGISTER = "REGISTER", + ACK = "ACK", + INVITE = "INVITE", + OPTIONS = "OPTIONS", + REGISTER = "REGISTER", } -- Error constants Error = { - TRYING = 100, - RING = 180, - TIMEOUT = 408, - BUSY = 486, - DECLINE = 603, - OK = 200, - UNAUTHORIZED = 401, - FORBIDDEN = 403, - NOTFOUND = 404, - PROXY_AUTH_REQUIRED = 407, + TRYING = 100, + RING = 180, + TIMEOUT = 408, + BUSY = 486, + DECLINE = 603, + OK = 200, + UNAUTHORIZED = 401, + FORBIDDEN = 403, + NOTFOUND = 404, + PROXY_AUTH_REQUIRED = 407, } -- The SessionData class SessionData = { - --- Creates a new instance of sessiondata - -- @return o an instance of SessionData - new = function(self, o) - local o = o or {} - setmetatable(o, self) - self.__index = self - o.user = "user" - return o - end, + --- Creates a new instance of sessiondata + -- @return o an instance of SessionData + new = function(self, o) + local o = o or {} + setmetatable(o, self) + self.__index = self + o.user = "user" + return o + end, - --- Sets the session username - -- @param user string containing the username - setUsername = function(self, user) self.user = user end, + --- Sets the session username + -- @param user string containing the username + setUsername = function(self, user) self.user = user end, - --- Sets the session password - -- @param pass string containing the password - setPassword = function(self, pass) self.pass = pass end, + --- Sets the session password + -- @param pass string containing the password + setPassword = function(self, pass) self.pass = pass end, - --- Sets the SIP domain - -- @param domain string containing the SIP domain - setDomain = function(self, domain) self.domain = domain end, + --- Sets the SIP domain + -- @param domain string containing the SIP domain + setDomain = function(self, domain) self.domain = domain end, - --- Sets the ip and port of the remote server - -- @param host string containing the ip of the remote server - -- @param port number containing the port of the remote server - setServer = function(self, host, port) self.server = { host = host, port = port } end, + --- Sets the ip and port of the remote server + -- @param host string containing the ip of the remote server + -- @param port number containing the port of the remote server + setServer = function(self, host, port) self.server = { host = host, port = port } end, - --- Sets the ip and port of the client - -- @param host string containing the ip of the client - -- @param port number containing the port of the client - setClient = function(self, host, port) self.client = { host = host, port = port } end, + --- Sets the ip and port of the client + -- @param host string containing the ip of the client + -- @param port number containing the port of the client + setClient = function(self, host, port) self.client = { host = host, port = port } end, - --- Sets the SIP users Full Name - -- @param name string containing the full name of the user - setName = function(self, name) self.name = name end, + --- Sets the SIP users Full Name + -- @param name string containing the full name of the user + setName = function(self, name) self.name = name end, - --- Retrieves the username - -- @return user string containing the sessions username - getUsername = function(self) return self.user end, + --- Retrieves the username + -- @return user string containing the sessions username + getUsername = function(self) return self.user end, - --- Retrieves the session password - -- @return pass string containing the session password - getPassword = function(self) return self.pass end, + --- Retrieves the session password + -- @return pass string containing the session password + getPassword = function(self) return self.pass end, - --- Retrieves the SIP domain - -- @return domain string containing the SIP domain - getDomain = function(self) return self.domain end, + --- Retrieves the SIP domain + -- @return domain string containing the SIP domain + getDomain = function(self) return self.domain end, - --- Retrieves the client IP and port - -- @return host string containing the client IP - -- @return port number containing the client port - getClient = function(self) return self.client.host, self.client.port end, + --- Retrieves the client IP and port + -- @return host string containing the client IP + -- @return port number containing the client port + getClient = function(self) return self.client.host, self.client.port end, - --- Retrieves the server IP and port - -- @return host string containing the server IP - -- @return port number containing the server port - getServer = function(self) return self.server.host, self.server.port end, + --- Retrieves the server IP and port + -- @return host string containing the server IP + -- @return port number containing the server port + getServer = function(self) return self.server.host, self.server.port end, - --- Retrieves the SIP users full name - -- @return name string containing the users full name - getName = function(self) return self.name or "Nmap NSE" end, + --- Retrieves the SIP users full name + -- @return name string containing the users full name + getName = function(self) return self.name or "Nmap NSE" end, } -- The session class holds the code necessary to register a SIP session Session = { - --- Creates a new session instance - -- @param host table containing the remote host to connect to - -- @param port table containing the remote port to connect to - -- @param sessdata instance of SessionData - -- @param options table containing zero or more of the following options - -- expires - the expire value in seconds - -- timeout - the socket timeout in seconds - -- @return o containing a new instance of the Session class - new = function(self, host, port, sessdata, options) - local o = {} - setmetatable(o, self) - self.__index = self - o.protocol = port.protocol:upper() - o.expires = (options and options.expires) or 300 - o.conn = Connection:new(host,port) - o.cseq = (options and options.cseq) or 1234 - local timeout = ( ( options and options.timeout ) and - options.timeout * 1000 ) or 5000 - o.conn.socket:set_timeout( timeout ) - o.sessdata = sessdata or SessionData:new() - return o - end, + --- Creates a new session instance + -- @param host table containing the remote host to connect to + -- @param port table containing the remote port to connect to + -- @param sessdata instance of SessionData + -- @param options table containing zero or more of the following options + -- expires - the expire value in seconds + -- timeout - the socket timeout in seconds + -- @return o containing a new instance of the Session class + new = function(self, host, port, sessdata, options) + local o = {} + setmetatable(o, self) + self.__index = self + o.protocol = port.protocol:upper() + o.expires = (options and options.expires) or 300 + o.conn = Connection:new(host,port) + o.cseq = (options and options.cseq) or 1234 + local timeout = ( ( options and options.timeout ) and + options.timeout * 1000 ) or 5000 + o.conn.socket:set_timeout( timeout ) + o.sessdata = sessdata or SessionData:new() + return o + end, - --- Connect the session - -- @return true on success, false on failure - -- @return err string containing error message - connect = function(self) - local status, err = self.conn:connect() - if (not(status)) then - return false, "ERROR: Failed to connect to server" - end - local status, lhost, lport, rhost, rport = self.conn.socket:get_info() - if ( not(status) ) then - return false, "Failed to retreive socket information" - end - self.sessdata:setClient(lhost, lport) - self.sessdata:setServer(rhost, rport) - return true - end, + --- Connect the session + -- @return true on success, false on failure + -- @return err string containing error message + connect = function(self) + local status, err = self.conn:connect() + if (not(status)) then + return false, "ERROR: Failed to connect to server" + end + local status, lhost, lport, rhost, rport = self.conn.socket:get_info() + if ( not(status) ) then + return false, "Failed to retreive socket information" + end + self.sessdata:setClient(lhost, lport) + self.sessdata:setServer(rhost, rport) + return true + end, - --- Closes the session - -- TODO: We should probably send some "closing" packets here - -- @return true on success, false on failure - close = function(self) return self.conn:close() end, + --- Closes the session + -- TODO: We should probably send some "closing" packets here + -- @return true on success, false on failure + close = function(self) return self.conn:close() end, - --- Sends and SIP invite - -- @param uri - invite = function(self, uri) - local request = Request:new(Method.INVITE, self.protocol) + --- Sends and SIP invite + -- @param uri + invite = function(self, uri) + local request = Request:new(Method.INVITE, self.protocol) - local lhost, _ = self.sessdata:getClient() - local tm = os.time() + local lhost, _ = self.sessdata:getClient() + local tm = os.time() - local uri = (uri and uri:match("^sip:.*@.*")) or - ("sip:%s@%s"):format(uri, self.sessdata:getDomain()) + local uri = (uri and uri:match("^sip:.*@.*")) or + ("sip:%s@%s"):format(uri, self.sessdata:getDomain()) - request:setUri(uri) - request:setSessionData(self.sessdata) + request:setUri(uri) + request:setSessionData(self.sessdata) - local data = {} - table.insert(data, "v=0") - table.insert(data, ("o=- %s %s IN IP4 %s"):format(tm, tm, lhost)) - table.insert(data, "s=-") - table.insert(data, ("c=IN IP4 %s"):format(lhost)) - table.insert(data, "t=0 0") - table.insert(data, "m=audio 49174 RTP/AVP 0") - table.insert(data, "a=rtpmap:0 PCMU/8000") + local data = {} + table.insert(data, "v=0") + table.insert(data, ("o=- %s %s IN IP4 %s"):format(tm, tm, lhost)) + table.insert(data, "s=-") + table.insert(data, ("c=IN IP4 %s"):format(lhost)) + table.insert(data, "t=0 0") + table.insert(data, "m=audio 49174 RTP/AVP 0") + table.insert(data, "a=rtpmap:0 PCMU/8000") - request:setContent(stdnse.strjoin("\r\n", data)) - request:setContentType("application/sdp") + request:setContent(stdnse.strjoin("\r\n", data)) + request:setContentType("application/sdp") - local status, response = self:exch(request) - if ( not(status) ) then return false, response end + local status, response = self:exch(request) + if ( not(status) ) then return false, response end - local errcode = response:getErrorCode() + local errcode = response:getErrorCode() - if ( Error.PROXY_AUTH_REQUIRED == errcode or - Error.UNAUTHORIZED == errcode ) then + if ( Error.PROXY_AUTH_REQUIRED == errcode or + Error.UNAUTHORIZED == errcode ) then - -- Send an ACK to the server - request:setMethod(Method.ACK) - local status, err = self.conn:send( tostring(request) ) - if ( not(status) ) then return status, "ERROR: Failed to send request" end + -- Send an ACK to the server + request:setMethod(Method.ACK) + local status, err = self.conn:send( tostring(request) ) + if ( not(status) ) then return status, "ERROR: Failed to send request" end - -- Send an authenticated INVITE to the server - request:setMethod(Method.INVITE) - self.cseq = self.cseq + 1 - status, data = self:authenticate(request, response) - if ( not(status) ) then return false, "SIP Authentication failed" end - response = Response:new(data) + -- Send an authenticated INVITE to the server + request:setMethod(Method.INVITE) + self.cseq = self.cseq + 1 + status, data = self:authenticate(request, response) + if ( not(status) ) then return false, "SIP Authentication failed" end + response = Response:new(data) - -- read a bunch of 180 Ringing and 100 Trying requests, until we get a 200 OK - while ( response:getErrorCode() ~= Error.OK ) do - status, data = self.conn:recv() - if ( not(status) ) then return status, "ERROR: Failed to receive response" end - response = Response:new(data) - end + -- read a bunch of 180 Ringing and 100 Trying requests, until we get a 200 OK + while ( response:getErrorCode() ~= Error.OK ) do + status, data = self.conn:recv() + if ( not(status) ) then return status, "ERROR: Failed to receive response" end + response = Response:new(data) + end - end + end - return true - end, + return true + end, - --- Prepares and sends the challenge response authentication to the server - -- @param request instance of the request object requiring authentication - -- @param authdata string containing authentication data - -- @return status true on success false on failure - -- @return err string containing an error message if status is false - authenticate = function(self, request, response) - local rhost, _ = self.sessdata:getServer() - local auth_header, auth_data = response:getAuthData() - local auth = SipAuth:new(auth_data) - auth:setUsername(self.sessdata:getUsername()) - auth:setPassword(self.sessdata:getPassword()) - auth:setMethod(request.method) - auth:setUri(("sip:%s"):format(rhost)) + --- Prepares and sends the challenge response authentication to the server + -- @param request instance of the request object requiring authentication + -- @param authdata string containing authentication data + -- @return status true on success false on failure + -- @return err string containing an error message if status is false + authenticate = function(self, request, response) + local rhost, _ = self.sessdata:getServer() + local auth_header, auth_data = response:getAuthData() + local auth = SipAuth:new(auth_data) + auth:setUsername(self.sessdata:getUsername()) + auth:setPassword(self.sessdata:getPassword()) + auth:setMethod(request.method) + auth:setUri(("sip:%s"):format(rhost)) - if ( auth_header == "WWW-Authenticate" ) then - request:setWWWAuth(auth:createResponse()) - else - request:setProxyAuth(auth:createResponse()) - end - request:setCseq(self.cseq) + if ( auth_header == "WWW-Authenticate" ) then + request:setWWWAuth(auth:createResponse()) + else + request:setProxyAuth(auth:createResponse()) + end + request:setCseq(self.cseq) - local status, err = self.conn:send( tostring(request) ) - if ( not(status) ) then return status, "ERROR: Failed to send request" end + local status, err = self.conn:send( tostring(request) ) + if ( not(status) ) then return status, "ERROR: Failed to send request" end - local data - status, data = self.conn:recv() - if ( not(status) and data ~= "TIMEOUT" ) then - return status, "ERROR: Failed to receive response" - end - return status, data - end, + local data + status, data = self.conn:recv() + if ( not(status) and data ~= "TIMEOUT" ) then + return status, "ERROR: Failed to receive response" + end + return status, data + end, - --- Sends a SIP Request and receives the Response - -- @param request instance of Request - -- @return status true on success, false on failure - -- @return resp containing a new Response instance - -- err containing error message if status is false - exch = function(self, request) - request:setCseq(self.cseq) + --- Sends a SIP Request and receives the Response + -- @param request instance of Request + -- @return status true on success, false on failure + -- @return resp containing a new Response instance + -- err containing error message if status is false + exch = function(self, request) + request:setCseq(self.cseq) - local status, err = self.conn:send( tostring(request) ) - if ( not(status) ) then return status, "ERROR: Failed to send request" end + local status, err = self.conn:send( tostring(request) ) + if ( not(status) ) then return status, "ERROR: Failed to send request" end - local status, data = self.conn:recv() - if ( not(status) ) then return status, "ERROR: Failed to receive response" end + local status, data = self.conn:recv() + if ( not(status) ) then return status, "ERROR: Failed to receive response" end - return true, Response:new(data) - end, + return true, Response:new(data) + end, - --- Sends a register request to the server - -- @return status true on success, false on failure - -- @return msg string containing the error message (if status is false) - register = function(self) - local request = Request:new(Method.REGISTER, self.protocol) + --- Sends a register request to the server + -- @return status true on success, false on failure + -- @return msg string containing the error message (if status is false) + register = function(self) + local request = Request:new(Method.REGISTER, self.protocol) - request:setUri("sip:" .. self.sessdata:getServer()) - request:setSessionData(self.sessdata) - request:setExpires(self.expires) + request:setUri("sip:" .. self.sessdata:getServer()) + request:setSessionData(self.sessdata) + request:setExpires(self.expires) - local status, response = self:exch(request) - if (not(status)) then return false, response end + local status, response = self:exch(request) + if (not(status)) then return false, response end - local errcode = response:getErrorCode() + local errcode = response:getErrorCode() - if ( status and errcode == Error.OK ) then - return true, response - elseif ( Error.PROXY_AUTH_REQUIRED == errcode or Error.UNAUTHORIZED == errcode ) then - local data - self.cseq = self.cseq + 1 - status, data = self:authenticate(request, response) - response = Response:new(data) - errcode = response:getErrorCode() - if ( not(status) or ( errcode and errcode ~= Error.OK ) ) then - return false, "ERROR: Failed to authenticate" - end - elseif ( Error.FORBIDDEN == errcode ) then - return false, "Authentication forbidden" - else - return false, ("Unhandled error: %d"):format(errcode) - end - return true - end, + if ( status and errcode == Error.OK ) then + return true, response + elseif ( Error.PROXY_AUTH_REQUIRED == errcode or Error.UNAUTHORIZED == errcode ) then + local data + self.cseq = self.cseq + 1 + status, data = self:authenticate(request, response) + response = Response:new(data) + errcode = response:getErrorCode() + if ( not(status) or ( errcode and errcode ~= Error.OK ) ) then + return false, "ERROR: Failed to authenticate" + end + elseif ( Error.FORBIDDEN == errcode ) then + return false, "Authentication forbidden" + else + return false, ("Unhandled error: %d"):format(errcode) + end + return true + end, - --- Sends an option request to the server and handles the response - -- @return status true on success, false on failure - -- @return response if status is true, nil else. - options = function(self) - local req = Request:new(Method.OPTIONS, self.protocol) - req:setUri("sip:" .. self.sessdata:getServer()) - req:setSessionData(self.sessdata) - req:setExpires(self.expires) - req:addHeader("Accept", "application/sdp") + --- Sends an option request to the server and handles the response + -- @return status true on success, false on failure + -- @return response if status is true, nil else. + options = function(self) + local req = Request:new(Method.OPTIONS, self.protocol) + req:setUri("sip:" .. self.sessdata:getServer()) + req:setSessionData(self.sessdata) + req:setExpires(self.expires) + req:addHeader("Accept", "application/sdp") - local status, response = self:exch(req) - if status then return true, response end + local status, response = self:exch(req) + if status then return true, response end - return false, nil - end, + return false, nil + end, } -- The connection class contains basic communication code Connection = { - --- Creates a new SIP Connection - -- @param host table containing the host to connect to - -- @param port table containing the port to connect to - -- @return o containing a new Connection instance - new = function(self, host, port) - local o = {} - setmetatable(o, self) - self.__index = self - o.host = host - o.port = port - o.socket = nmap.new_socket() - return o - end, + --- Creates a new SIP Connection + -- @param host table containing the host to connect to + -- @param port table containing the port to connect to + -- @return o containing a new Connection instance + new = function(self, host, port) + local o = {} + setmetatable(o, self) + self.__index = self + o.host = host + o.port = port + o.socket = nmap.new_socket() + return o + end, - --- Connects to the server - -- @return status containing true on success and false on failure - -- @return err containing the error message (if status is false) - connect = function(self) - local status, err = self.socket:connect(self.host, self.port) - if ( status ) then - local status, lhost, lport, _, _ = self.socket:get_info() - if ( status ) then - self.lhost = lhost - self.lport = lport - end - end - return status, err - end, + --- Connects to the server + -- @return status containing true on success and false on failure + -- @return err containing the error message (if status is false) + connect = function(self) + local status, err = self.socket:connect(self.host, self.port) + if ( status ) then + local status, lhost, lport, _, _ = self.socket:get_info() + if ( status ) then + self.lhost = lhost + self.lport = lport + end + end + return status, err + end, - --- Sends the data over the socket - -- @return status true on success, false on failure - send = function(self, data) - return self.socket:send(data) - end, + --- Sends the data over the socket + -- @return status true on success, false on failure + send = function(self, data) + return self.socket:send(data) + end, - --- Receives data from the socket - -- @return status true on success, false on failure - recv = function(self) - return self.socket:receive() - end, + --- Receives data from the socket + -- @return status true on success, false on failure + recv = function(self) + return self.socket:receive() + end, - --- Closes the communication channel (socket) - -- @return true on success false on failure - close = function(self) - return self.socket:close() - end, + --- Closes the communication channel (socket) + -- @return true on success false on failure + close = function(self) + return self.socket:close() + end, - --- Retrieves the client ip and port - -- @return lhost string containing the local ip - -- @return lport number containing the local port - getClient = function(self) return self.lhost, self.lport end, + --- Retrieves the client ip and port + -- @return lhost string containing the local ip + -- @return lport number containing the local port + getClient = function(self) return self.lhost, self.lport end, - --- Retrieves the server ip and port - -- @return rhost string containing the server ip - -- @return rport number containing the server port - getServer = function(self) return ( self.host.ip or self.host ), ( self.port.number or self.port ) end, + --- Retrieves the server ip and port + -- @return rhost string containing the server ip + -- @return rport number containing the server port + getServer = function(self) return ( self.host.ip or self.host ), ( self.port.number or self.port ) end, } @@ -412,424 +412,424 @@ Connection = { -- The response class holds the necessary methods and parameters to parse a response Response = { - --- Creates a new Response instance - -- @param str containing the data as received over the socket - -- @return o table containing a new Response instance - new = function(self, str) - local o = {} - setmetatable(o, self) - self.__index = self - o.tbl = stdnse.strsplit("\r\n", str) - return o - end, + --- Creates a new Response instance + -- @param str containing the data as received over the socket + -- @return o table containing a new Response instance + new = function(self, str) + local o = {} + setmetatable(o, self) + self.__index = self + o.tbl = stdnse.strsplit("\r\n", str) + return o + end, - --- Retrieves a given header value from the response - -- @param name string containing the name of the header - -- @return value string containing the header value - getHeader = function(self,name) - for _, line in ipairs(self.tbl) do - local header, value = line:match("^(.-): (.*)$") - if ( header and header:lower() == name:lower() ) then - return value - end - end - end, + --- Retrieves a given header value from the response + -- @param name string containing the name of the header + -- @return value string containing the header value + getHeader = function(self,name) + for _, line in ipairs(self.tbl) do + local header, value = line:match("^(.-): (.*)$") + if ( header and header:lower() == name:lower() ) then + return value + end + end + end, - --- Returns the error code from the SIP response - -- @return err number containing the error code - getErrorCode = function(self) - return tonumber(self.tbl[1]:match("SIP/%d%.%d (%d+)")) - end, + --- Returns the error code from the SIP response + -- @return err number containing the error code + getErrorCode = function(self) + return tonumber(self.tbl[1]:match("SIP/%d%.%d (%d+)")) + end, - --- Returns the error message returned by the server - -- @return errmsg string containing the error message - getErrorMessage = function(self) - return self.tbl[1]:match("^SIP/%d%.%d %d+ (.+)$") - end, + --- Returns the error message returned by the server + -- @return errmsg string containing the error message + getErrorMessage = function(self) + return self.tbl[1]:match("^SIP/%d%.%d %d+ (.+)$") + end, - --- Returns the message method - -- @return method string containing the method - getMethod = function(self) - return self.tbl[1]:match("^(.-)%s.*SIP/2%.0$") - end, + --- Returns the message method + -- @return method string containing the method + getMethod = function(self) + return self.tbl[1]:match("^(.-)%s.*SIP/2%.0$") + end, - --- Returns the authentication data from the SIP response - -- @return auth string containing the raw authentication data - getAuthData = function(self) - local auth = self:getHeader("WWW-Authenticate") or self:getHeader("Proxy-Authenticate") - if ( auth ) then - return ( self:getHeader("WWW-Authenticate") and - "WWW-Authenticate" or - "Proxy-Authenticate"), auth - end - end, + --- Returns the authentication data from the SIP response + -- @return auth string containing the raw authentication data + getAuthData = function(self) + local auth = self:getHeader("WWW-Authenticate") or self:getHeader("Proxy-Authenticate") + if ( auth ) then + return ( self:getHeader("WWW-Authenticate") and + "WWW-Authenticate" or + "Proxy-Authenticate"), auth + end + end, - --- Retrieves the current sequence number - -- @return cseq number containing the current sequence number - getCSeq = function(self) - local cseq = self:getHeader("CSeq") - cseq = (cseq and cseq:match("^(%d+)")) - return (cseq and tonumber(cseq)) - end, + --- Retrieves the current sequence number + -- @return cseq number containing the current sequence number + getCSeq = function(self) + local cseq = self:getHeader("CSeq") + cseq = (cseq and cseq:match("^(%d+)")) + return (cseq and tonumber(cseq)) + end, } -- The request class holds the necessary functions and parameters for a basic SIP request Request = { - --- Creates a new Request instance - -- @param method string containing the request method to use - -- @param proto Used protocol, could be "UDP" or "TCP" - -- @return o containing a new Request instance - new = function(self, method, proto) - local o = {} - setmetatable(o, self) - self.__index = self + --- Creates a new Request instance + -- @param method string containing the request method to use + -- @param proto Used protocol, could be "UDP" or "TCP" + -- @return o containing a new Request instance + new = function(self, method, proto) + local o = {} + setmetatable(o, self) + self.__index = self - o.ua = "Nmap NSE" - o.protocol = proto or "UDP" - o.expires = 0 - o.allow = "PRACK, INVITE ,ACK, BYE, CANCEL, UPDATE, SUBSCRIBE" - .. ",NOTIFY, REFER, MESSAGE, OPTIONS" + o.ua = "Nmap NSE" + o.protocol = proto or "UDP" + o.expires = 0 + o.allow = "PRACK, INVITE ,ACK, BYE, CANCEL, UPDATE, SUBSCRIBE" + .. ",NOTIFY, REFER, MESSAGE, OPTIONS" - o.maxfwd = 70 - o.method = method - o.length = 0 - o.cid = Util.get_random_string(60) - return o - end, + o.maxfwd = 70 + o.method = method + o.length = 0 + o.cid = Util.get_random_string(60) + return o + end, - --- Sets the sessiondata so that session information may be fetched - -- @param data instance of SessionData - setSessionData = function(self, data) self.sessdata = data end, + --- Sets the sessiondata so that session information may be fetched + -- @param data instance of SessionData + setSessionData = function(self, data) self.sessdata = data end, - --- Adds a custom header to the request - -- @param name string containing the header name - -- @param value string containing the header value - addHeader = function(self, name, value) - self.headers = self.headers or {} - table.insert(self.headers, ("%s: %s"):format(name, value)) - end, + --- Adds a custom header to the request + -- @param name string containing the header name + -- @param value string containing the header value + addHeader = function(self, name, value) + self.headers = self.headers or {} + table.insert(self.headers, ("%s: %s"):format(name, value)) + end, - --- Sets the SIP uri - -- @param uri string containing the SIP uri - setUri = function(self, uri) self.uri = uri end, + --- Sets the SIP uri + -- @param uri string containing the SIP uri + setUri = function(self, uri) self.uri = uri end, - --- Sets an error - -- @param code number containing the error code - -- @param msg string containing the error message - setError = function(self, code, msg) self.error = { code = code, msg = msg } end, + --- Sets an error + -- @param code number containing the error code + -- @param msg string containing the error message + setError = function(self, code, msg) self.error = { code = code, msg = msg } end, - --- Sets the request method - -- @param method string containing a valid SIP method (@see Method constant) - setMethod = function(self, method) self.method = method end, + --- Sets the request method + -- @param method string containing a valid SIP method (@see Method constant) + setMethod = function(self, method) self.method = method end, - --- Sets the sequence number - -- @param seq number containing the sequence number to set - setCseq = function(self, seq) self.cseq = seq end, + --- Sets the sequence number + -- @param seq number containing the sequence number to set + setCseq = function(self, seq) self.cseq = seq end, - --- Sets the allow header - -- @param allow table containing all of the allowed SIP methods - setAllow = function(self, allow) self.allow = stdnse.strjoin(", ", allow) end, + --- Sets the allow header + -- @param allow table containing all of the allowed SIP methods + setAllow = function(self, allow) self.allow = stdnse.strjoin(", ", allow) end, - --- Sets the request content data - -- @param string containing the content data - setContent = function(self, content) self.content = content end, + --- Sets the request content data + -- @param string containing the content data + setContent = function(self, content) self.content = content end, - --- Sets the requests' content type - -- @param t string containing the content type - setContentType = function(self, t) self.content_type = t end, + --- Sets the requests' content type + -- @param t string containing the content type + setContentType = function(self, t) self.content_type = t end, - --- Sets the supported SIP methods - -- @param supported string containing the supported methods - setSupported = function(self, supported) self.supported = supported end, + --- Sets the supported SIP methods + -- @param supported string containing the supported methods + setSupported = function(self, supported) self.supported = supported end, - --- Sets the content-length of the SIP request - -- @param len number containing the length of the actual request - setContentLength = function(self, len) self.length = len end, + --- Sets the content-length of the SIP request + -- @param len number containing the length of the actual request + setContentLength = function(self, len) self.length = len end, - --- Sets the expires header of the SIP request - -- @param expires number containing the expire value - setExpires = function(self, expires) self.expires = expires end, + --- Sets the expires header of the SIP request + -- @param expires number containing the expire value + setExpires = function(self, expires) self.expires = expires end, - --- Sets the User Agent being used to connect to the SIP server - -- @param ua string containing the User-Agent name (defaults to Nmap NSE) - setUA = function(self, ua) self.ua = ua end, + --- Sets the User Agent being used to connect to the SIP server + -- @param ua string containing the User-Agent name (defaults to Nmap NSE) + setUA = function(self, ua) self.ua = ua end, - --- Sets the caller ID information of the SIP request - -- @param cid string containing the callers id - setCallId = function(self, cid) self.cid = cid end, + --- Sets the caller ID information of the SIP request + -- @param cid string containing the callers id + setCallId = function(self, cid) self.cid = cid end, - --- Sets the maximum forwards allowed of this request - -- @param maxfwd number containing the maximum allowed forwards - setForwards = function(self, maxfwd) self.maxfwd = maxfwd end, + --- Sets the maximum forwards allowed of this request + -- @param maxfwd number containing the maximum allowed forwards + setForwards = function(self, maxfwd) self.maxfwd = maxfwd end, - --- Sets the proxy authentication data - -- @param auth string containing properly formatted proxy authentication data - setProxyAuth = function(self, auth) self.proxyauth = auth end, + --- Sets the proxy authentication data + -- @param auth string containing properly formatted proxy authentication data + setProxyAuth = function(self, auth) self.proxyauth = auth end, - --- Sets the www authentication data - -- @param auth string containing properly formatted proxy authentication data - setWWWAuth = function(self, auth) self.wwwauth = auth end, + --- Sets the www authentication data + -- @param auth string containing properly formatted proxy authentication data + setWWWAuth = function(self, auth) self.wwwauth = auth end, - --- Specifies the network protocol being used - -- @param proto should be either "UDP" or "TCP" - setProtocol = function(self, proto) - assert( proto == "UDP" or proto == "TCP", ("Unsupported protocol %s"):format(proto)) - self.protocol = proto - end, + --- Specifies the network protocol being used + -- @param proto should be either "UDP" or "TCP" + setProtocol = function(self, proto) + assert( proto == "UDP" or proto == "TCP", ("Unsupported protocol %s"):format(proto)) + self.protocol = proto + end, - --- Converts the request to a String suitable to be sent over the socket - -- @return ret string containing the complete request for sending over the socket - __tostring = function(self) - local data = {} - local branch = "z9hG4bK" .. Util.get_random_string(25) - -- must be at least 32-bit unique - self.from_tag = self.from_tag or Util.get_random_string(20) - local sessdata = self.sessdata - local lhost, lport = sessdata:getClient() - local rhost, rport = sessdata:getServer() + --- Converts the request to a String suitable to be sent over the socket + -- @return ret string containing the complete request for sending over the socket + __tostring = function(self) + local data = {} + local branch = "z9hG4bK" .. Util.get_random_string(25) + -- must be at least 32-bit unique + self.from_tag = self.from_tag or Util.get_random_string(20) + local sessdata = self.sessdata + local lhost, lport = sessdata:getClient() + local rhost, rport = sessdata:getServer() - local name, user, domain = sessdata:getName(), sessdata:getUsername(), sessdata:getDomain() + local name, user, domain = sessdata:getName(), sessdata:getUsername(), sessdata:getDomain() - assert(self.method, "No method specified") - assert(self.maxfwd, "Max forward not set") + assert(self.method, "No method specified") + assert(self.maxfwd, "Max forward not set") - -- if no domain was specified use the remote host instead - domain = domain or rhost + -- if no domain was specified use the remote host instead + domain = domain or rhost - if ( self.error ) then - table.insert(data, ("SIP/2.0 %s %d"):format(self.error.msg, self.error.code)) - else - if ( self.method == Method.ACK ) then - table.insert(data, ("%s %s:%d SIP/2.0"):format(self.method, self.uri, rport)) - else - table.insert(data, ("%s %s SIP/2.0"):format(self.method, self.uri)) - end - end - table.insert(data, ("Via: SIP/2.0/%s %s:%d;rport;branch=%s"):format(self.protocol, lhost, lport, branch)) - table.insert(data, ("Max-Forwards: %d"):format(self.maxfwd)) - table.insert(data, ("From: \"%s\" ;tag=%s"):format(name, user, domain, self.from_tag)) + if ( self.error ) then + table.insert(data, ("SIP/2.0 %s %d"):format(self.error.msg, self.error.code)) + else + if ( self.method == Method.ACK ) then + table.insert(data, ("%s %s:%d SIP/2.0"):format(self.method, self.uri, rport)) + else + table.insert(data, ("%s %s SIP/2.0"):format(self.method, self.uri)) + end + end + table.insert(data, ("Via: SIP/2.0/%s %s:%d;rport;branch=%s"):format(self.protocol, lhost, lport, branch)) + table.insert(data, ("Max-Forwards: %d"):format(self.maxfwd)) + table.insert(data, ("From: \"%s\" ;tag=%s"):format(name, user, domain, self.from_tag)) - if ( self.method == Method.INVITE ) then - table.insert(data, ("To: "):format(user, domain)) - else - table.insert(data, ("To: \"%s\" "):format(name, user, domain)) - end + if ( self.method == Method.INVITE ) then + table.insert(data, ("To: "):format(user, domain)) + else + table.insert(data, ("To: \"%s\" "):format(name, user, domain)) + end - table.insert(data, ("Call-ID: %s"):format(self.cid)) + table.insert(data, ("Call-ID: %s"):format(self.cid)) - if ( self.error and self.error.code == Error.OK ) then - table.insert(data, ("CSeq: %d OPTIONS"):format(self.cseq)) - else - table.insert(data, ("CSeq: %d %s"):format(self.cseq, self.method)) - end + if ( self.error and self.error.code == Error.OK ) then + table.insert(data, ("CSeq: %d OPTIONS"):format(self.cseq)) + else + table.insert(data, ("CSeq: %d %s"):format(self.cseq, self.method)) + end - if ( self.method ~= Method.ACK ) then - table.insert(data, ("User-Agent: %s"):format(self.ua)) - table.insert(data, ("Contact: \"%s\" "):format(name, user, lhost, lport)) - if ( self.expires ) then - table.insert(data, ("Expires: %d"):format(self.expires)) - end - if ( self.allow ) then - table.insert(data, ("Allow: %s"):format(self.allow)) - end - if ( self.supported ) then - table.insert(data, ("Supported: %s"):format(self.supported)) - end + if ( self.method ~= Method.ACK ) then + table.insert(data, ("User-Agent: %s"):format(self.ua)) + table.insert(data, ("Contact: \"%s\" "):format(name, user, lhost, lport)) + if ( self.expires ) then + table.insert(data, ("Expires: %d"):format(self.expires)) + end + if ( self.allow ) then + table.insert(data, ("Allow: %s"):format(self.allow)) + end + if ( self.supported ) then + table.insert(data, ("Supported: %s"):format(self.supported)) + end - if ( not(self.error) ) then - if ( self.proxyauth ) then - table.insert(data, ("Proxy-Authorization: %s"):format(self.proxyauth)) - end - if ( self.wwwauth ) then - table.insert(data, ("Authorization: %s"):format(self.wwwauth)) - end - end + if ( not(self.error) ) then + if ( self.proxyauth ) then + table.insert(data, ("Proxy-Authorization: %s"):format(self.proxyauth)) + end + if ( self.wwwauth ) then + table.insert(data, ("Authorization: %s"):format(self.wwwauth)) + end + end - self.length = (self.content and #self.content +2 or 0) - if ( self.headers ) then - for _, val in ipairs(self.headers) do - table.insert(data, val) - end - end - if ( self.content_type ) then - table.insert(data, ("Content-Type: %s"):format(self.content_type)) - end - table.insert(data, ("Content-Length: %d"):format(self.length)) - table.insert(data, "") + self.length = (self.content and #self.content +2 or 0) + if ( self.headers ) then + for _, val in ipairs(self.headers) do + table.insert(data, val) + end + end + if ( self.content_type ) then + table.insert(data, ("Content-Type: %s"):format(self.content_type)) + end + table.insert(data, ("Content-Length: %d"):format(self.length)) + table.insert(data, "") - if ( self.content ) then table.insert(data, self.content) end - table.insert(data, "") - else - self.length = (self.content and #self.content +2 or 0) + if ( self.content ) then table.insert(data, self.content) end + table.insert(data, "") + else + self.length = (self.content and #self.content +2 or 0) - table.insert(data, ("Content-Length: %d"):format(self.length)) - table.insert(data, "") - end - return stdnse.strjoin("\r\n", data) - end, + table.insert(data, ("Content-Length: %d"):format(self.length)) + table.insert(data, "") + end + return stdnse.strjoin("\r\n", data) + end, } -- A minimal Util class with supporting functions Util = { - --- Generates a random string of the requested length. - -- @param length (optional) The length of the string to return. Default: 8. - -- @param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. - -- @return The random string. - get_random_string = function(length, set) - if(length == nil) then - length = 8 - end + --- Generates a random string of the requested length. + -- @param length (optional) The length of the string to return. Default: 8. + -- @param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore. + -- @return The random string. + get_random_string = function(length, set) + if(length == nil) then + length = 8 + end - if(set == nil) then - set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" - end + if(set == nil) then + set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" + end - local str = "" + local str = "" - for i = 1, length, 1 do - local random = math.random(#set) - str = str .. string.sub(set, random, random) - end + for i = 1, length, 1 do + local random = math.random(#set) + str = str .. string.sub(set, random, random) + end - return str - end + return str + end } -- The SIP authentication class, supporting MD5 digest authentication SipAuth = { - --- Creates a new SipAuth instance - -- @param auth string containing the auth data as received from the server - new = function(self, auth) - local o = {} - setmetatable(o, self) - self.__index = self - o.auth = auth - return o - end, + --- Creates a new SipAuth instance + -- @param auth string containing the auth data as received from the server + new = function(self, auth) + local o = {} + setmetatable(o, self) + self.__index = self + o.auth = auth + return o + end, - --- Sets the username used for authentication - -- @param username string containing the name of the user - setUsername = function(self, username) self.username = username end, + --- Sets the username used for authentication + -- @param username string containing the name of the user + setUsername = function(self, username) self.username = username end, - --- Sets the password used for authentication - -- @param password string containing the password of the user - setPassword = function(self, password) self.password = password end, + --- Sets the password used for authentication + -- @param password string containing the password of the user + setPassword = function(self, password) self.password = password end, - --- Sets the method used for authentication - -- @param method string containing the method (Usually REGISTER) - setMethod = function(self, method) self.method = method end, + --- Sets the method used for authentication + -- @param method string containing the method (Usually REGISTER) + setMethod = function(self, method) self.method = method end, - --- Sets the uri used for authentication - -- @param uri string containing the uri (Usually sip:) - setUri = function(self, uri) self.uri = uri end, + --- Sets the uri used for authentication + -- @param uri string containing the uri (Usually sip:) + setUri = function(self, uri) self.uri = uri end, - --- Processes and parses a challenge as received from the server - parseChallenge = function(self) - if ( not(self.auth) ) then return end - self.nonce = self.auth:match("nonce=[\"]([^,]-)[\"]") - self.algorithm = self.auth:match("algorithm=[\"]*(.-)[\"]*,") - self.realm = self.auth:match("realm=[\"]([^,]-)[\"]") - assert(self.algorithm:upper() == "MD5", - ("Unsupported algorithm detected in authentication challenge (%s)"):format(self.algorithm:upper())) - end, + --- Processes and parses a challenge as received from the server + parseChallenge = function(self) + if ( not(self.auth) ) then return end + self.nonce = self.auth:match("nonce=[\"]([^,]-)[\"]") + self.algorithm = self.auth:match("algorithm=[\"]*(.-)[\"]*,") + self.realm = self.auth:match("realm=[\"]([^,]-)[\"]") + assert(self.algorithm:upper() == "MD5", + ("Unsupported algorithm detected in authentication challenge (%s)"):format(self.algorithm:upper())) + end, - --- Calculates the authentication response - -- @return reponse string containing the authentication response - calculateResponse = function(self) + --- Calculates the authentication response + -- @return reponse string containing the authentication response + calculateResponse = function(self) - if ( not(self.nonce) or not(self.algorithm) or not(self.realm) ) then - self:parseChallenge() - end + if ( not(self.nonce) or not(self.algorithm) or not(self.realm) ) then + self:parseChallenge() + end - assert(self.username, "SipAuth: No username specified") - assert(self.password, "SipAuth: No password specified") - assert(self.method, "SipAuth: No method specified") - assert(self.uri, "SipAuth: No uri specified") + assert(self.username, "SipAuth: No username specified") + assert(self.password, "SipAuth: No password specified") + assert(self.method, "SipAuth: No method specified") + assert(self.uri, "SipAuth: No uri specified") - local result - if ( self.algorithm == "MD5" ) then - local HA1 = select(2, bin.unpack("H16", openssl.md5(self.username .. ":" .. self.realm .. ":" .. self.password))) - local HA2 = select(2, bin.unpack("H16", openssl.md5(self.method .. ":" .. self.uri))) - result = openssl.md5(HA1:lower() .. ":" .. self.nonce ..":" .. HA2:lower()) - end - return select(2, bin.unpack("H16", result)):lower() - end, + local result + if ( self.algorithm == "MD5" ) then + local HA1 = select(2, bin.unpack("H16", openssl.md5(self.username .. ":" .. self.realm .. ":" .. self.password))) + local HA2 = select(2, bin.unpack("H16", openssl.md5(self.method .. ":" .. self.uri))) + result = openssl.md5(HA1:lower() .. ":" .. self.nonce ..":" .. HA2:lower()) + end + return select(2, bin.unpack("H16", result)):lower() + end, - --- Creates the complete authentication response - -- @return auth string containing the complete authentication digest - createResponse = function(self) - local response = self:calculateResponse() - return ("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\"," .. - " uri=\"%s\", response=\"%s\", algorithm=%s"):format(self.username, self.realm, - self.nonce, self.uri, response, self.algorithm) - end, + --- Creates the complete authentication response + -- @return auth string containing the complete authentication digest + createResponse = function(self) + local response = self:calculateResponse() + return ("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\"," .. + " uri=\"%s\", response=\"%s\", algorithm=%s"):format(self.username, self.realm, + self.nonce, self.uri, response, self.algorithm) + end, } -- The Helper class used as main script interface Helper = { - --- Creates a new instance of the Helper class - -- @param host table containing the remote host - -- @param port table containing the remote port - -- @param options table containing any options to pass along to the - -- session (@see Session:new for more details) - -- @return o containing a new instance of the Helper class - new = function(self, host, port, options) - local o = {} - setmetatable(o, self) - self.__index = self - local timeout = stdnse.get_script_args("sip.timeout") - if ( timeout ) then options.timeout = timeout end - o.sessdata = SessionData:new() - o.session = Session:new(host, port, o.sessdata, options) - return o - end, + --- Creates a new instance of the Helper class + -- @param host table containing the remote host + -- @param port table containing the remote port + -- @param options table containing any options to pass along to the + -- session (@see Session:new for more details) + -- @return o containing a new instance of the Helper class + new = function(self, host, port, options) + local o = {} + setmetatable(o, self) + self.__index = self + local timeout = stdnse.get_script_args("sip.timeout") + if ( timeout ) then options.timeout = timeout end + o.sessdata = SessionData:new() + o.session = Session:new(host, port, o.sessdata, options) + return o + end, - --- Connects the helper instance - connect = function(self) return self.session:connect() end, + --- Connects the helper instance + connect = function(self) return self.session:connect() end, - --- Disconnects and closes the helper instance - close = function(self) return self.session:close() end, + --- Disconnects and closes the helper instance + close = function(self) return self.session:close() end, - --- Sets the credentials used when performing authentication - -- @param username string containing the username to use for authentication - -- @param password string containing the password to use for authentication - setCredentials = function(self, username, password) - self.sessdata:setUsername(username) - self.sessdata:setPassword(password) - end, + --- Sets the credentials used when performing authentication + -- @param username string containing the username to use for authentication + -- @param password string containing the password to use for authentication + setCredentials = function(self, username, password) + self.sessdata:setUsername(username) + self.sessdata:setPassword(password) + end, - --- Sets the SIP domain - -- @param domain string containing the domain name - setDomain = function(self, domain) self.sessdata:setDomain(domain) end, + --- Sets the SIP domain + -- @param domain string containing the domain name + setDomain = function(self, domain) self.sessdata:setDomain(domain) end, - --- Register the UAC with the server - -- @param options table containing zero or more options - -- (@see Session:register for more details) - -- @return status true on success, false on failure - -- @return msg containing the error message if status is false - register = function(self, options) - local status, response = self.session:register(options) - if ( not(status) ) then return false, response end - return true - end, + --- Register the UAC with the server + -- @param options table containing zero or more options + -- (@see Session:register for more details) + -- @return status true on success, false on failure + -- @return msg containing the error message if status is false + register = function(self, options) + local status, response = self.session:register(options) + if ( not(status) ) then return false, response end + return true + end, - options = function(self) return self.session:options() end, + options = function(self) return self.session:options() end, - --- Attempts to INVITE the user at uri to a call - -- @param uri string containing the sip uri - -- @return status true on success, false on failure - invite = function(self, uri) - return self.session:invite(uri) - end, + --- Attempts to INVITE the user at uri to a call + -- @param uri string containing the sip uri + -- @return status true on success, false on failure + invite = function(self, uri) + return self.session:invite(uri) + end, } diff --git a/nselib/smb.lua b/nselib/smb.lua index 82623760e..1f40e3439 100644 --- a/nselib/smb.lua +++ b/nselib/smb.lua @@ -149,39 +149,39 @@ local TIMEOUT = 10000 ---Wrapper around smbauth.add_account. function add_account(host, username, domain, password, password_hash, hash_type, is_admin) - smbauth.add_account(host, username, domain, password, password_hash, hash_type, is_admin) + smbauth.add_account(host, username, domain, password, password_hash, hash_type, is_admin) end ---Wrapper around smbauth.get_account. function get_account(host) - return smbauth.get_account(host) + return smbauth.get_account(host) end ---Create an 'overrides' table function get_overrides(username, domain, password, password_hash, hash_type, overrides) - if(not(overrides)) then - return {username=username, domain=domain, password=password, password_hash=password_hash, hash_type=hash_type} - else - overrides['username'] = username - overrides['domain'] = domain - overrides['password'] = password - overrides['password_hash'] = password_hash - overrides['hash_type'] = hash_type - end + if(not(overrides)) then + return {username=username, domain=domain, password=password, password_hash=password_hash, hash_type=hash_type} + else + overrides['username'] = username + overrides['domain'] = domain + overrides['password'] = password + overrides['password_hash'] = password_hash + overrides['hash_type'] = hash_type + end end ---Get an 'overrides' table for the anonymous user -- --@param overrides [optional] A base table of overrides. The appropriate fields will be added. function get_overrides_anonymous(overrides) - if(not(overrides)) then - return {username='', domain='', password='', password_hash=nil, hash_type='none'} - else - overrides['username'] = '' - overrides['domain'] = '' - overrides['password'] = '' - overrides['password_hash'] = '' - overrides['hash_type'] = 'none' - end + if(not(overrides)) then + return {username='', domain='', password='', password_hash=nil, hash_type='none'} + else + overrides['username'] = '' + overrides['domain'] = '' + overrides['password'] = '' + overrides['password_hash'] = '' + overrides['hash_type'] = 'none' + end end ---Convert a status number from the SMB header into a status name, returning an error message (not nil) if @@ -191,18 +191,18 @@ end --@return A string representing the error. Never nil. function get_status_name(status) - if(status_names[status] == nil) then - -- If the name wasn't found in the array, do a linear search on it - for i, v in pairs(status_names) do - if(v == status) then - return i - end - end + if(status_names[status] == nil) then + -- If the name wasn't found in the array, do a linear search on it + for i, v in pairs(status_names) do + if(v == status) then + return i + end + end - return string.format("NT_STATUS_UNKNOWN (0x%08x)", status) - else - return status_names[status] - end + return string.format("NT_STATUS_UNKNOWN (0x%08x)", status) + else + return status_names[status] + end end @@ -215,33 +215,33 @@ end --@param host The host object. --@return The port number to use, or nil if we don't have an SMB port function get_port(host) - local port_u137 = nmap.get_port_state(host, {number=137, protocol="udp"}) - local port_t139 = nmap.get_port_state(host, {number=139, protocol="tcp"}) - local port_t445 = nmap.get_port_state(host, {number=445, protocol="tcp"}) - local custom_port = nil + local port_u137 = nmap.get_port_state(host, {number=137, protocol="udp"}) + local port_t139 = nmap.get_port_state(host, {number=139, protocol="tcp"}) + local port_t445 = nmap.get_port_state(host, {number=445, protocol="tcp"}) + local custom_port = nil - if(nmap.registry.args.smbport ~= nil) then - custom_port = nmap.get_port_state(host, {number=tonumber(nmap.registry.args.smbport), protocol="tcp"}) - end + if(nmap.registry.args.smbport ~= nil) then + custom_port = nmap.get_port_state(host, {number=tonumber(nmap.registry.args.smbport), protocol="tcp"}) + end - -- Try a user-defined port first - if(custom_port ~= nil and custom_port.state == "open") then - return custom_port.number - end + -- Try a user-defined port first + if(custom_port ~= nil and custom_port.state == "open") then + return custom_port.number + end - if(port_t445 ~= nil and port_t445.state == "open") then - -- tcp/445 is open, we're good - return 445 - end + if(port_t445 ~= nil and port_t445.state == "open") then + -- tcp/445 is open, we're good + return 445 + end - if(port_t139 ~= nil and port_t139.state == "open") then - -- tcp/139 is open, check uf udp/137 is open or unknown - if(port_u137 == nil or port_u137.state == "open" or port_u137.state == "open|filtered") then - return 139 - end - end + if(port_t139 ~= nil and port_t139.state == "open") then + -- tcp/139 is open, check uf udp/137 is open or unknown + if(port_u137 == nil or port_u137.state == "open" or port_u137.state == "open|filtered") then + return 139 + end + end - return nil + return nil end ---Turn off extended security negotiations for this connection. There are a few reasons you might want to @@ -249,7 +249,7 @@ end -- to give the same level of information in some cases (namely, it doesn't present the server's name). --@param smb The SMB state table. function disable_extended(smb) - smb['extended_security'] = false + smb['extended_security'] = false end --- Begins a SMB session, automatically determining the best way to connect. @@ -258,69 +258,69 @@ end -- @return (status, smb) if the status is true, result is the newly crated smb object; -- otherwise, socket is the error message. function start(host) - local port = get_port(host) - local status, result - local state = {} + local port = get_port(host) + local status, result + local state = {} - state['uid'] = 0 - state['tid'] = 0 - state['mid'] = 1 - state['pid'] = math.random(32766) + 1 - state['host'] = host - state['ip'] = host.ip - state['sequence'] = -1 + state['uid'] = 0 + state['tid'] = 0 + state['mid'] = 1 + state['pid'] = math.random(32766) + 1 + state['host'] = host + state['ip'] = host.ip + state['sequence'] = -1 - -- Check whether or not the user requested basic authentication - if(stdnse.get_script_args( "smbbasic" )) then - state['extended_security'] = false - else - state['extended_security'] = true - end + -- Check whether or not the user requested basic authentication + if(stdnse.get_script_args( "smbbasic" )) then + state['extended_security'] = false + else + state['extended_security'] = true + end - -- Store the name of the server - local nbcache_mutex = nmap.mutex("Netbios lookup mutex") - nbcache_mutex "lock" - if ( not(host.registry['netbios_name']) ) then - status, result = netbios.get_server_name(host.ip) - if(status == true) then - host.registry['netbios_name'] = result - state['name'] = result - end - else - stdnse.print_debug(2, "SMB: Resolved netbios name from cache") - state['name'] = host.registry['netbios_name'] - end - nbcache_mutex "done" + -- Store the name of the server + local nbcache_mutex = nmap.mutex("Netbios lookup mutex") + nbcache_mutex "lock" + if ( not(host.registry['netbios_name']) ) then + status, result = netbios.get_server_name(host.ip) + if(status == true) then + host.registry['netbios_name'] = result + state['name'] = result + end + else + stdnse.print_debug(2, "SMB: Resolved netbios name from cache") + state['name'] = host.registry['netbios_name'] + end + nbcache_mutex "done" - stdnse.print_debug(2, "SMB: Starting SMB session for %s (%s)", host.name, host.ip) + stdnse.print_debug(2, "SMB: Starting SMB session for %s (%s)", host.name, host.ip) - if(port == nil) then - return false, "SMB: Couldn't find a valid port to check" - end + if(port == nil) then + return false, "SMB: Couldn't find a valid port to check" + end - -- Initialize the accounts for logging on - smbauth.init_account(host) + -- Initialize the accounts for logging on + smbauth.init_account(host) - if(port ~= 139) then - status, state['socket'] = start_raw(host, port) - state['port'] = port + if(port ~= 139) then + status, state['socket'] = start_raw(host, port) + state['port'] = port - if(status == false) then - return false, state['socket'] - end - return true, state + if(status == false) then + return false, state['socket'] + end + return true, state - else - status, state['socket'] = start_netbios(host, port) - state['port'] = port - if(status == false) then - return false, state['socket'] - end - return true, state + else + status, state['socket'] = start_netbios(host, port) + state['port'] = port + if(status == false) then + return false, state['socket'] + end + return true, state - end + end - return false, "SMB: Couldn't find a valid port to check" + return false, "SMB: Couldn't find a valid port to check" end ---Initiates a SMB connection over whichever port it can, then optionally sends the common @@ -341,61 +341,61 @@ end -- to all functions. --@param bool_disable_extended [optional] If set to true, disables extended security negotiations. function start_ex(host, bool_negotiate_protocol, bool_start_session, str_tree_connect, str_create_file, bool_disable_extended, overrides) - local smbstate - local status, err + local smbstate + local status, err - -- Make sure we have overrides - overrides = overrides or {} + -- Make sure we have overrides + overrides = overrides or {} - -- Begin the SMB session - status, smbstate = start(host) - if(status == false) then - return false, smbstate - end + -- Begin the SMB session + status, smbstate = start(host) + if(status == false) then + return false, smbstate + end - -- Disable extended security if it was requested - if(bool_disable_extended == true) then - disable_extended(smbstate) - end + -- Disable extended security if it was requested + if(bool_disable_extended == true) then + disable_extended(smbstate) + end - if(bool_negotiate_protocol == true) then - -- Negotiate the protocol - status, err = negotiate_protocol(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + if(bool_negotiate_protocol == true) then + -- Negotiate the protocol + status, err = negotiate_protocol(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - if(bool_start_session == true) then - -- Start up a session - status, err = start_session(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + if(bool_start_session == true) then + -- Start up a session + status, err = start_session(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - if(str_tree_connect ~= nil) then - -- Connect to share - status, err = tree_connect(smbstate, str_tree_connect, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + if(str_tree_connect ~= nil) then + -- Connect to share + status, err = tree_connect(smbstate, str_tree_connect, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - if(str_create_file ~= nil) then - -- Try to connect to requested pipe - status, err = create_file(smbstate, str_create_file, overrides) - if(status == false) then - stop(smbstate) - return false, err - end - end - end - end - end + if(str_create_file ~= nil) then + -- Try to connect to requested pipe + status, err = create_file(smbstate, str_create_file, overrides) + if(status == false) then + stop(smbstate) + return false, err + end + end + end + end + end - -- Return everything - return true, smbstate + -- Return everything + return true, smbstate end --- Kills the SMB connection and closes the socket. @@ -408,24 +408,24 @@ end -- is undefined. function stop(smb) - if(smb['tid'] ~= 0) then - tree_disconnect(smb) - end + if(smb['tid'] ~= 0) then + tree_disconnect(smb) + end - if(smb['uid'] ~= 0) then - logoff(smb) - end + if(smb['uid'] ~= 0) then + logoff(smb) + end - stdnse.print_debug(2, "SMB: Closing socket") - if(smb['socket'] ~= nil) then - local status, err = smb['socket']:close() + stdnse.print_debug(2, "SMB: Closing socket") + if(smb['socket'] ~= nil) then + local status, err = smb['socket']:close() - if(status == false) then - return false, "SMB: Failed to close socket: " .. err - end - end + if(status == false) then + return false, "SMB: Failed to close socket: " .. err + end + end - return true + return true end --- Begins a raw SMB session, likely over port 445. Since nothing extra is required, this @@ -436,17 +436,17 @@ end --@return (status, socket) if status is true, result is the newly created socket. -- Otherwise, socket is the error message. function start_raw(host, port) - local status, err - local socket = nmap.new_socket() + local status, err + local socket = nmap.new_socket() - socket:set_timeout(TIMEOUT) - status, err = socket:connect(host, port, "tcp") + socket:set_timeout(TIMEOUT) + status, err = socket:connect(host, port, "tcp") - if(status == false) then - return false, "SMB: Failed to connect to host: " .. err - end + if(status == false) then + return false, "SMB: Failed to connect to host: " .. err + end - return true, socket + return true, socket end --- This function will take a string like "a.b.c.d" and return "a", "a.b", "a.b.c", and "a.b.c.d". @@ -456,22 +456,22 @@ end --@param name The name to take apart --@return An array of the sub names local function get_subnames(name) - local i = -1 - local list = {} + local i = -1 + local list = {} - repeat - local subname = name + repeat + local subname = name - i = string.find(name, "[.]", i + 1) - if(i ~= nil) then - subname = string.sub(name, 1, i - 1) - end + i = string.find(name, "[.]", i + 1) + if(i ~= nil) then + subname = string.sub(name, 1, i - 1) + end - list[#list + 1] = string.upper(subname) + list[#list + 1] = string.upper(subname) - until i == nil + until i == nil - return list + return list end --- Begins a SMB session over NetBIOS. This requires a NetBIOS Session Start message to @@ -495,103 +495,103 @@ end --@return (status, socket) if status is true, result is the port -- Otherwise, socket is the error message. function start_netbios(host, port, name) - local i - local status, err - local pos, result, flags, length - local socket = nmap.new_socket() + local i + local status, err + local pos, result, flags, length + local socket = nmap.new_socket() - -- First, populate the name array with all possible names, in order of significance - local names = {} + -- First, populate the name array with all possible names, in order of significance + local names = {} - -- Use the name parameter - if(name ~= nil) then - names[#names + 1] = name - end + -- Use the name parameter + if(name ~= nil) then + names[#names + 1] = name + end - -- Get the name of the server from NetBIOS - status, name = netbios.get_server_name(host.ip) - if(status == true) then - names[#names + 1] = name - end + -- Get the name of the server from NetBIOS + status, name = netbios.get_server_name(host.ip) + if(status == true) then + names[#names + 1] = name + end - -- "*SMBSERVER" is a special name that any server should respond to - names[#names + 1] = "*SMBSERVER" + -- "*SMBSERVER" is a special name that any server should respond to + names[#names + 1] = "*SMBSERVER" - -- If all else fails, use each substring of the DNS name (this is a HUGE hack, but is actually - -- a recommended way of doing this!) - if(host.name ~= nil and host.name ~= "") then - local new_names = get_subnames(host.name) - for i = 1, #new_names, 1 do - names[#names + 1] = new_names[i] - end - end + -- If all else fails, use each substring of the DNS name (this is a HUGE hack, but is actually + -- a recommended way of doing this!) + if(host.name ~= nil and host.name ~= "") then + local new_names = get_subnames(host.name) + for i = 1, #new_names, 1 do + names[#names + 1] = new_names[i] + end + end - -- This loop will try all the NetBIOS names we've collected, hoping one of them will work. Yes, - -- this is a hackish way, but it's actually the recommended way. - i = 1 - repeat + -- This loop will try all the NetBIOS names we've collected, hoping one of them will work. Yes, + -- this is a hackish way, but it's actually the recommended way. + i = 1 + repeat - -- Use the current name - name = names[i] + -- Use the current name + name = names[i] - -- Some debug information - stdnse.print_debug(1, "SMB: Trying to start NetBIOS session with name = '%s'", name) - -- Request a NetBIOS session - local session_request = bin.pack(">CCSzz", - 0x81, -- session request - 0x00, -- flags - 0x44, -- length - netbios.name_encode(name), -- server name - netbios.name_encode("NMAP") -- client name - ); + -- Some debug information + stdnse.print_debug(1, "SMB: Trying to start NetBIOS session with name = '%s'", name) + -- Request a NetBIOS session + local session_request = bin.pack(">CCSzz", + 0x81, -- session request + 0x00, -- flags + 0x44, -- length + netbios.name_encode(name), -- server name + netbios.name_encode("NMAP") -- client name + ); - stdnse.print_debug(3, "SMB: Connecting to %s", host.ip) - socket:set_timeout(TIMEOUT) - status, err = socket:connect(host, port, "tcp") - if(status == false) then - socket:close() - return false, "SMB: Failed to connect: " .. err - end + stdnse.print_debug(3, "SMB: Connecting to %s", host.ip) + socket:set_timeout(TIMEOUT) + status, err = socket:connect(host, port, "tcp") + if(status == false) then + socket:close() + return false, "SMB: Failed to connect: " .. err + end - -- Send the session request - stdnse.print_debug(3, "SMB: Sending NetBIOS session request with name %s", name) - status, err = socket:send(session_request) - if(status == false) then - socket:close() - return false, "SMB: Failed to send: " .. err - end - socket:set_timeout(TIMEOUT) + -- Send the session request + stdnse.print_debug(3, "SMB: Sending NetBIOS session request with name %s", name) + status, err = socket:send(session_request) + if(status == false) then + socket:close() + return false, "SMB: Failed to send: " .. err + end + socket:set_timeout(TIMEOUT) - -- Receive the session response - stdnse.print_debug(3, "SMB: Receiving NetBIOS session response") - status, result = socket:receive_buf(match.numbytes(4), true); - if(status == false) then - socket:close() - return false, "SMB: Failed to close socket: " .. result - end - pos, result, flags, length = bin.unpack(">CCS", result) - if(result == nil or length == nil) then - return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [1]" - end + -- Receive the session response + stdnse.print_debug(3, "SMB: Receiving NetBIOS session response") + status, result = socket:receive_buf(match.numbytes(4), true); + if(status == false) then + socket:close() + return false, "SMB: Failed to close socket: " .. result + end + pos, result, flags, length = bin.unpack(">CCS", result) + if(result == nil or length == nil) then + return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [1]" + end - -- Check for a positive session response (0x82) - if result == 0x82 then - stdnse.print_debug(3, "SMB: Successfully established NetBIOS session with server name %s", name) - return true, socket - end + -- Check for a positive session response (0x82) + if result == 0x82 then + stdnse.print_debug(3, "SMB: Successfully established NetBIOS session with server name %s", name) + return true, socket + end - -- If the session failed, close the socket and try the next name - stdnse.print_debug(1, "SMB: Session request failed, trying next name") - socket:close() + -- If the session failed, close the socket and try the next name + stdnse.print_debug(1, "SMB: Session request failed, trying next name") + socket:close() - -- Try the next name - i = i + 1 + -- Try the next name + i = i + 1 - until i > #names + until i > #names - -- We reached the end of our names list - stdnse.print_debug(1, "SMB: None of the NetBIOS names worked!") - return false, "SMB: Couldn't find a NetBIOS name that works for the server. Sorry!" + -- We reached the end of our names list + stdnse.print_debug(1, "SMB: None of the NetBIOS names worked!") + return false, "SMB: Couldn't find a NetBIOS name that works for the server. Sorry!" end --- Creates a string containing a SMB packet header. The header looks like this: @@ -629,52 +629,52 @@ end --@param overrides The overrides table. Keep in mind that overriding things like flags is generally a very bad idea, unless you know what you're doing. --@return A binary string containing the packed packet header. function smb_encode_header(smb, command, overrides) - -- Make sure we have an overrides array - overrides = overrides or {} + -- Make sure we have an overrides array + overrides = overrides or {} - -- Used for the header - local sig = string.char(0xFF) .. "SMB" + -- Used for the header + local sig = string.char(0xFF) .. "SMB" - -- Pretty much every flags is deprecated. We set these two because they're required to be on. - local flags = bit.bor(0x10, 0x08) -- SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES - -- These flags are less deprecated. We negotiate 32-bit status codes and long names. We also don't include Unicode, which tells - -- the server that we deal in ASCII. - local flags2 = bit.bor(0x4000, 0x2000, 0x0040, 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_EXECUTE_ONLY_READS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAMES + -- Pretty much every flags is deprecated. We set these two because they're required to be on. + local flags = bit.bor(0x10, 0x08) -- SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES + -- These flags are less deprecated. We negotiate 32-bit status codes and long names. We also don't include Unicode, which tells + -- the server that we deal in ASCII. + local flags2 = bit.bor(0x4000, 0x2000, 0x0040, 0x0001) -- SMB_FLAGS2_32BIT_STATUS | SMB_FLAGS2_EXECUTE_ONLY_READS | SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAMES - -- Unless the user's disabled the security signature, add it - if(nmap.registry.args.smbsign ~= "disable") then - flags2 = bit.bor(flags2, 0x0004) -- SMB_FLAGS2_SECURITY_SIGNATURE - end + -- Unless the user's disabled the security signature, add it + if(nmap.registry.args.smbsign ~= "disable") then + flags2 = bit.bor(flags2, 0x0004) -- SMB_FLAGS2_SECURITY_SIGNATURE + end - if(smb['extended_security'] == true) then - flags2 = bit.bor(flags2, 0x0800) -- SMB_EXTENDED_SECURITY - end + if(smb['extended_security'] == true) then + flags2 = bit.bor(flags2, 0x0800) -- SMB_EXTENDED_SECURITY + end - -- TreeID should never ever be 'nil', but it seems to happen once in awhile so print an error - if(smb['tid'] == nil) then - return false, string.format("SMB: ERROR: TreeID value was set to nil on host %s", smb['ip']) - end + -- TreeID should never ever be 'nil', but it seems to happen once in awhile so print an error + if(smb['tid'] == nil) then + return false, string.format("SMB: ERROR: TreeID value was set to nil on host %s", smb['ip']) + end - local header = bin.pack("message_sign, @@ -743,32 +743,32 @@ end --@param body The body of the packet that's being checked. --@return A true/false value -- true if the packet was signed properly, false if it wasn't. local function message_check_signature(smb, body) - smb['sequence'] = smb['sequence'] + 1 + smb['sequence'] = smb['sequence'] + 1 - if(smb['mac_key'] == nil) then - stdnse.print_debug(3, "SMB: Not signing message (missing mac_key)") - return true - elseif(nmap.registry.args.smbsign ~= "force" and bit.band(smb['security_mode'], 0x0A) ~= 0) then - stdnse.print_debug(3, "SMB: Not signing message (server doesn't support it -- default)") - return true - elseif(nmap.registry.args.smbsign == "disable" or nmap.registry.args.smbsign == "ignore") then - stdnse.print_debug(3, "SMB: Not signing message (disabled by user)") - return true - end + if(smb['mac_key'] == nil) then + stdnse.print_debug(3, "SMB: Not signing message (missing mac_key)") + return true + elseif(nmap.registry.args.smbsign ~= "force" and bit.band(smb['security_mode'], 0x0A) ~= 0) then + stdnse.print_debug(3, "SMB: Not signing message (server doesn't support it -- default)") + return true + elseif(nmap.registry.args.smbsign == "disable" or nmap.registry.args.smbsign == "ignore") then + stdnse.print_debug(3, "SMB: Not signing message (disabled by user)") + return true + end - -- Pull out the signature that they used - local signature = string.sub(body, 15, 22) + -- Pull out the signature that they used + local signature = string.sub(body, 15, 22) - -- Turn the sequence into a string - local sequence = bin.pack("III", netbios_data) - if(netbios_length == nil) then - return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [2]" - end - -- Make the length 24 bits - netbios_length = bit.band(netbios_length, 0x00FFFFFF) + -- The length of the packet is 4 bytes of big endian (for our purposes). + -- The NetBIOS header is 24 bits, big endian + pos, netbios_length = bin.unpack(">I", netbios_data) + if(netbios_length == nil) then + return false, "SMB: ERROR: Server returned less data than it was supposed to (one or more fields are missing); aborting [2]" + end + -- Make the length 24 bits + netbios_length = bit.band(netbios_length, 0x00FFFFFF) - -- The total length is the netbios_length, plus 4 (for the length itself) - length = netbios_length + 4 + -- The total length is the netbios_length, plus 4 (for the length itself) + length = netbios_length + 4 - local attempts = 5 - local smb_data - repeat - attempts = attempts - 1 - status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true) - until(status or (attempts == 0)) + local attempts = 5 + local smb_data + repeat + attempts = attempts - 1 + status, smb_data = smb['socket']:receive_buf(match.numbytes(netbios_length), true) + until(status or (attempts == 0)) - -- Make sure the connection is still alive - if(status ~= true) then - return false, "SMB: Failed to receive bytes after 5 attempts: " .. smb_data - end + -- Make sure the connection is still alive + if(status ~= true) then + return false, "SMB: Failed to receive bytes after 5 attempts: " .. smb_data + end - local result = netbios_data .. smb_data - if(#result ~= length) then - stdnse.print_debug(1, "SMB: ERROR: Received wrong number of bytes, there will likely be issues (recieved %d, expected %d)", #result, length) - return false, string.format("SMB: ERROR: Didn't receive the expected number of bytes; recieved %d, expected %d. This will almost certainly cause some errors.", #result, length) - end + local result = netbios_data .. smb_data + if(#result ~= length) then + stdnse.print_debug(1, "SMB: ERROR: Received wrong number of bytes, there will likely be issues (recieved %d, expected %d)", #result, length) + return false, string.format("SMB: ERROR: Didn't receive the expected number of bytes; recieved %d, expected %d. This will almost certainly cause some errors.", #result, length) + end - -- Check the message signature (ignoring the first four bytes, which are the netbios header) - local good_signature = message_check_signature(smb, string.sub(result, 5)) - if(good_signature == false) then - return false, "SMB: ERROR: Server returned invalid signature" - end + -- Check the message signature (ignoring the first four bytes, which are the netbios header) + local good_signature = message_check_signature(smb, string.sub(result, 5)) + if(good_signature == false) then + return false, "SMB: ERROR: Server returned invalid signature" + end - -- The header is 32 bytes. - pos, header = bin.unpack("SMB_COM_NEGOTIATE, which is typically the first SMB packet sent out. @@ -954,550 +954,550 @@ end -- * 'domain' The server's primary domain or workgroup -- * 'server' The server's name function negotiate_protocol(smb, overrides) - local header, parameters, data - local pos - local header1, header2, header3, ehader4, command, status, flags, flags2, pid_high, signature, unused, pid, mid + local header, parameters, data + local pos + local header1, header2, header3, ehader4, command, status, flags, flags2, pid_high, signature, unused, pid, mid - header = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'], overrides) + header = smb_encode_header(smb, command_codes['SMB_COM_NEGOTIATE'], overrides) - -- Make sure we have overrides - overrides = overrides or {} + -- Make sure we have overrides + overrides = overrides or {} - -- Parameters are blank - parameters = "" + -- Parameters are blank + parameters = "" - -- Data is a list of strings, terminated by a blank one. - if(overrides['dialects'] == nil) then - data = bin.pack(" 0 ) then - pos, smb['security_blob'] = bin.unpack(" 0 ) then + pos, smb['security_blob'] = bin.unpack(" 9) then - return false, "SMB: ERROR: Server has too many active connections; giving up." - end + if(busy_count > 9) then + return false, "SMB: ERROR: Server has too many active connections; giving up." + end - local backoff = math.random() * 10 - stdnse.print_debug(1, "SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100) - stdnse.sleep(backoff) - else - -- This username failed, print a warning and keep going - if(log_errors == nil or log_errors == true) then - stdnse.print_debug(1, "SMB: Login as %s\\%s failed (%s)", domain, stdnse.string_or_blank(username), get_status_name(status)) - end + local backoff = math.random() * 10 + stdnse.print_debug(1, "SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100) + stdnse.sleep(backoff) + else + -- This username failed, print a warning and keep going + if(log_errors == nil or log_errors == true) then + stdnse.print_debug(1, "SMB: Login as %s\\%s failed (%s)", domain, stdnse.string_or_blank(username), get_status_name(status)) + end - -- Go to the next account - if(overrides == nil or overrides['username'] == nil) then - smbauth.next_account(smb['host']) - result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host']) - else - result = false - end - end - end - end + -- Go to the next account + if(overrides == nil or overrides['username'] == nil) then + smbauth.next_account(smb['host']) + result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host']) + else + result = false + end + end + end + end - if(log_errors ~= false) then - stdnse.print_debug(1, "SMB: ERROR: %s", username) - end + if(log_errors ~= false) then + stdnse.print_debug(1, "SMB: ERROR: %s", username) + end - if (status ~= nil) then - return false, get_status_name(status) - else - return false, username - end + if (status ~= nil) then + return false, get_status_name(status) + else + return false, username + end end --- This is an internal function and should not be called externally. Use -- the start_session() function instead. local function start_session_extended(smb, log_errors, overrides) - local i - local status, status_name, result, err - local header, parameters, data - local pos - local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid - local andx_command, andx_reserved, andx_offset, action, security_blob_length - local os, lanmanager - local username, domain, password, password_hash, hash_type - local busy_count = 0 + local i + local status, status_name, result, err + local header, parameters, data + local pos + local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid + local andx_command, andx_reserved, andx_offset, action, security_blob_length + local os, lanmanager + local username, domain, password, password_hash, hash_type + local busy_count = 0 - -- Set a default status_name, in case everything fails - status_name = "An unknown error has occurred" + -- Set a default status_name, in case everything fails + status_name = "An unknown error has occurred" - -- Get the first account, unless they overrode it - if(overrides ~= nil and overrides['username'] ~= nil) then - result = true - username = overrides['username'] - domain = overrides['domain'] - password = overrides['password'] - password_hash = overrides['password_hash'] - hash_type = overrides['hash_type'] - else - result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host']) - if(not(result)) then - return result, username - end - end + -- Get the first account, unless they overrode it + if(overrides ~= nil and overrides['username'] ~= nil) then + result = true + username = overrides['username'] + domain = overrides['domain'] + password = overrides['password'] + password_hash = overrides['password_hash'] + hash_type = overrides['hash_type'] + else + result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host']) + if(not(result)) then + return result, username + end + end - -- check what kind of security blob we were given in the negotiate protocol request - local sp_nego = false - if ( smb['security_blob'] and #smb['security_blob'] > 11 ) then - local pos, oid = bin.unpack(">A6", smb['security_blob'], 5) - sp_nego = ( oid == "\x2b\x06\x01\x05\x05\x02" ) -- check for SPNEGO OID 1.3.6.1.5.5.2 - end + -- check what kind of security blob we were given in the negotiate protocol request + local sp_nego = false + if ( smb['security_blob'] and #smb['security_blob'] > 11 ) then + local pos, oid = bin.unpack(">A6", smb['security_blob'], 5) + sp_nego = ( oid == "\x2b\x06\x01\x05\x05\x02" ) -- check for SPNEGO OID 1.3.6.1.5.5.2 + end - while result ~= false do - -- These are loop variables - local security_blob = nil - local security_blob_length = 0 + while result ~= false do + -- These are loop variables + local security_blob = nil + local security_blob_length = 0 - -- This loop takes care of the multiple packets that "extended security" requires - repeat - -- Get the new security blob, passing the old security blob as a parameter. If there was no previous security blob, then nil is passed, which creates a new one - if ( not(security_blob) ) then - status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215)) + -- This loop takes care of the multiple packets that "extended security" requires + repeat + -- Get the new security blob, passing the old security blob as a parameter. If there was no previous security blob, then nil is passed, which creates a new one + if ( not(security_blob) ) then + status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215)) - if ( sp_nego ) then - local enc = asn1.ASN1Encoder:new() - local mechtype = enc:encode( { type = 'A0', value = enc:encode( { type = '30', value = enc:encode( { type = '06', value = bin.pack("H", "2b06010401823702020a") } ) } ) } ) - local oid = enc:encode( { type = '06', value = bin.pack("H", "2b0601050502") } ) + if ( sp_nego ) then + local enc = asn1.ASN1Encoder:new() + local mechtype = enc:encode( { type = 'A0', value = enc:encode( { type = '30', value = enc:encode( { type = '06', value = bin.pack("H", "2b06010401823702020a") } ) } ) } ) + local oid = enc:encode( { type = '06', value = bin.pack("H", "2b0601050502") } ) - security_blob = enc:encode(security_blob) - security_blob = enc:encode( { type = 'A2', value = security_blob } ) - security_blob = mechtype .. security_blob - security_blob = enc:encode( { type = '30', value = security_blob } ) - security_blob = enc:encode( { type = 'A0', value = security_blob } ) - security_blob = oid .. security_blob - security_blob = enc:encode( { type = '60', value = security_blob } ) - end - else - if ( sp_nego ) then - if ( smb['domain'] or smb['server'] and ( not(domain) or #domain == 0 ) ) then - domain = smb['domain'] or smb['server'] - end - hash_type = "ntlm" - end + security_blob = enc:encode(security_blob) + security_blob = enc:encode( { type = 'A2', value = security_blob } ) + security_blob = mechtype .. security_blob + security_blob = enc:encode( { type = '30', value = security_blob } ) + security_blob = enc:encode( { type = 'A0', value = security_blob } ) + security_blob = oid .. security_blob + security_blob = enc:encode( { type = '60', value = security_blob } ) + end + else + if ( sp_nego ) then + if ( smb['domain'] or smb['server'] and ( not(domain) or #domain == 0 ) ) then + domain = smb['domain'] or smb['server'] + end + hash_type = "ntlm" + end - status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215)) + status, security_blob, smb['mac_key'] = smbauth.get_security_blob(security_blob, smb['ip'], username, domain, password, password_hash, hash_type, (sp_nego and 0x00088215)) - if ( sp_nego ) then - local enc = asn1.ASN1Encoder:new() - security_blob = enc:encode(security_blob) - security_blob = enc:encode( { type = 'A2', value = security_blob } ) - security_blob = enc:encode( { type = '30', value = security_blob } ) - security_blob = enc:encode( { type = 'A1', value = security_blob } ) - end + if ( sp_nego ) then + local enc = asn1.ASN1Encoder:new() + security_blob = enc:encode(security_blob) + security_blob = enc:encode( { type = 'A2', value = security_blob } ) + security_blob = enc:encode( { type = '30', value = security_blob } ) + security_blob = enc:encode( { type = 'A1', value = security_blob } ) + end - end + end - -- There was an error processing the security blob - if(status == false) then - return false, string.format("SMB: ERROR: Security blob: %s", security_blob) - end + -- There was an error processing the security blob + if(status == false) then + return false, string.format("SMB: ERROR: Security blob: %s", security_blob) + end - header = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'], overrides) + header = smb_encode_header(smb, command_codes['SMB_COM_SESSION_SETUP_ANDX'], overrides) - -- Data is a list of strings, terminated by a blank one. - data = bin.pack(" 9) then - return false, "SMB: ERROR: Server has too many active connections; giving up." - end + if(busy_count > 9) then + return false, "SMB: ERROR: Server has too many active connections; giving up." + end - local backoff = math.random() * 10 - stdnse.print_debug(1, "SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100) - stdnse.sleep(backoff) - else - -- Display a message to the user, and try the next account - if(log_errors == nil or log_errors == true) then - stdnse.print_debug(1, "SMB: Extended login to %s as %s\\%s failed (%s)", smb['ip'], domain, stdnse.string_or_blank(username), status_name) - end + local backoff = math.random() * 10 + stdnse.print_debug(1, "SMB: Server has too many active connections; pausing for %s seconds.", math.floor(backoff * 100) / 100) + stdnse.sleep(backoff) + else + -- Display a message to the user, and try the next account + if(log_errors == nil or log_errors == true) then + stdnse.print_debug(1, "SMB: Extended login to %s as %s\\%s failed (%s)", smb['ip'], domain, stdnse.string_or_blank(username), status_name) + end - -- Go to the next account - if(overrides == nil or overrides['username'] == nil) then - smbauth.next_account(smb['host']) - result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host']) - if(not(result)) then - return false, username - end - else - result = false - end - end + -- Go to the next account + if(overrides == nil or overrides['username'] == nil) then + smbauth.next_account(smb['host']) + result, username, domain, password, password_hash, hash_type = smbauth.get_account(smb['host']) + if(not(result)) then + return false, username + end + else + result = false + end + end - -- Reset the user id - smb['uid'] = 0 + -- Reset the user id + smb['uid'] = 0 - end -- Loop over the accounts + end -- Loop over the accounts - if(log_errors == nil or log_errors == true) then - stdnse.print_debug(1, "SMB: ERROR: All logins failed, sorry it didn't work out!") - end + if(log_errors == nil or log_errors == true) then + stdnse.print_debug(1, "SMB: ERROR: All logins failed, sorry it didn't work out!") + end - return false, status_name + return false, status_name end --- Sends out SMB_COM_SESSION_SETUP_ANDX, which attempts to log a user in. @@ -1524,19 +1524,19 @@ end -- * 'os' The operating system -- * 'lanmanager' The servers's LAN Manager function start_session(smb, overrides, log_errors) - -- Use a mutex to avoid some issues (see http://seclists.org/nmap-dev/2011/q1/464) - local smb_auth_mutex = nmap.mutex( "SMB Authentication Mutex" ) - smb_auth_mutex( "lock" ) + -- Use a mutex to avoid some issues (see http://seclists.org/nmap-dev/2011/q1/464) + local smb_auth_mutex = nmap.mutex( "SMB Authentication Mutex" ) + smb_auth_mutex( "lock" ) - local status, result - if(smb['extended_security'] == true) then - status, result = start_session_extended(smb, log_errors, overrides) - else - status, result = start_session_basic(smb, log_errors, overrides) - end + local status, result + if(smb['extended_security'] == true) then + status, result = start_session_extended(smb, log_errors, overrides) + else + status, result = start_session_basic(smb, log_errors, overrides) + end - smb_auth_mutex( "done" ) - return status, result + smb_auth_mutex( "done" ) + return status, result end --- Sends out SMB_COM_SESSION_TREE_CONNECT_ANDX, which attempts to connect to a share. @@ -1555,59 +1555,59 @@ end -- table with the following elements: -- * 'tid' The TreeID for the session function tree_connect(smb, path, overrides) - local header, parameters, data, err, result - local pos - local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid - local andx_command, andx_reserved, andx_offset, action + local header, parameters, data, err, result + local pos + local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid + local andx_command, andx_reserved, andx_offset, action - -- Make sure we have overrides - overrides = overrides or {} + -- Make sure we have overrides + overrides = overrides or {} - header = smb_encode_header(smb, command_codes['SMB_COM_TREE_CONNECT_ANDX'], overrides) - parameters = bin.pack(" 10) then - return false, "SMB: ERROR: Server returned NT_STATUS_PIPE_NOT_AVAILABLE too many times; giving up." - end - stdnse.print_debug(1, "WARNING: Server refused connection with NT_STATUS_PIPE_NOT_AVAILABLE; trying again") - stdnse.sleep(.2) - end - until (status ~= 0xc00000ac) + -- Check if we were allowed in + local uid, tid + pos, header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, tid, pid, uid, mid = bin.unpack(" 10) then + return false, "SMB: ERROR: Server returned NT_STATUS_PIPE_NOT_AVAILABLE too many times; giving up." + end + stdnse.print_debug(1, "WARNING: Server refused connection with NT_STATUS_PIPE_NOT_AVAILABLE; trying again") + stdnse.sleep(.2) + end + until (status ~= 0xc00000ac) - if(status ~= 0) then - return false, get_status_name(status) - end + if(status ~= 0) then + return false, get_status_name(status) + end - -- Parse the parameters - pos, andx_command, andx_reserved, andx_offset, oplock_level, fid, create_action, created, last_access, last_write, last_change, attributes, allocation_size, end_of_file, filetype, ipc_state, is_directory = bin.unpack(" #data) then - return false, "SMB: Data returned runs off the end of the packet" - end + -- Make sure we don't run off the edge of the packet + if(data_offset + response['data_length'] > #data) then + return false, "SMB: Data returned runs off the end of the packet" + end - -- Pull the data string out of the data - response['data'] = string.sub(data, data_offset + 1, data_offset + response['data_length']) - end + -- Pull the data string out of the data + response['data'] = string.sub(data, data_offset + 1, data_offset + response['data_length']) + end - return true, response + return true, response end --- This sends a SMB request to write to a file (or a pipe). @@ -1916,68 +1916,68 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a table -- containing a lot of different elements, the most important one being 'fid', the handle to the opened file. function write_file(smb, write_data, offset, overrides) - overrides = overrides or {} - local header, parameters, data - local pos - local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid - local andx_command, andx_reserved, andx_offset - local response = {} + overrides = overrides or {} + local header, parameters, data + local pos + local header1, header2, header3, header4, command, status, flags, flags2, pid_high, signature, unused, pid, mid + local andx_command, andx_reserved, andx_offset + local response = {} - header = smb_encode_header(smb, command_codes['SMB_COM_WRITE_ANDX'], overrides) - parameters = bin.pack(" 0) do + local i = 0 + local data = handle:read(chunk) + while(data ~= nil and #data > 0) do - if(encoded) then - local new_data = "" - for j = 1, #data, 1 do - new_data = new_data .. string.char(bit.bxor(0xFF, string.byte(data, j))) - end - data = new_data - end + if(encoded) then + local new_data = "" + for j = 1, #data, 1 do + new_data = new_data .. string.char(bit.bxor(0xFF, string.byte(data, j))) + end + data = new_data + end - status, err = write_file(smbstate, data, i) - if(status == false) then - stop(smbstate) - return false, err - end + status, err = write_file(smbstate, data, i) + if(status == false) then + stop(smbstate) + return false, err + end - data = handle:read(chunk) - i = i + chunk - end + data = handle:read(chunk) + i = i + chunk + end - handle:close() - status, err = close_file(smbstate) - if(status == false) then - stop(smbstate) - return false, err - end + handle:close() + status, err = close_file(smbstate) + if(status == false) then + stop(smbstate) + return false, err + end - -- Stop the session - stop(smbstate) + -- Stop the session + stop(smbstate) - return true + return true end ---Write given data to the remote machine on the given share. This is similar to file_upload, except the @@ -2473,44 +2473,44 @@ end --@param use_anonymous [optional] If set to 'true', test is done by the anonymous user rather than the current user. --@return (status, err) If status is false, err is an error message. Otherwise, err is undefined. function file_write(host, data, share, remotefile, use_anonymous) - local status, err, smbstate - local chunk = 1024 - local overrides = nil + local status, err, smbstate + local chunk = 1024 + local overrides = nil - -- If anonymous is being used, create some overrides - if(use_anonymous) then - overrides = get_overrides_anonymous() - end + -- If anonymous is being used, create some overrides + if(use_anonymous) then + overrides = get_overrides_anonymous() + end - -- Create the SMB sessioan - status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides) + -- Create the SMB sessioan + status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides) - if(status == false) then - return false, smbstate - end + if(status == false) then + return false, smbstate + end - local i = 1 - while(i <= #data) do - local chunkdata = string.sub(data, i, i + chunk - 1) - status, err = write_file(smbstate, chunkdata, i - 1) - if(status == false) then - stop(smbstate) - return false, err - end + local i = 1 + while(i <= #data) do + local chunkdata = string.sub(data, i, i + chunk - 1) + status, err = write_file(smbstate, chunkdata, i - 1) + if(status == false) then + stop(smbstate) + return false, err + end - i = i + chunk - end + i = i + chunk + end - status, err = close_file(smbstate) - if(status == false) then - stop(smbstate) - return false, err - end + status, err = close_file(smbstate) + if(status == false) then + stop(smbstate) + return false, err + end - -- Stop the session - stop(smbstate) + -- Stop the session + stop(smbstate) - return true + return true end ---Write given data to the remote machine on the given share. This is similar to file_upload, except the @@ -2523,51 +2523,51 @@ end --@param overrides [optional] Override various fields in the SMB packets. --@return (status, err) If status is false, err is an error message. Otherwise, err is undefined. function file_read(host, share, remotefile, use_anonymous, overrides) - local status, err, smbstate - local result - local chunk = 1024 - local read = "" + local status, err, smbstate + local result + local chunk = 1024 + local read = "" - -- Make sure we got overrides - overrides = overrides or {} + -- Make sure we got overrides + overrides = overrides or {} - -- If anonymous is being used, create some overrides - if(use_anonymous) then - overrides = get_overrides_anonymous(overrides) - end + -- If anonymous is being used, create some overrides + if(use_anonymous) then + overrides = get_overrides_anonymous(overrides) + end - -- Create the SMB sessioan - status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides) + -- Create the SMB sessioan + status, smbstate = start_ex(host, true, true, share, remotefile, nil, overrides) - if(status == false) then - return false, smbstate - end + if(status == false) then + return false, smbstate + end - local i = 1 - while true do - status, result = read_file(smbstate, i - 1, chunk) - if(status == false) then - stop(smbstate) - return false, result - end + local i = 1 + while true do + status, result = read_file(smbstate, i - 1, chunk) + if(status == false) then + stop(smbstate) + return false, result + end - if(result['data_length'] == 0) then - break - end + if(result['data_length'] == 0) then + break + end - read = read .. result['data'] - i = i + chunk - end + read = read .. result['data'] + i = i + chunk + end - status, err = close_file(smbstate) - if(status == false) then - stop(smbstate) - return false, err - end + status, err = close_file(smbstate) + if(status == false) then + stop(smbstate) + return false, err + end - -- Stop the session - stop(smbstate) - return true, read + -- Stop the session + stop(smbstate) + return true, read end ---Check how many files, in a given list, exist on the given share. @@ -2580,48 +2580,48 @@ end --@return count: The number of files that existed, or an error message if status is 'false' --@return files: A list of the files that existed. function files_exist(host, share, files, overrides) - local status, smbstate, result, err + local status, smbstate, result, err - -- Make sure we got overrides - overrides = overrides or {} + -- Make sure we got overrides + overrides = overrides or {} - -- We don't wan to be creating the files - overrides['file_create_disposition'] = 1 + -- We don't wan to be creating the files + overrides['file_create_disposition'] = 1 - -- Create the SMB sessioan - status, smbstate = start_ex(host, true, true, share, nil, nil, overrides) + -- Create the SMB sessioan + status, smbstate = start_ex(host, true, true, share, nil, nil, overrides) - if(status == false) then - return false, smbstate - end + if(status == false) then + return false, smbstate + end - local exist = 0 - local list = {} + local exist = 0 + local list = {} - for _, file in ipairs(files) do - -- Try and open the file - status, result = create_file(smbstate, file, overrides) + for _, file in ipairs(files) do + -- Try and open the file + status, result = create_file(smbstate, file, overrides) - -- If there was an error other than 'file already exists', return an error - if(not(status) and result ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then - return false, result - end + -- If there was an error other than 'file already exists', return an error + if(not(status) and result ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then + return false, result + end - -- If the file existed, count it and close it - if(status) then - exist = exist + 1 - table.insert(list, file) - status, err = close_file(smbstate) - if(status == false) then - stop(smbstate) - return false, err - end - end - end + -- If the file existed, count it and close it + if(status) then + exist = exist + 1 + table.insert(list, file) + status, err = close_file(smbstate) + if(status == false) then + stop(smbstate) + return false, err + end + end + end - -- Stop the session - stop(smbstate) - return true, exist, list + -- Stop the session + stop(smbstate) + return true, exist, list end ---Delete a file from the remote machine @@ -2631,35 +2631,35 @@ end --@param remotefile The remote file on the machine. It is relative to the share's root. It can be a string, or an array. --@return (status, err) If status is false, err is an error message. Otherwise, err is undefined. function file_delete(host, share, remotefile) - local status, smbstate, err + local status, smbstate, err - -- Create the SMB session - status, smbstate = start_ex(host, true, true, share) - if(status == false) then - return false, smbstate - end + -- Create the SMB session + status, smbstate = start_ex(host, true, true, share) + if(status == false) then + return false, smbstate + end - -- Make sure the remotefile is always a table, to save on duplicate code - if(type(remotefile) ~= "table") then - remotefile = {remotefile} - end + -- Make sure the remotefile is always a table, to save on duplicate code + if(type(remotefile) ~= "table") then + remotefile = {remotefile} + end - for _, file in ipairs(remotefile) do - status, err = delete_file(smbstate, file) - if(status == false) then - stdnse.print_debug(1, "SMB: Couldn't delete %s\\%s: %s", share, file, err) - if(err ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then - stop(smbstate) - return false, err - end - end - end + for _, file in ipairs(remotefile) do + status, err = delete_file(smbstate, file) + if(status == false) then + stdnse.print_debug(1, "SMB: Couldn't delete %s\\%s: %s", share, file, err) + if(err ~= 'NT_STATUS_OBJECT_NAME_NOT_FOUND') then + stop(smbstate) + return false, err + end + end + end - -- Stop the session - stop(smbstate) + -- Stop the session + stop(smbstate) - return true + return true end --- @@ -2677,129 +2677,129 @@ end -- archive - find archived files -- @return iterator function retreiving the next result function find_files(smbstate, fname, options) - local TRANS2_FIND_FIRST2, TRANS2_FIND_NEXT2 = 1, 2 - options = options or {} + local TRANS2_FIND_FIRST2, TRANS2_FIND_NEXT2 = 1, 2 + options = options or {} - if (not(options.srch_attrs)) then - options.srch_attrs = { ro = true, hidden = true, system = true, dir = true} - end + if (not(options.srch_attrs)) then + options.srch_attrs = { ro = true, hidden = true, system = true, dir = true} + end - local nattrs = ( options.srch_attrs.ro and 1 or 0 ) + ( options.srch_attrs.hidden and 2 or 0 ) + - ( options.srch_attrs.hidden and 2 or 0 ) + ( options.srch_attrs.system and 4 or 0 ) + - ( options.srch_attrs.volid and 8 or 0 ) + ( options.srch_attrs.dir and 16 or 0 ) + - ( options.srch_attrs.archive and 32 or 0 ) + local nattrs = ( options.srch_attrs.ro and 1 or 0 ) + ( options.srch_attrs.hidden and 2 or 0 ) + + ( options.srch_attrs.hidden and 2 or 0 ) + ( options.srch_attrs.system and 4 or 0 ) + + ( options.srch_attrs.volid and 8 or 0 ) + ( options.srch_attrs.dir and 16 or 0 ) + + ( options.srch_attrs.archive and 32 or 0 ) - if ( not(fname) ) then - fname = '\\*' - elseif( fname:sub(1,1) ~= '\\' ) then - fname = '\\' .. fname - end + if ( not(fname) ) then + fname = '\\*' + elseif( fname:sub(1,1) ~= '\\' ) then + fname = '\\' .. fname + end - fname = fname .. '\0' + fname = fname .. '\0' - -- Sends the request and takes care of short/fragmented responses - local function send_and_receive_find_request(smbstate, trans_type, function_parameters) + -- Sends the request and takes care of short/fragmented responses + local function send_and_receive_find_request(smbstate, trans_type, function_parameters) - local status, err = send_transaction2(smbstate, trans_type, function_parameters, "") - if ( not(status) ) then - return false, "Failed to send data to server: send_transaction2" - end + local status, err = send_transaction2(smbstate, trans_type, function_parameters, "") + if ( not(status) ) then + return false, "Failed to send data to server: send_transaction2" + end - local status, response = receive_transaction2(smbstate) - if ( not(status) ) then - return false, "Failed to receive data from server: receive_transaction2" - end + local status, response = receive_transaction2(smbstate) + if ( not(status) ) then + return false, "Failed to receive data from server: receive_transaction2" + end - local pos = ( TRANS2_FIND_FIRST2 == trans_type and 9 or 7 ) - local last_name_offset = select(2, bin.unpack(" ( #response.data - NE_UP_TO_FNAME_SIZE ) ) do - local status, tmp = receive_transaction2(smbstate) - if ( not(status) ) then - return false, "Failed to receive data from receive_transaction2" - end - response.data = response.data .. tmp.data - end + -- check if we need more packets to reassemble this transaction + local NE_UP_TO_FNAME_SIZE = 94 + while ( last_name_offset > ( #response.data - NE_UP_TO_FNAME_SIZE ) ) do + local status, tmp = receive_transaction2(smbstate) + if ( not(status) ) then + return false, "Failed to receive data from receive_transaction2" + end + response.data = response.data .. tmp.data + end - return true, response - end + return true, response + end - local srch_count = 173 -- picked up by wireshark - local flags = 6 -- Return RESUME keys, close search if END OF SEARCH is reached - local loi = 260 -- Level of interest, return SMB_FIND_FILE_BOTH_DIRECTORY_INFO - local storage_type = 0 -- despite the documentation of having to be either 0x01 or 0x40, wireshark reports 0 + local srch_count = 173 -- picked up by wireshark + local flags = 6 -- Return RESUME keys, close search if END OF SEARCH is reached + local loi = 260 -- Level of interest, return SMB_FIND_FILE_BOTH_DIRECTORY_INFO + local storage_type = 0 -- despite the documentation of having to be either 0x01 or 0x40, wireshark reports 0 - local function_parameters = bin.pack(" 0 ) then - for i=1, ( 4-pad ) do - function_parameters = function_parameters .. "\0" - end - end + -- SMB header: 32 + -- trans2 header: 36 + -- FIND_FIRST2 parameters: #function_parameters + local pad = ( 32 + 36 + #function_parameters ) % 4 + if ( pad > 0 ) then + for i=1, ( 4-pad ) do + function_parameters = function_parameters .. "\0" + end + end - local function next_item() + local function next_item() - local status, response = send_and_receive_find_request(smbstate, TRANS2_FIND_FIRST2, function_parameters) - if ( not(status) ) then - return - end + local status, response = send_and_receive_find_request(smbstate, TRANS2_FIND_FIRST2, function_parameters) + if ( not(status) ) then + return + end - local srch_id = select(2, bin.unpack("S", response.parameters, 3)) ~= 0 ) - end + -- check whether END-OF-SEARCH was set + stop_loop = ( select(2, bin.unpack(">S", response.parameters, 3)) ~= 0 ) + end - -- parse response, based on LOI == 260 - repeat - local fe, last_pos, ne, f_len, ea_len, sf_len, _ = {}, pos + -- parse response, based on LOI == 260 + repeat + local fe, last_pos, ne, f_len, ea_len, sf_len, _ = {}, pos - pos, ne, fe.fi, fe.created, fe.accessed, fe.write, fe.change, - fe.eof, fe.alloc_size, fe.attrs, f_len, ea_len, sf_len, _ = bin.unpack("share_host_returns_proper_error @@ -2886,49 +2886,49 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value: -- true if anonymous access is permitted, false otherwise. function share_anonymous_can_read(host, share) - local status, smbstate, err - local overrides = get_overrides_anonymous() + local status, smbstate, err + local overrides = get_overrides_anonymous() - -- Begin the SMB session - status, smbstate = start(host) - if(status == false) then - return false, smbstate - end + -- Begin the SMB session + status, smbstate = start(host) + if(status == false) then + return false, smbstate + end - -- Negotiate the protocol - status, err = negotiate_protocol(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + -- Negotiate the protocol + status, err = negotiate_protocol(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - -- Start up a null session - status, err = start_session(smbstate, overrides) + -- Start up a null session + status, err = start_session(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + if(status == false) then + stop(smbstate) + return false, err + end - -- Attempt a connection to the share - status, err = tree_connect(smbstate, share, overrides) - if(status == false) then + -- Attempt a connection to the share + status, err = tree_connect(smbstate, share, overrides) + if(status == false) then - -- Stop the session - stop(smbstate) + -- Stop the session + stop(smbstate) - -- ACCESS_DENIED is the expected error: it tells us that the connection failed - if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then - return true, false - else - return false, err - end - end + -- ACCESS_DENIED is the expected error: it tells us that the connection failed + if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then + return true, false + else + return false, err + end + end - stop(smbstate) - return true, true + stop(smbstate) + return true, true end ---Check whether or not a share is accessible by the current user. Assumes that share_host_returns_proper_error @@ -2939,46 +2939,46 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value: -- true if anonymous access is permitted, false otherwise. function share_user_can_read(host, share) - local status, smbstate, err - local overrides = {} + local status, smbstate, err + local overrides = {} - -- Begin the SMB session - status, smbstate = start(host) - if(status == false) then - return false, smbstate - end + -- Begin the SMB session + status, smbstate = start(host) + if(status == false) then + return false, smbstate + end - -- Negotiate the protocol - status, err = negotiate_protocol(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + -- Negotiate the protocol + status, err = negotiate_protocol(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - -- Start up a null session - status, err = start_session(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + -- Start up a null session + status, err = start_session(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - -- Attempt a connection to the share - status, err = tree_connect(smbstate, share, overrides) - if(status == false) then + -- Attempt a connection to the share + status, err = tree_connect(smbstate, share, overrides) + if(status == false) then - -- Stop the session - stop(smbstate) + -- Stop the session + stop(smbstate) - -- ACCESS_DENIED is the expected error: it tells us that the connection failed - if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then - return true, false - else - return false, err - end - end + -- ACCESS_DENIED is the expected error: it tells us that the connection failed + if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then + return true, false + else + return false, err + end + end - stop(smbstate) - return true, true + stop(smbstate) + return true, true end ---Determine whether or not a host will accept any share name (I've seen this on certain systems; it's @@ -2989,55 +2989,55 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value: -- true if the file was successfully written, false if it was not. function share_host_returns_proper_error(host, use_anonymous) - local status, smbstate, err - local share = "nmap-share-test" - local overrides + local status, smbstate, err + local share = "nmap-share-test" + local overrides - if ( use_anonymous ) then - overrides = get_overrides_anonymous() - end + if ( use_anonymous ) then + overrides = get_overrides_anonymous() + end - -- Begin the SMB session - status, smbstate = start(host) - if(status == false) then - return false, smbstate - end + -- Begin the SMB session + status, smbstate = start(host) + if(status == false) then + return false, smbstate + end - -- Negotiate the protocol - status, err = negotiate_protocol(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + -- Negotiate the protocol + status, err = negotiate_protocol(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - -- Start up a null session - status, err = start_session(smbstate, overrides) - if(status == false) then - stop(smbstate) - return false, err - end + -- Start up a null session + status, err = start_session(smbstate, overrides) + if(status == false) then + stop(smbstate) + return false, err + end - -- Connect to the share - stdnse.print_debug(1, "SMB: Trying a random share to see if server responds properly: %s", share) - status, err = tree_connect(smbstate, share, overrides) + -- Connect to the share + stdnse.print_debug(1, "SMB: Trying a random share to see if server responds properly: %s", share) + status, err = tree_connect(smbstate, share, overrides) - if(status == false) then - -- If the error is NT_STATUS_ACCESS_DENIED (0xc0000022), that's bad -- we don't want non-existent shares - -- showing up as 'access denied'. Any other error is ok. - if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then - stdnse.print_debug(1, "SMB: Server doesn't return proper value for non-existent shares (returns ACCESS_DENIED)") - stop(smbstate) - return true, false - end - else - -- If we were actually able to connect to this share, then there's probably a serious issue - stdnse.print_debug(1, "SMB: Server doesn't return proper value for non-existent shares (accepts the connection)") - stop(smbstate) - return true, false - end + if(status == false) then + -- If the error is NT_STATUS_ACCESS_DENIED (0xc0000022), that's bad -- we don't want non-existent shares + -- showing up as 'access denied'. Any other error is ok. + if(err == 0xc0000022 or err == 'NT_STATUS_ACCESS_DENIED') then + stdnse.print_debug(1, "SMB: Server doesn't return proper value for non-existent shares (returns ACCESS_DENIED)") + stop(smbstate) + return true, false + end + else + -- If we were actually able to connect to this share, then there's probably a serious issue + stdnse.print_debug(1, "SMB: Server doesn't return proper value for non-existent shares (accepts the connection)") + stop(smbstate) + return true, false + end - stop(smbstate) - return true, true + stop(smbstate) + return true, true end ---Get all the details we can about the share. These details are stored in a table and returned. @@ -3047,64 +3047,64 @@ end --@return (status, result) If status is false, result is an error message. Otherwise, result is a boolean value: -- true if the file was successfully written, false if it was not. function share_get_details(host, share) - local msrpc = require "msrpc" -- avoid require cycle - local smbstate, status, result - local i - local details = {} + local msrpc = require "msrpc" -- avoid require cycle + local smbstate, status, result + local i + local details = {} - -- Save the name - details['name'] = share + -- Save the name + details['name'] = share - -- Check if the current user can read the share - stdnse.print_debug(1, "SMB: Checking if share %s can be read by the current user", share) - status, result = share_user_can_read(host, share) - if(status == false) then - return false, result - end - details['user_can_read'] = result + -- Check if the current user can read the share + stdnse.print_debug(1, "SMB: Checking if share %s can be read by the current user", share) + status, result = share_user_can_read(host, share) + if(status == false) then + return false, result + end + details['user_can_read'] = result - -- Check if the anonymous reader can read the share - stdnse.print_debug(1, "SMB: Checking if share %s can be read by the anonymous user", share) - status, result = share_anonymous_can_read(host, share) - if(status == true) then - details['anonymous_can_read'] = result - end + -- Check if the anonymous reader can read the share + stdnse.print_debug(1, "SMB: Checking if share %s can be read by the anonymous user", share) + status, result = share_anonymous_can_read(host, share) + if(status == true) then + details['anonymous_can_read'] = result + end - -- Check if the current user can write to the share - stdnse.print_debug(1, "SMB: Checking if share %s can be written by the current user", share) - status, result = share_user_can_write(host, share) - if(status == false) then - if(result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then - details['user_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND" - else - return false, result - end - end - details['user_can_write'] = result + -- Check if the current user can write to the share + stdnse.print_debug(1, "SMB: Checking if share %s can be written by the current user", share) + status, result = share_user_can_write(host, share) + if(status == false) then + if(result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then + details['user_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND" + else + return false, result + end + end + details['user_can_write'] = result - -- Check if the anonymous user can write to the share - stdnse.print_debug(1, "SMB: Checking if share %s can be written by the anonymous user", share) - status, result = share_anonymous_can_write(host, share) - if(status == false and result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then - details['anonymous_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND" - elseif( status == true ) then - details['anonymous_can_write'] = result - end + -- Check if the anonymous user can write to the share + stdnse.print_debug(1, "SMB: Checking if share %s can be written by the anonymous user", share) + status, result = share_anonymous_can_write(host, share) + if(status == false and result == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then + details['anonymous_can_write'] = "NT_STATUS_OBJECT_NAME_NOT_FOUND" + elseif( status == true ) then + details['anonymous_can_write'] = result + end - -- Try and get full details about the share - status, result = msrpc.get_share_info(host, share) - if(status == false) then - -- We don't stop for this error (it's pretty common since administive privileges are required here) - stdnse.print_debug(1, "SMB: Failed to get share info for %s: %s", share, result) - details['details'] = result - else - -- Process the result a bit - result = result['info'] - if(result['max_users'] == 0xFFFFFFFF) then - result['max_users'] = "" - end - details['details'] = result - end + -- Try and get full details about the share + status, result = msrpc.get_share_info(host, share) + if(status == false) then + -- We don't stop for this error (it's pretty common since administive privileges are required here) + stdnse.print_debug(1, "SMB: Failed to get share info for %s: %s", share, result) + details['details'] = result + else + -- Process the result a bit + result = result['info'] + if(result['max_users'] == 0xFFFFFFFF) then + result['max_users'] = "" + end + details['details'] = result + end return true, details end @@ -3120,74 +3120,74 @@ end --@return (status, result, extra) If status is false, result is an error message. Otherwise, result is an array of shares with as much -- detail as we could get. If extra isn't nil, it is set to extra information that should be displayed (such as a warning). function share_get_list(host) - local msrpc = require "msrpc" -- avoid require cycle - local status, result - local enum_status - local extra = "" - local shares = {} - local share_details = {} + local msrpc = require "msrpc" -- avoid require cycle + local status, result + local enum_status + local extra = "" + local shares = {} + local share_details = {} - -- Try and do this the good way, make a MSRPC call to get the shares - stdnse.print_debug(1, "SMB: Attempting to log into the system to enumerate shares") - enum_status, shares = msrpc.enum_shares(host) + -- Try and do this the good way, make a MSRPC call to get the shares + stdnse.print_debug(1, "SMB: Attempting to log into the system to enumerate shares") + enum_status, shares = msrpc.enum_shares(host) - -- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the - -- best we can do. - if(enum_status == false) then - stdnse.print_debug(1, "SMB: Enumerating shares failed, guessing at common ones (%s)", shares) - extra = string.format("ERROR: Enumerating shares failed, guessing at common ones (%s)", shares) + -- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the + -- best we can do. + if(enum_status == false) then + stdnse.print_debug(1, "SMB: Enumerating shares failed, guessing at common ones (%s)", shares) + extra = string.format("ERROR: Enumerating shares failed, guessing at common ones (%s)", shares) - -- Take some common share names I've seen (thanks to Brandon Enright for most of these, except the last few) - shares = {"ADMIN", "BACKUP", "DATA", "DESKTOP", "DOCS", "FILES", "GROUPS", "HD", "HOME", "INFO", "IPC", "MEDIA", "MY DOCUMENTS", "NETLOGON", "PICTURES", "PORN", "PR0N", "PRINT", "PROGRAMS", "PRON", "PUBLIC", "SHARE", "SHARED", "SOFTWARE", "STMP", "TEMP", "TEST", "TMP", "USERS", "WEB DOCUMENTS","WEBSERVER", "WWW", "XSERVE" } + -- Take some common share names I've seen (thanks to Brandon Enright for most of these, except the last few) + shares = {"ADMIN", "BACKUP", "DATA", "DESKTOP", "DOCS", "FILES", "GROUPS", "HD", "HOME", "INFO", "IPC", "MEDIA", "MY DOCUMENTS", "NETLOGON", "PICTURES", "PORN", "PR0N", "PRINT", "PROGRAMS", "PRON", "PUBLIC", "SHARE", "SHARED", "SOFTWARE", "STMP", "TEMP", "TEST", "TMP", "USERS", "WEB DOCUMENTS","WEBSERVER", "WWW", "XSERVE" } - -- Try every alphabetic share - for i = string.byte("A", 1), string.byte("Z", 1), 1 do - shares[#shares + 1] = string.char(i) - end + -- Try every alphabetic share + for i = string.byte("A", 1), string.byte("Z", 1), 1 do + shares[#shares + 1] = string.char(i) + end - -- For each share, add one with the same name and a trailing '$' - local sharesLength = #shares - for shareItr = 1, sharesLength, 1 do - shares[ sharesLength + shareItr ] = shares[ shareItr ] .. '$' - end - else - stdnse.print_debug(1, "SMB: Found %d shares, will attempt to find more information", #shares) - end + -- For each share, add one with the same name and a trailing '$' + local sharesLength = #shares + for shareItr = 1, sharesLength, 1 do + shares[ sharesLength + shareItr ] = shares[ shareItr ] .. '$' + end + else + stdnse.print_debug(1, "SMB: Found %d shares, will attempt to find more information", #shares) + end - -- Sort the shares - table.sort(shares) + -- Sort the shares + table.sort(shares) - -- Ensure that the server returns the proper error message - -- first try anonymously, then using a user account (in case anonymous connections are not supported) - for _, anon in ipairs({true, false}) do - status, result = share_host_returns_proper_error(host) + -- Ensure that the server returns the proper error message + -- first try anonymously, then using a user account (in case anonymous connections are not supported) + for _, anon in ipairs({true, false}) do + status, result = share_host_returns_proper_error(host) - if(status == true and result == false) then - return false, "Server doesn't return proper value for non-existent shares; can't enumerate shares" - end - end + if(status == true and result == false) then + return false, "Server doesn't return proper value for non-existent shares; can't enumerate shares" + end + end - if(status == false) then - return false, result - end + if(status == false) then + return false, result + end - -- Get more information on each share - for i = 1, #shares, 1 do - local status, result - stdnse.print_debug(1, "SMB: Getting information for share: %s", shares[i]) - status, result = share_get_details(host, shares[i]) - if(status == false and result == 'NT_STATUS_BAD_NETWORK_NAME') then - stdnse.print_debug(1, "SMB: Share doesn't exist: %s", shares[i]) - elseif(status == false) then - stdnse.print_debug(1, "SMB: Error while getting share details: %s", result) - return false, result - else - -- Save the share details - table.insert(share_details, result) - end - end + -- Get more information on each share + for i = 1, #shares, 1 do + local status, result + stdnse.print_debug(1, "SMB: Getting information for share: %s", shares[i]) + status, result = share_get_details(host, shares[i]) + if(status == false and result == 'NT_STATUS_BAD_NETWORK_NAME') then + stdnse.print_debug(1, "SMB: Share doesn't exist: %s", shares[i]) + elseif(status == false) then + stdnse.print_debug(1, "SMB: Error while getting share details: %s", result) + return false, result + else + -- Save the share details + table.insert(share_details, result) + end + end - return true, share_details, extra + return true, share_details, extra end ---Find a share that the current user can write to. Return it, along with its path. If no share could be found, @@ -3197,36 +3197,36 @@ end --@return (status, name, path, names) If status is false, result is an error message. Otherwise, name is the name of the share, -- path is its path, if it could be determined, and names is a list of all writable shares. function share_find_writable(host) - local i - local status, shares - local main_name, main_path - local names = {} - local writable = {} + local i + local status, shares + local main_name, main_path + local names = {} + local writable = {} - status, shares = share_get_list(host) - if(status == false) then - return false, shares - end + status, shares = share_get_list(host) + if(status == false) then + return false, shares + end - for i = 1, #shares, 1 do - if(shares[i]['user_can_write'] == true) then - if(main_name == nil) then - main_name = shares[i]['name'] + for i = 1, #shares, 1 do + if(shares[i]['user_can_write'] == true) then + if(main_name == nil) then + main_name = shares[i]['name'] - if(shares[i]['details'] ~= nil) then - main_path = shares[i]['details']['path'] - end - end + if(shares[i]['details'] ~= nil) then + main_path = shares[i]['details']['path'] + end + end - table.insert(names, shares[i]['name']) - end - end + table.insert(names, shares[i]['name']) + end + end - if(main_name == nil) then - return false, "Couldn't find a writable share!" - else - return true, main_name, main_path, names - end + if(main_name == nil) then + return false, "Couldn't find a writable share!" + else + return true, main_name, main_path, names + end end --- Converts numbered Windows version strings ("Windows 5.0", "Windows 5.1") to names ("Windows 2000", "Windows XP"). @@ -3234,13 +3234,13 @@ end --@return The actual name of the OS (or the same as the os parameter if no match was found). function get_windows_version(os) - if(os == "Windows 5.0") then - return "Windows 2000" - elseif(os == "Windows 5.1")then - return "Windows XP" - end + if(os == "Windows 5.0") then + return "Windows 2000" + elseif(os == "Windows 5.1")then + return "Windows XP" + end - return os + return os end @@ -3265,61 +3265,61 @@ end --@param host The host object --@return (status, data) If status is true, data is a table of values; otherwise, data is an error message. function get_os(host) - local state - local status, smbstate + local state + local status, smbstate - local response = {} + local response = {} - -- Start up SMB - status, smbstate = start_ex(host, true, true, nil, nil, true) - if(status == false) then - return false, smbstate + -- Start up SMB + status, smbstate = start_ex(host, true, true, nil, nil, true) + if(status == false) then + return false, smbstate + end + + -- See if we actually got something + if(smbstate['os'] == nil and smbstate['lanmanager'] == nil) then + return false, "Server didn't return OS details" + end + + response['os'] = smbstate['os'] + response['lanmanager'] = smbstate['lanmanager'] + response['domain'] = smbstate['domain'] + response['server'] = smbstate['server'] + response['date'] = smbstate['date'] + response['time'] = smbstate['time'] + response['timezone_str'] = smbstate['timezone_str'] + response['timezone'] = smbstate['timezone'] + + -- Kill SMB + stop(smbstate) + + + -- Start another session with extended security. This will allow us to get + -- additional information about the target. + status, smbstate = start_ex(host, true, true, nil, nil, false) + if(status == true) then + -- See if we actually got something + if (smbstate['fqdn'] or smbstate['domain_dns'] or smbstate['forest_dns']) then + response['fqdn'] = smbstate['fqdn'] + response['domain_dns'] = smbstate['domain_dns'] + response['forest_dns'] = smbstate['forest_dns'] + -- After a non-extended security negotiation, smbstate['domain'] will + -- contain the NetBIOS domain name, or the workgroup name. However, + -- after an extended-security session setup, smbstate['domain'] will + -- contain the NetBIOS domain name. For hosts in a workgroup, Windows + -- uses the NetBIOS hostname as the NetBIOS domain name. Comparing the + -- two will reveal whether the target is in a domain or a workgroup. + if ( smbstate['domain'] ~= nil and response['domain'] ~= smbstate['domain'] ) then + response['workgroup'] = response['domain'] + response['domain'] = nil + end end - -- See if we actually got something - if(smbstate['os'] == nil and smbstate['lanmanager'] == nil) then - return false, "Server didn't return OS details" - end - - response['os'] = smbstate['os'] - response['lanmanager'] = smbstate['lanmanager'] - response['domain'] = smbstate['domain'] - response['server'] = smbstate['server'] - response['date'] = smbstate['date'] - response['time'] = smbstate['time'] - response['timezone_str'] = smbstate['timezone_str'] - response['timezone'] = smbstate['timezone'] - - -- Kill SMB + -- Kill SMB again stop(smbstate) + end - - -- Start another session with extended security. This will allow us to get - -- additional information about the target. - status, smbstate = start_ex(host, true, true, nil, nil, false) - if(status == true) then - -- See if we actually got something - if (smbstate['fqdn'] or smbstate['domain_dns'] or smbstate['forest_dns']) then - response['fqdn'] = smbstate['fqdn'] - response['domain_dns'] = smbstate['domain_dns'] - response['forest_dns'] = smbstate['forest_dns'] - -- After a non-extended security negotiation, smbstate['domain'] will - -- contain the NetBIOS domain name, or the workgroup name. However, - -- after an extended-security session setup, smbstate['domain'] will - -- contain the NetBIOS domain name. For hosts in a workgroup, Windows - -- uses the NetBIOS hostname as the NetBIOS domain name. Comparing the - -- two will reveal whether the target is in a domain or a workgroup. - if ( smbstate['domain'] ~= nil and response['domain'] ~= smbstate['domain'] ) then - response['workgroup'] = response['domain'] - response['domain'] = nil - end - end - - -- Kill SMB again - stop(smbstate) - end - - return true, response + return true, response end ---Basically a wrapper around socket:get_info, except that it also makes a SMB connection before calling the @@ -3333,31 +3333,31 @@ end --@return The report port. --@return The mac address, if possible; nil otherwise. function get_socket_info(host) - local status, lhost, lport, rhost, rport - local smbstate, socket + local status, lhost, lport, rhost, rport + local smbstate, socket - -- Start SMB (we need a socket to get the proper local ip - status, smbstate = start_ex(host) - if(status == false) then - return false, smbstate - end + -- Start SMB (we need a socket to get the proper local ip + status, smbstate = start_ex(host) + if(status == false) then + return false, smbstate + end - socket = smbstate['socket'] - status, lhost, lport, rhost, rport = socket:get_info() - if(status == false) then - return false, lhost - end + socket = smbstate['socket'] + status, lhost, lport, rhost, rport = socket:get_info() + if(status == false) then + return false, lhost + end - -- Stop SMB - stop(smbstate) + -- Stop SMB + stop(smbstate) - -- Get the mac in hex format, if possible - local lmac = nil - if(host.mac_addr_src) then - lmac = stdnse.tohex(host.mac_addr_src, {separator = ":"}) - end + -- Get the mac in hex format, if possible + local lmac = nil + if(host.mac_addr_src) then + lmac = stdnse.tohex(host.mac_addr_src, {separator = ":"}) + end - return true, lhost, lport, rhost, rport, lmac + return true, lhost, lport, rhost, rport, lmac end ---Generate a string that's somewhat unique, but is based on factors that won't change on a host. At the moment, this is a very simple @@ -3373,740 +3373,740 @@ end --@return (status, data) If status is true, data is a table of values; otherwise, data is an error message. Can be any kind of string. function get_uniqueish_name(host, extension, seed) - local status - local lhost, lport, rhost, rport - if(type(host) == "table") then - status, lhost = get_socket_info(host) - else - lhost = host - end + local status + local lhost, lport, rhost, rport + if(type(host) == "table") then + status, lhost = get_socket_info(host) + else + lhost = host + end - -- Create our ultra-weak hash by using a simple xor/shift algorithm - -- I tested this, and in 255 tests, there were roughly 10 collisions. That's about what I'm looking for. - local hash = 0 - local i - local str = lhost .. (seed or "") .. (extension or "") .. (nmap.registry.args.randomseed or "") + -- Create our ultra-weak hash by using a simple xor/shift algorithm + -- I tested this, and in 255 tests, there were roughly 10 collisions. That's about what I'm looking for. + local hash = 0 + local i + local str = lhost .. (seed or "") .. (extension or "") .. (nmap.registry.args.randomseed or "") - for i = 1, #str, 1 do - local chr = string.byte(string.sub(str, i, i), 1) - hash = bit.bxor(hash, chr) - hash = bit.bor(bit.lshift(hash, 3), bit.rshift(hash, 29)) - hash = bit.bxor(hash, 3) - hash = bit.band(hash, 0xFFFFFFFF) - end + for i = 1, #str, 1 do + local chr = string.byte(string.sub(str, i, i), 1) + hash = bit.bxor(hash, chr) + hash = bit.bor(bit.lshift(hash, 3), bit.rshift(hash, 29)) + hash = bit.bxor(hash, 3) + hash = bit.band(hash, 0xFFFFFFFF) + end - local response - if(extension) then - response = string.format("%x.%s", hash, extension) - else - response = string.format("%x", hash) - end + local response + if(extension) then + response = string.format("%x.%s", hash, extension) + else + response = string.format("%x", hash) + end - return true, response + return true, response end ---Determines, as accurately as possible, whether or not an account is an administrator. If there is an error, -- 'false' is simply returned. function is_admin(host, username, domain, password, password_hash, hash_type) - local msrpc = require "msrpc" -- avoid require cycle - local status, smbstate, err, result - local overrides = get_overrides(username, domain, password, password_hash, hash_type) + local msrpc = require "msrpc" -- avoid require cycle + local status, smbstate, err, result + local overrides = get_overrides(username, domain, password, password_hash, hash_type) - stdnse.print_debug("SMB: Checking if %s is an administrator", username) + stdnse.print_debug("SMB: Checking if %s is an administrator", username) - status, smbstate = start(host) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Failed to start SMB: %s [%s]", smbstate, username) - stop(smbstate) - return false - end + status, smbstate = start(host) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Failed to start SMB: %s [%s]", smbstate, username) + stop(smbstate) + return false + end - status, err = negotiate_protocol(smbstate, overrides) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Failed to negotiatie protocol: %s [%s]", err, username) - stop(smbstate) - return false - end + status, err = negotiate_protocol(smbstate, overrides) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Failed to negotiatie protocol: %s [%s]", err, username) + stop(smbstate) + return false + end - status, err = start_session(smbstate, overrides) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Failed to start session %s [%s]", err, username) - stop(smbstate) - return false - end + status, err = start_session(smbstate, overrides) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Failed to start session %s [%s]", err, username) + stop(smbstate) + return false + end - status, err = tree_connect(smbstate, "IPC$", overrides) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Failed to connect tree: %s [%s]", err, username) - stop(smbstate) - return false - end + status, err = tree_connect(smbstate, "IPC$", overrides) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Failed to connect tree: %s [%s]", err, username) + stop(smbstate) + return false + end - status, err = create_file(smbstate, msrpc.SRVSVC_PATH, overrides) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Failed to create file: %s [%s]", err, username) - stop(smbstate) - return false - end + status, err = create_file(smbstate, msrpc.SRVSVC_PATH, overrides) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Failed to create file: %s [%s]", err, username) + stop(smbstate) + return false + end - status, err = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Failed to bind: %s [%s]", err, username) - stop(smbstate) - return false - end + status, err = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Failed to bind: %s [%s]", err, username) + stop(smbstate) + return false + end - -- Call netservergetstatistics for 'server' - status, err = msrpc.srvsvc_netservergetstatistics(smbstate, host.ip) - if(status == false) then - stdnse.print_debug("SMB; is_admin: Couldn't get server stats (may be normal): %s [%s]", err, username) - stop(smbstate) - return false - end + -- Call netservergetstatistics for 'server' + status, err = msrpc.srvsvc_netservergetstatistics(smbstate, host.ip) + if(status == false) then + stdnse.print_debug("SMB; is_admin: Couldn't get server stats (may be normal): %s [%s]", err, username) + stop(smbstate) + return false + end - stop(smbstate) + stop(smbstate) - return true + return true end command_codes = { - SMB_COM_CREATE_DIRECTORY = 0x00, - SMB_COM_DELETE_DIRECTORY = 0x01, - SMB_COM_OPEN = 0x02, - SMB_COM_CREATE = 0x03, - SMB_COM_CLOSE = 0x04, - SMB_COM_FLUSH = 0x05, - SMB_COM_DELETE = 0x06, - SMB_COM_RENAME = 0x07, - SMB_COM_QUERY_INFORMATION = 0x08, - SMB_COM_SET_INFORMATION = 0x09, - SMB_COM_READ = 0x0A, - SMB_COM_WRITE = 0x0B, - SMB_COM_LOCK_BYTE_RANGE = 0x0C, - SMB_COM_UNLOCK_BYTE_RANGE = 0x0D, - SMB_COM_CREATE_TEMPORARY = 0x0E, - SMB_COM_CREATE_NEW = 0x0F, - SMB_COM_CHECK_DIRECTORY = 0x10, - SMB_COM_PROCESS_EXIT = 0x11, - SMB_COM_SEEK = 0x12, - SMB_COM_LOCK_AND_READ = 0x13, - SMB_COM_WRITE_AND_UNLOCK = 0x14, - SMB_COM_READ_RAW = 0x1A, - SMB_COM_READ_MPX = 0x1B, - SMB_COM_READ_MPX_SECONDARY = 0x1C, - SMB_COM_WRITE_RAW = 0x1D, - SMB_COM_WRITE_MPX = 0x1E, - SMB_COM_WRITE_MPX_SECONDARY = 0x1F, - SMB_COM_WRITE_COMPLETE = 0x20, - SMB_COM_QUERY_SERVER = 0x21, - SMB_COM_SET_INFORMATION2 = 0x22, - SMB_COM_QUERY_INFORMATION2 = 0x23, - SMB_COM_LOCKING_ANDX = 0x24, - SMB_COM_TRANSACTION = 0x25, - SMB_COM_TRANSACTION_SECONDARY = 0x26, - SMB_COM_IOCTL = 0x27, - SMB_COM_IOCTL_SECONDARY = 0x28, - SMB_COM_COPY = 0x29, - SMB_COM_MOVE = 0x2A, - SMB_COM_ECHO = 0x2B, - SMB_COM_WRITE_AND_CLOSE = 0x2C, - SMB_COM_OPEN_ANDX = 0x2D, - SMB_COM_READ_ANDX = 0x2E, - SMB_COM_WRITE_ANDX = 0x2F, - SMB_COM_NEW_FILE_SIZE = 0x30, - SMB_COM_CLOSE_AND_TREE_DISC = 0x31, - SMB_COM_TRANSACTION2 = 0x32, - SMB_COM_TRANSACTION2_SECONDARY = 0x33, - SMB_COM_FIND_CLOSE2 = 0x34, - SMB_COM_FIND_NOTIFY_CLOSE = 0x35, - SMB_COM_TREE_CONNECT = 0x70, - SMB_COM_TREE_DISCONNECT = 0x71, - SMB_COM_NEGOTIATE = 0x72, - SMB_COM_SESSION_SETUP_ANDX = 0x73, - SMB_COM_LOGOFF_ANDX = 0x74, - SMB_COM_TREE_CONNECT_ANDX = 0x75, - SMB_COM_QUERY_INFORMATION_DISK = 0x80, - SMB_COM_SEARCH = 0x81, - SMB_COM_FIND = 0x82, - SMB_COM_FIND_UNIQUE = 0x83, - SMB_COM_FIND_CLOSE = 0x84, - SMB_COM_NT_TRANSACT = 0xA0, - SMB_COM_NT_TRANSACT_SECONDARY = 0xA1, - SMB_COM_NT_CREATE_ANDX = 0xA2, - SMB_COM_NT_CANCEL = 0xA4, - SMB_COM_NT_RENAME = 0xA5, - SMB_COM_OPEN_PRINT_FILE = 0xC0, - SMB_COM_WRITE_PRINT_FILE = 0xC1, - SMB_COM_CLOSE_PRINT_FILE = 0xC2, - SMB_COM_GET_PRINT_QUEUE = 0xC3, - SMB_COM_READ_BULK = 0xD8, - SMB_COM_WRITE_BULK = 0xD9, - SMB_COM_WRITE_BULK_DATA = 0xDA, - SMB_NO_FURTHER_COMMANDS = 0xFF + SMB_COM_CREATE_DIRECTORY = 0x00, + SMB_COM_DELETE_DIRECTORY = 0x01, + SMB_COM_OPEN = 0x02, + SMB_COM_CREATE = 0x03, + SMB_COM_CLOSE = 0x04, + SMB_COM_FLUSH = 0x05, + SMB_COM_DELETE = 0x06, + SMB_COM_RENAME = 0x07, + SMB_COM_QUERY_INFORMATION = 0x08, + SMB_COM_SET_INFORMATION = 0x09, + SMB_COM_READ = 0x0A, + SMB_COM_WRITE = 0x0B, + SMB_COM_LOCK_BYTE_RANGE = 0x0C, + SMB_COM_UNLOCK_BYTE_RANGE = 0x0D, + SMB_COM_CREATE_TEMPORARY = 0x0E, + SMB_COM_CREATE_NEW = 0x0F, + SMB_COM_CHECK_DIRECTORY = 0x10, + SMB_COM_PROCESS_EXIT = 0x11, + SMB_COM_SEEK = 0x12, + SMB_COM_LOCK_AND_READ = 0x13, + SMB_COM_WRITE_AND_UNLOCK = 0x14, + SMB_COM_READ_RAW = 0x1A, + SMB_COM_READ_MPX = 0x1B, + SMB_COM_READ_MPX_SECONDARY = 0x1C, + SMB_COM_WRITE_RAW = 0x1D, + SMB_COM_WRITE_MPX = 0x1E, + SMB_COM_WRITE_MPX_SECONDARY = 0x1F, + SMB_COM_WRITE_COMPLETE = 0x20, + SMB_COM_QUERY_SERVER = 0x21, + SMB_COM_SET_INFORMATION2 = 0x22, + SMB_COM_QUERY_INFORMATION2 = 0x23, + SMB_COM_LOCKING_ANDX = 0x24, + SMB_COM_TRANSACTION = 0x25, + SMB_COM_TRANSACTION_SECONDARY = 0x26, + SMB_COM_IOCTL = 0x27, + SMB_COM_IOCTL_SECONDARY = 0x28, + SMB_COM_COPY = 0x29, + SMB_COM_MOVE = 0x2A, + SMB_COM_ECHO = 0x2B, + SMB_COM_WRITE_AND_CLOSE = 0x2C, + SMB_COM_OPEN_ANDX = 0x2D, + SMB_COM_READ_ANDX = 0x2E, + SMB_COM_WRITE_ANDX = 0x2F, + SMB_COM_NEW_FILE_SIZE = 0x30, + SMB_COM_CLOSE_AND_TREE_DISC = 0x31, + SMB_COM_TRANSACTION2 = 0x32, + SMB_COM_TRANSACTION2_SECONDARY = 0x33, + SMB_COM_FIND_CLOSE2 = 0x34, + SMB_COM_FIND_NOTIFY_CLOSE = 0x35, + SMB_COM_TREE_CONNECT = 0x70, + SMB_COM_TREE_DISCONNECT = 0x71, + SMB_COM_NEGOTIATE = 0x72, + SMB_COM_SESSION_SETUP_ANDX = 0x73, + SMB_COM_LOGOFF_ANDX = 0x74, + SMB_COM_TREE_CONNECT_ANDX = 0x75, + SMB_COM_QUERY_INFORMATION_DISK = 0x80, + SMB_COM_SEARCH = 0x81, + SMB_COM_FIND = 0x82, + SMB_COM_FIND_UNIQUE = 0x83, + SMB_COM_FIND_CLOSE = 0x84, + SMB_COM_NT_TRANSACT = 0xA0, + SMB_COM_NT_TRANSACT_SECONDARY = 0xA1, + SMB_COM_NT_CREATE_ANDX = 0xA2, + SMB_COM_NT_CANCEL = 0xA4, + SMB_COM_NT_RENAME = 0xA5, + SMB_COM_OPEN_PRINT_FILE = 0xC0, + SMB_COM_WRITE_PRINT_FILE = 0xC1, + SMB_COM_CLOSE_PRINT_FILE = 0xC2, + SMB_COM_GET_PRINT_QUEUE = 0xC3, + SMB_COM_READ_BULK = 0xD8, + SMB_COM_WRITE_BULK = 0xD9, + SMB_COM_WRITE_BULK_DATA = 0xDA, + SMB_NO_FURTHER_COMMANDS = 0xFF } for i, v in pairs(command_codes) do - command_names[v] = i + command_names[v] = i end -- see http://msdn.microsoft.com/en-us/library/cc231196(v=prot.10).aspx status_codes = { - NT_STATUS_SUCCESS = 0x00000000, - NT_STATUS_WERR_BADFILE = 0x00000002, - NT_STATUS_WERR_ACCESS_DENIED = 0x00000005, - NT_STATUS_WERR_INVALID_PARAMETER = 0x00000057, - NT_STATUS_WERR_INVALID_NAME = 0x0000007b, - NT_STATUS_WERR_UNKNOWN_LEVEL = 0x0000007c, - NT_STATUS_WERR_MORE_DATA = 0x000000ea, - NT_STATUS_NO_MORE_ITEMS = 0x00000103, - NT_STATUS_MORE_ENTRIES = 0x00000105, - NT_STATUS_SOME_NOT_MAPPED = 0x00000107, - NT_STATUS_SERVICE_REQUEST_TIMEOUT = 0x0000041D, - NT_STATUS_SERVICE_NO_THREAD = 0x0000041E, - NT_STATUS_SERVICE_DATABASE_LOCKED = 0x0000041F, - NT_STATUS_SERVICE_ALREADY_RUNNING = 0x00000420, - NT_STATUS_INVALID_SERVICE_ACCOUNT = 0x00000421, - NT_STATUS_SERVICE_DISABLED = 0x00000422, - NT_STATUS_CIRCULAR_DEPENDENCY = 0x00000423, - NT_STATUS_SERVICE_DOES_NOT_EXIST = 0x00000424, - NT_STATUS_SERVICE_CANNOT_ACCEPT_CTRL = 0x00000425, - NT_STATUS_SERVICE_NOT_ACTIVE = 0x00000426, - NT_STATUS_FAILED_SERVICE_CONTROLLER_CONNECT = 0x00000427, - NT_STATUS_EXCEPTION_IN_SERVICE = 0x00000428, - NT_STATUS_DATABASE_DOES_NOT_EXIST = 0x00000429, - NT_STATUS_SERVICE_SPECIFIC_ERROR = 0x0000042a, - NT_STATUS_PROCESS_ABORTED = 0x0000042b, - NT_STATUS_SERVICE_DEPENDENCY_FAIL = 0x0000042c, - NT_STATUS_SERVICE_LOGON_FAILED = 0x0000042d, - NT_STATUS_SERVICE_START_HANG = 0x0000042e, - NT_STATUS_INVALID_SERVICE_LOCK = 0x0000042f, - NT_STATUS_SERVICE_MARKED_FOR_DELETE = 0x00000430, - NT_STATUS_SERVICE_EXISTS = 0x00000431, - NT_STATUS_ALREADY_RUNNING_LKG = 0x00000432, - NT_STATUS_SERVICE_DEPENDENCY_DELETED = 0x00000433, - NT_STATUS_BOOT_ALREADY_ACCEPTED = 0x00000434, - NT_STATUS_SERVICE_NEVER_STARTED = 0x00000435, - NT_STATUS_DUPLICATE_SERVICE_NAME = 0x00000436, - NT_STATUS_DIFFERENT_SERVICE_ACCOUNT = 0x00000437, - NT_STATUS_CANNOT_DETECT_DRIVER_FAILURE = 0x00000438, - DOS_STATUS_UNKNOWN_ERROR = 0x00010001, - DOS_STATUS_NONSPECIFIC_ERROR = 0x00010002, - DOS_STATUS_DIRECTORY_NOT_FOUND = 0x00030001, - DOS_STATUS_ACCESS_DENIED = 0x00050001, - DOS_STATUS_INVALID_FID = 0x00060001, - DOS_STATUS_INVALID_NETWORK_NAME = 0x00060002, - NT_STATUS_BUFFER_OVERFLOW = 0x80000005, - NT_STATUS_UNSUCCESSFUL = 0xc0000001, - NT_STATUS_NOT_IMPLEMENTED = 0xc0000002, - NT_STATUS_INVALID_INFO_CLASS = 0xc0000003, - NT_STATUS_INFO_LENGTH_MISMATCH = 0xc0000004, - NT_STATUS_ACCESS_VIOLATION = 0xc0000005, - NT_STATUS_IN_PAGE_ERROR = 0xc0000006, - NT_STATUS_PAGEFILE_QUOTA = 0xc0000007, - NT_STATUS_INVALID_HANDLE = 0xc0000008, - NT_STATUS_BAD_INITIAL_STACK = 0xc0000009, - NT_STATUS_BAD_INITIAL_PC = 0xc000000a, - NT_STATUS_INVALID_CID = 0xc000000b, - NT_STATUS_TIMER_NOT_CANCELED = 0xc000000c, - NT_STATUS_INVALID_PARAMETER = 0xc000000d, - NT_STATUS_NO_SUCH_DEVICE = 0xc000000e, - NT_STATUS_NO_SUCH_FILE = 0xc000000f, - NT_STATUS_INVALID_DEVICE_REQUEST = 0xc0000010, - NT_STATUS_END_OF_FILE = 0xc0000011, - NT_STATUS_WRONG_VOLUME = 0xc0000012, - NT_STATUS_NO_MEDIA_IN_DEVICE = 0xc0000013, - NT_STATUS_UNRECOGNIZED_MEDIA = 0xc0000014, - NT_STATUS_NONEXISTENT_SECTOR = 0xc0000015, - NT_STATUS_MORE_PROCESSING_REQUIRED = 0xc0000016, - NT_STATUS_NO_MEMORY = 0xc0000017, - NT_STATUS_CONFLICTING_ADDRESSES = 0xc0000018, - NT_STATUS_NOT_MAPPED_VIEW = 0xc0000019, - NT_STATUS_UNABLE_TO_FREE_VM = 0xc000001a, - NT_STATUS_UNABLE_TO_DELETE_SECTION = 0xc000001b, - NT_STATUS_INVALID_SYSTEM_SERVICE = 0xc000001c, - NT_STATUS_ILLEGAL_INSTRUCTION = 0xc000001d, - NT_STATUS_INVALID_LOCK_SEQUENCE = 0xc000001e, - NT_STATUS_INVALID_VIEW_SIZE = 0xc000001f, - NT_STATUS_INVALID_FILE_FOR_SECTION = 0xc0000020, - NT_STATUS_ALREADY_COMMITTED = 0xc0000021, - NT_STATUS_ACCESS_DENIED = 0xc0000022, - NT_STATUS_BUFFER_TOO_SMALL = 0xc0000023, - NT_STATUS_OBJECT_TYPE_MISMATCH = 0xc0000024, - NT_STATUS_NONCONTINUABLE_EXCEPTION = 0xc0000025, - NT_STATUS_INVALID_DISPOSITION = 0xc0000026, - NT_STATUS_UNWIND = 0xc0000027, - NT_STATUS_BAD_STACK = 0xc0000028, - NT_STATUS_INVALID_UNWIND_TARGET = 0xc0000029, - NT_STATUS_NOT_LOCKED = 0xc000002a, - NT_STATUS_PARITY_ERROR = 0xc000002b, - NT_STATUS_UNABLE_TO_DECOMMIT_VM = 0xc000002c, - NT_STATUS_NOT_COMMITTED = 0xc000002d, - NT_STATUS_INVALID_PORT_ATTRIBUTES = 0xc000002e, - NT_STATUS_PORT_MESSAGE_TOO_LONG = 0xc000002f, - NT_STATUS_INVALID_PARAMETER_MIX = 0xc0000030, - NT_STATUS_INVALID_QUOTA_LOWER = 0xc0000031, - NT_STATUS_DISK_CORRUPT_ERROR = 0xc0000032, - NT_STATUS_OBJECT_NAME_INVALID = 0xc0000033, - NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034, - NT_STATUS_OBJECT_NAME_COLLISION = 0xc0000035, - NT_STATUS_HANDLE_NOT_WAITABLE = 0xc0000036, - NT_STATUS_PORT_DISCONNECTED = 0xc0000037, - NT_STATUS_DEVICE_ALREADY_ATTACHED = 0xc0000038, - NT_STATUS_OBJECT_PATH_INVALID = 0xc0000039, - NT_STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a, - NT_STATUS_OBJECT_PATH_SYNTAX_BAD = 0xc000003b, - NT_STATUS_DATA_OVERRUN = 0xc000003c, - NT_STATUS_DATA_LATE_ERROR = 0xc000003d, - NT_STATUS_DATA_ERROR = 0xc000003e, - NT_STATUS_CRC_ERROR = 0xc000003f, - NT_STATUS_SECTION_TOO_BIG = 0xc0000040, - NT_STATUS_PORT_CONNECTION_REFUSED = 0xc0000041, - NT_STATUS_INVALID_PORT_HANDLE = 0xc0000042, - NT_STATUS_SHARING_VIOLATION = 0xc0000043, - NT_STATUS_QUOTA_EXCEEDED = 0xc0000044, - NT_STATUS_INVALID_PAGE_PROTECTION = 0xc0000045, - NT_STATUS_MUTANT_NOT_OWNED = 0xc0000046, - NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED = 0xc0000047, - NT_STATUS_PORT_ALREADY_SET = 0xc0000048, - NT_STATUS_SECTION_NOT_IMAGE = 0xc0000049, - NT_STATUS_SUSPEND_COUNT_EXCEEDED = 0xc000004a, - NT_STATUS_THREAD_IS_TERMINATING = 0xc000004b, - NT_STATUS_BAD_WORKING_SET_LIMIT = 0xc000004c, - NT_STATUS_INCOMPATIBLE_FILE_MAP = 0xc000004d, - NT_STATUS_SECTION_PROTECTION = 0xc000004e, - NT_STATUS_EAS_NOT_SUPPORTED = 0xc000004f, - NT_STATUS_EA_TOO_LARGE = 0xc0000050, - NT_STATUS_NONEXISTENT_EA_ENTRY = 0xc0000051, - NT_STATUS_NO_EAS_ON_FILE = 0xc0000052, - NT_STATUS_EA_CORRUPT_ERROR = 0xc0000053, - NT_STATUS_FILE_LOCK_CONFLICT = 0xc0000054, - NT_STATUS_LOCK_NOT_GRANTED = 0xc0000055, - NT_STATUS_DELETE_PENDING = 0xc0000056, - NT_STATUS_CTL_FILE_NOT_SUPPORTED = 0xc0000057, - NT_STATUS_UNKNOWN_REVISION = 0xc0000058, - NT_STATUS_REVISION_MISMATCH = 0xc0000059, - NT_STATUS_INVALID_OWNER = 0xc000005a, - NT_STATUS_INVALID_PRIMARY_GROUP = 0xc000005b, - NT_STATUS_NO_IMPERSONATION_TOKEN = 0xc000005c, - NT_STATUS_CANT_DISABLE_MANDATORY = 0xc000005d, - NT_STATUS_NO_LOGON_SERVERS = 0xc000005e, - NT_STATUS_NO_SUCH_LOGON_SESSION = 0xc000005f, - NT_STATUS_NO_SUCH_PRIVILEGE = 0xc0000060, - NT_STATUS_PRIVILEGE_NOT_HELD = 0xc0000061, - NT_STATUS_INVALID_ACCOUNT_NAME = 0xc0000062, - NT_STATUS_USER_EXISTS = 0xc0000063, - NT_STATUS_NO_SUCH_USER = 0xc0000064, - NT_STATUS_GROUP_EXISTS = 0xc0000065, - NT_STATUS_NO_SUCH_GROUP = 0xc0000066, - NT_STATUS_MEMBER_IN_GROUP = 0xc0000067, - NT_STATUS_MEMBER_NOT_IN_GROUP = 0xc0000068, - NT_STATUS_LAST_ADMIN = 0xc0000069, - NT_STATUS_WRONG_PASSWORD = 0xc000006a, - NT_STATUS_ILL_FORMED_PASSWORD = 0xc000006b, - NT_STATUS_PASSWORD_RESTRICTION = 0xc000006c, - NT_STATUS_LOGON_FAILURE = 0xc000006d, - NT_STATUS_ACCOUNT_RESTRICTION = 0xc000006e, - NT_STATUS_INVALID_LOGON_HOURS = 0xc000006f, - NT_STATUS_INVALID_WORKSTATION = 0xc0000070, - NT_STATUS_PASSWORD_EXPIRED = 0xc0000071, - NT_STATUS_ACCOUNT_DISABLED = 0xc0000072, - NT_STATUS_NONE_MAPPED = 0xc0000073, - NT_STATUS_TOO_MANY_LUIDS_REQUESTED = 0xc0000074, - NT_STATUS_LUIDS_EXHAUSTED = 0xc0000075, - NT_STATUS_INVALID_SUB_AUTHORITY = 0xc0000076, - NT_STATUS_INVALID_ACL = 0xc0000077, - NT_STATUS_INVALID_SID = 0xc0000078, - NT_STATUS_INVALID_SECURITY_DESCR = 0xc0000079, - NT_STATUS_PROCEDURE_NOT_FOUND = 0xc000007a, - NT_STATUS_INVALID_IMAGE_FORMAT = 0xc000007b, - NT_STATUS_NO_TOKEN = 0xc000007c, - NT_STATUS_BAD_INHERITANCE_ACL = 0xc000007d, - NT_STATUS_RANGE_NOT_LOCKED = 0xc000007e, - NT_STATUS_DISK_FULL = 0xc000007f, - NT_STATUS_SERVER_DISABLED = 0xc0000080, - NT_STATUS_SERVER_NOT_DISABLED = 0xc0000081, - NT_STATUS_TOO_MANY_GUIDS_REQUESTED = 0xc0000082, - NT_STATUS_GUIDS_EXHAUSTED = 0xc0000083, - NT_STATUS_INVALID_ID_AUTHORITY = 0xc0000084, - NT_STATUS_AGENTS_EXHAUSTED = 0xc0000085, - NT_STATUS_INVALID_VOLUME_LABEL = 0xc0000086, - NT_STATUS_SECTION_NOT_EXTENDED = 0xc0000087, - NT_STATUS_NOT_MAPPED_DATA = 0xc0000088, - NT_STATUS_RESOURCE_DATA_NOT_FOUND = 0xc0000089, - NT_STATUS_RESOURCE_TYPE_NOT_FOUND = 0xc000008a, - NT_STATUS_RESOURCE_NAME_NOT_FOUND = 0xc000008b, - NT_STATUS_ARRAY_BOUNDS_EXCEEDED = 0xc000008c, - NT_STATUS_FLOAT_DENORMAL_OPERAND = 0xc000008d, - NT_STATUS_FLOAT_DIVIDE_BY_ZERO = 0xc000008e, - NT_STATUS_FLOAT_INEXACT_RESULT = 0xc000008f, - NT_STATUS_FLOAT_INVALID_OPERATION = 0xc0000090, - NT_STATUS_FLOAT_OVERFLOW = 0xc0000091, - NT_STATUS_FLOAT_STACK_CHECK = 0xc0000092, - NT_STATUS_FLOAT_UNDERFLOW = 0xc0000093, - NT_STATUS_INTEGER_DIVIDE_BY_ZERO = 0xc0000094, - NT_STATUS_INTEGER_OVERFLOW = 0xc0000095, - NT_STATUS_PRIVILEGED_INSTRUCTION = 0xc0000096, - NT_STATUS_TOO_MANY_PAGING_FILES = 0xc0000097, - NT_STATUS_FILE_INVALID = 0xc0000098, - NT_STATUS_ALLOTTED_SPACE_EXCEEDED = 0xc0000099, - NT_STATUS_INSUFFICIENT_RESOURCES = 0xc000009a, - NT_STATUS_DFS_EXIT_PATH_FOUND = 0xc000009b, - NT_STATUS_DEVICE_DATA_ERROR = 0xc000009c, - NT_STATUS_DEVICE_NOT_CONNECTED = 0xc000009d, - NT_STATUS_DEVICE_POWER_FAILURE = 0xc000009e, - NT_STATUS_FREE_VM_NOT_AT_BASE = 0xc000009f, - NT_STATUS_MEMORY_NOT_ALLOCATED = 0xc00000a0, - NT_STATUS_WORKING_SET_QUOTA = 0xc00000a1, - NT_STATUS_MEDIA_WRITE_PROTECTED = 0xc00000a2, - NT_STATUS_DEVICE_NOT_READY = 0xc00000a3, - NT_STATUS_INVALID_GROUP_ATTRIBUTES = 0xc00000a4, - NT_STATUS_BAD_IMPERSONATION_LEVEL = 0xc00000a5, - NT_STATUS_CANT_OPEN_ANONYMOUS = 0xc00000a6, - NT_STATUS_BAD_VALIDATION_CLASS = 0xc00000a7, - NT_STATUS_BAD_TOKEN_TYPE = 0xc00000a8, - NT_STATUS_BAD_MASTER_BOOT_RECORD = 0xc00000a9, - NT_STATUS_INSTRUCTION_MISALIGNMENT = 0xc00000aa, - NT_STATUS_INSTANCE_NOT_AVAILABLE = 0xc00000ab, - NT_STATUS_PIPE_NOT_AVAILABLE = 0xc00000ac, - NT_STATUS_INVALID_PIPE_STATE = 0xc00000ad, - NT_STATUS_PIPE_BUSY = 0xc00000ae, - NT_STATUS_ILLEGAL_FUNCTION = 0xc00000af, - NT_STATUS_PIPE_DISCONNECTED = 0xc00000b0, - NT_STATUS_PIPE_CLOSING = 0xc00000b1, - NT_STATUS_PIPE_CONNECTED = 0xc00000b2, - NT_STATUS_PIPE_LISTENING = 0xc00000b3, - NT_STATUS_INVALID_READ_MODE = 0xc00000b4, - NT_STATUS_IO_TIMEOUT = 0xc00000b5, - NT_STATUS_FILE_FORCED_CLOSED = 0xc00000b6, - NT_STATUS_PROFILING_NOT_STARTED = 0xc00000b7, - NT_STATUS_PROFILING_NOT_STOPPED = 0xc00000b8, - NT_STATUS_COULD_NOT_INTERPRET = 0xc00000b9, - NT_STATUS_FILE_IS_A_DIRECTORY = 0xc00000ba, - NT_STATUS_NOT_SUPPORTED = 0xc00000bb, - NT_STATUS_REMOTE_NOT_LISTENING = 0xc00000bc, - NT_STATUS_DUPLICATE_NAME = 0xc00000bd, - NT_STATUS_BAD_NETWORK_PATH = 0xc00000be, - NT_STATUS_NETWORK_BUSY = 0xc00000bf, - NT_STATUS_DEVICE_DOES_NOT_EXIST = 0xc00000c0, - NT_STATUS_TOO_MANY_COMMANDS = 0xc00000c1, - NT_STATUS_ADAPTER_HARDWARE_ERROR = 0xc00000c2, - NT_STATUS_INVALID_NETWORK_RESPONSE = 0xc00000c3, - NT_STATUS_UNEXPECTED_NETWORK_ERROR = 0xc00000c4, - NT_STATUS_BAD_REMOTE_ADAPTER = 0xc00000c5, - NT_STATUS_PRINT_QUEUE_FULL = 0xc00000c6, - NT_STATUS_NO_SPOOL_SPACE = 0xc00000c7, - NT_STATUS_PRINT_CANCELLED = 0xc00000c8, - NT_STATUS_NETWORK_NAME_DELETED = 0xc00000c9, - NT_STATUS_NETWORK_ACCESS_DENIED = 0xc00000ca, - NT_STATUS_BAD_DEVICE_TYPE = 0xc00000cb, - NT_STATUS_BAD_NETWORK_NAME = 0xc00000cc, - NT_STATUS_TOO_MANY_NAMES = 0xc00000cd, - NT_STATUS_TOO_MANY_SESSIONS = 0xc00000ce, - NT_STATUS_SHARING_PAUSED = 0xc00000cf, - NT_STATUS_REQUEST_NOT_ACCEPTED = 0xc00000d0, - NT_STATUS_REDIRECTOR_PAUSED = 0xc00000d1, - NT_STATUS_NET_WRITE_FAULT = 0xc00000d2, - NT_STATUS_PROFILING_AT_LIMIT = 0xc00000d3, - NT_STATUS_NOT_SAME_DEVICE = 0xc00000d4, - NT_STATUS_FILE_RENAMED = 0xc00000d5, - NT_STATUS_VIRTUAL_CIRCUIT_CLOSED = 0xc00000d6, - NT_STATUS_NO_SECURITY_ON_OBJECT = 0xc00000d7, - NT_STATUS_CANT_WAIT = 0xc00000d8, - NT_STATUS_PIPE_EMPTY = 0xc00000d9, - NT_STATUS_CANT_ACCESS_DOMAIN_INFO = 0xc00000da, - NT_STATUS_CANT_TERMINATE_SELF = 0xc00000db, - NT_STATUS_INVALID_SERVER_STATE = 0xc00000dc, - NT_STATUS_INVALID_DOMAIN_STATE = 0xc00000dd, - NT_STATUS_INVALID_DOMAIN_ROLE = 0xc00000de, - NT_STATUS_NO_SUCH_DOMAIN = 0xc00000df, - NT_STATUS_DOMAIN_EXISTS = 0xc00000e0, - NT_STATUS_DOMAIN_LIMIT_EXCEEDED = 0xc00000e1, - NT_STATUS_OPLOCK_NOT_GRANTED = 0xc00000e2, - NT_STATUS_INVALID_OPLOCK_PROTOCOL = 0xc00000e3, - NT_STATUS_INTERNAL_DB_CORRUPTION = 0xc00000e4, - NT_STATUS_INTERNAL_ERROR = 0xc00000e5, - NT_STATUS_GENERIC_NOT_MAPPED = 0xc00000e6, - NT_STATUS_BAD_DESCRIPTOR_FORMAT = 0xc00000e7, - NT_STATUS_INVALID_USER_BUFFER = 0xc00000e8, - NT_STATUS_UNEXPECTED_IO_ERROR = 0xc00000e9, - NT_STATUS_UNEXPECTED_MM_CREATE_ERR = 0xc00000ea, - NT_STATUS_UNEXPECTED_MM_MAP_ERROR = 0xc00000eb, - NT_STATUS_UNEXPECTED_MM_EXTEND_ERR = 0xc00000ec, - NT_STATUS_NOT_LOGON_PROCESS = 0xc00000ed, - NT_STATUS_LOGON_SESSION_EXISTS = 0xc00000ee, - NT_STATUS_INVALID_PARAMETER_1 = 0xc00000ef, - NT_STATUS_INVALID_PARAMETER_2 = 0xc00000f0, - NT_STATUS_INVALID_PARAMETER_3 = 0xc00000f1, - NT_STATUS_INVALID_PARAMETER_4 = 0xc00000f2, - NT_STATUS_INVALID_PARAMETER_5 = 0xc00000f3, - NT_STATUS_INVALID_PARAMETER_6 = 0xc00000f4, - NT_STATUS_INVALID_PARAMETER_7 = 0xc00000f5, - NT_STATUS_INVALID_PARAMETER_8 = 0xc00000f6, - NT_STATUS_INVALID_PARAMETER_9 = 0xc00000f7, - NT_STATUS_INVALID_PARAMETER_10 = 0xc00000f8, - NT_STATUS_INVALID_PARAMETER_11 = 0xc00000f9, - NT_STATUS_INVALID_PARAMETER_12 = 0xc00000fa, - NT_STATUS_REDIRECTOR_NOT_STARTED = 0xc00000fb, - NT_STATUS_REDIRECTOR_STARTED = 0xc00000fc, - NT_STATUS_STACK_OVERFLOW = 0xc00000fd, - NT_STATUS_NO_SUCH_PACKAGE = 0xc00000fe, - NT_STATUS_BAD_FUNCTION_TABLE = 0xc00000ff, - NT_STATUS_DIRECTORY_NOT_EMPTY = 0xc0000101, - NT_STATUS_FILE_CORRUPT_ERROR = 0xc0000102, - NT_STATUS_NOT_A_DIRECTORY = 0xc0000103, - NT_STATUS_BAD_LOGON_SESSION_STATE = 0xc0000104, - NT_STATUS_LOGON_SESSION_COLLISION = 0xc0000105, - NT_STATUS_NAME_TOO_LONG = 0xc0000106, - NT_STATUS_FILES_OPEN = 0xc0000107, - NT_STATUS_CONNECTION_IN_USE = 0xc0000108, - NT_STATUS_MESSAGE_NOT_FOUND = 0xc0000109, - NT_STATUS_PROCESS_IS_TERMINATING = 0xc000010a, - NT_STATUS_INVALID_LOGON_TYPE = 0xc000010b, - NT_STATUS_NO_GUID_TRANSLATION = 0xc000010c, - NT_STATUS_CANNOT_IMPERSONATE = 0xc000010d, - NT_STATUS_IMAGE_ALREADY_LOADED = 0xc000010e, - NT_STATUS_ABIOS_NOT_PRESENT = 0xc000010f, - NT_STATUS_ABIOS_LID_NOT_EXIST = 0xc0000110, - NT_STATUS_ABIOS_LID_ALREADY_OWNED = 0xc0000111, - NT_STATUS_ABIOS_NOT_LID_OWNER = 0xc0000112, - NT_STATUS_ABIOS_INVALID_COMMAND = 0xc0000113, - NT_STATUS_ABIOS_INVALID_LID = 0xc0000114, - NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE = 0xc0000115, - NT_STATUS_ABIOS_INVALID_SELECTOR = 0xc0000116, - NT_STATUS_NO_LDT = 0xc0000117, - NT_STATUS_INVALID_LDT_SIZE = 0xc0000118, - NT_STATUS_INVALID_LDT_OFFSET = 0xc0000119, - NT_STATUS_INVALID_LDT_DESCRIPTOR = 0xc000011a, - NT_STATUS_INVALID_IMAGE_NE_FORMAT = 0xc000011b, - NT_STATUS_RXACT_INVALID_STATE = 0xc000011c, - NT_STATUS_RXACT_COMMIT_FAILURE = 0xc000011d, - NT_STATUS_MAPPED_FILE_SIZE_ZERO = 0xc000011e, - NT_STATUS_TOO_MANY_OPENED_FILES = 0xc000011f, - NT_STATUS_CANCELLED = 0xc0000120, - NT_STATUS_CANNOT_DELETE = 0xc0000121, - NT_STATUS_INVALID_COMPUTER_NAME = 0xc0000122, - NT_STATUS_FILE_DELETED = 0xc0000123, - NT_STATUS_SPECIAL_ACCOUNT = 0xc0000124, - NT_STATUS_SPECIAL_GROUP = 0xc0000125, - NT_STATUS_SPECIAL_USER = 0xc0000126, - NT_STATUS_MEMBERS_PRIMARY_GROUP = 0xc0000127, - NT_STATUS_FILE_CLOSED = 0xc0000128, - NT_STATUS_TOO_MANY_THREADS = 0xc0000129, - NT_STATUS_THREAD_NOT_IN_PROCESS = 0xc000012a, - NT_STATUS_TOKEN_ALREADY_IN_USE = 0xc000012b, - NT_STATUS_PAGEFILE_QUOTA_EXCEEDED = 0xc000012c, - NT_STATUS_COMMITMENT_LIMIT = 0xc000012d, - NT_STATUS_INVALID_IMAGE_LE_FORMAT = 0xc000012e, - NT_STATUS_INVALID_IMAGE_NOT_MZ = 0xc000012f, - NT_STATUS_INVALID_IMAGE_PROTECT = 0xc0000130, - NT_STATUS_INVALID_IMAGE_WIN_16 = 0xc0000131, - NT_STATUS_LOGON_SERVER_CONFLICT = 0xc0000132, - NT_STATUS_TIME_DIFFERENCE_AT_DC = 0xc0000133, - NT_STATUS_SYNCHRONIZATION_REQUIRED = 0xc0000134, - NT_STATUS_DLL_NOT_FOUND = 0xc0000135, - NT_STATUS_OPEN_FAILED = 0xc0000136, - NT_STATUS_IO_PRIVILEGE_FAILED = 0xc0000137, - NT_STATUS_ORDINAL_NOT_FOUND = 0xc0000138, - NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xc0000139, - NT_STATUS_CONTROL_C_EXIT = 0xc000013a, - NT_STATUS_LOCAL_DISCONNECT = 0xc000013b, - NT_STATUS_REMOTE_DISCONNECT = 0xc000013c, - NT_STATUS_REMOTE_RESOURCES = 0xc000013d, - NT_STATUS_LINK_FAILED = 0xc000013e, - NT_STATUS_LINK_TIMEOUT = 0xc000013f, - NT_STATUS_INVALID_CONNECTION = 0xc0000140, - NT_STATUS_INVALID_ADDRESS = 0xc0000141, - NT_STATUS_DLL_INIT_FAILED = 0xc0000142, - NT_STATUS_MISSING_SYSTEMFILE = 0xc0000143, - NT_STATUS_UNHANDLED_EXCEPTION = 0xc0000144, - NT_STATUS_APP_INIT_FAILURE = 0xc0000145, - NT_STATUS_PAGEFILE_CREATE_FAILED = 0xc0000146, - NT_STATUS_NO_PAGEFILE = 0xc0000147, - NT_STATUS_INVALID_LEVEL = 0xc0000148, - NT_STATUS_WRONG_PASSWORD_CORE = 0xc0000149, - NT_STATUS_ILLEGAL_FLOAT_CONTEXT = 0xc000014a, - NT_STATUS_PIPE_BROKEN = 0xc000014b, - NT_STATUS_REGISTRY_CORRUPT = 0xc000014c, - NT_STATUS_REGISTRY_IO_FAILED = 0xc000014d, - NT_STATUS_NO_EVENT_PAIR = 0xc000014e, - NT_STATUS_UNRECOGNIZED_VOLUME = 0xc000014f, - NT_STATUS_SERIAL_NO_DEVICE_INITED = 0xc0000150, - NT_STATUS_NO_SUCH_ALIAS = 0xc0000151, - NT_STATUS_MEMBER_NOT_IN_ALIAS = 0xc0000152, - NT_STATUS_MEMBER_IN_ALIAS = 0xc0000153, - NT_STATUS_ALIAS_EXISTS = 0xc0000154, - NT_STATUS_LOGON_NOT_GRANTED = 0xc0000155, - NT_STATUS_TOO_MANY_SECRETS = 0xc0000156, - NT_STATUS_SECRET_TOO_LONG = 0xc0000157, - NT_STATUS_INTERNAL_DB_ERROR = 0xc0000158, - NT_STATUS_FULLSCREEN_MODE = 0xc0000159, - NT_STATUS_TOO_MANY_CONTEXT_IDS = 0xc000015a, - NT_STATUS_LOGON_TYPE_NOT_GRANTED = 0xc000015b, - NT_STATUS_NOT_REGISTRY_FILE = 0xc000015c, - NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED = 0xc000015d, - NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR = 0xc000015e, - NT_STATUS_FT_MISSING_MEMBER = 0xc000015f, - NT_STATUS_ILL_FORMED_SERVICE_ENTRY = 0xc0000160, - NT_STATUS_ILLEGAL_CHARACTER = 0xc0000161, - NT_STATUS_UNMAPPABLE_CHARACTER = 0xc0000162, - NT_STATUS_UNDEFINED_CHARACTER = 0xc0000163, - NT_STATUS_FLOPPY_VOLUME = 0xc0000164, - NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND = 0xc0000165, - NT_STATUS_FLOPPY_WRONG_CYLINDER = 0xc0000166, - NT_STATUS_FLOPPY_UNKNOWN_ERROR = 0xc0000167, - NT_STATUS_FLOPPY_BAD_REGISTERS = 0xc0000168, - NT_STATUS_DISK_RECALIBRATE_FAILED = 0xc0000169, - NT_STATUS_DISK_OPERATION_FAILED = 0xc000016a, - NT_STATUS_DISK_RESET_FAILED = 0xc000016b, - NT_STATUS_SHARED_IRQ_BUSY = 0xc000016c, - NT_STATUS_FT_ORPHANING = 0xc000016d, - NT_STATUS_PARTITION_FAILURE = 0xc0000172, - NT_STATUS_INVALID_BLOCK_LENGTH = 0xc0000173, - NT_STATUS_DEVICE_NOT_PARTITIONED = 0xc0000174, - NT_STATUS_UNABLE_TO_LOCK_MEDIA = 0xc0000175, - NT_STATUS_UNABLE_TO_UNLOAD_MEDIA = 0xc0000176, - NT_STATUS_EOM_OVERFLOW = 0xc0000177, - NT_STATUS_NO_MEDIA = 0xc0000178, - NT_STATUS_NO_SUCH_MEMBER = 0xc000017a, - NT_STATUS_INVALID_MEMBER = 0xc000017b, - NT_STATUS_KEY_DELETED = 0xc000017c, - NT_STATUS_NO_LOG_SPACE = 0xc000017d, - NT_STATUS_TOO_MANY_SIDS = 0xc000017e, - NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED = 0xc000017f, - NT_STATUS_KEY_HAS_CHILDREN = 0xc0000180, - NT_STATUS_CHILD_MUST_BE_VOLATILE = 0xc0000181, - NT_STATUS_DEVICE_CONFIGURATION_ERROR = 0xc0000182, - NT_STATUS_DRIVER_INTERNAL_ERROR = 0xc0000183, - NT_STATUS_INVALID_DEVICE_STATE = 0xc0000184, - NT_STATUS_IO_DEVICE_ERROR = 0xc0000185, - NT_STATUS_DEVICE_PROTOCOL_ERROR = 0xc0000186, - NT_STATUS_BACKUP_CONTROLLER = 0xc0000187, - NT_STATUS_LOG_FILE_FULL = 0xc0000188, - NT_STATUS_TOO_LATE = 0xc0000189, - NT_STATUS_NO_TRUST_LSA_SECRET = 0xc000018a, - NT_STATUS_NO_TRUST_SAM_ACCOUNT = 0xc000018b, - NT_STATUS_TRUSTED_DOMAIN_FAILURE = 0xc000018c, - NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE = 0xc000018d, - NT_STATUS_EVENTLOG_FILE_CORRUPT = 0xc000018e, - NT_STATUS_EVENTLOG_CANT_START = 0xc000018f, - NT_STATUS_TRUST_FAILURE = 0xc0000190, - NT_STATUS_MUTANT_LIMIT_EXCEEDED = 0xc0000191, - NT_STATUS_NETLOGON_NOT_STARTED = 0xc0000192, - NT_STATUS_ACCOUNT_EXPIRED = 0xc0000193, - NT_STATUS_POSSIBLE_DEADLOCK = 0xc0000194, - NT_STATUS_NETWORK_CREDENTIAL_CONFLICT = 0xc0000195, - NT_STATUS_REMOTE_SESSION_LIMIT = 0xc0000196, - NT_STATUS_EVENTLOG_FILE_CHANGED = 0xc0000197, - NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xc0000198, - NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xc0000199, - NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT = 0xc000019a, - NT_STATUS_DOMAIN_TRUST_INCONSISTENT = 0xc000019b, - NT_STATUS_FS_DRIVER_REQUIRED = 0xc000019c, - NT_STATUS_NO_USER_SESSION_KEY = 0xc0000202, - NT_STATUS_USER_SESSION_DELETED = 0xc0000203, - NT_STATUS_RESOURCE_LANG_NOT_FOUND = 0xc0000204, - NT_STATUS_INSUFF_SERVER_RESOURCES = 0xc0000205, - NT_STATUS_INVALID_BUFFER_SIZE = 0xc0000206, - NT_STATUS_INVALID_ADDRESS_COMPONENT = 0xc0000207, - NT_STATUS_INVALID_ADDRESS_WILDCARD = 0xc0000208, - NT_STATUS_TOO_MANY_ADDRESSES = 0xc0000209, - NT_STATUS_ADDRESS_ALREADY_EXISTS = 0xc000020a, - NT_STATUS_ADDRESS_CLOSED = 0xc000020b, - NT_STATUS_CONNECTION_DISCONNECTED = 0xc000020c, - NT_STATUS_CONNECTION_RESET = 0xc000020d, - NT_STATUS_TOO_MANY_NODES = 0xc000020e, - NT_STATUS_TRANSACTION_ABORTED = 0xc000020f, - NT_STATUS_TRANSACTION_TIMED_OUT = 0xc0000210, - NT_STATUS_TRANSACTION_NO_RELEASE = 0xc0000211, - NT_STATUS_TRANSACTION_NO_MATCH = 0xc0000212, - NT_STATUS_TRANSACTION_RESPONDED = 0xc0000213, - NT_STATUS_TRANSACTION_INVALID_ID = 0xc0000214, - NT_STATUS_TRANSACTION_INVALID_TYPE = 0xc0000215, - NT_STATUS_NOT_SERVER_SESSION = 0xc0000216, - NT_STATUS_NOT_CLIENT_SESSION = 0xc0000217, - NT_STATUS_CANNOT_LOAD_REGISTRY_FILE = 0xc0000218, - NT_STATUS_DEBUG_ATTACH_FAILED = 0xc0000219, - NT_STATUS_SYSTEM_PROCESS_TERMINATED = 0xc000021a, - NT_STATUS_DATA_NOT_ACCEPTED = 0xc000021b, - NT_STATUS_NO_BROWSER_SERVERS_FOUND = 0xc000021c, - NT_STATUS_VDM_HARD_ERROR = 0xc000021d, - NT_STATUS_DRIVER_CANCEL_TIMEOUT = 0xc000021e, - NT_STATUS_REPLY_MESSAGE_MISMATCH = 0xc000021f, - NT_STATUS_MAPPED_ALIGNMENT = 0xc0000220, - NT_STATUS_IMAGE_CHECKSUM_MISMATCH = 0xc0000221, - NT_STATUS_LOST_WRITEBEHIND_DATA = 0xc0000222, - NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID = 0xc0000223, - NT_STATUS_PASSWORD_MUST_CHANGE = 0xc0000224, - NT_STATUS_NOT_FOUND = 0xc0000225, - NT_STATUS_NOT_TINY_STREAM = 0xc0000226, - NT_STATUS_RECOVERY_FAILURE = 0xc0000227, - NT_STATUS_STACK_OVERFLOW_READ = 0xc0000228, - NT_STATUS_FAIL_CHECK = 0xc0000229, - NT_STATUS_DUPLICATE_OBJECTID = 0xc000022a, - NT_STATUS_OBJECTID_EXISTS = 0xc000022b, - NT_STATUS_CONVERT_TO_LARGE = 0xc000022c, - NT_STATUS_RETRY = 0xc000022d, - NT_STATUS_FOUND_OUT_OF_SCOPE = 0xc000022e, - NT_STATUS_ALLOCATE_BUCKET = 0xc000022f, - NT_STATUS_PROPSET_NOT_FOUND = 0xc0000230, - NT_STATUS_MARSHALL_OVERFLOW = 0xc0000231, - NT_STATUS_INVALID_VARIANT = 0xc0000232, - NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND = 0xc0000233, - NT_STATUS_ACCOUNT_LOCKED_OUT = 0xc0000234, - NT_STATUS_HANDLE_NOT_CLOSABLE = 0xc0000235, - NT_STATUS_CONNECTION_REFUSED = 0xc0000236, - NT_STATUS_GRACEFUL_DISCONNECT = 0xc0000237, - NT_STATUS_ADDRESS_ALREADY_ASSOCIATED = 0xc0000238, - NT_STATUS_ADDRESS_NOT_ASSOCIATED = 0xc0000239, - NT_STATUS_CONNECTION_INVALID = 0xc000023a, - NT_STATUS_CONNECTION_ACTIVE = 0xc000023b, - NT_STATUS_NETWORK_UNREACHABLE = 0xc000023c, - NT_STATUS_HOST_UNREACHABLE = 0xc000023d, - NT_STATUS_PROTOCOL_UNREACHABLE = 0xc000023e, - NT_STATUS_PORT_UNREACHABLE = 0xc000023f, - NT_STATUS_REQUEST_ABORTED = 0xc0000240, - NT_STATUS_CONNECTION_ABORTED = 0xc0000241, - NT_STATUS_BAD_COMPRESSION_BUFFER = 0xc0000242, - NT_STATUS_USER_MAPPED_FILE = 0xc0000243, - NT_STATUS_AUDIT_FAILED = 0xc0000244, - NT_STATUS_TIMER_RESOLUTION_NOT_SET = 0xc0000245, - NT_STATUS_CONNECTION_COUNT_LIMIT = 0xc0000246, - NT_STATUS_LOGIN_TIME_RESTRICTION = 0xc0000247, - NT_STATUS_LOGIN_WKSTA_RESTRICTION = 0xc0000248, - NT_STATUS_IMAGE_MP_UP_MISMATCH = 0xc0000249, - NT_STATUS_INSUFFICIENT_LOGON_INFO = 0xc0000250, - NT_STATUS_BAD_DLL_ENTRYPOINT = 0xc0000251, - NT_STATUS_BAD_SERVICE_ENTRYPOINT = 0xc0000252, - NT_STATUS_LPC_REPLY_LOST = 0xc0000253, - NT_STATUS_IP_ADDRESS_CONFLICT1 = 0xc0000254, - NT_STATUS_IP_ADDRESS_CONFLICT2 = 0xc0000255, - NT_STATUS_REGISTRY_QUOTA_LIMIT = 0xc0000256, - NT_STATUS_PATH_NOT_COVERED = 0xc0000257, - NT_STATUS_NO_CALLBACK_ACTIVE = 0xc0000258, - NT_STATUS_LICENSE_QUOTA_EXCEEDED = 0xc0000259, - NT_STATUS_PWD_TOO_SHORT = 0xc000025a, - NT_STATUS_PWD_TOO_RECENT = 0xc000025b, - NT_STATUS_PWD_HISTORY_CONFLICT = 0xc000025c, - NT_STATUS_PLUGPLAY_NO_DEVICE = 0xc000025e, - NT_STATUS_UNSUPPORTED_COMPRESSION = 0xc000025f, - NT_STATUS_INVALID_HW_PROFILE = 0xc0000260, - NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH = 0xc0000261, - NT_STATUS_DRIVER_ORDINAL_NOT_FOUND = 0xc0000262, - NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND = 0xc0000263, - NT_STATUS_RESOURCE_NOT_OWNED = 0xc0000264, - NT_STATUS_TOO_MANY_LINKS = 0xc0000265, - NT_STATUS_QUOTA_LIST_INCONSISTENT = 0xc0000266, - NT_STATUS_FILE_IS_OFFLINE = 0xc0000267, - NT_STATUS_DS_NO_MORE_RIDS = 0xc00002a8, - NT_STATUS_NOT_A_REPARSE_POINT = 0xc0000275, - NT_STATUS_NO_SUCH_JOB = 0xc000EDE + NT_STATUS_SUCCESS = 0x00000000, + NT_STATUS_WERR_BADFILE = 0x00000002, + NT_STATUS_WERR_ACCESS_DENIED = 0x00000005, + NT_STATUS_WERR_INVALID_PARAMETER = 0x00000057, + NT_STATUS_WERR_INVALID_NAME = 0x0000007b, + NT_STATUS_WERR_UNKNOWN_LEVEL = 0x0000007c, + NT_STATUS_WERR_MORE_DATA = 0x000000ea, + NT_STATUS_NO_MORE_ITEMS = 0x00000103, + NT_STATUS_MORE_ENTRIES = 0x00000105, + NT_STATUS_SOME_NOT_MAPPED = 0x00000107, + NT_STATUS_SERVICE_REQUEST_TIMEOUT = 0x0000041D, + NT_STATUS_SERVICE_NO_THREAD = 0x0000041E, + NT_STATUS_SERVICE_DATABASE_LOCKED = 0x0000041F, + NT_STATUS_SERVICE_ALREADY_RUNNING = 0x00000420, + NT_STATUS_INVALID_SERVICE_ACCOUNT = 0x00000421, + NT_STATUS_SERVICE_DISABLED = 0x00000422, + NT_STATUS_CIRCULAR_DEPENDENCY = 0x00000423, + NT_STATUS_SERVICE_DOES_NOT_EXIST = 0x00000424, + NT_STATUS_SERVICE_CANNOT_ACCEPT_CTRL = 0x00000425, + NT_STATUS_SERVICE_NOT_ACTIVE = 0x00000426, + NT_STATUS_FAILED_SERVICE_CONTROLLER_CONNECT = 0x00000427, + NT_STATUS_EXCEPTION_IN_SERVICE = 0x00000428, + NT_STATUS_DATABASE_DOES_NOT_EXIST = 0x00000429, + NT_STATUS_SERVICE_SPECIFIC_ERROR = 0x0000042a, + NT_STATUS_PROCESS_ABORTED = 0x0000042b, + NT_STATUS_SERVICE_DEPENDENCY_FAIL = 0x0000042c, + NT_STATUS_SERVICE_LOGON_FAILED = 0x0000042d, + NT_STATUS_SERVICE_START_HANG = 0x0000042e, + NT_STATUS_INVALID_SERVICE_LOCK = 0x0000042f, + NT_STATUS_SERVICE_MARKED_FOR_DELETE = 0x00000430, + NT_STATUS_SERVICE_EXISTS = 0x00000431, + NT_STATUS_ALREADY_RUNNING_LKG = 0x00000432, + NT_STATUS_SERVICE_DEPENDENCY_DELETED = 0x00000433, + NT_STATUS_BOOT_ALREADY_ACCEPTED = 0x00000434, + NT_STATUS_SERVICE_NEVER_STARTED = 0x00000435, + NT_STATUS_DUPLICATE_SERVICE_NAME = 0x00000436, + NT_STATUS_DIFFERENT_SERVICE_ACCOUNT = 0x00000437, + NT_STATUS_CANNOT_DETECT_DRIVER_FAILURE = 0x00000438, + DOS_STATUS_UNKNOWN_ERROR = 0x00010001, + DOS_STATUS_NONSPECIFIC_ERROR = 0x00010002, + DOS_STATUS_DIRECTORY_NOT_FOUND = 0x00030001, + DOS_STATUS_ACCESS_DENIED = 0x00050001, + DOS_STATUS_INVALID_FID = 0x00060001, + DOS_STATUS_INVALID_NETWORK_NAME = 0x00060002, + NT_STATUS_BUFFER_OVERFLOW = 0x80000005, + NT_STATUS_UNSUCCESSFUL = 0xc0000001, + NT_STATUS_NOT_IMPLEMENTED = 0xc0000002, + NT_STATUS_INVALID_INFO_CLASS = 0xc0000003, + NT_STATUS_INFO_LENGTH_MISMATCH = 0xc0000004, + NT_STATUS_ACCESS_VIOLATION = 0xc0000005, + NT_STATUS_IN_PAGE_ERROR = 0xc0000006, + NT_STATUS_PAGEFILE_QUOTA = 0xc0000007, + NT_STATUS_INVALID_HANDLE = 0xc0000008, + NT_STATUS_BAD_INITIAL_STACK = 0xc0000009, + NT_STATUS_BAD_INITIAL_PC = 0xc000000a, + NT_STATUS_INVALID_CID = 0xc000000b, + NT_STATUS_TIMER_NOT_CANCELED = 0xc000000c, + NT_STATUS_INVALID_PARAMETER = 0xc000000d, + NT_STATUS_NO_SUCH_DEVICE = 0xc000000e, + NT_STATUS_NO_SUCH_FILE = 0xc000000f, + NT_STATUS_INVALID_DEVICE_REQUEST = 0xc0000010, + NT_STATUS_END_OF_FILE = 0xc0000011, + NT_STATUS_WRONG_VOLUME = 0xc0000012, + NT_STATUS_NO_MEDIA_IN_DEVICE = 0xc0000013, + NT_STATUS_UNRECOGNIZED_MEDIA = 0xc0000014, + NT_STATUS_NONEXISTENT_SECTOR = 0xc0000015, + NT_STATUS_MORE_PROCESSING_REQUIRED = 0xc0000016, + NT_STATUS_NO_MEMORY = 0xc0000017, + NT_STATUS_CONFLICTING_ADDRESSES = 0xc0000018, + NT_STATUS_NOT_MAPPED_VIEW = 0xc0000019, + NT_STATUS_UNABLE_TO_FREE_VM = 0xc000001a, + NT_STATUS_UNABLE_TO_DELETE_SECTION = 0xc000001b, + NT_STATUS_INVALID_SYSTEM_SERVICE = 0xc000001c, + NT_STATUS_ILLEGAL_INSTRUCTION = 0xc000001d, + NT_STATUS_INVALID_LOCK_SEQUENCE = 0xc000001e, + NT_STATUS_INVALID_VIEW_SIZE = 0xc000001f, + NT_STATUS_INVALID_FILE_FOR_SECTION = 0xc0000020, + NT_STATUS_ALREADY_COMMITTED = 0xc0000021, + NT_STATUS_ACCESS_DENIED = 0xc0000022, + NT_STATUS_BUFFER_TOO_SMALL = 0xc0000023, + NT_STATUS_OBJECT_TYPE_MISMATCH = 0xc0000024, + NT_STATUS_NONCONTINUABLE_EXCEPTION = 0xc0000025, + NT_STATUS_INVALID_DISPOSITION = 0xc0000026, + NT_STATUS_UNWIND = 0xc0000027, + NT_STATUS_BAD_STACK = 0xc0000028, + NT_STATUS_INVALID_UNWIND_TARGET = 0xc0000029, + NT_STATUS_NOT_LOCKED = 0xc000002a, + NT_STATUS_PARITY_ERROR = 0xc000002b, + NT_STATUS_UNABLE_TO_DECOMMIT_VM = 0xc000002c, + NT_STATUS_NOT_COMMITTED = 0xc000002d, + NT_STATUS_INVALID_PORT_ATTRIBUTES = 0xc000002e, + NT_STATUS_PORT_MESSAGE_TOO_LONG = 0xc000002f, + NT_STATUS_INVALID_PARAMETER_MIX = 0xc0000030, + NT_STATUS_INVALID_QUOTA_LOWER = 0xc0000031, + NT_STATUS_DISK_CORRUPT_ERROR = 0xc0000032, + NT_STATUS_OBJECT_NAME_INVALID = 0xc0000033, + NT_STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034, + NT_STATUS_OBJECT_NAME_COLLISION = 0xc0000035, + NT_STATUS_HANDLE_NOT_WAITABLE = 0xc0000036, + NT_STATUS_PORT_DISCONNECTED = 0xc0000037, + NT_STATUS_DEVICE_ALREADY_ATTACHED = 0xc0000038, + NT_STATUS_OBJECT_PATH_INVALID = 0xc0000039, + NT_STATUS_OBJECT_PATH_NOT_FOUND = 0xc000003a, + NT_STATUS_OBJECT_PATH_SYNTAX_BAD = 0xc000003b, + NT_STATUS_DATA_OVERRUN = 0xc000003c, + NT_STATUS_DATA_LATE_ERROR = 0xc000003d, + NT_STATUS_DATA_ERROR = 0xc000003e, + NT_STATUS_CRC_ERROR = 0xc000003f, + NT_STATUS_SECTION_TOO_BIG = 0xc0000040, + NT_STATUS_PORT_CONNECTION_REFUSED = 0xc0000041, + NT_STATUS_INVALID_PORT_HANDLE = 0xc0000042, + NT_STATUS_SHARING_VIOLATION = 0xc0000043, + NT_STATUS_QUOTA_EXCEEDED = 0xc0000044, + NT_STATUS_INVALID_PAGE_PROTECTION = 0xc0000045, + NT_STATUS_MUTANT_NOT_OWNED = 0xc0000046, + NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED = 0xc0000047, + NT_STATUS_PORT_ALREADY_SET = 0xc0000048, + NT_STATUS_SECTION_NOT_IMAGE = 0xc0000049, + NT_STATUS_SUSPEND_COUNT_EXCEEDED = 0xc000004a, + NT_STATUS_THREAD_IS_TERMINATING = 0xc000004b, + NT_STATUS_BAD_WORKING_SET_LIMIT = 0xc000004c, + NT_STATUS_INCOMPATIBLE_FILE_MAP = 0xc000004d, + NT_STATUS_SECTION_PROTECTION = 0xc000004e, + NT_STATUS_EAS_NOT_SUPPORTED = 0xc000004f, + NT_STATUS_EA_TOO_LARGE = 0xc0000050, + NT_STATUS_NONEXISTENT_EA_ENTRY = 0xc0000051, + NT_STATUS_NO_EAS_ON_FILE = 0xc0000052, + NT_STATUS_EA_CORRUPT_ERROR = 0xc0000053, + NT_STATUS_FILE_LOCK_CONFLICT = 0xc0000054, + NT_STATUS_LOCK_NOT_GRANTED = 0xc0000055, + NT_STATUS_DELETE_PENDING = 0xc0000056, + NT_STATUS_CTL_FILE_NOT_SUPPORTED = 0xc0000057, + NT_STATUS_UNKNOWN_REVISION = 0xc0000058, + NT_STATUS_REVISION_MISMATCH = 0xc0000059, + NT_STATUS_INVALID_OWNER = 0xc000005a, + NT_STATUS_INVALID_PRIMARY_GROUP = 0xc000005b, + NT_STATUS_NO_IMPERSONATION_TOKEN = 0xc000005c, + NT_STATUS_CANT_DISABLE_MANDATORY = 0xc000005d, + NT_STATUS_NO_LOGON_SERVERS = 0xc000005e, + NT_STATUS_NO_SUCH_LOGON_SESSION = 0xc000005f, + NT_STATUS_NO_SUCH_PRIVILEGE = 0xc0000060, + NT_STATUS_PRIVILEGE_NOT_HELD = 0xc0000061, + NT_STATUS_INVALID_ACCOUNT_NAME = 0xc0000062, + NT_STATUS_USER_EXISTS = 0xc0000063, + NT_STATUS_NO_SUCH_USER = 0xc0000064, + NT_STATUS_GROUP_EXISTS = 0xc0000065, + NT_STATUS_NO_SUCH_GROUP = 0xc0000066, + NT_STATUS_MEMBER_IN_GROUP = 0xc0000067, + NT_STATUS_MEMBER_NOT_IN_GROUP = 0xc0000068, + NT_STATUS_LAST_ADMIN = 0xc0000069, + NT_STATUS_WRONG_PASSWORD = 0xc000006a, + NT_STATUS_ILL_FORMED_PASSWORD = 0xc000006b, + NT_STATUS_PASSWORD_RESTRICTION = 0xc000006c, + NT_STATUS_LOGON_FAILURE = 0xc000006d, + NT_STATUS_ACCOUNT_RESTRICTION = 0xc000006e, + NT_STATUS_INVALID_LOGON_HOURS = 0xc000006f, + NT_STATUS_INVALID_WORKSTATION = 0xc0000070, + NT_STATUS_PASSWORD_EXPIRED = 0xc0000071, + NT_STATUS_ACCOUNT_DISABLED = 0xc0000072, + NT_STATUS_NONE_MAPPED = 0xc0000073, + NT_STATUS_TOO_MANY_LUIDS_REQUESTED = 0xc0000074, + NT_STATUS_LUIDS_EXHAUSTED = 0xc0000075, + NT_STATUS_INVALID_SUB_AUTHORITY = 0xc0000076, + NT_STATUS_INVALID_ACL = 0xc0000077, + NT_STATUS_INVALID_SID = 0xc0000078, + NT_STATUS_INVALID_SECURITY_DESCR = 0xc0000079, + NT_STATUS_PROCEDURE_NOT_FOUND = 0xc000007a, + NT_STATUS_INVALID_IMAGE_FORMAT = 0xc000007b, + NT_STATUS_NO_TOKEN = 0xc000007c, + NT_STATUS_BAD_INHERITANCE_ACL = 0xc000007d, + NT_STATUS_RANGE_NOT_LOCKED = 0xc000007e, + NT_STATUS_DISK_FULL = 0xc000007f, + NT_STATUS_SERVER_DISABLED = 0xc0000080, + NT_STATUS_SERVER_NOT_DISABLED = 0xc0000081, + NT_STATUS_TOO_MANY_GUIDS_REQUESTED = 0xc0000082, + NT_STATUS_GUIDS_EXHAUSTED = 0xc0000083, + NT_STATUS_INVALID_ID_AUTHORITY = 0xc0000084, + NT_STATUS_AGENTS_EXHAUSTED = 0xc0000085, + NT_STATUS_INVALID_VOLUME_LABEL = 0xc0000086, + NT_STATUS_SECTION_NOT_EXTENDED = 0xc0000087, + NT_STATUS_NOT_MAPPED_DATA = 0xc0000088, + NT_STATUS_RESOURCE_DATA_NOT_FOUND = 0xc0000089, + NT_STATUS_RESOURCE_TYPE_NOT_FOUND = 0xc000008a, + NT_STATUS_RESOURCE_NAME_NOT_FOUND = 0xc000008b, + NT_STATUS_ARRAY_BOUNDS_EXCEEDED = 0xc000008c, + NT_STATUS_FLOAT_DENORMAL_OPERAND = 0xc000008d, + NT_STATUS_FLOAT_DIVIDE_BY_ZERO = 0xc000008e, + NT_STATUS_FLOAT_INEXACT_RESULT = 0xc000008f, + NT_STATUS_FLOAT_INVALID_OPERATION = 0xc0000090, + NT_STATUS_FLOAT_OVERFLOW = 0xc0000091, + NT_STATUS_FLOAT_STACK_CHECK = 0xc0000092, + NT_STATUS_FLOAT_UNDERFLOW = 0xc0000093, + NT_STATUS_INTEGER_DIVIDE_BY_ZERO = 0xc0000094, + NT_STATUS_INTEGER_OVERFLOW = 0xc0000095, + NT_STATUS_PRIVILEGED_INSTRUCTION = 0xc0000096, + NT_STATUS_TOO_MANY_PAGING_FILES = 0xc0000097, + NT_STATUS_FILE_INVALID = 0xc0000098, + NT_STATUS_ALLOTTED_SPACE_EXCEEDED = 0xc0000099, + NT_STATUS_INSUFFICIENT_RESOURCES = 0xc000009a, + NT_STATUS_DFS_EXIT_PATH_FOUND = 0xc000009b, + NT_STATUS_DEVICE_DATA_ERROR = 0xc000009c, + NT_STATUS_DEVICE_NOT_CONNECTED = 0xc000009d, + NT_STATUS_DEVICE_POWER_FAILURE = 0xc000009e, + NT_STATUS_FREE_VM_NOT_AT_BASE = 0xc000009f, + NT_STATUS_MEMORY_NOT_ALLOCATED = 0xc00000a0, + NT_STATUS_WORKING_SET_QUOTA = 0xc00000a1, + NT_STATUS_MEDIA_WRITE_PROTECTED = 0xc00000a2, + NT_STATUS_DEVICE_NOT_READY = 0xc00000a3, + NT_STATUS_INVALID_GROUP_ATTRIBUTES = 0xc00000a4, + NT_STATUS_BAD_IMPERSONATION_LEVEL = 0xc00000a5, + NT_STATUS_CANT_OPEN_ANONYMOUS = 0xc00000a6, + NT_STATUS_BAD_VALIDATION_CLASS = 0xc00000a7, + NT_STATUS_BAD_TOKEN_TYPE = 0xc00000a8, + NT_STATUS_BAD_MASTER_BOOT_RECORD = 0xc00000a9, + NT_STATUS_INSTRUCTION_MISALIGNMENT = 0xc00000aa, + NT_STATUS_INSTANCE_NOT_AVAILABLE = 0xc00000ab, + NT_STATUS_PIPE_NOT_AVAILABLE = 0xc00000ac, + NT_STATUS_INVALID_PIPE_STATE = 0xc00000ad, + NT_STATUS_PIPE_BUSY = 0xc00000ae, + NT_STATUS_ILLEGAL_FUNCTION = 0xc00000af, + NT_STATUS_PIPE_DISCONNECTED = 0xc00000b0, + NT_STATUS_PIPE_CLOSING = 0xc00000b1, + NT_STATUS_PIPE_CONNECTED = 0xc00000b2, + NT_STATUS_PIPE_LISTENING = 0xc00000b3, + NT_STATUS_INVALID_READ_MODE = 0xc00000b4, + NT_STATUS_IO_TIMEOUT = 0xc00000b5, + NT_STATUS_FILE_FORCED_CLOSED = 0xc00000b6, + NT_STATUS_PROFILING_NOT_STARTED = 0xc00000b7, + NT_STATUS_PROFILING_NOT_STOPPED = 0xc00000b8, + NT_STATUS_COULD_NOT_INTERPRET = 0xc00000b9, + NT_STATUS_FILE_IS_A_DIRECTORY = 0xc00000ba, + NT_STATUS_NOT_SUPPORTED = 0xc00000bb, + NT_STATUS_REMOTE_NOT_LISTENING = 0xc00000bc, + NT_STATUS_DUPLICATE_NAME = 0xc00000bd, + NT_STATUS_BAD_NETWORK_PATH = 0xc00000be, + NT_STATUS_NETWORK_BUSY = 0xc00000bf, + NT_STATUS_DEVICE_DOES_NOT_EXIST = 0xc00000c0, + NT_STATUS_TOO_MANY_COMMANDS = 0xc00000c1, + NT_STATUS_ADAPTER_HARDWARE_ERROR = 0xc00000c2, + NT_STATUS_INVALID_NETWORK_RESPONSE = 0xc00000c3, + NT_STATUS_UNEXPECTED_NETWORK_ERROR = 0xc00000c4, + NT_STATUS_BAD_REMOTE_ADAPTER = 0xc00000c5, + NT_STATUS_PRINT_QUEUE_FULL = 0xc00000c6, + NT_STATUS_NO_SPOOL_SPACE = 0xc00000c7, + NT_STATUS_PRINT_CANCELLED = 0xc00000c8, + NT_STATUS_NETWORK_NAME_DELETED = 0xc00000c9, + NT_STATUS_NETWORK_ACCESS_DENIED = 0xc00000ca, + NT_STATUS_BAD_DEVICE_TYPE = 0xc00000cb, + NT_STATUS_BAD_NETWORK_NAME = 0xc00000cc, + NT_STATUS_TOO_MANY_NAMES = 0xc00000cd, + NT_STATUS_TOO_MANY_SESSIONS = 0xc00000ce, + NT_STATUS_SHARING_PAUSED = 0xc00000cf, + NT_STATUS_REQUEST_NOT_ACCEPTED = 0xc00000d0, + NT_STATUS_REDIRECTOR_PAUSED = 0xc00000d1, + NT_STATUS_NET_WRITE_FAULT = 0xc00000d2, + NT_STATUS_PROFILING_AT_LIMIT = 0xc00000d3, + NT_STATUS_NOT_SAME_DEVICE = 0xc00000d4, + NT_STATUS_FILE_RENAMED = 0xc00000d5, + NT_STATUS_VIRTUAL_CIRCUIT_CLOSED = 0xc00000d6, + NT_STATUS_NO_SECURITY_ON_OBJECT = 0xc00000d7, + NT_STATUS_CANT_WAIT = 0xc00000d8, + NT_STATUS_PIPE_EMPTY = 0xc00000d9, + NT_STATUS_CANT_ACCESS_DOMAIN_INFO = 0xc00000da, + NT_STATUS_CANT_TERMINATE_SELF = 0xc00000db, + NT_STATUS_INVALID_SERVER_STATE = 0xc00000dc, + NT_STATUS_INVALID_DOMAIN_STATE = 0xc00000dd, + NT_STATUS_INVALID_DOMAIN_ROLE = 0xc00000de, + NT_STATUS_NO_SUCH_DOMAIN = 0xc00000df, + NT_STATUS_DOMAIN_EXISTS = 0xc00000e0, + NT_STATUS_DOMAIN_LIMIT_EXCEEDED = 0xc00000e1, + NT_STATUS_OPLOCK_NOT_GRANTED = 0xc00000e2, + NT_STATUS_INVALID_OPLOCK_PROTOCOL = 0xc00000e3, + NT_STATUS_INTERNAL_DB_CORRUPTION = 0xc00000e4, + NT_STATUS_INTERNAL_ERROR = 0xc00000e5, + NT_STATUS_GENERIC_NOT_MAPPED = 0xc00000e6, + NT_STATUS_BAD_DESCRIPTOR_FORMAT = 0xc00000e7, + NT_STATUS_INVALID_USER_BUFFER = 0xc00000e8, + NT_STATUS_UNEXPECTED_IO_ERROR = 0xc00000e9, + NT_STATUS_UNEXPECTED_MM_CREATE_ERR = 0xc00000ea, + NT_STATUS_UNEXPECTED_MM_MAP_ERROR = 0xc00000eb, + NT_STATUS_UNEXPECTED_MM_EXTEND_ERR = 0xc00000ec, + NT_STATUS_NOT_LOGON_PROCESS = 0xc00000ed, + NT_STATUS_LOGON_SESSION_EXISTS = 0xc00000ee, + NT_STATUS_INVALID_PARAMETER_1 = 0xc00000ef, + NT_STATUS_INVALID_PARAMETER_2 = 0xc00000f0, + NT_STATUS_INVALID_PARAMETER_3 = 0xc00000f1, + NT_STATUS_INVALID_PARAMETER_4 = 0xc00000f2, + NT_STATUS_INVALID_PARAMETER_5 = 0xc00000f3, + NT_STATUS_INVALID_PARAMETER_6 = 0xc00000f4, + NT_STATUS_INVALID_PARAMETER_7 = 0xc00000f5, + NT_STATUS_INVALID_PARAMETER_8 = 0xc00000f6, + NT_STATUS_INVALID_PARAMETER_9 = 0xc00000f7, + NT_STATUS_INVALID_PARAMETER_10 = 0xc00000f8, + NT_STATUS_INVALID_PARAMETER_11 = 0xc00000f9, + NT_STATUS_INVALID_PARAMETER_12 = 0xc00000fa, + NT_STATUS_REDIRECTOR_NOT_STARTED = 0xc00000fb, + NT_STATUS_REDIRECTOR_STARTED = 0xc00000fc, + NT_STATUS_STACK_OVERFLOW = 0xc00000fd, + NT_STATUS_NO_SUCH_PACKAGE = 0xc00000fe, + NT_STATUS_BAD_FUNCTION_TABLE = 0xc00000ff, + NT_STATUS_DIRECTORY_NOT_EMPTY = 0xc0000101, + NT_STATUS_FILE_CORRUPT_ERROR = 0xc0000102, + NT_STATUS_NOT_A_DIRECTORY = 0xc0000103, + NT_STATUS_BAD_LOGON_SESSION_STATE = 0xc0000104, + NT_STATUS_LOGON_SESSION_COLLISION = 0xc0000105, + NT_STATUS_NAME_TOO_LONG = 0xc0000106, + NT_STATUS_FILES_OPEN = 0xc0000107, + NT_STATUS_CONNECTION_IN_USE = 0xc0000108, + NT_STATUS_MESSAGE_NOT_FOUND = 0xc0000109, + NT_STATUS_PROCESS_IS_TERMINATING = 0xc000010a, + NT_STATUS_INVALID_LOGON_TYPE = 0xc000010b, + NT_STATUS_NO_GUID_TRANSLATION = 0xc000010c, + NT_STATUS_CANNOT_IMPERSONATE = 0xc000010d, + NT_STATUS_IMAGE_ALREADY_LOADED = 0xc000010e, + NT_STATUS_ABIOS_NOT_PRESENT = 0xc000010f, + NT_STATUS_ABIOS_LID_NOT_EXIST = 0xc0000110, + NT_STATUS_ABIOS_LID_ALREADY_OWNED = 0xc0000111, + NT_STATUS_ABIOS_NOT_LID_OWNER = 0xc0000112, + NT_STATUS_ABIOS_INVALID_COMMAND = 0xc0000113, + NT_STATUS_ABIOS_INVALID_LID = 0xc0000114, + NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE = 0xc0000115, + NT_STATUS_ABIOS_INVALID_SELECTOR = 0xc0000116, + NT_STATUS_NO_LDT = 0xc0000117, + NT_STATUS_INVALID_LDT_SIZE = 0xc0000118, + NT_STATUS_INVALID_LDT_OFFSET = 0xc0000119, + NT_STATUS_INVALID_LDT_DESCRIPTOR = 0xc000011a, + NT_STATUS_INVALID_IMAGE_NE_FORMAT = 0xc000011b, + NT_STATUS_RXACT_INVALID_STATE = 0xc000011c, + NT_STATUS_RXACT_COMMIT_FAILURE = 0xc000011d, + NT_STATUS_MAPPED_FILE_SIZE_ZERO = 0xc000011e, + NT_STATUS_TOO_MANY_OPENED_FILES = 0xc000011f, + NT_STATUS_CANCELLED = 0xc0000120, + NT_STATUS_CANNOT_DELETE = 0xc0000121, + NT_STATUS_INVALID_COMPUTER_NAME = 0xc0000122, + NT_STATUS_FILE_DELETED = 0xc0000123, + NT_STATUS_SPECIAL_ACCOUNT = 0xc0000124, + NT_STATUS_SPECIAL_GROUP = 0xc0000125, + NT_STATUS_SPECIAL_USER = 0xc0000126, + NT_STATUS_MEMBERS_PRIMARY_GROUP = 0xc0000127, + NT_STATUS_FILE_CLOSED = 0xc0000128, + NT_STATUS_TOO_MANY_THREADS = 0xc0000129, + NT_STATUS_THREAD_NOT_IN_PROCESS = 0xc000012a, + NT_STATUS_TOKEN_ALREADY_IN_USE = 0xc000012b, + NT_STATUS_PAGEFILE_QUOTA_EXCEEDED = 0xc000012c, + NT_STATUS_COMMITMENT_LIMIT = 0xc000012d, + NT_STATUS_INVALID_IMAGE_LE_FORMAT = 0xc000012e, + NT_STATUS_INVALID_IMAGE_NOT_MZ = 0xc000012f, + NT_STATUS_INVALID_IMAGE_PROTECT = 0xc0000130, + NT_STATUS_INVALID_IMAGE_WIN_16 = 0xc0000131, + NT_STATUS_LOGON_SERVER_CONFLICT = 0xc0000132, + NT_STATUS_TIME_DIFFERENCE_AT_DC = 0xc0000133, + NT_STATUS_SYNCHRONIZATION_REQUIRED = 0xc0000134, + NT_STATUS_DLL_NOT_FOUND = 0xc0000135, + NT_STATUS_OPEN_FAILED = 0xc0000136, + NT_STATUS_IO_PRIVILEGE_FAILED = 0xc0000137, + NT_STATUS_ORDINAL_NOT_FOUND = 0xc0000138, + NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xc0000139, + NT_STATUS_CONTROL_C_EXIT = 0xc000013a, + NT_STATUS_LOCAL_DISCONNECT = 0xc000013b, + NT_STATUS_REMOTE_DISCONNECT = 0xc000013c, + NT_STATUS_REMOTE_RESOURCES = 0xc000013d, + NT_STATUS_LINK_FAILED = 0xc000013e, + NT_STATUS_LINK_TIMEOUT = 0xc000013f, + NT_STATUS_INVALID_CONNECTION = 0xc0000140, + NT_STATUS_INVALID_ADDRESS = 0xc0000141, + NT_STATUS_DLL_INIT_FAILED = 0xc0000142, + NT_STATUS_MISSING_SYSTEMFILE = 0xc0000143, + NT_STATUS_UNHANDLED_EXCEPTION = 0xc0000144, + NT_STATUS_APP_INIT_FAILURE = 0xc0000145, + NT_STATUS_PAGEFILE_CREATE_FAILED = 0xc0000146, + NT_STATUS_NO_PAGEFILE = 0xc0000147, + NT_STATUS_INVALID_LEVEL = 0xc0000148, + NT_STATUS_WRONG_PASSWORD_CORE = 0xc0000149, + NT_STATUS_ILLEGAL_FLOAT_CONTEXT = 0xc000014a, + NT_STATUS_PIPE_BROKEN = 0xc000014b, + NT_STATUS_REGISTRY_CORRUPT = 0xc000014c, + NT_STATUS_REGISTRY_IO_FAILED = 0xc000014d, + NT_STATUS_NO_EVENT_PAIR = 0xc000014e, + NT_STATUS_UNRECOGNIZED_VOLUME = 0xc000014f, + NT_STATUS_SERIAL_NO_DEVICE_INITED = 0xc0000150, + NT_STATUS_NO_SUCH_ALIAS = 0xc0000151, + NT_STATUS_MEMBER_NOT_IN_ALIAS = 0xc0000152, + NT_STATUS_MEMBER_IN_ALIAS = 0xc0000153, + NT_STATUS_ALIAS_EXISTS = 0xc0000154, + NT_STATUS_LOGON_NOT_GRANTED = 0xc0000155, + NT_STATUS_TOO_MANY_SECRETS = 0xc0000156, + NT_STATUS_SECRET_TOO_LONG = 0xc0000157, + NT_STATUS_INTERNAL_DB_ERROR = 0xc0000158, + NT_STATUS_FULLSCREEN_MODE = 0xc0000159, + NT_STATUS_TOO_MANY_CONTEXT_IDS = 0xc000015a, + NT_STATUS_LOGON_TYPE_NOT_GRANTED = 0xc000015b, + NT_STATUS_NOT_REGISTRY_FILE = 0xc000015c, + NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED = 0xc000015d, + NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR = 0xc000015e, + NT_STATUS_FT_MISSING_MEMBER = 0xc000015f, + NT_STATUS_ILL_FORMED_SERVICE_ENTRY = 0xc0000160, + NT_STATUS_ILLEGAL_CHARACTER = 0xc0000161, + NT_STATUS_UNMAPPABLE_CHARACTER = 0xc0000162, + NT_STATUS_UNDEFINED_CHARACTER = 0xc0000163, + NT_STATUS_FLOPPY_VOLUME = 0xc0000164, + NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND = 0xc0000165, + NT_STATUS_FLOPPY_WRONG_CYLINDER = 0xc0000166, + NT_STATUS_FLOPPY_UNKNOWN_ERROR = 0xc0000167, + NT_STATUS_FLOPPY_BAD_REGISTERS = 0xc0000168, + NT_STATUS_DISK_RECALIBRATE_FAILED = 0xc0000169, + NT_STATUS_DISK_OPERATION_FAILED = 0xc000016a, + NT_STATUS_DISK_RESET_FAILED = 0xc000016b, + NT_STATUS_SHARED_IRQ_BUSY = 0xc000016c, + NT_STATUS_FT_ORPHANING = 0xc000016d, + NT_STATUS_PARTITION_FAILURE = 0xc0000172, + NT_STATUS_INVALID_BLOCK_LENGTH = 0xc0000173, + NT_STATUS_DEVICE_NOT_PARTITIONED = 0xc0000174, + NT_STATUS_UNABLE_TO_LOCK_MEDIA = 0xc0000175, + NT_STATUS_UNABLE_TO_UNLOAD_MEDIA = 0xc0000176, + NT_STATUS_EOM_OVERFLOW = 0xc0000177, + NT_STATUS_NO_MEDIA = 0xc0000178, + NT_STATUS_NO_SUCH_MEMBER = 0xc000017a, + NT_STATUS_INVALID_MEMBER = 0xc000017b, + NT_STATUS_KEY_DELETED = 0xc000017c, + NT_STATUS_NO_LOG_SPACE = 0xc000017d, + NT_STATUS_TOO_MANY_SIDS = 0xc000017e, + NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED = 0xc000017f, + NT_STATUS_KEY_HAS_CHILDREN = 0xc0000180, + NT_STATUS_CHILD_MUST_BE_VOLATILE = 0xc0000181, + NT_STATUS_DEVICE_CONFIGURATION_ERROR = 0xc0000182, + NT_STATUS_DRIVER_INTERNAL_ERROR = 0xc0000183, + NT_STATUS_INVALID_DEVICE_STATE = 0xc0000184, + NT_STATUS_IO_DEVICE_ERROR = 0xc0000185, + NT_STATUS_DEVICE_PROTOCOL_ERROR = 0xc0000186, + NT_STATUS_BACKUP_CONTROLLER = 0xc0000187, + NT_STATUS_LOG_FILE_FULL = 0xc0000188, + NT_STATUS_TOO_LATE = 0xc0000189, + NT_STATUS_NO_TRUST_LSA_SECRET = 0xc000018a, + NT_STATUS_NO_TRUST_SAM_ACCOUNT = 0xc000018b, + NT_STATUS_TRUSTED_DOMAIN_FAILURE = 0xc000018c, + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE = 0xc000018d, + NT_STATUS_EVENTLOG_FILE_CORRUPT = 0xc000018e, + NT_STATUS_EVENTLOG_CANT_START = 0xc000018f, + NT_STATUS_TRUST_FAILURE = 0xc0000190, + NT_STATUS_MUTANT_LIMIT_EXCEEDED = 0xc0000191, + NT_STATUS_NETLOGON_NOT_STARTED = 0xc0000192, + NT_STATUS_ACCOUNT_EXPIRED = 0xc0000193, + NT_STATUS_POSSIBLE_DEADLOCK = 0xc0000194, + NT_STATUS_NETWORK_CREDENTIAL_CONFLICT = 0xc0000195, + NT_STATUS_REMOTE_SESSION_LIMIT = 0xc0000196, + NT_STATUS_EVENTLOG_FILE_CHANGED = 0xc0000197, + NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xc0000198, + NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xc0000199, + NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT = 0xc000019a, + NT_STATUS_DOMAIN_TRUST_INCONSISTENT = 0xc000019b, + NT_STATUS_FS_DRIVER_REQUIRED = 0xc000019c, + NT_STATUS_NO_USER_SESSION_KEY = 0xc0000202, + NT_STATUS_USER_SESSION_DELETED = 0xc0000203, + NT_STATUS_RESOURCE_LANG_NOT_FOUND = 0xc0000204, + NT_STATUS_INSUFF_SERVER_RESOURCES = 0xc0000205, + NT_STATUS_INVALID_BUFFER_SIZE = 0xc0000206, + NT_STATUS_INVALID_ADDRESS_COMPONENT = 0xc0000207, + NT_STATUS_INVALID_ADDRESS_WILDCARD = 0xc0000208, + NT_STATUS_TOO_MANY_ADDRESSES = 0xc0000209, + NT_STATUS_ADDRESS_ALREADY_EXISTS = 0xc000020a, + NT_STATUS_ADDRESS_CLOSED = 0xc000020b, + NT_STATUS_CONNECTION_DISCONNECTED = 0xc000020c, + NT_STATUS_CONNECTION_RESET = 0xc000020d, + NT_STATUS_TOO_MANY_NODES = 0xc000020e, + NT_STATUS_TRANSACTION_ABORTED = 0xc000020f, + NT_STATUS_TRANSACTION_TIMED_OUT = 0xc0000210, + NT_STATUS_TRANSACTION_NO_RELEASE = 0xc0000211, + NT_STATUS_TRANSACTION_NO_MATCH = 0xc0000212, + NT_STATUS_TRANSACTION_RESPONDED = 0xc0000213, + NT_STATUS_TRANSACTION_INVALID_ID = 0xc0000214, + NT_STATUS_TRANSACTION_INVALID_TYPE = 0xc0000215, + NT_STATUS_NOT_SERVER_SESSION = 0xc0000216, + NT_STATUS_NOT_CLIENT_SESSION = 0xc0000217, + NT_STATUS_CANNOT_LOAD_REGISTRY_FILE = 0xc0000218, + NT_STATUS_DEBUG_ATTACH_FAILED = 0xc0000219, + NT_STATUS_SYSTEM_PROCESS_TERMINATED = 0xc000021a, + NT_STATUS_DATA_NOT_ACCEPTED = 0xc000021b, + NT_STATUS_NO_BROWSER_SERVERS_FOUND = 0xc000021c, + NT_STATUS_VDM_HARD_ERROR = 0xc000021d, + NT_STATUS_DRIVER_CANCEL_TIMEOUT = 0xc000021e, + NT_STATUS_REPLY_MESSAGE_MISMATCH = 0xc000021f, + NT_STATUS_MAPPED_ALIGNMENT = 0xc0000220, + NT_STATUS_IMAGE_CHECKSUM_MISMATCH = 0xc0000221, + NT_STATUS_LOST_WRITEBEHIND_DATA = 0xc0000222, + NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID = 0xc0000223, + NT_STATUS_PASSWORD_MUST_CHANGE = 0xc0000224, + NT_STATUS_NOT_FOUND = 0xc0000225, + NT_STATUS_NOT_TINY_STREAM = 0xc0000226, + NT_STATUS_RECOVERY_FAILURE = 0xc0000227, + NT_STATUS_STACK_OVERFLOW_READ = 0xc0000228, + NT_STATUS_FAIL_CHECK = 0xc0000229, + NT_STATUS_DUPLICATE_OBJECTID = 0xc000022a, + NT_STATUS_OBJECTID_EXISTS = 0xc000022b, + NT_STATUS_CONVERT_TO_LARGE = 0xc000022c, + NT_STATUS_RETRY = 0xc000022d, + NT_STATUS_FOUND_OUT_OF_SCOPE = 0xc000022e, + NT_STATUS_ALLOCATE_BUCKET = 0xc000022f, + NT_STATUS_PROPSET_NOT_FOUND = 0xc0000230, + NT_STATUS_MARSHALL_OVERFLOW = 0xc0000231, + NT_STATUS_INVALID_VARIANT = 0xc0000232, + NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND = 0xc0000233, + NT_STATUS_ACCOUNT_LOCKED_OUT = 0xc0000234, + NT_STATUS_HANDLE_NOT_CLOSABLE = 0xc0000235, + NT_STATUS_CONNECTION_REFUSED = 0xc0000236, + NT_STATUS_GRACEFUL_DISCONNECT = 0xc0000237, + NT_STATUS_ADDRESS_ALREADY_ASSOCIATED = 0xc0000238, + NT_STATUS_ADDRESS_NOT_ASSOCIATED = 0xc0000239, + NT_STATUS_CONNECTION_INVALID = 0xc000023a, + NT_STATUS_CONNECTION_ACTIVE = 0xc000023b, + NT_STATUS_NETWORK_UNREACHABLE = 0xc000023c, + NT_STATUS_HOST_UNREACHABLE = 0xc000023d, + NT_STATUS_PROTOCOL_UNREACHABLE = 0xc000023e, + NT_STATUS_PORT_UNREACHABLE = 0xc000023f, + NT_STATUS_REQUEST_ABORTED = 0xc0000240, + NT_STATUS_CONNECTION_ABORTED = 0xc0000241, + NT_STATUS_BAD_COMPRESSION_BUFFER = 0xc0000242, + NT_STATUS_USER_MAPPED_FILE = 0xc0000243, + NT_STATUS_AUDIT_FAILED = 0xc0000244, + NT_STATUS_TIMER_RESOLUTION_NOT_SET = 0xc0000245, + NT_STATUS_CONNECTION_COUNT_LIMIT = 0xc0000246, + NT_STATUS_LOGIN_TIME_RESTRICTION = 0xc0000247, + NT_STATUS_LOGIN_WKSTA_RESTRICTION = 0xc0000248, + NT_STATUS_IMAGE_MP_UP_MISMATCH = 0xc0000249, + NT_STATUS_INSUFFICIENT_LOGON_INFO = 0xc0000250, + NT_STATUS_BAD_DLL_ENTRYPOINT = 0xc0000251, + NT_STATUS_BAD_SERVICE_ENTRYPOINT = 0xc0000252, + NT_STATUS_LPC_REPLY_LOST = 0xc0000253, + NT_STATUS_IP_ADDRESS_CONFLICT1 = 0xc0000254, + NT_STATUS_IP_ADDRESS_CONFLICT2 = 0xc0000255, + NT_STATUS_REGISTRY_QUOTA_LIMIT = 0xc0000256, + NT_STATUS_PATH_NOT_COVERED = 0xc0000257, + NT_STATUS_NO_CALLBACK_ACTIVE = 0xc0000258, + NT_STATUS_LICENSE_QUOTA_EXCEEDED = 0xc0000259, + NT_STATUS_PWD_TOO_SHORT = 0xc000025a, + NT_STATUS_PWD_TOO_RECENT = 0xc000025b, + NT_STATUS_PWD_HISTORY_CONFLICT = 0xc000025c, + NT_STATUS_PLUGPLAY_NO_DEVICE = 0xc000025e, + NT_STATUS_UNSUPPORTED_COMPRESSION = 0xc000025f, + NT_STATUS_INVALID_HW_PROFILE = 0xc0000260, + NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH = 0xc0000261, + NT_STATUS_DRIVER_ORDINAL_NOT_FOUND = 0xc0000262, + NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND = 0xc0000263, + NT_STATUS_RESOURCE_NOT_OWNED = 0xc0000264, + NT_STATUS_TOO_MANY_LINKS = 0xc0000265, + NT_STATUS_QUOTA_LIST_INCONSISTENT = 0xc0000266, + NT_STATUS_FILE_IS_OFFLINE = 0xc0000267, + NT_STATUS_DS_NO_MORE_RIDS = 0xc00002a8, + NT_STATUS_NOT_A_REPARSE_POINT = 0xc0000275, + NT_STATUS_NO_SUCH_JOB = 0xc000EDE } for i, v in pairs(status_codes) do - status_names[v] = i + status_names[v] = i end @@ -4114,171 +4114,171 @@ local NP_LIBRARY_NAME = "PIPE" namedpipes = { - get_pipe_subpath = function( pipeName, writeToDebugLog ) - local status, pipeSubPath - if not pipeName then return false end + get_pipe_subpath = function( pipeName, writeToDebugLog ) + local status, pipeSubPath + if not pipeName then return false end - local _, _, match = pipeName:match( "^(\\+)(.-)\\pipe(\\.-)$" ) - if match then - pipeSubPath = match - status = true - if writeToDebugLog then - stdnse.print_debug( 2, "%s: Converting %s to subpath %s", NP_LIBRARY_NAME, pipeName, match ) - end - else - status = false - pipeSubPath = pipeName - end + local _, _, match = pipeName:match( "^(\\+)(.-)\\pipe(\\.-)$" ) + if match then + pipeSubPath = match + status = true + if writeToDebugLog then + stdnse.print_debug( 2, "%s: Converting %s to subpath %s", NP_LIBRARY_NAME, pipeName, match ) + end + else + status = false + pipeSubPath = pipeName + end - return status, pipeSubPath - end, + return status, pipeSubPath + end, - make_pipe_name = function( hostnameOrIp, pipeSubPath ) - if pipeSubPath:sub(1,1) ~= "\\" then - pipeSubPath = "\\" .. pipeSubPath - end + make_pipe_name = function( hostnameOrIp, pipeSubPath ) + if pipeSubPath:sub(1,1) ~= "\\" then + pipeSubPath = "\\" .. pipeSubPath + end - return string.format( "\\\\%s\\pipe%s", hostnameOrIp, pipeSubPath ) - end, + return string.format( "\\\\%s\\pipe%s", hostnameOrIp, pipeSubPath ) + end, - named_pipe = { + named_pipe = { - _smbstate = nil, - _host = nil, - _pipeSubPath = nil, - _overrides = nil, - name = nil, + _smbstate = nil, + _host = nil, + _pipeSubPath = nil, + _overrides = nil, + name = nil, - new = function(self,o) - o = o or {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self,o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o + end, - connect = function( self, host, pipeSubPath, overrides ) + connect = function( self, host, pipeSubPath, overrides ) - stdnse.print_debug( 2, "%s: connect() called with %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) ) - self._overrides = overrides or {} - self._host = host - self._pipeSubPath = pipeSubPath - if not host and not host.ip then return false, "host table is required" end - if not pipeSubPath then return false, "pipeSubPath is required" end + stdnse.print_debug( 2, "%s: connect() called with %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) ) + self._overrides = overrides or {} + self._host = host + self._pipeSubPath = pipeSubPath + if not host and not host.ip then return false, "host table is required" end + if not pipeSubPath then return false, "pipeSubPath is required" end - -- If we got a full pipe name, not a sub-path, fix it - if ( pipeSubPath:match( "^\\\\(.-)$" ) ) then - local status - status, self._pipeSubPath = namedpipes.get_pipe_subpath( self._pipeSubPath, true ) - if ( not status ) then - stdnse.print_debug( 1, "%s: Attempt to connect to invalid pipe name: %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) ) - return false, "Invalid pipe name" - end - end - self.name = namedpipes.make_pipe_name( self._host.ip, self._pipeSubPath ) + -- If we got a full pipe name, not a sub-path, fix it + if ( pipeSubPath:match( "^\\\\(.-)$" ) ) then + local status + status, self._pipeSubPath = namedpipes.get_pipe_subpath( self._pipeSubPath, true ) + if ( not status ) then + stdnse.print_debug( 1, "%s: Attempt to connect to invalid pipe name: %s", NP_LIBRARY_NAME, tostring( pipeSubPath ) ) + return false, "Invalid pipe name" + end + end + self.name = namedpipes.make_pipe_name( self._host.ip, self._pipeSubPath ) - stdnse.print_debug( 2, "%s: Connecting to named pipe: %s", NP_LIBRARY_NAME, self.name ) - local status, result, errorMessage - local bool_negotiate_protocol, bool_start_session, bool_disable_extended = true, true, false - status, result = start_ex( self._host, bool_negotiate_protocol, bool_start_session, - "IPC$", self._pipeSubPath, bool_disable_extended, self._overrides ) + stdnse.print_debug( 2, "%s: Connecting to named pipe: %s", NP_LIBRARY_NAME, self.name ) + local status, result, errorMessage + local bool_negotiate_protocol, bool_start_session, bool_disable_extended = true, true, false + status, result = start_ex( self._host, bool_negotiate_protocol, bool_start_session, + "IPC$", self._pipeSubPath, bool_disable_extended, self._overrides ) - if status then - self._smbstate = result - else - errorMessage = string.format( "Connection failed: %s", result ) - stdnse.print_debug( 2, "%s: Connection to named pipe (%s) failed: %s", - NP_LIBRARY_NAME, self.name, errorMessage ) - end + if status then + self._smbstate = result + else + errorMessage = string.format( "Connection failed: %s", result ) + stdnse.print_debug( 2, "%s: Connection to named pipe (%s) failed: %s", + NP_LIBRARY_NAME, self.name, errorMessage ) + end - return status, errorMessage, result - end, + return status, errorMessage, result + end, - disconnect = function( self ) - if ( self._smbstate ) then - stdnse.print_debug( 2, "%s: Disconnecting named pipe: %s", NP_LIBRARY_NAME, self.name ) - return stop( self._smbstate ) - else - stdnse.print_debug( 2, "%s: disconnect() called, but SMB connection is already closed: %s", NP_LIBRARY_NAME, self.name ) - end - end, + disconnect = function( self ) + if ( self._smbstate ) then + stdnse.print_debug( 2, "%s: Disconnecting named pipe: %s", NP_LIBRARY_NAME, self.name ) + return stop( self._smbstate ) + else + stdnse.print_debug( 2, "%s: disconnect() called, but SMB connection is already closed: %s", NP_LIBRARY_NAME, self.name ) + end + end, - send = function( self, messageData ) - if not self._smbstate then - stdnse.print_debug( 2, "%s: send() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name ) - return false, "Failed to send message on named pipe" - end + send = function( self, messageData ) + if not self._smbstate then + stdnse.print_debug( 2, "%s: send() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name ) + return false, "Failed to send message on named pipe" + end - local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity - local status, result, errorMessage + local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity + local status, result, errorMessage - status, result = write_file( self._smbstate, messageData, offset, self._overrides ) + status, result = write_file( self._smbstate, messageData, offset, self._overrides ) - -- if status is true, result is data that we don't need to pay attention to - if not status then - stdnse.print_debug( 2, "%s: Write to named pipe (%s) failed: %s", - NP_LIBRARY_NAME, self.name, result ) - errorMessage = "Failed to send message on named pipe", result - end + -- if status is true, result is data that we don't need to pay attention to + if not status then + stdnse.print_debug( 2, "%s: Write to named pipe (%s) failed: %s", + NP_LIBRARY_NAME, self.name, result ) + errorMessage = "Failed to send message on named pipe", result + end - return status, errorMessage - end, + return status, errorMessage + end, - receive = function( self ) - if not self._smbstate then - stdnse.print_debug( 2, "%s: receive() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name ) - return false, "Failed to read from named pipe" - end + receive = function( self ) + if not self._smbstate then + stdnse.print_debug( 2, "%s: receive() called on closed pipe (%s)", NP_LIBRARY_NAME, self.name ) + return false, "Failed to read from named pipe" + end - local status, result, messageData - -- Packet header values - local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity - local MAX_BYTES_PER_READ = 4096 + local status, result, messageData + -- Packet header values + local offset = 0 -- offset is actually ignored for named pipes, but we'll define the argument for clarity + local MAX_BYTES_PER_READ = 4096 - status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides ) + status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides ) - if status and result.data then - messageData = result.data - else - stdnse.print_debug( 2, "%s: Read from named pipe (%s) failed: %s", - NP_LIBRARY_NAME, self.name, result ) - return false, "Failed to read from named pipe", result - end + if status and result.data then + messageData = result.data + else + stdnse.print_debug( 2, "%s: Read from named pipe (%s) failed: %s", + NP_LIBRARY_NAME, self.name, result ) + return false, "Failed to read from named pipe", result + end - while (result["status"] == status_codes.NT_STATUS_BUFFER_OVERFLOW) do - status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides ) + while (result["status"] == status_codes.NT_STATUS_BUFFER_OVERFLOW) do + status, result = read_file( self._smbstate, offset, MAX_BYTES_PER_READ, self._overrides ) - if status and result.data then - messageData = messageData .. result.data - else - stdnse.print_debug( 2, "%s: Read additional data from named pipe (%s) failed: %s", - NP_LIBRARY_NAME, self.name, result ) - return false, "Failed to read from named pipe", result - end - end + if status and result.data then + messageData = messageData .. result.data + else + stdnse.print_debug( 2, "%s: Read additional data from named pipe (%s) failed: %s", + NP_LIBRARY_NAME, self.name, result ) + return false, "Failed to read from named pipe", result + end + end - return status, messageData - end, - } + return status, messageData + end, + } } filetype_codes = { - FILE_TYPE_DISK = 0x00, - FILE_TYPE_BYTE_MODE_PIPE = 0x01, - FILE_TYPE_MESSAGE_MODE_PIPE = 0x02, - FILE_TYPE_PRINTER = 0x03, - FILE_TYPE_UNKNOWN = 0xFF + FILE_TYPE_DISK = 0x00, + FILE_TYPE_BYTE_MODE_PIPE = 0x01, + FILE_TYPE_MESSAGE_MODE_PIPE = 0x02, + FILE_TYPE_PRINTER = 0x03, + FILE_TYPE_UNKNOWN = 0xFF } for i, v in pairs(filetype_codes) do - filetype_names[v] = i + filetype_names[v] = i end return _ENV; diff --git a/nselib/smbauth.lua b/nselib/smbauth.lua index ed68eccf9..0db8cd4e5 100644 --- a/nselib/smbauth.lua +++ b/nselib/smbauth.lua @@ -98,36 +98,36 @@ local session_key = string.rep(string.char(0x00), 16) -- Types of accounts (ordered by how useful they are local ACCOUNT_TYPES = { - ANONYMOUS = 0, - GUEST = 1, - USER = 2, - ADMIN = 3 + ANONYMOUS = 0, + GUEST = 1, + USER = 2, + ADMIN = 3 } local function account_exists(host, username, domain) - if(host.registry['smbaccounts'] == nil) then - return false - end + if(host.registry['smbaccounts'] == nil) then + return false + end - for i, j in pairs(host.registry['smbaccounts']) do - if(j['username'] == username and j['domain'] == domain) then - return true - end - end + for i, j in pairs(host.registry['smbaccounts']) do + if(j['username'] == username and j['domain'] == domain) then + return true + end + end - return false + return false end function next_account(host, num) - if(num == nil) then - if(host.registry['smbindex'] == nil) then - host.registry['smbindex'] = 1 - else - host.registry['smbindex'] = host.registry['smbindex'] + 1 - end - else - host.registry['smbindex'] = num - end + if(num == nil) then + if(host.registry['smbindex'] == nil) then + host.registry['smbindex'] = 1 + else + host.registry['smbindex'] = host.registry['smbindex'] + 1 + end + else + host.registry['smbindex'] = num + end end ---Writes the given account to the registry. There are several places where accounts are stored: @@ -148,71 +148,71 @@ end --@param hash_type The hash type to use. --@param is_admin [optional] Set to 'true' the account is known to be an administrator. function add_account(host, username, domain, password, password_hash, hash_type, is_admin) - -- Save the username in a global list -- TODO: restore this --- if(nmap.registry.usernames == nil) then --- nmap.registry.usernames = {} --- end --- nmap.registry.usernames[username] = true --- --- -- Save the username/password pair in a global list --- if(nmap.registry.smbaccounts == nil) then --- nmap.registry.smbaccounts = {} --- end --- nmap.registry.smbaccounts[username] = password + -- Save the username in a global list -- TODO: restore this + -- if(nmap.registry.usernames == nil) then + -- nmap.registry.usernames = {} + -- end + -- nmap.registry.usernames[username] = true + -- + -- -- Save the username/password pair in a global list + -- if(nmap.registry.smbaccounts == nil) then + -- nmap.registry.smbaccounts = {} + -- end + -- nmap.registry.smbaccounts[username] = password - -- Check if we've already recorded this account - if(account_exists(host, username, domain)) then - return - end + -- Check if we've already recorded this account + if(account_exists(host, username, domain)) then + return + end - if(host.registry['smbaccounts'] == nil) then - host.registry['smbaccounts'] = {} - end + if(host.registry['smbaccounts'] == nil) then + host.registry['smbaccounts'] = {} + end - -- Determine the type of account, if it wasn't given - local account_type = nil - if(is_admin) then - account_type = ACCOUNT_TYPES.ADMIN - else - if(username == '') then - -- Anonymous account - account_type = ACCOUNT_TYPES.ANONYMOUS - elseif(string.lower(username) == 'guest') then - -- Guest account - account_type = ACCOUNT_TYPES.GUEST - else - -- We have to assume it's a user-level account (we just can't call any SMB functions from inside here) - account_type = ACCOUNT_TYPES.USER - end - end + -- Determine the type of account, if it wasn't given + local account_type = nil + if(is_admin) then + account_type = ACCOUNT_TYPES.ADMIN + else + if(username == '') then + -- Anonymous account + account_type = ACCOUNT_TYPES.ANONYMOUS + elseif(string.lower(username) == 'guest') then + -- Guest account + account_type = ACCOUNT_TYPES.GUEST + else + -- We have to assume it's a user-level account (we just can't call any SMB functions from inside here) + account_type = ACCOUNT_TYPES.USER + end + end - -- Set some defaults - if(hash_type == nil) then - hash_type = 'ntlm' - end + -- Set some defaults + if(hash_type == nil) then + hash_type = 'ntlm' + end - -- Save the new account if this is our first one, or our other account isn't an admin - local new_entry = {} - new_entry['username'] = username - new_entry['domain'] = domain - new_entry['password'] = password - new_entry['password_hash'] = password_hash - new_entry['hash_type'] = string.lower(hash_type) - new_entry['account_type'] = account_type + -- Save the new account if this is our first one, or our other account isn't an admin + local new_entry = {} + new_entry['username'] = username + new_entry['domain'] = domain + new_entry['password'] = password + new_entry['password_hash'] = password_hash + new_entry['hash_type'] = string.lower(hash_type) + new_entry['account_type'] = account_type - -- Insert the new entry into the table - table.insert(host.registry['smbaccounts'], new_entry) + -- Insert the new entry into the table + table.insert(host.registry['smbaccounts'], new_entry) - -- Sort the table based on the account type (we want anonymous at the end, administrator at the front) - table.sort(host.registry['smbaccounts'], function(a,b) return a['account_type'] > b['account_type'] end) + -- Sort the table based on the account type (we want anonymous at the end, administrator at the front) + table.sort(host.registry['smbaccounts'], function(a,b) return a['account_type'] > b['account_type'] end) - -- Print a debug message - stdnse.print_debug(1, "SMB: Added account '%s' to account list", username) + -- Print a debug message + stdnse.print_debug(1, "SMB: Added account '%s' to account list", username) - -- Reset the credentials - next_account(host, 1) + -- Reset the credentials + next_account(host, 1) --- io.write("\n\n" .. nsedebug.tostr(host.registry['smbaccounts']) .. "\n\n") + -- io.write("\n\n" .. nsedebug.tostr(host.registry['smbaccounts']) .. "\n\n") end ---Retrieve the current set of credentials set in the registry. If these fail, next_credentials should be @@ -222,18 +222,18 @@ end --@return (result, username, domain, password, password_hash, hash_type) If result is false, username is an error message. Otherwise, username and password are -- the current username and password that should be used. function get_account(host) - if(host.registry['smbindex'] == nil) then - host.registry['smbindex'] = 1 - end + if(host.registry['smbindex'] == nil) then + host.registry['smbindex'] = 1 + end - local index = host.registry['smbindex'] - local account = host.registry['smbaccounts'][index] + local index = host.registry['smbindex'] + local account = host.registry['smbaccounts'][index] - if(account == nil) then - return false, "No accounts left to try" - end + if(account == nil) then + return false, "No accounts left to try" + end - return true, account['username'], account['domain'], account['password'], account['password_hash'], account['hash_type'] + return true, account['username'], account['domain'], account['password'], account['password_hash'], account['hash_type'] end ---Create the account table with the anonymous and guest users, as well as the user given in the script's @@ -241,88 +241,88 @@ end -- --@param host The host object. function init_account(host) - -- Don't run this more than once for each host - if(host.registry['smbaccounts'] ~= nil) then - return - end + -- Don't run this more than once for each host + if(host.registry['smbaccounts'] ~= nil) then + return + end - -- Create the list - host.registry['smbaccounts'] = {} + -- Create the list + host.registry['smbaccounts'] = {} - -- Add the anonymous/guest accounts - add_account(host, '', '', '', nil, 'none') + -- Add the anonymous/guest accounts + add_account(host, '', '', '', nil, 'none') - if(not stdnse.get_script_args( "smbnoguest" )) then - add_account(host, 'guest', '', '', nil, 'ntlm') - end + if(not stdnse.get_script_args( "smbnoguest" )) then + add_account(host, 'guest', '', '', nil, 'ntlm') + end - -- Add the account given on the commandline (TODO: allow more than one?) - local args = nmap.registry.args - local username = nil - local domain = '' - local password = nil - local password_hash = nil - local hash_type = 'ntlm' + -- Add the account given on the commandline (TODO: allow more than one?) + local args = nmap.registry.args + local username = nil + local domain = '' + local password = nil + local password_hash = nil + local hash_type = 'ntlm' - -- Do the username first - if(args.smbusername ~= nil) then - username = args.smbusername - elseif(args.smbuser ~= nil) then - username = args.smbuser - end + -- Do the username first + if(args.smbusername ~= nil) then + username = args.smbusername + elseif(args.smbuser ~= nil) then + username = args.smbuser + end - -- If the username exists, do everything else - if(username ~= nil) then - -- Domain - if(args.smbdomain ~= nil) then - domain = args.smbdomain - end + -- If the username exists, do everything else + if(username ~= nil) then + -- Domain + if(args.smbdomain ~= nil) then + domain = args.smbdomain + end - -- Type - if(args.smbtype ~= nil) then - hash_type = args.smbtype - end + -- Type + if(args.smbtype ~= nil) then + hash_type = args.smbtype + end - -- Do the password - if(args.smbpassword ~= nil) then - password = args.smbpassword - elseif(args.smbpass ~= nil) then - password = args.smbpass - end + -- Do the password + if(args.smbpassword ~= nil) then + password = args.smbpassword + elseif(args.smbpass ~= nil) then + password = args.smbpass + end - -- Only use the hash if there's no password - if(password == nil) then - password_hash = args.smbhash - end + -- Only use the hash if there's no password + if(password == nil) then + password_hash = args.smbhash + end - -- Add the account, if we got a password - if(password == nil and password_hash == nil) then - stdnse.print_debug(1, "SMB: Either smbpass, smbpassword, or smbhash have to be passed as script arguments to use an account") - else - add_account(host, username, domain, password, password_hash, hash_type) - end - end + -- Add the account, if we got a password + if(password == nil and password_hash == nil) then + stdnse.print_debug(1, "SMB: Either smbpass, smbpassword, or smbhash have to be passed as script arguments to use an account") + else + add_account(host, username, domain, password, password_hash, hash_type) + end + end end local function to_unicode(str) - local unicode = "" + local unicode = "" - for i = 1, #str, 1 do - unicode = unicode .. bin.pack("NTLMv2 function, @@ -503,11 +503,11 @@ end --@param challenge The server challenge. --@return (status, response) If status is true, the response is returned; otherwise, an error message is returned. function lmv2_create_response(ntlm, username, domain, challenge) - if(have_ssl ~= true) then - return false, "SMB: OpenSSL not present" - end + if(have_ssl ~= true) then + return false, "SMB: OpenSSL not present" + end - return ntlmv2_create_response(ntlm, username, domain, challenge, 8) + return ntlmv2_create_response(ntlm, username, domain, challenge, 8) end ---Create the NTLMv2 response, which can be sent back to the server. This is done by using the HMAC-MD5 algorithm @@ -520,15 +520,15 @@ end -- guaranteed to be much longer than 24 bytes. So, I just use a random string generated by OpenSSL. I've tested -- it on every Windows system from Windows 2000 to Windows Vista, and it has always worked. function ntlmv2_create_response(ntlm, username, domain, challenge, client_challenge_length) - if(have_ssl ~= true) then - return false, "SMB: OpenSSL not present" - end + if(have_ssl ~= true) then + return false, "SMB: OpenSSL not present" + end - local client_challenge = openssl.rand_bytes(client_challenge_length) + local client_challenge = openssl.rand_bytes(client_challenge_length) - local status, ntlmv2_hash = ntlmv2_create_hash(ntlm, username, domain) + local status, ntlmv2_hash = ntlmv2_create_hash(ntlm, username, domain) - return true, openssl.hmac("MD5", ntlmv2_hash, challenge .. client_challenge) .. client_challenge + return true, openssl.hmac("MD5", ntlmv2_hash, challenge .. client_challenge) .. client_challenge end ---Generate the Lanman and NTLM password hashes. The password itself is taken from the function parameters, @@ -549,262 +549,262 @@ end --@return (lm_response, ntlm_response, mac_key) The two strings that can be sent directly back to the server, -- and the mac_key, which is used for message signing. function get_password_response(ip, username, domain, password, password_hash, hash_type, challenge, is_extended) - local status - local lm_hash = nil - local ntlm_hash = nil - local mac_key = nil - local lm_response, ntlm_response + local status + local lm_hash = nil + local ntlm_hash = nil + local mac_key = nil + local lm_response, ntlm_response - -- Check for a blank password - if(password == nil and password_hash == nil) then - stdnse.print_debug(2, "SMB: Couldn't find password or hash to use (assuming blank)") - password = "" - end + -- Check for a blank password + if(password == nil and password_hash == nil) then + stdnse.print_debug(2, "SMB: Couldn't find password or hash to use (assuming blank)") + password = "" + end - -- The anonymous user requires a single 0-byte instead of a LANMAN hash (don't ask me why, but it doesn't work without) - if(hash_type == 'none') then - return string.char(0), '', nil - end + -- The anonymous user requires a single 0-byte instead of a LANMAN hash (don't ask me why, but it doesn't work without) + if(hash_type == 'none') then + return string.char(0), '', nil + end - -- If we got a password, hash it - if(password ~= nil) then - status, lm_hash = lm_create_hash(password) - status, ntlm_hash = ntlm_create_hash(password) - else - if(password_hash ~= nil) then - if(string.find(password_hash, "^" .. string.rep("%x%x", 16) .. "$")) then - stdnse.print_debug(2, "SMB: Found a 16-byte hex string") - lm_hash = bin.pack("H", password_hash:sub(1, 32)) - ntlm_hash = bin.pack("H", password_hash:sub(1, 32)) - elseif(string.find(password_hash, "^" .. string.rep("%x%x", 32) .. "$")) then - stdnse.print_debug(2, "SMB: Found a 32-byte hex string") - lm_hash = bin.pack("H", password_hash:sub(1, 32)) - ntlm_hash = bin.pack("H", password_hash:sub(33, 64)) - elseif(string.find(password_hash, "^" .. string.rep("%x%x", 16) .. "." .. string.rep("%x%x", 16) .. "$")) then - stdnse.print_debug(2, "SMB: Found two 16-byte hex strings") - lm_hash = bin.pack("H", password_hash:sub(1, 32)) - ntlm_hash = bin.pack("H", password_hash:sub(34, 65)) - else - stdnse.print_debug(1, "SMB: ERROR: Hash(es) provided in an invalid format (should be 32, 64, or 65 hex characters)") - lm_hash = nil - ntlm_hash = nil - end - end - end + -- If we got a password, hash it + if(password ~= nil) then + status, lm_hash = lm_create_hash(password) + status, ntlm_hash = ntlm_create_hash(password) + else + if(password_hash ~= nil) then + if(string.find(password_hash, "^" .. string.rep("%x%x", 16) .. "$")) then + stdnse.print_debug(2, "SMB: Found a 16-byte hex string") + lm_hash = bin.pack("H", password_hash:sub(1, 32)) + ntlm_hash = bin.pack("H", password_hash:sub(1, 32)) + elseif(string.find(password_hash, "^" .. string.rep("%x%x", 32) .. "$")) then + stdnse.print_debug(2, "SMB: Found a 32-byte hex string") + lm_hash = bin.pack("H", password_hash:sub(1, 32)) + ntlm_hash = bin.pack("H", password_hash:sub(33, 64)) + elseif(string.find(password_hash, "^" .. string.rep("%x%x", 16) .. "." .. string.rep("%x%x", 16) .. "$")) then + stdnse.print_debug(2, "SMB: Found two 16-byte hex strings") + lm_hash = bin.pack("H", password_hash:sub(1, 32)) + ntlm_hash = bin.pack("H", password_hash:sub(34, 65)) + else + stdnse.print_debug(1, "SMB: ERROR: Hash(es) provided in an invalid format (should be 32, 64, or 65 hex characters)") + lm_hash = nil + ntlm_hash = nil + end + end + end - -- At this point, we should have a good lm_hash and ntlm_hash if we're getting one - if(lm_hash == nil or ntlm_hash == nil) then - stdnse.print_debug(2, "SMB: Couldn't determine which password to use, using a blank one") - return "", "" - end + -- At this point, we should have a good lm_hash and ntlm_hash if we're getting one + if(lm_hash == nil or ntlm_hash == nil) then + stdnse.print_debug(2, "SMB: Couldn't determine which password to use, using a blank one") + return "", "" + end - -- Output what we've got so far - stdnse.print_debug(2, "SMB: Lanman hash: %s", stdnse.tohex(lm_hash)) - stdnse.print_debug(2, "SMB: NTLM hash: %s", stdnse.tohex(ntlm_hash)) + -- Output what we've got so far + stdnse.print_debug(2, "SMB: Lanman hash: %s", stdnse.tohex(lm_hash)) + stdnse.print_debug(2, "SMB: NTLM hash: %s", stdnse.tohex(ntlm_hash)) - -- Hash the password the way the user wants - if(hash_type == "v1") then - -- LM and NTLM are hashed with their respective algorithms - stdnse.print_debug(2, "SMB: Creating v1 response") - status, lm_response = lm_create_response(lm_hash, challenge) - status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) + -- Hash the password the way the user wants + if(hash_type == "v1") then + -- LM and NTLM are hashed with their respective algorithms + stdnse.print_debug(2, "SMB: Creating v1 response") + status, lm_response = lm_create_response(lm_hash, challenge) + status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) - mac_key = ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) + mac_key = ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) - elseif(hash_type == "lm") then - -- LM is hashed with its algorithm, NTLM is blank - stdnse.print_debug(2, "SMB: Creating LMv1 response") - status, lm_response = lm_create_response(lm_hash, challenge) - ntlm_response = "" + elseif(hash_type == "lm") then + -- LM is hashed with its algorithm, NTLM is blank + stdnse.print_debug(2, "SMB: Creating LMv1 response") + status, lm_response = lm_create_response(lm_hash, challenge) + ntlm_response = "" - mac_key = lm_create_mac_key(lm_hash, lm_response, is_extended) + mac_key = lm_create_mac_key(lm_hash, lm_response, is_extended) - elseif(hash_type == "ntlm") then - -- LM and NTLM both use the NTLM algorithm - stdnse.print_debug(2, "SMB: Creating NTLMv1 response") - status, lm_response = ntlm_create_response(ntlm_hash, challenge) - status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) + elseif(hash_type == "ntlm") then + -- LM and NTLM both use the NTLM algorithm + stdnse.print_debug(2, "SMB: Creating NTLMv1 response") + status, lm_response = ntlm_create_response(ntlm_hash, challenge) + status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) - mac_key = ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) + mac_key = ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended) - elseif(hash_type == "v2") then - -- LM and NTLM are hashed with their respective v2 algorithms - stdnse.print_debug(2, "SMB: Creating v2 response") - status, lm_response = lmv2_create_response(ntlm_hash, username, domain, challenge) - status, ntlm_response = ntlmv2_create_response(ntlm_hash, username, domain, challenge, 24) + elseif(hash_type == "v2") then + -- LM and NTLM are hashed with their respective v2 algorithms + stdnse.print_debug(2, "SMB: Creating v2 response") + status, lm_response = lmv2_create_response(ntlm_hash, username, domain, challenge) + status, ntlm_response = ntlmv2_create_response(ntlm_hash, username, domain, challenge, 24) - elseif(hash_type == "lmv2") then - -- LM is hashed with its v2 algorithm, NTLM is blank - stdnse.print_debug(2, "SMB: Creating LMv2 response") - status, lm_response = lmv2_create_response(ntlm_hash, username, domain, challenge) - ntlm_response = "" + elseif(hash_type == "lmv2") then + -- LM is hashed with its v2 algorithm, NTLM is blank + stdnse.print_debug(2, "SMB: Creating LMv2 response") + status, lm_response = lmv2_create_response(ntlm_hash, username, domain, challenge) + ntlm_response = "" - else - -- Default to NTLMv1 - if(hash_type ~= nil) then - stdnse.print_debug(1, "SMB: Invalid login type specified ('%s'), using default (NTLM)", hash_type) - else - stdnse.print_debug(1, "SMB: No login type specified, using default (NTLM)") - end + else + -- Default to NTLMv1 + if(hash_type ~= nil) then + stdnse.print_debug(1, "SMB: Invalid login type specified ('%s'), using default (NTLM)", hash_type) + else + stdnse.print_debug(1, "SMB: No login type specified, using default (NTLM)") + end - status, lm_response = ntlm_create_response(ntlm_hash, challenge) - status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) + status, lm_response = ntlm_create_response(ntlm_hash, challenge) + status, ntlm_response = ntlm_create_response(ntlm_hash, challenge) - end + end - stdnse.print_debug(2, "SMB: Lanman response: %s", stdnse.tohex(lm_response)) - stdnse.print_debug(2, "SMB: NTLM response: %s", stdnse.tohex(ntlm_response)) + stdnse.print_debug(2, "SMB: Lanman response: %s", stdnse.tohex(lm_response)) + stdnse.print_debug(2, "SMB: NTLM response: %s", stdnse.tohex(ntlm_response)) - return lm_response, ntlm_response, mac_key + return lm_response, ntlm_response, mac_key end function get_security_blob(security_blob, ip, username, domain, password, password_hash, hash_type, flags) - local pos = 1 - local new_blob - local flags = flags or 0x00008215 -- (NEGOTIATE_SIGN_ALWAYS | NEGOTIATE_NTLM | NEGOTIATE_SIGN | REQUEST_TARGET | NEGOTIATE_UNICODE) + local pos = 1 + local new_blob + local flags = flags or 0x00008215 -- (NEGOTIATE_SIGN_ALWAYS | NEGOTIATE_NTLM | NEGOTIATE_SIGN | REQUEST_TARGET | NEGOTIATE_UNICODE) - if(security_blob == nil) then - -- If security_blob is nil, this is the initial packet - new_blob = bin.pack(" 0 ) and to_unicode(domain) or "" - ntlm = (#username > 0 ) and ntlm or "" - lanman = (#username > 0 ) and lanman or string.char(0) + -- Convert the username and domain to unicode (TODO: Disable the unicode flag, evaluate if that'll work) + local hostname = to_unicode("nmap") + username = to_unicode(username) + domain = (#username > 0 ) and to_unicode(domain) or "" + ntlm = (#username > 0 ) and ntlm or "" + lanman = (#username > 0 ) and lanman or string.char(0) - local domain_offset = 0x40 - local username_offset = domain_offset + #domain - local hostname_offset = username_offset + #username - local lanman_offset = hostname_offset + #hostname - local ntlm_offset = lanman_offset + #lanman - local sessionkey_offset = ntlm_offset + #ntlm + local domain_offset = 0x40 + local username_offset = domain_offset + #domain + local hostname_offset = username_offset + #username + local lanman_offset = hostname_offset + #hostname + local ntlm_offset = lanman_offset + #lanman + local sessionkey_offset = ntlm_offset + #ntlm - new_blob = bin.pack(" 0 and #ntlm - 16 or 0 ), - ( #ntlm > 0 and #ntlm - 16 or 0 ), - ntlm_offset, - #domain, - #domain, - domain_offset, - #username, - #username, - username_offset, - #hostname, - #hostname, - hostname_offset, - #session_key, - #session_key, - sessionkey_offset, - flags, - domain, - username, - hostname, - lanman, - ntlm, - session_key) + new_blob = bin.pack(" 0 and #ntlm - 16 or 0 ), + ( #ntlm > 0 and #ntlm - 16 or 0 ), + ntlm_offset, + #domain, + #domain, + domain_offset, + #username, + #username, + username_offset, + #hostname, + #hostname, + hostname_offset, + #session_key, + #session_key, + sessionkey_offset, + flags, + domain, + username, + hostname, + lanman, + ntlm, + session_key) - return true, new_blob, mac_key - end + return true, new_blob, mac_key + end end function get_host_info_from_security_blob(security_blob) - local ntlm_challenge = {} - --local pos, identifier, message_type, domain_length, domain_max, domain_offset, server_flags, challenge, reserved, target_info_length, target_info_max, target_info_offset = bin.unpack(" 0 ) then - local length = domain_length - local pos = domain_offset + 1 -- +1 to convert to Lua's 1-based indexes - local target_realm - pos, target_realm = bin.unpack( string.format( "A%d", length ), security_blob, pos ) - ntlm_challenge[ "target_realm" ] = from_unicode( target_realm ) - end + -- Parse the TargetName data (i.e. the server authentication realm) + if ( domain_length > 0 ) then + local length = domain_length + local pos = domain_offset + 1 -- +1 to convert to Lua's 1-based indexes + local target_realm + pos, target_realm = bin.unpack( string.format( "A%d", length ), security_blob, pos ) + ntlm_challenge[ "target_realm" ] = from_unicode( target_realm ) + end - -- Parse the TargetInfo data (Wireshark calls this the "Address List") - if ( target_info_length > 0 ) then + -- Parse the TargetInfo data (Wireshark calls this the "Address List") + if ( target_info_length > 0 ) then - -- Definition of AvId values (IDs for AV_PAIR (attribute-value pair) structures), - -- as definied by the NTLM Authentication Protocol specification [MS-NLMP]. - local NTLM_AV_ID_VALUES = { - MsvAvEOL = 0x0, - MsvAvNbComputerName = 0x1, - MsvAvNbDomainName = 0x2, - MsvAvDnsComputerName = 0x3, - MsvAvDnsDomainName = 0x4, - MsvAvDnsTreeName = 0x5, - MsvAvFlags = 0x6, - MsvAvTimestamp = 0x7, - MsvAvRestrictions = 0x8, - MsvAvTargetName = 0x9, - MsvAvChannelBindings = 0xA, - } - -- Friendlier names for AvId values, to be used as keys in the results table - -- e.g. ntlm_challenge[ "dns_computer_name" ] -> "host.test.local" - local NTLM_AV_ID_NAMES = { - [NTLM_AV_ID_VALUES.MsvAvNbComputerName] = "netbios_computer_name", - [NTLM_AV_ID_VALUES.MsvAvNbDomainName] = "netbios_domain_name", - [NTLM_AV_ID_VALUES.MsvAvDnsComputerName] = "fqdn", - [NTLM_AV_ID_VALUES.MsvAvDnsDomainName] = "dns_domain_name", - [NTLM_AV_ID_VALUES.MsvAvDnsTreeName] = "dns_forest_name", - [NTLM_AV_ID_VALUES.MsvAvTimestamp] = "timestamp", - } + -- Definition of AvId values (IDs for AV_PAIR (attribute-value pair) structures), + -- as definied by the NTLM Authentication Protocol specification [MS-NLMP]. + local NTLM_AV_ID_VALUES = { + MsvAvEOL = 0x0, + MsvAvNbComputerName = 0x1, + MsvAvNbDomainName = 0x2, + MsvAvDnsComputerName = 0x3, + MsvAvDnsDomainName = 0x4, + MsvAvDnsTreeName = 0x5, + MsvAvFlags = 0x6, + MsvAvTimestamp = 0x7, + MsvAvRestrictions = 0x8, + MsvAvTargetName = 0x9, + MsvAvChannelBindings = 0xA, + } + -- Friendlier names for AvId values, to be used as keys in the results table + -- e.g. ntlm_challenge[ "dns_computer_name" ] -> "host.test.local" + local NTLM_AV_ID_NAMES = { + [NTLM_AV_ID_VALUES.MsvAvNbComputerName] = "netbios_computer_name", + [NTLM_AV_ID_VALUES.MsvAvNbDomainName] = "netbios_domain_name", + [NTLM_AV_ID_VALUES.MsvAvDnsComputerName] = "fqdn", + [NTLM_AV_ID_VALUES.MsvAvDnsDomainName] = "dns_domain_name", + [NTLM_AV_ID_VALUES.MsvAvDnsTreeName] = "dns_forest_name", + [NTLM_AV_ID_VALUES.MsvAvTimestamp] = "timestamp", + } - local length = target_info_length - local pos = target_info_offset + 1 -- +1 to convert to Lua's 1-based indexes - local target_info - pos, target_info = bin.unpack( string.format( "A%d", length ), security_blob, pos ) + local length = target_info_length + local pos = target_info_offset + 1 -- +1 to convert to Lua's 1-based indexes + local target_info + pos, target_info = bin.unpack( string.format( "A%d", length ), security_blob, pos ) - pos = 1 -- reset pos to 1, since we'll be working out of just the target_info - repeat - local value, av_id, av_len - pos, av_id, av_len = bin.unpack( "= #target_info ) - end + if ( av_id == NTLM_AV_ID_VALUES.MsvAvEOL ) then + break + elseif ( av_id == NTLM_AV_ID_VALUES.MsvAvTimestamp ) then + -- this is a FILETIME value (see [MS-DTYP]), representing the time in 100-ns increments since 1/1/1601 + ntlm_challenge[ friendly_name ] = bin.unpack( "= #target_info ) + end - return ntlm_challenge + return ntlm_challenge end ---Create an 8-byte message signature that's sent with all SMB packets. @@ -815,11 +815,11 @@ end -- sent, except with the signature slot replaced with the sequence number. --@return The 8-byte signature. The signature is equal to the first eight bytes of md5(mac_key .. smb_data) function calculate_signature(mac_key, data) - if(have_ssl) then - return string.sub(openssl.md5(mac_key .. data), 1, 8) - else - return string.rep(string.char(0), 8) - end + if(have_ssl) then + return string.sub(openssl.md5(mac_key .. data), 1, 8) + else + return string.rep(string.char(0), 8) + end end diff --git a/nselib/smtp.lua b/nselib/smtp.lua index ec0941b0c..607e40132 100644 --- a/nselib/smtp.lua +++ b/nselib/smtp.lua @@ -596,80 +596,80 @@ end -- error message on failures. login = function(socket, username, password, mech) - assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5" - or mech == "DIGEST-MD5" or mech == "NTLM", - ("Unsupported authentication mechanism (%s)"):format(mech or "nil")) - local status, response = query(socket, "AUTH", mech) - if ( not(status) ) then - return false, "ERROR: Failed to send AUTH to server" - end + assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5" + or mech == "DIGEST-MD5" or mech == "NTLM", + ("Unsupported authentication mechanism (%s)"):format(mech or "nil")) + local status, response = query(socket, "AUTH", mech) + if ( not(status) ) then + return false, "ERROR: Failed to send AUTH to server" + end - if ( mech == "LOGIN" ) then - local tmp = response:match("334 (.*)") - if ( not(tmp) ) then - return false, "ERROR: Failed to decode LOGIN response" - end - tmp = base64.dec(tmp):lower() - if ( not(tmp:match("^username")) ) then - return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp) - end - status, response = query(socket, base64.enc(username)) - if ( not(status) ) then - return false, "ERROR: Failed to read LOGIN response" - end - tmp = response:match("334 (.*)") - if ( not(tmp) ) then - return false, "ERROR: Failed to decode LOGIN response" - end - tmp = base64.dec(tmp):lower() - if ( not(tmp:match("^password")) ) then - return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp) - end - status, response = query(socket, base64.enc(password)) - if ( not(status) ) then - return false, "ERROR: Failed to read LOGIN response" - end - if ( response:match("^235") ) then - return true, "Login success" - end - return false, response - end + if ( mech == "LOGIN" ) then + local tmp = response:match("334 (.*)") + if ( not(tmp) ) then + return false, "ERROR: Failed to decode LOGIN response" + end + tmp = base64.dec(tmp):lower() + if ( not(tmp:match("^username")) ) then + return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp) + end + status, response = query(socket, base64.enc(username)) + if ( not(status) ) then + return false, "ERROR: Failed to read LOGIN response" + end + tmp = response:match("334 (.*)") + if ( not(tmp) ) then + return false, "ERROR: Failed to decode LOGIN response" + end + tmp = base64.dec(tmp):lower() + if ( not(tmp:match("^password")) ) then + return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp) + end + status, response = query(socket, base64.enc(password)) + if ( not(status) ) then + return false, "ERROR: Failed to read LOGIN response" + end + if ( response:match("^235") ) then + return true, "Login success" + end + return false, response + end - if ( mech == "NTLM" ) then - -- sniffed of the wire, seems to always be the same - -- decodes to some NTLMSSP blob greatness - status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==") - if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end - end + if ( mech == "NTLM" ) then + -- sniffed of the wire, seems to always be the same + -- decodes to some NTLMSSP blob greatness + status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==") + if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end + end - local chall = response:match("^334 (.*)") - chall = (chall and base64.dec(chall)) - if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end + local chall = response:match("^334 (.*)") + chall = (chall and base64.dec(chall)) + if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end - -- All mechanisms expect username and pass - -- add the otheronce for those who need them - local mech_params = { username, password, chall, "smtp" } - local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params)) - auth_data = base64.enc(auth_data) + -- All mechanisms expect username and pass + -- add the otheronce for those who need them + local mech_params = { username, password, chall, "smtp" } + local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params)) + auth_data = base64.enc(auth_data) - status, response = query(socket, auth_data) - if ( not(status) ) then - return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech) - end + status, response = query(socket, auth_data) + if ( not(status) ) then + return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech) + end - if ( mech == "DIGEST-MD5" ) then - local rspauth = response:match("^334 (.*)") - if ( rspauth ) then - rspauth = base64.dec(rspauth) - status, response = query(socket,"") - end - end + if ( mech == "DIGEST-MD5" ) then + local rspauth = response:match("^334 (.*)") + if ( rspauth ) then + rspauth = base64.dec(rspauth) + status, response = query(socket,"") + end + end - if ( response:match("^235") ) then return true, "Login success" end + if ( response:match("^235") ) then return true, "Login success" end - return false, response + return false, response end return _ENV; diff --git a/nselib/stdnse.lua b/nselib/stdnse.lua index 5b90093ee..bbeb98b6d 100644 --- a/nselib/stdnse.lua +++ b/nselib/stdnse.lua @@ -647,9 +647,9 @@ local function format_output_sub(status, data, indent) local lines = splitlines(value) for j, line in ipairs(lines) do - insert(output, format("%s %s%s\n", - format_get_indent(indent, i == #data and j == #lines), - prefix, line)) + insert(output, format("%s %s%s\n", + format_get_indent(indent, i == #data and j == #lines), + prefix, line)) end end end @@ -1019,65 +1019,65 @@ do end -- no function here, see nse_main.lua --@param port_range a port range string in Nmap standard format (ex. "T:80,1-30,U:31337,21-25") --@returns boolean indicating whether the port is in the port range function in_port_range(port,port_range) - assert(port and type(port.number)=="number" and type(port.protocol)=="string" and - (port.protocol=="udp" or port.protocol=="tcp"),"Port structure missing or invalid: port={ number=, protocol= }") - assert((type(port_range)=="string" or type(port_range)=="number") and port_range~="","Incorrect port range specification.") + assert(port and type(port.number)=="number" and type(port.protocol)=="string" and + (port.protocol=="udp" or port.protocol=="tcp"),"Port structure missing or invalid: port={ number=, protocol= }") + assert((type(port_range)=="string" or type(port_range)=="number") and port_range~="","Incorrect port range specification.") - -- Proto - true for TCP, false for UDP - local proto - if(port.protocol=="tcp") then proto = true else proto = false end + -- Proto - true for TCP, false for UDP + local proto + if(port.protocol=="tcp") then proto = true else proto = false end - --TCP flag for iteration - true for TCP, false for UDP, if not specified we presume TCP - local tcp_flag = true + --TCP flag for iteration - true for TCP, false for UDP, if not specified we presume TCP + local tcp_flag = true - -- in case the port_range is a single number - if type(port_range)=="number" then - if proto and port_range==port.number then return true - else return false - end - end + -- in case the port_range is a single number + if type(port_range)=="number" then + if proto and port_range==port.number then return true + else return false + end + end - --clean the string a bit - port_range=port_range:gsub("%s+","") + --clean the string a bit + port_range=port_range:gsub("%s+","") - -- single_pr - single port range - for i, single_pr in ipairs(strsplit(",",port_range)) do - if single_pr:match("T:") then - tcp_flag = true - single_pr = single_pr:gsub("T:","") - else - if single_pr:match("U:") then - tcp_flag = false - single_pr = single_pr:gsub("U:","") - end - end + -- single_pr - single port range + for i, single_pr in ipairs(strsplit(",",port_range)) do + if single_pr:match("T:") then + tcp_flag = true + single_pr = single_pr:gsub("T:","") + else + if single_pr:match("U:") then + tcp_flag = false + single_pr = single_pr:gsub("U:","") + end + end - -- compare ports only when the port's protocol is the same as - -- the current single port range - if tcp_flag == proto then - local pone = single_pr:match("^(%d+)$") - if pone then - pone = tonumber(pone) - assert(pone>-1 and pone<65536, "Port range number out of range (0-65535).") + -- compare ports only when the port's protocol is the same as + -- the current single port range + if tcp_flag == proto then + local pone = single_pr:match("^(%d+)$") + if pone then + pone = tonumber(pone) + assert(pone>-1 and pone<65536, "Port range number out of range (0-65535).") - if pone == port.number then - return true - end - else - local pstart, pend = single_pr:match("^(%d+)%-(%d+)$") - pstart, pend = tonumber(pstart), tonumber(pend) - assert(pstart,"Incorrect port range specification.") - assert(pstart<=pend,"Incorrect port range specification, the starting port should have a smaller value than the ending port.") - assert(pstart>-1 and pstart<65536 and pend>-1 and pend<65536, "Port range number out of range (0-65535).") + if pone == port.number then + return true + end + else + local pstart, pend = single_pr:match("^(%d+)%-(%d+)$") + pstart, pend = tonumber(pstart), tonumber(pend) + assert(pstart,"Incorrect port range specification.") + assert(pstart<=pend,"Incorrect port range specification, the starting port should have a smaller value than the ending port.") + assert(pstart>-1 and pstart<65536 and pend>-1 and pend<65536, "Port range number out of range (0-65535).") - if port.number >=pstart and port.number <= pend then - return true - end - end - end - end - -- if no match is found then the port doesn't belong to the port_range - return false + if port.number >=pstart and port.number <= pend then + return true + end + end + end + end + -- if no match is found then the port doesn't belong to the port_range + return false end --- Module function that mimics some behavior of Lua 5.1 module function. @@ -1244,12 +1244,12 @@ end --@return Boolean true if the item was found, false if not --@return The index or key where the value was found, or nil function contains(tab, item) - for k, val in pairs(tab) do - if val == item then - return true, k - end - end - return false, nil + for k, val in pairs(tab) do + if val == item then + return true, k + end + end + return false, nil end diff --git a/nselib/tns.lua b/nselib/tns.lua index 745f3b54f..af24ce1b3 100644 --- a/nselib/tns.lua +++ b/nselib/tns.lua @@ -3,36 +3,36 @@ -- -- Summary -- ------- --- The library currently provides functionality to connect and authenticate --- to the Oracle database server. Some preliminary query support has been --- added, which only works against a few specific versions. The library has --- been tested against and known to work with Oracle 10g and 11g. Please check --- the matrix below for tested versions that are known to work. +-- The library currently provides functionality to connect and authenticate +-- to the Oracle database server. Some preliminary query support has been +-- added, which only works against a few specific versions. The library has +-- been tested against and known to work with Oracle 10g and 11g. Please check +-- the matrix below for tested versions that are known to work. -- --- Due to the lack of documentation the library is based mostly on guesswork --- with a lot of unknowns. Bug reports are therefore both welcome and --- important in order to further improve this library. In addition, knowing --- that the library works against versions not in the test matrix is valuable --- as well. +-- Due to the lack of documentation the library is based mostly on guesswork +-- with a lot of unknowns. Bug reports are therefore both welcome and +-- important in order to further improve this library. In addition, knowing +-- that the library works against versions not in the test matrix is valuable +-- as well. -- -- Overview -- -------- -- The library contains the following classes: -- --- o Packet.* --- - The Packet classes contain specific packets and function to serialize +-- o Packet.* +-- - The Packet classes contain specific packets and function to serialize -- them to strings that can be sent over the wire. Each class may also -- contain a function to parse the servers response. -- -- o Comm --- - Implements a number of functions to handle communication +-- - Implements a number of functions to handle communication -- -- o Crypt --- - Implements encryption algorithms and functions to support +-- - Implements encryption algorithms and functions to support -- authentication with Oracle 10G and Oracle 11G. -- -- o Helper --- - A helper class that provides easy access to the rest of the library +-- - A helper class that provides easy access to the rest of the library -- -- -- Example @@ -41,10 +41,10 @@ -- to interface the library: -- -- --- tnshelper = tns.Helper:new(host, port) --- status, err = tnshelper:Connect() --- status, res = tnshelper:Login("sys", "change_on_install") --- status, err = tnshelper:Close() +-- tnshelper = tns.Helper:new(host, port) +-- status, err = tnshelper:Connect() +-- status, res = tnshelper:Login("sys", "change_on_install") +-- status, err = tnshelper:Close() -- -- -- Additional information @@ -53,9 +53,9 @@ -- analysis of packet dumps: -- -- o Oracle 10g TNS AES-128 authentication details (Massimiliano Montoro) --- x http://www.oxid.it/downloads/oracle_tns_aes128_check.txt +-- x http://www.oxid.it/downloads/oracle_tns_aes128_check.txt -- o Oracle 11g TNS AES-192 authentication details (Massimiliano Montoro) --- x http://www.oxid.it/downloads/oracle_tns_aes192_check.txt +-- x http://www.oxid.it/downloads/oracle_tns_aes192_check.txt -- o Initial analysis of Oracle native authentication version 11g -- (László Tóth) -- x http://www.soonerorlater.hu/index.khtml?article_id=512 @@ -126,28 +126,28 @@ ORACLE_VERSION_11G = 314 -- Data type to number conversions DataTypes = { - NUMBER = 2, - DATE = 12, + NUMBER = 2, + DATE = 12, } -- A class containing some basic authentication options AuthOptions = { - -- Creates a new AuthOptions instance - -- @return o new instance of AuthOptions - new = function( self ) - local o = { - auth_term = "pts/" .. math.random(255), - auth_prog = ("sqlplus@nmap_%d (TNS V1-V3)"):format(math.random(32768)), - auth_machine = "nmap_target", - auth_pid = "" .. math.random(32768), - auth_sid = "nmap_" .. math.random(32768) - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new AuthOptions instance + -- @return o new instance of AuthOptions + new = function( self ) + local o = { + auth_term = "pts/" .. math.random(255), + auth_prog = ("sqlplus@nmap_%d (TNS V1-V3)"):format(math.random(32768)), + auth_machine = "nmap_target", + auth_pid = "" .. math.random(32768), + auth_sid = "nmap_" .. math.random(32768) + } + setmetatable(o, self) + self.__index = self + return o + end, } @@ -155,55 +155,55 @@ AuthOptions = -- tns data packets DataTypeDecoders = { - -- Decodes a number - [DataTypes.NUMBER] = function(val) - if ( #val == 0 ) then return "" end - if ( #val == 1 and val == '\128' ) then return 0 end + -- Decodes a number + [DataTypes.NUMBER] = function(val) + if ( #val == 0 ) then return "" end + if ( #val == 1 and val == '\128' ) then return 0 end - local bytes = {} - for i=1, #val do bytes[i] = select(2, bin.unpack("C", val, i)) end + local bytes = {} + for i=1, #val do bytes[i] = select(2, bin.unpack("C", val, i)) end - local positive = ( bit.band(bytes[1], 0x80) ~= 0 ) + local positive = ( bit.band(bytes[1], 0x80) ~= 0 ) - local function convert_bytes(bytes, positive) - local ret_bytes = {} - local len = #bytes + local function convert_bytes(bytes, positive) + local ret_bytes = {} + local len = #bytes - if ( positive ) then - ret_bytes[1] = bit.band(bytes[1], 0x7F) - 65 - for i=2, len do ret_bytes[i] = bytes[i] - 1 end - else - ret_bytes[1] = bit.band(bit.bxor(bytes[1], 0xFF), 0x7F) - 65 - for i=2, len do ret_bytes[i] = 101 - bytes[i] end - end + if ( positive ) then + ret_bytes[1] = bit.band(bytes[1], 0x7F) - 65 + for i=2, len do ret_bytes[i] = bytes[i] - 1 end + else + ret_bytes[1] = bit.band(bit.bxor(bytes[1], 0xFF), 0x7F) - 65 + for i=2, len do ret_bytes[i] = 101 - bytes[i] end + end - return ret_bytes - end + return ret_bytes + end - bytes = convert_bytes(bytes, positive) + bytes = convert_bytes(bytes, positive) - local k = ( #bytes - 1 > bytes[1] +1 ) and ( bytes[1] + 1 ) or #bytes - 1 - local l = 0 - for m=1, k do l = l * 100 + bytes[m+1] end - for m=bytes[1]-#bytes - 1, 0, -1 do l = l * 100 end + local k = ( #bytes - 1 > bytes[1] +1 ) and ( bytes[1] + 1 ) or #bytes - 1 + local l = 0 + for m=1, k do l = l * 100 + bytes[m+1] end + for m=bytes[1]-#bytes - 1, 0, -1 do l = l * 100 end - return (positive and l or -l) - end, + return (positive and l or -l) + end, - -- Decodes a date - [DataTypes.DATE] = function(val) - local bytes = {} + -- Decodes a date + [DataTypes.DATE] = function(val) + local bytes = {} - if (#val == 0) then - return "" - elseif( #val ~= 7 ) then - return "ERROR: Failed to decode date" - end + if (#val == 0) then + return "" + elseif( #val ~= 7 ) then + return "ERROR: Failed to decode date" + end - for i=1, 7 do bytes[i] = select(2, bin.unpack("C", val, i)) end + for i=1, 7 do bytes[i] = select(2, bin.unpack("C", val, i)) end - return ("%d-%02d-%02d"):format( (bytes[1] - 100 ) * 100 + bytes[2] - 100, bytes[3], bytes[4] ) - end, + return ("%d-%02d-%02d"):format( (bytes[1] - 100 ) * 100 + bytes[2] - 100, bytes[3], bytes[4] ) + end, @@ -212,7 +212,7 @@ DataTypeDecoders = { -- Packet class table -- -- Each Packet type SHOULD implement: --- o tns_type - A variable indicating the TNS Type of the Packet +-- o tns_type - A variable indicating the TNS Type of the Packet -- o toString - A function that serializes the object to string -- -- Each Packet MAY also optionally implement: @@ -226,161 +226,161 @@ Packet = {} -- TNS packet. Packet.TNS = { - checksum = 0, - hdr_checksum = 0, - length = 0, - reserved = 0, + checksum = 0, + hdr_checksum = 0, + length = 0, + reserved = 0, - Type = - { - CONNECT = 1, - ACCEPT = 2, - REFUSE = 4, - DATA = 6, - RESEND = 11, - MARKER = 12, - }, + Type = + { + CONNECT = 1, + ACCEPT = 2, + REFUSE = 4, + DATA = 6, + RESEND = 11, + MARKER = 12, + }, - new = function( self, typ ) - local o = { - type = typ - } - setmetatable(o, self) - self.__index = self - return o - end, + new = function( self, typ ) + local o = { + type = typ + } + setmetatable(o, self) + self.__index = self + return o + end, - --- Read a TNS packet of the socket - -- - -- @return true on success, false on failure - -- @return err string containing error message on failure - recv = function( self ) - local status, data = self.socket:receive_buf( match.numbytes(2), true ) + --- Read a TNS packet of the socket + -- + -- @return true on success, false on failure + -- @return err string containing error message on failure + recv = function( self ) + local status, data = self.socket:receive_buf( match.numbytes(2), true ) - if ( not(status) ) then - return status, data - end + if ( not(status) ) then + return status, data + end - local _ - _, self.length = bin.unpack(">S", data ) + local _ + _, self.length = bin.unpack(">S", data ) - status, data = self.socket:receive_buf( match.numbytes(6), true ) - if ( not(status) ) then - return status, data - end + status, data = self.socket:receive_buf( match.numbytes(6), true ) + if ( not(status) ) then + return status, data + end - _, self.checksum, self.type, self.reserved, self.hdr_checksum = bin.unpack(">SCCS", data) + _, self.checksum, self.type, self.reserved, self.hdr_checksum = bin.unpack(">SCCS", data) - status, data = self.socket:receive_buf( match.numbytes(self.length - 8), true ) - if ( status ) then - self.data = data - end + status, data = self.socket:receive_buf( match.numbytes(self.length - 8), true ) + if ( status ) then + self.data = data + end - return true - end, + return true + end, - parse = function(data) - local tns = Packet.TNS:new() - local pos - pos, tns.length, tns.checksum, tns.type, tns.reserved, tns.hdr_checksum = bin.unpack(">SSCCS", data) - pos, tns.data = bin.unpack("A" .. ( tns.length - 8 ), data, pos) - return tns - end, + parse = function(data) + local tns = Packet.TNS:new() + local pos + pos, tns.length, tns.checksum, tns.type, tns.reserved, tns.hdr_checksum = bin.unpack(">SSCCS", data) + pos, tns.data = bin.unpack("A" .. ( tns.length - 8 ), data, pos) + return tns + end, - --- Converts the TNS packet to string suitable to be sent over the socket - -- - -- @return string containing the TNS packet - __tostring = function( self ) - local data = bin.pack(">SSCCSA", self.length, self.checksum, self.type, self.reserved, self.hdr_checksum, self.data ) - return data - end, + --- Converts the TNS packet to string suitable to be sent over the socket + -- + -- @return string containing the TNS packet + __tostring = function( self ) + local data = bin.pack(">SSCCSA", self.length, self.checksum, self.type, self.reserved, self.hdr_checksum, self.data ) + return data + end, } -- Initiates the connection to the listener Packet.Connect = { - CONN_STR = [[ - (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%d)) - (CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=%s)(CID= - (PROGRAM=sqlplus)(HOST=%s)(USER=nmap))))]], + CONN_STR = [[ + (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%d)) + (CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=%s)(CID= + (PROGRAM=sqlplus)(HOST=%s)(USER=nmap))))]], - version = 314, - version_comp = 300, - svc_options = 0x0c41, - sess_dus = 8192, - max_trans_dus = 32767, - nt_proto_char = 0x7f08, - line_turnaround = 0, - value_of_1_in_hw = 0x0100, - conn_data_len = 0, - conn_data_offset = 58, - conn_data_max_recv = 512, - conn_data_flags_0 = 0x41, - conn_data_flags_1 = 0x41, - trace_cross_1 = 0, - trace_cross_2 = 0, - trace_unique_conn = 0, - tns_type = Packet.TNS.Type.CONNECT, + version = 314, + version_comp = 300, + svc_options = 0x0c41, + sess_dus = 8192, + max_trans_dus = 32767, + nt_proto_char = 0x7f08, + line_turnaround = 0, + value_of_1_in_hw = 0x0100, + conn_data_len = 0, + conn_data_offset = 58, + conn_data_max_recv = 512, + conn_data_flags_0 = 0x41, + conn_data_flags_1 = 0x41, + trace_cross_1 = 0, + trace_cross_2 = 0, + trace_unique_conn = 0, + tns_type = Packet.TNS.Type.CONNECT, - -- Creates a new Connect instance - -- @param rhost string containing host or ip - -- @param rport string containing the port number - -- @param dbinstance string containing the instance name - -- @return o containing new Connect instance - new = function( self, rhost, rport, dbinstance ) - local o = { - rhost = rhost, - rport = rport, - conn_data = Packet.Connect.CONN_STR:format( rhost, rport, dbinstance, rhost ), - dbinstance = dbinstance:upper() - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new Connect instance + -- @param rhost string containing host or ip + -- @param rport string containing the port number + -- @param dbinstance string containing the instance name + -- @return o containing new Connect instance + new = function( self, rhost, rport, dbinstance ) + local o = { + rhost = rhost, + rport = rport, + conn_data = Packet.Connect.CONN_STR:format( rhost, rport, dbinstance, rhost ), + dbinstance = dbinstance:upper() + } + setmetatable(o, self) + self.__index = self + return o + end, - setCmd = function( self, cmd ) - local tmp = [[ - (DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=%s)(USER=nmap))(COMMAND=%s)(ARGUMENTS=64)(SERVICE=%s:%d)(VERSION=185599488))) - ]] - self.conn_data = tmp:format( self.rhost, cmd, self.rhost, self.rport ) - end, + setCmd = function( self, cmd ) + local tmp = [[ + (DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=%s)(USER=nmap))(COMMAND=%s)(ARGUMENTS=64)(SERVICE=%s:%d)(VERSION=185599488))) + ]] + self.conn_data = tmp:format( self.rhost, cmd, self.rhost, self.rport ) + end, - --- Parses the server response from the CONNECT - -- - -- @param tns Packet.TNS containing the TNS packet received from the - -- server - -- @return true on success, false on failure - -- @return version number containing the version supported by the - -- server or an error message on failure - parseResponse = function( self, tns ) - local pos, version + --- Parses the server response from the CONNECT + -- + -- @param tns Packet.TNS containing the TNS packet received from the + -- server + -- @return true on success, false on failure + -- @return version number containing the version supported by the + -- server or an error message on failure + parseResponse = function( self, tns ) + local pos, version - if ( tns.type ~= Packet.TNS.Type.ACCEPT ) then - if ( tns.data:match("ERR=12514") ) then - return false, ("TNS: The listener could not resolve \"%s\""):format(self.dbinstance) - end - return false, tns.data:match("%(ERR=(%d*)%)") - end + if ( tns.type ~= Packet.TNS.Type.ACCEPT ) then + if ( tns.data:match("ERR=12514") ) then + return false, ("TNS: The listener could not resolve \"%s\""):format(self.dbinstance) + end + return false, tns.data:match("%(ERR=(%d*)%)") + end - pos, version = bin.unpack(">S", tns.data ) - return true, version - end, + pos, version = bin.unpack(">S", tns.data ) + return true, version + end, - --- Converts the CONNECT packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - self.conn_data_len = #self.conn_data + --- Converts the CONNECT packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + self.conn_data_len = #self.conn_data - return bin.pack(">SSSSSSSSSSICCIILLA", self.version, self.version_comp, self.svc_options, - self.sess_dus, self.max_trans_dus, self.nt_proto_char, - self.line_turnaround, self.value_of_1_in_hw, self.conn_data_len, - self.conn_data_offset, self.conn_data_max_recv, self.conn_data_flags_0, - self.conn_data_flags_1, self.trace_cross_1, self.trace_cross_2, - self.trace_unique_conn, 0, self.conn_data ) - end, + return bin.pack(">SSSSSSSSSSICCIILLA", self.version, self.version_comp, self.svc_options, + self.sess_dus, self.max_trans_dus, self.nt_proto_char, + self.line_turnaround, self.value_of_1_in_hw, self.conn_data_len, + self.conn_data_offset, self.conn_data_max_recv, self.conn_data_flags_0, + self.conn_data_flags_1, self.trace_cross_1, self.trace_cross_2, + self.trace_unique_conn, 0, self.conn_data ) + end, } @@ -388,28 +388,28 @@ Packet.Connect = { -- A TNS data packet, one of the most common packets Packet.Data = { - flag = 0, + flag = 0, - -- Createas a new Data instance - -- @return o new instance of Data - new = function( self, data ) - local o = { - TNS = Packet.TNS:new( Packet.TNS.Type.DATA ), - data = data - } - setmetatable(o, self) - self.__index = self - return o - end, + -- Createas a new Data instance + -- @return o new instance of Data + new = function( self, data ) + local o = { + TNS = Packet.TNS:new( Packet.TNS.Type.DATA ), + data = data + } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - local data = bin.pack( ">S", self.flag ) .. self.data - self.TNS.length = #data + 8 - return tostring(self.TNS) .. data - end, + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + local data = bin.pack( ">S", self.flag ) .. self.data + self.TNS.length = #data + 8 + return tostring(self.TNS) .. data + end, } @@ -417,413 +417,413 @@ Packet.Data = { -- communication. Packet.Attention = { - tns_type = Packet.TNS.Type.MARKER, + tns_type = Packet.TNS.Type.MARKER, - -- Creates a new instance of the Attention packet - -- @return o new instance of Attention - new = function( self, typ, data ) - local o = { data = data, att_type = typ } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new instance of the Attention packet + -- @return o new instance of Attention + new = function( self, typ, data ) + local o = { data = data, att_type = typ } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the MARKER packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - return bin.pack( ">C", self.att_type ) .. self.data - end, + --- Converts the MARKER packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + return bin.pack( ">C", self.att_type ) .. self.data + end, } -- Packet initializing challenge response authentication Packet.PreAuth = { - tns_type = Packet.TNS.Type.DATA, - flags = 0, - param_order = { - { ["AUTH_TERMINAL"] = "auth_term" }, - { ["AUTH_PROGRAM_NM"] = "auth_prog" }, - { ["AUTH_MACHINE"] = "auth_machine" }, - { ["AUTH_PID"] = "auth_pid" }, - { ["AUTH_SID"] = "auth_sid" } - }, + tns_type = Packet.TNS.Type.DATA, + flags = 0, + param_order = { + { ["AUTH_TERMINAL"] = "auth_term" }, + { ["AUTH_PROGRAM_NM"] = "auth_prog" }, + { ["AUTH_MACHINE"] = "auth_machine" }, + { ["AUTH_PID"] = "auth_pid" }, + { ["AUTH_SID"] = "auth_sid" } + }, - --- Creates a new PreAuth packet - -- - -- @param user string containing the user name - -- @return a new instance of Packet.PreAuth - new = function(self, user, options, ver) - local o = { auth_user = user, auth_options = options, version = ver } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new PreAuth packet + -- + -- @param user string containing the user name + -- @return a new instance of Packet.PreAuth + new = function(self, user, options, ver) + local o = { auth_user = user, auth_options = options, version = ver } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - local packet_type = 0x0376 - local UNKNOWN_MAP = { - ["Linuxi386/Linux-2.0.34-8.1.0"] = bin.pack("HCH","0238be0808", #self.auth_user, "00000001000000a851bfbf05000000504ebfbf7853bfbf"), - ["IBMPC/WIN_NT-8.1.0"] = bin.pack("HCH","0238be0808", #self.auth_user, "00000001000000a851bfbf05000000504ebfbf7853bfbf"), - ["IBMPC/WIN_NT64-9.1.0"] = bin.pack("H", "0201040000000100000001050000000101"), - ["x86_64/Linux 2.4.xx"] = bin.pack("H", "0201040000000100000001050000000101"), - } - local unknown = UNKNOWN_MAP[self.version] or "" - local data = bin.pack(">SSA", self.flags, packet_type, unknown) + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + local packet_type = 0x0376 + local UNKNOWN_MAP = { + ["Linuxi386/Linux-2.0.34-8.1.0"] = bin.pack("HCH","0238be0808", #self.auth_user, "00000001000000a851bfbf05000000504ebfbf7853bfbf"), + ["IBMPC/WIN_NT-8.1.0"] = bin.pack("HCH","0238be0808", #self.auth_user, "00000001000000a851bfbf05000000504ebfbf7853bfbf"), + ["IBMPC/WIN_NT64-9.1.0"] = bin.pack("H", "0201040000000100000001050000000101"), + ["x86_64/Linux 2.4.xx"] = bin.pack("H", "0201040000000100000001050000000101"), + } + local unknown = UNKNOWN_MAP[self.version] or "" + local data = bin.pack(">SSA", self.flags, packet_type, unknown) - data = data .. bin.pack("CA", #self.auth_user, self.auth_user ) - for _, v in ipairs( Packet.PreAuth.param_order ) do - for k, v2 in pairs(v) do - data = data .. Marshaller.marshalKvp( k, self.auth_options[v2] ) - end - end + data = data .. bin.pack("CA", #self.auth_user, self.auth_user ) + for _, v in ipairs( Packet.PreAuth.param_order ) do + for k, v2 in pairs(v) do + data = data .. Marshaller.marshalKvp( k, self.auth_options[v2] ) + end + end - return data - end, + return data + end, - --- Parses the PreAuth packet response and extracts data needed to - -- perform authentication - -- - -- @param tns Packet.TNS containing the TNS packet recieved from the server - -- @return table containing the keys and values returned by the server - parseResponse = function( self, tns ) - local kvps = {} - local pos, kvp_count = bin.unpack( "C", tns.data, 4 ) - pos = 6 + --- Parses the PreAuth packet response and extracts data needed to + -- perform authentication + -- + -- @param tns Packet.TNS containing the TNS packet recieved from the server + -- @return table containing the keys and values returned by the server + parseResponse = function( self, tns ) + local kvps = {} + local pos, kvp_count = bin.unpack( "C", tns.data, 4 ) + pos = 6 - for kvp_itr=1, kvp_count do - local key, val, kvp_flags - pos, key, val, kvp_flags = Marshaller.unmarshalKvp( tns.data, pos ) - -- we don't actually do anything with the flags currently, but they're there - kvps[key] = val - end + for kvp_itr=1, kvp_count do + local key, val, kvp_flags + pos, key, val, kvp_flags = Marshaller.unmarshalKvp( tns.data, pos ) + -- we don't actually do anything with the flags currently, but they're there + kvps[key] = val + end - return true, kvps - end, + return true, kvps + end, } -- Packet containing authentication data Packet.Auth = { - tns_type = Packet.TNS.Type.DATA, - flags = 0, - param_order = { - { ['key'] = "AUTH_RTT", ['def'] = "25456" }, - { ['key'] = "AUTH_CLNT_MEM", ['def'] = "4096" }, - { ['key'] = "AUTH_TERMINAL", ['var'] = "auth_term" }, - { ['key'] = "AUTH_PROGRAM_NM", ['var'] = "auth_prog" }, - { ['key'] = "AUTH_MACHINE", ['var'] = "auth_machine" }, - { ['key'] = "AUTH_PID", ['var'] = "auth_pid" }, - { ['key'] = "AUTH_SID", ['var'] = "auth_sid" }, - { ['key'] = "AUTH_ACL", ['def'] = "4400" }, - { ['key'] = "AUTH_ALTER_SESSION", ['def'] = "ALTER SESSION SET TIME_ZONE='+02:00'\0" }, - { ['key'] = "AUTH_LOGICAL_SESSION_ID", ['def'] = select(2, bin.unpack("H16", openssl.rand_pseudo_bytes(16))) }, - { ['key'] = "AUTH_FAILOVER_ID", ['def'] = "" }, - }, + tns_type = Packet.TNS.Type.DATA, + flags = 0, + param_order = { + { ['key'] = "AUTH_RTT", ['def'] = "25456" }, + { ['key'] = "AUTH_CLNT_MEM", ['def'] = "4096" }, + { ['key'] = "AUTH_TERMINAL", ['var'] = "auth_term" }, + { ['key'] = "AUTH_PROGRAM_NM", ['var'] = "auth_prog" }, + { ['key'] = "AUTH_MACHINE", ['var'] = "auth_machine" }, + { ['key'] = "AUTH_PID", ['var'] = "auth_pid" }, + { ['key'] = "AUTH_SID", ['var'] = "auth_sid" }, + { ['key'] = "AUTH_ACL", ['def'] = "4400" }, + { ['key'] = "AUTH_ALTER_SESSION", ['def'] = "ALTER SESSION SET TIME_ZONE='+02:00'\0" }, + { ['key'] = "AUTH_LOGICAL_SESSION_ID", ['def'] = select(2, bin.unpack("H16", openssl.rand_pseudo_bytes(16))) }, + { ['key'] = "AUTH_FAILOVER_ID", ['def'] = "" }, + }, - --- Creates a new Auth packet - -- - -- @param auth_sesskey the encrypted session key - -- @param auth_pass the encrypted user password - -- @return a new instance of Packet.Auth - new = function(self, user, options, auth_sesskey, auth_pass, ver) - local o = { - auth_sesskey = auth_sesskey, - auth_pass = auth_pass, - auth_options = options, - user = user, - version = ver - } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new Auth packet + -- + -- @param auth_sesskey the encrypted session key + -- @param auth_pass the encrypted user password + -- @return a new instance of Packet.Auth + new = function(self, user, options, auth_sesskey, auth_pass, ver) + local o = { + auth_sesskey = auth_sesskey, + auth_pass = auth_pass, + auth_options = options, + user = user, + version = ver + } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - local UNKNOWN_MAP = { - ["Linuxi386/Linux-2.0.34-8.1.0"] = bin.pack("HCH","0338be0808", #self.user, "00000001010000cc7dbfbf0d000000747abfbf608abfbf"), - ["IBMPC/WIN_NT-8.1.0"] = bin.pack("HCH","0338be0808", #self.user, "00000001010000cc7dbfbf0d000000747abfbf608abfbf"), - ["IBMPC/WIN_NT64-9.1.0"] = bin.pack("H","03010400000001010000010d0000000101"), - ["x86_64/Linux 2.4.xx"] = bin.pack("H","03010400000001010000010d0000000101") - } + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + local UNKNOWN_MAP = { + ["Linuxi386/Linux-2.0.34-8.1.0"] = bin.pack("HCH","0338be0808", #self.user, "00000001010000cc7dbfbf0d000000747abfbf608abfbf"), + ["IBMPC/WIN_NT-8.1.0"] = bin.pack("HCH","0338be0808", #self.user, "00000001010000cc7dbfbf0d000000747abfbf608abfbf"), + ["IBMPC/WIN_NT64-9.1.0"] = bin.pack("H","03010400000001010000010d0000000101"), + ["x86_64/Linux 2.4.xx"] = bin.pack("H","03010400000001010000010d0000000101") + } - local sess_id = select(2, bin.unpack("H16", openssl.rand_pseudo_bytes(16))) - local unknown = UNKNOWN_MAP[self.version] or "" - local data = bin.pack(">SSA", self.flags, 0x0373, unknown) - data = data .. bin.pack("CA", #self.user, self.user ) - data = data .. Marshaller.marshalKvp( "AUTH_SESSKEY", self.auth_sesskey, 1 ) - data = data .. Marshaller.marshalKvp( "AUTH_PASSWORD", self.auth_pass ) + local sess_id = select(2, bin.unpack("H16", openssl.rand_pseudo_bytes(16))) + local unknown = UNKNOWN_MAP[self.version] or "" + local data = bin.pack(">SSA", self.flags, 0x0373, unknown) + data = data .. bin.pack("CA", #self.user, self.user ) + data = data .. Marshaller.marshalKvp( "AUTH_SESSKEY", self.auth_sesskey, 1 ) + data = data .. Marshaller.marshalKvp( "AUTH_PASSWORD", self.auth_pass ) - for k, v in ipairs( self.param_order ) do - if ( v['def'] ) then - data = data .. Marshaller.marshalKvp( v['key'], v['def'] ) - elseif ( self.auth_options[ v['var'] ] ) then - data = data .. Marshaller.marshalKvp( v['key'], self.auth_options[ v['var'] ] ) - elseif ( self[ v['var'] ] ) then - data = data .. Marshaller.marshalKvp( v['key'], self[ v['var'] ] ) - end - end - return data - end, + for k, v in ipairs( self.param_order ) do + if ( v['def'] ) then + data = data .. Marshaller.marshalKvp( v['key'], v['def'] ) + elseif ( self.auth_options[ v['var'] ] ) then + data = data .. Marshaller.marshalKvp( v['key'], self.auth_options[ v['var'] ] ) + elseif ( self[ v['var'] ] ) then + data = data .. Marshaller.marshalKvp( v['key'], self[ v['var'] ] ) + end + end + return data + end, - -- Parses the response of an Auth packet - -- - -- @param tns Packet.TNS containing the TNS packet recieved from the server - -- @return table containing the key pair values from the Auth packet - parseResponse = function( self, tns ) - local kvps = {} - local pos, kvp_count = bin.unpack( "C", tns.data, 4 ) - pos = 6 + -- Parses the response of an Auth packet + -- + -- @param tns Packet.TNS containing the TNS packet recieved from the server + -- @return table containing the key pair values from the Auth packet + parseResponse = function( self, tns ) + local kvps = {} + local pos, kvp_count = bin.unpack( "C", tns.data, 4 ) + pos = 6 - for kvp_itr=1, kvp_count do - local key, val, kvp_flags - pos, key, val, kvp_flags = Marshaller.unmarshalKvp( tns.data, pos ) - -- we don't actually do anything with the flags currently, but they're there - kvps[key] = val - end + for kvp_itr=1, kvp_count do + local key, val, kvp_flags + pos, key, val, kvp_flags = Marshaller.unmarshalKvp( tns.data, pos ) + -- we don't actually do anything with the flags currently, but they're there + kvps[key] = val + end - return true, kvps - end, + return true, kvps + end, } Packet.SNS = { - tns_type = Packet.TNS.Type.DATA, - flags = 0, + tns_type = Packet.TNS.Type.DATA, + flags = 0, - -- Creates a new SNS instance - -- - -- @return o new instance of the SNS packet - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new SNS instance + -- + -- @return o new instance of the SNS packet + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - return bin.pack("SH", self.flags, - [[ - deadbeef00920b1006000004000004000300000000000400050b10060000080 - 001000015cb353abecb00120001deadbeef0003000000040004000100010002 - 0001000300000000000400050b10060000020003e0e100020006fcff0002000 - 200000000000400050b100600000c0001001106100c0f0a0b08020103000300 - 0200000000000400050b10060000030001000301 - ]] ) - end, + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + return bin.pack("SH", self.flags, + [[ + deadbeef00920b1006000004000004000300000000000400050b10060000080 + 001000015cb353abecb00120001deadbeef0003000000040004000100010002 + 0001000300000000000400050b10060000020003e0e100020006fcff0002000 + 200000000000400050b100600000c0001001106100c0f0a0b08020103000300 + 0200000000000400050b10060000030001000301 + ]] ) + end, } -- Packet containing protocol negotiation Packet.ProtoNeg = { - tns_type = Packet.TNS.Type.DATA, - flags = 0, + tns_type = Packet.TNS.Type.DATA, + flags = 0, - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - local pfx = bin.pack(">SH", self.flags, "0106050403020100") - return pfx .. "Linuxi386/Linux-2.0.34-8.1.0\0" - end, + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + local pfx = bin.pack(">SH", self.flags, "0106050403020100") + return pfx .. "Linuxi386/Linux-2.0.34-8.1.0\0" + end, - --- Parses and verifies the server response - -- - -- @param tns Packet.TNS containing the response from the server - parseResponse = function( self, tns ) - local pos, flags, neg, ver, _, srv = bin.unpack(">SCCCz", tns.data) - if ( neg ~= 1 ) then - return false, "Error protocol negotiation failed" - end + --- Parses and verifies the server response + -- + -- @param tns Packet.TNS containing the response from the server + parseResponse = function( self, tns ) + local pos, flags, neg, ver, _, srv = bin.unpack(">SCCCz", tns.data) + if ( neg ~= 1 ) then + return false, "Error protocol negotiation failed" + end - if ( ver ~= 6 ) then - return false, ("Error protocol version (%d) not supported"):format(ver) - end - return true, srv - end + if ( ver ~= 6 ) then + return false, ("Error protocol version (%d) not supported"):format(ver) + end + return true, srv + end } Packet.Unknown1 = { - tns_type = Packet.TNS.Type.DATA, - flags = 0, + tns_type = Packet.TNS.Type.DATA, + flags = 0, - --- Creates a new Packet.Unknown1 - -- - -- @param version containing the version of the packet to send - -- @return new instance of Packet.Unknown1 - new = function(self, os) - local o = { os = os } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new Packet.Unknown1 + -- + -- @param version containing the version of the packet to send + -- @return new instance of Packet.Unknown1 + new = function(self, os) + local o = { os = os } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) - if ( self.os:match("IBMPC/WIN_NT[64]*[-]%d%.%d%.%d") ) then - return bin.pack(">SH", self.flags, [[ - 02b200b2004225060101010d010105010101010101017fff0309030301007f0 - 11fff010301013f01010500010702010000180001800000003c3c3c80000000 - d007000100010001000000020002000a00000008000800010000000c000c000 - a00000017001700010000001800180001000000190019001800190001000000 - 1a001a0019001a00010000001b001b000a001b00010000001c001c0016001c0 - 0010000001d001d0017001d00010000001e001e0017001e00010000001f001f - 0019001f0001000000200020000a00200001000000210021000a00210001000 - 0000a000a00010000000b000b00010000002800280001000000290029000100 - 000075007500010000007800780001000001220122000100000123012300010 - 12300010000012401240001000001250125000100000126012600010000012a - 012a00010000012b012b00010000012c012c00010000012d012d00010000012 - e012e00010000012f012f000100000130013000010000013101310001000001 - 320132000100000133013300010000013401340001000001350135000100000 - 136013600010000013701370001000001380138000100000139013900010000 - 013b013b00010000013c013c00010000013d013d00010000013e013e0001000 - 0013f013f000100000140014000010000014101410001000001420142000100 - 000143014300010000014701470001000001480148000100000149014900010 - 000014b014b00010000014d014d00010000014e014e00010000014f014f0001 - 000001500150000100000151015100010000015201520001000001530153000 - 100000154015400010000015501550001000001560156000100000157015700 - 0101570001000001580158000100000159015900010000015a015a000100000 - 15c015c00010000015d015d0001000001620162000100000163016300010000 - 0167016700010000016b016b00010000017c017c0001014200010000017d017 - d00010000017e017e00010000017f017f000100000180018000010000018101 - 810001000001820182000100000183018300010000018401840001000001850 - 18500010000018601860001000001870187000100000189018900010000018a - 018a00010000018b018b00010000018c018c00010000018d018d00010000018 - e018e00010000018f018f000100000190019000010000019101910001000001 - 940194000101250001000001950195000100000196019600010000019701970 - 0010000019d019d00010000019e019e00010000019f019f0001000001a001a0 - 0001000001a101a10001000001a201a20001000001a301a30001000001a401a - 40001000001a501a50001000001a601a60001000001a701a70001000001a801 - a80001000001a901a90001000001aa01aa0001000001ab01ab0001000001ad0 - 1ad0001000001ae01ae0001000001af01af0001000001b001b00001000001b1 - 01b10001000001c101c10001000001c201c2000101250001000001c601c6000 - 1000001c701c70001000001c801c80001000001c901c90001000001ca01ca00 - 01019f0001000001cb01cb000101a00001000001cc01cc000101a2000100000 - 1cd01cd000101a30001000001ce01ce000101b10001000001cf01cf00010122 - 0001000001d201d20001000001d301d3000101ab0001000001d401d40001000 - 001d501d50001000001d601d60001000001d701d70001000001d801d8000100 - 0001d901d90001000001da01da0001000001db01db0001000001dc01dc00010 - 00001dd01dd0001000001de01de0001000001df01df0001000001e001e00001 - 000001e101e10001000001e201e20001000001e301e30001016b0001000001e - 401e40001000001e501e50001000001e601e60001000001ea01ea0001000001 - eb01eb0001000001ec01ec0001000001ed01ed0001000001ee01ee000100000 - 1ef01ef0001000001f001f00001000001f201f20001000001f301f300010000 - 01f401f40001000001f501f50001000001f601f60001000001fd01fd0001000 - 001fe01fe000100000201020100010000020202020001000002040204000100 - 000205020500010000020602060001000002070207000100000208020800010 - 0000209020900010000020a020a00010000020b020b00010000020c020c0001 - 0000020d020d00010000020e020e00010000020f020f0001000002100210000 - 100000211021100010000021202120001000002130213000100000214021400 - 010000021502150001000002160216000100000217021700010000021802180 - 00100000219021900010000021a021a00010000021b021b00010000021c021c - 00010000021d021d00010000021e021e00010000021f021f000100000220022 - 000010000022102210001000002220222000100000223022300010000022402 - 240001000002250225000100000226022600010000022702270001000002280 - 228000100000229022900010000022a022a00010000022b022b00010000022c - 022c00010000022d022d00010000022e022e00010000022f022f00010000023 - 102310001000002320232000100000233023300010000023402340001000002 - 3702370001000002380238000100000239023900010000023a023a000100000 - 23b023b00010000023c023c00010000023d023d00010000023e023e00010000 - 023f023f0001000002400240000100000241024100010000024202420001000 - 002430243000100000244024400010000 - ]]) - elseif ( "x86_64/Linux 2.4.xx" == self.os ) then - return bin.pack(">SH", self.flags, [[ - 02b200b2004221060101010d01010401010101010101ffff0308030001003f0 - 1073f010101010301050201000018800000003c3c3c80000000d00700010001 - 0001000000020002000a00000008000800010000000c000c000a00000017001 - 7000100000018001800010000001900190018001900010000001a001a001900 - 1a00010000001b001b000a001b00010000001c001c0016001c00010000001d0 - 01d0017001d00010000001e001e0017001e00010000001f001f0019001f0001 - 000000200020000a00200001000000210021000a002100010000000a000a000 - 10000000b000b00010000002800280001000000290029000100000075007500 - 010000007800780001000001220122000100000123012300010123000100000 - 12401240001000001250125000100000126012600010000012a012a00010000 - 012b012b00010000012c012c00010000012d012d00010000012e012e0001000 - 0012f012f000100000130013000010000013101310001000001320132000100 - 000133013300010000013401340001000001350135000100000136013600010 - 000013701370001000001380138000100000139013900010000013b013b0001 - 0000013c013c00010000013d013d00010000013e013e00010000013f013f000 - 100000140014000010000014101410001000001420142000100000143014300 - 010000014701470001000001480148000100000149014900010000014b014b0 - 0010000014d014d00010000014e014e00010000014f014f0001000001500150 - 000100000151015100010000015201520001000001530153000100000154015 - 400010000015501550001000001560156000100000157015700010157000100 - 0001580158000100000159015900010000015a015a00010000015c015c00010 - 000015d015d0001000001620162000100000163016300010000016701670001 - 0000016b016b00010000017c017c0001014200010000017d017d00010000017 - e017e00010000017f017f000100000180018000010000018101810001000001 - 820182000100000183018300010000018401840001000001850185000100000 - 18601860001000001870187000100000189018900010000018a018a00010000 - 018b018b00010000018c018c00010000018d018d00010000018e018e0001000 - 0018f018f000100000190019000010000019101910001000001940194000101 - 2500010000019501950001000001960196000100000197019700010000019d0 - 19d00010000019e019e00010000019f019f0001000001a001a00001000001a1 - 01a10001000001a201a20001000001a301a30001000001a401a40001000001a - 501a50001000001a601a60001000001a701a70001000001a801a80001000001 - a901a90001000001aa01aa0001000001ab01ab0001000001ad01ad000100000 - 1ae01ae0001000001af01af0001000001b001b00001000001b101b100010000 - 01c101c10001000001c201c2000101250001000001c601c60001000001c701c - 70001000001c801c80001000001c901c90001000001ca01ca0001019f000100 - 0001cb01cb000101a00001000001cc01cc000101a20001000001cd01cd00010 - 1a30001000001ce01ce000101b10001000001cf01cf000101220001000001d2 - 01d20001000001d301d3000101ab0001000001d401d40001000001d501d5000 - 1000001d601d60001000001d701d70001000001d801d80001000001d901d900 - 01000001da01da0001000001db01db0001000001dc01dc0001000001dd01dd0 - 001000001de01de0001000001df01df0001000001e001e00001000001e101e1 - 0001000001e201e20001000001e301e30001016b0001000001e401e40001000 - 001e501e50001000001e601e60001000001ea01ea0001000001eb01eb000100 - 0001ec01ec0001000001ed01ed0001000001ee01ee0001000001ef01ef00010 - 00001f001f00001000001f201f20001000001f301f30001000001f401f40001 - 000001f501f50001000001f601f60001000001fd01fd0001000001fe01fe000 - 100000201020100010000020202020001000002040204000100000205020500 - 010000020602060001000002070207000100000208020800010000020902090 - 0010000020a020a00010000020b020b00010000020c020c00010000020d020d - 00010000020e020e00010000020f020f0001000002100210000100000211021 - 100010000021202120001000002130213000100000214021400010000021502 - 150001000002160216000100000217021700010000021802180001000002190 - 21900010000021a021a00010000021b021b0001000000030002000a00000004 - 0002000a0000000500010001000000060002000a000000070002000a0000000 - 9000100010000000d0000000e0000000f001700010000001000000011000000 - 12000000130000001400000015000000160000002700780001015d000101260 - 0010000003a003a0001000000440002000a00000045000000460000004a006d - 00010000004c0000005b0002000a0000005e000100010000005f00170001000 - 000600060000100000061006000010000006400640001000000650065000100 - 0000660066000100000068000000690000006a006a00010000006c006d00010 - 000006d006d00010000006e006f00010000006f006f00010000007000700001 - 000000710071000100000072007200010000007300730001000000740066000 - 100000076000000770000007900790001 - ]]) - else - return bin.pack(">SH", self.flags, "02b200b2004225060101010d010105010101010101017fff0309030301007f011" .. - "fff010301013f01010500010702010000180001800000003c3c3c80000000d007") - end - end, + if ( self.os:match("IBMPC/WIN_NT[64]*[-]%d%.%d%.%d") ) then + return bin.pack(">SH", self.flags, [[ + 02b200b2004225060101010d010105010101010101017fff0309030301007f0 + 11fff010301013f01010500010702010000180001800000003c3c3c80000000 + d007000100010001000000020002000a00000008000800010000000c000c000 + a00000017001700010000001800180001000000190019001800190001000000 + 1a001a0019001a00010000001b001b000a001b00010000001c001c0016001c0 + 0010000001d001d0017001d00010000001e001e0017001e00010000001f001f + 0019001f0001000000200020000a00200001000000210021000a00210001000 + 0000a000a00010000000b000b00010000002800280001000000290029000100 + 000075007500010000007800780001000001220122000100000123012300010 + 12300010000012401240001000001250125000100000126012600010000012a + 012a00010000012b012b00010000012c012c00010000012d012d00010000012 + e012e00010000012f012f000100000130013000010000013101310001000001 + 320132000100000133013300010000013401340001000001350135000100000 + 136013600010000013701370001000001380138000100000139013900010000 + 013b013b00010000013c013c00010000013d013d00010000013e013e0001000 + 0013f013f000100000140014000010000014101410001000001420142000100 + 000143014300010000014701470001000001480148000100000149014900010 + 000014b014b00010000014d014d00010000014e014e00010000014f014f0001 + 000001500150000100000151015100010000015201520001000001530153000 + 100000154015400010000015501550001000001560156000100000157015700 + 0101570001000001580158000100000159015900010000015a015a000100000 + 15c015c00010000015d015d0001000001620162000100000163016300010000 + 0167016700010000016b016b00010000017c017c0001014200010000017d017 + d00010000017e017e00010000017f017f000100000180018000010000018101 + 810001000001820182000100000183018300010000018401840001000001850 + 18500010000018601860001000001870187000100000189018900010000018a + 018a00010000018b018b00010000018c018c00010000018d018d00010000018 + e018e00010000018f018f000100000190019000010000019101910001000001 + 940194000101250001000001950195000100000196019600010000019701970 + 0010000019d019d00010000019e019e00010000019f019f0001000001a001a0 + 0001000001a101a10001000001a201a20001000001a301a30001000001a401a + 40001000001a501a50001000001a601a60001000001a701a70001000001a801 + a80001000001a901a90001000001aa01aa0001000001ab01ab0001000001ad0 + 1ad0001000001ae01ae0001000001af01af0001000001b001b00001000001b1 + 01b10001000001c101c10001000001c201c2000101250001000001c601c6000 + 1000001c701c70001000001c801c80001000001c901c90001000001ca01ca00 + 01019f0001000001cb01cb000101a00001000001cc01cc000101a2000100000 + 1cd01cd000101a30001000001ce01ce000101b10001000001cf01cf00010122 + 0001000001d201d20001000001d301d3000101ab0001000001d401d40001000 + 001d501d50001000001d601d60001000001d701d70001000001d801d8000100 + 0001d901d90001000001da01da0001000001db01db0001000001dc01dc00010 + 00001dd01dd0001000001de01de0001000001df01df0001000001e001e00001 + 000001e101e10001000001e201e20001000001e301e30001016b0001000001e + 401e40001000001e501e50001000001e601e60001000001ea01ea0001000001 + eb01eb0001000001ec01ec0001000001ed01ed0001000001ee01ee000100000 + 1ef01ef0001000001f001f00001000001f201f20001000001f301f300010000 + 01f401f40001000001f501f50001000001f601f60001000001fd01fd0001000 + 001fe01fe000100000201020100010000020202020001000002040204000100 + 000205020500010000020602060001000002070207000100000208020800010 + 0000209020900010000020a020a00010000020b020b00010000020c020c0001 + 0000020d020d00010000020e020e00010000020f020f0001000002100210000 + 100000211021100010000021202120001000002130213000100000214021400 + 010000021502150001000002160216000100000217021700010000021802180 + 00100000219021900010000021a021a00010000021b021b00010000021c021c + 00010000021d021d00010000021e021e00010000021f021f000100000220022 + 000010000022102210001000002220222000100000223022300010000022402 + 240001000002250225000100000226022600010000022702270001000002280 + 228000100000229022900010000022a022a00010000022b022b00010000022c + 022c00010000022d022d00010000022e022e00010000022f022f00010000023 + 102310001000002320232000100000233023300010000023402340001000002 + 3702370001000002380238000100000239023900010000023a023a000100000 + 23b023b00010000023c023c00010000023d023d00010000023e023e00010000 + 023f023f0001000002400240000100000241024100010000024202420001000 + 002430243000100000244024400010000 + ]]) + elseif ( "x86_64/Linux 2.4.xx" == self.os ) then + return bin.pack(">SH", self.flags, [[ + 02b200b2004221060101010d01010401010101010101ffff0308030001003f0 + 1073f010101010301050201000018800000003c3c3c80000000d00700010001 + 0001000000020002000a00000008000800010000000c000c000a00000017001 + 7000100000018001800010000001900190018001900010000001a001a001900 + 1a00010000001b001b000a001b00010000001c001c0016001c00010000001d0 + 01d0017001d00010000001e001e0017001e00010000001f001f0019001f0001 + 000000200020000a00200001000000210021000a002100010000000a000a000 + 10000000b000b00010000002800280001000000290029000100000075007500 + 010000007800780001000001220122000100000123012300010123000100000 + 12401240001000001250125000100000126012600010000012a012a00010000 + 012b012b00010000012c012c00010000012d012d00010000012e012e0001000 + 0012f012f000100000130013000010000013101310001000001320132000100 + 000133013300010000013401340001000001350135000100000136013600010 + 000013701370001000001380138000100000139013900010000013b013b0001 + 0000013c013c00010000013d013d00010000013e013e00010000013f013f000 + 100000140014000010000014101410001000001420142000100000143014300 + 010000014701470001000001480148000100000149014900010000014b014b0 + 0010000014d014d00010000014e014e00010000014f014f0001000001500150 + 000100000151015100010000015201520001000001530153000100000154015 + 400010000015501550001000001560156000100000157015700010157000100 + 0001580158000100000159015900010000015a015a00010000015c015c00010 + 000015d015d0001000001620162000100000163016300010000016701670001 + 0000016b016b00010000017c017c0001014200010000017d017d00010000017 + e017e00010000017f017f000100000180018000010000018101810001000001 + 820182000100000183018300010000018401840001000001850185000100000 + 18601860001000001870187000100000189018900010000018a018a00010000 + 018b018b00010000018c018c00010000018d018d00010000018e018e0001000 + 0018f018f000100000190019000010000019101910001000001940194000101 + 2500010000019501950001000001960196000100000197019700010000019d0 + 19d00010000019e019e00010000019f019f0001000001a001a00001000001a1 + 01a10001000001a201a20001000001a301a30001000001a401a40001000001a + 501a50001000001a601a60001000001a701a70001000001a801a80001000001 + a901a90001000001aa01aa0001000001ab01ab0001000001ad01ad000100000 + 1ae01ae0001000001af01af0001000001b001b00001000001b101b100010000 + 01c101c10001000001c201c2000101250001000001c601c60001000001c701c + 70001000001c801c80001000001c901c90001000001ca01ca0001019f000100 + 0001cb01cb000101a00001000001cc01cc000101a20001000001cd01cd00010 + 1a30001000001ce01ce000101b10001000001cf01cf000101220001000001d2 + 01d20001000001d301d3000101ab0001000001d401d40001000001d501d5000 + 1000001d601d60001000001d701d70001000001d801d80001000001d901d900 + 01000001da01da0001000001db01db0001000001dc01dc0001000001dd01dd0 + 001000001de01de0001000001df01df0001000001e001e00001000001e101e1 + 0001000001e201e20001000001e301e30001016b0001000001e401e40001000 + 001e501e50001000001e601e60001000001ea01ea0001000001eb01eb000100 + 0001ec01ec0001000001ed01ed0001000001ee01ee0001000001ef01ef00010 + 00001f001f00001000001f201f20001000001f301f30001000001f401f40001 + 000001f501f50001000001f601f60001000001fd01fd0001000001fe01fe000 + 100000201020100010000020202020001000002040204000100000205020500 + 010000020602060001000002070207000100000208020800010000020902090 + 0010000020a020a00010000020b020b00010000020c020c00010000020d020d + 00010000020e020e00010000020f020f0001000002100210000100000211021 + 100010000021202120001000002130213000100000214021400010000021502 + 150001000002160216000100000217021700010000021802180001000002190 + 21900010000021a021a00010000021b021b0001000000030002000a00000004 + 0002000a0000000500010001000000060002000a000000070002000a0000000 + 9000100010000000d0000000e0000000f001700010000001000000011000000 + 12000000130000001400000015000000160000002700780001015d000101260 + 0010000003a003a0001000000440002000a00000045000000460000004a006d + 00010000004c0000005b0002000a0000005e000100010000005f00170001000 + 000600060000100000061006000010000006400640001000000650065000100 + 0000660066000100000068000000690000006a006a00010000006c006d00010 + 000006d006d00010000006e006f00010000006f006f00010000007000700001 + 000000710071000100000072007200010000007300730001000000740066000 + 100000076000000770000007900790001 + ]]) + else + return bin.pack(">SH", self.flags, "02b200b2004225060101010d010105010101010101017fff0309030301007f011" .. + "fff010301013f01010500010702010000180001800000003c3c3c80000000d007") + end + end, } @@ -831,105 +831,105 @@ Packet.Unknown1 = { --- This packet is only used by Oracle10 and older Packet.Unknown2 = { - tns_type = Packet.TNS.Type.DATA, - flags = 0, + tns_type = Packet.TNS.Type.DATA, + flags = 0, - new = function(self, os) - local o = { os = os } - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self, os) + local o = { os = os } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - if ( "x86_64/Linux 2.4.xx" == self.os ) then - return bin.pack(">SH", self.flags, [[ - 0000007a007a00010000007b007b00010000008800000092009200010000009 - 300930001000000980002000a000000990002000a0000009a0002000a000000 - 9b000100010000009c000c000a000000ac0002000a000000b200b2000100000 - 0b300b30001000000b400b40001000000b500b50001000000b600b600010000 - 00b700b70001000000b8000c000a000000b900b20001000000ba00b30001000 - 000bb00b40001000000bc00b50001000000bd00b60001000000be00b7000100 - 0000bf000000c0000000c300700001000000c400710001000000c5007200010 - 00000d000d00001000000d1000000e700e70001000000e800e70001000000e9 - 00e90001000000f1006d0001000002030203000100000000]] - ) - else - return bin.pack(">SH", self.flags, [[ - 024502450001000002460246000100000247024700010000024802480001000 - 0024902490001000000030002000a000000040002000a000000050001000100 - 0000060002000a000000070002000a00000009000100010000000d0000000e0 - 000000f00170001000000100000001100000012000000130000001400000015 - 000000160000002700780001015d0001012600010000003a003a00010000004 - 40002000a00000045000000460000004a006d00010000004c0000005b000200 - 0a0000005e000100010000005f0017000100000060006000010000006100600 - 001000000640064000100000065006500010000006600660001000000680000 - 00690000006a006a00010000006c006d00010000006d006d00010000006e006 - f00010000006f006f0001000000700070000100000071007100010000007200 - 720001000000730073000100000074006600010000007600000077000000790 - 07900010000007a007a00010000007b007b0001000000880000009200920001 - 0000009300930001000000980002000a000000990002000a0000009a0002000 - a0000009b000100010000009c000c000a000000ac0002000a000000b200b200 - 01000000b300b30001000000b400b40001000000b500b50001000000b600b60 - 001000000b700b70001000000b8000c000a000000b900b20001000000ba00b3 - 0001000000bb00b40001000000bc00b50001000000bd00b60001000000be00b - 70001000000bf000000c0000000c300700001000000c400710001000000c500 - 720001000000d000d00001000000d1000000e700e70001000000e800e700010 - 00000e900e90001000000f1006d0001000002030203000100000000 - ]]) - end - end, + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + if ( "x86_64/Linux 2.4.xx" == self.os ) then + return bin.pack(">SH", self.flags, [[ + 0000007a007a00010000007b007b00010000008800000092009200010000009 + 300930001000000980002000a000000990002000a0000009a0002000a000000 + 9b000100010000009c000c000a000000ac0002000a000000b200b2000100000 + 0b300b30001000000b400b40001000000b500b50001000000b600b600010000 + 00b700b70001000000b8000c000a000000b900b20001000000ba00b30001000 + 000bb00b40001000000bc00b50001000000bd00b60001000000be00b7000100 + 0000bf000000c0000000c300700001000000c400710001000000c5007200010 + 00000d000d00001000000d1000000e700e70001000000e800e70001000000e9 + 00e90001000000f1006d0001000002030203000100000000]] + ) + else + return bin.pack(">SH", self.flags, [[ + 024502450001000002460246000100000247024700010000024802480001000 + 0024902490001000000030002000a000000040002000a000000050001000100 + 0000060002000a000000070002000a00000009000100010000000d0000000e0 + 000000f00170001000000100000001100000012000000130000001400000015 + 000000160000002700780001015d0001012600010000003a003a00010000004 + 40002000a00000045000000460000004a006d00010000004c0000005b000200 + 0a0000005e000100010000005f0017000100000060006000010000006100600 + 001000000640064000100000065006500010000006600660001000000680000 + 00690000006a006a00010000006c006d00010000006d006d00010000006e006 + f00010000006f006f0001000000700070000100000071007100010000007200 + 720001000000730073000100000074006600010000007600000077000000790 + 07900010000007a007a00010000007b007b0001000000880000009200920001 + 0000009300930001000000980002000a000000990002000a0000009a0002000 + a0000009b000100010000009c000c000a000000ac0002000a000000b200b200 + 01000000b300b30001000000b400b40001000000b500b50001000000b600b60 + 001000000b700b70001000000b8000c000a000000b900b20001000000ba00b3 + 0001000000bb00b40001000000bc00b50001000000bd00b60001000000be00b + 70001000000bf000000c0000000c300700001000000c400710001000000c500 + 720001000000d000d00001000000d1000000e700e70001000000e800e700010 + 00000e900e90001000000f1006d0001000002030203000100000000 + ]]) + end + end, } -- Signals that we're about to close the connection Packet.EOF = { - tns_type = Packet.TNS.Type.DATA, - flags = 0x0040, + tns_type = Packet.TNS.Type.DATA, + flags = 0x0040, - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - return o - end, + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - return bin.pack(">S", self.flags ) - end + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + return bin.pack(">S", self.flags ) + end } Packet.PostLogin = { - tns_type = Packet.TNS.Type.DATA, - flags = 0x0000, + tns_type = Packet.TNS.Type.DATA, + flags = 0x0000, - -- Creates a new PostLogin instance - -- - -- @param sessid number containing session id - -- @return o a new instance of PostLogin - new = function(self, sessid) - local o = { sessid = sessid } - setmetatable(o, self) - self.__index = self - return o - end, + -- Creates a new PostLogin instance + -- + -- @param sessid number containing session id + -- @return o a new instance of PostLogin + new = function(self, sessid) + local o = { sessid = sessid } + setmetatable(o, self) + self.__index = self + return o + end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - local unknown1 = "116b04" - local unknown2 = "0000002200000001000000033b05fefffffff4010000fefffffffeffffff" - return bin.pack(">SHCH", self.flags, unknown1, tonumber(self.sessid), unknown2 ) - end + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + local unknown1 = "116b04" + local unknown2 = "0000002200000001000000033b05fefffffff4010000fefffffffeffffff" + return bin.pack(">SHCH", self.flags, unknown1, tonumber(self.sessid), unknown2 ) + end } @@ -938,105 +938,105 @@ Packet.PostLogin = { -- guesswork. Packet.Query = { - tns_type = Packet.TNS.Type.DATA, - flags = 0x0000, + tns_type = Packet.TNS.Type.DATA, + flags = 0x0000, - --- Creates a new instance of Query - -- @param query string containing the SQL query - -- @return instance of Query - new = function(self, query) - local o = { query = query, counter = 0 } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new instance of Query + -- @param query string containing the SQL query + -- @return instance of Query + new = function(self, query) + local o = { query = query, counter = 0 } + setmetatable(o, self) + self.__index = self + return o + end, - --- Gets the current counter value - -- @return counter number containing the current counter value - getCounter = function(self) return self.counter end, + --- Gets the current counter value + -- @return counter number containing the current counter value + getCounter = function(self) return self.counter end, - --- Sets the current counter value - -- This function is called from sendTNSPacket - -- @param counter number containing the counter value to set - setCounter = function(self, counter) self.counter = counter end, + --- Sets the current counter value + -- This function is called from sendTNSPacket + -- @param counter number containing the counter value to set + setCounter = function(self, counter) self.counter = counter end, - --- Converts the DATA packet to string - -- - -- @return string containing the packet - __tostring = function( self ) - local unknown1 = "035e" - local unknown2 = "6180000000000000feffffff" - local unknown3 = "000000feffffff0d000000fefffffffeffffff000000000100000000000000000000000000000000000000feffffff00000000fefffffffeffffff54d25d020000000000000000fefffffffeffffff0000000000000000000000000000000000000000" - local unknown4 = "01000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000" - return bin.pack(">SHCHCHCAH", self.flags, unknown1, self.counter, unknown2, #self.query, unknown3, #self.query, self.query, unknown4 ) - end, + --- Converts the DATA packet to string + -- + -- @return string containing the packet + __tostring = function( self ) + local unknown1 = "035e" + local unknown2 = "6180000000000000feffffff" + local unknown3 = "000000feffffff0d000000fefffffffeffffff000000000100000000000000000000000000000000000000feffffff00000000fefffffffeffffff54d25d020000000000000000fefffffffeffffff0000000000000000000000000000000000000000" + local unknown4 = "01000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000" + return bin.pack(">SHCHCHCAH", self.flags, unknown1, self.counter, unknown2, #self.query, unknown3, #self.query, self.query, unknown4 ) + end, - --- Parses the Query response from the server - -- @param tns response as received from the Comm.recvTNSPacket - -- function. - -- @return result table containing: - -- columns - a column indexed table with the column names - -- types - a column indexed table with the data types - -- rows - a table containing a row table for each row - -- the row table is a column indexed table of - -- column values. - parseResponse = function( self, tns ) - local data = tns.data - local result = {} + --- Parses the Query response from the server + -- @param tns response as received from the Comm.recvTNSPacket + -- function. + -- @return result table containing: + -- columns - a column indexed table with the column names + -- types - a column indexed table with the data types + -- rows - a table containing a row table for each row + -- the row table is a column indexed table of + -- column values. + parseResponse = function( self, tns ) + local data = tns.data + local result = {} - local pos, columns = bin.unpack("C", tns.data, 35) + local pos, columns = bin.unpack("C", tns.data, 35) - pos = 40 - for i=1, columns do - local sql_type - pos, sql_type = bin.unpack("C", data, pos) - pos = pos + 34 - local name, len - pos, len = bin.unpack("C", tns.data, pos) - pos, name= bin.unpack("A" .. len, tns.data, pos) - result.columns = result.columns or {} - result.types = result.types or {} - table.insert(result.columns, name) - table.insert(result.types, sql_type) - pos = pos + 10 - end + pos = 40 + for i=1, columns do + local sql_type + pos, sql_type = bin.unpack("C", data, pos) + pos = pos + 34 + local name, len + pos, len = bin.unpack("C", tns.data, pos) + pos, name= bin.unpack("A" .. len, tns.data, pos) + result.columns = result.columns or {} + result.types = result.types or {} + table.insert(result.columns, name) + table.insert(result.types, sql_type) + pos = pos + 10 + end - pos = pos + 55 + pos = pos + 55 - result.rows = {} - local row = {} - for i=1, columns do - local val, len - pos, len = bin.unpack("C", tns.data, pos) - pos, val = bin.unpack("A" .. len, tns.data, pos) + result.rows = {} + local row = {} + for i=1, columns do + local val, len + pos, len = bin.unpack("C", tns.data, pos) + pos, val = bin.unpack("A" .. len, tns.data, pos) - -- if we're at the first row and first column and the len is 0 - -- assume we got an empty resultset - if ( len == 0 and #result.rows == 0 and i == 1 ) then - return true, { data = result, moredata = false } - end + -- if we're at the first row and first column and the len is 0 + -- assume we got an empty resultset + if ( len == 0 and #result.rows == 0 and i == 1 ) then + return true, { data = result, moredata = false } + end - local sql_type = result.types[i] - if ( DataTypeDecoders[sql_type] ) then - val = DataTypeDecoders[sql_type](val) - end - table.insert(row, val) - end - table.insert(result.rows, row) + local sql_type = result.types[i] + if ( DataTypeDecoders[sql_type] ) then + val = DataTypeDecoders[sql_type](val) + end + table.insert(row, val) + end + table.insert(result.rows, row) - local moredata = true - -- check if we've got any more data? - if ( #data > pos + 97 ) then - local len, err - pos, len = bin.unpack(">S", data, pos + 97) - pos, err = bin.unpack("A" .. len, data, pos) - if ( err:match("^ORA%-01403") ) then - moredata = false - end - end + local moredata = true + -- check if we've got any more data? + if ( #data > pos + 97 ) then + local len, err + pos, len = bin.unpack(">S", data, pos + 97) + pos, err = bin.unpack("A" .. len, data, pos) + if ( err:match("^ORA%-01403") ) then + moredata = false + end + end - return true, { data = result, moredata = moredata } - end, + return true, { data = result, moredata = moredata } + end, } -- Class responsible for acknowledging a query response from the server @@ -1044,774 +1044,774 @@ Packet.Query = { -- is mostly based on packet captures and guesswork. Packet.QueryResponseAck = { - tns_type = Packet.TNS.Type.DATA, - flags = 0x0000, + tns_type = Packet.TNS.Type.DATA, + flags = 0x0000, - --- Creates a new QueryResponseAck instance - -- @param result table containing the results as received from the - -- Query.parseResponse function. - -- @return instance new instance of QueryResponseAck - new = function(self, result) - local o = { result = result } - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new QueryResponseAck instance + -- @param result table containing the results as received from the + -- Query.parseResponse function. + -- @return instance new instance of QueryResponseAck + new = function(self, result) + local o = { result = result } + setmetatable(o, self) + self.__index = self + return o + end, - --- Gets the current counter value - -- @return counter number containing the current counter value - getCounter = function(self) return self.counter end, + --- Gets the current counter value + -- @return counter number containing the current counter value + getCounter = function(self) return self.counter end, - --- Sets the current counter value - -- This function is called from sendTNSPacket - -- @param counter number containing the counter value to set - setCounter = function(self, counter) self.counter = counter end, + --- Sets the current counter value + -- This function is called from sendTNSPacket + -- @param counter number containing the counter value to set + setCounter = function(self, counter) self.counter = counter end, - --- Serializes the packet into a string suitable to be sent to the DB - -- server. - -- @return str string containing the serialized packet - __tostring = function(self) - return bin.pack(">SHCH", self.flags, "0305", self.counter, "030000000f000000") - end, + --- Serializes the packet into a string suitable to be sent to the DB + -- server. + -- @return str string containing the serialized packet + __tostring = function(self) + return bin.pack(">SHCH", self.flags, "0305", self.counter, "030000000f000000") + end, - -- - -- This is how I (Patrik Karlsson) think this is supposed to work - -- At this point we have the 2nd row (the query response has the first) - -- Every row looks like this, where the leading mask marker (0x15) and mask - -- is optional. - -- | (mask mark)| (bytes) | sor | byte | len * bytes | ... next_column | - -- | 0x15 | [mask] | 0x07 | [len]| [column_val]| ... next_column | + -- + -- This is how I (Patrik Karlsson) think this is supposed to work + -- At this point we have the 2nd row (the query response has the first) + -- Every row looks like this, where the leading mask marker (0x15) and mask + -- is optional. + -- | (mask mark)| (bytes) | sor | byte | len * bytes | ... next_column | + -- | 0x15 | [mask] | 0x07 | [len]| [column_val]| ... next_column | - -- The mask is used in order to achieve "compression" and is essentially - -- at a bit mask that decides what columns should be fetched from the - -- preceeding row. The mask is provided in reverse order and a set bit - -- indicates that data is provided while an unset bit indicates that the - -- column data should be fetched from the previous row. - -- - -- Once a value is fetched the sql data type is verified against the - -- DataTypeDecoder table. If there's a function registered for the fetched - -- data it is run through a decoder, that decodes the *real* value from - -- the encoded data. - -- - parseResponse = function( self, tns ) - local data = tns.data - local pos, len = bin.unpack("C", data, 21) - local mask = "" + -- The mask is used in order to achieve "compression" and is essentially + -- at a bit mask that decides what columns should be fetched from the + -- preceeding row. The mask is provided in reverse order and a set bit + -- indicates that data is provided while an unset bit indicates that the + -- column data should be fetched from the previous row. + -- + -- Once a value is fetched the sql data type is verified against the + -- DataTypeDecoder table. If there's a function registered for the fetched + -- data it is run through a decoder, that decodes the *real* value from + -- the encoded data. + -- + parseResponse = function( self, tns ) + local data = tns.data + local pos, len = bin.unpack("C", data, 21) + local mask = "" - -- calculate the initial mask - if ( len > 0 ) then - while( len > 0) do - local mask_part - pos, mask_part = bin.unpack("B", data, pos) - mask = mask .. mask_part:reverse() - len = len - 1 - end - pos = pos + 4 - else - pos = pos +3 - end + -- calculate the initial mask + if ( len > 0 ) then + while( len > 0) do + local mask_part + pos, mask_part = bin.unpack("B", data, pos) + mask = mask .. mask_part:reverse() + len = len - 1 + end + pos = pos + 4 + else + pos = pos +3 + end - while(true) do - local row = {} - local result = self.result - local cols = #result.columns + while(true) do + local row = {} + local result = self.result + local cols = #result.columns - -- check for start of data marker - local marker - pos, marker = bin.unpack("C", data, pos) - if ( marker == 0x15 ) then - mask = "" - local _ - -- not sure what this value is - pos, _ = bin.unpack(" 0 ) do - local mask_part - pos, mask_part = bin.unpack("B", data, pos) - mask = mask .. mask_part:reverse() - len = len - 8 - end - pos, marker = bin.unpack("C", data, pos) - end - if ( marker ~= 0x07 ) then - stdnse.print_debug(2, "Encountered unknown marker: %d", marker) - break - end + -- calculate the bitmask for the columns that do contain + -- data. + len = cols + while( len > 0 ) do + local mask_part + pos, mask_part = bin.unpack("B", data, pos) + mask = mask .. mask_part:reverse() + len = len - 8 + end + pos, marker = bin.unpack("C", data, pos) + end + if ( marker ~= 0x07 ) then + stdnse.print_debug(2, "Encountered unknown marker: %d", marker) + break + end - local val - local rows = self.result.rows - for col=1, cols do - if ( #mask > 0 and mask:sub(col, col) == '0' ) then - val = rows[#rows][col] - else - pos, len = bin.unpack("C", data, pos) - pos, val = bin.unpack("A" .. len, data, pos) + local val + local rows = self.result.rows + for col=1, cols do + if ( #mask > 0 and mask:sub(col, col) == '0' ) then + val = rows[#rows][col] + else + pos, len = bin.unpack("C", data, pos) + pos, val = bin.unpack("A" .. len, data, pos) - local sql_type = result.types[col] - if ( DataTypeDecoders[sql_type] ) then - val = DataTypeDecoders[sql_type](val) - end - end - table.insert(row, val) - end + local sql_type = result.types[col] + if ( DataTypeDecoders[sql_type] ) then + val = DataTypeDecoders[sql_type](val) + end + end + table.insert(row, val) + end - -- add row to result - table.insert(rows, row) - end + -- add row to result + table.insert(rows, row) + end - return true, tns.data - end, + return true, tns.data + end, } Marshaller = { - --- Marshals a TNS key-value pair data structure - -- - -- @param key The key - -- @param value The value - -- @param flags The flags - -- @return A binary packed string representing the KVP structure - marshalKvp = function( key, value, flags ) - return Marshaller.marshalKvpComponent( key ) .. - Marshaller.marshalKvpComponent( value ) .. - bin.pack( " 0 ) then - -- 64 bytes seems to be the maximum length before Oracle starts - -- chunking strings - local MAX_CHUNK_LENGTH = 64 - local split_into_chunks = ( #value > MAX_CHUNK_LENGTH ) + result = result .. bin.pack( " 0 ) then + -- 64 bytes seems to be the maximum length before Oracle starts + -- chunking strings + local MAX_CHUNK_LENGTH = 64 + local split_into_chunks = ( #value > MAX_CHUNK_LENGTH ) - if ( not( split_into_chunks ) ) then - -- It's pretty easy if we don't have to split up the string - result = result .. bin.pack( "p", value ) - else - -- Otherwise, it's a bit more involved: - -- First, write the multiple-chunk indicator - result = result .. bin.pack( "C", 0xFE ) + if ( not( split_into_chunks ) ) then + -- It's pretty easy if we don't have to split up the string + result = result .. bin.pack( "p", value ) + else + -- Otherwise, it's a bit more involved: + -- First, write the multiple-chunk indicator + result = result .. bin.pack( "C", 0xFE ) - -- Loop through the string, chunk by chunk - while ( #value > 0 ) do - -- Figure out how much we're writing in this chunk, the - -- remainder of the string, or the maximum, whichever is less - local write_length = MAX_CHUNK_LENGTH - if (#value < MAX_CHUNK_LENGTH) then - write_length = #value - end + -- Loop through the string, chunk by chunk + while ( #value > 0 ) do + -- Figure out how much we're writing in this chunk, the + -- remainder of the string, or the maximum, whichever is less + local write_length = MAX_CHUNK_LENGTH + if (#value < MAX_CHUNK_LENGTH) then + write_length = #value + end - -- get a substring of what we're going to write... - local write_value = value:sub( 1, write_length ) - -- ...and remove that piece from the remaining string - value = value:sub( write_length + 1 ) - result = result .. bin.pack( "p", write_value ) - end + -- get a substring of what we're going to write... + local write_value = value:sub( 1, write_length ) + -- ...and remove that piece from the remaining string + value = value:sub( write_length + 1 ) + result = result .. bin.pack( "p", write_value ) + end - -- put a null byte at the end - result = result .. bin.pack( "C", 0 ) - end - end + -- put a null byte at the end + result = result .. bin.pack( "C", 0 ) + end + end - return result - end, + return result + end, - --- Parses a key or value element from a TNS key-value pair data structure. - -- - -- @param data Packed string to parse - -- @param pos Position in the string at which the element begins - -- @return table containing the last position read and the value parsed - unmarshalKvpComponent = function( data, pos ) - local value_len, chunk_len - local value, chunk = "", "" - local has_multiple_chunks = false + --- Parses a key or value element from a TNS key-value pair data structure. + -- + -- @param data Packed string to parse + -- @param pos Position in the string at which the element begins + -- @return table containing the last position read and the value parsed + unmarshalKvpComponent = function( data, pos ) + local value_len, chunk_len + local value, chunk = "", "" + local has_multiple_chunks = false - -- read the 32-bit total length of the value - pos, value_len = bin.unpack("S", header ) - local status, data = self.socket:receive_buf( match.numbytes(length - 8), true ) - if ( not(status) ) then - return false, data - else - return status, Packet.TNS.parse(header .. data) - end - end + local _, length = bin.unpack(">S", header ) + local status, data = self.socket:receive_buf( match.numbytes(length - 8), true ) + if ( not(status) ) then + return false, data + else + return status, Packet.TNS.parse(header .. data) + end + end - local status - status, tns = recv() - if ( not(status) ) then - if ( retries == 0 ) then - return false, "ERROR: recvTNSPacket failed to receive TNS headers" - end - retries = retries - 1 - elseif ( tns.type == Packet.TNS.Type.RESEND ) then - self:sendTNSPacket( self.pkt ) - end - until ( status and tns.type ~= Packet.TNS.Type.RESEND ) + local status + status, tns = recv() + if ( not(status) ) then + if ( retries == 0 ) then + return false, "ERROR: recvTNSPacket failed to receive TNS headers" + end + retries = retries - 1 + elseif ( tns.type == Packet.TNS.Type.RESEND ) then + self:sendTNSPacket( self.pkt ) + end + until ( status and tns.type ~= Packet.TNS.Type.RESEND ) - return true, tns - end, + return true, tns + end, - --- Sends a TNS packet and recieves (and handles) the response - -- - -- @param pkt containingt the Packet.* to send to the server - -- @return status true on success, false on failure - -- @return the parsed response as return from the respective parseResponse - -- function or error message if status was false - exchTNSPacket = function( self, pkt ) - local status = self:sendTNSPacket( pkt ) - local tns, response + --- Sends a TNS packet and recieves (and handles) the response + -- + -- @param pkt containingt the Packet.* to send to the server + -- @return status true on success, false on failure + -- @return the parsed response as return from the respective parseResponse + -- function or error message if status was false + exchTNSPacket = function( self, pkt ) + local status = self:sendTNSPacket( pkt ) + local tns, response - if ( not(status) ) then - return false, "sendTNSPacket failed" - end + if ( not(status) ) then + return false, "sendTNSPacket failed" + end - status, tns = self:recvTNSPacket() - if ( not(status) ) then - return false, tns - end + status, tns = self:recvTNSPacket() + if ( not(status) ) then + return false, tns + end - --- handle TNS MARKERS - if ( tns.type == Packet.TNS.Type.MARKER ) then - return self:handleMarker() - end + --- handle TNS MARKERS + if ( tns.type == Packet.TNS.Type.MARKER ) then + return self:handleMarker() + end - if ( pkt.parseResponse ) then - status, response = pkt:parseResponse( tns ) - end + if ( pkt.parseResponse ) then + status, response = pkt:parseResponse( tns ) + end - return status, response - end + return status, response + end } --- Class that handles all Oracle encryption Crypt = { - -- Test function, not currently in use - Decrypt11g = function(self, c_sesskey, s_sesskey, auth_password, pass, salt ) - local combined_sesskey = "" - local sha1 = openssl.sha1(pass .. salt) .. "\0\0\0\0" - local auth_sesskey = s_sesskey - local auth_sesskey_c = c_sesskey - local server_sesskey = openssl.decrypt( "aes-192-cbc", sha1, nil, auth_sesskey ) - local client_sesskey = openssl.decrypt( "aes-192-cbc", sha1, nil, auth_sesskey_c ) + -- Test function, not currently in use + Decrypt11g = function(self, c_sesskey, s_sesskey, auth_password, pass, salt ) + local combined_sesskey = "" + local sha1 = openssl.sha1(pass .. salt) .. "\0\0\0\0" + local auth_sesskey = s_sesskey + local auth_sesskey_c = c_sesskey + local server_sesskey = openssl.decrypt( "aes-192-cbc", sha1, nil, auth_sesskey ) + local client_sesskey = openssl.decrypt( "aes-192-cbc", sha1, nil, auth_sesskey_c ) - combined_sesskey = "" - for i=17, 40 do - combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(server_sesskey, i), string.byte(client_sesskey,i) ) ) - end - combined_sesskey = ( openssl.md5( combined_sesskey:sub(1,16) ) .. openssl.md5( combined_sesskey:sub(17) ) ):sub(1, 24) + combined_sesskey = "" + for i=17, 40 do + combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(server_sesskey, i), string.byte(client_sesskey,i) ) ) + end + combined_sesskey = ( openssl.md5( combined_sesskey:sub(1,16) ) .. openssl.md5( combined_sesskey:sub(17) ) ):sub(1, 24) - local p = openssl.decrypt( "aes-192-cbc", combined_sesskey, nil, auth_password, false ) - return p:sub(17) - end, + local p = openssl.decrypt( "aes-192-cbc", combined_sesskey, nil, auth_password, false ) + return p:sub(17) + end, - --- Creates an Oracle 10G password hash - -- - -- @param username containing the Oracle user name - -- @param password containing the Oracle user password - -- @return hash containing the Oracle hash - HashPassword10g = function( self, username, password ) - local uspw = ( username .. password ):gsub("(%w)", "\0%1") - local key = bin.pack("H", "0123456789abcdef") + --- Creates an Oracle 10G password hash + -- + -- @param username containing the Oracle user name + -- @param password containing the Oracle user password + -- @return hash containing the Oracle hash + HashPassword10g = function( self, username, password ) + local uspw = ( username .. password ):gsub("(%w)", "\0%1") + local key = bin.pack("H", "0123456789abcdef") - -- do padding - if ( #uspw % 8 > 0 ) then - for i=1,(8-(#uspw % 8)) do - uspw = uspw .. "\0" - end - end + -- do padding + if ( #uspw % 8 > 0 ) then + for i=1,(8-(#uspw % 8)) do + uspw = uspw .. "\0" + end + end - local iv2 = openssl.encrypt( "DES-CBC", key, nil, uspw, false ):sub(-8) - local enc = openssl.encrypt( "DES-CBC", iv2, nil, uspw, false ):sub(-8) - return enc - end, + local iv2 = openssl.encrypt( "DES-CBC", key, nil, uspw, false ):sub(-8) + local enc = openssl.encrypt( "DES-CBC", iv2, nil, uspw, false ):sub(-8) + return enc + end, - -- Test function, not currently in use - Decrypt10g = function(self, user, pass, srv_sesskey_enc ) - local pwhash = self:HashPassword10g( user:upper(), pass:upper() ) .. "\0\0\0\0\0\0\0\0" - local cli_sesskey_enc = bin.pack("H", "7B244D7A1DB5ABE553FB9B7325110024911FCBE95EF99E7965A754BC41CF31C0") - local srv_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, srv_sesskey_enc ) - local cli_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, cli_sesskey_enc ) - local auth_pass = bin.pack("H", "4C5E28E66B6382117F9D41B08957A3B9E363B42760C33B44CA5D53EA90204ABE" ) - local combined_sesskey = "" - local pass + -- Test function, not currently in use + Decrypt10g = function(self, user, pass, srv_sesskey_enc ) + local pwhash = self:HashPassword10g( user:upper(), pass:upper() ) .. "\0\0\0\0\0\0\0\0" + local cli_sesskey_enc = bin.pack("H", "7B244D7A1DB5ABE553FB9B7325110024911FCBE95EF99E7965A754BC41CF31C0") + local srv_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, srv_sesskey_enc ) + local cli_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, cli_sesskey_enc ) + local auth_pass = bin.pack("H", "4C5E28E66B6382117F9D41B08957A3B9E363B42760C33B44CA5D53EA90204ABE" ) + local combined_sesskey = "" + local pass - for i=17, 32 do - combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) ) - end - combined_sesskey = openssl.md5( combined_sesskey ) + for i=17, 32 do + combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) ) + end + combined_sesskey = openssl.md5( combined_sesskey ) - pass = openssl.decrypt( "AES-128-CBC", combined_sesskey, nil, auth_pass ):sub(17) + pass = openssl.decrypt( "AES-128-CBC", combined_sesskey, nil, auth_pass ):sub(17) - print( select(2, bin.unpack("H" .. #srv_sesskey, srv_sesskey ))) - print( select(2, bin.unpack("H" .. #cli_sesskey, cli_sesskey ))) - print( select(2, bin.unpack("H" .. #combined_sesskey, combined_sesskey ))) - print( "pass=" .. pass ) - end, + print( select(2, bin.unpack("H" .. #srv_sesskey, srv_sesskey ))) + print( select(2, bin.unpack("H" .. #cli_sesskey, cli_sesskey ))) + print( select(2, bin.unpack("H" .. #combined_sesskey, combined_sesskey ))) + print( "pass=" .. pass ) + end, - --- Performs the relevant encryption needed for the Oracle 10g response - -- - -- @param user containing the Oracle user name - -- @param pass containing the Oracle user password - -- @param srv_sesskey_enc containing the encrypted server session key as - -- recieved from the PreAuth packet - -- @return cli_sesskey_enc the encrypted client session key - -- @return auth_pass the encrypted Oracle password - Encrypt10g = function( self, user, pass, srv_sesskey_enc ) + --- Performs the relevant encryption needed for the Oracle 10g response + -- + -- @param user containing the Oracle user name + -- @param pass containing the Oracle user password + -- @param srv_sesskey_enc containing the encrypted server session key as + -- recieved from the PreAuth packet + -- @return cli_sesskey_enc the encrypted client session key + -- @return auth_pass the encrypted Oracle password + Encrypt10g = function( self, user, pass, srv_sesskey_enc ) - local pwhash = self:HashPassword10g( user:upper(), pass:upper() ) .. "\0\0\0\0\0\0\0\0" - -- We're currently using a static client session key, this should - -- probably be changed to a random value in the future - local cli_sesskey = bin.pack("H", "FAF5034314546426F329B1DAB1CDC5B8FF94349E0875623160350B0E13A0DA36") - local srv_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, srv_sesskey_enc ) - local cli_sesskey_enc = openssl.encrypt( "AES-128-CBC", pwhash, nil, cli_sesskey ) - -- This value should really be random, not this static cruft - local rnd = bin.pack("H", "4C31AFE05F3B012C0AE9AB0CDFF0C508") - local combined_sesskey = "" - local auth_pass + local pwhash = self:HashPassword10g( user:upper(), pass:upper() ) .. "\0\0\0\0\0\0\0\0" + -- We're currently using a static client session key, this should + -- probably be changed to a random value in the future + local cli_sesskey = bin.pack("H", "FAF5034314546426F329B1DAB1CDC5B8FF94349E0875623160350B0E13A0DA36") + local srv_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, srv_sesskey_enc ) + local cli_sesskey_enc = openssl.encrypt( "AES-128-CBC", pwhash, nil, cli_sesskey ) + -- This value should really be random, not this static cruft + local rnd = bin.pack("H", "4C31AFE05F3B012C0AE9AB0CDFF0C508") + local combined_sesskey = "" + local auth_pass - for i=17, 32 do - combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) ) - end - combined_sesskey = openssl.md5( combined_sesskey ) - auth_pass = openssl.encrypt("AES-128-CBC", combined_sesskey, nil, rnd .. pass, true ) - auth_pass = select(2, bin.unpack("H" .. #auth_pass, auth_pass)) - cli_sesskey_enc = select(2, bin.unpack("H" .. #cli_sesskey_enc, cli_sesskey_enc)) - return cli_sesskey_enc, auth_pass - end, + for i=17, 32 do + combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) ) + end + combined_sesskey = openssl.md5( combined_sesskey ) + auth_pass = openssl.encrypt("AES-128-CBC", combined_sesskey, nil, rnd .. pass, true ) + auth_pass = select(2, bin.unpack("H" .. #auth_pass, auth_pass)) + cli_sesskey_enc = select(2, bin.unpack("H" .. #cli_sesskey_enc, cli_sesskey_enc)) + return cli_sesskey_enc, auth_pass + end, - --- Performs the relevant encryption needed for the Oracle 11g response - -- - -- @param pass containing the Oracle user password - -- @param srv_sesskey_enc containing the encrypted server session key as - -- recieved from the PreAuth packet - -- @param auth_vrfy_data containing the password salt as recieved from the - -- PreAuth packet - -- @return cli_sesskey_enc the encrypted client session key - -- @return auth_pass the encrypted Oracle password - Encrypt11g = function( self, pass, srv_sesskey_enc, auth_vrfy_data ) + --- Performs the relevant encryption needed for the Oracle 11g response + -- + -- @param pass containing the Oracle user password + -- @param srv_sesskey_enc containing the encrypted server session key as + -- recieved from the PreAuth packet + -- @param auth_vrfy_data containing the password salt as recieved from the + -- PreAuth packet + -- @return cli_sesskey_enc the encrypted client session key + -- @return auth_pass the encrypted Oracle password + Encrypt11g = function( self, pass, srv_sesskey_enc, auth_vrfy_data ) - -- This value should really be random, not this static cruft - local rnd = openssl.rand_pseudo_bytes(16) - local cli_sesskey = openssl.rand_pseudo_bytes(40) .. bin.pack("H", "0808080808080808") - local pw_hash = openssl.sha1(pass .. auth_vrfy_data) .. "\0\0\0\0" - local srv_sesskey = openssl.decrypt( "aes-192-cbc", pw_hash, nil, srv_sesskey_enc ) - local auth_password - local cli_sesskey_enc - local combined_sesskey = "" - local data = "" + -- This value should really be random, not this static cruft + local rnd = openssl.rand_pseudo_bytes(16) + local cli_sesskey = openssl.rand_pseudo_bytes(40) .. bin.pack("H", "0808080808080808") + local pw_hash = openssl.sha1(pass .. auth_vrfy_data) .. "\0\0\0\0" + local srv_sesskey = openssl.decrypt( "aes-192-cbc", pw_hash, nil, srv_sesskey_enc ) + local auth_password + local cli_sesskey_enc + local combined_sesskey = "" + local data = "" - for i=17, 40 do - combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) ) - end - combined_sesskey = ( openssl.md5( combined_sesskey:sub(1,16) ) .. openssl.md5( combined_sesskey:sub(17) ) ):sub(1, 24) + for i=17, 40 do + combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) ) + end + combined_sesskey = ( openssl.md5( combined_sesskey:sub(1,16) ) .. openssl.md5( combined_sesskey:sub(17) ) ):sub(1, 24) - cli_sesskey_enc = openssl.encrypt( "aes-192-cbc", pw_hash, nil, cli_sesskey ) - cli_sesskey_enc = select(2,bin.unpack("H" .. #cli_sesskey_enc, cli_sesskey_enc)) + cli_sesskey_enc = openssl.encrypt( "aes-192-cbc", pw_hash, nil, cli_sesskey ) + cli_sesskey_enc = select(2,bin.unpack("H" .. #cli_sesskey_enc, cli_sesskey_enc)) - auth_password = openssl.encrypt( "aes-192-cbc", combined_sesskey, nil, rnd .. pass, true ) - auth_password = select(2, bin.unpack("H" .. #auth_password, auth_password)) + auth_password = openssl.encrypt( "aes-192-cbc", combined_sesskey, nil, rnd .. pass, true ) + auth_password = select(2, bin.unpack("H" .. #auth_password, auth_password)) - return cli_sesskey_enc, auth_password - end, + return cli_sesskey_enc, auth_password + end, } Helper = { - --- Creates a new Helper instance - -- - -- @param host table containing the host table as received by action - -- @param port table containing the port table as received by action - -- @param instance string containing the instance name - -- @return o new instance of Helper - new = function(self, host, port, instance ) - local o = { - host = host, - port = port, - socket = nmap.new_socket(), - dbinstance = instance or stdnse.get_script_args('tns.sid') or "orcl" - } - o.socket:set_timeout(30000) - setmetatable(o, self) - self.__index = self - return o - end, + --- Creates a new Helper instance + -- + -- @param host table containing the host table as received by action + -- @param port table containing the port table as received by action + -- @param instance string containing the instance name + -- @return o new instance of Helper + new = function(self, host, port, instance ) + local o = { + host = host, + port = port, + socket = nmap.new_socket(), + dbinstance = instance or stdnse.get_script_args('tns.sid') or "orcl" + } + o.socket:set_timeout(30000) + setmetatable(o, self) + self.__index = self + return o + end, - --- Connects and performs protocol negotiation with the Oracle server - -- - -- @return true on success, false on failure - -- @return err containing error message when status is false - Connect = function( self ) - local SUPPORTED_VERSIONS = { - "IBMPC/WIN_NT64-9.1.0", - "IBMPC/WIN_NT-8.1.0", - "Linuxi386/Linux-2.0.34-8.1.0", - "x86_64/Linux 2.4.xx" - } - local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) - local conn, packet, tns + --- Connects and performs protocol negotiation with the Oracle server + -- + -- @return true on success, false on failure + -- @return err containing error message when status is false + Connect = function( self ) + local SUPPORTED_VERSIONS = { + "IBMPC/WIN_NT64-9.1.0", + "IBMPC/WIN_NT-8.1.0", + "Linuxi386/Linux-2.0.34-8.1.0", + "x86_64/Linux 2.4.xx" + } + local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) + local conn, packet, tns - if( not(status) ) then return status, data end + if( not(status) ) then return status, data end - self.comm = Comm:new( self.socket ) + self.comm = Comm:new( self.socket ) - status, self.version = self.comm:exchTNSPacket( Packet.Connect:new( self.host.ip, self.port.number, self.dbinstance ) ) - if ( not(status) ) then return false, self.version end + status, self.version = self.comm:exchTNSPacket( Packet.Connect:new( self.host.ip, self.port.number, self.dbinstance ) ) + if ( not(status) ) then return false, self.version end - if ( self.version ~= ORACLE_VERSION_11G and self.version ~= ORACLE_VERSION_10G ) then - return false, ("Unsupported Oracle Version (%d)"):format(self.version) - end + if ( self.version ~= ORACLE_VERSION_11G and self.version ~= ORACLE_VERSION_10G ) then + return false, ("Unsupported Oracle Version (%d)"):format(self.version) + end - status = self.comm:exchTNSPacket( Packet.SNS:new( self.version ) ) - if ( not(status) ) then return false, "ERROR: Helper.Connect failed" end + status = self.comm:exchTNSPacket( Packet.SNS:new( self.version ) ) + if ( not(status) ) then return false, "ERROR: Helper.Connect failed" end - status, self.os = self.comm:exchTNSPacket( Packet.ProtoNeg:new( self.version ) ) - if ( not(status) ) then - return false, data - end + status, self.os = self.comm:exchTNSPacket( Packet.ProtoNeg:new( self.version ) ) + if ( not(status) ) then + return false, data + end - -- used for testing unsupported versions - self.os = stdnse.get_script_args("tns.forceos") or self.os + -- used for testing unsupported versions + self.os = stdnse.get_script_args("tns.forceos") or self.os - status = false - for _, ver in pairs(SUPPORTED_VERSIONS) do - if ( self.os == ver ) then - status = true - break - end - end + status = false + for _, ver in pairs(SUPPORTED_VERSIONS) do + if ( self.os == ver ) then + status = true + break + end + end - if ( not(status) ) then - stdnse.print_debug(2, "ERROR: Version %s is not yet supported", self.os) - return false, ("ERROR: Connect to version %s is not yet supported"):format(self.os) - end + if ( not(status) ) then + stdnse.print_debug(2, "ERROR: Version %s is not yet supported", self.os) + return false, ("ERROR: Connect to version %s is not yet supported"):format(self.os) + end - if ( self.os:match("IBMPC/WIN_NT") ) then - status = self.comm:sendTNSPacket( Packet.Unknown1:new( self.os ) ) - if ( not(status) ) then - return false, "ERROR: Helper.Connect failed" - end - status, data = self.comm:sendTNSPacket( Packet.Unknown2:new( self.os ) ) - if ( not(status) ) then return false, data end + if ( self.os:match("IBMPC/WIN_NT") ) then + status = self.comm:sendTNSPacket( Packet.Unknown1:new( self.os ) ) + if ( not(status) ) then + return false, "ERROR: Helper.Connect failed" + end + status, data = self.comm:sendTNSPacket( Packet.Unknown2:new( self.os ) ) + if ( not(status) ) then return false, data end - status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) ) - if ( not(status) ) then return false, data end - -- Oracle 10g under Windows needs this additional read, there's - -- probably a better way to detect this by analysing the packets - -- further. - if ( self.version == ORACLE_VERSION_10G ) then - status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) ) - if ( not(status) ) then return false, data end - end - elseif ( "x86_64/Linux 2.4.xx" == self.os ) then - status = self.comm:sendTNSPacket( Packet.Unknown1:new( self.os ) ) - if ( not(status) ) then - return false, "ERROR: Helper.Connect failed" - end + status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) ) + if ( not(status) ) then return false, data end + -- Oracle 10g under Windows needs this additional read, there's + -- probably a better way to detect this by analysing the packets + -- further. + if ( self.version == ORACLE_VERSION_10G ) then + status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) ) + if ( not(status) ) then return false, data end + end + elseif ( "x86_64/Linux 2.4.xx" == self.os ) then + status = self.comm:sendTNSPacket( Packet.Unknown1:new( self.os ) ) + if ( not(status) ) then + return false, "ERROR: Helper.Connect failed" + end - status = self.comm:sendTNSPacket( Packet.Unknown2:new( self.os ) ) - if ( not(status) ) then - return false, "ERROR: Helper.Connect failed" - end + status = self.comm:sendTNSPacket( Packet.Unknown2:new( self.os ) ) + if ( not(status) ) then + return false, "ERROR: Helper.Connect failed" + end - status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) ) - if ( not(status) ) then - return false, data - end + status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) ) + if ( not(status) ) then + return false, data + end - else - status = self.comm:exchTNSPacket( Packet.Unknown1:new( self.os ) ) - if ( not(status) ) then - return false, "ERROR: Helper.Connect failed" - end - end + else + status = self.comm:exchTNSPacket( Packet.Unknown1:new( self.os ) ) + if ( not(status) ) then + return false, "ERROR: Helper.Connect failed" + end + end - return true - end, + return true + end, - --- Sends a command to the TNS lsnr - -- It currently accepts and tries to send all commands recieved - -- - -- @param cmd string containing the command to send to the server - -- @return data string containing the result recieved from the server - lsnrCtl = function( self, cmd ) - local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) - local conn, packet, tns, pkt + --- Sends a command to the TNS lsnr + -- It currently accepts and tries to send all commands recieved + -- + -- @param cmd string containing the command to send to the server + -- @return data string containing the result recieved from the server + lsnrCtl = function( self, cmd ) + local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" ) + local conn, packet, tns, pkt - if( not(status) ) then - return status, data - end + if( not(status) ) then + return status, data + end - self.comm = Comm:new( self.socket ) - pkt = Packet.Connect:new( self.host.ip, self.port.number, self.dbinstance ) - pkt:setCmd(cmd) + self.comm = Comm:new( self.socket ) + pkt = Packet.Connect:new( self.host.ip, self.port.number, self.dbinstance ) + pkt:setCmd(cmd) - if ( not(self.comm:exchTNSPacket( pkt )) ) then - return false, self.version - end + if ( not(self.comm:exchTNSPacket( pkt )) ) then + return false, self.version + end - data = "" - repeat - status, tns = self.comm:recvTNSPacket() - if ( not(status) ) then - self:Close() - return status, tns - end - local _, flags = bin.unpack(">S", tns.data ) - data = data .. tns.data:sub(3) - until ( flags ~= 0 ) - self:Close() + data = "" + repeat + status, tns = self.comm:recvTNSPacket() + if ( not(status) ) then + self:Close() + return status, tns + end + local _, flags = bin.unpack(">S", tns.data ) + data = data .. tns.data:sub(3) + until ( flags ~= 0 ) + self:Close() - return true, data - end, + return true, data + end, - --- Authenticates to the database - -- - -- @param user containing the Oracle user name - -- @param pass containing the Oracle user password - -- @return true on success, false on failure - -- @return err containing error message when status is false - Login = function( self, user, password ) - local data, packet, status, tns, parser - local sesskey_enc, auth_pass, auth - local auth_options = AuthOptions:new() + --- Authenticates to the database + -- + -- @param user containing the Oracle user name + -- @param pass containing the Oracle user password + -- @return true on success, false on failure + -- @return err containing error message when status is false + Login = function( self, user, password ) + local data, packet, status, tns, parser + local sesskey_enc, auth_pass, auth + local auth_options = AuthOptions:new() - status, auth = self.comm:exchTNSPacket( Packet.PreAuth:new( user, auth_options, self.os ) ) - if ( not(status) ) then - return false, auth - end + status, auth = self.comm:exchTNSPacket( Packet.PreAuth:new( user, auth_options, self.os ) ) + if ( not(status) ) then + return false, auth + end - -- Check what version of the DB to authenticate against AND verify whether - -- case sensitive login is enabled or not. In case-sensitive mode the salt - -- is longer, so we check the length of auth["AUTH_VFR_DATA"] - if ( self.version == ORACLE_VERSION_11G and #auth["AUTH_VFR_DATA"] > 2 ) then - sesskey_enc, auth_pass = Crypt:Encrypt11g( password, bin.pack( "H", auth["AUTH_SESSKEY"] ), bin.pack("H", auth["AUTH_VFR_DATA"] ) ) - else - sesskey_enc, auth_pass = Crypt:Encrypt10g( user, password, bin.pack( "H", auth["AUTH_SESSKEY"] ) ) - end + -- Check what version of the DB to authenticate against AND verify whether + -- case sensitive login is enabled or not. In case-sensitive mode the salt + -- is longer, so we check the length of auth["AUTH_VFR_DATA"] + if ( self.version == ORACLE_VERSION_11G and #auth["AUTH_VFR_DATA"] > 2 ) then + sesskey_enc, auth_pass = Crypt:Encrypt11g( password, bin.pack( "H", auth["AUTH_SESSKEY"] ), bin.pack("H", auth["AUTH_VFR_DATA"] ) ) + else + sesskey_enc, auth_pass = Crypt:Encrypt10g( user, password, bin.pack( "H", auth["AUTH_SESSKEY"] ) ) + end - status, data = self.comm:exchTNSPacket( Packet.Auth:new( user, auth_options, sesskey_enc, auth_pass, self.os ) ) - if ( not(status) ) then return false, data end - self.auth_session = data["AUTH_SESSION_ID"] - return true - end, + status, data = self.comm:exchTNSPacket( Packet.Auth:new( user, auth_options, sesskey_enc, auth_pass, self.os ) ) + if ( not(status) ) then return false, data end + self.auth_session = data["AUTH_SESSION_ID"] + return true + end, - --- Steal auth data from database - -- @param user containing the Oracle user name - -- @param pass containing the Oracle user password - -- @return true on success, false on failure - -- @return err containing error message when status is false - StealthLogin = function( self, user, password ) - local data, packet, status, tns, parser - local sesskey_enc, auth_pass, auth - local auth_options = AuthOptions:new() + --- Steal auth data from database + -- @param user containing the Oracle user name + -- @param pass containing the Oracle user password + -- @return true on success, false on failure + -- @return err containing error message when status is false + StealthLogin = function( self, user, password ) + local data, packet, status, tns, parser + local sesskey_enc, auth_pass, auth + local auth_options = AuthOptions:new() - status, auth = self.comm:exchTNSPacket( Packet.PreAuth:new( user, auth_options, self.os ) ) - if ( not(status) ) then - return false, auth - elseif ( auth["AUTH_SESSKEY"] ) then - return true, auth - else - return false - end - end, + status, auth = self.comm:exchTNSPacket( Packet.PreAuth:new( user, auth_options, self.os ) ) + if ( not(status) ) then + return false, auth + elseif ( auth["AUTH_SESSKEY"] ) then + return true, auth + else + return false + end + end, - --- Queries the database - -- - -- @param query string containing the SQL query - -- @return true on success, false on failure - -- @return result table containing fields - -- rows - -- columns - -- @return err containing error message when status is false - Query = function(self, query) + --- Queries the database + -- + -- @param query string containing the SQL query + -- @return true on success, false on failure + -- @return result table containing fields + -- rows + -- columns + -- @return err containing error message when status is false + Query = function(self, query) - local SUPPORTED_VERSIONS = { - "IBMPC/WIN_NT-8.1.0", - } + local SUPPORTED_VERSIONS = { + "IBMPC/WIN_NT-8.1.0", + } - local status = false - for _, ver in pairs(SUPPORTED_VERSIONS) do - if ( self.os == ver ) then - status = true - break - end - end + local status = false + for _, ver in pairs(SUPPORTED_VERSIONS) do + if ( self.os == ver ) then + status = true + break + end + end - if ( not(status) ) then - stdnse.print_debug(2, "ERROR: Version %s is not yet supported", self.os) - return false, ("ERROR: Querying version %s is not yet supported"):format(self.os) - end + if ( not(status) ) then + stdnse.print_debug(2, "ERROR: Version %s is not yet supported", self.os) + return false, ("ERROR: Querying version %s is not yet supported"):format(self.os) + end - if ( not(query) ) then return false, "No query was supplied by user" end + if ( not(query) ) then return false, "No query was supplied by user" end - local data - status, data = self.comm:exchTNSPacket( Packet.PostLogin:new(self.auth_session) ) - if ( not(status) ) then - return false, "ERROR: Postlogin packet failed" - end + local data + status, data = self.comm:exchTNSPacket( Packet.PostLogin:new(self.auth_session) ) + if ( not(status) ) then + return false, "ERROR: Postlogin packet failed" + end - local status, result = self.comm:exchTNSPacket( Packet.Query:new(query) ) - if ( not(status) ) then return false, result end + local status, result = self.comm:exchTNSPacket( Packet.Query:new(query) ) + if ( not(status) ) then return false, result end - if ( not(result.moredata) ) then return true, result.data end - result = result.data + if ( not(result.moredata) ) then return true, result.data end + result = result.data - repeat - status, data = self.comm:exchTNSPacket( Packet.QueryResponseAck:new(result) ) - until(not(status) or data:match(".*ORA%-01403: no data found\n$")) + repeat + status, data = self.comm:exchTNSPacket( Packet.QueryResponseAck:new(result) ) + until(not(status) or data:match(".*ORA%-01403: no data found\n$")) - return true, result - end, + return true, result + end, - --- Ends the Oracle communication - Close = function( self ) - -- We should probably stick some slick sqlplus termination stuff in here - local status = self.comm:sendTNSPacket( Packet.EOF:new( ) ) - self.socket:close() - end, + --- Ends the Oracle communication + Close = function( self ) + -- We should probably stick some slick sqlplus termination stuff in here + local status = self.comm:sendTNSPacket( Packet.EOF:new( ) ) + self.socket:close() + end, } diff --git a/nselib/vuzedht.lua b/nselib/vuzedht.lua index 1e738ae98..e08c8c75c 100644 --- a/nselib/vuzedht.lua +++ b/nselib/vuzedht.lua @@ -204,7 +204,7 @@ Response = { parse = function(self) local pos pos, self.action, self.trans_id, self.conn_id, - self.proto_version, self.vendor_id, self.network_id, + self.proto_version, self.vendor_id, self.network_id, self.instance_id = bin.unpack(">IIH8CCII", self.data) end,