1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-14 19:59:02 +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
if params.DeviceId then
xmldata = xmldata .. "<DeviceId>" .. params.DeviceId .. "</DeviceId>"
xmldata = xmldata .. "<DeviceId>" .. params.DeviceId .. "</DeviceId>"
end
for _, srvtype in pairs(params.ServerType) do

View File

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

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

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

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

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

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
SetVersion = function(self, version)
if self.checkprogver then
if (RPC_version[self.program] and RPC_args[self.program] and
nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then
self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver])
elseif (not(self.version) and version) then
self.version = version
end
else
self.version = version
end
return true, nil
if self.checkprogver then
if (RPC_version[self.program] and RPC_args[self.program] and
nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then
self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver])
elseif (not(self.version) and version) then
self.version = version
end
else
self.version = version
end
return true, nil
end,
--- Sets the verification of the specified program and version support
-- before trying to connecting.
-- @param check boolean to enable or disable checking of program and version support.
SetCheckProgVer = function(self, check)
self.checkprogver = check
self.checkprogver = check
end,
--- Sets the RPC program ID to use.
-- @param progid number Program ID to set.
SetProgID = function(self, progid)
self.program_id = progid
self.program_id = progid
end,
--- Checks if data contains enough bytes to read the <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.
login = function(socket, username, password, mech)
assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5"
or mech == "DIGEST-MD5" or mech == "NTLM",
("Unsupported authentication mechanism (%s)"):format(mech or "nil"))
local status, response = query(socket, "AUTH", mech)
if ( not(status) ) then
return false, "ERROR: Failed to send AUTH to server"
end
assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5"
or mech == "DIGEST-MD5" or mech == "NTLM",
("Unsupported authentication mechanism (%s)"):format(mech or "nil"))
local status, response = query(socket, "AUTH", mech)
if ( not(status) ) then
return false, "ERROR: Failed to send AUTH to server"
end
if ( mech == "LOGIN" ) then
local tmp = response:match("334 (.*)")
if ( not(tmp) ) then
return false, "ERROR: Failed to decode LOGIN response"
end
tmp = base64.dec(tmp):lower()
if ( not(tmp:match("^username")) ) then
return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp)
end
status, response = query(socket, base64.enc(username))
if ( not(status) ) then
return false, "ERROR: Failed to read LOGIN response"
end
tmp = response:match("334 (.*)")
if ( not(tmp) ) then
return false, "ERROR: Failed to decode LOGIN response"
end
tmp = base64.dec(tmp):lower()
if ( not(tmp:match("^password")) ) then
return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp)
end
status, response = query(socket, base64.enc(password))
if ( not(status) ) then
return false, "ERROR: Failed to read LOGIN response"
end
if ( response:match("^235") ) then
return true, "Login success"
end
return false, response
end
if ( mech == "LOGIN" ) then
local tmp = response:match("334 (.*)")
if ( not(tmp) ) then
return false, "ERROR: Failed to decode LOGIN response"
end
tmp = base64.dec(tmp):lower()
if ( not(tmp:match("^username")) ) then
return false, ("ERROR: Expected \"Username\", but received (%s)"):format(tmp)
end
status, response = query(socket, base64.enc(username))
if ( not(status) ) then
return false, "ERROR: Failed to read LOGIN response"
end
tmp = response:match("334 (.*)")
if ( not(tmp) ) then
return false, "ERROR: Failed to decode LOGIN response"
end
tmp = base64.dec(tmp):lower()
if ( not(tmp:match("^password")) ) then
return false, ("ERROR: Expected \"password\", but received (%s)"):format(tmp)
end
status, response = query(socket, base64.enc(password))
if ( not(status) ) then
return false, "ERROR: Failed to read LOGIN response"
end
if ( response:match("^235") ) then
return true, "Login success"
end
return false, response
end
if ( mech == "NTLM" ) then
-- sniffed of the wire, seems to always be the same
-- decodes to some NTLMSSP blob greatness
status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==")
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
end
if ( mech == "NTLM" ) then
-- sniffed of the wire, seems to always be the same
-- decodes to some NTLMSSP blob greatness
status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==")
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
end
local chall = response:match("^334 (.*)")
chall = (chall and base64.dec(chall))
if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end
local chall = response:match("^334 (.*)")
chall = (chall and base64.dec(chall))
if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end
-- All mechanisms expect username and pass
-- add the otheronce for those who need them
local mech_params = { username, password, chall, "smtp" }
local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params))
auth_data = base64.enc(auth_data)
-- All mechanisms expect username and pass
-- add the otheronce for those who need them
local mech_params = { username, password, chall, "smtp" }
local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params))
auth_data = base64.enc(auth_data)
status, response = query(socket, auth_data)
if ( not(status) ) then
return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech)
end
status, response = query(socket, auth_data)
if ( not(status) ) then
return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech)
end
if ( mech == "DIGEST-MD5" ) then
local rspauth = response:match("^334 (.*)")
if ( rspauth ) then
rspauth = base64.dec(rspauth)
status, response = query(socket,"")
end
end
if ( mech == "DIGEST-MD5" ) then
local rspauth = response:match("^334 (.*)")
if ( rspauth ) then
rspauth = base64.dec(rspauth)
status, response = query(socket,"")
end
end
if ( response:match("^235") ) then return true, "Login success" end
if ( response:match("^235") ) then return true, "Login success" end
return false, response
return false, response
end
return _ENV;

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -204,7 +204,7 @@ Response = {
parse = function(self)
local pos
pos, self.action, self.trans_id, self.conn_id,
self.proto_version, self.vendor_id, self.network_id,
self.proto_version, self.vendor_id, self.network_id,
self.instance_id = bin.unpack(">IIH8CCII", self.data)
end,