1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-10 09:49:05 +00:00

Reindent the last of the NSE libraries.

https://secwiki.org/w/Nmap/Code_Standards
This commit is contained in:
dmiller
2014-02-04 19:47:26 +00:00
parent d5746993e2
commit 69e343f0aa
34 changed files with 26811 additions and 26811 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -517,7 +517,7 @@ function request_reconnect_session_data(host, port, params)
end end
if params.DeviceId then if params.DeviceId then
xmldata = xmldata .. "<DeviceId>" .. params.DeviceId .. "</DeviceId>" xmldata = xmldata .. "<DeviceId>" .. params.DeviceId .. "</DeviceId>"
end end
for _, srvtype in pairs(params.ServerType) do for _, srvtype in pairs(params.ServerType) do

View File

@@ -26,14 +26,14 @@ _ENV = stdnse.module("dhcp", stdnse.seeall)
request_types = request_types =
{ {
DHCPDISCOVER = 1, DHCPDISCOVER = 1,
DHCPOFFER = 2, DHCPOFFER = 2,
DHCPREQUEST = 3, DHCPREQUEST = 3,
DHCPDECLINE = 4, DHCPDECLINE = 4,
DHCPACK = 5, DHCPACK = 5,
DHCPNAK = 6, DHCPNAK = 6,
DHCPRELEASE = 7, DHCPRELEASE = 7,
DHCPINFORM = 8 DHCPINFORM = 8
} }
request_types_str = {} 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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_ip(data, pos, length) local function read_ip(data, pos, length)
if(length ~= 4) then if(length ~= 4) then
if((length % 4) ~= 0) then if((length % 4) ~= 0) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for an ip address (%d)", length) stdnse.print_debug(1, "dhcp-discover: Invalid length for an ip address (%d)", length)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
else else
local results = {} local results = {}
for i=1, length, 4 do for i=1, length, 4 do
local value local value
pos, value = bin.unpack("<I", data, pos) pos, value = bin.unpack("<I", data, pos)
table.insert(results, ipOps.fromdword(value)) table.insert(results, ipOps.fromdword(value))
end end
return pos, results return pos, results
end end
else else
local value local value
pos, value = bin.unpack("<I", data, pos) pos, value = bin.unpack("<I", data, pos)
return pos, ipOps.fromdword(value) return pos, ipOps.fromdword(value)
end end
end end
---Read a string. The length of the string is given by the length field. ---Read a string. The length of the string is given by the length field.
@@ -86,7 +86,7 @@ end
--@return The new position (will always be pos + length, no matter what we think it should be) --@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. --@return The value of the field, or nil if the field length was wrong.
local function read_string(data, pos, length) local function read_string(data, pos, length)
return bin.unpack(string.format("A%d", length), data, pos) return bin.unpack(string.format("A%d", length), data, pos)
end end
---Read a single byte. Print an error if the length isn't 1. ---Read a single byte. Print an error if the length isn't 1.
@@ -97,12 +97,12 @@ end
--@return The new position (will always be pos + length, no matter what we think it should be) --@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. --@return The value of the field, or nil if the field length was wrong.
local function read_1_byte(data, pos, length) local function read_1_byte(data, pos, length)
if(length ~= 1) then if(length ~= 1) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 1) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 1)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
end end
return bin.unpack("C", data, pos) return bin.unpack("C", data, pos)
end end
---Read a message type. This is a single-byte value that's looked up in the <code>request_types_str</code> ---Read a message type. This is a single-byte value that's looked up in the <code>request_types_str</code>
@@ -114,15 +114,15 @@ end
--@return The new position (will always be pos + length, no matter what we think it should be) --@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. --@return The value of the field, or nil if the field length was wrong.
local function read_message_type(data, pos, length) local function read_message_type(data, pos, length)
local value local value
pos, value = read_1_byte(data, pos, length) pos, value = read_1_byte(data, pos, length)
if(value == nil) then if(value == nil) then
stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte message type") stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte message type")
return pos, nil return pos, nil
end end
return pos, request_types_str[value] return pos, request_types_str[value]
end end
---Read a single byte, and return 'false' if it's 0, or 'true' if it's non-zero. Print an error if the ---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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_boolean(data, pos, length) local function read_boolean(data, pos, length)
local result local result
pos, result = read_1_byte(data, pos, length) pos, result = read_1_byte(data, pos, length)
if(result == nil) then if(result == nil) then
stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte boolean") stdnse.print_debug(1, "dhcp-discover: Couldn't read the 1-byte boolean")
return pos, nil return pos, nil
elseif(result == 0) then elseif(result == 0) then
return pos, "false" return pos, "false"
else else
return pos, "true" return pos, "true"
end end
end end
---Read a 2-byte unsigned little endian value. Print an error if the length isn't 2. ---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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_2_bytes(data, pos, length) local function read_2_bytes(data, pos, length)
if(length ~= 2) then if(length ~= 2) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 2) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 2)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
end end
return bin.unpack(">S", data, pos) return bin.unpack(">S", data, pos)
end 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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_2_bytes_list(data, pos, length) local function read_2_bytes_list(data, pos, length)
if((length % 2) ~= 0) then if((length % 2) ~= 0) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 2) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 2)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
else else
local results = {} local results = {}
for i=1, length, 2 do for i=1, length, 2 do
local value local value
pos, value = bin.unpack(">S", data, pos) pos, value = bin.unpack(">S", data, pos)
table.insert(results, value) table.insert(results, value)
end end
return pos, results return pos, results
end end
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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_4_bytes(data, pos, length) local function read_4_bytes(data, pos, length)
if(length ~= 4) then if(length ~= 4) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
end end
return bin.unpack(">I", data, pos) return bin.unpack(">I", data, pos)
end end
---Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an ---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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_time(data, pos, length) local function read_time(data, pos, length)
local result local result
if(length ~= 4) then if(length ~= 4) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
end end
pos, result = bin.unpack(">I", data, pos) 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] -- 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 local days, hours, minutes, seconds, htime, mtime, stime
days = math.floor(result / 86400) days = math.floor(result / 86400)
htime = math.fmod(result, 86400) htime = math.fmod(result, 86400)
hours = math.floor(htime / 3600) hours = math.floor(htime / 3600)
mtime = math.fmod(htime, 3600) mtime = math.fmod(htime, 3600)
minutes = math.floor(mtime / 60) minutes = math.floor(mtime / 60)
seconds = math.fmod(mtime, 60) seconds = math.fmod(mtime, 60)
local dayLabel local dayLabel
if days == 1 then if days == 1 then
dayLabel = "day" dayLabel = "day"
else else
dayLabel = "days" dayLabel = "days"
end 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 end
---Read a list of static routes. Each of them are a pair of IP addresses, a destination and a ---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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_static_route(data, pos, length) local function read_static_route(data, pos, length)
if((length % 8) ~= 0) then if((length % 8) ~= 0) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
else else
local results = {} local results = {}
for i=1, length, 8 do for i=1, length, 8 do
local destination, router local destination, router
pos, destination = read_ip(data, pos, 4) pos, destination = read_ip(data, pos, 4)
pos, router = read_ip(data, pos, 4) pos, router = read_ip(data, pos, 4)
table.insert(results, {destination=destination, router=router}) table.insert(results, {destination=destination, router=router})
end end
return pos, results return pos, results
end end
end end
---Read a list of policy filters. Each of them are a pair of IP addresses, an address and a ---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 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. --@return The value of the field, or nil if the field length was wrong.
local function read_policy_filter(data, pos, length) local function read_policy_filter(data, pos, length)
if((length % 8) ~= 0) then if((length % 8) ~= 0) then
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8) stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8)
pos = pos + length pos = pos + length
return pos, nil return pos, nil
else else
local results = {} local results = {}
for i=1, length, 8 do for i=1, length, 8 do
local address, router, mask local address, router, mask
pos, address = read_ip(data, pos, 4) pos, address = read_ip(data, pos, 4)
pos, mask = read_ip(data, pos, 4) pos, mask = read_ip(data, pos, 4)
table.insert(results, {address=address, mask=mask}) table.insert(results, {address=address, mask=mask})
end end
return pos, results return pos, results
end end
end end
---These are the different fields for DHCP. These have to come after the read_* function ---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. --- Does the send/receive, doesn't build/parse anything.
local function dhcp_send(socket, host, packet) local function dhcp_send(socket, host, packet)
-- Send out the packet -- Send out the packet
return socket:sendto(host, { number=67, protocol="udp" }, packet) return socket:sendto(host, { number=67, protocol="udp" }, packet)
end end
local function dhcp_receive(socket, transaction_id) local function dhcp_receive(socket, transaction_id)
local status, data = socket:receive() local status, data = socket:receive()
if ( not(status) ) then if ( not(status) ) then
socket:close() socket:close()
return false, data return false, data
end end
-- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly -- 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) -- generated and different for every instance of a script (to prevent collisions)
while status and data:sub(5, 8) ~= transaction_id do while status and data:sub(5, 8) ~= transaction_id do
status, data = socket:receive() status, data = socket:receive()
end end
return status, data return status, data
end end
--- Builds a DHCP packet --- Builds a DHCP packet
@@ -417,58 +417,58 @@ end
--@return status (true or false) --@return status (true or false)
--@return The parsed response, as a table. --@return The parsed response, as a table.
function dhcp_build(request_type, ip_address, mac_address, options, request_options, overrides, lease_time, transaction_id) 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 -- Set up the default overrides
if(overrides == nil) then if(overrides == nil) then
overrides = {} overrides = {}
end end
if(request_options == nil) then if(request_options == nil) then
-- Request the defaults, or there's no verbosity; otherwise, request everything! -- Request the defaults, or there's no verbosity; otherwise, request everything!
request_options = '' request_options = ''
for i = 1, 61, 1 do for i = 1, 61, 1 do
if(nmap.verbosity() > 0) then if(nmap.verbosity() > 0) then
request_options = request_options .. string.char(i) request_options = request_options .. string.char(i)
else else
if(actions[i] and actions[i].default) then if(actions[i] and actions[i].default) then
request_options = request_options .. string.char(i) request_options = request_options .. string.char(i)
end end
end end
end end
end end
-- Header -- 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 .. 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 .. ( 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(">SS", overrides['secs'] or 0, overrides['flags'] or 0x0000) -- Secs, flags
packet = packet .. bin.pack("A", ip_address) -- Client address packet = packet .. bin.pack("A", ip_address) -- Client address
packet = packet .. bin.pack("<I", overrides['yiaddr'] or 0) -- yiaddr packet = packet .. bin.pack("<I", overrides['yiaddr'] or 0) -- yiaddr
packet = packet .. bin.pack("<I", overrides['siaddr'] or 0) -- siaddr packet = packet .. bin.pack("<I", overrides['siaddr'] or 0) -- siaddr
packet = packet .. bin.pack("<I", overrides['giaddr'] or 0) -- giaddr packet = packet .. bin.pack("<I", overrides['giaddr'] or 0) -- giaddr
packet = packet .. mac_address .. string.rep(string.char(0), 16 - #mac_address) -- chaddr (MAC address) packet = packet .. mac_address .. string.rep(string.char(0), 16 - #mac_address) -- chaddr (MAC address)
packet = packet .. (overrides['sname'] or string.rep(string.char(0), 64)) -- sname packet = packet .. (overrides['sname'] or string.rep(string.char(0), 64)) -- sname
packet = packet .. (overrides['file'] or string.rep(string.char(0), 128)) -- file packet = packet .. (overrides['file'] or string.rep(string.char(0), 128)) -- file
packet = packet .. bin.pack(">I", overrides['cookie'] or 0x63825363) -- Magic cookie packet = packet .. bin.pack(">I", overrides['cookie'] or 0x63825363) -- Magic cookie
-- Options -- Options
packet = packet .. bin.pack(">CCC", 0x35, 1, request_type) -- Request type packet = packet .. bin.pack(">CCC", 0x35, 1, request_type) -- Request type
for _, option in ipairs(options or {}) do for _, option in ipairs(options or {}) do
packet = packet .. bin.pack(">C", option.number) packet = packet .. bin.pack(">C", option.number)
if ( "string" == option.type ) then if ( "string" == option.type ) then
packet = packet .. bin.pack("p", option.value) packet = packet .. bin.pack("p", option.value)
elseif( "ip" == option.type ) then elseif( "ip" == option.type ) then
packet = packet .. bin.pack(">CI", 4, option.value) packet = packet .. bin.pack(">CI", 4, option.value)
end end
end end
packet = packet .. bin.pack(">CCA", 0x37, #request_options, request_options) -- Request options 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(">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 end
---Parse a DHCP packet (either a request or a response) and return the results as a table. The ---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, --@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). -- DHCP packets are padded with \x00 bytes).
function dhcp_parse(data, transaction_id) function dhcp_parse(data, transaction_id)
local pos = 1 local pos = 1
local result = {} local result = {}
-- Receive the first bit and make sure we got the correct operation back -- 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) pos, result['op'], result['htype'], result['hlen'], result['hops'] = bin.unpack(">CCCC", data, pos)
if(result['op'] ~= 2) then if(result['op'] ~= 2) then
return false, string.format("DHCP server returned invalid reply ('op' wasn't BOOTREPLY (it was 0x%02x))", result['op']) return false, string.format("DHCP server returned invalid reply ('op' wasn't BOOTREPLY (it was 0x%02x))", result['op'])
end end
-- Confirm the transaction id -- Confirm the transaction id
pos, result['xid'] = bin.unpack("A4", data, pos) pos, result['xid'] = bin.unpack("A4", data, pos)
if(result['xid'] ~= transaction_id) then 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) return false, string.format("DHCP server returned invalid reply (transaction id didn't match (%s != %s))", result['xid'], transaction_id)
end end
-- Unpack the secs, flags, addresses, sname, and file -- Unpack the secs, flags, addresses, sname, and file
pos, result['secs'], result['flags'] = bin.unpack(">SS", data, pos) pos, result['secs'], result['flags'] = bin.unpack(">SS", data, pos)
pos, result['ciaddr'] = bin.unpack("<I", data, pos) pos, result['ciaddr'] = bin.unpack("<I", data, pos)
pos, result['yiaddr'] = bin.unpack("<I", data, pos) pos, result['yiaddr'] = bin.unpack("<I", data, pos)
pos, result['siaddr'] = bin.unpack("<I", data, pos) pos, result['siaddr'] = bin.unpack("<I", data, pos)
pos, result['giaddr'] = bin.unpack("<I", data, pos) pos, result['giaddr'] = bin.unpack("<I", data, pos)
pos, result['chaddr'] = bin.unpack("A16", data, pos) pos, result['chaddr'] = bin.unpack("A16", data, pos)
pos, result['sname'] = bin.unpack("A64", data, pos) pos, result['sname'] = bin.unpack("A64", data, pos)
pos, result['file'] = bin.unpack("A128", data, pos) pos, result['file'] = bin.unpack("A128", data, pos)
-- Convert the addresses to strings -- Convert the addresses to strings
result['ciaddr_str'] = ipOps.fromdword(result['ciaddr']) result['ciaddr_str'] = ipOps.fromdword(result['ciaddr'])
result['yiaddr_str'] = ipOps.fromdword(result['yiaddr']) result['yiaddr_str'] = ipOps.fromdword(result['yiaddr'])
result['siaddr_str'] = ipOps.fromdword(result['siaddr']) result['siaddr_str'] = ipOps.fromdword(result['siaddr'])
result['giaddr_str'] = ipOps.fromdword(result['giaddr']) result['giaddr_str'] = ipOps.fromdword(result['giaddr'])
-- Confirm the cookie -- Confirm the cookie
pos, result['cookie'] = bin.unpack(">I", data, pos) pos, result['cookie'] = bin.unpack(">I", data, pos)
if(result['cookie'] ~= 0x63825363) then if(result['cookie'] ~= 0x63825363) then
return false, "DHCP server returned invalid reply (the magic cookie was invalid)" return false, "DHCP server returned invalid reply (the magic cookie was invalid)"
end end
-- Parse the options -- Parse the options
result['options'] = {} result['options'] = {}
while true do while true do
local option, length local option, length
pos, option, length = bin.unpack(">CC", data, pos) pos, option, length = bin.unpack(">CC", data, pos)
-- Check for termination condition -- Check for termination condition
if(option == 0xFF) then if(option == 0xFF) then
break; break;
end end
-- Get the action from the array, based on the code -- Get the action from the array, based on the code
local action = actions[option] local action = actions[option]
-- Verify we got a valid code (if we didn't, we're probably in big trouble) -- Verify we got a valid code (if we didn't, we're probably in big trouble)
local value local value
if(action == nil) then if(action == nil) then
stdnse.print_debug(1, "dhcp-discover: Unknown option: %d", option) stdnse.print_debug(1, "dhcp-discover: Unknown option: %d", option)
pos = pos + length pos = pos + length
else else
-- Call the function to parse the option, and insert the result into our results table -- 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']) stdnse.print_debug(2, "dhcp-discover: Attempting to parse %s", action['name'])
pos, value = action['func'](data, pos, length) pos, value = action['func'](data, pos, length)
if(nmap.verbosity() == 0 and action.default == false) then if(nmap.verbosity() == 0 and action.default == false) then
stdnse.print_debug(1, "dhcp-discover: Server returned unrequested option (%s => %s)", action['name'], value) stdnse.print_debug(1, "dhcp-discover: Server returned unrequested option (%s => %s)", action['name'], value)
else else
if(value) then if(value) then
table.insert(result['options'], {name=action['name'], value=value}) table.insert(result['options'], {name=action['name'], value=value})
else else
stdnse.print_debug(1, "dhcp-discover: Couldn't determine value for %s", action['name']); stdnse.print_debug(1, "dhcp-discover: Couldn't determine value for %s", action['name']);
end end
end 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 -- 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. -- run out of data.
if(option == 52) then if(option == 52) then
if(value == 1) then if(value == 1) then
data = data .. result['file'] data = data .. result['file']
elseif(value == 2) then elseif(value == 2) then
data = data .. result['sname'] data = data .. result['sname']
elseif(value == 3) then elseif(value == 3) then
data = data .. result['file'] .. result['sname'] data = data .. result['file'] .. result['sname']
else else
stdnse.print_debug(1, "dhcp-discover: Warning: 'Option Overload' gave an unsupported value: %d", value) stdnse.print_debug(1, "dhcp-discover: Warning: 'Option Overload' gave an unsupported value: %d", value)
end end
end end
end end
return true, result return true, result
end end
---Build and send any kind of DHCP packet, and parse the response. This is the only interface ---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 status (true or false)
--@return The parsed response, as a table. --@return The parsed response, as a table.
function make_request(target, request_type, ip_address, mac_address, options, request_options, overrides, lease_time) 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) -- 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", math.random(0, 0x7FFFFFFF)) local transaction_id = overrides and overrides['xid'] or bin.pack("<I", math.random(0, 0x7FFFFFFF))
-- Generate the packet -- 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) 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 if(not(status)) then
stdnse.print_debug(1, "dhcp: Couldn't build packet: " .. packet) stdnse.print_debug(1, "dhcp: Couldn't build packet: " .. packet)
return false, "Couldn't build packet: " .. packet return false, "Couldn't build packet: " .. packet
end end
local socket = nmap.new_socket("udp") local socket = nmap.new_socket("udp")
socket:bind(nil, 68) socket:bind(nil, 68)
socket:set_timeout(5000) socket:set_timeout(5000)
-- Send the packet and get the response -- Send the packet and get the response
local status, response = dhcp_send(socket, target, packet) local status, response = dhcp_send(socket, target, packet)
if(not(status)) then if(not(status)) then
stdnse.print_debug(1, "dhcp: Couldn't send packet: " .. response) stdnse.print_debug(1, "dhcp: Couldn't send packet: " .. response)
return false, "Couldn't send packet: " .. response return false, "Couldn't send packet: " .. response
end end
status, response = dhcp_receive(socket, transaction_id) status, response = dhcp_receive(socket, transaction_id)
socket:close() socket:close()
if ( not(status) ) then if ( not(status) ) then
stdnse.print_debug(1, "dhcp: Couldn't receive packet: " .. response) stdnse.print_debug(1, "dhcp: Couldn't receive packet: " .. response)
return false, "Couldn't receive packet: " .. response return false, "Couldn't receive packet: " .. response
end end
-- Parse the response -- Parse the response
local status, parsed = dhcp_parse(response, transaction_id) local status, parsed = dhcp_parse(response, transaction_id)
if(not(status)) then if(not(status)) then
stdnse.print_debug(1, "dhcp: Couldn't parse response: " .. parsed) stdnse.print_debug(1, "dhcp: Couldn't parse response: " .. parsed)
return false, "Couldn't parse response: " .. parsed return false, "Couldn't parse response: " .. parsed
end end
return true, parsed return true, parsed
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -12,19 +12,19 @@ description = [[
A very basic IKE library. A very basic IKE library.
The current funcionality includes: The current funcionality includes:
1. Generating a Main or Aggressive Mode IKE request packet with a variable amount of transforms and a vpn group. 1. Generating a Main or Aggressive Mode IKE request packet with a variable amount of transforms and a vpn group.
2. Sending a packet 2. Sending a packet
3. Receiving the response 3. Receiving the response
4. Parsing the response for VIDs 4. Parsing the response for VIDs
5. Searching for the VIDs in 'ike-fingerprints.lua' 5. Searching for the VIDs in 'ike-fingerprints.lua'
6. returning a parsed info table 6. returning a parsed info table
This library is meant for extension, which could include: This library is meant for extension, which could include:
1. complete parsing of the response packet (might allow for better fingerprinting) 1. complete parsing of the response packet (might allow for better fingerprinting)
2. adding more options to the request packet 2. adding more options to the request packet
vendor field (might give better fingerprinting of services, e.g. Checkpoint) vendor field (might give better fingerprinting of services, e.g. Checkpoint)
3. backoff pattern analyses 3. backoff pattern analyses
... ...
An a implementation resembling 'ike-scan' could be built. 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"} categories = {"discovery", "safe"}
local ENC_METHODS = { local ENC_METHODS = {
["des"] = 0x80010001, ["des"] = 0x80010001,
["3des"] = 0x80010005, ["3des"] = 0x80010005,
["cast"] = 0x80010006, ["cast"] = 0x80010006,
["aes/128"] = { 0x80010007, 0x800E0080 }, ["aes/128"] = { 0x80010007, 0x800E0080 },
["aes/192"] = { 0x80010007, 0x800E00C0 }, ["aes/192"] = { 0x80010007, 0x800E00C0 },
["aes/256"] = { 0x80010007, 0x800E0100 }, ["aes/256"] = { 0x80010007, 0x800E0100 },
} }
local AUTH_TYPES = { local AUTH_TYPES = {
["psk"] = 0x80030001, ["psk"] = 0x80030001,
["rsa"] = 0x80030003, ["rsa"] = 0x80030003,
["ECDSA"] = 0x80030008, ["ECDSA"] = 0x80030008,
["Hybrid"] = 0x8003FADD, ["Hybrid"] = 0x8003FADD,
["XAUTH"] = 0x8003FDE9, ["XAUTH"] = 0x8003FDE9,
} }
local HASH_ALGORITHM = { local HASH_ALGORITHM = {
["md5"] = 0x80020001, ["md5"] = 0x80020001,
["sha1"] = 0x80020002, ["sha1"] = 0x80020002,
["sha2-256"] = 0x80020004, ["sha2-256"] = 0x80020004,
["sha2-384"] = 0x80020005, ["sha2-384"] = 0x80020005,
["sha2-512"] = 0x80020006, ["sha2-512"] = 0x80020006,
} }
local GROUP_DESCRIPTION = { local GROUP_DESCRIPTION = {
["768"] = 0x80040001, ["768"] = 0x80040001,
["1024"] = 0x80040002, ["1024"] = 0x80040002,
["1536"] = 0x80040005, ["1536"] = 0x80040005,
["2048"] = 0x0004000E, ["2048"] = 0x0004000E,
} }
local EXCHANGE_MODE = { local EXCHANGE_MODE = {
["Main"] = 0x02, ["Main"] = 0x02,
["Aggressive"] = 0x04, ["Aggressive"] = 0x04,
} }
local PROTOCOL_IDS = { local PROTOCOL_IDS = {
["tcp"] = "06", ["tcp"] = "06",
["udp"] = "11", ["udp"] = "11",
} }
-- Response packet types -- Response packet types
local EXCHANGE_TYPE = { local EXCHANGE_TYPE = {
["02"] = "Main", ["02"] = "Main",
["04"] = "Aggressive", ["04"] = "Aggressive",
["05"] = "Informational", ["05"] = "Informational",
} }
-- Payload names -- Payload names
local PAYLOADS = { local PAYLOADS = {
["00"] = "None", ["00"] = "None",
["01"] = "SA", ["01"] = "SA",
["03"] = "Transform", ["03"] = "Transform",
["04"] = "Key Exchange", ["04"] = "Key Exchange",
["05"] = "ID", ["05"] = "ID",
["08"] = "Hash", ["08"] = "Hash",
["0A"] = "Nonce", ["0A"] = "Nonce",
["0D"] = "VID", ["0D"] = "VID",
} }
@@ -102,98 +102,98 @@ local PAYLOADS = {
-- (located in: nselib/data/ike-fingerprints.lua) -- (located in: nselib/data/ike-fingerprints.lua)
-- --
local function load_fingerprints() local function load_fingerprints()
local file, filename_full, fingerprints local file, filename_full, fingerprints
-- Check if fingerprints are cached -- Check if fingerprints are cached
if(nmap.registry.ike_fingerprints ~= nil) then if(nmap.registry.ike_fingerprints ~= nil) then
stdnse.print_debug(1, "ike: Loading cached fingerprints") stdnse.print_debug(1, "ike: Loading cached fingerprints")
return nmap.registry.ike_fingerprints return nmap.registry.ike_fingerprints
end end
-- Try and find the file -- Try and find the file
-- If it isn't in Nmap's directories, take it as a direct path -- If it isn't in Nmap's directories, take it as a direct path
filename_full = nmap.fetchfile('nselib/data/ike-fingerprints.lua') filename_full = nmap.fetchfile('nselib/data/ike-fingerprints.lua')
-- Load the file -- Load the file
stdnse.print_debug(1, "ike: Loading fingerprints: %s", filename_full) stdnse.print_debug(1, "ike: Loading fingerprints: %s", filename_full)
local env = setmetatable({fingerprints = {}}, {__index = _G}); local env = setmetatable({fingerprints = {}}, {__index = _G});
file = loadfile(filename_full, "t", env) file = loadfile(filename_full, "t", env)
if( not(file) ) then if( not(file) ) then
stdnse.print_debug(1, "ike: Couldn't load the file: %s", filename_full) stdnse.print_debug(1, "ike: Couldn't load the file: %s", filename_full)
return false, "Couldn't load fingerprint file: " .. filename_full return false, "Couldn't load fingerprint file: " .. filename_full
end end
file() file()
fingerprints = env.fingerprints fingerprints = env.fingerprints
-- Check there are fingerprints to use -- Check there are fingerprints to use
if(#fingerprints == 0 ) then if(#fingerprints == 0 ) then
return false, "No fingerprints were loaded after processing ".. filename_full return false, "No fingerprints were loaded after processing ".. filename_full
end end
return true, fingerprints return true, fingerprints
end end
-- generate a random hex-string of length 'length' -- generate a random hex-string of length 'length'
-- --
local function generate_random(length) local function generate_random(length)
local rnd = "" local rnd = ""
for i=1, length do for i=1, length do
rnd = rnd .. string.format("%.2X", math.random(255)) rnd = rnd .. string.format("%.2X", math.random(255))
end end
return rnd return rnd
end end
-- convert a string to a hex-string (of the ASCII representation) -- convert a string to a hex-string (of the ASCII representation)
-- --
local function convert_to_hex(id) local function convert_to_hex(id)
local hex_str = "" local hex_str = ""
for c in string.gmatch(id, ".") do for c in string.gmatch(id, ".") do
hex_str = hex_str .. string.format("%X", c:byte()) hex_str = hex_str .. string.format("%X", c:byte())
end end
return hex_str return hex_str
end end
-- Extract Payloads -- Extract Payloads
local function extract_payloads(packet) local function extract_payloads(packet)
-- packet only contains HDR -- packet only contains HDR
if packet:len() < 61 then return {} end if packet:len() < 61 then return {} end
local np = packet:sub(33,34) -- next payload local np = packet:sub(33,34) -- next payload
local index = 61 -- starting point for search local index = 61 -- starting point for search
local ike_headers = {} -- ike headers local ike_headers = {} -- ike headers
local payload = '' local payload = ''
-- loop over packet -- loop over packet
while PAYLOADS[np] ~= "None" and index <= packet:len() do while PAYLOADS[np] ~= "None" and index <= packet:len() do
local payload_length = tonumber("0x"..packet:sub(index, index+3)) * 2 local payload_length = tonumber("0x"..packet:sub(index, index+3)) * 2
payload = string.lower(packet:sub(index+4, index+payload_length-5)) payload = string.lower(packet:sub(index+4, index+payload_length-5))
-- debug -- debug
if PAYLOADS[np] == 'VID' then if PAYLOADS[np] == 'VID' then
stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s - %s', np, PAYLOADS[np], payload) stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s - %s', np, PAYLOADS[np], payload)
else else
stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s', np, PAYLOADS[np]) stdnse.print_debug(2, 'IKE: Found IKE Header: %s: %s', np, PAYLOADS[np])
end end
-- Store payload -- Store payload
if ike_headers[PAYLOADS[np]] == nil then if ike_headers[PAYLOADS[np]] == nil then
ike_headers[PAYLOADS[np]] = {payload} ike_headers[PAYLOADS[np]] = {payload}
else else
table.insert(ike_headers[PAYLOADS[np]], payload) table.insert(ike_headers[PAYLOADS[np]], payload)
end end
-- find the next payload type -- find the next payload type
np = packet:sub(index-4, index-3) np = packet:sub(index-4, index-3)
-- jump to the next payload -- jump to the next payload
index = index + payload_length index = index + payload_length
end end
return ike_headers return ike_headers
end end
@@ -201,152 +201,152 @@ end
-- Search the fingerprint database for matches -- Search the fingerprint database for matches
-- This is a (currently) divided into two parts -- This is a (currently) divided into two parts
-- 1) version detection based on single fingerprints -- 1) version detection based on single fingerprints
-- 2) version detection based on the order of all vendor ids -- 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 -- Input is a table of collected vendor-ids, output is a table
-- with fields: -- with fields:
-- vendor, version, name, attributes (table), guess (table), os -- vendor, version, name, attributes (table), guess (table), os
local function lookup(vendor_ids) 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 -- concat all vids to one string
local all_vids = '' local all_vids = ''
for _,vid in pairs(vendor_ids) do all_vids = all_vids .. vid end for _,vid in pairs(vendor_ids) do all_vids = all_vids .. vid end
-- the results -- the results
local info = { local info = {
vendor = nil, vendor = nil,
attribs = {}, attribs = {},
} }
local status, fingerprints local status, fingerprints
status, fingerprints = load_fingerprints() status, fingerprints = load_fingerprints()
if status then if status then
-- loop over the vendor_ids returned in ike request -- loop over the vendor_ids returned in ike request
for _,vendor_id in pairs(vendor_ids) do for _,vendor_id in pairs(vendor_ids) do
-- loop over the fingerprints found in database -- loop over the fingerprints found in database
for _,row in pairs(fingerprints) do 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 a match is found, check if it's a version detection or attribute
if row.category == 'vendor' then if row.category == 'vendor' then
local debug_string = '' local debug_string = ''
if row.vendor ~= nil then debug_string = debug_string .. row.vendor .. ' ' end if row.vendor ~= nil then debug_string = debug_string .. row.vendor .. ' ' end
if row.version ~= nil then debug_string = debug_string .. row.version 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) stdnse.print_debug(2, "IKE: Fingerprint: %s matches %s", vendor_id, debug_string)
-- Only store the first match -- Only store the first match
if info.vendor == nil then if info.vendor == nil then
-- the fingerprint contains information about the VID -- the fingerprint contains information about the VID
info.vendor = row info.vendor = row
end end
elseif row.category == 'attribute' then elseif row.category == 'attribute' then
info.attribs[ #info.attribs + 1] = row info.attribs[ #info.attribs + 1] = row
stdnse.print_debug(2, "IKE: Attribute: %s matches %s", vendor_id, row.text) stdnse.print_debug(2, "IKE: Attribute: %s matches %s", vendor_id, row.text)
break break
end end
end end
end end
end end
end end
--------------------------------------------------- ---------------------------------------------------
-- Search for the order of the vids -- Search for the order of the vids
-- Uses category 'vid_ordering' -- Uses category 'vid_ordering'
--- ---
-- search in the 'vid_ordering' category -- search in the 'vid_ordering' category
local debug_string = '' local debug_string = ''
for _,row in pairs(fingerprints) do 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 -- Use ordering information if there where no vendor matches from prevoius step
if info.vendor == nil then if info.vendor == nil then
info.vendor = row info.vendor = row
-- Debugging info -- Debugging info
debug_string = '' debug_string = ''
if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' ' end 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.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 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) stdnse.print_debug(2, 'IKE: No vendor match, but ordering match found: %s', debug_string)
return info return info
-- Update OS based on ordering -- Update OS based on ordering
elseif info.vendor.vendor == row.vendor then elseif info.vendor.vendor == row.vendor then
info.vendor.ostype = row.ostype info.vendor.ostype = row.ostype
-- Debugging info -- Debugging info
debug_string = '' debug_string = ''
if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' to ' end 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 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) 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 -- Only print debugging information if conflicting information is detected
else else
-- Debugging info -- Debugging info
debug_string = '' debug_string = ''
if info.vendor.vendor ~= nil then debug_string = debug_string .. info.vendor.vendor .. ' vs ' end 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 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) 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 end
-- Handle a response packet -- Handle a response packet
-- A very limited response parser -- A very limited response parser
-- Currently only the VIDs are extracted -- Currently only the VIDs are extracted
-- This could be made more advanced to -- This could be made more advanced to
-- allow for fingerprinting via the order -- allow for fingerprinting via the order
-- of the returned headers -- of the returned headers
--- ---
function response(packet) 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 -- extract the return type
local resp_type = EXCHANGE_TYPE[packet:sub(37,38)] local resp_type = EXCHANGE_TYPE[packet:sub(37,38)]
local ike_headers = {} local ike_headers = {}
-- simple check that the type is something other than 'Informational' -- simple check that the type is something other than 'Informational'
-- as this type does not include VIDs -- as this type does not include VIDs
if resp_type ~= "Informational" then if resp_type ~= "Informational" then
resp["mode"] = resp_type resp["mode"] = resp_type
ike_headers = extract_payloads(packet) ike_headers = extract_payloads(packet)
-- Extract the VIDs -- Extract the VIDs
resp['vids'] = ike_headers['VID'] resp['vids'] = ike_headers['VID']
-- search for fingerprints -- search for fingerprints
resp["info"] = lookup(resp['vids']) resp["info"] = lookup(resp['vids'])
-- indicate that a packet 'useful' packet was returned -- indicate that a packet 'useful' packet was returned
resp['success'] = true resp['success'] = true
end end
end end
return resp return resp
end end
@@ -356,212 +356,212 @@ end
-- --
function send_request( host, port, packet ) function send_request( host, port, packet )
local socket = nmap.new_socket() local socket = nmap.new_socket()
local s_status, r_status, data, i, hexstring, _ local s_status, r_status, data, i, hexstring, _
-- lock resource (port 500/udp) -- lock resource (port 500/udp)
local mutex = nmap.mutex("ike_port_500"); local mutex = nmap.mutex("ike_port_500");
mutex "lock"; mutex "lock";
-- send the request packet -- send the request packet
socket:set_timeout(1000) socket:set_timeout(1000)
socket:bind(nil, port.number) socket:bind(nil, port.number)
socket:connect(host, port, "udp") socket:connect(host, port, "udp")
s_status,_ = socket:send(packet) s_status,_ = socket:send(packet)
-- receive answer -- receive answer
if s_status then if s_status then
r_status, data = socket:receive_lines(1) r_status, data = socket:receive_lines(1)
if r_status then if r_status then
i, hexstring = bin.unpack("H" .. data:len(), data) i, hexstring = bin.unpack("H" .. data:len(), data)
socket:close() socket:close()
-- release mutex -- release mutex
mutex "done"; mutex "done";
return response(hexstring) return response(hexstring)
else else
socket:close() socket:close()
end end
else else
socket:close() socket:close()
end end
-- release mutex -- release mutex
mutex "done"; mutex "done";
return {} return {}
end end
-- Create the aggressive part of a packet -- Create the aggressive part of a packet
-- Aggressive mode includes the user-id, so the -- Aggressive mode includes the user-id, so the
-- length of this has to be taken into account -- length of this has to be taken into account
-- --
local function generate_aggressive(port, protocol, id, diffie) local function generate_aggressive(port, protocol, id, diffie)
local hex_port = string.format("%.4X", port) local hex_port = string.format("%.4X", port)
local hex_prot = PROTOCOL_IDS[protocol] local hex_prot = PROTOCOL_IDS[protocol]
local id_len = string.format("%.4X", 8 + id:len()) local id_len = string.format("%.4X", 8 + id:len())
-- get length of key data based on diffie -- get length of key data based on diffie
local key_length local key_length
if diffie == 1 then if diffie == 1 then
key_length = 96 key_length = 96
elseif diffie == 2 then elseif diffie == 2 then
key_length = 128 key_length = 128
elseif diffie == 5 then elseif diffie == 5 then
key_length = 192 key_length = 192
end end
return bin.pack(">SHHSSHSHCHHH", return bin.pack(">SHHSSHSHCHHH",
-- Key Exchange -- Key Exchange
0x0a00 , -- Next payload (Nonce) 0x0a00, -- Next payload (Nonce)
string.format("%04X", key_length+4) , -- Length (132-bit) string.format("%04X", key_length+4), -- Length (132-bit)
generate_random(key_length) , -- Random key data generate_random(key_length), -- Random key data
-- Nonce -- Nonce
0x0500 , -- Next payload (Identification) 0x0500, -- Next payload (Identification)
0x0018 , -- Length (24) 0x0018, -- Length (24)
generate_random(20) , -- Nonce data generate_random(20), -- Nonce data
-- Identification -- Identification
0x0000 , -- Next Payload (None) 0x0000, -- Next Payload (None)
id_len , -- Payload length (id + 8) id_len, -- Payload length (id + 8)
0x03 , -- ID Type (USER_FQDN) 0x03, -- ID Type (USER_FQDN)
hex_prot , -- Protocol ID (UDP) hex_prot, -- Protocol ID (UDP)
hex_port , -- Port (500) hex_port, -- Port (500)
convert_to_hex(id) -- Id Data (as hex) convert_to_hex(id) -- Id Data (as hex)
) )
end end
-- Create the transform -- Create the transform
-- AES encryption needs an extra value to define the key length -- AES encryption needs an extra value to define the key length
-- Currently only DES, 3DES and AES encryption is supported -- Currently only DES, 3DES and AES encryption is supported
-- --
local function generate_transform(auth, encryption, hash, group, number, total) local function generate_transform(auth, encryption, hash, group, number, total)
local key_length, trans_length, aes_enc, sep, enc local key_length, trans_length, aes_enc, sep, enc
local next_payload, payload_number local next_payload, payload_number
-- handle special case of aes -- handle special case of aes
if encryption:sub(1,3) == "aes" then if encryption:sub(1,3) == "aes" then
trans_length = 0x0028 trans_length = 0x0028
enc = ENC_METHODS[encryption][1] enc = ENC_METHODS[encryption][1]
key_length = ENC_METHODS[encryption][2] key_length = ENC_METHODS[encryption][2]
else else
trans_length = 0x0024 trans_length = 0x0024
enc = ENC_METHODS[encryption] enc = ENC_METHODS[encryption]
key_length = nil key_length = nil
end end
-- check if there are more transforms -- check if there are more transforms
if number == total then if number == total then
next_payload = 0x0000 -- none next_payload = 0x0000 -- none
else else
next_payload = 0x0300 -- transform next_payload = 0x0300 -- transform
end end
-- set the payload number -- set the payload number
payload_number = string.format("%.2X", number) payload_number = string.format("%.2X", number)
local trans = bin.pack(">SSHCSIIII", local trans = bin.pack(">SSHCSIIII",
next_payload , -- Next payload next_payload, -- Next payload
trans_length , -- Transform length trans_length, -- Transform length
payload_number , -- Transform number payload_number, -- Transform number
0x01 , -- Transform ID (IKE) 0x01, -- Transform ID (IKE)
0x0000 , -- spacers ? 0x0000, -- spacers ?
enc , -- Encryption algorithm enc, -- Encryption algorithm
HASH_ALGORITHM[hash] , -- Hash algorithm HASH_ALGORITHM[hash], -- Hash algorithm
AUTH_TYPES[auth] , -- Authentication method AUTH_TYPES[auth], -- Authentication method
GROUP_DESCRIPTION[group] -- Group Description GROUP_DESCRIPTION[group] -- Group Description
) )
if key_length ~= nil then if key_length ~= nil then
trans = trans .. bin.pack(">I", key_length) -- only set for aes trans = trans .. bin.pack(">I", key_length) -- only set for aes
end end
trans = trans .. bin.pack(">IL", trans = trans .. bin.pack(">IL",
0x800b0001 , -- Life type (seconds) 0x800b0001, -- Life type (seconds)
0x000c000400007080 -- Life duration (28800) 0x000c000400007080 -- Life duration (28800)
) )
return trans return trans
end end
-- Generate multiple transforms -- 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 function generate_transforms(transform_table)
local transforms = '' local transforms = ''
for i,t in pairs(transform_table) do for i,t in pairs(transform_table) do
transforms = transforms .. generate_transform(t.auth, t.encryption, t.hash, t.group, i, #transform_table) transforms = transforms .. generate_transform(t.auth, t.encryption, t.hash, t.group, i, #transform_table)
end end
return transforms return transforms
end end
-- Create a request packet -- Create a request packet
-- Support for multiple transforms, which minimizes the -- Support for multiple transforms, which minimizes the
-- the amount of traffic/packets needed to be sendt -- the amount of traffic/packets needed to be sendt
-- --
function request(port, proto, mode, transforms, diffie, id) function request(port, proto, mode, transforms, diffie, id)
local payload_after_sa, str_aggressive, l, l_sa, l_pro local payload_after_sa, str_aggressive, l, l_sa, l_pro
local number_transforms, transform_string local number_transforms, transform_string
transform_string = generate_transforms(transforms) transform_string = generate_transforms(transforms)
number_transforms = string.format("%.2X", #transforms) number_transforms = string.format("%.2X", #transforms)
-- check for aggressive vs Main mode -- check for aggressive vs Main mode
if mode == "Aggressive" then if mode == "Aggressive" then
str_aggressive = generate_aggressive(port, proto, id, diffie) str_aggressive = generate_aggressive(port, proto, id, diffie)
payload_after_sa = 0x0400 payload_after_sa = 0x0400
else else
str_aggressive = "" str_aggressive = ""
payload_after_sa = 0x0000 payload_after_sa = 0x0000
end end
-- calculate lengths -- calculate lengths
l = string.format("%.8X", 48 + transform_string:len() + str_aggressive:len()) l = string.format("%.8X", 48 + transform_string:len() + str_aggressive:len())
l_sa = string.format("%.4X", 20 + transform_string:len()) l_sa = string.format("%.4X", 20 + transform_string:len())
l_pro = string.format("%.4X", 8 + transform_string:len()) l_pro = string.format("%.4X", 8 + transform_string:len())
-- Build the packet -- Build the packet
local packet = bin.pack(">HLCCCCIHSHIISHCCCH", local packet = bin.pack(">HLCCCCIHSHIISHCCCH",
generate_random(8) , -- Initiator cookie generate_random(8), -- Initiator cookie
0x0000000000000000 , -- Responder cookie 0x0000000000000000, -- Responder cookie
0x01 , -- Next payload (SA) 0x01, -- Next payload (SA)
0x10 , -- Version 0x10, -- Version
EXCHANGE_MODE[mode] , -- Exchange type EXCHANGE_MODE[mode], -- Exchange type
0x00 , -- Flags 0x00, -- Flags
0x00000000 , -- Message id 0x00000000, -- Message id
l , -- packet length l, -- packet length
-- Security Association -- Security Association
payload_after_sa , -- Next payload (Key exchange, if aggressive mode) payload_after_sa, -- Next payload (Key exchange, if aggressive mode)
l_sa , -- Length l_sa, -- Length
0x00000001 , -- IPSEC 0x00000001, -- IPSEC
0x00000001 , -- Situation 0x00000001, -- Situation
--## Proposal --## Proposal
0x0000 , -- Next payload (None) 0x0000, -- Next payload (None)
l_pro , -- Payload length l_pro, -- Payload length
0x01 , -- Proposal number 0x01, -- Proposal number
0x01 , -- Protocol ID (ISAKMP) 0x01, -- Protocol ID (ISAKMP)
0x00 , -- SPI Size 0x00, -- SPI Size
number_transforms -- Proposal transforms number_transforms -- Proposal transforms
) )
packet = packet .. transform_string -- transform packet = packet .. transform_string -- transform
if mode == 'Aggressive' then if mode == 'Aggressive' then
packet = packet .. str_aggressive packet = packet .. str_aggressive
end end
return packet return packet
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ local arg_DB = stdnse.get_script_args("mongodb.db")
-- Some lazy shortcuts -- Some lazy shortcuts
local function dbg(str,...) local function dbg(str,...)
stdnse.print_debug(3, "MngoDb:"..str, ...) stdnse.print_debug(3, "MngoDb:"..str, ...)
end end
--local dbg =stdnse.print_debug --local dbg =stdnse.print_debug
@@ -55,7 +55,7 @@ local err =stdnse.print_debug
--module("bson", package.seeall) --module("bson", package.seeall)
--require("bin") --require("bin")
local function dbg_err(str,...) local function dbg_err(str,...)
stdnse.print_debug("Bson-ERR:"..str, ...) stdnse.print_debug("Bson-ERR:"..str, ...)
end end
--local err =stdnse.log_error --local err =stdnse.log_error
@@ -63,7 +63,7 @@ end
--@param input the string to pack --@param input the string to pack
--@return the packed nullterminated string --@return the packed nullterminated string
local function make_nullterminated_string(input) local function make_nullterminated_string(input)
return bin.pack("z",input) return bin.pack("z",input)
end end
--Converts an element (key, value) into bson binary data --Converts an element (key, value) into bson binary data
@@ -73,35 +73,35 @@ end
--@return result : the packed binary data OR error message --@return result : the packed binary data OR error message
local function _element_to_bson(key, value) local function _element_to_bson(key, value)
--Some constraints-checking --Some constraints-checking
if type(key) ~= 'string' then if type(key) ~= 'string' then
return false, "Documents must have only string keys, key was " .. type(key) return false, "Documents must have only string keys, key was " .. type(key)
end end
if key:sub(1,1) == "$" then if key:sub(1,1) == "$" then
return false, "key must not start with $: ".. key return false, "key must not start with $: ".. key
end end
if key:find("%.") then if key:find("%.") then
return false, ("key %r must not contain '.'"):format(tostring(key)) return false, ("key %r must not contain '.'"):format(tostring(key))
end end
local name =bin.pack("z",key) -- null-terminated string local name =bin.pack("z",key) -- null-terminated string
if type(value) == 'string' then if type(value) == 'string' then
local cstring = bin.pack("z",value) -- null-terminated string local cstring = bin.pack("z",value) -- null-terminated string
local length = bin.pack("<i", cstring:len()) local length = bin.pack("<i", cstring:len())
local op = bin.pack('H','02') local op = bin.pack('H','02')
return true, op .. name .. length .. cstring return true, op .. name .. length .. cstring
elseif type(value) =='table' then elseif type(value) =='table' then
return true, bin.pack('H','02') .. name .. toBson(value) return true, bin.pack('H','02') .. name .. toBson(value)
elseif type(value)== 'boolean' then elseif type(value)== 'boolean' then
return true, bin.pack('H','08') + name + bin.pack('H',value and '01' or '00') return true, bin.pack('H','08') + name + bin.pack('H',value and '01' or '00')
elseif type(value) == 'number' then elseif type(value) == 'number' then
--return bin.pack('H','10').. name .. bin.pack("<i", value) --return bin.pack('H','10').. name .. bin.pack("<i", value)
-- Use 01 - double for - works better than 10 -- Use 01 - double for - works better than 10
return true, bin.pack('H','01') .. name .. bin.pack("<d", value) return true, bin.pack('H','01') .. name .. bin.pack("<d", value)
end end
local _ = ("cannot convert value of type %s to bson"):format(type(value)) local _ = ("cannot convert value of type %s to bson"):format(type(value))
return false, _ return false, _
end end
--Converts a table of elements to binary bson format --Converts a table of elements to binary bson format
@@ -110,37 +110,37 @@ end
--@return result : a string of binary data OR error message --@return result : a string of binary data OR error message
function toBson(dict) function toBson(dict)
local elements = "" local elements = ""
--Put id first --Put id first
if dict._id then if dict._id then
local status,res = _element_to_bson("_id", dict._id) local status,res = _element_to_bson("_id", dict._id)
if not status then return false, res end if not status then return false, res end
elements = elements..res elements = elements..res
elseif ( dict._cmd ) then elseif ( dict._cmd ) then
for k, v in pairs(dict._cmd) do for k, v in pairs(dict._cmd) do
local status,res = _element_to_bson(k, v) local status,res = _element_to_bson(k, v)
if not status then return false, res end if not status then return false, res end
elements = elements..res elements = elements..res
end end
end end
--Concatenate binary values --Concatenate binary values
for key, value in pairs( dict ) do for key, value in pairs( dict ) do
if key ~= "_id" and key ~= "_cmd" then if key ~= "_id" and key ~= "_cmd" then
dbg("dictionary to bson : key,value =(%s,%s)",key,value) dbg("dictionary to bson : key,value =(%s,%s)",key,value)
local status,res = _element_to_bson(key,value) local status,res = _element_to_bson(key,value)
if not status then return false, res end if not status then return false, res end
elements = elements..res elements = elements..res
end end
end end
-- Get length -- Get length
local length = #elements + 5 local length = #elements + 5
if length > 4 * 1024 * 1024 then if length > 4 * 1024 * 1024 then
return false, "document too large - BSON documents are limited to 4 MB" return false, "document too large - BSON documents are limited to 4 MB"
end end
dbg("Packet length is %d",length) dbg("Packet length is %d",length)
--Final pack --Final pack
return true, bin.pack("I", length) .. elements .. bin.pack('H',"00") return true, bin.pack("I", length) .. elements .. bin.pack('H',"00")
end end
-- Reads a null-terminated string. If length is supplied, it is just cut -- Reads a null-terminated string. If length is supplied, it is just cut
@@ -150,18 +150,18 @@ end
--@return the string --@return the string
--@return the remaining data (*without* null-char) --@return the remaining data (*without* null-char)
local function get_c_string(data,length) local function get_c_string(data,length)
if not length then if not length then
local index = data:find(string.char(0)) local index = data:find(string.char(0))
if index == nil then if index == nil then
error({code="C-string did not contain NULL char"}) error({code="C-string did not contain NULL char"})
end end
length = index length = index
end end
local value = data:sub(1,length-1) 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 end
-- Element parser. Parse data elements -- Element parser. Parse data elements
@@ -170,47 +170,47 @@ end
-- @return Unpacked value -- @return Unpacked value
-- @return error string if error occurred -- @return error string if error occurred
local function parse(code,data) local function parse(code,data)
if 1 == code then -- double if 1 == code then -- double
return bin.unpack("<d", data) return bin.unpack("<d", data)
elseif 2 == code then -- string elseif 2 == code then -- string
-- data length = first four bytes -- data length = first four bytes
local _,len = bin.unpack("<i",data) local _,len = bin.unpack("<i",data)
-- string data = data[5] --> -- string data = data[5] -->
local value = get_c_string(data:sub(5), len) local value = get_c_string(data:sub(5), len)
-- Count position as header (=4) + length of string (=len)+ null char (=1) -- Count position as header (=4) + length of string (=len)+ null char (=1)
return 4+len+1,value return 4+len+1,value
elseif 3 == code or 4 == code then -- table or array elseif 3 == code or 4 == code then -- table or array
local object, err local object, err
-- Need to know the length, to return later -- Need to know the length, to return later
local _,obj_size = bin.unpack("<i", data) local _,obj_size = bin.unpack("<i", data)
-- Now, get the data object -- Now, get the data object
dbg("Recursing into bson array") dbg("Recursing into bson array")
object, data, err = fromBson(data) object, data, err = fromBson(data)
dbg("Recurse finished, got data object") dbg("Recurse finished, got data object")
-- And return where the parsing stopped -- And return where the parsing stopped
return obj_size+1, object return obj_size+1, object
--6 = _get_null --6 = _get_null
--7 = _get_oid --7 = _get_oid
elseif 8 == code then -- Boolean elseif 8 == code then -- Boolean
return 2, data:byte(1) == 1 return 2, data:byte(1) == 1
elseif 9 == code then -- int64, UTC datetime elseif 9 == code then -- int64, UTC datetime
return bin.unpack("<l", data) return bin.unpack("<l", data)
elseif 10 == code then -- nullvalue elseif 10 == code then -- nullvalue
return 0,nil return 0,nil
--11= _get_regex --11= _get_regex
--12= _get_ref --12= _get_ref
--13= _get_string, # code --13= _get_string, # code
--14= _get_string, # symbol --14= _get_string, # symbol
--15= _get_code_w_scope --15= _get_code_w_scope
elseif 16 == code then -- 4 byte integer elseif 16 == code then -- 4 byte integer
return bin.unpack("<i", data) return bin.unpack("<i", data)
--17= _get_timestamp --17= _get_timestamp
elseif 18 == code then -- long elseif 18 == code then -- long
return bin.unpack("<l", data) return bin.unpack("<l", data)
end end
local err = ("Getter for %d not implemented"):format(code) local err = ("Getter for %d not implemented"):format(code)
return 0, data, err return 0, data, err
end end
@@ -221,38 +221,38 @@ end
--@return Residual data not used --@return Residual data not used
--@return any error that occurred --@return any error that occurred
local function _element_to_dict(data) local function _element_to_dict(data)
local element_type, element_name, err, pos, value local element_type, element_name, err, pos, value
--local element_size = data:byte(1) --local element_size = data:byte(1)
element_type = data:byte(1) element_type = data:byte(1)
element_name, data = get_c_string(data:sub(2)) element_name, data = get_c_string(data:sub(2))
dbg(" Read element name '%s' (type:%s), data left: %d",element_name, element_type,data:len()) dbg(" Read element name '%s' (type:%s), data left: %d",element_name, element_type,data:len())
--pos,value,err = parsers.get(element_type)(data) --pos,value,err = parsers.get(element_type)(data)
pos,value,err = parse(element_type,data) pos,value,err = parse(element_type,data)
if(err ~= nil) then if(err ~= nil) then
dbg_err(err) dbg_err(err)
return nil,nil, data, err return nil,nil, data, err
end end
data=data:sub(pos) data=data:sub(pos)
dbg(" Read element value '%s', data left: %d",tostring(value), data:len()) dbg(" Read element value '%s', data left: %d",tostring(value), data:len())
return element_name, value, data return element_name, value, data
end end
--Reads all elements from binary to BSon --Reads all elements from binary to BSon
--@param data the data to read from --@param data the data to read from
--@return the resulting table --@return the resulting table
local function _elements_to_dict(data) local function _elements_to_dict(data)
local result = {} local result = {}
local key,value local key,value
while data and data:len() > 1 do while data and data:len() > 1 do
key, value, data = _element_to_dict(data) key, value, data = _element_to_dict(data)
dbg("Parsed (%s='%s'), data left : %d", tostring(key),tostring(value), data:len()) dbg("Parsed (%s='%s'), data left : %d", tostring(key),tostring(value), data:len())
if type(value) ~= 'table' then value=tostring(value) end if type(value) ~= 'table' then value=tostring(value) end
result[key] = value result[key] = value
end end
return result return result
end end
--Checks if enough data to parse the result is captured --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 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 --@return required size of packet, if known, otherwise nil
function isPacketComplete(data) function isPacketComplete(data)
-- First, we check that the header is complete -- First, we check that the header is complete
if data:len() < 4 then if data:len() < 4 then
local err_msg = "Not enough data in buffer, at least 4 bytes header info expected" local err_msg = "Not enough data in buffer, at least 4 bytes header info expected"
return false return false
end end
local _,obj_size = bin.unpack("<i", data) local _,obj_size = bin.unpack("<i", data)
dbg("BSon packet size is %s", obj_size) dbg("BSon packet size is %s", obj_size)
-- Check that all data is read and the packet is complete -- Check that all data is read and the packet is complete
if data:len() < obj_size then if data:len() < obj_size then
return false,obj_size return false,obj_size
end end
return true,obj_size return true,obj_size
end end
-- Converts bson binary data read from socket into a table -- Converts bson binary data read from socket into a table
@@ -285,18 +285,18 @@ end
--@return error message if not enough data was in packet --@return error message if not enough data was in packet
function fromBson(data) function fromBson(data)
dbg("Decoding, got %s bytes of data", data:len()) dbg("Decoding, got %s bytes of data", data:len())
local complete, object_size = isPacketComplete(data) local complete, object_size = isPacketComplete(data)
if not complete then if not complete then
local err_msg = ("Not enough data in buffer, expected %s but only has %d"):format(object_size or "?", data:len()) local err_msg = ("Not enough data in buffer, expected %s but only has %d"):format(object_size or "?", data:len())
dbg(err_msg) dbg(err_msg)
return {},data, err_msg return {},data, err_msg
end end
local element_portion = data:sub(5,object_size) local element_portion = data:sub(5,object_size)
local remainder = data:sub(object_size+1) local remainder = data:sub(object_size+1)
return _elements_to_dict(element_portion), remainder return _elements_to_dict(element_portion), remainder
end end
@@ -304,14 +304,14 @@ end
-- Test-code for debugging purposes below -- Test-code for debugging purposes below
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
function testBson() function testBson()
local p = toBson({hello="world", test="ok"}) local p = toBson({hello="world", test="ok"})
print( "Encoded something ok") print( "Encoded something ok")
local orig = fromBson(p) local orig = fromBson(p)
print(" Decoded something else ok") print(" Decoded something else ok")
for i,v in pairs(orig) do for i,v in pairs(orig) do
print(i,v) print(i,v)
end end
end end
--testBson() --testBson()
-------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------
@@ -330,15 +330,15 @@ struct {
} }
Opcodes : Opcodes :
OP_REPLY 1 Reply to a client request. responseTo is set OP_REPLY 1 Reply to a client request. responseTo is set
OP_MSG 1000 generic msg command followed by a string OP_MSG 1000 generic msg command followed by a string
OP_UPDATE 2001 update document OP_UPDATE 2001 update document
OP_INSERT 2002 insert new document OP_INSERT 2002 insert new document
OP_GET_BY_OID 2003 is this used? OP_GET_BY_OID 2003 is this used?
OP_QUERY 2004 query a collection OP_QUERY 2004 query a collection
OP_GET_MORE 2005 Get more data from a query. See Cursors OP_GET_MORE 2005 Get more data from a query. See Cursors
OP_DELETE 2006 Delete documents OP_DELETE 2006 Delete documents
OP_KILL_CURSORS 2007 Tell database client is done with a cursor OP_KILL_CURSORS 2007 Tell database client is done with a cursor
Query message : Query message :
struct { struct {
@@ -358,64 +358,64 @@ For more info about the MongoDB wire protocol, see http://www.mongodb.org/displa
-- DIY lua-class to create Mongo packets -- DIY lua-class to create Mongo packets
--@usage call MongoData:new({opCode=MongoData.OP.QUERY}) to create query object --@usage call MongoData:new({opCode=MongoData.OP.QUERY}) to create query object
MongoData ={ MongoData ={
uniqueRequestId = 12345, uniqueRequestId = 12345,
-- Opcodes used by Mongo db -- Opcodes used by Mongo db
OP = { OP = {
REPLY = 1, REPLY = 1,
MSG = 1000, MSG = 1000,
UPDATE = 2001, UPDATE = 2001,
INSERT = 2002, INSERT = 2002,
GET_BY_IOD =2003, GET_BY_IOD = 2003,
QUERY = 2004, QUERY = 2004,
GET_MORE = 2005, GET_MORE = 2005,
DELETE = 2006, DELETE = 2006,
KILL_CURSORS = 2007, KILL_CURSORS = 2007,
}, },
-- Lua-DIY constructor -- Lua-DIY constructor
new = function (self,o,opCode,responseTo) new = function (self,o,opCode,responseTo)
o = o or {} -- create object if user does not provide one o = o or {} -- create object if user does not provide one
setmetatable(o, self) -- DIY inheritance a'la javascript setmetatable(o, self) -- DIY inheritance a'la javascript
self.__index = self self.__index = self
self.valueString = '' self.valueString = ''
self.requestID = MongoData.uniqueRequestId -- Create unique id for message self.requestID = MongoData.uniqueRequestId -- Create unique id for message
MongoData.uniqueRequestId = MongoData.uniqueRequestId +1 MongoData.uniqueRequestId = MongoData.uniqueRequestId +1
return o return o
end end
} }
--Adds unsigned int32 to the message body --Adds unsigned int32 to the message body
--@param value the value to add --@param value the value to add
function MongoData:addUnsignedInt32(value) function MongoData:addUnsignedInt32(value)
self.valueString = self.valueString..bin.pack("I",value) self.valueString = self.valueString..bin.pack("I",value)
end end
-- Adds a string to the message body -- Adds a string to the message body
--@param value the string to add --@param value the string to add
function MongoData:addString(value) function MongoData:addString(value)
self.valueString = self.valueString..bin.pack('z',value) self.valueString = self.valueString..bin.pack('z',value)
end end
-- Add a table as a BSon object to the body -- Add a table as a BSon object to the body
--@param dict the table to be converted to BSon --@param dict the table to be converted to BSon
--@return status : true if ok, false if error occurred --@return status : true if ok, false if error occurred
--@return Error message if error occurred --@return Error message if error occurred
function MongoData:addBSON(dict) function MongoData:addBSON(dict)
-- status, res = bson.toBson(dict) -- status, res = bson.toBson(dict)
local status, res = toBson(dict) local status, res = toBson(dict)
if not status then if not status then
dbg(res) dbg(res)
return status,res return status,res
end end
self.valueString = self.valueString..res self.valueString = self.valueString..res
return true return true
end end
-- Returns the data in this packet in a raw string format to be sent on socket -- Returns the data in this packet in a raw string format to be sent on socket
-- This method creates necessary header information and puts it with the body -- This method creates necessary header information and puts it with the body
function MongoData:data() function MongoData:data()
local header = MongoData:new() local header = MongoData:new()
header:addUnsignedInt32( self.valueString:len()+4+4+4+4) header:addUnsignedInt32( self.valueString:len()+4+4+4+4)
header:addUnsignedInt32( self.requestID) header:addUnsignedInt32( self.requestID)
header:addUnsignedInt32( self.responseTo or 0xFFFFFFFF) header:addUnsignedInt32( self.responseTo or 0xFFFFFFFF)
header:addUnsignedInt32( self.opCode) header:addUnsignedInt32( self.opCode)
return header.valueString .. self.valueString return header.valueString .. self.valueString
end end
-- Creates a query -- Creates a query
-- @param collectionName string specifying the collection to run query against -- @param collectionName string specifying the collection to run query against
@@ -423,54 +423,54 @@ end
--@return status : true for OK, false for error --@return status : true for OK, false for error
--@return packet data OR error message --@return packet data OR error message
local function createQuery(collectionName, query) local function createQuery(collectionName, query)
local packet = MongoData:new({opCode=MongoData.OP.QUERY}) local packet = MongoData:new({opCode=MongoData.OP.QUERY})
packet:addUnsignedInt32(0); -- options packet:addUnsignedInt32(0); -- options
packet:addString(collectionName); packet:addString(collectionName);
packet:addUnsignedInt32(0) -- number to skip packet:addUnsignedInt32(0) -- number to skip
packet:addUnsignedInt32(-1) -- number to return : no limit packet:addUnsignedInt32(-1) -- number to return : no limit
local status, error = packet:addBSON(query) local status, error = packet:addBSON(query)
if not status then if not status then
return status, error return status, error
end end
return true, packet:data() return true, packet:data()
end end
-- Creates a get last error query -- Creates a get last error query
-- @param responseTo optional identifier this packet is a response to -- @param responseTo optional identifier this packet is a response to
--@return status : true for OK, false for error --@return status : true for OK, false for error
--@return packet data OR error message --@return packet data OR error message
function lastErrorQuery(responseTo) function lastErrorQuery(responseTo)
local collectionName = "test.$cmd" local collectionName = "test.$cmd"
local query = {getlasterror=1} local query = {getlasterror=1}
return createQuery(collectionName, query) return createQuery(collectionName, query)
end end
-- Creates a server status query -- Creates a server status query
-- @param responseTo optional identifier this packet is a response to -- @param responseTo optional identifier this packet is a response to
--@return status : true for OK, false for error --@return status : true for OK, false for error
--@return packet data OR error message --@return packet data OR error message
function serverStatusQuery(responseTo) function serverStatusQuery(responseTo)
local collectionName = "test.$cmd" local collectionName = "test.$cmd"
local query = {serverStatus = 1} local query = {serverStatus = 1}
return createQuery(collectionName, query) return createQuery(collectionName, query)
end end
-- Creates a optime query -- Creates a optime query
-- @param responseTo optional identifier this packet is a response to -- @param responseTo optional identifier this packet is a response to
--@return status : true for OK, false for error --@return status : true for OK, false for error
--@return packet data OR error message --@return packet data OR error message
function opTimeQuery(responseTo) function opTimeQuery(responseTo)
local collectionName = "test.$cmd" local collectionName = "test.$cmd"
local query = {getoptime = 1} local query = {getoptime = 1}
return createQuery(collectionName, query) return createQuery(collectionName, query)
end end
-- Creates a list databases query -- Creates a list databases query
-- @param responseTo optional identifier this packet is a response to -- @param responseTo optional identifier this packet is a response to
--@return status : true for OK, false for error --@return status : true for OK, false for error
--@return packet data OR error message --@return packet data OR error message
function listDbQuery(responseTo) function listDbQuery(responseTo)
local collectionName = "admin.$cmd" local collectionName = "admin.$cmd"
local query = {listDatabases = 1} local query = {listDatabases = 1}
return createQuery(collectionName, query) return createQuery(collectionName, query)
end end
-- Creates a build info query -- Creates a build info query
-- @param responseTo optional identifier this packet is a response to -- @param responseTo optional identifier this packet is a response to
@@ -479,20 +479,20 @@ end
--@return status : true for OK, false for error --@return status : true for OK, false for error
--@return packet data OR error message --@return packet data OR error message
function buildInfoQuery(responseTo) function buildInfoQuery(responseTo)
local collectionName = "admin.$cmd" local collectionName = "admin.$cmd"
local query = {buildinfo = 1} local query = {buildinfo = 1}
return createQuery(collectionName, query) return createQuery(collectionName, query)
end end
--Reads an int32 from data --Reads an int32 from data
--@return int32 value --@return int32 value
--@return data unread --@return data unread
local function parseInt32(data) local function parseInt32(data)
local pos,val = bin.unpack("<i",data) local pos,val = bin.unpack("<i",data)
return val, data:sub(pos) return val, data:sub(pos)
end end
local function parseInt64(data) local function parseInt64(data)
local pos,val = bin.unpack("<l",data) local pos,val = bin.unpack("<l",data)
return val, data:sub(pos) return val, data:sub(pos)
end end
-- Parses response header -- Parses response header
-- The response header looks like this : -- The response header looks like this :
@@ -509,44 +509,44 @@ struct {
--@param the data from socket --@param the data from socket
--@return a table containing the header data --@return a table containing the header data
local function parseResponseHeader(data) local function parseResponseHeader(data)
local response= {} local response= {}
local hdr, rflag, cID, sfrom, nRet, docs local hdr, rflag, cID, sfrom, nRet, docs
-- First std message header -- First std message header
hdr ={} hdr ={}
hdr["messageLength"], data = parseInt32(data) hdr["messageLength"], data = parseInt32(data)
hdr["requestID"], data = parseInt32(data) hdr["requestID"], data = parseInt32(data)
hdr["responseTo"], data = parseInt32(data) hdr["responseTo"], data = parseInt32(data)
hdr["opCode"], data = parseInt32(data) hdr["opCode"], data = parseInt32(data)
response["header"] = hdr response["header"] = hdr
-- Some additional fields -- Some additional fields
response["responseFlag"] ,data = parseInt32(data) response["responseFlag"] ,data = parseInt32(data)
response["cursorID"] ,data = parseInt64(data) response["cursorID"] ,data = parseInt64(data)
response["startingFrom"] ,data = parseInt32(data) response["startingFrom"] ,data = parseInt32(data)
response["numberReturned"] ,data = parseInt32(data) response["numberReturned"] ,data = parseInt32(data)
response["bson"] = data response["bson"] = data
return response return response
end end
--Checks if enough data to parse the result is captured --Checks if enough data to parse the result is captured
--@data binary mongodb data read from socket --@data binary mongodb data read from socket
--@return true if the full mongodb packet is contained in the data, false if data is incomplete --@return true if the full mongodb packet is contained in the data, false if data is incomplete
--@return required size of packet, if known, otherwise nil --@return required size of packet, if known, otherwise nil
function isPacketComplete(data) function isPacketComplete(data)
-- First, we check that the header is complete -- First, we check that the header is complete
if data:len() < 4 then if data:len() < 4 then
local err_msg = "Not enough data in buffer, at least 4 bytes header info expected" local err_msg = "Not enough data in buffer, at least 4 bytes header info expected"
return false return false
end end
local _,obj_size = bin.unpack("<i", data) local _,obj_size = bin.unpack("<i", data)
dbg("MongoDb Packet size is %s, (got %d)", obj_size,data:len()) dbg("MongoDb Packet size is %s, (got %d)", obj_size,data:len())
-- Check that all data is read and the packet is complete -- Check that all data is read and the packet is complete
if data:len() < obj_size then if data:len() < obj_size then
return false,obj_size return false,obj_size
end end
return true,obj_size return true,obj_size
end end
-- Sends a packet over a socket, reads the response -- Sends a packet over a socket, reads the response
@@ -555,77 +555,77 @@ end
--@return result : table of status ok, error msg if bad --@return result : table of status ok, error msg if bad
--@return if status ok : remaining data read from socket but not used --@return if status ok : remaining data read from socket but not used
function query(socket, data) function query(socket, data)
--Create an error handler --Create an error handler
local catch = function() local catch = function()
socket:close() socket:close()
stdnse.print_debug(string.format("Query failed")) stdnse.print_debug(string.format("Query failed"))
end end
local try = nmap.new_try(catch) local try = nmap.new_try(catch)
try( socket:send( data ) ) try( socket:send( data ) )
local data = "" local data = ""
local result = {} local result = {}
local err_msg local err_msg
local isComplete, pSize local isComplete, pSize
while not isComplete do while not isComplete do
dbg("mongo: waiting for data from socket, got %d bytes so far...",data:len()) dbg("mongo: waiting for data from socket, got %d bytes so far...",data:len())
data = data .. try( socket:receive() ) data = data .. try( socket:receive() )
isComplete, pSize = isPacketComplete(data) isComplete, pSize = isPacketComplete(data)
end end
-- All required data shold be read now -- All required data shold be read now
local packetData = data:sub(1,pSize) local packetData = data:sub(1,pSize)
local residualData = data:sub(pSize+1) local residualData = data:sub(pSize+1)
local responseHeader = parseResponseHeader(packetData) local responseHeader = parseResponseHeader(packetData)
if responseHeader["responseFlag"] ~= 0 then if responseHeader["responseFlag"] ~= 0 then
dbg("Response flag not zero : %d, some error occurred", responseHeader["responseFlag"]) dbg("Response flag not zero : %d, some error occurred", responseHeader["responseFlag"])
end end
local bsonData = responseHeader["bson"] local bsonData = responseHeader["bson"]
if #bsonData == 0 then if #bsonData == 0 then
dbg("No BSon data returned ") dbg("No BSon data returned ")
return false, "No Bson data returned" return false, "No Bson data returned"
end end
-- result, data, err_msg = bson.fromBson(bsonData) -- result, data, err_msg = bson.fromBson(bsonData)
result, data, err_msg = fromBson(bsonData) result, data, err_msg = fromBson(bsonData)
if err_msg then if err_msg then
dbg("Got error converting from bson: %s" , err_msg) dbg("Got error converting from bson: %s" , err_msg)
return false, ("Got error converting from bson: %s"):format(err_msg) return false, ("Got error converting from bson: %s"):format(err_msg)
end end
return true,result, residualData return true,result, residualData
end end
function login(socket, db, username, password) function login(socket, db, username, password)
local collectionName = ("%s.$cmd"):format(arg_DB or db) local collectionName = ("%s.$cmd"):format(arg_DB or db)
local query = { getnonce = 1 } local query = { getnonce = 1 }
local status, packet = createQuery(collectionName, query) local status, packet = createQuery(collectionName, query)
local response local response
status, response = query(socket, packet) status, response = query(socket, packet)
if ( not(status) or not(response.nonce) ) then if ( not(status) or not(response.nonce) ) then
return false, "Failed to retrieve nonce" return false, "Failed to retrieve nonce"
end end
local nonce = response.nonce local nonce = response.nonce
local pwdigest = stdnse.tohex(openssl.md5(username .. ':mongo:' ..password)) local pwdigest = stdnse.tohex(openssl.md5(username .. ':mongo:' ..password))
local digest = stdnse.tohex(openssl.md5(nonce .. username .. pwdigest)) local digest = stdnse.tohex(openssl.md5(nonce .. username .. pwdigest))
query = { user = username, nonce = nonce, key = digest } query = { user = username, nonce = nonce, key = digest }
query._cmd = { authenticate = 1 } query._cmd = { authenticate = 1 }
local status, packet = createQuery(collectionName, query) local status, packet = createQuery(collectionName, query)
status, response = query(socket, packet) status, response = query(socket, packet)
if ( not(status) ) then if ( not(status) ) then
return status, response return status, response
elseif ( response.errmsg == "auth fails" ) then elseif ( response.errmsg == "auth fails" ) then
return false, "Authentication failed" return false, "Authentication failed"
elseif ( response.errmsg ) then elseif ( response.errmsg ) then
return false, response.errmsg return false, response.errmsg
end end
return status, response return status, response
end end
@@ -634,16 +634,16 @@ end
-- @return table suitable for <code>stdnse.format_output</code> -- @return table suitable for <code>stdnse.format_output</code>
function queryResultToTable( resultTable ) function queryResultToTable( resultTable )
local result = {} local result = {}
for k,v in pairs( resultTable ) do for k,v in pairs( resultTable ) do
if type(v) == 'table' then if type(v) == 'table' then
table.insert(result,k) table.insert(result,k)
table.insert(result,queryResultToTable(v)) table.insert(result,queryResultToTable(v))
else else
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v)))) table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
end end
end end
return result return result
end end
---------------------------------------------------------------------------------- ----------------------------------------------------------------------------------
@@ -656,30 +656,30 @@ end
-- @param strData the data in a string format -- @param strData the data in a string format
local function printBuffer(strData) local function printBuffer(strData)
local out = '' local out = ''
local ch local ch
for i = 1,strData:len() do for i = 1,strData:len() do
out = out .." " out = out .." "
ch =strData:byte(i) ch =strData:byte(i)
if(ch < 16) then if(ch < 16) then
ch = string.format("0%x",ch) ch = string.format("0%x",ch)
else ch = string.format("%x",ch) else ch = string.format("%x",ch)
end end
--if ch > 64 and ch < 123 then --if ch > 64 and ch < 123 then
-- out = out .. string.char(ch) -- out = out .. string.char(ch)
--else --else
out = out .. ch out = out .. ch
--end --end
end end
print(out) print(out)
end end
-- function test() -- function test()
-- local res -- local res
-- res = versionQuery() -- res = versionQuery()
-- print(type(res),res:len(),res) -- print(type(res),res:len(),res)
-- local out= bin.unpack('C'..#res,res) -- local out= bin.unpack('C'..#res,res)
-- printBuffer(res) -- printBuffer(res)
-- end -- end
--test() --test()

File diff suppressed because it is too large Load Diff

View File

@@ -31,59 +31,59 @@ _ENV = stdnse.module("msrpcperformance", stdnse.seeall)
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_title_database(data, pos) local function parse_perf_title_database(data, pos)
local result = {} local result = {}
local i = 1 local i = 1
repeat repeat
local number, name local number, name
pos, number, name = bin.unpack("<zz", data, pos) pos, number, name = bin.unpack("<zz", data, pos)
if(number == nil) then if(number == nil) then
return false, "Couldn't parse the title database: end of string encountered early" return false, "Couldn't parse the title database: end of string encountered early"
elseif(tonumber(number) == nil) then -- Not sure if this actually happens, but it doesn't hurt to check elseif(tonumber(number) == nil) then -- Not sure if this actually happens, but it doesn't hurt to check
stdnse.print_debug(1, "MSRPC: ERROR: Couldn't parse the title database: string found where number expected (%d: '%s')", i, number) stdnse.print_debug(1, "MSRPC: ERROR: Couldn't parse the title database: string found where number expected (%d: '%s')", i, number)
return false, "Couldn't parse the title database" return false, "Couldn't parse the title database"
end end
result[tonumber(number)] = name result[tonumber(number)] = name
i = i + 1 i = i + 1
until pos >= #data until pos >= #data
return true, pos, result return true, pos, result
end end
---Parses a PERF_DATA_BLOCK, which has the following definition (from "WinPerf.h" on Visual Studio 8): ---Parses a PERF_DATA_BLOCK, which has the following definition (from "WinPerf.h" on Visual Studio 8):
-- --
--<code> --<code>
-- typedef struct _PERF_DATA_BLOCK { -- typedef struct _PERF_DATA_BLOCK {
-- WCHAR Signature[4]; // Signature: Unicode "PERF" -- WCHAR Signature[4]; // Signature: Unicode "PERF"
-- DWORD LittleEndian; // 0 = Big Endian, 1 = Little Endian -- DWORD LittleEndian; // 0 = Big Endian, 1 = Little Endian
-- DWORD Version; // Version of these data structures -- DWORD Version; // Version of these data structures
-- // starting at 1 -- // starting at 1
-- DWORD Revision; // Revision of these data structures -- DWORD Revision; // Revision of these data structures
-- // starting at 0 for each Version -- // starting at 0 for each Version
-- DWORD TotalByteLength; // Total length of data block -- DWORD TotalByteLength; // Total length of data block
-- DWORD HeaderLength; // Length of this structure -- DWORD HeaderLength; // Length of this structure
-- DWORD NumObjectTypes; // Number of types of objects -- DWORD NumObjectTypes; // Number of types of objects
-- // being reported -- // being reported
-- LONG DefaultObject; // Object Title Index of default -- LONG DefaultObject; // Object Title Index of default
-- // object to display when data from -- // object to display when data from
-- // this system is retrieved (-1 = -- // this system is retrieved (-1 =
-- // none, but this is not expected to -- // none, but this is not expected to
-- // be used) -- // be used)
-- SYSTEMTIME SystemTime; // Time at the system under -- SYSTEMTIME SystemTime; // Time at the system under
-- // measurement -- // measurement
-- LARGE_INTEGER PerfTime; // Performance counter value -- LARGE_INTEGER PerfTime; // Performance counter value
-- // at the system under measurement -- // at the system under measurement
-- LARGE_INTEGER PerfFreq; // Performance counter frequency -- LARGE_INTEGER PerfFreq; // Performance counter frequency
-- // at the system under measurement -- // at the system under measurement
-- LARGE_INTEGER PerfTime100nSec; // Performance counter time in 100 nsec -- LARGE_INTEGER PerfTime100nSec; // Performance counter time in 100 nsec
-- // units at the system under measurement -- // units at the system under measurement
-- DWORD SystemNameLength; // Length of the system name -- DWORD SystemNameLength; // Length of the system name
-- DWORD SystemNameOffset; // Offset, from beginning of this -- DWORD SystemNameOffset; // Offset, from beginning of this
-- // structure, to name of system -- // structure, to name of system
-- // being measured -- // being measured
-- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK; -- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK;
--</code> --</code>
-- --
--@param data The data being processed. --@param data The data being processed.
@@ -91,47 +91,47 @@ end
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_data_block(data, pos) local function parse_perf_data_block(data, pos)
local result = {} local result = {}
pos, result['Signature'] = msrpctypes.unicode_to_string(data, pos, 4, false) pos, result['Signature'] = msrpctypes.unicode_to_string(data, pos, 4, false)
if(result['Signature'] ~= "PERF") then if(result['Signature'] ~= "PERF") then
return false, "MSRPC: PERF_DATA_BLOCK signature is missing or incorrect" return false, "MSRPC: PERF_DATA_BLOCK signature is missing or incorrect"
end end
pos, result['LittleEndian'] = msrpctypes.unmarshall_int32(data, pos) pos, result['LittleEndian'] = msrpctypes.unmarshall_int32(data, pos)
if(result['LittleEndian'] ~= 1) then if(result['LittleEndian'] ~= 1) then
return false, "MSRPC: PERF_DATA_BLOCK returned a non-understood endianness" return false, "MSRPC: PERF_DATA_BLOCK returned a non-understood endianness"
end end
-- Parse the header -- Parse the header
pos, result['Version'] = msrpctypes.unmarshall_int32(data, pos) pos, result['Version'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['Revision'] = msrpctypes.unmarshall_int32(data, pos) pos, result['Revision'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos) pos, result['TotalByteLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos) pos, result['HeaderLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NumObjectTypes'] = msrpctypes.unmarshall_int32(data, pos) pos, result['NumObjectTypes'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['DefaultObject'] = msrpctypes.unmarshall_int32(data, pos) pos, result['DefaultObject'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['SystemTime'] = msrpctypes.unmarshall_SYSTEMTIME(data, pos) pos, result['SystemTime'] = msrpctypes.unmarshall_SYSTEMTIME(data, pos)
pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos)
pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) pos, result['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos)
pos, result['PerfTime100nSec'] = 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 = pos + 4 -- This value doesn't seem to line up, so add 4
pos, result['SystemNameLength'] = msrpctypes.unmarshall_int32(data, pos) pos, result['SystemNameLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['SystemNameOffset'] = 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 -- 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 -- (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. -- could be anywhere else.
if(pos ~= result['SystemNameOffset'] + 1) then if(pos ~= result['SystemNameOffset'] + 1) then
return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location" return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location"
end end
-- Read the system name from the next location (which happens to be identical to SystemNameOffset, on a proper system) -- 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, 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 end
@@ -144,74 +144,74 @@ end
-- // type section begins with a _PERF_OBJECT_TYPE structure. -- // type section begins with a _PERF_OBJECT_TYPE structure.
-- // -- //
-- typedef struct _PERF_OBJECT_TYPE { -- typedef struct _PERF_OBJECT_TYPE {
-- DWORD TotalByteLength; // Length of this object definition -- DWORD TotalByteLength; // Length of this object definition
-- // including this structure, the -- // including this structure, the
-- // counter definitions, and the -- // counter definitions, and the
-- // instance definitions and the -- // instance definitions and the
-- // counter blocks for each instance: -- // counter blocks for each instance:
-- // This is the offset from this -- // This is the offset from this
-- // structure to the next object, if -- // structure to the next object, if
-- // any -- // any
-- DWORD DefinitionLength; // Length of object definition, -- DWORD DefinitionLength; // Length of object definition,
-- // which includes this structure -- // which includes this structure
-- // and the counter definition -- // and the counter definition
-- // structures for this object: this -- // structures for this object: this
-- // is the offset of the first -- // is the offset of the first
-- // instance or of the counters -- // instance or of the counters
-- // for this object if there is -- // for this object if there is
-- // no instance -- // no instance
-- DWORD HeaderLength; // Length of this structure: this -- DWORD HeaderLength; // Length of this structure: this
-- // is the offset to the first -- // is the offset to the first
-- // counter definition for this -- // counter definition for this
-- // object -- // object
-- DWORD ObjectNameTitleIndex; -- DWORD ObjectNameTitleIndex;
-- // Index to name in Title Database -- // Index to name in Title Database
-- #ifdef _WIN64 -- #ifdef _WIN64
-- DWORD ObjectNameTitle; // Should use this as an offset -- DWORD ObjectNameTitle; // Should use this as an offset
-- #else -- #else
-- LPWSTR ObjectNameTitle; // Initially NULL, for use by -- LPWSTR ObjectNameTitle; // Initially NULL, for use by
-- // analysis program to point to -- // analysis program to point to
-- // retrieved title string -- // retrieved title string
-- #endif -- #endif
-- DWORD ObjectHelpTitleIndex; -- DWORD ObjectHelpTitleIndex;
-- // Index to Help in Title Database -- // Index to Help in Title Database
-- #ifdef _WIN64 -- #ifdef _WIN64
-- DWORD ObjectHelpTitle; // Should use this as an offset -- DWORD ObjectHelpTitle; // Should use this as an offset
-- #else -- #else
-- LPWSTR ObjectHelpTitle; // Initially NULL, for use by -- LPWSTR ObjectHelpTitle; // Initially NULL, for use by
-- // analysis program to point to -- // analysis program to point to
-- // retrieved title string -- // retrieved title string
-- #endif -- #endif
-- DWORD DetailLevel; // Object level of detail (for -- DWORD DetailLevel; // Object level of detail (for
-- // controlling display complexity); -- // controlling display complexity);
-- // will be min of detail levels -- // will be min of detail levels
-- // for all this object's counters -- // for all this object's counters
-- DWORD NumCounters; // Number of counters in each -- DWORD NumCounters; // Number of counters in each
-- // counter block (one counter -- // counter block (one counter
-- // block per instance) -- // block per instance)
-- LONG DefaultCounter; // Default counter to display when -- LONG DefaultCounter; // Default counter to display when
-- // this object is selected, index -- // this object is selected, index
-- // starting at 0 (-1 = none, but -- // starting at 0 (-1 = none, but
-- // this is not expected to be used) -- // this is not expected to be used)
-- LONG NumInstances; // Number of object instances -- LONG NumInstances; // Number of object instances
-- // for which counters are being -- // for which counters are being
-- // returned from the system under -- // returned from the system under
-- // measurement. If the object defined -- // measurement. If the object defined
-- // will never have any instance data -- // will never have any instance data
-- // structures (PERF_INSTANCE_DEFINITION) -- // structures (PERF_INSTANCE_DEFINITION)
-- // then this value should be -1, if the -- // then this value should be -1, if the
-- // object can have 0 or more instances, -- // object can have 0 or more instances,
-- // but has none present, then this -- // but has none present, then this
-- // should be 0, otherwise this field -- // should be 0, otherwise this field
-- // contains the number of instances of -- // contains the number of instances of
-- // this counter. -- // this counter.
-- DWORD CodePage; // 0 if instance strings are in -- DWORD CodePage; // 0 if instance strings are in
-- // UNICODE, else the Code Page of -- // UNICODE, else the Code Page of
-- // the instance names -- // the instance names
-- LARGE_INTEGER PerfTime; // Sample Time in "Object" units -- LARGE_INTEGER PerfTime; // Sample Time in "Object" units
-- // -- //
-- LARGE_INTEGER PerfFreq; // Frequency of "Object" units in -- LARGE_INTEGER PerfFreq; // Frequency of "Object" units in
-- // counts per second. -- // counts per second.
-- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE; -- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE;
--</code> --</code>
-- --
@@ -220,69 +220,69 @@ end
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_object_type(data, pos) 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['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['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['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['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['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['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['ObjectHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) -- TODO: will this workw ith 64-bit?
pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos) 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['NumCounters'] = msrpctypes.unmarshall_int32(data, pos) -- The number of counters in each counter block
pos, result['DefaultCounter'] = msrpctypes.unmarshall_int32(data, pos) 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['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 pos, result['CodePage'] = msrpctypes.unmarshall_int32(data, pos) -- 0 if strings are in UNICODE, otherwise the Code Page
-- if(result['CodePage'] ~= 0) then -- if(result['CodePage'] ~= 0) then
-- return false, string.format("Unknown Code Page for data: %d\n", result['CodePage']) -- return false, string.format("Unknown Code Page for data: %d\n", result['CodePage'])
-- end -- end
pos, result['PerfTime'] = msrpctypes.unmarshall_int64(data, pos) -- Sample time in "Object" units 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['PerfFreq'] = msrpctypes.unmarshall_int64(data, pos) -- Frequency of "Object" units in counts/second
return true, pos, result return true, pos, result
end end
---Parse a PERF_COUNTER_DEFINITION structure. From Microsoft's documentation: ---Parse a PERF_COUNTER_DEFINITION structure. From Microsoft's documentation:
-- --
--<code> --<code>
-- // There is one of the following for each of the -- // There is one of the following for each of the
-- // PERF_OBJECT_TYPE.NumCounters. The Unicode names in this structure MUST -- // PERF_OBJECT_TYPE.NumCounters. The Unicode names in this structure MUST
-- // come from a message file. -- // come from a message file.
-- typedef struct _PERF_COUNTER_DEFINITION { -- typedef struct _PERF_COUNTER_DEFINITION {
-- DWORD ByteLength; // Length in bytes of this structure -- DWORD ByteLength; // Length in bytes of this structure
-- DWORD CounterNameTitleIndex; -- DWORD CounterNameTitleIndex;
-- // Index of Counter name into -- // Index of Counter name into
-- // Title Database -- // Title Database
-- #ifdef _WIN64 -- #ifdef _WIN64
-- DWORD CounterNameTitle; -- DWORD CounterNameTitle;
-- #else -- #else
-- LPWSTR CounterNameTitle; // Initially NULL, for use by -- LPWSTR CounterNameTitle; // Initially NULL, for use by
-- // analysis program to point to -- // analysis program to point to
-- // retrieved title string -- // retrieved title string
-- #endif -- #endif
-- DWORD CounterHelpTitleIndex; -- DWORD CounterHelpTitleIndex;
-- // Index of Counter Help into -- // Index of Counter Help into
-- // Title Database -- // Title Database
-- #ifdef _WIN64 -- #ifdef _WIN64
-- DWORD CounterHelpTitle; -- DWORD CounterHelpTitle;
-- #else -- #else
-- LPWSTR CounterHelpTitle; // Initially NULL, for use by -- LPWSTR CounterHelpTitle; // Initially NULL, for use by
-- // analysis program to point to -- // analysis program to point to
-- // retrieved title string -- // retrieved title string
-- #endif -- #endif
-- LONG DefaultScale; // Power of 10 by which to scale -- LONG DefaultScale; // Power of 10 by which to scale
-- // chart line if vertical axis is 100 -- // chart line if vertical axis is 100
-- // 0 ==> 1, 1 ==> 10, -1 ==>1/10, etc. -- // 0 ==> 1, 1 ==> 10, -1 ==>1/10, etc.
-- DWORD DetailLevel; // Counter level of detail (for -- DWORD DetailLevel; // Counter level of detail (for
-- // controlling display complexity) -- // controlling display complexity)
-- DWORD CounterType; // Type of counter -- DWORD CounterType; // Type of counter
-- DWORD CounterSize; // Size of counter in bytes -- DWORD CounterSize; // Size of counter in bytes
-- DWORD CounterOffset; // Offset from the start of the -- DWORD CounterOffset; // Offset from the start of the
-- // PERF_COUNTER_BLOCK to the first -- // PERF_COUNTER_BLOCK to the first
-- // byte of this counter -- // byte of this counter
-- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION; -- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION;
--</code> --</code>
-- --
--@param data The data being processed. --@param data The data being processed.
@@ -290,23 +290,23 @@ end
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_counter_definition(data, pos) local function parse_perf_counter_definition(data, pos)
local result = {} local result = {}
local initial_pos = pos local initial_pos = pos
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) pos, result['CounterNameTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterNameTitle'] = msrpctypes.unmarshall_int32(data, pos) pos, result['CounterNameTitle'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) pos, result['CounterHelpTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterHelpTitle'] = msrpctypes.unmarshall_int32(data, pos) pos, result['CounterHelpTitle'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['DefaultScale'] = msrpctypes.unmarshall_int32(data, pos) pos, result['DefaultScale'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos) pos, result['DetailLevel'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterType'] = msrpctypes.unmarshall_int32(data, pos) pos, result['CounterType'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterSize'] = msrpctypes.unmarshall_int32(data, pos) pos, result['CounterSize'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['CounterOffset'] = 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 end
---Parse the actual counter value. This is a fairly simple function, it takes a counter ---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 <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_counter(data, pos, counter_definition) local function parse_perf_counter(data, pos, counter_definition)
local result local result
if(counter_definition['CounterSize'] == 4) then if(counter_definition['CounterSize'] == 4) then
pos, result = msrpctypes.unmarshall_int32(data, pos) pos, result = msrpctypes.unmarshall_int32(data, pos)
elseif(counter_definition['CounterSize'] == 8) then elseif(counter_definition['CounterSize'] == 8) then
pos, result = msrpctypes.unmarshall_int64(data, pos) pos, result = msrpctypes.unmarshall_int64(data, pos)
-- pos, result = bin.unpack("<d", data, pos) -- pos, result = bin.unpack("<d", data, pos)
else else
pos, result = msrpctypes.unmarshall_raw(data, pos, counter_definition['CounterSize']) pos, result = msrpctypes.unmarshall_raw(data, pos, counter_definition['CounterSize'])
end end
return true, pos, result return true, pos, result
end end
---Parse a PERF_INSTANCE_DEFINITION structure. From Microsoft's documentation: ---Parse a PERF_INSTANCE_DEFINITION structure. From Microsoft's documentation:
-- --
--<code> --<code>
-- // If (PERF_DATA_BLOCK.NumInstances >= 0) then there will be -- // If (PERF_DATA_BLOCK.NumInstances >= 0) then there will be
-- // PERF_DATA_BLOCK.NumInstances of a (PERF_INSTANCE_DEFINITION -- // PERF_DATA_BLOCK.NumInstances of a (PERF_INSTANCE_DEFINITION
-- // followed by a PERF_COUNTER_BLOCK followed by the counter data fields) -- // followed by a PERF_COUNTER_BLOCK followed by the counter data fields)
-- // for each instance. -- // for each instance.
-- // -- //
-- // If (PERF_DATA_BLOCK.NumInstances < 0) then the counter definition -- // If (PERF_DATA_BLOCK.NumInstances < 0) then the counter definition
-- // strucutre above will be followed by only a PERF_COUNTER_BLOCK and the -- // strucutre above will be followed by only a PERF_COUNTER_BLOCK and the
-- // counter data for that COUNTER. -- // counter data for that COUNTER.
-- typedef struct _PERF_INSTANCE_DEFINITION { -- typedef struct _PERF_INSTANCE_DEFINITION {
-- DWORD ByteLength; // Length in bytes of this structure, -- DWORD ByteLength; // Length in bytes of this structure,
-- // including the subsequent name -- // including the subsequent name
-- DWORD ParentObjectTitleIndex; -- DWORD ParentObjectTitleIndex;
-- // Title Index to name of "parent" -- // Title Index to name of "parent"
-- // object (e.g., if thread, then -- // object (e.g., if thread, then
-- // process is parent object type); -- // process is parent object type);
-- // if logical drive, the physical -- // if logical drive, the physical
-- // drive is parent object type -- // drive is parent object type
-- DWORD ParentObjectInstance; -- DWORD ParentObjectInstance;
-- // Index to instance of parent object -- // Index to instance of parent object
-- // type which is the parent of this -- // type which is the parent of this
-- // instance. -- // instance.
-- LONG UniqueID; // A unique ID used instead of -- LONG UniqueID; // A unique ID used instead of
-- // matching the name to identify -- // matching the name to identify
-- // this instance, -1 = none -- // this instance, -1 = none
-- DWORD NameOffset; // Offset from beginning of -- DWORD NameOffset; // Offset from beginning of
-- // this struct to the Unicode name -- // this struct to the Unicode name
-- // of this instance -- // of this instance
-- DWORD NameLength; // Length in bytes of name; 0 = none -- DWORD NameLength; // Length in bytes of name; 0 = none
-- // this length includes the characters -- // this length includes the characters
-- // in the string plus the size of the -- // in the string plus the size of the
-- // terminating NULL char. It does not -- // terminating NULL char. It does not
-- // include any additional pad bytes to -- // include any additional pad bytes to
-- // correct structure alignment -- // correct structure alignment
-- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION; -- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION;
--</code> --</code>
-- --
--@param data The data being processed. --@param data The data being processed.
@@ -379,33 +379,33 @@ end
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_instance_definition(data, pos) 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 -- 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 -- determine it by adding ByteLength to the initial position
local initial_pos = pos local initial_pos = pos
pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos) pos, result['ByteLength'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['ParentObjectTitleIndex'] = msrpctypes.unmarshall_int32(data, pos) pos, result['ParentObjectTitleIndex'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['ParentObjectInstance'] = msrpctypes.unmarshall_int32(data, pos) pos, result['ParentObjectInstance'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['UniqueID'] = msrpctypes.unmarshall_int32(data, pos) pos, result['UniqueID'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NameOffset'] = msrpctypes.unmarshall_int32(data, pos) pos, result['NameOffset'] = msrpctypes.unmarshall_int32(data, pos)
pos, result['NameLength'] = 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 end
---Parse a PERF_COUNTER_BLOCK structure. From Microsoft's documentation: ---Parse a PERF_COUNTER_BLOCK structure. From Microsoft's documentation:
-- --
--<code> --<code>
-- typedef struct _PERF_COUNTER_BLOCK { -- typedef struct _PERF_COUNTER_BLOCK {
-- DWORD ByteLength; // Length in bytes of this structure, -- DWORD ByteLength; // Length in bytes of this structure,
-- // including the following counters -- // including the following counters
-- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK; -- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK;
-- --
--</code> --</code>
-- --
@@ -414,11 +414,11 @@ end
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error --@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
-- message), and a table representing the datatype, if any. -- message), and a table representing the datatype, if any.
local function parse_perf_counter_block(data, pos) 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 end
---Retrieve the parsed performance data from the given host for the requested object values. To get a list of possible ---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. --@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database.
function get_performance_data(host, objects) function get_performance_data(host, objects)
-- Create the SMB session -- Create the SMB session
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH) local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
if(status == false) then if(status == false) then
return false, smbstate return false, smbstate
end end
-- Bind to WINREG service -- Bind to WINREG service
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil) local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, bind_result return false, bind_result
end end
-- Open HKEY_PERFORMANCE_DATA -- Open HKEY_PERFORMANCE_DATA
local status, openhkpd_result = msrpc.winreg_openhkpd(smbstate) local status, openhkpd_result = msrpc.winreg_openhkpd(smbstate)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, openhkpd_result return false, openhkpd_result
end end
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], "Counter 009") local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], "Counter 009")
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, queryvalue_result return false, queryvalue_result
end end
-- Parse the title database -- Parse the title database
local pos = 1 local pos = 1
local status local status
local result = {} local result = {}
status, pos, result['title_database'] = parse_perf_title_database(queryvalue_result['value'], pos) status, pos, result['title_database'] = parse_perf_title_database(queryvalue_result['value'], pos)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
result['title_database'][0] = "<null>" result['title_database'][0] = "<null>"
if(objects ~= nil and #objects > 0) then if(objects ~= nil and #objects > 0) then
-- Query for the objects -- Query for the objects
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], objects) local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openhkpd_result['handle'], objects)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, queryvalue_result return false, queryvalue_result
end end
-- Parse the header -- Parse the header
pos = 1 pos = 1
local status, data_block local status, data_block
status, pos, data_block = parse_perf_data_block(queryvalue_result['value'], pos) status, pos, data_block = parse_perf_data_block(queryvalue_result['value'], pos)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
-- Move past the header -- Move past the header
pos = 1 + data_block['HeaderLength'] pos = 1 + data_block['HeaderLength']
-- Parse the data sections -- Parse the data sections
for i = 1, data_block['NumObjectTypes'], 1 do for i = 1, data_block['NumObjectTypes'], 1 do
local object_start = pos local object_start = pos
local counter_definitions = {} local counter_definitions = {}
local object_instances = {} local object_instances = {}
local counter_definitions = {} local counter_definitions = {}
-- Get the type of the object (this is basically the class definition -- info about the object instances) -- Get the type of the object (this is basically the class definition -- info about the object instances)
local status, object_type local status, object_type
status, pos, object_type = parse_perf_object_type(queryvalue_result['value'], pos) status, pos, object_type = parse_perf_object_type(queryvalue_result['value'], pos)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
-- Start setting up the result object -- Start setting up the result object
--stdnse.print_debug("Index = %d\n", object_type['ObjectNameTitleIndex']) --stdnse.print_debug("Index = %d\n", object_type['ObjectNameTitleIndex'])
local object_name = result['title_database'][object_type['ObjectNameTitleIndex']] local object_name = result['title_database'][object_type['ObjectNameTitleIndex']]
result[object_name] = {} result[object_name] = {}
--stdnse.print_debug("\n\nOBJECT: %s\n", object_name) --stdnse.print_debug("\n\nOBJECT: %s\n", object_name)
--stdnse.print_debug(" Counters: %d\n", object_type['NumCounters']) --stdnse.print_debug(" Counters: %d\n", object_type['NumCounters'])
--stdnse.print_debug(" Instances: %d\n", object_type['NumInstances']) --stdnse.print_debug(" Instances: %d\n", object_type['NumInstances'])
--stdnse.print_debug("-----------------\n") --stdnse.print_debug("-----------------\n")
-- Bring the position to the beginning of the counter definitions -- Bring the position to the beginning of the counter definitions
pos = object_start + object_type['HeaderLength'] pos = object_start + object_type['HeaderLength']
-- Parse the counter definitions -- Parse the counter definitions
for j = 1, object_type['NumCounters'], 1 do for j = 1, object_type['NumCounters'], 1 do
status, pos, counter_definitions[j] = parse_perf_counter_definition(queryvalue_result['value'], pos) status, pos, counter_definitions[j] = parse_perf_counter_definition(queryvalue_result['value'], pos)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
--stdnse.print_debug(" Counter definition #%2d: [%d bytes] %s\n", j, counter_definitions[j]['CounterSize'], result['title_database'][counter_definitions[j]['CounterNameTitleIndex']]) --stdnse.print_debug(" Counter definition #%2d: [%d bytes] %s\n", j, counter_definitions[j]['CounterSize'], result['title_database'][counter_definitions[j]['CounterNameTitleIndex']])
end end
-- Bring the position to the beginning of the instances (or counters) -- Bring the position to the beginning of the instances (or counters)
pos = object_start + object_type['DefinitionLength'] 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) -- 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 if(bit.band(object_type['NumInstances'], 0x80000000) == 0) then
-- Parse the object instances and counters -- Parse the object instances and counters
for j = 1, object_type['NumInstances'], 1 do for j = 1, object_type['NumInstances'], 1 do
local instance_start = pos local instance_start = pos
-- Instance definition -- Instance definition
local status local status
status, pos, object_instances[j] = parse_perf_instance_definition(queryvalue_result['value'], pos) status, pos, object_instances[j] = parse_perf_instance_definition(queryvalue_result['value'], pos)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
-- Set up the instance array -- Set up the instance array
local instance_name = object_instances[j]['InstanceName'] local instance_name = object_instances[j]['InstanceName']
result[object_name][instance_name] = {} result[object_name][instance_name] = {}
-- Bring the pos to the start of the counter block -- Bring the pos to the start of the counter block
pos = instance_start + object_instances[j]['ByteLength'] pos = instance_start + object_instances[j]['ByteLength']
--stdnse.print_debug("\n INSTANCE: %s\n", instance_name) --stdnse.print_debug("\n INSTANCE: %s\n", instance_name)
--stdnse.print_debug(" Length: %d\n", object_instances[j]['ByteLength']) --stdnse.print_debug(" Length: %d\n", object_instances[j]['ByteLength'])
--stdnse.print_debug(" NameOffset: %d\n", object_instances[j]['NameOffset']) --stdnse.print_debug(" NameOffset: %d\n", object_instances[j]['NameOffset'])
--stdnse.print_debug(" NameLength: %d\n", object_instances[j]['NameLength']) --stdnse.print_debug(" NameLength: %d\n", object_instances[j]['NameLength'])
--stdnse.print_debug(" --------------\n") --stdnse.print_debug(" --------------\n")
-- The counter block -- The counter block
local status, counter_block local status, counter_block
status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos) status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos)
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
for k = 1, object_type['NumCounters'], 1 do for k = 1, object_type['NumCounters'], 1 do
-- Each individual counter -- Each individual counter
local status, counter_result local status, counter_result
status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k]) status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k])
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']] local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']]
--stdnse.print_debug(" %s: %s\n", counter_name, counter_result) --stdnse.print_debug(" %s: %s\n", counter_name, counter_result)
-- Save it in the result -- Save it in the result
result[object_name][instance_name][counter_name] = counter_result result[object_name][instance_name][counter_name] = counter_result
end end
-- Bring the pos to the end of the next section -- Bring the pos to the end of the next section
pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength'] pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength']
end end
else else
for k = 1, object_type['NumCounters'], 1 do for k = 1, object_type['NumCounters'], 1 do
-- Each individual counter -- Each individual counter
local status, counter_result local status, counter_result
status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k]) status, pos, counter_result = parse_perf_counter(queryvalue_result['value'], pos, counter_definitions[k])
if(status == false) then if(status == false) then
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return false, pos return false, pos
end end
local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']] local counter_name = result['title_database'][counter_definitions[k]['CounterNameTitleIndex']]
--stdnse.print_debug(" %s: %s\n", counter_name, counter_result) --stdnse.print_debug(" %s: %s\n", counter_name, counter_result)
-- Save it in the result -- Save it in the result
result[object_name][counter_name] = counter_result result[object_name][counter_name] = counter_result
end end
end end
end end
-- Blank out the database -- Blank out the database
result['title_database'] = nil result['title_database'] = nil
end end
msrpc.stop_smb(smbstate) msrpc.stop_smb(smbstate)
return true, result return true, result
end end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -29,52 +29,52 @@ local HAVE_SSL, openssl = pcall(require,'openssl')
Capabilities = Capabilities =
{ {
LongPassword = 0x1, LongPassword = 0x1,
FoundRows = 0x2, FoundRows = 0x2,
LongColumnFlag = 0x4, LongColumnFlag = 0x4,
ConnectWithDatabase = 0x8, ConnectWithDatabase = 0x8,
DontAllowDatabaseTableColumn = 0x10, DontAllowDatabaseTableColumn = 0x10,
SupportsCompression = 0x20, SupportsCompression = 0x20,
ODBCClient = 0x40, ODBCClient = 0x40,
SupportsLoadDataLocal = 0x80, SupportsLoadDataLocal = 0x80,
IgnoreSpaceBeforeParenthesis = 0x100, IgnoreSpaceBeforeParenthesis = 0x100,
Speaks41ProtocolNew = 0x200, Speaks41ProtocolNew = 0x200,
InteractiveClient = 0x400, InteractiveClient = 0x400,
SwitchToSSLAfterHandshake = 0x800, SwitchToSSLAfterHandshake = 0x800,
IgnoreSigpipes = 0x1000, IgnoreSigpipes = 0x1000,
SupportsTransactions = 0x2000, SupportsTransactions = 0x2000,
Speaks41ProtocolOld = 0x4000, Speaks41ProtocolOld = 0x4000,
Support41Auth = 0x8000 Support41Auth = 0x8000
} }
ExtCapabilities = ExtCapabilities =
{ {
SupportsMultipleStatments = 0x1, SupportsMultipleStatments = 0x1,
SupportsMultipleResults = 0x2 SupportsMultipleResults = 0x2
} }
Charset = Charset =
{ {
latin1_COLLATE_latin1_swedish_ci = 0x8 latin1_COLLATE_latin1_swedish_ci = 0x8
} }
ServerStatus = ServerStatus =
{ {
InTransaction = 0x1, InTransaction = 0x1,
AutoCommit = 0x2, AutoCommit = 0x2,
MoreResults = 0x4, MoreResults = 0x4,
MultiQuery = 0x8, MultiQuery = 0x8,
BadIndexUsed = 0x10, BadIndexUsed = 0x10,
NoIndexUsed = 0x20, NoIndexUsed = 0x20,
CursorExists = 0x40, CursorExists = 0x40,
LastRowSebd = 0x80, LastRowSebd = 0x80,
DatabaseDropped = 0x100, DatabaseDropped = 0x100,
NoBackslashEscapes = 0x200 NoBackslashEscapes = 0x200
} }
Command = Command =
{ {
Query = 3 Query = 3
} }
local MAXPACKET = 16777216 local MAXPACKET = 16777216
@@ -87,14 +87,14 @@ local HEADER_SIZE = 4
-- @return response table containing the fields <code>len</code> and <code>packetno</code> -- @return response table containing the fields <code>len</code> and <code>packetno</code>
local function decodeHeader( data, pos ) local function decodeHeader( data, pos )
local response = {} local response = {}
local pos, tmp = pos or 1, 0 local pos, tmp = pos or 1, 0
pos, tmp = bin.unpack( "I", data, pos ) pos, tmp = bin.unpack( "I", data, pos )
response.len = bit.band( tmp,255 ) response.len = bit.band( tmp,255 )
response.number = bit.rshift( tmp, 24 ) response.number = bit.rshift( tmp, 24 )
return pos, response return pos, response
end end
--- Recieves the server greeting upon intial connection --- Recieves the server greeting upon intial connection
@@ -106,43 +106,43 @@ end
-- <code>status</code> or error message on failure (status == false) -- <code>status</code> or error message on failure (status == false)
function receiveGreeting( socket ) function receiveGreeting( socket )
local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end
local try = nmap.new_try(catch) local try = nmap.new_try(catch)
local data = try( socket:receive_bytes(HEADER_SIZE) ) local data = try( socket:receive_bytes(HEADER_SIZE) )
local pos, response, tmp, _ local pos, response, tmp, _
pos, response = decodeHeader( data, 1 ) pos, response = decodeHeader( data, 1 )
-- do we need to read the remainder -- do we need to read the remainder
if ( #data - HEADER_SIZE < response.len ) then if ( #data - HEADER_SIZE < response.len ) then
local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) ) local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) )
data = data .. tmp data = data .. tmp
end end
local is_error local is_error
pos, is_error = bin.unpack( "C", data, pos ) pos, is_error = bin.unpack( "C", data, pos )
if ( is_error == 0xff ) then if ( is_error == 0xff ) then
pos, response.errorcode = bin.unpack( "S", data, pos ) pos, response.errorcode = bin.unpack( "S", data, pos )
pos, response.errormsg = bin.unpack("A" .. (#data - pos + 1), data, pos ) pos, response.errormsg = bin.unpack("A" .. (#data - pos + 1), data, pos )
return false, response.errormsg return false, response.errormsg
end end
pos, response.proto = bin.unpack( "C", data, pos ) pos, response.proto = bin.unpack( "C", data, pos )
pos, response.version = bin.unpack( "z", data, pos ) pos, response.version = bin.unpack( "z", data, pos )
pos, response.threadid = bin.unpack( "I", data, pos ) pos, response.threadid = bin.unpack( "I", data, pos )
pos, response.salt, _ = bin.unpack( "A8C", data, pos ) pos, response.salt, _ = bin.unpack( "A8C", data, pos )
pos, response.capabilities = bin.unpack( "S", data, pos ) pos, response.capabilities = bin.unpack( "S", data, pos )
pos, response.charset = bin.unpack( "C", data, pos ) pos, response.charset = bin.unpack( "C", data, pos )
pos, response.status = bin.unpack( "S", data, pos ) pos, response.status = bin.unpack( "S", data, pos )
pos, _ = bin.unpack( "A13", data, pos ) pos, _ = bin.unpack( "A13", data, pos )
pos, tmp = bin.unpack( "A12", data, pos ) pos, tmp = bin.unpack( "A12", data, pos )
response.salt = response.salt .. tmp response.salt = response.salt .. tmp
response.errorcode = 0 response.errorcode = 0
return true, response return true, response
end end
@@ -153,28 +153,28 @@ end
-- @param salt string containing the servers salt as obtained from <code>receiveGreeting</code> -- @param salt string containing the servers salt as obtained from <code>receiveGreeting</code>
-- @return reply string containing the raw hashed value -- @return reply string containing the raw hashed value
local function createLoginHash(pass, salt) local function createLoginHash(pass, salt)
local hash_stage1 local hash_stage1
local hash_stage2 local hash_stage2
local hash_stage3 local hash_stage3
local reply = "" local reply = ""
local pos, b1, b2, b3, _ = 1, 0, 0, 0 local pos, b1, b2, b3, _ = 1, 0, 0, 0
if ( not(HAVE_SSL) ) then if ( not(HAVE_SSL) ) then
return nil return nil
end end
hash_stage1 = openssl.sha1( pass ) hash_stage1 = openssl.sha1( pass )
hash_stage2 = openssl.sha1( hash_stage1 ) hash_stage2 = openssl.sha1( hash_stage1 )
hash_stage3 = openssl.sha1( salt .. hash_stage2 ) hash_stage3 = openssl.sha1( salt .. hash_stage2 )
for pos=1, hash_stage1:len() do for pos=1, hash_stage1:len() do
_, b1 = bin.unpack( "C", hash_stage1, pos ) _, b1 = bin.unpack( "C", hash_stage1, pos )
_, b2 = bin.unpack( "C", hash_stage3, pos ) _, b2 = bin.unpack( "C", hash_stage3, pos )
reply = reply .. string.char( bit.bxor( b2, b1 ) ) reply = reply .. string.char( bit.bxor( b2, b1 ) )
end end
return reply return reply
end end
@@ -183,9 +183,9 @@ end
-- --
-- @param socket already connected to the remote server -- @param socket already connected to the remote server
-- @param params table with additional options to the loginrequest -- @param params table with additional options to the loginrequest
-- current supported fields are <code>charset</code> and <code>authversion</code> -- current supported fields are <code>charset</code> and <code>authversion</code>
-- authversion is either "pre41" or "post41" (default is post41) -- authversion is either "pre41" or "post41" (default is post41)
-- currently only post41 authentication is supported -- currently only post41 authentication is supported
-- @param username string containing the username of the user that is authenticating -- @param username string containing the username of the user that is authenticating
-- @param password string containing the users password or nil if empty -- @param password string containing the users password or nil if empty
-- @param salt string containing the servers salt as recieved from <code>receiveGreeting</code> -- @param salt string containing the servers salt as recieved from <code>receiveGreeting</code>
@@ -193,84 +193,84 @@ end
-- @return response table or error message on failure -- @return response table or error message on failure
function loginRequest( socket, params, username, password, salt ) function loginRequest( socket, params, username, password, salt )
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
local try = nmap.new_try(catch) local try = nmap.new_try(catch)
local packetno = 1 local packetno = 1
local authversion = params.authversion or "post41" local authversion = params.authversion or "post41"
local username = username or "" local username = username or ""
if not(HAVE_SSL) then if not(HAVE_SSL) then
return false, "No OpenSSL" return false, "No OpenSSL"
end end
if authversion ~= "post41" then if authversion ~= "post41" then
return false, "Unsupported authentication version: " .. authversion return false, "Unsupported authentication version: " .. authversion
end end
local clicap = Capabilities.LongPassword local clicap = Capabilities.LongPassword
clicap = clicap + Capabilities.LongColumnFlag clicap = clicap + Capabilities.LongColumnFlag
clicap = clicap + Capabilities.SupportsLoadDataLocal clicap = clicap + Capabilities.SupportsLoadDataLocal
clicap = clicap + Capabilities.Speaks41ProtocolNew clicap = clicap + Capabilities.Speaks41ProtocolNew
clicap = clicap + Capabilities.InteractiveClient clicap = clicap + Capabilities.InteractiveClient
clicap = clicap + Capabilities.SupportsTransactions clicap = clicap + Capabilities.SupportsTransactions
clicap = clicap + Capabilities.Support41Auth clicap = clicap + Capabilities.Support41Auth
local extcapabilities = ExtCapabilities.SupportsMultipleStatments local extcapabilities = ExtCapabilities.SupportsMultipleStatments
extcapabilities = extcapabilities + ExtCapabilities.SupportsMultipleResults extcapabilities = extcapabilities + ExtCapabilities.SupportsMultipleResults
local packet = bin.pack( "S", clicap ) local packet = bin.pack( "S", clicap )
packet = packet .. bin.pack( "S", extcapabilities ) packet = packet .. bin.pack( "S", extcapabilities )
packet = packet .. bin.pack( "I", MAXPACKET ) packet = packet .. bin.pack( "I", MAXPACKET )
packet = packet .. bin.pack( "C", Charset.latin1_COLLATE_latin1_swedish_ci ) 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( "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 ) packet = packet .. bin.pack( "z", username )
if ( password ~= nil and password:len() > 0 ) then if ( password ~= nil and password:len() > 0 ) then
local hash = createLoginHash( password, salt ) local hash = createLoginHash( password, salt )
packet = packet .. bin.pack( "A", string.char( 0x14 ) .. hash ) packet = packet .. bin.pack( "A", string.char( 0x14 ) .. hash )
else else
packet = packet .. bin.pack( "C", 0 ) packet = packet .. bin.pack( "C", 0 )
end 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) ) try( socket:send(packet) )
packet = try( socket:receive_bytes(HEADER_SIZE) ) packet = try( socket:receive_bytes(HEADER_SIZE) )
local pos, response = decodeHeader( packet ) local pos, response = decodeHeader( packet )
-- do we need to read the remainder -- do we need to read the remainder
if ( #packet - HEADER_SIZE < response.len ) then if ( #packet - HEADER_SIZE < response.len ) then
local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) ) local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) )
packet = packet .. tmp packet = packet .. tmp
end 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 if is_error > 0 then
pos, response.errorcode = bin.unpack( "S", packet, pos ) pos, response.errorcode = bin.unpack( "S", packet, pos )
local has_sqlstate local has_sqlstate
pos, has_sqlstate = bin.unpack( "C", packet, pos ) pos, has_sqlstate = bin.unpack( "C", packet, pos )
if has_sqlstate == 35 then if has_sqlstate == 35 then
pos, response.sqlstate = bin.unpack( "A5", packet, pos ) pos, response.sqlstate = bin.unpack( "A5", packet, pos )
end end
pos, response.errormessage = bin.unpack( "z", packet, pos ) pos, response.errormessage = bin.unpack( "z", packet, pos )
return false, response.errormessage return false, response.errormessage
else else
response.errorcode = 0 response.errorcode = 0
pos, response.affectedrows = bin.unpack( "C", packet, pos ) pos, response.affectedrows = bin.unpack( "C", packet, pos )
pos, response.serverstatus = bin.unpack( "S", packet, pos ) pos, response.serverstatus = bin.unpack( "S", packet, pos )
pos, response.warnings = bin.unpack( "S", packet, pos ) pos, response.warnings = bin.unpack( "S", packet, pos )
end end
return true, response return true, response
end end
@@ -283,42 +283,42 @@ end
-- the position should point to the data in this buffer (ie. after the header) -- 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 pos number containing the position after the field was decoded
-- @return field table containing <code>catalog</code>, <code>database</code>, <code>table</code>, -- @return field table containing <code>catalog</code>, <code>database</code>, <code>table</code>,
-- <code>origt_table</code>, <code>name</code>, <code>orig_name</code>, -- <code>origt_table</code>, <code>name</code>, <code>orig_name</code>,
-- <code>length</code> and <code>type</code> -- <code>length</code> and <code>type</code>
function decodeField( data, pos ) function decodeField( data, pos )
local header, len local header, len
local def, _ local def, _
local field = {} local field = {}
pos, len = bin.unpack( "C", data, pos ) pos, len = bin.unpack( "C", data, pos )
pos, field.catalog = bin.unpack( "A" .. len, data, pos ) pos, field.catalog = bin.unpack( "A" .. len, data, pos )
pos, len = bin.unpack( "C", data, pos ) pos, len = bin.unpack( "C", data, pos )
pos, field.database = bin.unpack( "A" .. len, data, pos ) pos, field.database = bin.unpack( "A" .. len, data, pos )
pos, len = bin.unpack( "C", data, pos ) pos, len = bin.unpack( "C", data, pos )
pos, field.table = bin.unpack( "A" .. len, data, pos ) pos, field.table = bin.unpack( "A" .. len, data, pos )
pos, len = bin.unpack( "C", data, pos ) pos, len = bin.unpack( "C", data, pos )
pos, field.orig_table = bin.unpack( "A" .. len, data, pos ) pos, field.orig_table = bin.unpack( "A" .. len, data, pos )
pos, len = bin.unpack( "C", data, pos ) pos, len = bin.unpack( "C", data, pos )
pos, field.name = bin.unpack( "A" .. len, data, pos ) pos, field.name = bin.unpack( "A" .. len, data, pos )
pos, len = bin.unpack( "C", data, pos ) pos, len = bin.unpack( "C", data, pos )
pos, field.orig_name = bin.unpack( "A" .. len, data, pos ) pos, field.orig_name = bin.unpack( "A" .. len, data, pos )
-- should be 0x0C -- should be 0x0C
pos, _ = bin.unpack( "C", data, pos ) pos, _ = bin.unpack( "C", data, pos )
-- charset, in my case 0x0800 -- charset, in my case 0x0800
pos, _ = bin.unpack( "S", data, pos ) pos, _ = bin.unpack( "S", data, pos )
pos, field.length = bin.unpack( "I", data, pos ) pos, field.length = bin.unpack( "I", data, pos )
pos, field.type = bin.unpack( "A6", data, pos ) pos, field.type = bin.unpack( "A6", data, pos )
return pos, field return pos, field
end end
@@ -330,81 +330,81 @@ end
-- @return table containing the following <code>header</code>, <code>fields</code> and <code>data</code> -- @return table containing the following <code>header</code>, <code>fields</code> and <code>data</code>
function decodeQueryResponse( socket ) function decodeQueryResponse( socket )
local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end
local try = nmap.new_try(catch) local try = nmap.new_try(catch)
local data, header, pos local data, header, pos
local rs, blocks = {}, {} local rs, blocks = {}, {}
local block_start, block_end local block_start, block_end
local EOF_MARKER = 254 local EOF_MARKER = 254
data = try( socket:receive_bytes(HEADER_SIZE) ) data = try( socket:receive_bytes(HEADER_SIZE) )
pos, header = decodeHeader( data, pos ) pos, header = decodeHeader( data, pos )
-- --
-- First, Let's attempt to read the "Result Set Header Packet" -- First, Let's attempt to read the "Result Set Header Packet"
-- --
if data:len() < header.len then if data:len() < header.len then
data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) ) data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) )
end end
rs.header = data:sub( 1, HEADER_SIZE + header.len ) rs.header = data:sub( 1, HEADER_SIZE + header.len )
-- abort on MySQL error -- abort on MySQL error
if rs.header:sub(HEADER_SIZE + 1, HEADER_SIZE + 1) == string.char(0xFF) then if rs.header:sub(HEADER_SIZE + 1, HEADER_SIZE + 1) == string.char(0xFF) then
-- is this a 4.0 or 4.1 error message -- is this a 4.0 or 4.1 error message
if rs.header:find("#") then if rs.header:find("#") then
return false, rs.header:sub(HEADER_SIZE+10) return false, rs.header:sub(HEADER_SIZE+10)
else else
return false, rs.header:sub(HEADER_SIZE+4) return false, rs.header:sub(HEADER_SIZE+4)
end end
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" -- Second, Let's attempt to read the "Field Packets" and "Row Data Packets"
-- They're separated by an "EOF Packet" -- They're separated by an "EOF Packet"
for i=1,2 do for i=1,2 do
-- marks the start of our block -- marks the start of our block
block_start = pos block_start = pos
while true do while true do
if data:len() - pos < HEADER_SIZE then if data:len() - pos < HEADER_SIZE then
data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) ) data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) )
end end
pos, header = decodeHeader( data, pos ) pos, header = decodeHeader( data, pos )
if data:len() - pos < header.len - 1 then if data:len() - pos < header.len - 1 then
data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) ) data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) )
end end
if header.len > 0 then if header.len > 0 then
local _, b = bin.unpack("C", data, pos ) local _, b = bin.unpack("C", data, pos )
-- Is this the EOF packet? -- Is this the EOF packet?
if b == EOF_MARKER then if b == EOF_MARKER then
-- we don't want the EOF Packet included -- we don't want the EOF Packet included
block_end = pos - HEADER_SIZE block_end = pos - HEADER_SIZE
pos = pos + header.len pos = pos + header.len
break break
end end
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.fields = blocks[1]
rs.data = blocks[2] rs.data = blocks[2]
return true, rs return true, rs
end end
@@ -419,20 +419,20 @@ end
-- or string containing error message if status is false -- or string containing error message if status is false
function decodeFieldPackets( data, count ) function decodeFieldPackets( data, count )
local pos, header local pos, header
local field, fields = {}, {} local field, fields = {}, {}
if count < 1 then if count < 1 then
return false, "Field count was less than one, aborting" return false, "Field count was less than one, aborting"
end end
for i=1, count do for i=1, count do
pos, header = decodeHeader( data, pos ) pos, header = decodeHeader( data, pos )
pos, field = decodeField( data, pos ) pos, field = decodeField( data, pos )
table.insert( fields, field ) table.insert( fields, field )
end end
return true, fields return true, fields
end end
-- Decodes the result set header -- Decodes the result set header
@@ -443,15 +443,15 @@ end
-- @return number containing the amount of fields -- @return number containing the amount of fields
function decodeResultSetHeader( data ) function decodeResultSetHeader( data )
local _, fields local _, fields
if data:len() ~= HEADER_SIZE + 1 then if data:len() ~= HEADER_SIZE + 1 then
return false, "Result set header was incorrect" return false, "Result set header was incorrect"
end end
_, fields = bin.unpack( "C", data, HEADER_SIZE + 1 ) _, fields = bin.unpack( "C", data, HEADER_SIZE + 1 )
return true, fields return true, fields
end end
--- Decodes the row data --- Decodes the row data
@@ -464,23 +464,23 @@ end
-- @return rows table containing row tables -- @return rows table containing row tables
function decodeDataPackets( data, count ) function decodeDataPackets( data, count )
local len, pos = 0, 1, 1 local len, pos = 0, 1, 1
local header, row, rows = {}, {}, {} local header, row, rows = {}, {}, {}
while pos < data:len() do while pos < data:len() do
row = {} row = {}
pos, header = decodeHeader( data, pos ) pos, header = decodeHeader( data, pos )
for i=1, count do for i=1, count do
pos, len = bin.unpack("C", data, pos ) pos, len = bin.unpack("C", data, pos )
pos, row[i] = bin.unpack("A" .. len, data, pos) pos, row[i] = bin.unpack("A" .. len, data, pos)
end end
table.insert( rows, row ) table.insert( rows, row )
end end
return true, rows return true, rows
end end
@@ -492,54 +492,54 @@ end
-- @return rows table containing row tabels as decoded by <code>decodeDataPackets</code> -- @return rows table containing row tabels as decoded by <code>decodeDataPackets</code>
function sqlQuery( socket, query ) function sqlQuery( socket, query )
local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end
local try = nmap.new_try(catch) local try = nmap.new_try(catch)
local packetno = 0 local packetno = 0
local querylen = query:len() + 1 local querylen = query:len() + 1
local packet, packet_len, pos, header local packet, packet_len, pos, header
local status, fields, field_count, rows, rs 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 -- http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Result_Set_Header_Packet
-- --
-- (Result Set Header Packet) the number of columns -- (Result Set Header Packet) the number of columns
-- (Field Packets) column descriptors -- (Field Packets) column descriptors
-- (EOF Packet) marker: end of Field Packets -- (EOF Packet) marker: end of Field Packets
-- (Row Data Packets) row contents -- (Row Data Packets) row contents
-- (EOF Packet) marker: end of Data Packets -- (EOF Packet) marker: end of Data Packets
try( socket:send(packet) ) try( socket:send(packet) )
-- --
-- Let's read all the data into a table -- Let's read all the data into a table
-- This way we avoid the hustle with reading from the socket -- This way we avoid the hustle with reading from the socket
status, rs = decodeQueryResponse( socket ) status, rs = decodeQueryResponse( socket )
if not status then if not status then
return false, rs return false, rs
end end
status, field_count = decodeResultSetHeader(rs.header) status, field_count = decodeResultSetHeader(rs.header)
if not status then if not status then
return false, field_count return false, field_count
end end
status, fields = decodeFieldPackets(rs.fields, field_count) status, fields = decodeFieldPackets(rs.fields, field_count)
if not status then if not status then
return false, fields return false, fields
end end
status, rows = decodeDataPackets(rs.data, field_count) status, rows = decodeDataPackets(rs.data, field_count)
if not status then if not status then
return false, rows return false, rows
end end
return true, { cols = fields, rows = rows } return true, { cols = fields, rows = rows }
end end
--- ---
@@ -550,24 +550,24 @@ end
-- - <code>noheaders</code> - does not include column names in result -- - <code>noheaders</code> - does not include column names in result
-- @return string containing the formated resultset table -- @return string containing the formated resultset table
function formatResultset(rs, options) function formatResultset(rs, options)
options = options or {} options = options or {}
if ( not(rs) or not(rs.cols) or not(rs.rows) ) then if ( not(rs) or not(rs.cols) or not(rs.rows) ) then
return return
end end
local restab = tab.new(#rs.cols) local restab = tab.new(#rs.cols)
local colnames = {} local colnames = {}
if ( not(options.noheaders) ) then if ( not(options.noheaders) ) then
for _, col in ipairs(rs.cols) do table.insert(colnames, col.name) end for _, col in ipairs(rs.cols) do table.insert(colnames, col.name) end
tab.addrow(restab, table.unpack(colnames)) tab.addrow(restab, table.unpack(colnames))
end end
for _, row in ipairs(rs.rows) do for _, row in ipairs(rs.rows) do
tab.addrow(restab, table.unpack(row)) tab.addrow(restab, table.unpack(row))
end end
return tab.dump(restab) return tab.dump(restab)
end end
return _ENV; return _ENV;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -254,30 +254,30 @@ Comm = {
-- --
-- @return status boolean true -- @return status boolean true
SetVersion = function(self, version) SetVersion = function(self, version)
if self.checkprogver then if self.checkprogver then
if (RPC_version[self.program] and RPC_args[self.program] and if (RPC_version[self.program] and RPC_args[self.program] and
nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then
self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver]) self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver])
elseif (not(self.version) and version) then elseif (not(self.version) and version) then
self.version = version self.version = version
end end
else else
self.version = version self.version = version
end end
return true, nil return true, nil
end, end,
--- Sets the verification of the specified program and version support --- Sets the verification of the specified program and version support
-- before trying to connecting. -- before trying to connecting.
-- @param check boolean to enable or disable checking of program and version support. -- @param check boolean to enable or disable checking of program and version support.
SetCheckProgVer = function(self, check) SetCheckProgVer = function(self, check)
self.checkprogver = check self.checkprogver = check
end, end,
--- Sets the RPC program ID to use. --- Sets the RPC program ID to use.
-- @param progid number Program ID to set. -- @param progid number Program ID to set.
SetProgID = function(self, progid) SetProgID = function(self, progid)
self.program_id = progid self.program_id = progid
end, end,
--- Checks if data contains enough bytes to read the <code>needed</code> amount --- Checks if data contains enough bytes to read the <code>needed</code> amount

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -596,80 +596,80 @@ end
-- error message on failures. -- error message on failures.
login = function(socket, username, password, mech) login = function(socket, username, password, mech)
assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5" assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5"
or mech == "DIGEST-MD5" or mech == "NTLM", or mech == "DIGEST-MD5" or mech == "NTLM",
("Unsupported authentication mechanism (%s)"):format(mech or "nil")) ("Unsupported authentication mechanism (%s)"):format(mech or "nil"))
local status, response = query(socket, "AUTH", mech) local status, response = query(socket, "AUTH", mech)
if ( not(status) ) then if ( not(status) ) then
return false, "ERROR: Failed to send AUTH to server" return false, "ERROR: Failed to send AUTH to server"
end end
if ( mech == "LOGIN" ) then if ( mech == "LOGIN" ) then
local tmp = response:match("334 (.*)") local tmp = response:match("334 (.*)")
if ( not(tmp) ) then if ( not(tmp) ) then
return false, "ERROR: Failed to decode LOGIN response" return false, "ERROR: Failed to decode LOGIN response"
end end
tmp = base64.dec(tmp):lower() tmp = base64.dec(tmp):lower()
if ( not(tmp:match("^username")) ) then if ( not(tmp:match("^username")) ) then
return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp) return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp)
end end
status, response = query(socket, base64.enc(username)) status, response = query(socket, base64.enc(username))
if ( not(status) ) then if ( not(status) ) then
return false, "ERROR: Failed to read LOGIN response" return false, "ERROR: Failed to read LOGIN response"
end end
tmp = response:match("334 (.*)") tmp = response:match("334 (.*)")
if ( not(tmp) ) then if ( not(tmp) ) then
return false, "ERROR: Failed to decode LOGIN response" return false, "ERROR: Failed to decode LOGIN response"
end end
tmp = base64.dec(tmp):lower() tmp = base64.dec(tmp):lower()
if ( not(tmp:match("^password")) ) then if ( not(tmp:match("^password")) ) then
return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp) return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp)
end end
status, response = query(socket, base64.enc(password)) status, response = query(socket, base64.enc(password))
if ( not(status) ) then if ( not(status) ) then
return false, "ERROR: Failed to read LOGIN response" return false, "ERROR: Failed to read LOGIN response"
end end
if ( response:match("^235") ) then if ( response:match("^235") ) then
return true, "Login success" return true, "Login success"
end end
return false, response return false, response
end end
if ( mech == "NTLM" ) then if ( mech == "NTLM" ) then
-- sniffed of the wire, seems to always be the same -- sniffed of the wire, seems to always be the same
-- decodes to some NTLMSSP blob greatness -- decodes to some NTLMSSP blob greatness
status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==") status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==")
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
end end
local chall = response:match("^334 (.*)") local chall = response:match("^334 (.*)")
chall = (chall and base64.dec(chall)) chall = (chall and base64.dec(chall))
if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end
-- All mechanisms expect username and pass -- All mechanisms expect username and pass
-- add the otheronce for those who need them -- add the otheronce for those who need them
local mech_params = { username, password, chall, "smtp" } local mech_params = { username, password, chall, "smtp" }
local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params)) local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params))
auth_data = base64.enc(auth_data) auth_data = base64.enc(auth_data)
status, response = query(socket, auth_data) status, response = query(socket, auth_data)
if ( not(status) ) then if ( not(status) ) then
return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech) return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech)
end end
if ( mech == "DIGEST-MD5" ) then if ( mech == "DIGEST-MD5" ) then
local rspauth = response:match("^334 (.*)") local rspauth = response:match("^334 (.*)")
if ( rspauth ) then if ( rspauth ) then
rspauth = base64.dec(rspauth) rspauth = base64.dec(rspauth)
status, response = query(socket,"") status, response = query(socket,"")
end end
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 end
return _ENV; return _ENV;

View File

@@ -647,9 +647,9 @@ local function format_output_sub(status, data, indent)
local lines = splitlines(value) local lines = splitlines(value)
for j, line in ipairs(lines) do for j, line in ipairs(lines) do
insert(output, format("%s %s%s\n", insert(output, format("%s %s%s\n",
format_get_indent(indent, i == #data and j == #lines), format_get_indent(indent, i == #data and j == #lines),
prefix, line)) prefix, line))
end end
end 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") --@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 --@returns boolean indicating whether the port is in the port range
function in_port_range(port,port_range) function in_port_range(port,port_range)
assert(port and type(port.number)=="number" and type(port.protocol)=="string" and 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=<port_number>, protocol=<port_protocol> }") (port.protocol=="udp" or port.protocol=="tcp"),"Port structure missing or invalid: port={ number=<port_number>, protocol=<port_protocol> }")
assert((type(port_range)=="string" or type(port_range)=="number") and port_range~="","Incorrect port range specification.") assert((type(port_range)=="string" or type(port_range)=="number") and port_range~="","Incorrect port range specification.")
-- Proto - true for TCP, false for UDP -- Proto - true for TCP, false for UDP
local proto local proto
if(port.protocol=="tcp") then proto = true else proto = false end 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 --TCP flag for iteration - true for TCP, false for UDP, if not specified we presume TCP
local tcp_flag = true local tcp_flag = true
-- in case the port_range is a single number -- in case the port_range is a single number
if type(port_range)=="number" then if type(port_range)=="number" then
if proto and port_range==port.number then return true if proto and port_range==port.number then return true
else return false else return false
end end
end end
--clean the string a bit --clean the string a bit
port_range=port_range:gsub("%s+","") port_range=port_range:gsub("%s+","")
-- single_pr - single port range -- single_pr - single port range
for i, single_pr in ipairs(strsplit(",",port_range)) do for i, single_pr in ipairs(strsplit(",",port_range)) do
if single_pr:match("T:") then if single_pr:match("T:") then
tcp_flag = true tcp_flag = true
single_pr = single_pr:gsub("T:","") single_pr = single_pr:gsub("T:","")
else else
if single_pr:match("U:") then if single_pr:match("U:") then
tcp_flag = false tcp_flag = false
single_pr = single_pr:gsub("U:","") single_pr = single_pr:gsub("U:","")
end end
end end
-- compare ports only when the port's protocol is the same as -- compare ports only when the port's protocol is the same as
-- the current single port range -- the current single port range
if tcp_flag == proto then if tcp_flag == proto then
local pone = single_pr:match("^(%d+)$") local pone = single_pr:match("^(%d+)$")
if pone then if pone then
pone = tonumber(pone) pone = tonumber(pone)
assert(pone>-1 and pone<65536, "Port range number out of range (0-65535).") assert(pone>-1 and pone<65536, "Port range number out of range (0-65535).")
if pone == port.number then if pone == port.number then
return true return true
end end
else else
local pstart, pend = single_pr:match("^(%d+)%-(%d+)$") local pstart, pend = single_pr:match("^(%d+)%-(%d+)$")
pstart, pend = tonumber(pstart), tonumber(pend) pstart, pend = tonumber(pstart), tonumber(pend)
assert(pstart,"Incorrect port range specification.") 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<=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).") 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 if port.number >=pstart and port.number <= pend then
return true return true
end end
end end
end end
end end
-- if no match is found then the port doesn't belong to the port_range -- if no match is found then the port doesn't belong to the port_range
return false return false
end end
--- Module function that mimics some behavior of Lua 5.1 module function. --- 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 Boolean true if the item was found, false if not
--@return The index or key where the value was found, or nil --@return The index or key where the value was found, or nil
function contains(tab, item) function contains(tab, item)
for k, val in pairs(tab) do for k, val in pairs(tab) do
if val == item then if val == item then
return true, k return true, k
end end
end end
return false, nil return false, nil
end end

File diff suppressed because it is too large Load Diff

View File

@@ -204,7 +204,7 @@ Response = {
parse = function(self) parse = function(self)
local pos local pos
pos, self.action, self.trans_id, self.conn_id, 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) self.instance_id = bin.unpack(">IIH8CCII", self.data)
end, end,