mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 13:11:28 +00:00
Final re-indent for scripts.
This commit is contained in:
@@ -118,60 +118,60 @@ categories = {"default", "safe"}
|
|||||||
|
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Match an address (array of bytes) against a hex-encoded pattern. "XX" in the
|
-- Match an address (array of bytes) against a hex-encoded pattern. "XX" in the
|
||||||
-- pattern is a wildcard.
|
-- pattern is a wildcard.
|
||||||
local function matches(addr, pattern)
|
local function matches(addr, pattern)
|
||||||
local octet_patterns
|
local octet_patterns
|
||||||
|
|
||||||
octet_patterns = {}
|
octet_patterns = {}
|
||||||
for op in pattern:gmatch("([%xX][%xX])") do
|
for op in pattern:gmatch("([%xX][%xX])") do
|
||||||
octet_patterns[#octet_patterns + 1] = op
|
octet_patterns[#octet_patterns + 1] = op
|
||||||
end
|
end
|
||||||
|
|
||||||
if #addr ~= #octet_patterns then
|
if #addr ~= #octet_patterns then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, #addr do
|
for i = 1, #addr do
|
||||||
local a, op
|
local a, op
|
||||||
|
|
||||||
a = addr[i]
|
a = addr[i]
|
||||||
op = octet_patterns[i]
|
op = octet_patterns[i]
|
||||||
if not (op == "XX" or a == tonumber(op, 16)) then
|
if not (op == "XX" or a == tonumber(op, 16)) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_manuf(mac)
|
local function get_manuf(mac)
|
||||||
local catch = function() return "Unknown" end
|
local catch = function() return "Unknown" end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
||||||
local prefix = string.upper(string.format("%02x%02x%02x", mac[1], mac[2], mac[3]))
|
local prefix = string.upper(string.format("%02x%02x%02x", mac[1], mac[2], mac[3]))
|
||||||
return mac_prefixes[prefix] or "Unknown"
|
return mac_prefixes[prefix] or "Unknown"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_mac(mac)
|
local function format_mac(mac)
|
||||||
local out = stdnse.output_table()
|
local out = stdnse.output_table()
|
||||||
out.address = stdnse.format_mac(string.char(table.unpack(mac)))
|
out.address = stdnse.format_mac(string.char(table.unpack(mac)))
|
||||||
out.manuf = get_manuf(mac)
|
out.manuf = get_manuf(mac)
|
||||||
return out
|
return out
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_ipv4(ipv4)
|
local function format_ipv4(ipv4)
|
||||||
local octets
|
local octets
|
||||||
|
|
||||||
octets = {}
|
octets = {}
|
||||||
for _, v in ipairs(ipv4) do
|
for _, v in ipairs(ipv4) do
|
||||||
octets[#octets + 1] = string.format("%d", v)
|
octets[#octets + 1] = string.format("%d", v)
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.strjoin(".", octets)
|
return stdnse.strjoin(".", octets)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function do_ipv4(addr)
|
local function do_ipv4(addr)
|
||||||
@@ -179,120 +179,120 @@ end
|
|||||||
|
|
||||||
-- EUI-64 from MAC, RFC 4291.
|
-- EUI-64 from MAC, RFC 4291.
|
||||||
local function decode_eui_64(eui_64)
|
local function decode_eui_64(eui_64)
|
||||||
if eui_64[4] == 0xff and eui_64[5] == 0xfe then
|
if eui_64[4] == 0xff and eui_64[5] == 0xfe then
|
||||||
return { bit.bxor(eui_64[1], 0x02),
|
return { bit.bxor(eui_64[1], 0x02),
|
||||||
eui_64[2], eui_64[3], eui_64[6], eui_64[7], eui_64[8] }
|
eui_64[2], eui_64[3], eui_64[6], eui_64[7], eui_64[8] }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function do_ipv6(addr)
|
local function do_ipv6(addr)
|
||||||
local label
|
local label
|
||||||
local output
|
local output
|
||||||
|
|
||||||
output = stdnse.output_table()
|
output = stdnse.output_table()
|
||||||
|
|
||||||
if matches(addr, "0000:0000:0000:0000:0000:0000:0000:0001") then
|
if matches(addr, "0000:0000:0000:0000:0000:0000:0000:0001") then
|
||||||
-- ::1 is localhost. Not much to report.
|
-- ::1 is localhost. Not much to report.
|
||||||
return nil
|
return nil
|
||||||
elseif matches(addr, "0000:0000:0000:0000:0000:0000:XXXX:XXXX") then
|
elseif matches(addr, "0000:0000:0000:0000:0000:0000:XXXX:XXXX") then
|
||||||
-- RFC 4291 2.5.5.1.
|
-- RFC 4291 2.5.5.1.
|
||||||
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
|
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
|
||||||
return {["IPv4-compatible"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
|
return {["IPv4-compatible"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
|
||||||
elseif matches(addr, "0000:0000:0000:0000:0000:ffff:XXXX:XXXX") then
|
elseif matches(addr, "0000:0000:0000:0000:0000:ffff:XXXX:XXXX") then
|
||||||
-- RFC 4291 2.5.5.2.
|
-- RFC 4291 2.5.5.2.
|
||||||
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
|
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
|
||||||
return {["IPv4-mapped"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
|
return {["IPv4-mapped"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
|
||||||
elseif matches(addr, "2001:0000:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
|
elseif matches(addr, "2001:0000:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
|
||||||
-- Teredo, RFC 4380.
|
-- Teredo, RFC 4380.
|
||||||
local server_ipv4 = { addr[5], addr[6], addr[7], addr[8] }
|
local server_ipv4 = { addr[5], addr[6], addr[7], addr[8] }
|
||||||
-- RFC 5991 makes the flags mostly meaningless.
|
-- RFC 5991 makes the flags mostly meaningless.
|
||||||
local flags = addr[9] * 256 + addr[10]
|
local flags = addr[9] * 256 + addr[10]
|
||||||
local obs_port = addr[11] * 256 + addr[12]
|
local obs_port = addr[11] * 256 + addr[12]
|
||||||
local obs_client_ipv4 = { addr[13], addr[14], addr[15], addr[16] }
|
local obs_client_ipv4 = { addr[13], addr[14], addr[15], addr[16] }
|
||||||
local port, client_ipv4
|
local port, client_ipv4
|
||||||
|
|
||||||
-- Invert obs_port.
|
-- Invert obs_port.
|
||||||
port = bit.bxor(obs_port, 0xffff)
|
port = bit.bxor(obs_port, 0xffff)
|
||||||
|
|
||||||
-- Invert obs_client_ipv4.
|
-- Invert obs_client_ipv4.
|
||||||
client_ipv4 = {}
|
client_ipv4 = {}
|
||||||
for _, octet in ipairs(obs_client_ipv4) do
|
for _, octet in ipairs(obs_client_ipv4) do
|
||||||
client_ipv4[#client_ipv4 + 1] = bit.bxor(octet, 0xff)
|
client_ipv4[#client_ipv4 + 1] = bit.bxor(octet, 0xff)
|
||||||
end
|
end
|
||||||
|
|
||||||
output["Server IPv4 address"] = format_ipv4(server_ipv4)
|
output["Server IPv4 address"] = format_ipv4(server_ipv4)
|
||||||
output["Client IPv4 address"] = format_ipv4(client_ipv4)
|
output["Client IPv4 address"] = format_ipv4(client_ipv4)
|
||||||
output["UDP port"] = tostring(port)
|
output["UDP port"] = tostring(port)
|
||||||
|
|
||||||
return {["Teredo"] = output}
|
return {["Teredo"] = output}
|
||||||
elseif matches(addr, "0064:ff9b:XXXX:XXXX:00XX:XXXX:XXXX:XXXX") then
|
elseif matches(addr, "0064:ff9b:XXXX:XXXX:00XX:XXXX:XXXX:XXXX") then
|
||||||
--IPv4-embedded IPv6 addresses. RFC 6052, Section 2
|
--IPv4-embedded IPv6 addresses. RFC 6052, Section 2
|
||||||
|
|
||||||
--skip addr[9]
|
--skip addr[9]
|
||||||
if matches(addr,"0064:ff9b:0000:0000:0000:0000:XXXX:XXXX") then
|
if matches(addr,"0064:ff9b:0000:0000:0000:0000:XXXX:XXXX") then
|
||||||
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
|
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
|
||||||
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
||||||
elseif addr[5] ~= 0x01 then
|
elseif addr[5] ~= 0x01 then
|
||||||
local ipv4 = {addr[5], addr[6], addr[7], addr[8]}
|
local ipv4 = {addr[5], addr[6], addr[7], addr[8]}
|
||||||
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
||||||
elseif addr[6] ~= 0x22 then
|
elseif addr[6] ~= 0x22 then
|
||||||
local ipv4 = {addr[6], addr[7], addr[8], addr[10]}
|
local ipv4 = {addr[6], addr[7], addr[8], addr[10]}
|
||||||
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
||||||
elseif addr[7] ~= 0x03 then
|
elseif addr[7] ~= 0x03 then
|
||||||
local ipv4 = {addr[7], addr[8], addr[10], addr[11]}
|
local ipv4 = {addr[7], addr[8], addr[10], addr[11]}
|
||||||
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
||||||
elseif addr[8] ~= 0x44 then
|
elseif addr[8] ~= 0x44 then
|
||||||
local ipv4 = {addr[8], addr[10], addr[11], addr[12]}
|
local ipv4 = {addr[8], addr[10], addr[11], addr[12]}
|
||||||
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
||||||
elseif addr[10] == 0x00 and addr[11] == 0x00 and addr[12] == 0x00 then
|
elseif addr[10] == 0x00 and addr[11] == 0x00 and addr[12] == 0x00 then
|
||||||
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
|
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
|
||||||
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
|
||||||
end
|
end
|
||||||
elseif matches(addr, "0000:0000:0000:0000:ffff:0000:XXXX:XXXX") then
|
elseif matches(addr, "0000:0000:0000:0000:ffff:0000:XXXX:XXXX") then
|
||||||
-- IPv4-translated IPv6 addresses. RFC 2765, Section 2.1
|
-- IPv4-translated IPv6 addresses. RFC 2765, Section 2.1
|
||||||
return {["IPv4-translated IPv6 address"]=
|
return {["IPv4-translated IPv6 address"]=
|
||||||
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
|
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
|
||||||
elseif matches(addr, "XXXX:XXXX:XXXX:XX00:0000:5efe:XXXX:XXXX") then
|
elseif matches(addr, "XXXX:XXXX:XXXX:XX00:0000:5efe:XXXX:XXXX") then
|
||||||
-- ISATAP. RFC 5214, Appendix A
|
-- ISATAP. RFC 5214, Appendix A
|
||||||
-- XXXX:XXXX:XXXX:XX00:0000:5EFE:a.b.c.d
|
-- XXXX:XXXX:XXXX:XX00:0000:5EFE:a.b.c.d
|
||||||
return {["ISATAP Modified EUI-64 IPv6 Address"]=
|
return {["ISATAP Modified EUI-64 IPv6 Address"]=
|
||||||
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
|
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- These following use common handling for the Interface ID part
|
-- These following use common handling for the Interface ID part
|
||||||
-- (last 64 bits).
|
-- (last 64 bits).
|
||||||
|
|
||||||
if matches(addr, "2002:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
|
if matches(addr, "2002:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
|
||||||
-- 6to4, RFC 3056.
|
-- 6to4, RFC 3056.
|
||||||
local ipv4 = { addr[3], addr[4], addr[5], addr[6] }
|
local ipv4 = { addr[3], addr[4], addr[5], addr[6] }
|
||||||
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
|
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
|
||||||
addr[13], addr[14], addr[15], addr[16] })
|
addr[13], addr[14], addr[15], addr[16] })
|
||||||
|
|
||||||
label = "6to4"
|
label = "6to4"
|
||||||
output["IPv4 address"] = format_ipv4(ipv4)
|
output["IPv4 address"] = format_ipv4(ipv4)
|
||||||
end
|
end
|
||||||
|
|
||||||
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
|
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
|
||||||
addr[13], addr[14], addr[15], addr[16] })
|
addr[13], addr[14], addr[15], addr[16] })
|
||||||
if mac then
|
if mac then
|
||||||
output["MAC address"] = format_mac(mac)
|
output["MAC address"] = format_mac(mac)
|
||||||
if not label then
|
if not label then
|
||||||
label = "IPv6 EUI-64"
|
label = "IPv6 EUI-64"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {[label]= output}
|
return {[label]= output}
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local addr_s, addr_t
|
local addr_s, addr_t
|
||||||
|
|
||||||
addr_s = host.bin_ip
|
addr_s = host.bin_ip
|
||||||
addr_t = { string.byte(addr_s, 1, #addr_s) }
|
addr_t = { string.byte(addr_s, 1, #addr_s) }
|
||||||
|
|
||||||
if #addr_t == 4 then
|
if #addr_t == 4 then
|
||||||
return do_ipv4(addr_t)
|
return do_ipv4(addr_t)
|
||||||
elseif #addr_t == 16 then
|
elseif #addr_t == 16 then
|
||||||
return do_ipv6(addr_t)
|
return do_ipv6(addr_t)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,249 +50,249 @@ categories = {"intrusive", "brute"}
|
|||||||
-- This portrule succeeds only when the open|filtered port is in the port range
|
-- This portrule succeeds only when the open|filtered port is in the port range
|
||||||
-- which is specified by the ports script argument
|
-- which is specified by the ports script argument
|
||||||
portrule = function(host, port)
|
portrule = function(host, port)
|
||||||
if not stdnse.get_script_args(SCRIPT_NAME .. ".ports") then
|
if not stdnse.get_script_args(SCRIPT_NAME .. ".ports") then
|
||||||
stdnse.print_debug(3,"Skipping '%s' %s, 'ports' argument is missing.",SCRIPT_NAME, SCRIPT_TYPE)
|
stdnse.print_debug(3,"Skipping '%s' %s, 'ports' argument is missing.",SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local ports = stdnse.get_script_args(SCRIPT_NAME .. ".ports")
|
local ports = stdnse.get_script_args(SCRIPT_NAME .. ".ports")
|
||||||
|
|
||||||
--print out a debug message if port 31337/udp is open
|
--print out a debug message if port 31337/udp is open
|
||||||
if port.number==31337 and port.protocol == "udp" and not(ports) then
|
if port.number==31337 and port.protocol == "udp" and not(ports) then
|
||||||
stdnse.print_debug("Port 31337/udp is open. Possibility of version detection and password bruteforcing using the backorifice-brute script")
|
stdnse.print_debug("Port 31337/udp is open. Possibility of version detection and password bruteforcing using the backorifice-brute script")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return port.protocol == "udp" and stdnse.in_port_range(port, ports:gsub(",",",") ) and
|
return port.protocol == "udp" and stdnse.in_port_range(port, ports:gsub(",",",") ) and
|
||||||
not(shortport.port_is_excluded(port.number,port.protocol))
|
not(shortport.port_is_excluded(port.number,port.protocol))
|
||||||
end
|
end
|
||||||
|
|
||||||
local backorifice =
|
local backorifice =
|
||||||
{
|
{
|
||||||
new = function(self, host, port)
|
new = function(self, host, port)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.host = host
|
o.host = host
|
||||||
o.port = port
|
o.port = port
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Initializes the backorifice object
|
--- Initializes the backorifice object
|
||||||
--
|
--
|
||||||
initialize = function(self)
|
initialize = function(self)
|
||||||
--create socket
|
--create socket
|
||||||
self.socket = nmap.new_socket("udp")
|
self.socket = nmap.new_socket("udp")
|
||||||
self.socket:set_timeout(self.host.times.timeout * 1000)
|
self.socket:set_timeout(self.host.times.timeout * 1000)
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to send an encrypted PING packet to BackOrifice service
|
--- Attempts to send an encrypted PING packet to BackOrifice service
|
||||||
--
|
--
|
||||||
-- @param password string containing password for encryption
|
-- @param password string containing password for encryption
|
||||||
-- @param initial_seed number containing initial encryption seed
|
-- @param initial_seed number containing initial encryption seed
|
||||||
-- @return status, true on success, false on failure
|
-- @return status, true on success, false on failure
|
||||||
-- @return err string containing error message on failure
|
-- @return err string containing error message on failure
|
||||||
try_password = function(self, password, initial_seed)
|
try_password = function(self, password, initial_seed)
|
||||||
--initialize BackOrifice PING packet: |MAGICSTRING|size|packetID|TYPE_PING|arg1|arg_separat|arg2|CRC/disregarded|
|
--initialize BackOrifice PING packet: |MAGICSTRING|size|packetID|TYPE_PING|arg1|arg_separat|arg2|CRC/disregarded|
|
||||||
local PING_PACKET = bin.pack("A<IICACAC", "*!*QWTY?", 19, 0, 0x01, "", 0x00, "", 0x00)
|
local PING_PACKET = bin.pack("A<IICACAC", "*!*QWTY?", 19, 0, 0x01, "", 0x00, "", 0x00)
|
||||||
local seed, status, response, encrypted_ping
|
local seed, status, response, encrypted_ping
|
||||||
|
|
||||||
if not(initial_seed) then
|
if not(initial_seed) then
|
||||||
seed = self:gen_initial_seed(password)
|
seed = self:gen_initial_seed(password)
|
||||||
else
|
else
|
||||||
seed = initial_seed
|
seed = initial_seed
|
||||||
end
|
end
|
||||||
|
|
||||||
encrypted_ping = self:BOcrypt(PING_PACKET,seed)
|
encrypted_ping = self:BOcrypt(PING_PACKET,seed)
|
||||||
|
|
||||||
status, response = self.socket:sendto(self.host.ip, self.port.number, encrypted_ping)
|
status, response = self.socket:sendto(self.host.ip, self.port.number, encrypted_ping)
|
||||||
if not(status) then
|
if not(status) then
|
||||||
return false, response
|
return false, response
|
||||||
end
|
end
|
||||||
status, response = self.socket:receive()
|
status, response = self.socket:receive()
|
||||||
|
|
||||||
-- The first 8 bytes of both response and sent data are
|
-- The first 8 bytes of both response and sent data are
|
||||||
-- magicstring = "*!*QWTY?", without the quotes, and since
|
-- magicstring = "*!*QWTY?", without the quotes, and since
|
||||||
-- both are encrypted with the same initial seed, this is
|
-- both are encrypted with the same initial seed, this is
|
||||||
-- how we verify we are talking to a BackOrifice service.
|
-- how we verify we are talking to a BackOrifice service.
|
||||||
-- The statement is optimized so as not to decrypt unless
|
-- The statement is optimized so as not to decrypt unless
|
||||||
-- comparison of encrypted magicstrings succeds
|
-- comparison of encrypted magicstrings succeds
|
||||||
if status and response:sub(1,8) == encrypted_ping:sub(1,8)
|
if status and response:sub(1,8) == encrypted_ping:sub(1,8)
|
||||||
and self:BOcrypt(response,seed):match("!PONG!(1%.20)!.*!") then
|
and self:BOcrypt(response,seed):match("!PONG!(1%.20)!.*!") then
|
||||||
local BOversion, BOhostname = self:BOcrypt(response,seed):match("!PONG!(1%.20)!(.*)!")
|
local BOversion, BOhostname = self:BOcrypt(response,seed):match("!PONG!(1%.20)!(.*)!")
|
||||||
self:insert_version_info(BOversion,BOhostname,nil,password)
|
self:insert_version_info(BOversion,BOhostname,nil,password)
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
if not(status) then
|
if not(status) then
|
||||||
return false, response
|
return false, response
|
||||||
else
|
else
|
||||||
return false,"Response not recognized."
|
return false,"Response not recognized."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Close the socket
|
--- Close the socket
|
||||||
--
|
--
|
||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
close = function(self)
|
close = function(self)
|
||||||
return self.socket:close()
|
return self.socket:close()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Generates the initial encryption seed from a password
|
--- Generates the initial encryption seed from a password
|
||||||
--
|
--
|
||||||
-- @param password string containing password
|
-- @param password string containing password
|
||||||
-- @return seed number containing initial seed
|
-- @return seed number containing initial seed
|
||||||
gen_initial_seed = function(self, password)
|
gen_initial_seed = function(self, password)
|
||||||
if password == nil then
|
if password == nil then
|
||||||
return 31337
|
return 31337
|
||||||
else
|
else
|
||||||
local y = #password
|
local y = #password
|
||||||
local z = 0
|
local z = 0
|
||||||
|
|
||||||
for x = 1,y do
|
for x = 1,y do
|
||||||
local pchar = string.byte(password,x)
|
local pchar = string.byte(password,x)
|
||||||
z = z + pchar
|
z = z + pchar
|
||||||
end
|
end
|
||||||
|
|
||||||
for x=1,y do
|
for x=1,y do
|
||||||
local pchar = string.byte(password,x)
|
local pchar = string.byte(password,x)
|
||||||
if (x-1)%2 == 1 then
|
if (x-1)%2 == 1 then
|
||||||
z = z - (pchar * (y-(x-1)+1))
|
z = z - (pchar * (y-(x-1)+1))
|
||||||
else
|
else
|
||||||
z = z + (pchar * (y-(x-1)+1))
|
z = z + (pchar * (y-(x-1)+1))
|
||||||
end
|
end
|
||||||
z = z % 0x7fffffff
|
z = z % 0x7fffffff
|
||||||
end
|
end
|
||||||
z = (z*y) % 0x7fffffff
|
z = (z*y) % 0x7fffffff
|
||||||
return z
|
return z
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Generates next encryption seed from given seed
|
--- Generates next encryption seed from given seed
|
||||||
--
|
--
|
||||||
-- @param seed number containing current seed
|
-- @param seed number containing current seed
|
||||||
-- @return seed number containing next seed
|
-- @return seed number containing next seed
|
||||||
gen_next_seed = function(self, seed)
|
gen_next_seed = function(self, seed)
|
||||||
seed = seed*214013 + 2531011
|
seed = seed*214013 + 2531011
|
||||||
seed = bit.band(seed,0xffffff)
|
seed = bit.band(seed,0xffffff)
|
||||||
return seed
|
return seed
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Encrypts/decrypts data using BackOrifice algorithm
|
--- Encrypts/decrypts data using BackOrifice algorithm
|
||||||
--
|
--
|
||||||
-- @param data binary string containing data to be encrypted/decrypted
|
-- @param data binary string containing data to be encrypted/decrypted
|
||||||
-- @param initial_seed number containing initial encryption seed
|
-- @param initial_seed number containing initial encryption seed
|
||||||
-- @return data binary string containing encrypted/decrypted data
|
-- @return data binary string containing encrypted/decrypted data
|
||||||
BOcrypt = function(self, data, initial_seed )
|
BOcrypt = function(self, data, initial_seed )
|
||||||
if data==nil then return end
|
if data==nil then return end
|
||||||
|
|
||||||
local output =""
|
local output =""
|
||||||
local seed = initial_seed
|
local seed = initial_seed
|
||||||
local data_byte
|
local data_byte
|
||||||
local crypto_byte
|
local crypto_byte
|
||||||
|
|
||||||
for i = 1, #data do
|
for i = 1, #data do
|
||||||
data_byte = string.byte(data,i)
|
data_byte = string.byte(data,i)
|
||||||
|
|
||||||
--calculate next seed
|
--calculate next seed
|
||||||
seed = self:gen_next_seed(seed)
|
seed = self:gen_next_seed(seed)
|
||||||
--calculate encryption key based on seed
|
--calculate encryption key based on seed
|
||||||
local key = bit.band(bit.arshift(seed,16), 0xff)
|
local key = bit.band(bit.arshift(seed,16), 0xff)
|
||||||
|
|
||||||
crypto_byte = bit.bxor(data_byte,key)
|
crypto_byte = bit.bxor(data_byte,key)
|
||||||
output = bin.pack("AC",output,crypto_byte)
|
output = bin.pack("AC",output,crypto_byte)
|
||||||
--ARGSIZE limitation from BackOrifice server
|
--ARGSIZE limitation from BackOrifice server
|
||||||
if i == 256 then break end
|
if i == 256 then break end
|
||||||
end
|
end
|
||||||
return output
|
return output
|
||||||
end,
|
end,
|
||||||
|
|
||||||
insert_version_info = function(self,BOversion,BOhostname,initial_seed,password)
|
insert_version_info = function(self,BOversion,BOhostname,initial_seed,password)
|
||||||
if not self.port.version then self.port.version={} end
|
if not self.port.version then self.port.version={} end
|
||||||
if not self.port.version.name then
|
if not self.port.version.name then
|
||||||
self.port.version.name ="BackOrifice"
|
self.port.version.name ="BackOrifice"
|
||||||
self.port.version.name_confidence = 10
|
self.port.version.name_confidence = 10
|
||||||
end
|
end
|
||||||
if not self.port.version.product then self.port.version.product ="BackOrifice trojan" end
|
if not self.port.version.product then self.port.version.product ="BackOrifice trojan" end
|
||||||
if not self.port.version.version then self.port.version.version = BOversion end
|
if not self.port.version.version then self.port.version.version = BOversion end
|
||||||
if not self.port.version.extrainfo then
|
if not self.port.version.extrainfo then
|
||||||
if not password then
|
if not password then
|
||||||
if not initial_seed then
|
if not initial_seed then
|
||||||
self.port.version.extrainfo = "no password"
|
self.port.version.extrainfo = "no password"
|
||||||
else
|
else
|
||||||
self.port.version.extrainfo = "initial encryption seed="..initial_seed
|
self.port.version.extrainfo = "initial encryption seed="..initial_seed
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.port.version.extrainfo = "password="..password
|
self.port.version.extrainfo = "password="..password
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.port.version.hostname = BOhostname
|
self.port.version.hostname = BOhostname
|
||||||
if not self.port.version.ostype then self.port.version.ostype = "Windows" end
|
if not self.port.version.ostype then self.port.version.ostype = "Windows" end
|
||||||
nmap.set_port_version(self.host, self.port)
|
nmap.set_port_version(self.host, self.port)
|
||||||
nmap.set_port_state(self.host,self.port,"open")
|
nmap.set_port_state(self.host,self.port,"open")
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
local Driver =
|
local Driver =
|
||||||
{
|
{
|
||||||
new = function(self, host, port)
|
new = function(self, host, port)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.host = host
|
o.host = host
|
||||||
o.port = port
|
o.port = port
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
connect=function(self)
|
connect=function(self)
|
||||||
--only initialize since BackOrifice service knows no connect()
|
--only initialize since BackOrifice service knows no connect()
|
||||||
self.bo = backorifice:new(self.host,self.port)
|
self.bo = backorifice:new(self.host,self.port)
|
||||||
self.bo:initialize()
|
self.bo:initialize()
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
disconnect = function( self )
|
disconnect = function( self )
|
||||||
self.bo:close()
|
self.bo:close()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to send encrypted PING packet to BackOrifice service
|
--- Attempts to send encrypted PING packet to BackOrifice service
|
||||||
--
|
--
|
||||||
-- @param username string containing username which is disregarded
|
-- @param username string containing username which is disregarded
|
||||||
-- @param password string containing login password
|
-- @param password string containing login password
|
||||||
-- @return brute.Error object on failure
|
-- @return brute.Error object on failure
|
||||||
-- brute.Account object on success
|
-- brute.Account object on success
|
||||||
login = function( self, username, password )
|
login = function( self, username, password )
|
||||||
local status, msg = self.bo:try_password(password,nil)
|
local status, msg = self.bo:try_password(password,nil)
|
||||||
if status then
|
if status then
|
||||||
if not(nmap.registry['credentials']) then
|
if not(nmap.registry['credentials']) then
|
||||||
nmap.registry['credentials']={}
|
nmap.registry['credentials']={}
|
||||||
end
|
end
|
||||||
if ( not( nmap.registry.credentials['backorifice'] ) ) then
|
if ( not( nmap.registry.credentials['backorifice'] ) ) then
|
||||||
nmap.registry.credentials['backorifice'] = {}
|
nmap.registry.credentials['backorifice'] = {}
|
||||||
end
|
end
|
||||||
table.insert( nmap.registry.credentials.backorifice, { password = password } )
|
table.insert( nmap.registry.credentials.backorifice, { password = password } )
|
||||||
return true, brute.Account:new("", password, creds.State.VALID)
|
return true, brute.Account:new("", password, creds.State.VALID)
|
||||||
else
|
else
|
||||||
-- The only indication that the password is incorrect is a timeout
|
-- The only indication that the password is incorrect is a timeout
|
||||||
local err = brute.Error:new( "Incorrect password" )
|
local err = brute.Error:new( "Incorrect password" )
|
||||||
err:setRetry(false)
|
err:setRetry(false)
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local status, result
|
local status, result
|
||||||
local engine = brute.Engine:new(Driver,host,port)
|
local engine = brute.Engine:new(Driver,host,port)
|
||||||
|
|
||||||
engine.options.firstonly = true
|
engine.options.firstonly = true
|
||||||
engine.options.passonly = true
|
engine.options.passonly = true
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
|
|
||||||
status, result = engine:start()
|
status, result = engine:start()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -85,240 +85,240 @@ local g_packet = 0
|
|||||||
--"constants"
|
--"constants"
|
||||||
local MAGICSTRING ="*!*QWTY?"
|
local MAGICSTRING ="*!*QWTY?"
|
||||||
local TYPE = {
|
local TYPE = {
|
||||||
ERROR = 0x00,
|
ERROR = 0x00,
|
||||||
PARTIAL_PACKET = 0x80,
|
PARTIAL_PACKET = 0x80,
|
||||||
CONTINUED_PACKET = 0x40,
|
CONTINUED_PACKET = 0x40,
|
||||||
PING = 0x01,
|
PING = 0x01,
|
||||||
SYSINFO = 0x06,
|
SYSINFO = 0x06,
|
||||||
PROCESSLIST = 0x20,
|
PROCESSLIST = 0x20,
|
||||||
NETVIEW = 0x39,
|
NETVIEW = 0x39,
|
||||||
NETEXPORTLIST = 0x12,
|
NETEXPORTLIST = 0x12,
|
||||||
REDIRLIST = 0x0D,
|
REDIRLIST = 0x0D,
|
||||||
APPLIST = 0x3F,
|
APPLIST = 0x3F,
|
||||||
PLUGINLIST = 0x2F
|
PLUGINLIST = 0x2F
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
--table of commands which have output
|
--table of commands which have output
|
||||||
local cmds = {
|
local cmds = {
|
||||||
{cmd_name="PING REPLY",p_code=TYPE.PING,arg1="",arg2="",
|
{cmd_name="PING REPLY",p_code=TYPE.PING,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
data = string.gsub(data," ","")
|
data = string.gsub(data," ","")
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
{cmd_name="SYSTEM INFO",p_code=TYPE.SYSINFO,arg1="",arg2="",
|
{cmd_name="SYSTEM INFO",p_code=TYPE.SYSINFO,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"End of system info") then return nil end
|
if string.match(data,"End of system info") then return nil end
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
{cmd_name="PROCESS LIST",p_code=TYPE.PROCESSLIST,arg1="",arg2="",
|
{cmd_name="PROCESS LIST",p_code=TYPE.PROCESSLIST,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"End of processes") then return nil end
|
if string.match(data,"End of processes") then return nil end
|
||||||
data = string.gsub(data,"pid","PID")
|
data = string.gsub(data,"pid","PID")
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
{cmd_name="NETWORK RESOURCES - NET VIEW",p_code=TYPE.NETVIEW,arg1="",arg2="",
|
{cmd_name="NETWORK RESOURCES - NET VIEW",p_code=TYPE.NETVIEW,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"Network resources:") then return nil end
|
if string.match(data,"Network resources:") then return nil end
|
||||||
if string.match(data,"End of resource list") then return nil end
|
if string.match(data,"End of resource list") then return nil end
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
{cmd_name="SHARELIST",p_code=TYPE.NETEXPORTLIST,arg1="",arg2="",
|
{cmd_name="SHARELIST",p_code=TYPE.NETEXPORTLIST,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"Shares as returned by system:") then return nil end
|
if string.match(data,"Shares as returned by system:") then return nil end
|
||||||
if string.match(data,"End of shares") then return nil end
|
if string.match(data,"End of shares") then return nil end
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
{cmd_name="REDIRECTED PORTS",p_code=TYPE.REDIRLIST,arg1="",arg2="",
|
{cmd_name="REDIRECTED PORTS",p_code=TYPE.REDIRLIST,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"Redirected ports:%s") then return nil end
|
if string.match(data,"Redirected ports:%s") then return nil end
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
{cmd_name="LISTENING CONSOLE APPLICATIONS",p_code=TYPE.APPLIST,arg1="",arg2="",
|
{cmd_name="LISTENING CONSOLE APPLICATIONS",p_code=TYPE.APPLIST,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"Active apps:") then return nil end
|
if string.match(data,"Active apps:") then return nil end
|
||||||
return data
|
return data
|
||||||
end},
|
end},
|
||||||
-- I !think! plugin list MUST be last because it causes problems server-side
|
-- I !think! plugin list MUST be last because it causes problems server-side
|
||||||
{cmd_name="PLUGIN LIST",p_code=TYPE.PLUGINLIST,arg1="",arg2="",
|
{cmd_name="PLUGIN LIST",p_code=TYPE.PLUGINLIST,arg1="",arg2="",
|
||||||
filter = function(data)
|
filter = function(data)
|
||||||
if string.match(data,"Plugins:") then return nil end
|
if string.match(data,"Plugins:") then return nil end
|
||||||
return data
|
return data
|
||||||
end}
|
end}
|
||||||
}
|
}
|
||||||
|
|
||||||
local function gen_next_seed(seed)
|
local function gen_next_seed(seed)
|
||||||
seed = seed*214013 + 2531011
|
seed = seed*214013 + 2531011
|
||||||
seed = bit.band(seed,0xffffff)
|
seed = bit.band(seed,0xffffff)
|
||||||
return seed
|
return seed
|
||||||
end
|
end
|
||||||
|
|
||||||
local function gen_initial_seed(password)
|
local function gen_initial_seed(password)
|
||||||
if password == nil then
|
if password == nil then
|
||||||
return 31337
|
return 31337
|
||||||
else
|
else
|
||||||
local y = #password
|
local y = #password
|
||||||
local z = 0
|
local z = 0
|
||||||
|
|
||||||
for x = 1,y do
|
for x = 1,y do
|
||||||
local pchar = string.byte(password,x)
|
local pchar = string.byte(password,x)
|
||||||
z = z + pchar
|
z = z + pchar
|
||||||
end
|
end
|
||||||
|
|
||||||
for x=1,y do
|
for x=1,y do
|
||||||
local pchar = string.byte(password,x)
|
local pchar = string.byte(password,x)
|
||||||
if (x-1)%2 == 1 then
|
if (x-1)%2 == 1 then
|
||||||
z = z - (pchar * (y-(x-1)+1))
|
z = z - (pchar * (y-(x-1)+1))
|
||||||
else
|
else
|
||||||
z = z + (pchar * (y-(x-1)+1))
|
z = z + (pchar * (y-(x-1)+1))
|
||||||
end
|
end
|
||||||
z = z % 0x7fffffff
|
z = z % 0x7fffffff
|
||||||
end
|
end
|
||||||
z = (z*y) % 0x7fffffff
|
z = (z*y) % 0x7fffffff
|
||||||
return z
|
return z
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--BOcrypt returns encrypted/decrypted data
|
--BOcrypt returns encrypted/decrypted data
|
||||||
local function BOcrypt(data, password, initial_seed )
|
local function BOcrypt(data, password, initial_seed )
|
||||||
local output =""
|
local output =""
|
||||||
if data==nil then return end
|
if data==nil then return end
|
||||||
|
|
||||||
local seed
|
local seed
|
||||||
if(initial_seed == nil) then
|
if(initial_seed == nil) then
|
||||||
--calculate initial seed
|
--calculate initial seed
|
||||||
seed = gen_initial_seed(password)
|
seed = gen_initial_seed(password)
|
||||||
else
|
else
|
||||||
--in case initial seed is set by backorifice brute
|
--in case initial seed is set by backorifice brute
|
||||||
seed = initial_seed
|
seed = initial_seed
|
||||||
end
|
end
|
||||||
|
|
||||||
local data_byte
|
local data_byte
|
||||||
local crypto_byte
|
local crypto_byte
|
||||||
|
|
||||||
for i = 1, #data do
|
for i = 1, #data do
|
||||||
data_byte = string.byte(data,i)
|
data_byte = string.byte(data,i)
|
||||||
|
|
||||||
--calculate next seed
|
--calculate next seed
|
||||||
seed = gen_next_seed(seed)
|
seed = gen_next_seed(seed)
|
||||||
--calculate encryption key based on seed
|
--calculate encryption key based on seed
|
||||||
local key = bit.band(bit.arshift(seed,16), 0xff)
|
local key = bit.band(bit.arshift(seed,16), 0xff)
|
||||||
|
|
||||||
crypto_byte = bit.bxor(data_byte,key)
|
crypto_byte = bit.bxor(data_byte,key)
|
||||||
output = bin.pack("AC",output,crypto_byte)
|
output = bin.pack("AC",output,crypto_byte)
|
||||||
if i == 256 then break end --ARGSIZE limitation
|
if i == 256 then break end --ARGSIZE limitation
|
||||||
end
|
end
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
local function BOpack(type_packet, str1, str2)
|
local function BOpack(type_packet, str1, str2)
|
||||||
-- create BO packet
|
-- create BO packet
|
||||||
local data = ""
|
local data = ""
|
||||||
local size = #MAGICSTRING + 4*2 + 3 + #str1 + #str2
|
local size = #MAGICSTRING + 4*2 + 3 + #str1 + #str2
|
||||||
data = bin.pack("A<IICACAC",MAGICSTRING,size,g_packet,type_packet,str1,0x00,str2,0x00)
|
data = bin.pack("A<IICACAC",MAGICSTRING,size,g_packet,type_packet,str1,0x00,str2,0x00)
|
||||||
g_packet = g_packet + 1
|
g_packet = g_packet + 1
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
local function BOunpack(packet)
|
local function BOunpack(packet)
|
||||||
local pos, magic = bin.unpack("A8",packet)
|
local pos, magic = bin.unpack("A8",packet)
|
||||||
|
|
||||||
if magic ~= MAGICSTRING then return nil,TYPE.ERROR end --received non-BO packet
|
if magic ~= MAGICSTRING then return nil,TYPE.ERROR end --received non-BO packet
|
||||||
|
|
||||||
local packetsize, packetid, type_packet, data
|
local packetsize, packetid, type_packet, data
|
||||||
pos, packetsize, packetid, type_packet = bin.unpack("<IIC",packet,pos)
|
pos, packetsize, packetid, type_packet = bin.unpack("<IIC",packet,pos)
|
||||||
pos, data = bin.unpack("A"..(packetsize-pos-1),packet,pos)
|
pos, data = bin.unpack("A"..(packetsize-pos-1),packet,pos)
|
||||||
|
|
||||||
return data, type_packet
|
return data, type_packet
|
||||||
end
|
end
|
||||||
|
|
||||||
local function insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
|
local function insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
|
||||||
if(port.version==nil) then port.version={} end
|
if(port.version==nil) then port.version={} end
|
||||||
if(port.version.name==nil) then
|
if(port.version.name==nil) then
|
||||||
port.version.name ="BackOrifice"
|
port.version.name ="BackOrifice"
|
||||||
port.version.name_confidence = 10
|
port.version.name_confidence = 10
|
||||||
end
|
end
|
||||||
if(port.version.product==nil) then port.version.product ="BackOrifice trojan" end
|
if(port.version.product==nil) then port.version.product ="BackOrifice trojan" end
|
||||||
if(port.version.version == nil) then port.version.version = BOversion end
|
if(port.version.version == nil) then port.version.version = BOversion end
|
||||||
if(port.version.extrainfo == nil) then
|
if(port.version.extrainfo == nil) then
|
||||||
if password == nil then
|
if password == nil then
|
||||||
if initial_seed == nil then
|
if initial_seed == nil then
|
||||||
port.version.extrainfo = "no password"
|
port.version.extrainfo = "no password"
|
||||||
else
|
else
|
||||||
port.version.extrainfo = "initial encryption seed="..initial_seed
|
port.version.extrainfo = "initial encryption seed="..initial_seed
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
port.version.extrainfo = "password="..password
|
port.version.extrainfo = "password="..password
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
port.version.hostname = BOhostname
|
port.version.hostname = BOhostname
|
||||||
if(port.version.ostype == nil) then port.version.ostype = "Windows" end
|
if(port.version.ostype == nil) then port.version.ostype = "Windows" end
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
--initial seed is set by backorifice-brute
|
--initial seed is set by backorifice-brute
|
||||||
local initial_seed = stdnse.get_script_args( SCRIPT_NAME .. ".seed" )
|
local initial_seed = stdnse.get_script_args( SCRIPT_NAME .. ".seed" )
|
||||||
local password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
|
local password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
|
||||||
local socket = nmap.new_socket("udp")
|
local socket = nmap.new_socket("udp")
|
||||||
local try = nmap.new_try(function() socket:close() end)
|
local try = nmap.new_try(function() socket:close() end)
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
|
|
||||||
local output_all={}
|
local output_all={}
|
||||||
|
|
||||||
for i=1,#cmds do
|
for i=1,#cmds do
|
||||||
--send command
|
--send command
|
||||||
local data = BOpack( cmds[i].p_code, cmds[i].arg1, cmds[i].arg2 )
|
local data = BOpack( cmds[i].p_code, cmds[i].arg1, cmds[i].arg2 )
|
||||||
data = BOcrypt(data, password, initial_seed)
|
data = BOcrypt(data, password, initial_seed)
|
||||||
try(socket:sendto(host.ip, port.number, data))
|
try(socket:sendto(host.ip, port.number, data))
|
||||||
|
|
||||||
--receive info
|
--receive info
|
||||||
local output, response, p_type, multi_flag
|
local output, response, p_type, multi_flag
|
||||||
output = {}
|
output = {}
|
||||||
output.name = cmds[i].cmd_name
|
output.name = cmds[i].cmd_name
|
||||||
multi_flag = false
|
multi_flag = false
|
||||||
while true do
|
while true do
|
||||||
response = try(socket:receive())
|
response = try(socket:receive())
|
||||||
response = BOcrypt(response,password,initial_seed)
|
response = BOcrypt(response,password,initial_seed)
|
||||||
response, p_type = BOunpack(response) -- p_type -> error, singular, partial, continued
|
response, p_type = BOunpack(response) -- p_type -> error, singular, partial, continued
|
||||||
|
|
||||||
if p_type ~= TYPE.ERROR then
|
if p_type ~= TYPE.ERROR then
|
||||||
local tmp_str = cmds[i].filter(response)
|
local tmp_str = cmds[i].filter(response)
|
||||||
if tmp_str ~= nil then
|
if tmp_str ~= nil then
|
||||||
if cmds[i].p_code==TYPE.PING then
|
if cmds[i].p_code==TYPE.PING then
|
||||||
--invalid chars for hostname are allowed on old windows boxes
|
--invalid chars for hostname are allowed on old windows boxes
|
||||||
local BOversion, BOhostname = string.match(tmp_str,"!PONG!(1%.20)!(.*)!")
|
local BOversion, BOhostname = string.match(tmp_str,"!PONG!(1%.20)!(.*)!")
|
||||||
if BOversion==nil then
|
if BOversion==nil then
|
||||||
--in case of bad PING reply return ""
|
--in case of bad PING reply return ""
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
--fill up version information
|
--fill up version information
|
||||||
insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
|
insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(output,tmp_str)
|
table.insert(output,tmp_str)
|
||||||
end
|
end
|
||||||
|
|
||||||
--singular
|
--singular
|
||||||
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00
|
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00
|
||||||
and bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then break end
|
and bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then break end
|
||||||
|
|
||||||
--first
|
--first
|
||||||
if bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then
|
if bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then
|
||||||
multi_flag = true
|
multi_flag = true
|
||||||
end
|
end
|
||||||
|
|
||||||
--last
|
--last
|
||||||
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00 then break end
|
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00 then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
--gather all responses in table
|
--gather all responses in table
|
||||||
table.insert(output_all,output)
|
table.insert(output_all,output)
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
return stdnse.format_output(true,output_all)
|
return stdnse.format_output(true,output_all)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -76,15 +76,15 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|||||||
categories = {"discovery", "broadcast", "safe"}
|
categories = {"discovery", "broadcast", "safe"}
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -92,22 +92,22 @@ end
|
|||||||
--@param interface Network interface to use.
|
--@param interface Network interface to use.
|
||||||
--@param eigrp_raw Raw eigrp packet.
|
--@param eigrp_raw Raw eigrp packet.
|
||||||
local eigrpSend = function(interface, eigrp_raw)
|
local eigrpSend = function(interface, eigrp_raw)
|
||||||
local srcip = interface.address
|
local srcip = interface.address
|
||||||
local dstip = "224.0.0.10"
|
local dstip = "224.0.0.10"
|
||||||
|
|
||||||
local ip_raw = bin.pack("H", "45c00040ed780000015818bc0a00c8750a00c86b") .. eigrp_raw
|
local ip_raw = bin.pack("H", "45c00040ed780000015818bc0a00c8750a00c86b") .. eigrp_raw
|
||||||
local eigrp_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
local eigrp_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
||||||
eigrp_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
eigrp_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
||||||
eigrp_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
eigrp_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
||||||
eigrp_packet:ip_set_len(#eigrp_packet.buf)
|
eigrp_packet:ip_set_len(#eigrp_packet.buf)
|
||||||
eigrp_packet:ip_count_checksum()
|
eigrp_packet:ip_count_checksum()
|
||||||
|
|
||||||
local sock = nmap.new_dnet()
|
local sock = nmap.new_dnet()
|
||||||
sock:ethernet_open(interface.device)
|
sock:ethernet_open(interface.device)
|
||||||
-- Ethernet IPv4 multicast, our ethernet address and packet type IP
|
-- Ethernet IPv4 multicast, our ethernet address and packet type IP
|
||||||
local eth_hdr = bin.pack("HAH", "01 00 5e 00 00 0a", interface.mac, "08 00")
|
local eth_hdr = bin.pack("HAH", "01 00 5e 00 00 0a", interface.mac, "08 00")
|
||||||
sock:ethernet_send(eth_hdr .. eigrp_packet.buf)
|
sock:ethernet_send(eth_hdr .. eigrp_packet.buf)
|
||||||
sock:ethernet_close()
|
sock:ethernet_close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -116,48 +116,48 @@ end
|
|||||||
--@param timeout Ammount of time to listen for.
|
--@param timeout Ammount of time to listen for.
|
||||||
--@param responses Table to put valid responses into.
|
--@param responses Table to put valid responses into.
|
||||||
local eigrpListener = function(interface, timeout, responses)
|
local eigrpListener = function(interface, timeout, responses)
|
||||||
local condvar = nmap.condvar(responses)
|
local condvar = nmap.condvar(responses)
|
||||||
local routers = {}
|
local routers = {}
|
||||||
local status, l3data, response, p, eigrp_raw, _
|
local status, l3data, response, p, eigrp_raw, _
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
-- Filter for EIGRP packets that are sent either to us or to multicast
|
-- Filter for EIGRP packets that are sent either to us or to multicast
|
||||||
local filter = "ip proto 88 and (ip dst host " .. interface.address .. " or 224.0.0.10)"
|
local filter = "ip proto 88 and (ip dst host " .. interface.address .. " or 224.0.0.10)"
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
listener:set_timeout(500)
|
listener:set_timeout(500)
|
||||||
listener:pcap_open(interface.device, 1024, true, filter)
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
|
|
||||||
-- For each EIGRP packet captured until timeout
|
-- For each EIGRP packet captured until timeout
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
response = {}
|
response = {}
|
||||||
response.tlvs = {}
|
response.tlvs = {}
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
p = packet.Packet:new(l3data, #l3data)
|
p = packet.Packet:new(l3data, #l3data)
|
||||||
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||||
-- Check if it is an EIGRPv2 Update
|
-- Check if it is an EIGRPv2 Update
|
||||||
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x01 then
|
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x01 then
|
||||||
-- Skip if did get the info from this router before
|
-- Skip if did get the info from this router before
|
||||||
if not routers[p.ip_src] then
|
if not routers[p.ip_src] then
|
||||||
-- Parse header
|
-- Parse header
|
||||||
response = eigrp.EIGRP.parse(eigrp_raw)
|
response = eigrp.EIGRP.parse(eigrp_raw)
|
||||||
response.src = p.ip_src
|
response.src = p.ip_src
|
||||||
response.interface = interface.shortname
|
response.interface = interface.shortname
|
||||||
end
|
end
|
||||||
if response then
|
if response then
|
||||||
-- See, if it has routing information
|
-- See, if it has routing information
|
||||||
for _,tlv in pairs(response.tlvs) do
|
for _,tlv in pairs(response.tlvs) do
|
||||||
if eigrp.EIGRP.isRoutingTLV(tlv.type) then
|
if eigrp.EIGRP.isRoutingTLV(tlv.type) then
|
||||||
routers[p.ip_src] = true
|
routers[p.ip_src] = true
|
||||||
table.insert(responses, response)
|
table.insert(responses, response)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
return
|
condvar("signal")
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listens for EIGRP announcements to grab a valid Autonomous System value.
|
-- Listens for EIGRP announcements to grab a valid Autonomous System value.
|
||||||
@@ -165,176 +165,176 @@ end
|
|||||||
--@param timeout Max amount of time to listen for.
|
--@param timeout Max amount of time to listen for.
|
||||||
--@param astab Table to put result into.
|
--@param astab Table to put result into.
|
||||||
local asListener = function(interface, timeout, astab)
|
local asListener = function(interface, timeout, astab)
|
||||||
local condvar = nmap.condvar(astab)
|
local condvar = nmap.condvar(astab)
|
||||||
local status, l3data, p, eigrp_raw, eigrp_hello, _
|
local status, l3data, p, eigrp_raw, eigrp_hello, _
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
local filter = "ip proto 88 and ip dst host 224.0.0.10"
|
local filter = "ip proto 88 and ip dst host 224.0.0.10"
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
listener:set_timeout(500)
|
listener:set_timeout(500)
|
||||||
listener:pcap_open(interface.device, 1024, true, filter)
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
-- Check if another listener already found an A.S value.
|
-- Check if another listener already found an A.S value.
|
||||||
if #astab > 0 then break end
|
if #astab > 0 then break end
|
||||||
|
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
p = packet.Packet:new(l3data, #l3data)
|
p = packet.Packet:new(l3data, #l3data)
|
||||||
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||||
-- Listen for EIGRPv2 Hello packets
|
-- Listen for EIGRPv2 Hello packets
|
||||||
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x05 then
|
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x05 then
|
||||||
eigrp_hello = eigrp.EIGRP.parse(eigrp_raw)
|
eigrp_hello = eigrp.EIGRP.parse(eigrp_raw)
|
||||||
if eigrp_hello and eigrp_hello.as then
|
if eigrp_hello and eigrp_hello.as then
|
||||||
table.insert(astab, eigrp_hello.as)
|
table.insert(astab, eigrp_hello.as)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
-- Get script arguments
|
-- Get script arguments
|
||||||
local as = stdnse.get_script_args(SCRIPT_NAME .. ".as")
|
local as = stdnse.get_script_args(SCRIPT_NAME .. ".as")
|
||||||
local kparams = stdnse.get_script_args(SCRIPT_NAME .. ".kparams") or "101000"
|
local kparams = stdnse.get_script_args(SCRIPT_NAME .. ".kparams") or "101000"
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
local output, responses, interfaces, lthreads = {}, {}, {}, {}
|
local output, responses, interfaces, lthreads = {}, {}, {}, {}
|
||||||
local result, response, route, eigrp_hello, k
|
local result, response, route, eigrp_hello, k
|
||||||
local timeout = (timeout or 10) * 1000
|
local timeout = (timeout or 10) * 1000
|
||||||
|
|
||||||
-- K params should be of length 6
|
-- K params should be of length 6
|
||||||
-- Cisco routers ignore eigrp packets that don't have matching K parameters
|
-- Cisco routers ignore eigrp packets that don't have matching K parameters
|
||||||
if #kparams < 6 or #kparams > 6 then
|
if #kparams < 6 or #kparams > 6 then
|
||||||
return "\n ERROR: kparams should be of size 6."
|
return "\n ERROR: kparams should be of size 6."
|
||||||
else
|
else
|
||||||
k = {}
|
k = {}
|
||||||
k[1] = string.sub(kparams, 1,1)
|
k[1] = string.sub(kparams, 1,1)
|
||||||
k[2] = string.sub(kparams, 2,2)
|
k[2] = string.sub(kparams, 2,2)
|
||||||
k[3] = string.sub(kparams, 3,3)
|
k[3] = string.sub(kparams, 3,3)
|
||||||
k[4] = string.sub(kparams, 4,4)
|
k[4] = string.sub(kparams, 4,4)
|
||||||
k[5] = string.sub(kparams, 5,5)
|
k[5] = string.sub(kparams, 5,5)
|
||||||
k[6] = string.sub(kparams, 6)
|
k[6] = string.sub(kparams, 6)
|
||||||
|
end
|
||||||
|
|
||||||
|
interface = interface or nmap.get_interface()
|
||||||
|
if interface then
|
||||||
|
-- If an interface was provided, get its information
|
||||||
|
interface = nmap.get_interface_info(interface)
|
||||||
|
if not interface then
|
||||||
|
return ("\n ERROR: Failed to retreive %s interface information."):format(interface)
|
||||||
end
|
end
|
||||||
|
interfaces = {interface}
|
||||||
|
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, interface.shortname)
|
||||||
|
else
|
||||||
|
local ifacelist = nmap.list_interfaces()
|
||||||
|
for _, iface in ipairs(ifacelist) do
|
||||||
|
-- Match all ethernet interfaces
|
||||||
|
if iface.address and iface.link=="ethernet" and
|
||||||
|
iface.address:match("%d+%.%d+%.%d+%.%d+") then
|
||||||
|
|
||||||
interface = interface or nmap.get_interface()
|
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, iface.shortname)
|
||||||
if interface then
|
table.insert(interfaces, iface)
|
||||||
-- If an interface was provided, get its information
|
end
|
||||||
interface = nmap.get_interface_info(interface)
|
|
||||||
if not interface then
|
|
||||||
return ("\n ERROR: Failed to retreive %s interface information."):format(interface)
|
|
||||||
end
|
|
||||||
interfaces = {interface}
|
|
||||||
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, interface.shortname)
|
|
||||||
else
|
|
||||||
local ifacelist = nmap.list_interfaces()
|
|
||||||
for _, iface in ipairs(ifacelist) do
|
|
||||||
-- Match all ethernet interfaces
|
|
||||||
if iface.address and iface.link=="ethernet" and
|
|
||||||
iface.address:match("%d+%.%d+%.%d+%.%d+") then
|
|
||||||
|
|
||||||
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, iface.shortname)
|
|
||||||
table.insert(interfaces, iface)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- If user didn't provide an Autonomous System value, we listen fro multicast
|
-- If user didn't provide an Autonomous System value, we listen fro multicast
|
||||||
-- HELLO router announcements to get one.
|
-- HELLO router announcements to get one.
|
||||||
if not as then
|
if not as then
|
||||||
-- We use a table for condvar
|
-- We use a table for condvar
|
||||||
local astab = {}
|
local astab = {}
|
||||||
stdnse.print_debug("%s: No A.S value provided, will sniff for one.", SCRIPT_NAME)
|
stdnse.print_debug("%s: No A.S value provided, will sniff for one.", SCRIPT_NAME)
|
||||||
-- We should iterate over interfaces
|
-- We should iterate over interfaces
|
||||||
for _, interface in pairs(interfaces) do
|
|
||||||
local co = stdnse.new_thread(asListener, interface, timeout, astab)
|
|
||||||
lthreads[co] = true
|
|
||||||
end
|
|
||||||
local condvar = nmap.condvar(astab)
|
|
||||||
-- Wait for the listening threads to finish
|
|
||||||
repeat
|
|
||||||
for thread in pairs(lthreads) do
|
|
||||||
if coroutine.status(thread) == "dead" then lthreads[thread] = nil end
|
|
||||||
end
|
|
||||||
if ( next(lthreads) ) then
|
|
||||||
condvar("wait")
|
|
||||||
end
|
|
||||||
until next(lthreads) == nil;
|
|
||||||
|
|
||||||
if #astab > 0 then
|
|
||||||
stdnse.print_debug("Will use %s A.S value.", astab[1])
|
|
||||||
as = astab[1]
|
|
||||||
else
|
|
||||||
return "\n ERROR: Couldn't get an A.S value."
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Craft Hello packet
|
|
||||||
eigrp_hello = eigrp.EIGRP:new(eigrp.OPCODE.HELLO, as)
|
|
||||||
-- K params
|
|
||||||
eigrp_hello:addTLV({ type = eigrp.TLV.PARAM, k = k, htime = 15})
|
|
||||||
-- Software version
|
|
||||||
eigrp_hello:addTLV({ type = eigrp.TLV.SWVER, majv = 12, minv = 4, majtlv = 1, mintlv = 2})
|
|
||||||
|
|
||||||
-- On each interface, launch the listening thread and send the Hello packet.
|
|
||||||
lthreads = {}
|
|
||||||
for _, interface in pairs(interfaces) do
|
for _, interface in pairs(interfaces) do
|
||||||
local co = stdnse.new_thread(eigrpListener, interface, timeout, responses)
|
local co = stdnse.new_thread(asListener, interface, timeout, astab)
|
||||||
-- We insert a small delay before sending the Hello so the listening
|
lthreads[co] = true
|
||||||
-- thread doesn't miss updates.
|
|
||||||
stdnse.sleep(0.5)
|
|
||||||
lthreads[co] = true
|
|
||||||
eigrpSend(interface, tostring(eigrp_hello))
|
|
||||||
end
|
end
|
||||||
|
local condvar = nmap.condvar(astab)
|
||||||
local condvar = nmap.condvar(responses)
|
|
||||||
-- Wait for the listening threads to finish
|
-- Wait for the listening threads to finish
|
||||||
repeat
|
repeat
|
||||||
condvar("wait")
|
for thread in pairs(lthreads) do
|
||||||
for thread in pairs(lthreads) do
|
if coroutine.status(thread) == "dead" then lthreads[thread] = nil end
|
||||||
if coroutine.status(thread) == "dead" then lthreads[thread] = nil end
|
end
|
||||||
end
|
if ( next(lthreads) ) then
|
||||||
|
condvar("wait")
|
||||||
|
end
|
||||||
until next(lthreads) == nil;
|
until next(lthreads) == nil;
|
||||||
|
|
||||||
-- Output the useful info from the responses
|
if #astab > 0 then
|
||||||
if #responses > 0 then
|
stdnse.print_debug("Will use %s A.S value.", astab[1])
|
||||||
for _, response in pairs(responses) do
|
as = astab[1]
|
||||||
result = {}
|
else
|
||||||
result.name = response.src
|
return "\n ERROR: Couldn't get an A.S value."
|
||||||
if target.ALLOW_NEW_TARGETS then target.add(response.src) end
|
|
||||||
table.insert(result, "Interface: " .. response.interface)
|
|
||||||
table.insert(result, ("A.S: %d"):format(response.as))
|
|
||||||
table.insert(result, ("Virtual Router ID: %d"):format(response.routerid))
|
|
||||||
-- Output routes information TLVs
|
|
||||||
for _, tlv in pairs(response.tlvs) do
|
|
||||||
route = {}
|
|
||||||
-- We are only interested in Internal or external routes
|
|
||||||
if tlv.type == eigrp.TLV.EXT then
|
|
||||||
route.name = "External route"
|
|
||||||
for name, value in pairs(eigrp.EXT_PROTO) do
|
|
||||||
if value == tlv.eproto then
|
|
||||||
table.insert(route, ("Protocol: %s"):format(name))
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(route, ("Originating A.S: %s"):format(tlv.oas))
|
|
||||||
table.insert(route, ("Originating Router ID: %s"):format(tlv.orouterid))
|
|
||||||
if target.ALLOW_NEW_TARGETS then target.add(tlv.orouterid) end
|
|
||||||
table.insert(route, ("Destination: %s/%d"):format(tlv.dst, tlv.mask))
|
|
||||||
table.insert(route, ("Next hop: %s"):format(tlv.nexth))
|
|
||||||
table.insert(result, route)
|
|
||||||
elseif tlv.type == eigrp.TLV.INT then
|
|
||||||
route.name = "Internal route"
|
|
||||||
table.insert(route, ("Destination: %s/%d"):format(tlv.dst, tlv.mask))
|
|
||||||
table.insert(route, ("Next hop: %s"):format(tlv.nexth))
|
|
||||||
table.insert(result, route)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(output, result)
|
|
||||||
end
|
|
||||||
if #output>0 and not target.ALLOW_NEW_TARGETS then
|
|
||||||
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
|
||||||
end
|
|
||||||
return stdnse.format_output(true, output)
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Craft Hello packet
|
||||||
|
eigrp_hello = eigrp.EIGRP:new(eigrp.OPCODE.HELLO, as)
|
||||||
|
-- K params
|
||||||
|
eigrp_hello:addTLV({ type = eigrp.TLV.PARAM, k = k, htime = 15})
|
||||||
|
-- Software version
|
||||||
|
eigrp_hello:addTLV({ type = eigrp.TLV.SWVER, majv = 12, minv = 4, majtlv = 1, mintlv = 2})
|
||||||
|
|
||||||
|
-- On each interface, launch the listening thread and send the Hello packet.
|
||||||
|
lthreads = {}
|
||||||
|
for _, interface in pairs(interfaces) do
|
||||||
|
local co = stdnse.new_thread(eigrpListener, interface, timeout, responses)
|
||||||
|
-- We insert a small delay before sending the Hello so the listening
|
||||||
|
-- thread doesn't miss updates.
|
||||||
|
stdnse.sleep(0.5)
|
||||||
|
lthreads[co] = true
|
||||||
|
eigrpSend(interface, tostring(eigrp_hello))
|
||||||
|
end
|
||||||
|
|
||||||
|
local condvar = nmap.condvar(responses)
|
||||||
|
-- Wait for the listening threads to finish
|
||||||
|
repeat
|
||||||
|
condvar("wait")
|
||||||
|
for thread in pairs(lthreads) do
|
||||||
|
if coroutine.status(thread) == "dead" then lthreads[thread] = nil end
|
||||||
|
end
|
||||||
|
until next(lthreads) == nil;
|
||||||
|
|
||||||
|
-- Output the useful info from the responses
|
||||||
|
if #responses > 0 then
|
||||||
|
for _, response in pairs(responses) do
|
||||||
|
result = {}
|
||||||
|
result.name = response.src
|
||||||
|
if target.ALLOW_NEW_TARGETS then target.add(response.src) end
|
||||||
|
table.insert(result, "Interface: " .. response.interface)
|
||||||
|
table.insert(result, ("A.S: %d"):format(response.as))
|
||||||
|
table.insert(result, ("Virtual Router ID: %d"):format(response.routerid))
|
||||||
|
-- Output routes information TLVs
|
||||||
|
for _, tlv in pairs(response.tlvs) do
|
||||||
|
route = {}
|
||||||
|
-- We are only interested in Internal or external routes
|
||||||
|
if tlv.type == eigrp.TLV.EXT then
|
||||||
|
route.name = "External route"
|
||||||
|
for name, value in pairs(eigrp.EXT_PROTO) do
|
||||||
|
if value == tlv.eproto then
|
||||||
|
table.insert(route, ("Protocol: %s"):format(name))
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(route, ("Originating A.S: %s"):format(tlv.oas))
|
||||||
|
table.insert(route, ("Originating Router ID: %s"):format(tlv.orouterid))
|
||||||
|
if target.ALLOW_NEW_TARGETS then target.add(tlv.orouterid) end
|
||||||
|
table.insert(route, ("Destination: %s/%d"):format(tlv.dst, tlv.mask))
|
||||||
|
table.insert(route, ("Next hop: %s"):format(tlv.nexth))
|
||||||
|
table.insert(result, route)
|
||||||
|
elseif tlv.type == eigrp.TLV.INT then
|
||||||
|
route.name = "Internal route"
|
||||||
|
table.insert(route, ("Destination: %s/%d"):format(tlv.dst, tlv.mask))
|
||||||
|
table.insert(route, ("Next hop: %s"):format(tlv.nexth))
|
||||||
|
table.insert(result, route)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(output, result)
|
||||||
|
end
|
||||||
|
if #output>0 and not target.ALLOW_NEW_TARGETS then
|
||||||
|
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
||||||
|
end
|
||||||
|
return stdnse.format_output(true, output)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -88,15 +88,15 @@ interfaces.
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if ( not(nmap.is_privileged()) ) then
|
if ( not(nmap.is_privileged()) ) then
|
||||||
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
author = "Hani Benhabiles"
|
author = "Hani Benhabiles"
|
||||||
@@ -109,54 +109,54 @@ categories = {"discovery", "safe", "broadcast"}
|
|||||||
-- @param data string IGMP Raw packet.
|
-- @param data string IGMP Raw packet.
|
||||||
-- @return response table Structured igmp packet.
|
-- @return response table Structured igmp packet.
|
||||||
local igmpParse = function(data)
|
local igmpParse = function(data)
|
||||||
local index
|
local index
|
||||||
local response = {}
|
local response = {}
|
||||||
local group, source
|
local group, source
|
||||||
-- Report type (0x12 == v1, 0x16 == v2, 0x22 == v3)
|
-- Report type (0x12 == v1, 0x16 == v2, 0x22 == v3)
|
||||||
index, response.type = bin.unpack(">C", data, index)
|
index, response.type = bin.unpack(">C", data, index)
|
||||||
if response.type == 0x12 or response.type == 0x16 then
|
if response.type == 0x12 or response.type == 0x16 then
|
||||||
-- Max response time
|
-- Max response time
|
||||||
index, response.maxrt = bin.unpack(">C", data, index)
|
index, response.maxrt = bin.unpack(">C", data, index)
|
||||||
-- Checksum
|
-- Checksum
|
||||||
index, response.checksum = bin.unpack(">S", data, index)
|
index, response.checksum = bin.unpack(">S", data, index)
|
||||||
-- Multicast group
|
-- Multicast group
|
||||||
index, response.group = bin.unpack("<I", data, index)
|
index, response.group = bin.unpack("<I", data, index)
|
||||||
response.group = ipOps.fromdword(response.group)
|
response.group = ipOps.fromdword(response.group)
|
||||||
return response
|
return response
|
||||||
elseif response.type == 0x22 and #data >= 12 then
|
elseif response.type == 0x22 and #data >= 12 then
|
||||||
-- Skip reserved byte
|
-- Skip reserved byte
|
||||||
index = index + 1
|
index = index + 1
|
||||||
-- Checksum
|
-- Checksum
|
||||||
index, response.checksum = bin.unpack(">S", data, index)
|
index, response.checksum = bin.unpack(">S", data, index)
|
||||||
-- Skip reserved byte
|
-- Skip reserved byte
|
||||||
index = index + 2
|
index = index + 2
|
||||||
-- Number of groups
|
-- Number of groups
|
||||||
index, response.ngroups = bin.unpack(">S", data, index)
|
index, response.ngroups = bin.unpack(">S", data, index)
|
||||||
response.groups = {}
|
response.groups = {}
|
||||||
for i=1,response.ngroups do
|
for i=1,response.ngroups do
|
||||||
group = {}
|
group = {}
|
||||||
-- Mode is either INCLUDE or EXCLUDE
|
-- Mode is either INCLUDE or EXCLUDE
|
||||||
index, group.mode = bin.unpack(">C", data, index)
|
index, group.mode = bin.unpack(">C", data, index)
|
||||||
-- Auxiliary data length in the group record (in 32bits units)
|
-- Auxiliary data length in the group record (in 32bits units)
|
||||||
index, group.auxdlen = bin.unpack(">C", data, index)
|
index, group.auxdlen = bin.unpack(">C", data, index)
|
||||||
-- Number of source addresses
|
-- Number of source addresses
|
||||||
index, group.nsrc = bin.unpack(">S", data, index)
|
index, group.nsrc = bin.unpack(">S", data, index)
|
||||||
index, group.address = bin.unpack("<I", data, index)
|
index, group.address = bin.unpack("<I", data, index)
|
||||||
group.address = ipOps.fromdword(group.address)
|
group.address = ipOps.fromdword(group.address)
|
||||||
group.src = {}
|
group.src = {}
|
||||||
if group.nsrc > 0 then
|
if group.nsrc > 0 then
|
||||||
for i=1,group.nsrc do
|
for i=1,group.nsrc do
|
||||||
index, source = bin.unpack("<I", data, index)
|
index, source = bin.unpack("<I", data, index)
|
||||||
table.insert(group.src, ipOps.fromdword(source))
|
table.insert(group.src, ipOps.fromdword(source))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Skip auxiliary data
|
-- Skip auxiliary data
|
||||||
index = index + group.auxdlen
|
index = index + group.auxdlen
|
||||||
-- Insert group
|
-- Insert group
|
||||||
table.insert(response.groups, group)
|
table.insert(response.groups, group)
|
||||||
end
|
|
||||||
return response
|
|
||||||
end
|
end
|
||||||
|
return response
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Listens for IGMP Membership reports packets.
|
--- Listens for IGMP Membership reports packets.
|
||||||
@@ -164,42 +164,42 @@ end
|
|||||||
-- @param timeout Amount of time to listen for.
|
-- @param timeout Amount of time to listen for.
|
||||||
-- @param responses table to put valid responses into.
|
-- @param responses table to put valid responses into.
|
||||||
local igmpListener = function(interface, timeout, responses)
|
local igmpListener = function(interface, timeout, responses)
|
||||||
local condvar = nmap.condvar(responses)
|
local condvar = nmap.condvar(responses)
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
local p, igmp_raw, status, l3data, response, _
|
local p, igmp_raw, status, l3data, response, _
|
||||||
local devices = {}
|
local devices = {}
|
||||||
listener:set_timeout(100)
|
listener:set_timeout(100)
|
||||||
listener:pcap_open(interface.device, 1024, true, 'ip proto 2')
|
listener:pcap_open(interface.device, 1024, true, 'ip proto 2')
|
||||||
|
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
p = packet.Packet:new(l3data, #l3data)
|
p = packet.Packet:new(l3data, #l3data)
|
||||||
igmp_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
igmp_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||||
if p then
|
if p then
|
||||||
-- check the first byte before sending to the parser
|
-- check the first byte before sending to the parser
|
||||||
-- response 0x12 == Membership Response version 1
|
-- response 0x12 == Membership Response version 1
|
||||||
-- response 0x16 == Membership Response version 2
|
-- response 0x16 == Membership Response version 2
|
||||||
-- response 0x22 == Membership Response version 3
|
-- response 0x22 == Membership Response version 3
|
||||||
local igmptype = igmp_raw:byte(1)
|
local igmptype = igmp_raw:byte(1)
|
||||||
if igmptype == 0x12 or igmptype == 0x16 or igmptype == 0x22 then
|
if igmptype == 0x12 or igmptype == 0x16 or igmptype == 0x22 then
|
||||||
response = igmpParse(igmp_raw)
|
response = igmpParse(igmp_raw)
|
||||||
if response then
|
if response then
|
||||||
response.src = p.ip_src
|
response.src = p.ip_src
|
||||||
response.interface = interface.shortname
|
response.interface = interface.shortname
|
||||||
-- Many hosts return more than one same response message
|
-- Many hosts return more than one same response message
|
||||||
-- this is to not output duplicates
|
-- this is to not output duplicates
|
||||||
if not devices[response.src..response.type..(response.group or response.ngroups)] then
|
if not devices[response.src..response.type..(response.group or response.ngroups)] then
|
||||||
devices[response.src..response.type..(response.group or response.ngroups)] = true
|
devices[response.src..response.type..(response.group or response.ngroups)] = true
|
||||||
table.insert(responses, response)
|
table.insert(responses, response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Crafts a raw IGMP packet.
|
--- Crafts a raw IGMP packet.
|
||||||
@@ -207,40 +207,40 @@ end
|
|||||||
-- @param vesion IGMP version. Could be 1, 2 or 3.
|
-- @param vesion IGMP version. Could be 1, 2 or 3.
|
||||||
-- @return string Raw IGMP packet.
|
-- @return string Raw IGMP packet.
|
||||||
local igmpRaw = function(interface, version)
|
local igmpRaw = function(interface, version)
|
||||||
-- Only 1, 2 and 3 are valid IGMP versions
|
-- Only 1, 2 and 3 are valid IGMP versions
|
||||||
if version ~= 1 and version ~= 2 and version ~= 3 then
|
if version ~= 1 and version ~= 2 and version ~= 3 then
|
||||||
stdnse.print_debug("IGMP version %s doesn't exist.", version)
|
stdnse.print_debug("IGMP version %s doesn't exist.", version)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Let's craft an IGMP Membership Query
|
-- Let's craft an IGMP Membership Query
|
||||||
local igmp_raw = bin.pack(">C", 0x11) -- Membership Query, same for all versions
|
local igmp_raw = bin.pack(">C", 0x11) -- Membership Query, same for all versions
|
||||||
if version == 1 then
|
if version == 1 then
|
||||||
igmp_raw = igmp_raw .. bin.pack(">C", 0x00) -- Unused, 0x00 for version 1 only
|
igmp_raw = igmp_raw .. bin.pack(">C", 0x00) -- Unused, 0x00 for version 1 only
|
||||||
else
|
else
|
||||||
igmp_raw = igmp_raw .. bin.pack(">C", 0x16) -- Max response time: 10 Seconds, for version 2 and 3
|
igmp_raw = igmp_raw .. bin.pack(">C", 0x16) -- Max response time: 10 Seconds, for version 2 and 3
|
||||||
end
|
end
|
||||||
|
|
||||||
igmp_raw = igmp_raw .. bin.pack(">S", 0x00) -- Checksum, calculated later
|
igmp_raw = igmp_raw .. bin.pack(">S", 0x00) -- Checksum, calculated later
|
||||||
igmp_raw = igmp_raw .. bin.pack(">I", 0x00) -- Multicast Address: 0.0.0.0
|
igmp_raw = igmp_raw .. bin.pack(">I", 0x00) -- Multicast Address: 0.0.0.0
|
||||||
|
|
||||||
if version == 3 then
|
if version == 3 then
|
||||||
-- Reserved = 4 bits (Should be zeroed)
|
-- Reserved = 4 bits (Should be zeroed)
|
||||||
-- Supress Flag = 1 bit
|
-- Supress Flag = 1 bit
|
||||||
-- QRV (Querier's Robustness Variable) = 3 bits
|
-- QRV (Querier's Robustness Variable) = 3 bits
|
||||||
-- all are set to 0
|
-- all are set to 0
|
||||||
igmp_raw = igmp_raw .. bin.pack(">C", 0x00)
|
igmp_raw = igmp_raw .. bin.pack(">C", 0x00)
|
||||||
-- QQIC (Querier's Query Interval Code) in seconds = Set to 0 to get insta replies.
|
-- QQIC (Querier's Query Interval Code) in seconds = Set to 0 to get insta replies.
|
||||||
igmp_raw = igmp_raw .. bin.pack(">C", 0x10)
|
igmp_raw = igmp_raw .. bin.pack(">C", 0x10)
|
||||||
-- Number of sources (in the next arrays) = 1 ( Our IP only)
|
-- Number of sources (in the next arrays) = 1 ( Our IP only)
|
||||||
igmp_raw = igmp_raw .. bin.pack(">S", 0x01)
|
igmp_raw = igmp_raw .. bin.pack(">S", 0x01)
|
||||||
-- Source = Our IP address
|
-- Source = Our IP address
|
||||||
igmp_raw = igmp_raw .. bin.pack(">I", ipOps.todword(interface.address))
|
igmp_raw = igmp_raw .. bin.pack(">I", ipOps.todword(interface.address))
|
||||||
end
|
end
|
||||||
|
|
||||||
igmp_raw = igmp_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(igmp_raw)) .. igmp_raw:sub(5)
|
igmp_raw = igmp_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(igmp_raw)) .. igmp_raw:sub(5)
|
||||||
|
|
||||||
return igmp_raw
|
return igmp_raw
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -249,181 +249,181 @@ local igmpQuery;
|
|||||||
-- @param interface Network interface to send on.
|
-- @param interface Network interface to send on.
|
||||||
-- @param vesion IGMP version. Could be 1, 2, 3 or all.
|
-- @param vesion IGMP version. Could be 1, 2, 3 or all.
|
||||||
igmpQuery = function(interface, version)
|
igmpQuery = function(interface, version)
|
||||||
local srcip = interface.address
|
local srcip = interface.address
|
||||||
local dstip = "224.0.0.1"
|
local dstip = "224.0.0.1"
|
||||||
|
|
||||||
if version == 'all' then
|
if version == 'all' then
|
||||||
-- Small pause to let listener begin and not miss reports.
|
-- Small pause to let listener begin and not miss reports.
|
||||||
stdnse.sleep(0.5)
|
stdnse.sleep(0.5)
|
||||||
igmpQuery(interface, 3)
|
igmpQuery(interface, 3)
|
||||||
igmpQuery(interface, 2)
|
igmpQuery(interface, 2)
|
||||||
igmpQuery(interface, 1)
|
igmpQuery(interface, 1)
|
||||||
else
|
else
|
||||||
local igmp_raw = igmpRaw(interface, version)
|
local igmp_raw = igmpRaw(interface, version)
|
||||||
|
|
||||||
local ip_raw = bin.pack("H", "45c00040ed780000010218bc0a00c8750a00c86b") .. igmp_raw
|
local ip_raw = bin.pack("H", "45c00040ed780000010218bc0a00c8750a00c86b") .. igmp_raw
|
||||||
local igmp_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
local igmp_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
||||||
igmp_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
igmp_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
||||||
igmp_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
igmp_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
||||||
igmp_packet:ip_set_len(#igmp_packet.buf)
|
igmp_packet:ip_set_len(#igmp_packet.buf)
|
||||||
igmp_packet:ip_count_checksum()
|
igmp_packet:ip_count_checksum()
|
||||||
|
|
||||||
local sock = nmap.new_dnet()
|
local sock = nmap.new_dnet()
|
||||||
sock:ethernet_open(interface.device)
|
sock:ethernet_open(interface.device)
|
||||||
|
|
||||||
-- Ethernet IPv4 multicast, our ethernet address and type IP
|
-- Ethernet IPv4 multicast, our ethernet address and type IP
|
||||||
local eth_hdr = bin.pack("HAH", "01 00 5e 00 00 01", interface.mac, "08 00")
|
local eth_hdr = bin.pack("HAH", "01 00 5e 00 00 01", interface.mac, "08 00")
|
||||||
sock:ethernet_send(eth_hdr .. igmp_packet.buf)
|
sock:ethernet_send(eth_hdr .. igmp_packet.buf)
|
||||||
sock:ethernet_close()
|
sock:ethernet_close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Function to compare wieght of an IGMP response message.
|
-- Function to compare wieght of an IGMP response message.
|
||||||
-- Used to sort elements in responses table.
|
-- Used to sort elements in responses table.
|
||||||
local respCompare = function(a,b)
|
local respCompare = function(a,b)
|
||||||
return ipOps.todword(a.src) + a.type + (a.ngroups or ipOps.todword(a.group))
|
return ipOps.todword(a.src) + a.type + (a.ngroups or ipOps.todword(a.group))
|
||||||
< ipOps.todword(b.src) + b.type + (b.ngroups or ipOps.todword(b.group))
|
< ipOps.todword(b.src) + b.type + (b.ngroups or ipOps.todword(b.group))
|
||||||
end
|
end
|
||||||
|
|
||||||
local mgroup_names_fetch = function(filename)
|
local mgroup_names_fetch = function(filename)
|
||||||
local groupnames_db = {}
|
local groupnames_db = {}
|
||||||
|
|
||||||
local file = io.open(filename, "r")
|
local file = io.open(filename, "r")
|
||||||
if not file then
|
if not file then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
for l in file:lines() do
|
for l in file:lines() do
|
||||||
groupnames_db[#groupnames_db + 1] = stdnse.strsplit("\t", l)
|
groupnames_db[#groupnames_db + 1] = stdnse.strsplit("\t", l)
|
||||||
end
|
end
|
||||||
|
|
||||||
file:close()
|
file:close()
|
||||||
return groupnames_db
|
return groupnames_db
|
||||||
end
|
end
|
||||||
|
|
||||||
local mgroup_name_identify = function(db, ip)
|
local mgroup_name_identify = function(db, ip)
|
||||||
--stdnse.print_debug("%s: '%s'", SCRIPT_NAME, ip)
|
--stdnse.print_debug("%s: '%s'", SCRIPT_NAME, ip)
|
||||||
for _, mg in ipairs(db) do
|
for _, mg in ipairs(db) do
|
||||||
local ip1 = mg[1]
|
local ip1 = mg[1]
|
||||||
local ip2 = mg[2]
|
local ip2 = mg[2]
|
||||||
local desc = mg[3]
|
local desc = mg[3]
|
||||||
--stdnse.print_debug("%s: try: %s <= %s <= %s (%s)", SCRIPT_NAME, ip1, ip, ip2, desc)
|
--stdnse.print_debug("%s: try: %s <= %s <= %s (%s)", SCRIPT_NAME, ip1, ip, ip2, desc)
|
||||||
if (not ipOps.compare_ip(ip, "lt", ip1) and not ipOps.compare_ip(ip2, "lt", ip)) then
|
if (not ipOps.compare_ip(ip, "lt", ip1) and not ipOps.compare_ip(ip2, "lt", ip)) then
|
||||||
--stdnse.print_debug("%s: found! %s <= %s <= %s (%s)", SCRIPT_NAME, ip1, ip, ip2, desc)
|
--stdnse.print_debug("%s: found! %s <= %s <= %s (%s)", SCRIPT_NAME, ip1, ip, ip2, desc)
|
||||||
return desc
|
return desc
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return false
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
local version = stdnse.get_script_args(SCRIPT_NAME .. ".version") or 2
|
local version = stdnse.get_script_args(SCRIPT_NAME .. ".version") or 2
|
||||||
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
timeout = (timeout or 7) * 1000
|
timeout = (timeout or 7) * 1000
|
||||||
if version ~= 'all' then
|
if version ~= 'all' then
|
||||||
version = tonumber(version)
|
version = tonumber(version)
|
||||||
|
end
|
||||||
|
|
||||||
|
local responses, results, interfaces, lthreads = {}, {}, {}, {}
|
||||||
|
local result, grouptable, sourcetable
|
||||||
|
|
||||||
|
local group_names_fname = stdnse.get_script_args(SCRIPT_NAME .. ".mgroupnamesdb") or
|
||||||
|
nmap.fetchfile("nselib/data/mgroupnames.db")
|
||||||
|
local mg_names_db = group_names_fname and mgroup_names_fetch(group_names_fname)
|
||||||
|
|
||||||
|
-- Check the interface
|
||||||
|
interface = interface or nmap.get_interface()
|
||||||
|
if interface then
|
||||||
|
-- Get the interface information
|
||||||
|
interface = nmap.get_interface_info(interface)
|
||||||
|
if not interface then
|
||||||
|
return ("ERROR: Failed to retreive %s interface information."):format(interface)
|
||||||
end
|
end
|
||||||
|
interfaces = {interface}
|
||||||
|
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, interface.shortname)
|
||||||
|
else
|
||||||
|
local ifacelist = nmap.list_interfaces()
|
||||||
|
for _, iface in ipairs(ifacelist) do
|
||||||
|
-- Match all ethernet interfaces
|
||||||
|
if iface.address and iface.link=="ethernet" and
|
||||||
|
iface.address:match("%d+%.%d+%.%d+%.%d+") then
|
||||||
|
|
||||||
local responses, results, interfaces, lthreads = {}, {}, {}, {}
|
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, iface.shortname)
|
||||||
local result, grouptable, sourcetable
|
table.insert(interfaces, iface)
|
||||||
|
end
|
||||||
local group_names_fname = stdnse.get_script_args(SCRIPT_NAME .. ".mgroupnamesdb") or
|
|
||||||
nmap.fetchfile("nselib/data/mgroupnames.db")
|
|
||||||
local mg_names_db = group_names_fname and mgroup_names_fetch(group_names_fname)
|
|
||||||
|
|
||||||
-- Check the interface
|
|
||||||
interface = interface or nmap.get_interface()
|
|
||||||
if interface then
|
|
||||||
-- Get the interface information
|
|
||||||
interface = nmap.get_interface_info(interface)
|
|
||||||
if not interface then
|
|
||||||
return ("ERROR: Failed to retreive %s interface information."):format(interface)
|
|
||||||
end
|
|
||||||
interfaces = {interface}
|
|
||||||
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, interface.shortname)
|
|
||||||
else
|
|
||||||
local ifacelist = nmap.list_interfaces()
|
|
||||||
for _, iface in ipairs(ifacelist) do
|
|
||||||
-- Match all ethernet interfaces
|
|
||||||
if iface.address and iface.link=="ethernet" and
|
|
||||||
iface.address:match("%d+%.%d+%.%d+%.%d+") then
|
|
||||||
|
|
||||||
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, iface.shortname)
|
|
||||||
table.insert(interfaces, iface)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
-- We should iterate over interfaces
|
-- We should iterate over interfaces
|
||||||
for _, interface in pairs(interfaces) do
|
for _, interface in pairs(interfaces) do
|
||||||
local co = stdnse.new_thread(igmpListener, interface, timeout, responses)
|
local co = stdnse.new_thread(igmpListener, interface, timeout, responses)
|
||||||
igmpQuery(interface, version)
|
igmpQuery(interface, version)
|
||||||
lthreads[co] = true
|
lthreads[co] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local condvar = nmap.condvar(responses)
|
||||||
|
-- Wait for the listening threads to finish
|
||||||
|
repeat
|
||||||
|
for thread in pairs(lthreads) do
|
||||||
|
if coroutine.status(thread) == "dead" then lthreads[thread] = nil end
|
||||||
end
|
end
|
||||||
|
if ( next(lthreads) ) then
|
||||||
local condvar = nmap.condvar(responses)
|
condvar("wait")
|
||||||
-- Wait for the listening threads to finish
|
|
||||||
repeat
|
|
||||||
for thread in pairs(lthreads) do
|
|
||||||
if coroutine.status(thread) == "dead" then lthreads[thread] = nil end
|
|
||||||
end
|
|
||||||
if ( next(lthreads) ) then
|
|
||||||
condvar("wait")
|
|
||||||
end
|
|
||||||
until next(lthreads) == nil;
|
|
||||||
|
|
||||||
-- Output useful info from the responses
|
|
||||||
if #responses > 0 then
|
|
||||||
-- We should sort our list here.
|
|
||||||
-- This is useful to have consistent results for tools such as Ndiff.
|
|
||||||
table.sort(responses, respCompare)
|
|
||||||
|
|
||||||
for _, response in pairs(responses) do
|
|
||||||
result = {}
|
|
||||||
result.name = response.src
|
|
||||||
table.insert(result, "Interface: " .. response.interface)
|
|
||||||
-- Add to new targets if newtargets script arg provided
|
|
||||||
if target.ALLOW_NEW_TARGETS then target.add(response.src) end
|
|
||||||
if response.type == 0x12 then
|
|
||||||
table.insert(result, "Version: 1")
|
|
||||||
table.insert(result, "Multicast group: ".. response.group)
|
|
||||||
elseif response.type == 0x16 then
|
|
||||||
table.insert(result, "Version: 2")
|
|
||||||
table.insert(result, "Group: ".. response.group)
|
|
||||||
local mg_desc = mgroup_name_identify(mg_names_db, response.group)
|
|
||||||
if mg_desc then
|
|
||||||
table.insert(result, "Description: ".. mg_desc)
|
|
||||||
end
|
|
||||||
elseif response.type == 0x22 then
|
|
||||||
table.insert(result, "Version: 3")
|
|
||||||
for _, group in pairs(response.groups) do
|
|
||||||
grouptable = {}
|
|
||||||
grouptable.name = "Group: " .. group.address
|
|
||||||
if group.mode == 0x01 then
|
|
||||||
table.insert(grouptable, "Mode: INCLUDE")
|
|
||||||
elseif group.mode == 0x02 then
|
|
||||||
table.insert(grouptable, "Mode: EXCLUDE")
|
|
||||||
end
|
|
||||||
local mg_desc = mgroup_name_identify(mg_names_db, group.address)
|
|
||||||
if mg_desc then
|
|
||||||
table.insert(grouptable, "Description: ".. mg_desc)
|
|
||||||
end
|
|
||||||
if group.nsrc > 0 then
|
|
||||||
sourcetable = {}
|
|
||||||
sourcetable.name = "Sources:"
|
|
||||||
table.insert(sourcetable, group.src)
|
|
||||||
table.insert(grouptable, sourcetable)
|
|
||||||
end
|
|
||||||
table.insert(result, grouptable)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.insert(results, result)
|
|
||||||
end
|
|
||||||
if #results>0 and not target.ALLOW_NEW_TARGETS then
|
|
||||||
table.insert(results,"Use the newtargets script-arg to add the results as targets")
|
|
||||||
end
|
|
||||||
return stdnse.format_output(true, results)
|
|
||||||
end
|
end
|
||||||
|
until next(lthreads) == nil;
|
||||||
|
|
||||||
|
-- Output useful info from the responses
|
||||||
|
if #responses > 0 then
|
||||||
|
-- We should sort our list here.
|
||||||
|
-- This is useful to have consistent results for tools such as Ndiff.
|
||||||
|
table.sort(responses, respCompare)
|
||||||
|
|
||||||
|
for _, response in pairs(responses) do
|
||||||
|
result = {}
|
||||||
|
result.name = response.src
|
||||||
|
table.insert(result, "Interface: " .. response.interface)
|
||||||
|
-- Add to new targets if newtargets script arg provided
|
||||||
|
if target.ALLOW_NEW_TARGETS then target.add(response.src) end
|
||||||
|
if response.type == 0x12 then
|
||||||
|
table.insert(result, "Version: 1")
|
||||||
|
table.insert(result, "Multicast group: ".. response.group)
|
||||||
|
elseif response.type == 0x16 then
|
||||||
|
table.insert(result, "Version: 2")
|
||||||
|
table.insert(result, "Group: ".. response.group)
|
||||||
|
local mg_desc = mgroup_name_identify(mg_names_db, response.group)
|
||||||
|
if mg_desc then
|
||||||
|
table.insert(result, "Description: ".. mg_desc)
|
||||||
|
end
|
||||||
|
elseif response.type == 0x22 then
|
||||||
|
table.insert(result, "Version: 3")
|
||||||
|
for _, group in pairs(response.groups) do
|
||||||
|
grouptable = {}
|
||||||
|
grouptable.name = "Group: " .. group.address
|
||||||
|
if group.mode == 0x01 then
|
||||||
|
table.insert(grouptable, "Mode: INCLUDE")
|
||||||
|
elseif group.mode == 0x02 then
|
||||||
|
table.insert(grouptable, "Mode: EXCLUDE")
|
||||||
|
end
|
||||||
|
local mg_desc = mgroup_name_identify(mg_names_db, group.address)
|
||||||
|
if mg_desc then
|
||||||
|
table.insert(grouptable, "Description: ".. mg_desc)
|
||||||
|
end
|
||||||
|
if group.nsrc > 0 then
|
||||||
|
sourcetable = {}
|
||||||
|
sourcetable.name = "Sources:"
|
||||||
|
table.insert(sourcetable, group.src)
|
||||||
|
table.insert(grouptable, sourcetable)
|
||||||
|
end
|
||||||
|
table.insert(result, grouptable)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(results, result)
|
||||||
|
end
|
||||||
|
if #results>0 and not target.ALLOW_NEW_TARGETS then
|
||||||
|
table.insert(results,"Use the newtargets script-arg to add the results as targets")
|
||||||
|
end
|
||||||
|
return stdnse.format_output(true, results)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -70,11 +70,11 @@ unless a specific interface was given using the -e argument to Nmap.
|
|||||||
-- Version 0.1
|
-- Version 0.1
|
||||||
-- Created 07/02/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 07/02/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 07/25/2011 - v0.2 -
|
-- Revised 07/25/2011 - v0.2 -
|
||||||
-- * added more documentation
|
-- * added more documentation
|
||||||
-- * added getInterfaces code to detect available
|
-- * added getInterfaces code to detect available
|
||||||
-- interfaces.
|
-- interfaces.
|
||||||
-- * corrected bug that would fail to load
|
-- * corrected bug that would fail to load
|
||||||
-- decoders if not in a relative directory.
|
-- decoders if not in a relative directory.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -86,11 +86,11 @@ categories = {"broadcast", "safe"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -101,26 +101,26 @@ end
|
|||||||
-- @return decoders table of decoder functions on success
|
-- @return decoders table of decoder functions on success
|
||||||
-- @return err string containing the error message on failure
|
-- @return err string containing the error message on failure
|
||||||
loadDecoders = function(fname)
|
loadDecoders = function(fname)
|
||||||
-- resolve the full, absolute, path
|
-- resolve the full, absolute, path
|
||||||
local abs_fname = nmap.fetchfile(fname)
|
local abs_fname = nmap.fetchfile(fname)
|
||||||
|
|
||||||
if ( not(abs_fname) ) then
|
if ( not(abs_fname) ) then
|
||||||
return false, ("ERROR: Failed to load decoder definition (%s)"):format(fname)
|
return false, ("ERROR: Failed to load decoder definition (%s)"):format(fname)
|
||||||
end
|
end
|
||||||
|
|
||||||
local env = setmetatable({Decoders = {}}, {__index = _G});
|
local env = setmetatable({Decoders = {}}, {__index = _G});
|
||||||
local file = loadfile(abs_fname, "t", env)
|
local file = loadfile(abs_fname, "t", env)
|
||||||
if(not(file)) then
|
if(not(file)) then
|
||||||
stdnse.print_debug("%s: Couldn't load decoder file: %s", SCRIPT_NAME, fname)
|
stdnse.print_debug("%s: Couldn't load decoder file: %s", SCRIPT_NAME, fname)
|
||||||
return false, "ERROR: Couldn't load decoder file: " .. fname
|
return false, "ERROR: Couldn't load decoder file: " .. fname
|
||||||
end
|
end
|
||||||
|
|
||||||
file()
|
file()
|
||||||
|
|
||||||
local d = env.Decoders
|
local d = env.Decoders
|
||||||
|
|
||||||
if ( d ) then return true, d end
|
if ( d ) then return true, d end
|
||||||
return false, "ERROR: Failed to load decoders"
|
return false, "ERROR: Failed to load decoders"
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -130,66 +130,66 @@ end
|
|||||||
-- @param iface table containing <code>name</code> and <code>address</code>
|
-- @param iface table containing <code>name</code> and <code>address</code>
|
||||||
-- @param Decoders the decoders class loaded externally
|
-- @param Decoders the decoders class loaded externally
|
||||||
-- @param decodertab the "result" table to which all discovered items are
|
-- @param decodertab the "result" table to which all discovered items are
|
||||||
-- reported
|
-- reported
|
||||||
sniffInterface = function(iface, Decoders, decodertab)
|
sniffInterface = function(iface, Decoders, decodertab)
|
||||||
local condvar = nmap.condvar(decodertab)
|
local condvar = nmap.condvar(decodertab)
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-listener.timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-listener.timeout"))
|
||||||
|
|
||||||
-- default to 30 seconds, if nothing else was set
|
-- default to 30 seconds, if nothing else was set
|
||||||
timeout = (timeout or 30) * 1000
|
timeout = (timeout or 30) * 1000
|
||||||
|
|
||||||
-- We want all packets that aren't explicitly for us
|
-- We want all packets that aren't explicitly for us
|
||||||
sock:pcap_open(iface.name, 1500, true, ("!host %s"):format(iface.address))
|
sock:pcap_open(iface.name, 1500, true, ("!host %s"):format(iface.address))
|
||||||
|
|
||||||
-- Set a short timeout so that we can timeout in time if needed
|
-- Set a short timeout so that we can timeout in time if needed
|
||||||
sock:set_timeout(100)
|
sock:set_timeout(100)
|
||||||
|
|
||||||
local start_time = nmap.clock_ms()
|
local start_time = nmap.clock_ms()
|
||||||
while( nmap.clock_ms() - start_time < timeout ) do
|
while( nmap.clock_ms() - start_time < timeout ) do
|
||||||
local status, _, _, data = sock:pcap_receive()
|
local status, _, _, data = sock:pcap_receive()
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local p = packet.Packet:new( data, #data )
|
local p = packet.Packet:new( data, #data )
|
||||||
|
|
||||||
-- if we have an UDP-based broadcast, we should have a proper packet
|
-- if we have an UDP-based broadcast, we should have a proper packet
|
||||||
if ( p and p.udp_dport and ( decodertab.udp[p.udp_dport] or Decoders.udp[p.udp_dport] ) ) then
|
if ( p and p.udp_dport and ( decodertab.udp[p.udp_dport] or Decoders.udp[p.udp_dport] ) ) then
|
||||||
if ( not(decodertab.udp[p.udp_dport]) ) then
|
if ( not(decodertab.udp[p.udp_dport]) ) then
|
||||||
decodertab.udp[p.udp_dport] = Decoders.udp[p.udp_dport]:new()
|
decodertab.udp[p.udp_dport] = Decoders.udp[p.udp_dport]:new()
|
||||||
end
|
end
|
||||||
decodertab.udp[p.udp_dport]:process(data)
|
decodertab.udp[p.udp_dport]:process(data)
|
||||||
-- The packet was decoded successfully but we don't have a valid decoder
|
-- The packet was decoded successfully but we don't have a valid decoder
|
||||||
-- Report this
|
-- Report this
|
||||||
elseif ( p and p.udp_dport ) then
|
elseif ( p and p.udp_dport ) then
|
||||||
stdnse.print_debug(2, "No decoder for dst port %d", p.udp_dport)
|
stdnse.print_debug(2, "No decoder for dst port %d", p.udp_dport)
|
||||||
-- we don't have a packet, so this is most likely something layer2 based
|
-- we don't have a packet, so this is most likely something layer2 based
|
||||||
-- in that case, check the ether Decoder table for pattern matches
|
-- in that case, check the ether Decoder table for pattern matches
|
||||||
else
|
else
|
||||||
-- attempt to find a match for a pattern
|
-- attempt to find a match for a pattern
|
||||||
local pos, hex = bin.unpack("H" .. #data, data)
|
local pos, hex = bin.unpack("H" .. #data, data)
|
||||||
local decoded = false
|
local decoded = false
|
||||||
for match, _ in pairs(Decoders.ether) do
|
for match, _ in pairs(Decoders.ether) do
|
||||||
-- attempts to match the "raw" packet against a filter
|
-- attempts to match the "raw" packet against a filter
|
||||||
-- supplied in each ethernet packet decoder
|
-- supplied in each ethernet packet decoder
|
||||||
if ( hex:match(match) ) then
|
if ( hex:match(match) ) then
|
||||||
if ( not(decodertab.ether[match]) ) then
|
if ( not(decodertab.ether[match]) ) then
|
||||||
decodertab.ether[match] = Decoders.ether[match]:new()
|
decodertab.ether[match] = Decoders.ether[match]:new()
|
||||||
end
|
end
|
||||||
-- start a new decoding thread. This way, if something gets foobared
|
-- start a new decoding thread. This way, if something gets foobared
|
||||||
-- the whole script doesn't break, only the packet decoding for that
|
-- the whole script doesn't break, only the packet decoding for that
|
||||||
-- specific packet.
|
-- specific packet.
|
||||||
stdnse.new_thread( decodertab.ether[match].process, decodertab.ether[match], data )
|
stdnse.new_thread( decodertab.ether[match].process, decodertab.ether[match], data )
|
||||||
decoded = true
|
decoded = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- no decoder was found for this layer2 packet
|
-- no decoder was found for this layer2 packet
|
||||||
if ( not(decoded) and #data > 10 ) then
|
if ( not(decoded) and #data > 10 ) then
|
||||||
stdnse.print_debug(1, "No decoder for packet hex: %s", select(2, bin.unpack("H10", data) ) )
|
stdnse.print_debug(1, "No decoder for packet hex: %s", select(2, bin.unpack("H10", data) ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -199,96 +199,96 @@ end
|
|||||||
-- @param link string containing the link type to filter
|
-- @param link string containing the link type to filter
|
||||||
-- @param up string containing the interface status to filter
|
-- @param up string containing the interface status to filter
|
||||||
-- @return result table containing tables of interfaces
|
-- @return result table containing tables of interfaces
|
||||||
-- each interface table has the following fields:
|
-- each interface table has the following fields:
|
||||||
-- <code>name</code> containing the device name
|
-- <code>name</code> containing the device name
|
||||||
-- <code>address</code> containing the device address
|
-- <code>address</code> containing the device address
|
||||||
getInterfaces = function(link, up)
|
getInterfaces = function(link, up)
|
||||||
if( not(nmap.list_interfaces) ) then return end
|
if( not(nmap.list_interfaces) ) then return end
|
||||||
local interfaces, err = nmap.list_interfaces()
|
local interfaces, err = nmap.list_interfaces()
|
||||||
local result = {}
|
local result = {}
|
||||||
if ( not(err) ) then
|
if ( not(err) ) then
|
||||||
for _, iface in ipairs(interfaces) do
|
for _, iface in ipairs(interfaces) do
|
||||||
if ( iface.link == link and
|
if ( iface.link == link and
|
||||||
iface.up == up and
|
iface.up == up and
|
||||||
iface.address ) then
|
iface.address ) then
|
||||||
|
|
||||||
-- exclude ipv6 addresses for now
|
-- exclude ipv6 addresses for now
|
||||||
if ( not(iface.address:match(":")) ) then
|
if ( not(iface.address:match(":")) ) then
|
||||||
table.insert(result, { name = iface.device,
|
table.insert(result, { name = iface.device,
|
||||||
address = iface.address } )
|
address = iface.address } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local DECODERFILE = "nselib/data/packetdecoders.lua"
|
local DECODERFILE = "nselib/data/packetdecoders.lua"
|
||||||
local iface = nmap.get_interface()
|
local iface = nmap.get_interface()
|
||||||
local interfaces = {}
|
local interfaces = {}
|
||||||
|
|
||||||
-- was an interface supplied using the -e argument?
|
-- was an interface supplied using the -e argument?
|
||||||
if ( iface ) then
|
if ( iface ) then
|
||||||
local iinfo, err = nmap.get_interface_info(iface)
|
local iinfo, err = nmap.get_interface_info(iface)
|
||||||
|
|
||||||
if ( not(iinfo.address) ) then
|
if ( not(iinfo.address) ) then
|
||||||
return "\n ERROR: The IP address of the interface could not be determined ..."
|
return "\n ERROR: The IP address of the interface could not be determined ..."
|
||||||
end
|
end
|
||||||
|
|
||||||
interfaces = { { name = iface, address = iinfo.address } }
|
interfaces = { { name = iface, address = iinfo.address } }
|
||||||
else
|
else
|
||||||
-- no interface was supplied, attempt autodiscovery
|
-- no interface was supplied, attempt autodiscovery
|
||||||
interfaces = getInterfaces("ethernet", "up")
|
interfaces = getInterfaces("ethernet", "up")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- make sure we have at least one interface to start sniffing
|
-- make sure we have at least one interface to start sniffing
|
||||||
if ( #interfaces == 0 ) then
|
if ( #interfaces == 0 ) then
|
||||||
return "\n ERROR: Could not determine any valid interfaces"
|
return "\n ERROR: Could not determine any valid interfaces"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- load the decoders from file
|
-- load the decoders from file
|
||||||
local status, Decoders = loadDecoders(DECODERFILE)
|
local status, Decoders = loadDecoders(DECODERFILE)
|
||||||
if ( not(status) ) then return "\n " .. Decoders end
|
if ( not(status) ) then return "\n " .. Decoders end
|
||||||
|
|
||||||
-- create a local table to handle instantiated decoders
|
-- create a local table to handle instantiated decoders
|
||||||
local decodertab = { udp = {}, ether = {} }
|
local decodertab = { udp = {}, ether = {} }
|
||||||
local condvar = nmap.condvar(decodertab)
|
local condvar = nmap.condvar(decodertab)
|
||||||
local threads = {}
|
local threads = {}
|
||||||
|
|
||||||
-- start a thread for each interface to sniff
|
-- start a thread for each interface to sniff
|
||||||
for _, iface in ipairs(interfaces) do
|
for _, iface in ipairs(interfaces) do
|
||||||
local co = stdnse.new_thread(sniffInterface, iface, Decoders, decodertab)
|
local co = stdnse.new_thread(sniffInterface, iface, Decoders, decodertab)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- wait for all threads to finish sniffing
|
-- wait for all threads to finish sniffing
|
||||||
repeat
|
repeat
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if coroutine.status(thread) == "dead" then
|
if coroutine.status(thread) == "dead" then
|
||||||
threads[thread] = nil
|
threads[thread] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
local out_outer = {}
|
local out_outer = {}
|
||||||
|
|
||||||
-- create the results table
|
-- create the results table
|
||||||
for proto, _ in pairs(decodertab) do
|
for proto, _ in pairs(decodertab) do
|
||||||
local out_inner = {}
|
local out_inner = {}
|
||||||
for key, decoder in pairs(decodertab[proto]) do
|
for key, decoder in pairs(decodertab[proto]) do
|
||||||
table.insert( out_inner, decodertab[proto][key]:getResults() )
|
table.insert( out_inner, decodertab[proto][key]:getResults() )
|
||||||
end
|
end
|
||||||
if ( #out_inner > 0 ) then
|
if ( #out_inner > 0 ) then
|
||||||
table.insert( out_outer, { name = proto, out_inner } )
|
table.insert( out_outer, { name = proto, out_inner } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.sort(out_outer, function(a, b) return a.name < b.name end)
|
table.sort(out_outer, function(a, b) return a.name < b.name end)
|
||||||
return stdnse.format_output(true, out_outer)
|
return stdnse.format_output(true, out_outer)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -57,24 +57,24 @@ portrule = shortport.port_or_service(3689, "daap")
|
|||||||
-- @param port table containing number and protocol fields.
|
-- @param port table containing number and protocol fields.
|
||||||
-- @return string containing the name of the library
|
-- @return string containing the name of the library
|
||||||
function getLibraryName( host, port )
|
function getLibraryName( host, port )
|
||||||
local _, libname, pos
|
local _, libname, pos
|
||||||
local url = "daap://" .. host.ip .. "/server-info"
|
local url = "daap://" .. host.ip .. "/server-info"
|
||||||
local response = http.get( host, port, url, nil, nil, nil)
|
local response = http.get( host, port, url, nil, nil, nil)
|
||||||
|
|
||||||
if response == nil or response.body == nil or response.body=="" then
|
if response == nil or response.body == nil or response.body=="" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
pos = string.find(response.body, "minm")
|
pos = string.find(response.body, "minm")
|
||||||
|
|
||||||
if pos > 0 then
|
if pos > 0 then
|
||||||
local len
|
local len
|
||||||
pos = pos + 4
|
pos = pos + 4
|
||||||
pos, len = bin.unpack( ">I", response.body, pos )
|
pos, len = bin.unpack( ">I", response.body, pos )
|
||||||
pos, libname = bin.unpack( "A" .. len, response.body, pos )
|
pos, libname = bin.unpack( "A" .. len, response.body, pos )
|
||||||
end
|
end
|
||||||
|
|
||||||
return libname
|
return libname
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Reads the first item value specified by name
|
--- Reads the first item value specified by name
|
||||||
@@ -84,23 +84,23 @@ end
|
|||||||
-- @return number
|
-- @return number
|
||||||
local function getAttributeAsInt( data, name )
|
local function getAttributeAsInt( data, name )
|
||||||
|
|
||||||
local pos = string.find(data, name)
|
local pos = string.find(data, name)
|
||||||
local attrib
|
local attrib
|
||||||
|
|
||||||
if pos and pos > 0 then
|
if pos and pos > 0 then
|
||||||
pos = pos + 4
|
pos = pos + 4
|
||||||
local len
|
local len
|
||||||
pos, len = bin.unpack( ">I", data, pos )
|
pos, len = bin.unpack( ">I", data, pos )
|
||||||
|
|
||||||
if ( len ~= 4 ) then
|
if ( len ~= 4 ) then
|
||||||
stdnse.print_debug( string.format("Unexpected length returned: %d", len ) )
|
stdnse.print_debug( string.format("Unexpected length returned: %d", len ) )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
pos, attrib = bin.unpack( ">I", data, pos )
|
pos, attrib = bin.unpack( ">I", data, pos )
|
||||||
end
|
end
|
||||||
|
|
||||||
return attrib
|
return attrib
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -111,14 +111,14 @@ end
|
|||||||
-- @return number containing the session identity received from the server
|
-- @return number containing the session identity received from the server
|
||||||
function getSessionId( host, port )
|
function getSessionId( host, port )
|
||||||
|
|
||||||
local _, sessionid
|
local _, sessionid
|
||||||
local response = http.get( host, port, "/login", nil, nil, nil )
|
local response = http.get( host, port, "/login", nil, nil, nil )
|
||||||
|
|
||||||
if response ~= nil then
|
if response ~= nil then
|
||||||
sessionid = getAttributeAsInt( response.body, "mlid")
|
sessionid = getAttributeAsInt( response.body, "mlid")
|
||||||
end
|
end
|
||||||
|
|
||||||
return sessionid
|
return sessionid
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets the revision number for the library
|
--- Gets the revision number for the library
|
||||||
@@ -128,15 +128,15 @@ end
|
|||||||
-- @param sessionid number containing session identifier from <code>getSessionId</code>
|
-- @param sessionid number containing session identifier from <code>getSessionId</code>
|
||||||
-- @return number containing the revision number for the library
|
-- @return number containing the revision number for the library
|
||||||
function getRevisionNumber( host, port, sessionid )
|
function getRevisionNumber( host, port, sessionid )
|
||||||
local url = "/update?session-id=" .. sessionid .. "&revision-number=1"
|
local url = "/update?session-id=" .. sessionid .. "&revision-number=1"
|
||||||
local _, revision
|
local _, revision
|
||||||
local response = http.get( host, port, url, nil, nil, nil )
|
local response = http.get( host, port, url, nil, nil, nil )
|
||||||
|
|
||||||
if response ~= nil then
|
if response ~= nil then
|
||||||
revision = getAttributeAsInt( response.body, "musr")
|
revision = getAttributeAsInt( response.body, "musr")
|
||||||
end
|
end
|
||||||
|
|
||||||
return revision
|
return revision
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets the database identitity for the library
|
--- Gets the database identitity for the library
|
||||||
@@ -146,15 +146,15 @@ end
|
|||||||
-- @param sessionid number containing session identifier from <code>getSessionId</code>
|
-- @param sessionid number containing session identifier from <code>getSessionId</code>
|
||||||
-- @param revid number containing the revision id as retrieved from <code>getRevisionNumber</code>
|
-- @param revid number containing the revision id as retrieved from <code>getRevisionNumber</code>
|
||||||
function getDatabaseId( host, port, sessionid, revid )
|
function getDatabaseId( host, port, sessionid, revid )
|
||||||
local url = "/databases?session-id=" .. sessionid .. "&revision-number=" .. revid
|
local url = "/databases?session-id=" .. sessionid .. "&revision-number=" .. revid
|
||||||
local response = http.get( host, port, url, nil, nil, nil )
|
local response = http.get( host, port, url, nil, nil, nil )
|
||||||
local miid
|
local miid
|
||||||
|
|
||||||
if response ~= nil then
|
if response ~= nil then
|
||||||
miid = getAttributeAsInt( response.body, "miid")
|
miid = getAttributeAsInt( response.body, "miid")
|
||||||
end
|
end
|
||||||
|
|
||||||
return miid
|
return miid
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets a string item type from data
|
--- Gets a string item type from data
|
||||||
@@ -164,19 +164,19 @@ end
|
|||||||
-- @return pos number containing new position after reading string
|
-- @return pos number containing new position after reading string
|
||||||
-- @return value string containing the string item that was read
|
-- @return value string containing the string item that was read
|
||||||
local function getStringItem( data, pos )
|
local function getStringItem( data, pos )
|
||||||
local len
|
local len
|
||||||
|
|
||||||
pos, len = bin.unpack(">I", data, pos)
|
pos, len = bin.unpack(">I", data, pos)
|
||||||
|
|
||||||
if ( len > 0 ) then
|
if ( len > 0 ) then
|
||||||
return bin.unpack( "A"..len, data, pos )
|
return bin.unpack( "A"..len, data, pos )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local itemFetcher = {}
|
local itemFetcher = {}
|
||||||
|
|
||||||
itemFetcher["mikd"] = function( data, pos ) return getStringItem( data, pos ) end
|
itemFetcher["mikd"] = function( data, pos ) return getStringItem( data, pos ) end
|
||||||
itemFetcher["miid"] = itemFetcher["mikd"]
|
itemFetcher["miid"] = itemFetcher["mikd"]
|
||||||
itemFetcher["minm"] = itemFetcher["mikd"]
|
itemFetcher["minm"] = itemFetcher["mikd"]
|
||||||
itemFetcher["asal"] = itemFetcher["mikd"]
|
itemFetcher["asal"] = itemFetcher["mikd"]
|
||||||
@@ -190,22 +190,22 @@ itemFetcher["asar"] = itemFetcher["mikd"]
|
|||||||
-- <code>asal</code> and <code>asar</code> when available
|
-- <code>asal</code> and <code>asar</code> when available
|
||||||
parseItem = function( data, len )
|
parseItem = function( data, len )
|
||||||
|
|
||||||
local pos, name, value = 1, nil, nil
|
local pos, name, value = 1, nil, nil
|
||||||
local item = {}
|
local item = {}
|
||||||
|
|
||||||
while( len - pos > 0 ) do
|
while( len - pos > 0 ) do
|
||||||
pos, name = bin.unpack( "A4", data, pos )
|
pos, name = bin.unpack( "A4", data, pos )
|
||||||
|
|
||||||
if itemFetcher[name] then
|
if itemFetcher[name] then
|
||||||
pos, item[name] = itemFetcher[name](data, pos )
|
pos, item[name] = itemFetcher[name](data, pos )
|
||||||
else
|
else
|
||||||
stdnse.print_debug( string.format("No itemfetcher for: %s", name) )
|
stdnse.print_debug( string.format("No itemfetcher for: %s", name) )
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -218,124 +218,124 @@ end
|
|||||||
-- @param limit number containing the maximum amount of songs to return
|
-- @param limit number containing the maximum amount of songs to return
|
||||||
-- @return table containing the following structure [artist][album][songs]
|
-- @return table containing the following structure [artist][album][songs]
|
||||||
function getItems( host, port, sessionid, revid, dbid, limit )
|
function getItems( host, port, sessionid, revid, dbid, limit )
|
||||||
local meta = "dmap.itemid,dmap.itemname,dmap.itemkind,daap.songalbum,daap.songartist"
|
local meta = "dmap.itemid,dmap.itemname,dmap.itemkind,daap.songalbum,daap.songartist"
|
||||||
local url = "/databases/" .. dbid .. "/items?type=music&meta=" .. meta .. "&session-id=" .. sessionid .. "&revision-number=" .. revid
|
local url = "/databases/" .. dbid .. "/items?type=music&meta=" .. meta .. "&session-id=" .. sessionid .. "&revision-number=" .. revid
|
||||||
local response = http.get( host, port, url, nil, nil, nil )
|
local response = http.get( host, port, url, nil, nil, nil )
|
||||||
local item, data, pos, len
|
local item, data, pos, len
|
||||||
local items = {}
|
local items = {}
|
||||||
local limit = limit or -1
|
local limit = limit or -1
|
||||||
|
|
||||||
if response == nil then
|
if response == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get our position to the list of items
|
-- get our position to the list of items
|
||||||
pos = string.find(response.body, "mlcl")
|
pos = string.find(response.body, "mlcl")
|
||||||
pos = pos + 4
|
pos = pos + 4
|
||||||
|
|
||||||
while ( pos > 0 and pos + 8 < response.body:len() ) do
|
while ( pos > 0 and pos + 8 < response.body:len() ) do
|
||||||
|
|
||||||
-- find the next single item
|
-- find the next single item
|
||||||
pos = string.find(response.body, "mlit", pos)
|
pos = string.find(response.body, "mlit", pos)
|
||||||
pos = pos + 4
|
pos = pos + 4
|
||||||
|
|
||||||
pos, len = bin.unpack( ">I", response.body, pos )
|
pos, len = bin.unpack( ">I", response.body, pos )
|
||||||
|
|
||||||
if ( pos < response.body:len() and pos + len < response.body:len() ) then
|
if ( pos < response.body:len() and pos + len < response.body:len() ) then
|
||||||
pos, data = bin.unpack( "A" .. len, response.body, pos )
|
pos, data = bin.unpack( "A" .. len, response.body, pos )
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse a single item
|
-- parse a single item
|
||||||
item = parseItem( data, len )
|
item = parseItem( data, len )
|
||||||
|
|
||||||
local album = item.asal or "unknown"
|
local album = item.asal or "unknown"
|
||||||
local artist= item.asar or "unknown"
|
local artist= item.asar or "unknown"
|
||||||
local song = item.minm or ""
|
local song = item.minm or ""
|
||||||
|
|
||||||
if items[artist] == nil then
|
if items[artist] == nil then
|
||||||
items[artist] = {}
|
items[artist] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
if items[artist][album] == nil then
|
if items[artist][album] == nil then
|
||||||
items[artist][album] = {}
|
items[artist][album] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
if limit == 0 then
|
if limit == 0 then
|
||||||
break
|
break
|
||||||
elseif limit > 0 then
|
elseif limit > 0 then
|
||||||
limit = limit - 1
|
limit = limit - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert( items[artist][album], song )
|
table.insert( items[artist][album], song )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local limit = tonumber(nmap.registry.args.daap_item_limit) or 100
|
local limit = tonumber(nmap.registry.args.daap_item_limit) or 100
|
||||||
local libname = getLibraryName( host, port )
|
local libname = getLibraryName( host, port )
|
||||||
|
|
||||||
if libname == nil then
|
if libname == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local sessionid = getSessionId( host, port )
|
local sessionid = getSessionId( host, port )
|
||||||
|
|
||||||
if sessionid == nil then
|
if sessionid == nil then
|
||||||
return stdnse.format_output(true, "Libname: " .. libname)
|
return stdnse.format_output(true, "Libname: " .. libname)
|
||||||
end
|
end
|
||||||
|
|
||||||
local revid = getRevisionNumber( host, port, sessionid )
|
local revid = getRevisionNumber( host, port, sessionid )
|
||||||
|
|
||||||
if revid == nil then
|
if revid == nil then
|
||||||
return stdnse.format_output(true, "Libname: " .. libname)
|
return stdnse.format_output(true, "Libname: " .. libname)
|
||||||
end
|
end
|
||||||
|
|
||||||
local dbid = getDatabaseId( host, port, sessionid, revid )
|
local dbid = getDatabaseId( host, port, sessionid, revid )
|
||||||
|
|
||||||
if dbid == nil then
|
if dbid == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local items = getItems( host, port, sessionid, revid, dbid, limit )
|
local items = getItems( host, port, sessionid, revid, dbid, limit )
|
||||||
|
|
||||||
if items == nil then
|
if items == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local albums, songs, artists, results = {}, {}, {}, {}
|
local albums, songs, artists, results = {}, {}, {}, {}
|
||||||
|
|
||||||
table.insert( results, libname )
|
table.insert( results, libname )
|
||||||
|
|
||||||
for artist, v in pairs(items) do
|
for artist, v in pairs(items) do
|
||||||
albums = {}
|
albums = {}
|
||||||
for album, v2 in pairs(v) do
|
for album, v2 in pairs(v) do
|
||||||
songs = {}
|
songs = {}
|
||||||
for _, song in pairs( v2 ) do
|
for _, song in pairs( v2 ) do
|
||||||
table.insert( songs, song )
|
table.insert( songs, song )
|
||||||
end
|
end
|
||||||
table.insert( albums, album )
|
table.insert( albums, album )
|
||||||
table.insert( albums, songs )
|
table.insert( albums, songs )
|
||||||
end
|
end
|
||||||
table.insert( artists, artist )
|
table.insert( artists, artist )
|
||||||
table.insert( artists, albums )
|
table.insert( artists, albums )
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert( results, artists )
|
table.insert( results, artists )
|
||||||
local output = stdnse.format_output( true, results )
|
local output = stdnse.format_output( true, results )
|
||||||
|
|
||||||
if limit > 0 then
|
if limit > 0 then
|
||||||
output = output .. string.format("\n\nOutput limited to %d items", limit )
|
output = output .. string.format("\n\nOutput limited to %d items", limit )
|
||||||
end
|
end
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -103,13 +103,13 @@ portrule = shortport.version_port_or_service({523}, nil,
|
|||||||
-- @return string containing the complete server profile
|
-- @return string containing the complete server profile
|
||||||
function extract_server_profile(data)
|
function extract_server_profile(data)
|
||||||
|
|
||||||
local server_profile_offset = 37
|
local server_profile_offset = 37
|
||||||
|
|
||||||
if server_profile_offset > data:len() then
|
if server_profile_offset > data:len() then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return data:sub(server_profile_offset)
|
return data:sub(server_profile_offset)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -124,28 +124,28 @@ end
|
|||||||
-- @return table with parsed data
|
-- @return table with parsed data
|
||||||
function parse_db2_packet(packet)
|
function parse_db2_packet(packet)
|
||||||
|
|
||||||
local info_length_offset = 158
|
local info_length_offset = 158
|
||||||
local info_offset = 160
|
local info_offset = 160
|
||||||
local version_offset = 97
|
local version_offset = 97
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
if packet.header.data_len < info_length_offset then
|
if packet.header.data_len < info_length_offset then
|
||||||
stdnse.print_debug( "db2-das-info: packet too short to be DB2 response...")
|
stdnse.print_debug( "db2-das-info: packet too short to be DB2 response...")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local _, len = bin.unpack(">S", packet.data:sub(info_length_offset, info_length_offset + 1))
|
local _, len = bin.unpack(">S", packet.data:sub(info_length_offset, info_length_offset + 1))
|
||||||
_, response.version = bin.unpack("z", packet.data:sub(version_offset) )
|
_, response.version = bin.unpack("z", packet.data:sub(version_offset) )
|
||||||
response.info_length = len - 4
|
response.info_length = len - 4
|
||||||
response.info = packet.data:sub(info_offset, info_offset + response.info_length - (info_offset-info_length_offset))
|
response.info = packet.data:sub(info_offset, info_offset + response.info_length - (info_offset-info_length_offset))
|
||||||
|
|
||||||
if(nmap.debugging() > 3) then
|
if(nmap.debugging() > 3) then
|
||||||
stdnse.print_debug( string.format("db2-das-info: version: %s", response.version) )
|
stdnse.print_debug( string.format("db2-das-info: version: %s", response.version) )
|
||||||
stdnse.print_debug( string.format("db2-das-info: info_length: %d", response.info_length) )
|
stdnse.print_debug( string.format("db2-das-info: info_length: %d", response.info_length) )
|
||||||
stdnse.print_debug( string.format("db2-das-info: response.info:len(): %d", response.info:len()))
|
stdnse.print_debug( string.format("db2-das-info: response.info:len(): %d", response.info:len()))
|
||||||
end
|
end
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -163,67 +163,67 @@ end
|
|||||||
-- @return table with header and data
|
-- @return table with header and data
|
||||||
function read_db2_packet(socket)
|
function read_db2_packet(socket)
|
||||||
|
|
||||||
local packet = {}
|
local packet = {}
|
||||||
local header_len = 41
|
local header_len = 41
|
||||||
local total_len = 0
|
local total_len = 0
|
||||||
local buf
|
local buf
|
||||||
|
|
||||||
local DATA_LENGTH_OFFSET = 38
|
local DATA_LENGTH_OFFSET = 38
|
||||||
local ENDIANESS_OFFSET = 23
|
local ENDIANESS_OFFSET = 23
|
||||||
|
|
||||||
local catch = function()
|
local catch = function()
|
||||||
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
|
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
packet.header = {}
|
packet.header = {}
|
||||||
|
|
||||||
buf = try( socket:receive_bytes(header_len) )
|
buf = try( socket:receive_bytes(header_len) )
|
||||||
|
|
||||||
packet.header.raw = buf:sub(1, header_len)
|
packet.header.raw = buf:sub(1, header_len)
|
||||||
|
|
||||||
if packet.header.raw:sub(1, 10) == string.char(0x00, 0x00, 0x00, 0x00, 0x44, 0x42, 0x32, 0x44, 0x41, 0x53) then
|
if packet.header.raw:sub(1, 10) == string.char(0x00, 0x00, 0x00, 0x00, 0x44, 0x42, 0x32, 0x44, 0x41, 0x53) then
|
||||||
|
|
||||||
stdnse.print_debug("db2-das-info: Got DB2DAS packet")
|
stdnse.print_debug("db2-das-info: Got DB2DAS packet")
|
||||||
|
|
||||||
local _, endian = bin.unpack( "A2", packet.header.raw, ENDIANESS_OFFSET )
|
local _, endian = bin.unpack( "A2", packet.header.raw, ENDIANESS_OFFSET )
|
||||||
|
|
||||||
if endian == "9z" then
|
if endian == "9z" then
|
||||||
_, packet.header.data_len = bin.unpack("I", packet.header.raw, DATA_LENGTH_OFFSET )
|
_, packet.header.data_len = bin.unpack("I", packet.header.raw, DATA_LENGTH_OFFSET )
|
||||||
else
|
else
|
||||||
_, packet.header.data_len = bin.unpack(">I", packet.header.raw, DATA_LENGTH_OFFSET )
|
_, packet.header.data_len = bin.unpack(">I", packet.header.raw, DATA_LENGTH_OFFSET )
|
||||||
end
|
end
|
||||||
|
|
||||||
total_len = header_len + packet.header.data_len
|
total_len = header_len + packet.header.data_len
|
||||||
|
|
||||||
if(nmap.debugging() > 3) then
|
if(nmap.debugging() > 3) then
|
||||||
stdnse.print_debug( string.format("db2-das-info: data_len: %d", packet.header.data_len) )
|
stdnse.print_debug( string.format("db2-das-info: data_len: %d", packet.header.data_len) )
|
||||||
stdnse.print_debug( string.format("db2-das-info: buf_len: %d", buf:len()))
|
stdnse.print_debug( string.format("db2-das-info: buf_len: %d", buf:len()))
|
||||||
stdnse.print_debug( string.format("db2-das-info: total_len: %d", total_len))
|
stdnse.print_debug( string.format("db2-das-info: total_len: %d", total_len))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- do we have all data as specified by data_len?
|
-- do we have all data as specified by data_len?
|
||||||
while total_len > buf:len() do
|
while total_len > buf:len() do
|
||||||
-- if not read additional bytes
|
-- if not read additional bytes
|
||||||
if(nmap.debugging() > 3) then
|
if(nmap.debugging() > 3) then
|
||||||
stdnse.print_debug( string.format("db2-das-info: Reading %d additional bytes", total_len - buf:len()))
|
stdnse.print_debug( string.format("db2-das-info: Reading %d additional bytes", total_len - buf:len()))
|
||||||
end
|
end
|
||||||
local tmp = try( socket:receive_bytes( total_len - buf:len() ) )
|
local tmp = try( socket:receive_bytes( total_len - buf:len() ) )
|
||||||
if(nmap.debugging() > 3) then
|
if(nmap.debugging() > 3) then
|
||||||
stdnse.print_debug( string.format("db2-das-info: Read %d bytes", tmp:len()))
|
stdnse.print_debug( string.format("db2-das-info: Read %d bytes", tmp:len()))
|
||||||
end
|
end
|
||||||
buf = buf .. tmp
|
buf = buf .. tmp
|
||||||
end
|
end
|
||||||
|
|
||||||
packet.data = buf:sub(header_len + 1)
|
packet.data = buf:sub(header_len + 1)
|
||||||
|
|
||||||
else
|
else
|
||||||
stdnse.print_debug("db2-das-info: Unknown packet, aborting ...")
|
stdnse.print_debug("db2-das-info: Unknown packet, aborting ...")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
return packet
|
return packet
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -234,16 +234,16 @@ end
|
|||||||
--
|
--
|
||||||
function send_db2_packet( socket, packet )
|
function send_db2_packet( socket, packet )
|
||||||
|
|
||||||
local catch = function()
|
local catch = function()
|
||||||
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
|
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
|
|
||||||
local buf = packet.header.raw .. packet.data
|
local buf = packet.header.raw .. packet.data
|
||||||
|
|
||||||
try( socket:send(buf) )
|
try( socket:send(buf) )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -261,172 +261,172 @@ end
|
|||||||
--
|
--
|
||||||
function create_das_packet( magic, data )
|
function create_das_packet( magic, data )
|
||||||
|
|
||||||
local packet = {}
|
local packet = {}
|
||||||
local data_len = data:len()
|
local data_len = data:len()
|
||||||
|
|
||||||
packet.header = {}
|
packet.header = {}
|
||||||
|
|
||||||
packet.header.raw = string.char(0x00, 0x00, 0x00, 0x00, 0x44, 0x42, 0x32, 0x44, 0x41, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20)
|
packet.header.raw = string.char(0x00, 0x00, 0x00, 0x00, 0x44, 0x42, 0x32, 0x44, 0x41, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20)
|
||||||
packet.header.raw = packet.header.raw .. string.char(0x01, 0x04, 0x00, 0x00, 0x00, 0x10, 0x39, 0x7a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
packet.header.raw = packet.header.raw .. string.char(0x01, 0x04, 0x00, 0x00, 0x00, 0x10, 0x39, 0x7a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
packet.header.raw = packet.header.raw .. string.char(0x00, 0x00, 0x00, 0x00 )
|
packet.header.raw = packet.header.raw .. string.char(0x00, 0x00, 0x00, 0x00 )
|
||||||
packet.header.raw = packet.header.raw .. bin.pack("C", magic)
|
packet.header.raw = packet.header.raw .. bin.pack("C", magic)
|
||||||
packet.header.raw = packet.header.raw .. bin.pack("S", data_len)
|
packet.header.raw = packet.header.raw .. bin.pack("S", data_len)
|
||||||
packet.header.raw = packet.header.raw .. string.char(0x00, 0x00)
|
packet.header.raw = packet.header.raw .. string.char(0x00, 0x00)
|
||||||
|
|
||||||
packet.header.data_len = data_len
|
packet.header.data_len = data_len
|
||||||
packet.data = data
|
packet.data = data
|
||||||
|
|
||||||
return packet
|
return packet
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
|
|
||||||
-- create the socket used for our connection
|
-- create the socket used for our connection
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
|
|
||||||
-- set a reasonable timeout value
|
-- set a reasonable timeout value
|
||||||
socket:set_timeout(10000)
|
socket:set_timeout(10000)
|
||||||
|
|
||||||
-- do some exception handling / cleanup
|
-- do some exception handling / cleanup
|
||||||
local catch = function()
|
local catch = function()
|
||||||
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with " .. host.ip .. " on port " .. port.number)
|
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with " .. host.ip .. " on port " .. port.number)
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
|
|
||||||
|
|
||||||
try(socket:connect(host, port))
|
try(socket:connect(host, port))
|
||||||
|
|
||||||
local query
|
local query
|
||||||
|
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
-- Transaction block 1
|
-- Transaction block 1
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
local data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x00)
|
local data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x00)
|
||||||
|
|
||||||
--try(socket:send(query))
|
--try(socket:send(query))
|
||||||
local db2packet = create_das_packet(0x02, data)
|
local db2packet = create_das_packet(0x02, data)
|
||||||
|
|
||||||
send_db2_packet( socket, db2packet )
|
send_db2_packet( socket, db2packet )
|
||||||
read_db2_packet( socket )
|
read_db2_packet( socket )
|
||||||
|
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
-- Transaction block 2
|
-- Transaction block 2
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
data = string.char(0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00)
|
data = string.char(0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x08, 0x59, 0xe7, 0x1f, 0x4b, 0x79, 0xf0, 0x90, 0x72, 0x85, 0xe0, 0x8f)
|
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x08, 0x59, 0xe7, 0x1f, 0x4b, 0x79, 0xf0, 0x90, 0x72, 0x85, 0xe0, 0x8f)
|
||||||
data = data .. string.char(0x3e, 0x38, 0x45, 0x38, 0xe3, 0xe5, 0x12, 0xc4, 0x3b, 0xe9, 0x7d, 0xe2, 0xf5, 0xf0, 0x78, 0xcc)
|
data = data .. string.char(0x3e, 0x38, 0x45, 0x38, 0xe3, 0xe5, 0x12, 0xc4, 0x3b, 0xe9, 0x7d, 0xe2, 0xf5, 0xf0, 0x78, 0xcc)
|
||||||
data = data .. string.char(0x81, 0x6f, 0x87, 0x5f, 0x91)
|
data = data .. string.char(0x81, 0x6f, 0x87, 0x5f, 0x91)
|
||||||
|
|
||||||
db2packet = create_das_packet(0x05, data)
|
db2packet = create_das_packet(0x05, data)
|
||||||
|
|
||||||
send_db2_packet( socket, db2packet )
|
send_db2_packet( socket, db2packet )
|
||||||
read_db2_packet( socket )
|
read_db2_packet( socket )
|
||||||
|
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
-- Transaction block 3
|
-- Transaction block 3
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x01, 0x00, 0x00, 0x00)
|
data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x01, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
||||||
data = data .. string.char(0x64, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x00, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x64, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x00, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
||||||
data = data .. string.char(0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x53, 0x72, 0x76, 0x00)
|
data = data .. string.char(0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x53, 0x72, 0x76, 0x00)
|
||||||
|
|
||||||
db2packet = create_das_packet(0x0a, data)
|
db2packet = create_das_packet(0x0a, data)
|
||||||
send_db2_packet( socket, db2packet )
|
send_db2_packet( socket, db2packet )
|
||||||
read_db2_packet( socket )
|
read_db2_packet( socket )
|
||||||
|
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
-- Transaction block 4
|
-- Transaction block 4
|
||||||
-- ************************************************************************************
|
-- ************************************************************************************
|
||||||
data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x01, 0x00, 0x00, 0x00)
|
data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x01, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03)
|
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03)
|
||||||
data = data .. string.char(0x48, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xfb, 0x42, 0x90, 0x00, 0x00, 0x24, 0x93, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x48, 0x00, 0x00, 0x00, 0x00, 0x4a, 0xfb, 0x42, 0x90, 0x00, 0x00, 0x24, 0x93, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
||||||
data = data .. string.char(0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x53, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x53, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
data = data .. string.char(0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x64, 0x62, 0x32)
|
||||||
data = data .. string.char(0x64, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x00, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x64, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x00, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00)
|
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x4c, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00)
|
||||||
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x00)
|
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x00)
|
||||||
|
|
||||||
db2packet = create_das_packet(0x06, data)
|
db2packet = create_das_packet(0x06, data)
|
||||||
send_db2_packet( socket, db2packet )
|
send_db2_packet( socket, db2packet )
|
||||||
|
|
||||||
data = string.char( 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00)
|
data = string.char( 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00)
|
||||||
data = data .. string.char(0x00, 0x04, 0xb8, 0x64, 0x62, 0x32, 0x64, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73)
|
data = data .. string.char(0x00, 0x04, 0xb8, 0x64, 0x62, 0x32, 0x64, 0x61, 0x73, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73)
|
||||||
data = data .. string.char(0x63, 0x76, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00)
|
data = data .. string.char(0x63, 0x76, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00)
|
||||||
data = data .. string.char(0x00, 0x04, 0xb8, 0x64, 0x62, 0x32, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x53)
|
data = data .. string.char(0x00, 0x04, 0xb8, 0x64, 0x62, 0x32, 0x4b, 0x6e, 0x6f, 0x77, 0x6e, 0x44, 0x73, 0x63, 0x76, 0x53)
|
||||||
data = data .. string.char(0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00)
|
data = data .. string.char(0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00)
|
||||||
data = data .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00)
|
data = data .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00)
|
||||||
data = data .. string.char(0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00)
|
data = data .. string.char(0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00)
|
||||||
data = data .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00)
|
data = data .. string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00)
|
||||||
data = data .. string.char(0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00)
|
data = data .. string.char(0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x00)
|
||||||
data = data .. string.char(0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00)
|
data = data .. string.char(0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00)
|
||||||
data = data .. string.char(0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x18)
|
data = data .. string.char(0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x18)
|
||||||
|
|
||||||
db2packet = create_das_packet(0x06, data)
|
db2packet = create_das_packet(0x06, data)
|
||||||
send_db2_packet( socket, db2packet )
|
send_db2_packet( socket, db2packet )
|
||||||
|
|
||||||
local packet = read_db2_packet( socket )
|
local packet = read_db2_packet( socket )
|
||||||
local db2response = parse_db2_packet(packet)
|
local db2response = parse_db2_packet(packet)
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
-- The next block of code is essentially the version extraction code from db2-info.nse
|
-- The next block of code is essentially the version extraction code from db2-info.nse
|
||||||
local server_version
|
local server_version
|
||||||
if string.sub(db2response.version,1,3) == "SQL" then
|
if string.sub(db2response.version,1,3) == "SQL" then
|
||||||
local major_version = string.sub(db2response.version,4,5)
|
local major_version = string.sub(db2response.version,4,5)
|
||||||
|
|
||||||
-- strip the leading 0 from the major version, for consistency with
|
-- strip the leading 0 from the major version, for consistency with
|
||||||
-- nmap-service-probes results
|
-- nmap-service-probes results
|
||||||
if string.sub(major_version,1,1) == "0" then
|
if string.sub(major_version,1,1) == "0" then
|
||||||
major_version = string.sub(major_version,2)
|
major_version = string.sub(major_version,2)
|
||||||
end
|
end
|
||||||
local minor_version = string.sub(db2response.version,6,7)
|
local minor_version = string.sub(db2response.version,6,7)
|
||||||
local hotfix = string.sub(db2response.version,8)
|
local hotfix = string.sub(db2response.version,8)
|
||||||
server_version = major_version .. "." .. minor_version .. "." .. hotfix
|
server_version = major_version .. "." .. minor_version .. "." .. hotfix
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try to determine which of the two values (probe version vs script) has more
|
-- Try to determine which of the two values (probe version vs script) has more
|
||||||
-- precision. A couple DB2 versions send DB2 UDB 7.1 vs SQL090204 (9.02.04)
|
-- precision. A couple DB2 versions send DB2 UDB 7.1 vs SQL090204 (9.02.04)
|
||||||
local _
|
local _
|
||||||
local current_count = 0
|
local current_count = 0
|
||||||
if port.version.version ~= nil then
|
if port.version.version ~= nil then
|
||||||
_, current_count = string.gsub(port.version.version, "%.", ".")
|
_, current_count = string.gsub(port.version.version, "%.", ".")
|
||||||
end
|
end
|
||||||
|
|
||||||
local new_count = 0
|
local new_count = 0
|
||||||
if server_version ~= nil then
|
if server_version ~= nil then
|
||||||
_, new_count = string.gsub(server_version, "%.", ".")
|
_, new_count = string.gsub(server_version, "%.", ".")
|
||||||
end
|
end
|
||||||
|
|
||||||
if current_count < new_count then
|
if current_count < new_count then
|
||||||
port.version.version = server_version
|
port.version.version = server_version
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = false
|
local result = false
|
||||||
|
|
||||||
local db2profile = extract_server_profile( db2response.info )
|
local db2profile = extract_server_profile( db2response.info )
|
||||||
|
|
||||||
if (db2profile ~= nil ) then
|
if (db2profile ~= nil ) then
|
||||||
result = "DB2 Administration Server Settings\r\n"
|
result = "DB2 Administration Server Settings\r\n"
|
||||||
result = result .. extract_server_profile( db2response.info )
|
result = result .. extract_server_profile( db2response.info )
|
||||||
|
|
||||||
-- Set port information
|
-- Set port information
|
||||||
port.version.name = "ibm-db2"
|
port.version.name = "ibm-db2"
|
||||||
port.version.product = "IBM DB2 Database Server"
|
port.version.product = "IBM DB2 Database Server"
|
||||||
port.version.name_confidence = 10
|
port.version.name_confidence = 10
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -67,170 +67,170 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|||||||
categories = {"intrusive", "discovery"}
|
categories = {"intrusive", "discovery"}
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not stdnse.get_script_args("dns-brute.domain") then
|
if not stdnse.get_script_args("dns-brute.domain") then
|
||||||
stdnse.print_debug(1,
|
stdnse.print_debug(1,
|
||||||
"Skipping '%s' %s, 'dns-brute.domain' argument is missing.",
|
"Skipping '%s' %s, 'dns-brute.domain' argument is missing.",
|
||||||
SCRIPT_NAME, SCRIPT_TYPE)
|
SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function guess_domain(host)
|
local function guess_domain(host)
|
||||||
local name
|
local name
|
||||||
|
|
||||||
name = stdnse.get_hostname(host)
|
name = stdnse.get_hostname(host)
|
||||||
if name and name ~= host.ip then
|
if name and name ~= host.ip then
|
||||||
return string.match(name, "%.([^.]+%..+)%.?$") or string.match(name, "^([^.]+%.[^.]+)%.?$")
|
return string.match(name, "%.([^.]+%..+)%.?$") or string.match(name, "^([^.]+%.[^.]+)%.?$")
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Single DNS lookup, returning all results. dtype should be e.g. "A", "AAAA".
|
-- Single DNS lookup, returning all results. dtype should be e.g. "A", "AAAA".
|
||||||
local function resolve(host, dtype)
|
local function resolve(host, dtype)
|
||||||
local status, result = dns.query(host, {dtype=dtype,retAll=true})
|
local status, result = dns.query(host, {dtype=dtype,retAll=true})
|
||||||
return status and result or false
|
return status and result or false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function array_iter(array, i, j)
|
local function array_iter(array, i, j)
|
||||||
return coroutine.wrap(function ()
|
return coroutine.wrap(function ()
|
||||||
while i <= j do
|
while i <= j do
|
||||||
coroutine.yield(array[i])
|
coroutine.yield(array[i])
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function thread_main(domainname, results, name_iter)
|
local function thread_main(domainname, results, name_iter)
|
||||||
local condvar = nmap.condvar( results )
|
local condvar = nmap.condvar( results )
|
||||||
for name in name_iter do
|
for name in name_iter do
|
||||||
for _, dtype in ipairs({"A", "AAAA"}) do
|
for _, dtype in ipairs({"A", "AAAA"}) do
|
||||||
local res = resolve(name..'.'..domainname, dtype)
|
local res = resolve(name..'.'..domainname, dtype)
|
||||||
if(res) then
|
if(res) then
|
||||||
for _,addr in ipairs(res) do
|
for _,addr in ipairs(res) do
|
||||||
local hostn = name..'.'..domainname
|
local hostn = name..'.'..domainname
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
stdnse.print_debug("Added target: "..hostn)
|
stdnse.print_debug("Added target: "..hostn)
|
||||||
local status,err = target.add(hostn)
|
local status,err = target.add(hostn)
|
||||||
end
|
end
|
||||||
stdnse.print_debug("Hostname: "..hostn.." IP: "..addr)
|
stdnse.print_debug("Hostname: "..hostn.." IP: "..addr)
|
||||||
local record = { hostname=hostn, address=addr }
|
local record = { hostname=hostn, address=addr }
|
||||||
setmetatable(record, {
|
setmetatable(record, {
|
||||||
__tostring = function(t)
|
__tostring = function(t)
|
||||||
return string.format("%s - %s", t.hostname, t.address)
|
return string.format("%s - %s", t.hostname, t.address)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
results[#results+1] = record
|
results[#results+1] = record
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function srv_main(domainname, srvresults, srv_iter)
|
local function srv_main(domainname, srvresults, srv_iter)
|
||||||
local condvar = nmap.condvar( srvresults )
|
local condvar = nmap.condvar( srvresults )
|
||||||
for name in srv_iter do
|
for name in srv_iter do
|
||||||
local res = resolve(name..'.'..domainname, "SRV")
|
local res = resolve(name..'.'..domainname, "SRV")
|
||||||
if(res) then
|
if(res) then
|
||||||
for _,addr in ipairs(res) do
|
for _,addr in ipairs(res) do
|
||||||
local hostn = name..'.'..domainname
|
local hostn = name..'.'..domainname
|
||||||
addr = stdnse.strsplit(":",addr)
|
addr = stdnse.strsplit(":",addr)
|
||||||
for _, dtype in ipairs({"A", "AAAA"}) do
|
for _, dtype in ipairs({"A", "AAAA"}) do
|
||||||
local srvres = resolve(addr[4], dtype)
|
local srvres = resolve(addr[4], dtype)
|
||||||
if(srvres) then
|
if(srvres) then
|
||||||
for srvhost,srvip in ipairs(srvres) do
|
for srvhost,srvip in ipairs(srvres) do
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
stdnse.print_debug("Added target: "..srvip)
|
stdnse.print_debug("Added target: "..srvip)
|
||||||
local status,err = target.add(srvip)
|
local status,err = target.add(srvip)
|
||||||
end
|
end
|
||||||
stdnse.print_debug("Hostname: "..hostn.." IP: "..srvip)
|
stdnse.print_debug("Hostname: "..hostn.." IP: "..srvip)
|
||||||
local record = { hostname=hostn, address=srvip }
|
local record = { hostname=hostn, address=srvip }
|
||||||
setmetatable(record, {
|
setmetatable(record, {
|
||||||
__tostring = function(t)
|
__tostring = function(t)
|
||||||
return string.format("%s - %s", t.hostname, t.address)
|
return string.format("%s - %s", t.hostname, t.address)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
srvresults[#srvresults+1] = record
|
srvresults[#srvresults+1] = record
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local domainname = stdnse.get_script_args('dns-brute.domain')
|
local domainname = stdnse.get_script_args('dns-brute.domain')
|
||||||
if not domainname then
|
if not domainname then
|
||||||
domainname = guess_domain(host)
|
domainname = guess_domain(host)
|
||||||
end
|
end
|
||||||
if not domainname then
|
if not domainname then
|
||||||
return string.format("Can't guess domain of \"%s\"; use %s.domain script argument.", stdnse.get_hostname(host), SCRIPT_NAME)
|
return string.format("Can't guess domain of \"%s\"; use %s.domain script argument.", stdnse.get_hostname(host), SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not nmap.registry.bruteddomains then
|
if not nmap.registry.bruteddomains then
|
||||||
nmap.registry.bruteddomains = {}
|
nmap.registry.bruteddomains = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
if nmap.registry.bruteddomains[domainname] then
|
if nmap.registry.bruteddomains[domainname] then
|
||||||
stdnse.print_debug("Skipping already-bruted domain %s", domainname)
|
stdnse.print_debug("Skipping already-bruted domain %s", domainname)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
nmap.registry.bruteddomains[domainname] = true
|
nmap.registry.bruteddomains[domainname] = true
|
||||||
stdnse.print_debug("Starting dns-brute at: "..domainname)
|
stdnse.print_debug("Starting dns-brute at: "..domainname)
|
||||||
local max_threads = stdnse.get_script_args('dns-brute.threads') and tonumber( stdnse.get_script_args('dns-brute.threads') ) or 5
|
local max_threads = stdnse.get_script_args('dns-brute.threads') and tonumber( stdnse.get_script_args('dns-brute.threads') ) or 5
|
||||||
local dosrv = stdnse.get_script_args("dns-brute.srv") or false
|
local dosrv = stdnse.get_script_args("dns-brute.srv") or false
|
||||||
stdnse.print_debug("THREADS: "..max_threads)
|
stdnse.print_debug("THREADS: "..max_threads)
|
||||||
-- First look for dns-brute.hostlist
|
-- First look for dns-brute.hostlist
|
||||||
local fileName = stdnse.get_script_args('dns-brute.hostlist')
|
local fileName = stdnse.get_script_args('dns-brute.hostlist')
|
||||||
-- Check fetchfile locations, then relative paths
|
-- Check fetchfile locations, then relative paths
|
||||||
local commFile = (fileName and nmap.fetchfile(fileName)) or fileName
|
local commFile = (fileName and nmap.fetchfile(fileName)) or fileName
|
||||||
-- Finally, fall back to vhosts-default.lst
|
-- Finally, fall back to vhosts-default.lst
|
||||||
commFile = commFile or nmap.fetchfile("nselib/data/vhosts-default.lst")
|
commFile = commFile or nmap.fetchfile("nselib/data/vhosts-default.lst")
|
||||||
local hostlist = {}
|
local hostlist = {}
|
||||||
if commFile then
|
if commFile then
|
||||||
for l in io.lines(commFile) do
|
for l in io.lines(commFile) do
|
||||||
if not l:match("#!comment:") then
|
if not l:match("#!comment:") then
|
||||||
table.insert(hostlist, l)
|
table.insert(hostlist, l)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "%s: Cannot find hostlist file, quitting", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: Cannot find hostlist file, quitting", SCRIPT_NAME)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local threads, results, srvresults = {}, {}, {}
|
local threads, results, srvresults = {}, {}, {}
|
||||||
local condvar = nmap.condvar( results )
|
local condvar = nmap.condvar( results )
|
||||||
local i = 1
|
local i = 1
|
||||||
local howmany = math.floor(#hostlist/max_threads)+1
|
local howmany = math.floor(#hostlist/max_threads)+1
|
||||||
stdnse.print_debug("Hosts per thread: "..howmany)
|
stdnse.print_debug("Hosts per thread: "..howmany)
|
||||||
repeat
|
repeat
|
||||||
local j = math.min(i+howmany, #hostlist)
|
local j = math.min(i+howmany, #hostlist)
|
||||||
local name_iter = array_iter(hostlist, i, j)
|
local name_iter = array_iter(hostlist, i, j)
|
||||||
threads[stdnse.new_thread(thread_main, domainname, results, name_iter)] = true
|
threads[stdnse.new_thread(thread_main, domainname, results, name_iter)] = true
|
||||||
i = j+1
|
i = j+1
|
||||||
until i > #hostlist
|
until i > #hostlist
|
||||||
local done
|
local done
|
||||||
-- wait for all threads to finish
|
-- wait for all threads to finish
|
||||||
while( not(done) ) do
|
while( not(done) ) do
|
||||||
done = true
|
done = true
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if (coroutine.status(thread) ~= "dead") then done = false end
|
if (coroutine.status(thread) ~= "dead") then done = false end
|
||||||
end
|
end
|
||||||
if ( not(done) ) then
|
if ( not(done) ) then
|
||||||
condvar("wait")
|
condvar("wait")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if(dosrv) then
|
if(dosrv) then
|
||||||
-- First look for dns-brute.srvlist
|
-- First look for dns-brute.srvlist
|
||||||
fileName = stdnse.get_script_args('dns-brute.srvlist')
|
fileName = stdnse.get_script_args('dns-brute.srvlist')
|
||||||
-- Check fetchfile locations, then relative paths
|
-- Check fetchfile locations, then relative paths
|
||||||
@@ -245,44 +245,44 @@ action = function(host)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
threads = {}
|
threads = {}
|
||||||
howmany = math.floor(#srvlist/max_threads)+1
|
howmany = math.floor(#srvlist/max_threads)+1
|
||||||
condvar = nmap.condvar( srvresults )
|
condvar = nmap.condvar( srvresults )
|
||||||
stdnse.print_debug("SRV's per thread: "..howmany)
|
stdnse.print_debug("SRV's per thread: "..howmany)
|
||||||
repeat
|
repeat
|
||||||
local j = math.min(i+howmany, #srvlist)
|
local j = math.min(i+howmany, #srvlist)
|
||||||
local name_iter = array_iter(srvlist, i, j)
|
local name_iter = array_iter(srvlist, i, j)
|
||||||
threads[stdnse.new_thread(srv_main, domainname, srvresults, name_iter)] = true
|
threads[stdnse.new_thread(srv_main, domainname, srvresults, name_iter)] = true
|
||||||
i = j+1
|
i = j+1
|
||||||
until i > #srvlist
|
until i > #srvlist
|
||||||
local done
|
local done
|
||||||
-- wait for all threads to finish
|
-- wait for all threads to finish
|
||||||
while( not(done) ) do
|
while( not(done) ) do
|
||||||
done = true
|
done = true
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if (coroutine.status(thread) ~= "dead") then done = false end
|
if (coroutine.status(thread) ~= "dead") then done = false end
|
||||||
end
|
end
|
||||||
if ( not(done) ) then
|
if ( not(done) ) then
|
||||||
condvar("wait")
|
condvar("wait")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "%s: Cannot find srvlist file, skipping", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: Cannot find srvlist file, skipping", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local response = stdnse.output_table()
|
local response = stdnse.output_table()
|
||||||
if(#results==0) then
|
if(#results==0) then
|
||||||
setmetatable(results, { __tostring = function(t) return "No results." end })
|
setmetatable(results, { __tostring = function(t) return "No results." end })
|
||||||
end
|
end
|
||||||
response["DNS Brute-force hostnames"] = results
|
response["DNS Brute-force hostnames"] = results
|
||||||
if(dosrv) then
|
if(dosrv) then
|
||||||
if(#srvresults==0) then
|
if(#srvresults==0) then
|
||||||
setmetatable(srvresults, { __tostring = function(t) return "No results." end })
|
setmetatable(srvresults, { __tostring = function(t) return "No results." end })
|
||||||
end
|
end
|
||||||
response["SRV results"] = srvresults
|
response["SRV results"] = srvresults
|
||||||
end
|
end
|
||||||
return response
|
return response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -59,392 +59,392 @@ hostrule = function(host) return ( arg_domain ~= nil ) end
|
|||||||
local PROBE_HOST = "scanme.nmap.org"
|
local PROBE_HOST = "scanme.nmap.org"
|
||||||
|
|
||||||
local Status = {
|
local Status = {
|
||||||
PASS = "PASS",
|
PASS = "PASS",
|
||||||
FAIL = "FAIL",
|
FAIL = "FAIL",
|
||||||
}
|
}
|
||||||
|
|
||||||
local function isValidSOA(res)
|
local function isValidSOA(res)
|
||||||
if ( not(res) or type(res.answers) ~= "table" or type(res.answers[1].SOA) ~= "table" ) then
|
if ( not(res) or type(res.answers) ~= "table" or type(res.answers[1].SOA) ~= "table" ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local dns_checks = {
|
local dns_checks = {
|
||||||
|
|
||||||
["NS"] = {
|
["NS"] = {
|
||||||
{
|
{
|
||||||
desc = "Recursive queries",
|
desc = "Recursive queries",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of DNS servers"
|
return false, "Failed to retrieve list of DNS servers"
|
||||||
end
|
end
|
||||||
for _, srv in ipairs(res or {}) do
|
for _, srv in ipairs(res or {}) do
|
||||||
local status, res = dns.query(PROBE_HOST, { host = srv, dtype='A' })
|
local status, res = dns.query(PROBE_HOST, { host = srv, dtype='A' })
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
table.insert(result, res)
|
table.insert(result, res)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local output = "None of the servers allow recursive queries."
|
local output = "None of the servers allow recursive queries."
|
||||||
if ( 0 < #result ) then
|
if ( 0 < #result ) then
|
||||||
output = ("The following servers allow recursive queries: %s"):format(stdnse.strjoin(", ", result))
|
output = ("The following servers allow recursive queries: %s"):format(stdnse.strjoin(", ", result))
|
||||||
return true, { status = Status.FAIL, output = output }
|
return true, { status = Status.FAIL, output = output }
|
||||||
end
|
end
|
||||||
return true, { status = Status.PASS, output = output }
|
return true, { status = Status.PASS, output = output }
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "Multiple name servers",
|
desc = "Multiple name servers",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of DNS servers"
|
return false, "Failed to retrieve list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status = Status.FAIL
|
local status = Status.FAIL
|
||||||
if ( 1 < #res ) then
|
if ( 1 < #res ) then
|
||||||
status = Status.PASS
|
status = Status.PASS
|
||||||
end
|
end
|
||||||
return true, { status = status, output = ("Server has %d name servers"):format(#res) }
|
return true, { status = status, output = ("Server has %d name servers"):format(#res) }
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "DNS name server IPs are public",
|
desc = "DNS name server IPs are public",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
|
|
||||||
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of DNS servers"
|
return false, "Failed to retrieve list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, srv in ipairs(res or {}) do
|
for _, srv in ipairs(res or {}) do
|
||||||
local status, res = dns.query(srv, { dtype='A', retAll = true })
|
local status, res = dns.query(srv, { dtype='A', retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, ("Failed to retrieve IP for DNS: %s"):format(srv)
|
return false, ("Failed to retrieve IP for DNS: %s"):format(srv)
|
||||||
end
|
end
|
||||||
for _, ip in ipairs(res) do
|
for _, ip in ipairs(res) do
|
||||||
if ( ipOps.isPrivate(ip) ) then
|
if ( ipOps.isPrivate(ip) ) then
|
||||||
table.insert(result, ip)
|
table.insert(result, ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local output = "All DNS IPs were public"
|
local output = "All DNS IPs were public"
|
||||||
if ( 0 < #result ) then
|
if ( 0 < #result ) then
|
||||||
output = ("The following private IPs were detected: %s"):format(stdnse.strjoin(", ", result))
|
output = ("The following private IPs were detected: %s"):format(stdnse.strjoin(", ", result))
|
||||||
status = Status.FAIL
|
status = Status.FAIL
|
||||||
else
|
else
|
||||||
status = Status.PASS
|
status = Status.PASS
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, { status = status, output = output }
|
return true, { status = status, output = output }
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "DNS server response",
|
desc = "DNS server response",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of DNS servers"
|
return false, "Failed to retrieve list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, srv in ipairs(res or {}) do
|
for _, srv in ipairs(res or {}) do
|
||||||
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
|
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
table.insert(result, res)
|
table.insert(result, res)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local output = "All servers respond to DNS queries"
|
local output = "All servers respond to DNS queries"
|
||||||
if ( 0 < #result ) then
|
if ( 0 < #result ) then
|
||||||
output = ("The following servers did not respond to DNS queries: %s"):format(stdnse.strjoin(", ", result))
|
output = ("The following servers did not respond to DNS queries: %s"):format(stdnse.strjoin(", ", result))
|
||||||
return true, { status = Status.FAIL, output = output }
|
return true, { status = Status.FAIL, output = output }
|
||||||
end
|
end
|
||||||
return true, { status = Status.PASS, output = output }
|
return true, { status = Status.PASS, output = output }
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "Missing nameservers reported by parent",
|
desc = "Missing nameservers reported by parent",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local tld = domain:match("%.(.*)$")
|
local tld = domain:match("%.(.*)$")
|
||||||
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
|
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of TLD DNS servers"
|
return false, "Failed to retrieve list of TLD DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
|
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve a list of parent DNS servers"
|
return false, "Failed to retrieve a list of parent DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(status) or not(parent_res) or type(parent_res.auth) ~= "table" ) then
|
if ( not(status) or not(parent_res) or type(parent_res.auth) ~= "table" ) then
|
||||||
return false, "Failed to retrieve a list of parent DNS servers"
|
return false, "Failed to retrieve a list of parent DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent_dns = {}
|
local parent_dns = {}
|
||||||
for _, auth in ipairs(parent_res.auth) do
|
for _, auth in ipairs(parent_res.auth) do
|
||||||
parent_dns[auth.domain] = true
|
parent_dns[auth.domain] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
|
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve a list of DNS servers"
|
return false, "Failed to retrieve a list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local domain_dns = {}
|
local domain_dns = {}
|
||||||
for _,srv in ipairs(res) do domain_dns[srv] = true end
|
for _,srv in ipairs(res) do domain_dns[srv] = true end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for srv in pairs(domain_dns) do
|
for srv in pairs(domain_dns) do
|
||||||
if ( not(parent_dns[srv]) ) then
|
if ( not(parent_dns[srv]) ) then
|
||||||
table.insert(result, srv)
|
table.insert(result, srv)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( 0 < #result ) then
|
if ( 0 < #result ) then
|
||||||
local output = ("The following servers were found in the zone, but not in the parent: %s"):format(stdnse.strjoin(", ", result))
|
local output = ("The following servers were found in the zone, but not in the parent: %s"):format(stdnse.strjoin(", ", result))
|
||||||
return true, { status = Status.FAIL, output = output }
|
return true, { status = Status.FAIL, output = output }
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, { status = Status.PASS, output = "All DNS servers match" }
|
return true, { status = Status.PASS, output = "All DNS servers match" }
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "Missing nameservers reported by your nameservers",
|
desc = "Missing nameservers reported by your nameservers",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local tld = domain:match("%.(.*)$")
|
local tld = domain:match("%.(.*)$")
|
||||||
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
|
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of TLD DNS servers"
|
return false, "Failed to retrieve list of TLD DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
|
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve a list of parent DNS servers"
|
return false, "Failed to retrieve a list of parent DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(status) or not(parent_res) or type(parent_res.auth) ~= "table" ) then
|
if ( not(status) or not(parent_res) or type(parent_res.auth) ~= "table" ) then
|
||||||
return false, "Failed to retrieve a list of parent DNS servers"
|
return false, "Failed to retrieve a list of parent DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local parent_dns = {}
|
local parent_dns = {}
|
||||||
for _, auth in ipairs(parent_res.auth) do
|
for _, auth in ipairs(parent_res.auth) do
|
||||||
parent_dns[auth.domain] = true
|
parent_dns[auth.domain] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
|
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve a list of DNS servers"
|
return false, "Failed to retrieve a list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local domain_dns = {}
|
local domain_dns = {}
|
||||||
for _,srv in ipairs(res) do domain_dns[srv] = true end
|
for _,srv in ipairs(res) do domain_dns[srv] = true end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for srv in pairs(parent_dns) do
|
for srv in pairs(parent_dns) do
|
||||||
if ( not(domain_dns[srv]) ) then
|
if ( not(domain_dns[srv]) ) then
|
||||||
table.insert(result, srv)
|
table.insert(result, srv)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( 0 < #result ) then
|
if ( 0 < #result ) then
|
||||||
local output = ("The following servers were found in the parent, but not in the zone: %s"):format(stdnse.strjoin(", ", result))
|
local output = ("The following servers were found in the parent, but not in the zone: %s"):format(stdnse.strjoin(", ", result))
|
||||||
return true, { status = Status.FAIL, output = output }
|
return true, { status = Status.FAIL, output = output }
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, { status = Status.PASS, output = "All DNS servers match" }
|
return true, { status = Status.PASS, output = "All DNS servers match" }
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
["SOA"] =
|
["SOA"] =
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
desc = "SOA REFRESH",
|
desc = "SOA REFRESH",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
||||||
if ( not(status) or not(isValidSOA(res)) ) then
|
if ( not(status) or not(isValidSOA(res)) ) then
|
||||||
return false, "Failed to retrieve SOA record"
|
return false, "Failed to retrieve SOA record"
|
||||||
end
|
end
|
||||||
|
|
||||||
local refresh = tonumber(res.answers[1].SOA.refresh)
|
local refresh = tonumber(res.answers[1].SOA.refresh)
|
||||||
if ( not(refresh) ) then
|
if ( not(refresh) ) then
|
||||||
return false, "Failed to retrieve SOA REFRESH"
|
return false, "Failed to retrieve SOA REFRESH"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( refresh < 1200 or refresh > 43200 ) then
|
if ( refresh < 1200 or refresh > 43200 ) then
|
||||||
return true, { status = Status.FAIL, output = ("SOA REFRESH was NOT within recommended range (%ss)"):format(refresh) }
|
return true, { status = Status.FAIL, output = ("SOA REFRESH was NOT within recommended range (%ss)"):format(refresh) }
|
||||||
else
|
else
|
||||||
return true, { status = Status.PASS, output = ("SOA REFRESH was within recommended range (%ss)"):format(refresh) }
|
return true, { status = Status.PASS, output = ("SOA REFRESH was within recommended range (%ss)"):format(refresh) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "SOA RETRY",
|
desc = "SOA RETRY",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
||||||
if ( not(status) or not(isValidSOA(res)) ) then
|
if ( not(status) or not(isValidSOA(res)) ) then
|
||||||
return false, "Failed to retrieve SOA record"
|
return false, "Failed to retrieve SOA record"
|
||||||
end
|
end
|
||||||
|
|
||||||
local retry = tonumber(res.answers[1].SOA.retry)
|
local retry = tonumber(res.answers[1].SOA.retry)
|
||||||
if ( not(retry) ) then
|
if ( not(retry) ) then
|
||||||
return false, "Failed to retrieve SOA RETRY"
|
return false, "Failed to retrieve SOA RETRY"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( retry < 180 ) then
|
if ( retry < 180 ) then
|
||||||
return true, { status = Status.FAIL, output = ("SOA RETRY was NOT within recommended range (%ss)"):format(retry) }
|
return true, { status = Status.FAIL, output = ("SOA RETRY was NOT within recommended range (%ss)"):format(retry) }
|
||||||
else
|
else
|
||||||
return true, { status = Status.PASS, output = ("SOA RETRY was within recommended range (%ss)"):format(retry) }
|
return true, { status = Status.PASS, output = ("SOA RETRY was within recommended range (%ss)"):format(retry) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "SOA EXPIRE",
|
desc = "SOA EXPIRE",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
||||||
if ( not(status) or not(isValidSOA(res)) ) then
|
if ( not(status) or not(isValidSOA(res)) ) then
|
||||||
return false, "Failed to retrieve SOA record"
|
return false, "Failed to retrieve SOA record"
|
||||||
end
|
end
|
||||||
|
|
||||||
local expire = tonumber(res.answers[1].SOA.expire)
|
local expire = tonumber(res.answers[1].SOA.expire)
|
||||||
if ( not(expire) ) then
|
if ( not(expire) ) then
|
||||||
return false, "Failed to retrieve SOA EXPIRE"
|
return false, "Failed to retrieve SOA EXPIRE"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( expire < 1209600 or expire > 2419200 ) then
|
if ( expire < 1209600 or expire > 2419200 ) then
|
||||||
return true, { status = Status.FAIL, output = ("SOA EXPIRE was NOT within recommended range (%ss)"):format(expire) }
|
return true, { status = Status.FAIL, output = ("SOA EXPIRE was NOT within recommended range (%ss)"):format(expire) }
|
||||||
else
|
else
|
||||||
return true, { status = Status.PASS, output = ("SOA EXPIRE was within recommended range (%ss)"):format(expire) }
|
return true, { status = Status.PASS, output = ("SOA EXPIRE was within recommended range (%ss)"):format(expire) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "SOA MNAME entry check",
|
desc = "SOA MNAME entry check",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
|
||||||
if ( not(status) or not(isValidSOA(res)) ) then
|
if ( not(status) or not(isValidSOA(res)) ) then
|
||||||
return false, "Failed to retrieve SOA record"
|
return false, "Failed to retrieve SOA record"
|
||||||
end
|
end
|
||||||
local mname = res.answers[1].SOA.mname
|
local mname = res.answers[1].SOA.mname
|
||||||
|
|
||||||
status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of DNS servers"
|
return false, "Failed to retrieve list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, srv in ipairs(res or {}) do
|
for _, srv in ipairs(res or {}) do
|
||||||
if ( srv == mname ) then
|
if ( srv == mname ) then
|
||||||
return true, { status = Status.PASS, output = "SOA MNAME record is listed as DNS server" }
|
return true, { status = Status.PASS, output = "SOA MNAME record is listed as DNS server" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true, { status = Status.FAIL, output = "SOA MNAME record is NOT listed as DNS server" }
|
return true, { status = Status.FAIL, output = "SOA MNAME record is NOT listed as DNS server" }
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "Zone serial numbers",
|
desc = "Zone serial numbers",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of DNS servers"
|
return false, "Failed to retrieve list of DNS servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
local serial
|
local serial
|
||||||
|
|
||||||
for _, srv in ipairs(res or {}) do
|
for _, srv in ipairs(res or {}) do
|
||||||
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
|
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
|
||||||
if ( not(status) or not(isValidSOA(res)) ) then
|
if ( not(status) or not(isValidSOA(res)) ) then
|
||||||
return false, "Failed to retrieve SOA record"
|
return false, "Failed to retrieve SOA record"
|
||||||
end
|
end
|
||||||
|
|
||||||
local s = res.answers[1].SOA.serial
|
local s = res.answers[1].SOA.serial
|
||||||
if ( not(serial) ) then
|
if ( not(serial) ) then
|
||||||
serial = s
|
serial = s
|
||||||
elseif( serial ~= s ) then
|
elseif( serial ~= s ) then
|
||||||
return true, { status = Status.FAIL, output = "Different zone serials were detected" }
|
return true, { status = Status.FAIL, output = "Different zone serials were detected" }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, { status = Status.PASS, output = "Zone serials match" }
|
return true, { status = Status.PASS, output = "Zone serials match" }
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
["MX"] = {
|
["MX"] = {
|
||||||
|
|
||||||
{
|
{
|
||||||
desc = "Reverse MX A records",
|
desc = "Reverse MX A records",
|
||||||
func = function(domain, server)
|
func = function(domain, server)
|
||||||
local status, res = dns.query(domain, { host = server, dtype='MX', retAll = true })
|
local status, res = dns.query(domain, { host = server, dtype='MX', retAll = true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve list of mail servers"
|
return false, "Failed to retrieve list of mail servers"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, record in ipairs(res or {}) do
|
for _, record in ipairs(res or {}) do
|
||||||
local prio, mx = record:match("^(%d*):([^:]*)")
|
local prio, mx = record:match("^(%d*):([^:]*)")
|
||||||
local ips
|
local ips
|
||||||
status, ips = dns.query(mx, { dtype='A', retAll=true })
|
status, ips = dns.query(mx, { dtype='A', retAll=true })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to retrieve A records for MX"
|
return false, "Failed to retrieve A records for MX"
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, ip in ipairs(ips) do
|
for _, ip in ipairs(ips) do
|
||||||
local status, res = dns.query(dns.reverse(ip), { dtype='PTR' })
|
local status, res = dns.query(dns.reverse(ip), { dtype='PTR' })
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
table.insert(result, ip)
|
table.insert(result, ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local output = "All MX records have PTR records"
|
local output = "All MX records have PTR records"
|
||||||
if ( 0 < #result ) then
|
if ( 0 < #result ) then
|
||||||
output = ("The following IPs do not have PTR records: %s"):format(stdnse.strjoin(", ", result))
|
output = ("The following IPs do not have PTR records: %s"):format(stdnse.strjoin(", ", result))
|
||||||
return true, { status = Status.FAIL, output = output }
|
return true, { status = Status.FAIL, output = output }
|
||||||
end
|
end
|
||||||
return true, { status = Status.PASS, output = output }
|
return true, { status = Status.PASS, output = output }
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local server = host.ip
|
local server = host.ip
|
||||||
local output = { name = ("DNS check results for domain: %s"):format(arg_domain) }
|
local output = { name = ("DNS check results for domain: %s"):format(arg_domain) }
|
||||||
|
|
||||||
for group in pairs(dns_checks) do
|
for group in pairs(dns_checks) do
|
||||||
local group_output = { name = group }
|
local group_output = { name = group }
|
||||||
for _, check in ipairs(dns_checks[group]) do
|
for _, check in ipairs(dns_checks[group]) do
|
||||||
local status, res = check.func(arg_domain, server)
|
local status, res = check.func(arg_domain, server)
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local test_res = ("%s - %s"):format(res.status, check.desc)
|
local test_res = ("%s - %s"):format(res.status, check.desc)
|
||||||
table.insert(group_output, { name = test_res, res.output })
|
table.insert(group_output, { name = test_res, res.output })
|
||||||
else
|
else
|
||||||
local test_res = ("ERROR - %s"):format(check.desc)
|
local test_res = ("ERROR - %s"):format(check.desc)
|
||||||
table.insert(group_output, { name = test_res, res })
|
table.insert(group_output, { name = test_res, res })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(output, group_output)
|
table.insert(output, group_output)
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,300 +58,300 @@ local argMask = stdnse.get_script_args(SCRIPT_NAME .. '.mask') or 24
|
|||||||
local argAddr = stdnse.get_script_args(SCRIPT_NAME .. '.address')
|
local argAddr = stdnse.get_script_args(SCRIPT_NAME .. '.address')
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if ( not(argDomain) or nmap.address_family() ~= "inet" ) then
|
if ( not(argDomain) or nmap.address_family() ~= "inet" ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = function(host, port)
|
portrule = function(host, port)
|
||||||
if ( nmap.address_family() ~= "inet" ) then
|
if ( nmap.address_family() ~= "inet" ) then
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
return shortport.port_or_service(53, "domain", {"tcp", "udp"})(host, port)
|
return shortport.port_or_service(53, "domain", {"tcp", "udp"})(host, port)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local areaIPs = {
|
local areaIPs = {
|
||||||
A4 = {ip=47763456, desc="GB,A4,Bath"},
|
A4 = {ip=47763456, desc="GB,A4,Bath"},
|
||||||
A5 = {ip=1043402336, desc="GB,A5,Biggleswade"},
|
A5 = {ip=1043402336, desc="GB,A5,Biggleswade"},
|
||||||
A6 = {ip=1364222182, desc="FR,A6,Ch<43>vremont"},
|
A6 = {ip=1364222182, desc="FR,A6,Ch<43>vremont"},
|
||||||
A7 = {ip=35357952, desc="GB,A7,Birmingham"},
|
A7 = {ip=35357952, desc="GB,A7,Birmingham"},
|
||||||
A8 = {ip=1050694009, desc="FR,A8,Romainville"},
|
A8 = {ip=1050694009, desc="FR,A8,Romainville"},
|
||||||
A9 = {ip=534257152, desc="FR,A9,Montpellier"},
|
A9 = {ip=534257152, desc="FR,A9,Montpellier"},
|
||||||
AB = {ip=2156920832, desc="CA,AB,Edmonton"},
|
AB = {ip=2156920832, desc="CA,AB,Edmonton"},
|
||||||
AK = {ip=202125312, desc="US,AK,Anchorage"},
|
AK = {ip=202125312, desc="US,AK,Anchorage"},
|
||||||
B1 = {ip=1041724648, desc="FR,B1,Robert"},
|
B1 = {ip=1041724648, desc="FR,B1,Robert"},
|
||||||
B2 = {ip=35138048, desc="GB,B2,Bournemouth"},
|
B2 = {ip=35138048, desc="GB,B2,Bournemouth"},
|
||||||
B3 = {ip=33949696, desc="FR,B3,Toulouse"},
|
B3 = {ip=33949696, desc="FR,B3,Toulouse"},
|
||||||
B4 = {ip=1050704998, desc="FR,B4,Lomme"},
|
B4 = {ip=1050704998, desc="FR,B4,Lomme"},
|
||||||
B5 = {ip=35213312, desc="GB,B5,Wembley"},
|
B5 = {ip=35213312, desc="GB,B5,Wembley"},
|
||||||
B6 = {ip=773106752, desc="FR,B6,Amiens"},
|
B6 = {ip=773106752, desc="FR,B6,Amiens"},
|
||||||
B7 = {ip=35148800, desc="GB,B7,Bristol"},
|
B7 = {ip=35148800, desc="GB,B7,Bristol"},
|
||||||
B8 = {ip=786088496, desc="FR,B8,Valbonne"},
|
B8 = {ip=786088496, desc="FR,B8,Valbonne"},
|
||||||
B9 = {ip=33753088, desc="FR,B9,Lyon"},
|
B9 = {ip=33753088, desc="FR,B9,Lyon"},
|
||||||
BC = {ip=201674096, desc="CA,BC,Victoria"},
|
BC = {ip=201674096, desc="CA,BC,Victoria"},
|
||||||
C1 = {ip=522223616, desc="FR,C1,Strasbourg"},
|
C1 = {ip=522223616, desc="FR,C1,Strasbourg"},
|
||||||
C2 = {ip=41598976, desc="GB,C2,Halifax"},
|
C2 = {ip=41598976, desc="GB,C2,Halifax"},
|
||||||
C3 = {ip=534676272, desc="GB,C3,Cambridge"},
|
C3 = {ip=534676272, desc="GB,C3,Cambridge"},
|
||||||
C5 = {ip=1043410032, desc="GB,C5,Runcorn"},
|
C5 = {ip=1043410032, desc="GB,C5,Runcorn"},
|
||||||
C6 = {ip=773987544, desc="GB,C6,Saltash"},
|
C6 = {ip=773987544, desc="GB,C6,Saltash"},
|
||||||
C7 = {ip=35165184, desc="GB,C7,Coventry"},
|
C7 = {ip=35165184, desc="GB,C7,Coventry"},
|
||||||
C8 = {ip=35248128, desc="GB,C8,Croydon"},
|
C8 = {ip=35248128, desc="GB,C8,Croydon"},
|
||||||
C9 = {ip=1892301824, desc="PH,C9,Iloilo"},
|
C9 = {ip=1892301824, desc="PH,C9,Iloilo"},
|
||||||
D1 = {ip=35414016, desc="GB,D1,Darlington"},
|
D1 = {ip=35414016, desc="GB,D1,Darlington"},
|
||||||
D2 = {ip=35164672, desc="GB,D2,Derby"},
|
D2 = {ip=35164672, desc="GB,D2,Derby"},
|
||||||
D3 = {ip=35301376, desc="GB,D3,Chesterfield"},
|
D3 = {ip=35301376, desc="GB,D3,Chesterfield"},
|
||||||
D4 = {ip=1043450424, desc="GB,D4,Barnstaple"},
|
D4 = {ip=1043450424, desc="GB,D4,Barnstaple"},
|
||||||
D5 = {ip=2036385792, desc="PH,D5,Legaspi"},
|
D5 = {ip=2036385792, desc="PH,D5,Legaspi"},
|
||||||
D7 = {ip=41451520, desc="GB,D7,Dudley"},
|
D7 = {ip=41451520, desc="GB,D7,Dudley"},
|
||||||
D8 = {ip=35279104, desc="GB,D8,Durham"},
|
D8 = {ip=35279104, desc="GB,D8,Durham"},
|
||||||
D9 = {ip=460228608, desc="PH,D9,Manila"},
|
D9 = {ip=460228608, desc="PH,D9,Manila"},
|
||||||
DC = {ip=68514448, desc="US,DC,Washington"},
|
DC = {ip=68514448, desc="US,DC,Washington"},
|
||||||
E1 = {ip=1040645056, desc="GB,E1,Beverley"},
|
E1 = {ip=1040645056, desc="GB,E1,Beverley"},
|
||||||
E2 = {ip=35206912, desc="GB,E2,Brighton"},
|
E2 = {ip=35206912, desc="GB,E2,Brighton"},
|
||||||
E3 = {ip=47822848, desc="GB,E3,Enfield"},
|
E3 = {ip=47822848, desc="GB,E3,Enfield"},
|
||||||
E4 = {ip=39874560, desc="GB,E4,Colchester"},
|
E4 = {ip=39874560, desc="GB,E4,Colchester"},
|
||||||
E5 = {ip=35270656, desc="GB,E5,Gateshead"},
|
E5 = {ip=35270656, desc="GB,E5,Gateshead"},
|
||||||
E6 = {ip=1368606720, desc="GB,E6,Coleford"},
|
E6 = {ip=1368606720, desc="GB,E6,Coleford"},
|
||||||
E7 = {ip=1051376056, desc="GB,E7,Woolwich"},
|
E7 = {ip=1051376056, desc="GB,E7,Woolwich"},
|
||||||
E8 = {ip=1044737528, desc="GB,E8,Hackney"},
|
E8 = {ip=1044737528, desc="GB,E8,Hackney"},
|
||||||
F1 = {ip=1043451648, desc="GB,F1,Hammersmith"},
|
F1 = {ip=1043451648, desc="GB,F1,Hammersmith"},
|
||||||
F2 = {ip=35176448, desc="GB,F2,Basingstoke"},
|
F2 = {ip=35176448, desc="GB,F2,Basingstoke"},
|
||||||
F4 = {ip=47998976, desc="GB,F4,Harrow"},
|
F4 = {ip=47998976, desc="GB,F4,Harrow"},
|
||||||
F5 = {ip=1040622704, desc="GB,F5,Hart"},
|
F5 = {ip=1040622704, desc="GB,F5,Hart"},
|
||||||
F6 = {ip=35230720, desc="GB,F6,Romford"},
|
F6 = {ip=35230720, desc="GB,F6,Romford"},
|
||||||
F8 = {ip=35214848, desc="GB,F8,Watford"},
|
F8 = {ip=35214848, desc="GB,F8,Watford"},
|
||||||
F9 = {ip=41693184, desc="GB,F9,Uxbridge"},
|
F9 = {ip=41693184, desc="GB,F9,Uxbridge"},
|
||||||
G1 = {ip=41437184, desc="GB,G1,Hounslow"},
|
G1 = {ip=41437184, desc="GB,G1,Hounslow"},
|
||||||
G2 = {ip=35188224, desc="GB,G2,Ryde"},
|
G2 = {ip=35188224, desc="GB,G2,Ryde"},
|
||||||
G3 = {ip=41861120, desc="GB,G3,Islington"},
|
G3 = {ip=41861120, desc="GB,G3,Islington"},
|
||||||
G4 = {ip=1040704992, desc="GB,G4,Kensington"},
|
G4 = {ip=1040704992, desc="GB,G4,Kensington"},
|
||||||
G5 = {ip=41506816, desc="GB,G5,Ashford"},
|
G5 = {ip=41506816, desc="GB,G5,Ashford"},
|
||||||
G6 = {ip=786894336, desc="GB,G6,Hull"},
|
G6 = {ip=786894336, desc="GB,G6,Hull"},
|
||||||
G8 = {ip=40112128, desc="GB,G8,Huddersfield"},
|
G8 = {ip=40112128, desc="GB,G8,Huddersfield"},
|
||||||
G9 = {ip=1380217968, desc="GB,G9,Knowsley"},
|
G9 = {ip=1380217968, desc="GB,G9,Knowsley"},
|
||||||
H1 = {ip=1044731464, desc="GB,H1,Lambeth"},
|
H1 = {ip=1044731464, desc="GB,H1,Lambeth"},
|
||||||
H2 = {ip=3512017264, desc="GB,H2,Earby"},
|
H2 = {ip=3512017264, desc="GB,H2,Earby"},
|
||||||
H3 = {ip=35221504, desc="GB,H3,Leeds"},
|
H3 = {ip=35221504, desc="GB,H3,Leeds"},
|
||||||
H4 = {ip=35158016, desc="GB,H4,Leicester"},
|
H4 = {ip=35158016, desc="GB,H4,Leicester"},
|
||||||
H5 = {ip=1043402716, desc="GB,H5,Loughborough"},
|
H5 = {ip=1043402716, desc="GB,H5,Loughborough"},
|
||||||
H6 = {ip=41732608, desc="GB,H6,Catford"},
|
H6 = {ip=41732608, desc="GB,H6,Catford"},
|
||||||
H7 = {ip=41863168, desc="GB,H7,Lincoln"},
|
H7 = {ip=41863168, desc="GB,H7,Lincoln"},
|
||||||
H8 = {ip=35294976, desc="GB,H8,Liverpool"},
|
H8 = {ip=35294976, desc="GB,H8,Liverpool"},
|
||||||
H9 = {ip=35196928, desc="GB,H9,London"},
|
H9 = {ip=35196928, desc="GB,H9,London"},
|
||||||
I1 = {ip=35253760, desc="GB,I1,Luton"},
|
I1 = {ip=35253760, desc="GB,I1,Luton"},
|
||||||
I2 = {ip=35263488, desc="GB,I2,Manchester"},
|
I2 = {ip=35263488, desc="GB,I2,Manchester"},
|
||||||
I3 = {ip=47714304, desc="GB,I3,Rochester"},
|
I3 = {ip=47714304, desc="GB,I3,Rochester"},
|
||||||
I4 = {ip=1298651136, desc="GB,I4,Morden"},
|
I4 = {ip=1298651136, desc="GB,I4,Morden"},
|
||||||
I5 = {ip=1382961968, desc="GB,I5,Middlesborough"},
|
I5 = {ip=1382961968, desc="GB,I5,Middlesborough"},
|
||||||
I8 = {ip=1371219061, desc="GB,I8,Stepney"},
|
I8 = {ip=1371219061, desc="GB,I8,Stepney"},
|
||||||
I9 = {ip=35282944, desc="GB,I9,Norwich"},
|
I9 = {ip=35282944, desc="GB,I9,Norwich"},
|
||||||
IA = {ip=201438272, desc="US,IA,Urbandale"},
|
IA = {ip=201438272, desc="US,IA,Urbandale"},
|
||||||
J1 = {ip=523578880, desc="GB,J1,Daventry"},
|
J1 = {ip=523578880, desc="GB,J1,Daventry"},
|
||||||
J2 = {ip=788492344, desc="GB,J2,Grimsby"},
|
J2 = {ip=788492344, desc="GB,J2,Grimsby"},
|
||||||
J3 = {ip=3282790208, desc="GB,J3,Flixborough"},
|
J3 = {ip=3282790208, desc="GB,J3,Flixborough"},
|
||||||
J5 = {ip=41759232, desc="GB,J5,Wallsend"},
|
J5 = {ip=41759232, desc="GB,J5,Wallsend"},
|
||||||
J6 = {ip=1043412268, desc="GB,J6,Alnwick"},
|
J6 = {ip=1043412268, desc="GB,J6,Alnwick"},
|
||||||
J7 = {ip=41783296, desc="GB,J7,Harrogate"},
|
J7 = {ip=41783296, desc="GB,J7,Harrogate"},
|
||||||
J8 = {ip=35160064, desc="GB,J8,Nottingham"},
|
J8 = {ip=35160064, desc="GB,J8,Nottingham"},
|
||||||
J9 = {ip=47742976, desc="GB,J9,Newark"},
|
J9 = {ip=47742976, desc="GB,J9,Newark"},
|
||||||
JA = {ip=1476096512, desc="RU,JA,Kurilsk"},
|
JA = {ip=1476096512, desc="RU,JA,Kurilsk"},
|
||||||
K1 = {ip=48015360, desc="GB,K1,Oldham"},
|
K1 = {ip=48015360, desc="GB,K1,Oldham"},
|
||||||
K2 = {ip=1043402360, desc="GB,K2,Kidlington"},
|
K2 = {ip=1043402360, desc="GB,K2,Kidlington"},
|
||||||
K3 = {ip=39956480, desc="GB,K3,Peterborough"},
|
K3 = {ip=39956480, desc="GB,K3,Peterborough"},
|
||||||
K4 = {ip=41735168, desc="GB,K4,Plymouth"},
|
K4 = {ip=41735168, desc="GB,K4,Plymouth"},
|
||||||
K5 = {ip=775747568, desc="GB,K5,Poole"},
|
K5 = {ip=775747568, desc="GB,K5,Poole"},
|
||||||
K6 = {ip=774162844, desc="GB,K6,Portsmouth"},
|
K6 = {ip=774162844, desc="GB,K6,Portsmouth"},
|
||||||
K7 = {ip=41746432, desc="GB,K7,Reading"},
|
K7 = {ip=41746432, desc="GB,K7,Reading"},
|
||||||
K8 = {ip=35229696, desc="GB,K8,Ilford"},
|
K8 = {ip=35229696, desc="GB,K8,Ilford"},
|
||||||
L1 = {ip=47773696, desc="GB,L1,Twickenham"},
|
L1 = {ip=47773696, desc="GB,L1,Twickenham"},
|
||||||
L2 = {ip=48103424, desc="GB,L2,Rochdale"},
|
L2 = {ip=48103424, desc="GB,L2,Rochdale"},
|
||||||
L3 = {ip=35304192, desc="GB,L3,Rotherham"},
|
L3 = {ip=35304192, desc="GB,L3,Rotherham"},
|
||||||
L4 = {ip=1043416984, desc="GB,L4,Oakham"},
|
L4 = {ip=1043416984, desc="GB,L4,Oakham"},
|
||||||
L5 = {ip=772988024, desc="GB,L5,Salford"},
|
L5 = {ip=772988024, desc="GB,L5,Salford"},
|
||||||
L6 = {ip=35336192, desc="GB,L6,Shrewsbury"},
|
L6 = {ip=35336192, desc="GB,L6,Shrewsbury"},
|
||||||
L7 = {ip=1043419464, desc="GB,L7,Oldbury"},
|
L7 = {ip=1043419464, desc="GB,L7,Oldbury"},
|
||||||
L8 = {ip=39936000, desc="GB,L8,Lytham"},
|
L8 = {ip=39936000, desc="GB,L8,Lytham"},
|
||||||
L9 = {ip=35304448, desc="GB,L9,Sheffield"},
|
L9 = {ip=35304448, desc="GB,L9,Sheffield"},
|
||||||
M1 = {ip=35384320, desc="GB,M1,Slough"},
|
M1 = {ip=35384320, desc="GB,M1,Slough"},
|
||||||
M2 = {ip=41470976, desc="GB,M2,Solihull"},
|
M2 = {ip=41470976, desc="GB,M2,Solihull"},
|
||||||
M4 = {ip=35139584, desc="GB,M4,Southampton"},
|
M4 = {ip=35139584, desc="GB,M4,Southampton"},
|
||||||
M5 = {ip=1043402176, desc="GB,M5,Southend-on-sea"},
|
M5 = {ip=1043402176, desc="GB,M5,Southend-on-sea"},
|
||||||
M6 = {ip=773986248, desc="GB,M6,Hill"},
|
M6 = {ip=773986248, desc="GB,M6,Hill"},
|
||||||
M8 = {ip=1443330688, desc="GB,M8,Camberwell"},
|
M8 = {ip=1443330688, desc="GB,M8,Camberwell"},
|
||||||
M9 = {ip=35322880, desc="GB,M9,Stafford"},
|
M9 = {ip=35322880, desc="GB,M9,Stafford"},
|
||||||
MB = {ip=1076550400, desc="CA,MB,Winnipeg"},
|
MB = {ip=1076550400, desc="CA,MB,Winnipeg"},
|
||||||
MI = {ip=201393888, desc="US,MI,Saginaw"},
|
MI = {ip=201393888, desc="US,MI,Saginaw"},
|
||||||
N1 = {ip=1318741928, desc="GB,N1,Haydock"},
|
N1 = {ip=1318741928, desc="GB,N1,Haydock"},
|
||||||
N2 = {ip=35266560, desc="GB,N2,Stockport"},
|
N2 = {ip=35266560, desc="GB,N2,Stockport"},
|
||||||
N3 = {ip=41832448, desc="GB,N3,Stockton-on-tees"},
|
N3 = {ip=41832448, desc="GB,N3,Stockton-on-tees"},
|
||||||
N4 = {ip=3231559680, desc="GB,N4,Longport"},
|
N4 = {ip=3231559680, desc="GB,N4,Longport"},
|
||||||
N5 = {ip=1043424608, desc="GB,N5,Beccles"},
|
N5 = {ip=1043424608, desc="GB,N5,Beccles"},
|
||||||
N6 = {ip=35276800, desc="GB,N6,Sunderland"},
|
N6 = {ip=35276800, desc="GB,N6,Sunderland"},
|
||||||
N7 = {ip=41551872, desc="GB,N7,Tadworth"},
|
N7 = {ip=41551872, desc="GB,N7,Tadworth"},
|
||||||
N8 = {ip=41697280, desc="GB,N8,Sutton"},
|
N8 = {ip=41697280, desc="GB,N8,Sutton"},
|
||||||
N9 = {ip=35252736, desc="GB,N9,Swindon"},
|
N9 = {ip=35252736, desc="GB,N9,Swindon"},
|
||||||
NB = {ip=2211053568, desc="CA,NB,Fredericton"},
|
NB = {ip=2211053568, desc="CA,NB,Fredericton"},
|
||||||
ND = {ip=201473536, desc="US,ND,Bismarck"},
|
ND = {ip=201473536, desc="US,ND,Bismarck"},
|
||||||
NH = {ip=201772808, desc="US,NH,Laconia"},
|
NH = {ip=201772808, desc="US,NH,Laconia"},
|
||||||
NJ = {ip=201352704, desc="US,NJ,Piscataway"},
|
NJ = {ip=201352704, desc="US,NJ,Piscataway"},
|
||||||
NS = {ip=3226164992, desc="CA,NS,Halifax"},
|
NS = {ip=3226164992, desc="CA,NS,Halifax"},
|
||||||
NT = {ip=3332472320, desc="CA,NT,Yellowknife"},
|
NT = {ip=3332472320, desc="CA,NT,Yellowknife"},
|
||||||
NV = {ip=202261184, desc="US,NV,Henderson"},
|
NV = {ip=202261184, desc="US,NV,Henderson"},
|
||||||
O2 = {ip=40251392, desc="GB,O2,Telford"},
|
O2 = {ip=40251392, desc="GB,O2,Telford"},
|
||||||
O3 = {ip=35230208, desc="GB,O3,Grays"},
|
O3 = {ip=35230208, desc="GB,O3,Grays"},
|
||||||
O4 = {ip=35318784, desc="GB,O4,Torquay"},
|
O4 = {ip=35318784, desc="GB,O4,Torquay"},
|
||||||
O5 = {ip=1368498352, desc="GB,O5,Poplar"},
|
O5 = {ip=1368498352, desc="GB,O5,Poplar"},
|
||||||
O6 = {ip=1546138112, desc="GB,O6,Stretford"},
|
O6 = {ip=1546138112, desc="GB,O6,Stretford"},
|
||||||
O7 = {ip=35219456, desc="GB,O7,Wakefield"},
|
O7 = {ip=35219456, desc="GB,O7,Wakefield"},
|
||||||
O8 = {ip=35321856, desc="GB,O8,Walsall"},
|
O8 = {ip=35321856, desc="GB,O8,Walsall"},
|
||||||
O9 = {ip=1359108248, desc="GB,O9,Walthamstow"},
|
O9 = {ip=1359108248, desc="GB,O9,Walthamstow"},
|
||||||
ON = {ip=201620304, desc="CA,ON,Ottawa"},
|
ON = {ip=201620304, desc="CA,ON,Ottawa"},
|
||||||
P1 = {ip=1043431736, desc="GB,P1,Wandsworth"},
|
P1 = {ip=1043431736, desc="GB,P1,Wandsworth"},
|
||||||
P2 = {ip=35260416, desc="GB,P2,Warrington"},
|
P2 = {ip=35260416, desc="GB,P2,Warrington"},
|
||||||
P3 = {ip=41766912, desc="GB,P3,Nuneaton"},
|
P3 = {ip=41766912, desc="GB,P3,Nuneaton"},
|
||||||
P4 = {ip=41893888, desc="GB,P4,Newbury"},
|
P4 = {ip=41893888, desc="GB,P4,Newbury"},
|
||||||
P5 = {ip=772987648, desc="GB,P5,Westminster"},
|
P5 = {ip=772987648, desc="GB,P5,Westminster"},
|
||||||
P7 = {ip=41466624, desc="GB,P7,Wigan"},
|
P7 = {ip=41466624, desc="GB,P7,Wigan"},
|
||||||
P8 = {ip=48087808, desc="GB,P8,Salisbury"},
|
P8 = {ip=48087808, desc="GB,P8,Salisbury"},
|
||||||
P9 = {ip=41793536, desc="GB,P9,Maidenhead"},
|
P9 = {ip=41793536, desc="GB,P9,Maidenhead"},
|
||||||
Q1 = {ip=41457664, desc="GB,Q1,Wallasey"},
|
Q1 = {ip=41457664, desc="GB,Q1,Wallasey"},
|
||||||
Q2 = {ip=1040739840, desc="GB,Q2,Wokingham"},
|
Q2 = {ip=1040739840, desc="GB,Q2,Wokingham"},
|
||||||
Q3 = {ip=35323392, desc="GB,Q3,Wolverhampton"},
|
Q3 = {ip=35323392, desc="GB,Q3,Wolverhampton"},
|
||||||
Q4 = {ip=539624744, desc="GB,Q4,Redditch"},
|
Q4 = {ip=539624744, desc="GB,Q4,Redditch"},
|
||||||
Q5 = {ip=1043415688, desc="GB,Q5,Wetherby"},
|
Q5 = {ip=1043415688, desc="GB,Q5,Wetherby"},
|
||||||
Q6 = {ip=1043439984, desc="GB,Q6,Antrim"},
|
Q6 = {ip=1043439984, desc="GB,Q6,Antrim"},
|
||||||
Q7 = {ip=41811456, desc="GB,Q7,Newtownards"},
|
Q7 = {ip=41811456, desc="GB,Q7,Newtownards"},
|
||||||
Q8 = {ip=1347208672, desc="GB,Q8,Armagh"},
|
Q8 = {ip=1347208672, desc="GB,Q8,Armagh"},
|
||||||
Q9 = {ip=1044726432, desc="GB,Q9,Connor"},
|
Q9 = {ip=1044726432, desc="GB,Q9,Connor"},
|
||||||
QC = {ip=2210594816, desc="CA,QC,Varennes"},
|
QC = {ip=2210594816, desc="CA,QC,Varennes"},
|
||||||
R1 = {ip=1482707288, desc="GB,R1,Ballymoney"},
|
R1 = {ip=1482707288, desc="GB,R1,Ballymoney"},
|
||||||
R3 = {ip=47828992, desc="GB,R3,Belfast"},
|
R3 = {ip=47828992, desc="GB,R3,Belfast"},
|
||||||
R4 = {ip=1051352576, desc="GB,R4,Eden"},
|
R4 = {ip=1051352576, desc="GB,R4,Eden"},
|
||||||
R5 = {ip=1056827328, desc="GB,R5,Castlereagh"},
|
R5 = {ip=1056827328, desc="GB,R5,Castlereagh"},
|
||||||
R6 = {ip=47895040, desc="GB,R6,Coleraine"},
|
R6 = {ip=47895040, desc="GB,R6,Coleraine"},
|
||||||
R7 = {ip=3270400320, desc="GB,R7,Dunmore"},
|
R7 = {ip=3270400320, desc="GB,R7,Dunmore"},
|
||||||
R8 = {ip=1367996672, desc="GB,R8,Portadown"},
|
R8 = {ip=1367996672, desc="GB,R8,Portadown"},
|
||||||
R9 = {ip=773985608, desc="GB,R9,Square"},
|
R9 = {ip=773985608, desc="GB,R9,Square"},
|
||||||
RI = {ip=67285760, desc="US,RI,Providence"},
|
RI = {ip=67285760, desc="US,RI,Providence"},
|
||||||
S1 = {ip=1040409048, desc="GB,S1,Drummond"},
|
S1 = {ip=1040409048, desc="GB,S1,Drummond"},
|
||||||
S2 = {ip=1353842208, desc="GB,S2,Enniskillen"},
|
S2 = {ip=1353842208, desc="GB,S2,Enniskillen"},
|
||||||
S3 = {ip=1368133632, desc="GB,S3,Larne"},
|
S3 = {ip=1368133632, desc="GB,S3,Larne"},
|
||||||
S4 = {ip=1446384520, desc="GB,S4,Ardmore"},
|
S4 = {ip=1446384520, desc="GB,S4,Ardmore"},
|
||||||
S5 = {ip=1043419184, desc="GB,S5,Lisburn"},
|
S5 = {ip=1043419184, desc="GB,S5,Lisburn"},
|
||||||
S6 = {ip=1056826304, desc="GB,S6,Londonderry"},
|
S6 = {ip=1056826304, desc="GB,S6,Londonderry"},
|
||||||
S7 = {ip=1359111383, desc="GB,S7,Curran"},
|
S7 = {ip=1359111383, desc="GB,S7,Curran"},
|
||||||
S8 = {ip=1369435392, desc="GB,S8,Waterfoot"},
|
S8 = {ip=1369435392, desc="GB,S8,Waterfoot"},
|
||||||
S9 = {ip=1043434592, desc="GB,S9,Newry"},
|
S9 = {ip=1043434592, desc="GB,S9,Newry"},
|
||||||
T1 = {ip=3242033152, desc="GB,T1,Jordanstown"},
|
T1 = {ip=3242033152, desc="GB,T1,Jordanstown"},
|
||||||
T2 = {ip=1043402000, desc="GB,T2,Bangor"},
|
T2 = {ip=1043402000, desc="GB,T2,Bangor"},
|
||||||
T3 = {ip=1043429728, desc="GB,T3,Omagh"},
|
T3 = {ip=1043429728, desc="GB,T3,Omagh"},
|
||||||
T4 = {ip=1043429520, desc="GB,T4,Strabane"},
|
T4 = {ip=1043429520, desc="GB,T4,Strabane"},
|
||||||
T5 = {ip=39849984, desc="GB,T5,Aberdeen"},
|
T5 = {ip=39849984, desc="GB,T5,Aberdeen"},
|
||||||
T6 = {ip=1043407024, desc="GB,T6,Inverurie"},
|
T6 = {ip=1043407024, desc="GB,T6,Inverurie"},
|
||||||
T7 = {ip=47917056, desc="GB,T7,Forfar"},
|
T7 = {ip=47917056, desc="GB,T7,Forfar"},
|
||||||
T8 = {ip=1051457600, desc="GB,T8,Sandbank"},
|
T8 = {ip=1051457600, desc="GB,T8,Sandbank"},
|
||||||
T9 = {ip=1043429424, desc="GB,T9,Melrose"},
|
T9 = {ip=1043429424, desc="GB,T9,Melrose"},
|
||||||
TX = {ip=201673024, desc="US,TX,Mckinney"},
|
TX = {ip=201673024, desc="US,TX,Mckinney"},
|
||||||
U1 = {ip=1043400976, desc="GB,U1,Alloa"},
|
U1 = {ip=1043400976, desc="GB,U1,Alloa"},
|
||||||
U2 = {ip=1353815544, desc="GB,U2,Langholm"},
|
U2 = {ip=1353815544, desc="GB,U2,Langholm"},
|
||||||
U3 = {ip=1042190336, desc="GB,U3,Dundee"},
|
U3 = {ip=1042190336, desc="GB,U3,Dundee"},
|
||||||
U4 = {ip=1043428036, desc="GB,U4,Newmilns"},
|
U4 = {ip=1043428036, desc="GB,U4,Newmilns"},
|
||||||
U5 = {ip=1051334704, desc="GB,U5,Bishopbriggs"},
|
U5 = {ip=1051334704, desc="GB,U5,Bishopbriggs"},
|
||||||
U6 = {ip=1040628912, desc="GB,U6,Musselburgh"},
|
U6 = {ip=1040628912, desc="GB,U6,Musselburgh"},
|
||||||
U7 = {ip=1056881248, desc="GB,U7,Barrhead"},
|
U7 = {ip=1056881248, desc="GB,U7,Barrhead"},
|
||||||
U8 = {ip=35188736, desc="GB,U8,Edinburgh"},
|
U8 = {ip=35188736, desc="GB,U8,Edinburgh"},
|
||||||
U9 = {ip=1318744616, desc="GB,U9,Blackstone"},
|
U9 = {ip=1318744616, desc="GB,U9,Blackstone"},
|
||||||
V1 = {ip=47947776, desc="GB,V1,Kirkcaldy"},
|
V1 = {ip=47947776, desc="GB,V1,Kirkcaldy"},
|
||||||
V2 = {ip=35190784, desc="GB,V2,Glasgow"},
|
V2 = {ip=35190784, desc="GB,V2,Glasgow"},
|
||||||
V4 = {ip=1043417560, desc="GB,V4,Greenock"},
|
V4 = {ip=1043417560, desc="GB,V4,Greenock"},
|
||||||
V5 = {ip=3570359128, desc="GB,V5,Borthwick"},
|
V5 = {ip=3570359128, desc="GB,V5,Borthwick"},
|
||||||
V6 = {ip=1398983520, desc="GB,V6,Findhorn"},
|
V6 = {ip=1398983520, desc="GB,V6,Findhorn"},
|
||||||
V7 = {ip=1043452928, desc="GB,V7,Saltcoats"},
|
V7 = {ip=1043452928, desc="GB,V7,Saltcoats"},
|
||||||
V8 = {ip=523564544, desc="GB,V8,Bothwell"},
|
V8 = {ip=523564544, desc="GB,V8,Bothwell"},
|
||||||
V9 = {ip=1353706504, desc="GB,V9,Redland"},
|
V9 = {ip=1353706504, desc="GB,V9,Redland"},
|
||||||
VT = {ip=201355264, desc="US,VT,Brattleboro"},
|
VT = {ip=201355264, desc="US,VT,Brattleboro"},
|
||||||
W1 = {ip=1042195200, desc="GB,W1,Perth"},
|
W1 = {ip=1042195200, desc="GB,W1,Perth"},
|
||||||
W2 = {ip=1043412560, desc="GB,W2,Paisley"},
|
W2 = {ip=1043412560, desc="GB,W2,Paisley"},
|
||||||
W4 = {ip=1056825616, desc="GB,W4,Dundonald"},
|
W4 = {ip=1056825616, desc="GB,W4,Dundonald"},
|
||||||
W5 = {ip=1040411544, desc="GB,W5,Douglas"},
|
W5 = {ip=1040411544, desc="GB,W5,Douglas"},
|
||||||
W6 = {ip=41547776, desc="GB,W6,Stirling"},
|
W6 = {ip=41547776, desc="GB,W6,Stirling"},
|
||||||
W7 = {ip=1443523584, desc="GB,W7,Bearsden"},
|
W7 = {ip=1443523584, desc="GB,W7,Bearsden"},
|
||||||
W8 = {ip=534572928, desc="GB,W8,Cross"},
|
W8 = {ip=534572928, desc="GB,W8,Cross"},
|
||||||
W9 = {ip=1042221056, desc="GB,W9,Livingston"},
|
W9 = {ip=1042221056, desc="GB,W9,Livingston"},
|
||||||
WA = {ip=201806720, desc="US,WA,Issaquah"},
|
WA = {ip=201806720, desc="US,WA,Issaquah"},
|
||||||
WY = {ip=135495936, desc="US,WY,Casper"},
|
WY = {ip=135495936, desc="US,WY,Casper"},
|
||||||
X1 = {ip=1043425760, desc="GB,X1,Valley"},
|
X1 = {ip=1043425760, desc="GB,X1,Valley"},
|
||||||
X2 = {ip=773988152, desc="GB,X2,Victoria"},
|
X2 = {ip=773988152, desc="GB,X2,Victoria"},
|
||||||
X3 = {ip=35149824, desc="GB,X3,Bridgend"},
|
X3 = {ip=35149824, desc="GB,X3,Bridgend"},
|
||||||
X4 = {ip=1043402272, desc="GB,X4,Blackwood"},
|
X4 = {ip=1043402272, desc="GB,X4,Blackwood"},
|
||||||
X5 = {ip=39946240, desc="GB,X5,Cardiff"},
|
X5 = {ip=39946240, desc="GB,X5,Cardiff"},
|
||||||
X6 = {ip=1043435700, desc="GB,X6,Aberystwyth"},
|
X6 = {ip=1043435700, desc="GB,X6,Aberystwyth"},
|
||||||
X7 = {ip=1043408760, desc="GB,X7,Llanelli"},
|
X7 = {ip=1043408760, desc="GB,X7,Llanelli"},
|
||||||
X8 = {ip=1368926208, desc="GB,X8,Abergele"},
|
X8 = {ip=1368926208, desc="GB,X8,Abergele"},
|
||||||
X9 = {ip=1043411032, desc="GB,X9,Rhyl"},
|
X9 = {ip=1043411032, desc="GB,X9,Rhyl"},
|
||||||
Y1 = {ip=1043407256, desc="GB,Y1,Holywell"},
|
Y1 = {ip=1043407256, desc="GB,Y1,Holywell"},
|
||||||
Y2 = {ip=1043401576, desc="GB,Y2,Caernarfon"},
|
Y2 = {ip=1043401576, desc="GB,Y2,Caernarfon"},
|
||||||
Y4 = {ip=1043428692, desc="GB,Y4,Cwmbran"},
|
Y4 = {ip=1043428692, desc="GB,Y4,Cwmbran"},
|
||||||
Y5 = {ip=3265794544, desc="GB,Y5,Cwmafan"},
|
Y5 = {ip=3265794544, desc="GB,Y5,Cwmafan"},
|
||||||
Y6 = {ip=35153920, desc="GB,Y6,Newport"},
|
Y6 = {ip=35153920, desc="GB,Y6,Newport"},
|
||||||
Y7 = {ip=1353763984, desc="GB,Y7,Haverfordwest"},
|
Y7 = {ip=1353763984, desc="GB,Y7,Haverfordwest"},
|
||||||
Y8 = {ip=1043430344, desc="GB,Y8,Welshpool"},
|
Y8 = {ip=1043430344, desc="GB,Y8,Welshpool"},
|
||||||
Z1 = {ip=40116224, desc="GB,Z1,Swansea"},
|
Z1 = {ip=40116224, desc="GB,Z1,Swansea"},
|
||||||
Z2 = {ip=40189952, desc="GB,Z2,Pontypool"},
|
Z2 = {ip=40189952, desc="GB,Z2,Pontypool"},
|
||||||
Z3 = {ip=35147776, desc="GB,Z3,Barry"},
|
Z3 = {ip=35147776, desc="GB,Z3,Barry"},
|
||||||
Z4 = {ip=40321024, desc="GB,Z4,Wrexham"}
|
Z4 = {ip=40321024, desc="GB,Z4,Wrexham"}
|
||||||
}
|
}
|
||||||
|
|
||||||
local get_addresses = function(address, mask, domain, nameserver)
|
local get_addresses = function(address, mask, domain, nameserver)
|
||||||
|
|
||||||
-- translate the IP's in the areaIPs to strings, as this is what the
|
-- translate the IP's in the areaIPs to strings, as this is what the
|
||||||
-- DNS library expects
|
-- DNS library expects
|
||||||
if ( "number" == type(address) ) then
|
if ( "number" == type(address) ) then
|
||||||
address = ipOps.fromdword(address)
|
address = ipOps.fromdword(address)
|
||||||
local a, b, c, d = address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
|
local a, b, c, d = address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
|
||||||
address = ("%d.%d.%d.%d"):format(d,c,b,a)
|
address = ("%d.%d.%d.%d"):format(d,c,b,a)
|
||||||
end
|
end
|
||||||
|
|
||||||
local subnet = { family = nmap.address_family(), address = address, mask = mask }
|
local subnet = { family = nmap.address_family(), address = address, mask = mask }
|
||||||
local status, resp = dns.query(domain, {host = nameserver, retAll=true, subnet=subnet})
|
local status, resp = dns.query(domain, {host = nameserver, retAll=true, subnet=subnet})
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if ( "table" ~= type(resp) ) then resp = { resp } end
|
if ( "table" ~= type(resp) ) then resp = { resp } end
|
||||||
return resp
|
return resp
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
if ( not(argDomain) ) then
|
if ( not(argDomain) ) then
|
||||||
return fail(SCRIPT_NAME .. ".domain was not specified")
|
return fail(SCRIPT_NAME .. ".domain was not specified")
|
||||||
end
|
end
|
||||||
|
|
||||||
local nameserver = argNS or (host and host.ip)
|
local nameserver = argNS or (host and host.ip)
|
||||||
-- as the nameserver argument overrides the host.ip, the prerule should
|
-- as the nameserver argument overrides the host.ip, the prerule should
|
||||||
-- already have done our work, so abort
|
-- already have done our work, so abort
|
||||||
if ( argNS and host ) then
|
if ( argNS and host ) then
|
||||||
return
|
return
|
||||||
-- if we have no nameserver argument and no host, we dont have sufficient
|
-- if we have no nameserver argument and no host, we dont have sufficient
|
||||||
-- information to continue, abort
|
-- information to continue, abort
|
||||||
elseif ( not(argNS) and not(host) ) then
|
elseif ( not(argNS) and not(host) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local addrs = argAddr or areaIPs
|
local addrs = argAddr or areaIPs
|
||||||
if ( "string" == type(addrs) ) then addrs = {{ ip = addrs }} end
|
if ( "string" == type(addrs) ) then addrs = {{ ip = addrs }} end
|
||||||
|
|
||||||
local lookup, result = {}, { name = argDomain }
|
local lookup, result = {}, { name = argDomain }
|
||||||
for _,ip in pairs(addrs) do
|
for _,ip in pairs(addrs) do
|
||||||
for _, addr in ipairs( get_addresses (ip.ip, argMask, argDomain, nameserver) ) do
|
for _, addr in ipairs( get_addresses (ip.ip, argMask, argDomain, nameserver) ) do
|
||||||
lookup[addr] = true
|
lookup[addr] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for addr in pairs(lookup) do table.insert(result, addr) end
|
for addr in pairs(lookup) do table.insert(result, addr) end
|
||||||
table.sort(result)
|
table.sort(result)
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -52,63 +52,63 @@ categories = {"discovery", "intrusive"}
|
|||||||
portrule = shortport.port_or_service(53, "domain", {"tcp", "udp"})
|
portrule = shortport.port_or_service(53, "domain", {"tcp", "udp"})
|
||||||
|
|
||||||
local function remove_empty(t)
|
local function remove_empty(t)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
for _, v in ipairs(t) do
|
for _, v in ipairs(t) do
|
||||||
if v ~= "" then
|
if v ~= "" then
|
||||||
result[#result + 1] = v
|
result[#result + 1] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local function split(domain)
|
local function split(domain)
|
||||||
return stdnse.strsplit("%.", domain)
|
return stdnse.strsplit("%.", domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function join(components)
|
local function join(components)
|
||||||
return stdnse.strjoin(".", remove_empty(components))
|
return stdnse.strjoin(".", remove_empty(components))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove the first component of a domain name. Return nil if the number of
|
-- Remove the first component of a domain name. Return nil if the number of
|
||||||
-- components drops below min_length (default 0).
|
-- components drops below min_length (default 0).
|
||||||
local function remove_component(domain, min_length)
|
local function remove_component(domain, min_length)
|
||||||
local components
|
local components
|
||||||
|
|
||||||
min_length = min_length or 0
|
min_length = min_length or 0
|
||||||
components = split(domain)
|
components = split(domain)
|
||||||
if #components <= min_length then
|
if #components <= min_length then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
table.remove(components, 1)
|
table.remove(components, 1)
|
||||||
|
|
||||||
return join(components)
|
return join(components)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Guess the domain given a host. Return nil on failure. This function removes
|
-- Guess the domain given a host. Return nil on failure. This function removes
|
||||||
-- a domain name component unless the name would become shorter than 2
|
-- a domain name component unless the name would become shorter than 2
|
||||||
-- components.
|
-- components.
|
||||||
local function guess_domain(host)
|
local function guess_domain(host)
|
||||||
local name
|
local name
|
||||||
local components
|
local components
|
||||||
|
|
||||||
name = stdnse.get_hostname(host)
|
name = stdnse.get_hostname(host)
|
||||||
if name and name ~= host.ip then
|
if name and name ~= host.ip then
|
||||||
return remove_component(name, 2) or name
|
return remove_component(name, 2) or name
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function invert(t)
|
local function invert(t)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
for k, v in pairs(t) do
|
for k, v in pairs(t) do
|
||||||
result[v] = k
|
result[v] = k
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RFC 952: "A 'name' is a text string up to 24 characters drawn from the
|
-- RFC 952: "A 'name' is a text string up to 24 characters drawn from the
|
||||||
@@ -125,259 +125,259 @@ local DNS_CHARS_INV = invert(DNS_CHARS)
|
|||||||
-- Return the lexicographically next component, or nil if component is the
|
-- Return the lexicographically next component, or nil if component is the
|
||||||
-- lexicographically last.
|
-- lexicographically last.
|
||||||
local function increment_component(name)
|
local function increment_component(name)
|
||||||
local i, bytes, indexes
|
local i, bytes, indexes
|
||||||
|
|
||||||
-- Easy cases first.
|
-- Easy cases first.
|
||||||
if #name == 0 then
|
if #name == 0 then
|
||||||
return "0"
|
return "0"
|
||||||
elseif #name < 63 then
|
elseif #name < 63 then
|
||||||
return name .. "-"
|
return name .. "-"
|
||||||
elseif #name > 64 then
|
elseif #name > 64 then
|
||||||
-- Shouldn't happen.
|
-- Shouldn't happen.
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Convert the string into an array of indexes into DNS_CHARS.
|
-- Convert the string into an array of indexes into DNS_CHARS.
|
||||||
indexes = {}
|
indexes = {}
|
||||||
for i, b in ipairs({ string.byte(name, 1, -1) }) do
|
for i, b in ipairs({ string.byte(name, 1, -1) }) do
|
||||||
indexes[i] = DNS_CHARS_INV[b]
|
indexes[i] = DNS_CHARS_INV[b]
|
||||||
end
|
end
|
||||||
-- Increment.
|
-- Increment.
|
||||||
i = #name
|
i = #name
|
||||||
while i >= 1 do
|
while i >= 1 do
|
||||||
repeat
|
repeat
|
||||||
indexes[i] = indexes[i] + 1
|
indexes[i] = indexes[i] + 1
|
||||||
-- No "-" in first position.
|
-- No "-" in first position.
|
||||||
until not (i == 1 and string.char(DNS_CHARS[indexes[i]]) == "-")
|
until not (i == 1 and string.char(DNS_CHARS[indexes[i]]) == "-")
|
||||||
if indexes[i] > #DNS_CHARS then
|
if indexes[i] > #DNS_CHARS then
|
||||||
-- Wrap around, next digit.
|
-- Wrap around, next digit.
|
||||||
indexes[i] = 1
|
indexes[i] = 1
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
i = i - 1
|
i = i - 1
|
||||||
end
|
end
|
||||||
-- Overflow.
|
-- Overflow.
|
||||||
if i == 0 then
|
if i == 0 then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
-- Convert array of indexes back into string.
|
-- Convert array of indexes back into string.
|
||||||
bytes = {}
|
bytes = {}
|
||||||
for i, index in ipairs(indexes) do
|
for i, index in ipairs(indexes) do
|
||||||
bytes[i] = DNS_CHARS[index]
|
bytes[i] = DNS_CHARS[index]
|
||||||
end
|
end
|
||||||
|
|
||||||
return string.char(table.unpack(bytes))
|
return string.char(table.unpack(bytes))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Return the lexicographically next domain name that does not add a new
|
-- Return the lexicographically next domain name that does not add a new
|
||||||
-- subdomain. This is used after enumerating a whole subzone to jump out of the
|
-- subdomain. This is used after enumerating a whole subzone to jump out of the
|
||||||
-- subzone and on to more names.
|
-- subzone and on to more names.
|
||||||
local function bump_domain(domain)
|
local function bump_domain(domain)
|
||||||
local components
|
local components
|
||||||
|
|
||||||
components = split(domain)
|
components = split(domain)
|
||||||
while #components > 0 do
|
while #components > 0 do
|
||||||
components[1] = increment_component(components[1])
|
components[1] = increment_component(components[1])
|
||||||
if components[1] then
|
if components[1] then
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
table.remove(components[1])
|
table.remove(components[1])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #components == 0 then
|
if #components == 0 then
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
return join(components)
|
return join(components)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Return the lexicographically next domain name. This adds a new subdomain
|
-- Return the lexicographically next domain name. This adds a new subdomain
|
||||||
-- consisting of the smallest character. This function never returns a domain
|
-- consisting of the smallest character. This function never returns a domain
|
||||||
-- outside the current subzone.
|
-- outside the current subzone.
|
||||||
local function next_domain(domain)
|
local function next_domain(domain)
|
||||||
if #domain == 0 then
|
if #domain == 0 then
|
||||||
return "0"
|
return "0"
|
||||||
else
|
else
|
||||||
return "0" .. "." .. domain
|
return "0" .. "." .. domain
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Cut out a portion of an array and return it as a new array, setting the
|
-- Cut out a portion of an array and return it as a new array, setting the
|
||||||
-- elements in the original array to nil.
|
-- elements in the original array to nil.
|
||||||
local function excise(t, i, j)
|
local function excise(t, i, j)
|
||||||
local result
|
local result
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
if j < 0 then
|
if j < 0 then
|
||||||
j = #t + j + 1
|
j = #t + j + 1
|
||||||
end
|
end
|
||||||
for i = i, j do
|
for i = i, j do
|
||||||
result[#result + 1] = t[i]
|
result[#result + 1] = t[i]
|
||||||
t[i] = nil
|
t[i] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove a suffix from a domain (to isolate a subdomain from its parent).
|
-- Remove a suffix from a domain (to isolate a subdomain from its parent).
|
||||||
local function remove_suffix(domain, suffix)
|
local function remove_suffix(domain, suffix)
|
||||||
local dc, sc
|
local dc, sc
|
||||||
|
|
||||||
dc = split(domain)
|
dc = split(domain)
|
||||||
sc = split(suffix)
|
sc = split(suffix)
|
||||||
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
|
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
|
||||||
dc[#dc] = nil
|
dc[#dc] = nil
|
||||||
sc[#sc] = nil
|
sc[#sc] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return join(dc), join(sc)
|
return join(dc), join(sc)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Return the subset of authoritative records with the given label.
|
-- Return the subset of authoritative records with the given label.
|
||||||
local function auth_filter(retPkt, label)
|
local function auth_filter(retPkt, label)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
for _, rec in ipairs(retPkt.auth) do
|
for _, rec in ipairs(retPkt.auth) do
|
||||||
if rec[label] then
|
if rec[label] then
|
||||||
result[#result + 1] = rec[label]
|
result[#result + 1] = rec[label]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- "Less than" function for two domain names. Compares starting with the last
|
-- "Less than" function for two domain names. Compares starting with the last
|
||||||
-- component.
|
-- component.
|
||||||
local function domain_lt(a, b)
|
local function domain_lt(a, b)
|
||||||
local a_parts, b_parts
|
local a_parts, b_parts
|
||||||
|
|
||||||
a_parts = split(a)
|
a_parts = split(a)
|
||||||
b_parts = split(b)
|
b_parts = split(b)
|
||||||
while #a_parts > 0 and #b_parts > 0 do
|
while #a_parts > 0 and #b_parts > 0 do
|
||||||
if a_parts[#a_parts] < b_parts[#b_parts] then
|
if a_parts[#a_parts] < b_parts[#b_parts] then
|
||||||
return true
|
return true
|
||||||
elseif a_parts[#a_parts] > b_parts[#b_parts] then
|
elseif a_parts[#a_parts] > b_parts[#b_parts] then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
a_parts[#a_parts] = nil
|
a_parts[#a_parts] = nil
|
||||||
b_parts[#b_parts] = nil
|
b_parts[#b_parts] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return #a_parts < #b_parts
|
return #a_parts < #b_parts
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Find the NSEC record that brackets the given domain.
|
-- Find the NSEC record that brackets the given domain.
|
||||||
local function get_next_nsec(retPkt, domain)
|
local function get_next_nsec(retPkt, domain)
|
||||||
for _, nsec in ipairs(auth_filter(retPkt, "NSEC")) do
|
for _, nsec in ipairs(auth_filter(retPkt, "NSEC")) do
|
||||||
-- The last NSEC record points backwards to the start of the subzone.
|
-- The last NSEC record points backwards to the start of the subzone.
|
||||||
if domain_lt(nsec.dname, domain) and not domain_lt(nsec.dname, nsec.next_dname) then
|
if domain_lt(nsec.dname, domain) and not domain_lt(nsec.dname, nsec.next_dname) then
|
||||||
return nsec
|
return nsec
|
||||||
end
|
end
|
||||||
if domain_lt(nsec.dname, domain) and domain_lt(domain, nsec.next_dname) then
|
if domain_lt(nsec.dname, domain) and domain_lt(domain, nsec.next_dname) then
|
||||||
return nsec
|
return nsec
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function empty(t)
|
local function empty(t)
|
||||||
return not next(t)
|
return not next(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Enumerate a single domain.
|
-- Enumerate a single domain.
|
||||||
local function enum(host, port, domain)
|
local function enum(host, port, domain)
|
||||||
local all_results = {}
|
local all_results = {}
|
||||||
local seen = {}
|
local seen = {}
|
||||||
local subdomain = next_domain("")
|
local subdomain = next_domain("")
|
||||||
|
|
||||||
while subdomain do
|
while subdomain do
|
||||||
local result = {}
|
local result = {}
|
||||||
local status, result, nsec
|
local status, result, nsec
|
||||||
stdnse.print_debug("Trying %q.%q", subdomain, domain)
|
stdnse.print_debug("Trying %q.%q", subdomain, domain)
|
||||||
status, result = dns.query(join({subdomain, domain}), {host = host.ip, dtype='A', retAll=true, retPkt=true, dnssec=true})
|
status, result = dns.query(join({subdomain, domain}), {host = host.ip, dtype='A', retAll=true, retPkt=true, dnssec=true})
|
||||||
nsec = status and get_next_nsec(result, join({subdomain, domain})) or nil
|
nsec = status and get_next_nsec(result, join({subdomain, domain})) or nil
|
||||||
if nsec then
|
if nsec then
|
||||||
local first, last, remainder
|
local first, last, remainder
|
||||||
local index
|
local index
|
||||||
|
|
||||||
first, remainder = remove_suffix(nsec.dname, domain)
|
first, remainder = remove_suffix(nsec.dname, domain)
|
||||||
if #remainder > 0 then
|
if #remainder > 0 then
|
||||||
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.dname, domain)
|
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.dname, domain)
|
||||||
subdomain = nil
|
subdomain = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
last, remainder = remove_suffix(nsec.next_dname, domain)
|
last, remainder = remove_suffix(nsec.next_dname, domain)
|
||||||
if #remainder > 0 then
|
if #remainder > 0 then
|
||||||
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.next_dname, domain)
|
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.next_dname, domain)
|
||||||
subdomain = nil
|
subdomain = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if #last == 0 then
|
if #last == 0 then
|
||||||
stdnse.print_debug("Wrapped")
|
stdnse.print_debug("Wrapped")
|
||||||
subdomain = nil
|
subdomain = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if not seen[first] then
|
if not seen[first] then
|
||||||
table.insert(all_results, join({first, domain}))
|
table.insert(all_results, join({first, domain}))
|
||||||
seen[first] = #all_results
|
seen[first] = #all_results
|
||||||
end
|
end
|
||||||
index = seen[last]
|
index = seen[last]
|
||||||
if index then
|
if index then
|
||||||
-- Ignore if first is the original domain.
|
-- Ignore if first is the original domain.
|
||||||
if #first > 0 then
|
if #first > 0 then
|
||||||
subdomain = bump_domain(last)
|
subdomain = bump_domain(last)
|
||||||
-- Replace a chunk of the output with a sub-table for the zone.
|
-- Replace a chunk of the output with a sub-table for the zone.
|
||||||
all_results[index] = excise(all_results, index, -1)
|
all_results[index] = excise(all_results, index, -1)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stdnse.print_debug("adding %s", last)
|
stdnse.print_debug("adding %s", last)
|
||||||
subdomain = next_domain(last)
|
subdomain = next_domain(last)
|
||||||
table.insert(all_results, join({last, domain}))
|
table.insert(all_results, join({last, domain}))
|
||||||
seen[last] = #all_results
|
seen[last] = #all_results
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local parent = remove_component(subdomain, 1)
|
local parent = remove_component(subdomain, 1)
|
||||||
|
|
||||||
-- This branch is entered if name resolution failed or
|
-- This branch is entered if name resolution failed or
|
||||||
-- there were no NSEC records. If at the top, quit.
|
-- there were no NSEC records. If at the top, quit.
|
||||||
-- Otherwise continue to the next subdomain.
|
-- Otherwise continue to the next subdomain.
|
||||||
if parent then
|
if parent then
|
||||||
subdomain = bump_domain(parent)
|
subdomain = bump_domain(parent)
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return all_results
|
return all_results
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local output = {}
|
local output = {}
|
||||||
local domains
|
local domains
|
||||||
|
|
||||||
domains = stdnse.get_script_args('dns-nsec-enum.domains')
|
domains = stdnse.get_script_args('dns-nsec-enum.domains')
|
||||||
if not domains then
|
if not domains then
|
||||||
domains = guess_domain(host)
|
domains = guess_domain(host)
|
||||||
end
|
end
|
||||||
if not domains then
|
if not domains then
|
||||||
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
|
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
if type(domains) == 'string' then
|
if type(domains) == 'string' then
|
||||||
domains = { domains }
|
domains = { domains }
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, domain in ipairs(domains) do
|
for _, domain in ipairs(domains) do
|
||||||
local result = enum(host, port, domain)
|
local result = enum(host, port, domain)
|
||||||
if type(result) == "table" then
|
if type(result) == "table" then
|
||||||
result["name"] = domain
|
result["name"] = domain
|
||||||
output[#output + 1] = result
|
output[#output + 1] = result
|
||||||
else
|
else
|
||||||
output[#output + 1] = "No NSEC records found"
|
output[#output + 1] = "No NSEC records found"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -82,345 +82,345 @@ all_results = {}
|
|||||||
|
|
||||||
-- get time (in miliseconds) when the script should finish
|
-- get time (in miliseconds) when the script should finish
|
||||||
local function get_end_time()
|
local function get_end_time()
|
||||||
local t = nmap.timing_level()
|
local t = nmap.timing_level()
|
||||||
local limit = stdnse.parse_timespec(stdnse.get_script_args('dns-nsec3-enum.timelimit') or "30m")
|
local limit = stdnse.parse_timespec(stdnse.get_script_args('dns-nsec3-enum.timelimit') or "30m")
|
||||||
local end_time = 1000 * limit + nmap.clock_ms()
|
local end_time = 1000 * limit + nmap.clock_ms()
|
||||||
return end_time
|
return end_time
|
||||||
end
|
end
|
||||||
|
|
||||||
local function remove_empty(t)
|
local function remove_empty(t)
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, v in ipairs(t) do
|
for _, v in ipairs(t) do
|
||||||
if v ~= "" then
|
if v ~= "" then
|
||||||
result[#result + 1] = v
|
result[#result + 1] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local function split(domain)
|
local function split(domain)
|
||||||
return stdnse.strsplit("%.", domain)
|
return stdnse.strsplit("%.", domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function join(components)
|
local function join(components)
|
||||||
return stdnse.strjoin(".", remove_empty(components))
|
return stdnse.strjoin(".", remove_empty(components))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove the first component of a domain name. Return nil if the number of
|
-- Remove the first component of a domain name. Return nil if the number of
|
||||||
-- components drops below min_length (default 0).
|
-- components drops below min_length (default 0).
|
||||||
local function remove_component(domain, min_length)
|
local function remove_component(domain, min_length)
|
||||||
local components
|
local components
|
||||||
|
|
||||||
min_length = min_length or 0
|
min_length = min_length or 0
|
||||||
components = split(domain)
|
components = split(domain)
|
||||||
if #components < min_length then
|
if #components < min_length then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
table.remove(components, 1)
|
table.remove(components, 1)
|
||||||
|
|
||||||
return join(components)
|
return join(components)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Guess the domain given a host. Return nil on failure. This function removes
|
-- Guess the domain given a host. Return nil on failure. This function removes
|
||||||
-- a domain name component unless the name would become shorter than 2
|
-- a domain name component unless the name would become shorter than 2
|
||||||
-- components.
|
-- components.
|
||||||
local function guess_domain(host)
|
local function guess_domain(host)
|
||||||
local name
|
local name
|
||||||
local components
|
local components
|
||||||
|
|
||||||
name = stdnse.get_hostname(host)
|
name = stdnse.get_hostname(host)
|
||||||
if name and name ~= host.ip then
|
if name and name ~= host.ip then
|
||||||
return remove_component(name, 2) or name
|
return remove_component(name, 2) or name
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove a suffix from a domain (to isolate a subdomain from its parent).
|
-- Remove a suffix from a domain (to isolate a subdomain from its parent).
|
||||||
local function remove_suffix(domain, suffix)
|
local function remove_suffix(domain, suffix)
|
||||||
local dc, sc
|
local dc, sc
|
||||||
|
|
||||||
dc = split(domain)
|
dc = split(domain)
|
||||||
sc = split(suffix)
|
sc = split(suffix)
|
||||||
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
|
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
|
||||||
dc[#dc] = nil
|
dc[#dc] = nil
|
||||||
sc[#sc] = nil
|
sc[#sc] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return join(dc), join(sc)
|
return join(dc), join(sc)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Return the subset of authoritative records with the given label.
|
-- Return the subset of authoritative records with the given label.
|
||||||
local function auth_filter(retPkt, label)
|
local function auth_filter(retPkt, label)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
for _, rec in ipairs(retPkt.auth) do
|
for _, rec in ipairs(retPkt.auth) do
|
||||||
if rec[label] then
|
if rec[label] then
|
||||||
result[#result + 1] = rec[label]
|
result[#result + 1] = rec[label]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function empty(t)
|
local function empty(t)
|
||||||
return not next(t)
|
return not next(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function random_string()
|
local function random_string()
|
||||||
return msrpc.random_crap(8,"etaoinshrdlucmfw")
|
return msrpc.random_crap(8,"etaoinshrdlucmfw")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- generate a random hash with domains suffix
|
-- generate a random hash with domains suffix
|
||||||
-- return both domain and it's hash
|
-- return both domain and it's hash
|
||||||
local function generate_hash(domain, iter, salt)
|
local function generate_hash(domain, iter, salt)
|
||||||
local rand_str = random_string()
|
local rand_str = random_string()
|
||||||
local random_domain = rand_str .. "." .. domain
|
local random_domain = rand_str .. "." .. domain
|
||||||
local packed_domain = ""
|
local packed_domain = ""
|
||||||
for word in string.gmatch(domain,"[^%.]+") do
|
for word in string.gmatch(domain,"[^%.]+") do
|
||||||
packed_domain = packed_domain .. bin.pack("c",string.len(word)) .. word
|
packed_domain = packed_domain .. bin.pack("c",string.len(word)) .. word
|
||||||
end
|
end
|
||||||
local to_hash = bin.pack("c",string.len(rand_str)) .. rand_str .. packed_domain .. bin.pack("c",0) .. bin.pack("H",salt)
|
local to_hash = bin.pack("c",string.len(rand_str)) .. rand_str .. packed_domain .. bin.pack("c",0) .. bin.pack("H",salt)
|
||||||
iter = iter - 1
|
iter = iter - 1
|
||||||
local hash = openssl.sha1(to_hash)
|
local hash = openssl.sha1(to_hash)
|
||||||
for i=0,iter do
|
for i=0,iter do
|
||||||
hash = hash .. bin.pack("H",salt)
|
hash = hash .. bin.pack("H",salt)
|
||||||
hash = openssl.sha1(hash)
|
hash = openssl.sha1(hash)
|
||||||
end
|
end
|
||||||
return string.lower(base32.enc(hash,true)), random_domain
|
return string.lower(base32.enc(hash,true)), random_domain
|
||||||
end
|
end
|
||||||
|
|
||||||
-- convenience function , returns size of a table
|
-- convenience function , returns size of a table
|
||||||
local function table_size(tbl)
|
local function table_size(tbl)
|
||||||
local numItems = 0
|
local numItems = 0
|
||||||
for k,v in pairs(tbl) do
|
for k,v in pairs(tbl) do
|
||||||
numItems = numItems + 1
|
numItems = numItems + 1
|
||||||
end
|
end
|
||||||
return numItems
|
return numItems
|
||||||
end
|
end
|
||||||
|
|
||||||
-- convenience function , return first item in a table
|
-- convenience function , return first item in a table
|
||||||
local function get_first(tbl)
|
local function get_first(tbl)
|
||||||
for k,v in pairs(tbl) do
|
for k,v in pairs(tbl) do
|
||||||
return k,v
|
return k,v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- queries the domain and parses the results
|
-- queries the domain and parses the results
|
||||||
-- returns the list of new ranges
|
-- returns the list of new ranges
|
||||||
local function query_for_hashes(host,subdomain,domain)
|
local function query_for_hashes(host,subdomain,domain)
|
||||||
local status
|
local status
|
||||||
local result
|
local result
|
||||||
local ranges = {}
|
local ranges = {}
|
||||||
status, result = dns.query(subdomain, {host = host.ip, dtype='NSEC3', retAll=true, retPkt=true, dnssec=true})
|
status, result = dns.query(subdomain, {host = host.ip, dtype='NSEC3', retAll=true, retPkt=true, dnssec=true})
|
||||||
if status then
|
if status then
|
||||||
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do
|
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do
|
||||||
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
|
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
|
||||||
local h2 = string.lower(nsec3.hash.base32)
|
local h2 = string.lower(nsec3.hash.base32)
|
||||||
if not stdnse.contains(all_results,"nexthash " .. h1 .. " " .. h2) then
|
if not stdnse.contains(all_results,"nexthash " .. h1 .. " " .. h2) then
|
||||||
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
|
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
|
||||||
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
|
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
|
||||||
end
|
end
|
||||||
ranges[h1] = h2
|
ranges[h1] = h2
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "DNS error: %s", result)
|
stdnse.print_debug(1, "DNS error: %s", result)
|
||||||
end
|
end
|
||||||
return ranges
|
return ranges
|
||||||
end
|
end
|
||||||
|
|
||||||
-- does the actuall enumeration
|
-- does the actuall enumeration
|
||||||
local function enum(host, port, domain)
|
local function enum(host, port, domain)
|
||||||
|
|
||||||
local seen, seen_subdomain = {}, {}
|
local seen, seen_subdomain = {}, {}
|
||||||
local ALG ={}
|
local ALG ={}
|
||||||
ALG[1] = "SHA-1"
|
ALG[1] = "SHA-1"
|
||||||
local todo = {}
|
local todo = {}
|
||||||
local dnssec, status, result = false, false, "No Answer"
|
local dnssec, status, result = false, false, "No Answer"
|
||||||
local result = {}
|
local result = {}
|
||||||
local subdomain = msrpc.random_crap(8,"etaoinshrdlucmfw")
|
local subdomain = msrpc.random_crap(8,"etaoinshrdlucmfw")
|
||||||
local full_domain = join({subdomain, domain})
|
local full_domain = join({subdomain, domain})
|
||||||
local iter
|
local iter
|
||||||
local salt
|
local salt
|
||||||
local end_time = get_end_time()
|
local end_time = get_end_time()
|
||||||
|
|
||||||
-- do one query to determine the hash and if DNSSEC is actually used
|
-- do one query to determine the hash and if DNSSEC is actually used
|
||||||
status, result = dns.query(full_domain, {host = host.ip, dtype='NSEC3', retAll=true, retPkt=true, dnssec=true})
|
status, result = dns.query(full_domain, {host = host.ip, dtype='NSEC3', retAll=true, retPkt=true, dnssec=true})
|
||||||
if status then
|
if status then
|
||||||
local is_nsec3 = false
|
local is_nsec3 = false
|
||||||
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do -- parse the results and add initial ranges
|
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do -- parse the results and add initial ranges
|
||||||
is_nsec3 = true
|
is_nsec3 = true
|
||||||
dnssec = true
|
dnssec = true
|
||||||
iter = nsec3.iterations
|
iter = nsec3.iterations
|
||||||
salt = nsec3.salt.hex
|
salt = nsec3.salt.hex
|
||||||
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
|
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
|
||||||
local h2 = string.lower(nsec3.hash.base32)
|
local h2 = string.lower(nsec3.hash.base32)
|
||||||
if table_size(todo) == 0 then
|
if table_size(todo) == 0 then
|
||||||
table.insert(all_results, "domain " .. domain)
|
table.insert(all_results, "domain " .. domain)
|
||||||
stdnse.print_debug("domain " .. domain)
|
stdnse.print_debug("domain " .. domain)
|
||||||
table.insert(all_results, "salt " .. salt)
|
table.insert(all_results, "salt " .. salt)
|
||||||
stdnse.print_debug("salt " .. salt)
|
stdnse.print_debug("salt " .. salt)
|
||||||
table.insert(all_results, "iterations " .. iter)
|
table.insert(all_results, "iterations " .. iter)
|
||||||
stdnse.print_debug("iterations " .. iter)
|
stdnse.print_debug("iterations " .. iter)
|
||||||
if h1 < h2 then
|
if h1 < h2 then
|
||||||
todo[h2] = h1
|
todo[h2] = h1
|
||||||
else
|
else
|
||||||
todo[h1] = h2
|
todo[h1] = h2
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for b,a in pairs(todo) do
|
for b,a in pairs(todo) do
|
||||||
if h1 == b and h2 == a then -- h2:a b:h1 case
|
if h1 == b and h2 == a then -- h2:a b:h1 case
|
||||||
todo[b] = nil
|
todo[b] = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if h1 == b and h2 > h1 then -- a b:h1 h2 case
|
if h1 == b and h2 > h1 then -- a b:h1 h2 case
|
||||||
todo[b] = nil
|
todo[b] = nil
|
||||||
todo[h2] = a
|
todo[h2] = a
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if h1 == b and h2 < a then -- h2 a b:h1
|
if h1 == b and h2 < a then -- h2 a b:h1
|
||||||
todo[b] = nil
|
todo[b] = nil
|
||||||
todo[b] = h2
|
todo[b] = h2
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if h1 > b then -- a b h1 h2
|
if h1 > b then -- a b h1 h2
|
||||||
todo[b] = nil
|
todo[b] = nil
|
||||||
todo[b] = h1
|
todo[b] = h1
|
||||||
todo[h2] = a
|
todo[h2] = a
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if h1 < a then -- h1 h2 a b
|
if h1 < a then -- h1 h2 a b
|
||||||
todo[b] = nil
|
todo[b] = nil
|
||||||
todo[b] = h1
|
todo[b] = h1
|
||||||
todo[h2] = a
|
todo[h2] = a
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end -- for
|
end -- for
|
||||||
end -- else
|
end -- else
|
||||||
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
|
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
|
||||||
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
|
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- find hash that falls into one of the ranges and query for it
|
-- find hash that falls into one of the ranges and query for it
|
||||||
while table_size(todo) > 0 and nmap.clock_ms() < end_time do
|
while table_size(todo) > 0 and nmap.clock_ms() < end_time do
|
||||||
local hash
|
local hash
|
||||||
hash, subdomain = generate_hash(domain,iter,salt)
|
hash, subdomain = generate_hash(domain,iter,salt)
|
||||||
local queried = false
|
local queried = false
|
||||||
for a,b in pairs(todo) do
|
for a,b in pairs(todo) do
|
||||||
if a == b then
|
if a == b then
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if a < b then -- [] range
|
if a < b then -- [] range
|
||||||
if hash > a and hash < b then
|
if hash > a and hash < b then
|
||||||
-- do the query
|
-- do the query
|
||||||
local hash_pairs = query_for_hashes(host,subdomain,domain)
|
local hash_pairs = query_for_hashes(host,subdomain,domain)
|
||||||
queried = true
|
queried = true
|
||||||
local changed = false
|
local changed = false
|
||||||
for h1,h2 in pairs(hash_pairs) do
|
for h1,h2 in pairs(hash_pairs) do
|
||||||
if h1 == a and h2 == b then -- h1:a h2:b case
|
if h1 == a and h2 == b then -- h1:a h2:b case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 == a then -- h1:a h2 b case
|
if h1 == a then -- h1:a h2 b case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[h2] = b
|
todo[h2] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h2 == b then -- a h1 bh:2 case
|
if h2 == b then -- a h1 bh:2 case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[a] = h1
|
todo[a] = h1
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 > a and h2 < b then -- a h1 h2 b case
|
if h1 > a and h2 < b then -- a h1 h2 b case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[a] = h1
|
todo[a] = h1
|
||||||
todo[h2] = b
|
todo[h2] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- if changed then
|
--if changed then
|
||||||
-- stdnse.print_debug("break[]")
|
-- stdnse.print_debug("break[]")
|
||||||
--break
|
--break
|
||||||
-- end
|
-- end
|
||||||
end
|
end
|
||||||
elseif a > b then -- ][ range
|
elseif a > b then -- ][ range
|
||||||
if hash > a or hash < b then
|
if hash > a or hash < b then
|
||||||
local hash_pairs = query_for_hashes(host,subdomain,domain)
|
local hash_pairs = query_for_hashes(host,subdomain,domain)
|
||||||
queried = true
|
queried = true
|
||||||
local changed = false
|
local changed = false
|
||||||
for h1,h2 in pairs(hash_pairs) do
|
for h1,h2 in pairs(hash_pairs) do
|
||||||
if h1 == a and h2 == b then -- h2:b a:h1 case
|
if h1 == a and h2 == b then -- h2:b a:h1 case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 == a and h2 > h1 then -- b a:h1 h2 case
|
if h1 == a and h2 > h1 then -- b a:h1 h2 case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[h1] = b
|
todo[h1] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 == a and h2 < b then -- h2 b a:h1 case
|
if h1 == a and h2 < b then -- h2 b a:h1 case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[h2] = b
|
todo[h2] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 > a and h2 > h1 then -- b a h1 h2 case
|
if h1 > a and h2 > h1 then -- b a h1 h2 case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[a] = h1
|
todo[a] = h1
|
||||||
todo[h2] = b
|
todo[h2] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 > a and h2 < b then -- h2 b a h1 case
|
if h1 > a and h2 < b then -- h2 b a h1 case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[a] = h1
|
todo[a] = h1
|
||||||
todo[h2] = b
|
todo[h2] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
if h1 < b then -- h1 h2 b a case
|
if h1 < b then -- h1 h2 b a case
|
||||||
todo[a] = nil
|
todo[a] = nil
|
||||||
todo[a] = h1
|
todo[a] = h1
|
||||||
todo[h2] = b
|
todo[h2] = b
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if changed then
|
if changed then
|
||||||
--break
|
--break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if queried then
|
if queried then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return dnssec, status, all_results
|
return dnssec, status, all_results
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local output = {}
|
local output = {}
|
||||||
local domains
|
local domains
|
||||||
domains = stdnse.get_script_args('dns-nsec3-enum.domains')
|
domains = stdnse.get_script_args('dns-nsec3-enum.domains')
|
||||||
if not domains then
|
if not domains then
|
||||||
domains = guess_domain(host)
|
domains = guess_domain(host)
|
||||||
end
|
end
|
||||||
if not domains then
|
if not domains then
|
||||||
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
|
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
if type(domains) == 'string' then
|
if type(domains) == 'string' then
|
||||||
domains = { domains }
|
domains = { domains }
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, domain in ipairs(domains) do
|
for _, domain in ipairs(domains) do
|
||||||
local dnssec, status, result = enum(host, port, domain)
|
local dnssec, status, result = enum(host, port, domain)
|
||||||
if dnssec and type(result) == "table" then
|
if dnssec and type(result) == "table" then
|
||||||
output[#output + 1] = result
|
output[#output + 1] = result
|
||||||
output[#output + 1] = "Total hashes found: " .. #result
|
output[#output + 1] = "Total hashes found: " .. #result
|
||||||
|
|
||||||
else
|
else
|
||||||
output[#output + 1] = "DNSSEC NSEC3 not supported"
|
output[#output + 1] = "DNSSEC NSEC3 not supported"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -102,15 +102,15 @@ prerule = function()
|
|||||||
|
|
||||||
if not dns_opts.domain then
|
if not dns_opts.domain then
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
|
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
|
||||||
SCRIPT_NAME, SCRIPT_TYPE)
|
SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not dns_opts.server then
|
if not dns_opts.server then
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Skipping '%s' %s, 'dnszonetransfer.server' argument is missing.",
|
"Skipping '%s' %s, 'dnszonetransfer.server' argument is missing.",
|
||||||
SCRIPT_NAME, SCRIPT_TYPE)
|
SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -132,8 +132,8 @@ portrule = function(host, port)
|
|||||||
else
|
else
|
||||||
-- can't do anything without a hostname
|
-- can't do anything without a hostname
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
|
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
|
||||||
SCRIPT_NAME, SCRIPT_TYPE)
|
SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -149,39 +149,39 @@ end
|
|||||||
--@class table
|
--@class table
|
||||||
--@name typetab
|
--@name typetab
|
||||||
local typetab = { 'A', 'NS', 'MD', 'MF', 'CNAME', 'SOA', 'MB', 'MG', 'MR',
|
local typetab = { 'A', 'NS', 'MD', 'MF', 'CNAME', 'SOA', 'MB', 'MG', 'MR',
|
||||||
'NULL', 'WKS', 'PTR', 'HINFO', 'MINFO', 'MX', 'TXT', 'RP', 'AFSDB', 'X25',
|
'NULL', 'WKS', 'PTR', 'HINFO', 'MINFO', 'MX', 'TXT', 'RP', 'AFSDB', 'X25',
|
||||||
'ISDN', 'RT', 'NSAP', 'NSAP-PTR', 'SIG', 'KEY', 'PX', 'GPOS', 'AAAA', 'LOC',
|
'ISDN', 'RT', 'NSAP', 'NSAP-PTR', 'SIG', 'KEY', 'PX', 'GPOS', 'AAAA', 'LOC',
|
||||||
'NXT', 'EID', 'NIMLOC', 'SRV', 'ATMA', 'NAPTR', 'KX', 'CERT', 'A6', 'DNAME',
|
'NXT', 'EID', 'NIMLOC', 'SRV', 'ATMA', 'NAPTR', 'KX', 'CERT', 'A6', 'DNAME',
|
||||||
'SINK', 'OPT', 'APL', 'DS', 'SSHFP', 'IPSECKEY', 'RRSIG', 'NSEC', 'DNSKEY',
|
'SINK', 'OPT', 'APL', 'DS', 'SSHFP', 'IPSECKEY', 'RRSIG', 'NSEC', 'DNSKEY',
|
||||||
'DHCID', 'NSEC3', 'NSEC3PARAM', 'TLSA', [55]='HIP', [56]='NINFO', [57]='RKEY',
|
'DHCID', 'NSEC3', 'NSEC3PARAM', 'TLSA', [55]='HIP', [56]='NINFO', [57]='RKEY',
|
||||||
[58]='TALINK', [59]='CDS', [99]='SPF', [100]='UINFO', [101]='UID', [102]='GID',
|
[58]='TALINK', [59]='CDS', [99]='SPF', [100]='UINFO', [101]='UID', [102]='GID',
|
||||||
[103]='UNSPEC', [249]='TKEY', [250]='TSIG', [251]='IXFR', [252]='AXFR',
|
[103]='UNSPEC', [249]='TKEY', [250]='TSIG', [251]='IXFR', [252]='AXFR',
|
||||||
[253]='MAILB', [254]='MAILA', [255]='ANY', [256]='ZXFR', [257]='CAA',
|
[253]='MAILB', [254]='MAILA', [255]='ANY', [256]='ZXFR', [257]='CAA',
|
||||||
[32768]='TA', [32769]='DLV',
|
[32768]='TA', [32769]='DLV',
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Whitelist of TLDs. Only way to reliably determine the root of a domain
|
--- Whitelist of TLDs. Only way to reliably determine the root of a domain
|
||||||
--@class table
|
--@class table
|
||||||
--@name tld
|
--@name tld
|
||||||
local tld = {
|
local tld = {
|
||||||
'aero', 'asia', 'biz', 'cat', 'com', 'coop', 'info', 'jobs', 'mobi', 'museum',
|
'aero', 'asia', 'biz', 'cat', 'com', 'coop', 'info', 'jobs', 'mobi', 'museum',
|
||||||
'name', 'net', 'org', 'pro', 'tel', 'travel', 'gov', 'edu', 'mil', 'int',
|
'name', 'net', 'org', 'pro', 'tel', 'travel', 'gov', 'edu', 'mil', 'int',
|
||||||
'ac','ad','ae','af','ag','ai','al','am','an','ao','aq','ar','as','at','au','aw',
|
'ac','ad','ae','af','ag','ai','al','am','an','ao','aq','ar','as','at','au','aw',
|
||||||
'ax','az','ba','bb','bd','be','bf','bg','bh','bi','bj','bm','bn','bo','br','bs',
|
'ax','az','ba','bb','bd','be','bf','bg','bh','bi','bj','bm','bn','bo','br','bs',
|
||||||
'bt','bv','bw','by','bz','ca','cc','cd','cf','cg','ch','ci','ck','cl','cm','cn',
|
'bt','bv','bw','by','bz','ca','cc','cd','cf','cg','ch','ci','ck','cl','cm','cn',
|
||||||
'co','cr','cu','cv','cx','cy','cz','de','dj','dk','dm','do','dz','ec','ee','eg',
|
'co','cr','cu','cv','cx','cy','cz','de','dj','dk','dm','do','dz','ec','ee','eg',
|
||||||
'eh','er','es','et','eu','fi','fj','fk','fm','fo','fr','ga','gb','gd','ge','gf',
|
'eh','er','es','et','eu','fi','fj','fk','fm','fo','fr','ga','gb','gd','ge','gf',
|
||||||
'gg','gh','gi','gl','gm','gn','gp','gq','gr','gs','gt','gu','gw','gy','hk','hm',
|
'gg','gh','gi','gl','gm','gn','gp','gq','gr','gs','gt','gu','gw','gy','hk','hm',
|
||||||
'hn','hr','ht','hu','id','ie','il','im','in','io','iq','ir','is','it','je','jm',
|
'hn','hr','ht','hu','id','ie','il','im','in','io','iq','ir','is','it','je','jm',
|
||||||
'jo','jp','ke','kg','kh','ki','km','kn','kp','kr','kw','ky','kz','la','lb','lc',
|
'jo','jp','ke','kg','kh','ki','km','kn','kp','kr','kw','ky','kz','la','lb','lc',
|
||||||
'li','lk','lr','ls','lt','lu','lv','ly','ma','mc','md','me','mg','mh','mk','ml',
|
'li','lk','lr','ls','lt','lu','lv','ly','ma','mc','md','me','mg','mh','mk','ml',
|
||||||
'mm','mn','mo','mp','mq','mr','ms','mt','mu','mv','mw','mx','my','mz','na','nc',
|
'mm','mn','mo','mp','mq','mr','ms','mt','mu','mv','mw','mx','my','mz','na','nc',
|
||||||
'ne','nf','ng','ni','nl','no','np','nr','nu','nz','om','pa','pe','pf','pg','ph',
|
'ne','nf','ng','ni','nl','no','np','nr','nu','nz','om','pa','pe','pf','pg','ph',
|
||||||
'pk','pl','pm','pn','pr','ps','pt','pw','py','qa','re','ro','rs','ru','rw','sa',
|
'pk','pl','pm','pn','pr','ps','pt','pw','py','qa','re','ro','rs','ru','rw','sa',
|
||||||
'sb','sc','sd','se','sg','sh','si','sj','sk','sl','sm','sn','so','sr','st','su',
|
'sb','sc','sd','se','sg','sh','si','sj','sk','sl','sm','sn','so','sr','st','su',
|
||||||
'sv','sy','sz','tc','td','tf','tg','th','tj','tk','tl','tm','tn','to','tp','tr',
|
'sv','sy','sz','tc','td','tf','tg','th','tj','tk','tl','tm','tn','to','tp','tr',
|
||||||
'tt','tv','tw','tz','ua','ug','uk','um','us','uy','uz','va','vc','ve','vg','vi',
|
'tt','tv','tw','tz','ua','ug','uk','um','us','uy','uz','va','vc','ve','vg','vi',
|
||||||
'vn','vu','wf','ws','ye','yt','yu','za','zm','zw'
|
'vn','vu','wf','ws','ye','yt','yu','za','zm','zw'
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Convert two bytes into a 16bit number.
|
--- Convert two bytes into a 16bit number.
|
||||||
@@ -189,61 +189,61 @@ local tld = {
|
|||||||
--@param idx Index in the string (first of two consecutive bytes).
|
--@param idx Index in the string (first of two consecutive bytes).
|
||||||
--@return 16 bit number represented by the two bytes.
|
--@return 16 bit number represented by the two bytes.
|
||||||
function bto16(data, idx)
|
function bto16(data, idx)
|
||||||
local b1 = string.byte(data, idx)
|
local b1 = string.byte(data, idx)
|
||||||
local b2 = string.byte(data, idx+1)
|
local b2 = string.byte(data, idx+1)
|
||||||
-- (b2 & 0xff) | ((b1 & 0xff) << 8)
|
-- (b2 & 0xff) | ((b1 & 0xff) << 8)
|
||||||
return bit.bor(bit.band(b2, 255), bit.lshift(bit.band(b1, 255), 8))
|
return bit.bor(bit.band(b2, 255), bit.lshift(bit.band(b1, 255), 8))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if domain name element is a tld
|
--- Check if domain name element is a tld
|
||||||
--@param elm Domain name element to check.
|
--@param elm Domain name element to check.
|
||||||
--@return boolean
|
--@return boolean
|
||||||
function valid_tld(elm)
|
function valid_tld(elm)
|
||||||
for i,v in ipairs(tld) do
|
for i,v in ipairs(tld) do
|
||||||
if elm == v then return true end
|
if elm == v then return true end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Parse an RFC 1035 domain name.
|
--- Parse an RFC 1035 domain name.
|
||||||
--@param data String of data.
|
--@param data String of data.
|
||||||
--@param offset Offset in the string to read the domain name.
|
--@param offset Offset in the string to read the domain name.
|
||||||
function parse_domain(data, offset)
|
function parse_domain(data, offset)
|
||||||
local offset, domain = dns.decStr(data, offset)
|
local offset, domain = dns.decStr(data, offset)
|
||||||
domain = domain or "<parse error>"
|
domain = domain or "<parse error>"
|
||||||
return offset, string.format("%s.", domain)
|
return offset, string.format("%s.", domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Build RFC 1035 root domain name from the name of the DNS server
|
--- Build RFC 1035 root domain name from the name of the DNS server
|
||||||
-- (e.g ns1.website.com.ar -> \007website\003com\002ar\000).
|
-- (e.g ns1.website.com.ar -> \007website\003com\002ar\000).
|
||||||
--@param host The host.
|
--@param host The host.
|
||||||
function build_domain(host)
|
function build_domain(host)
|
||||||
local names, buf, x
|
local names, buf, x
|
||||||
local abs_name, i, tmp
|
local abs_name, i, tmp
|
||||||
|
|
||||||
buf = strbuf.new()
|
buf = strbuf.new()
|
||||||
abs_name = {}
|
abs_name = {}
|
||||||
|
|
||||||
names = stdnse.strsplit('%.', host)
|
names = stdnse.strsplit('%.', host)
|
||||||
if names == nil then names = {host} end
|
if names == nil then names = {host} end
|
||||||
|
|
||||||
-- try to determine root of domain name
|
-- try to determine root of domain name
|
||||||
for i, x in ipairs(listop.reverse(names)) do
|
for i, x in ipairs(listop.reverse(names)) do
|
||||||
table.insert(abs_name, x)
|
table.insert(abs_name, x)
|
||||||
if not valid_tld(x) then break end
|
if not valid_tld(x) then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
abs_name = listop.reverse(abs_name)
|
abs_name = listop.reverse(abs_name)
|
||||||
|
|
||||||
-- prepend each element with its length
|
-- prepend each element with its length
|
||||||
while i <= #abs_name do
|
while i <= #abs_name do
|
||||||
buf = buf .. string.char(#abs_name[i]) .. abs_name[i]
|
buf = buf .. string.char(#abs_name[i]) .. abs_name[i]
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
buf = buf .. '\000'
|
buf = buf .. '\000'
|
||||||
return strbuf.dump(buf)
|
return strbuf.dump(buf)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parse_num_domain(data, offset)
|
local function parse_num_domain(data, offset)
|
||||||
@@ -264,25 +264,25 @@ end
|
|||||||
--- Retrieve type specific data (rdata) from dns packets
|
--- Retrieve type specific data (rdata) from dns packets
|
||||||
local RD = {
|
local RD = {
|
||||||
A = function(data, offset)
|
A = function(data, offset)
|
||||||
return offset+4, packet.toip(data:sub(offset, offset+3))
|
return offset+4, packet.toip(data:sub(offset, offset+3))
|
||||||
end,
|
end,
|
||||||
NS = parse_domain,
|
NS = parse_domain,
|
||||||
MD = parse_domain, -- obsolete per rfc1035, use MX
|
MD = parse_domain, -- obsolete per rfc1035, use MX
|
||||||
MF = parse_domain, -- obsolete per rfc1035, use MX
|
MF = parse_domain, -- obsolete per rfc1035, use MX
|
||||||
CNAME = parse_domain,
|
CNAME = parse_domain,
|
||||||
SOA = function(data, offset)
|
SOA = function(data, offset)
|
||||||
local field, info
|
local field, info
|
||||||
info = strbuf.new()
|
info = strbuf.new()
|
||||||
-- name server
|
-- name server
|
||||||
offset, field = parse_domain(data, offset)
|
offset, field = parse_domain(data, offset)
|
||||||
info = info .. field;
|
info = info .. field;
|
||||||
-- mail box
|
-- mail box
|
||||||
offset, field = parse_domain(data, offset)
|
offset, field = parse_domain(data, offset)
|
||||||
info = info .. field;
|
info = info .. field;
|
||||||
-- ignore other values
|
-- ignore other values
|
||||||
offset = offset + 20
|
offset = offset + 20
|
||||||
return offset, strbuf.dump(info, ' ')
|
return offset, strbuf.dump(info, ' ')
|
||||||
end,
|
end,
|
||||||
MB = parse_domain, -- experimental per RFC 1035
|
MB = parse_domain, -- experimental per RFC 1035
|
||||||
MG = parse_domain, -- experimental per RFC 1035
|
MG = parse_domain, -- experimental per RFC 1035
|
||||||
MR = parse_domain, -- experimental per RFC 1035
|
MR = parse_domain, -- experimental per RFC 1035
|
||||||
@@ -392,7 +392,7 @@ local RD = {
|
|||||||
lon = 0-lon
|
lon = 0-lon
|
||||||
end
|
end
|
||||||
return offset, string.format("%f %s %f %s %dm %0.1fm %0.1fm %0.1fm",
|
return offset, string.format("%f %s %f %s %dm %0.1fm %0.1fm %0.1fm",
|
||||||
lat, latd, lon, lond, alt/100 - 100000, siz, hp, vp)
|
lat, latd, lon, lond, alt/100 - 100000, siz, hp, vp)
|
||||||
end,
|
end,
|
||||||
--NXT --obsolete RR relating to DNSSEC
|
--NXT --obsolete RR relating to DNSSEC
|
||||||
--EID NIMLOC --related to Nimrod DARPA project (Patton1995)
|
--EID NIMLOC --related to Nimrod DARPA project (Patton1995)
|
||||||
@@ -453,44 +453,44 @@ local RD = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function get_rdata(data, offset, ttype)
|
function get_rdata(data, offset, ttype)
|
||||||
if typetab[ttype] == nil then
|
if typetab[ttype] == nil then
|
||||||
return offset, ''
|
return offset, ''
|
||||||
elseif RD[typetab[ttype]] then
|
elseif RD[typetab[ttype]] then
|
||||||
return RD[typetab[ttype]](data, offset)
|
return RD[typetab[ttype]](data, offset)
|
||||||
else
|
else
|
||||||
local field
|
local field
|
||||||
offset, field = bin.unpack("A" .. bto16(data, offset-2), data, offset)
|
offset, field = bin.unpack("A" .. bto16(data, offset-2), data, offset)
|
||||||
return offset, ("hex: %s"):format(stdnse.tohex(field))
|
return offset, ("hex: %s"):format(stdnse.tohex(field))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a single answer record from the current offset
|
--- Get a single answer record from the current offset
|
||||||
function get_answer_record(table, data, offset)
|
function get_answer_record(table, data, offset)
|
||||||
local line, rdlen, ttype
|
local line, rdlen, ttype
|
||||||
|
|
||||||
-- answer domain
|
-- answer domain
|
||||||
offset, line = parse_domain(data, offset)
|
offset, line = parse_domain(data, offset)
|
||||||
table.domain = line
|
table.domain = line
|
||||||
|
|
||||||
-- answer record type
|
-- answer record type
|
||||||
ttype = bto16(data, offset)
|
ttype = bto16(data, offset)
|
||||||
if not(typetab[ttype] == nil) then
|
if not(typetab[ttype] == nil) then
|
||||||
table.ttype = typetab[ttype]
|
table.ttype = typetab[ttype]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- length of type specific data
|
-- length of type specific data
|
||||||
rdlen = bto16(data, offset+8)
|
rdlen = bto16(data, offset+8)
|
||||||
|
|
||||||
-- extra data, ignore ttl and class
|
-- extra data, ignore ttl and class
|
||||||
offset, line = get_rdata(data, offset+10, ttype)
|
offset, line = get_rdata(data, offset+10, ttype)
|
||||||
if(line == '') then
|
if(line == '') then
|
||||||
offset = offset + rdlen
|
offset = offset + rdlen
|
||||||
return false, offset
|
return false, offset
|
||||||
else
|
else
|
||||||
table.rdata = line
|
table.rdata = line
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, offset
|
return true, offset
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse and save uniq records in the results table
|
-- parse and save uniq records in the results table
|
||||||
@@ -514,61 +514,61 @@ end
|
|||||||
|
|
||||||
-- parse and save only valid records
|
-- parse and save only valid records
|
||||||
function parse_records(number, data, results, offset)
|
function parse_records(number, data, results, offset)
|
||||||
while number > 0 do
|
while number > 0 do
|
||||||
local answer, st = {}
|
local answer, st = {}
|
||||||
st, offset = get_answer_record(answer, data, offset)
|
st, offset = get_answer_record(answer, data, offset)
|
||||||
if st then
|
if st then
|
||||||
parse_uniq_records(results, answer)
|
parse_uniq_records(results, answer)
|
||||||
end
|
|
||||||
number = number - 1
|
|
||||||
end
|
end
|
||||||
return offset
|
number = number - 1
|
||||||
|
end
|
||||||
|
return offset
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse and save all records in order to dump them to output
|
-- parse and save all records in order to dump them to output
|
||||||
function parse_records_table(number, data, table, offset)
|
function parse_records_table(number, data, table, offset)
|
||||||
while number > 0 do
|
while number > 0 do
|
||||||
local answer, st = {}
|
local answer, st = {}
|
||||||
st, offset = get_answer_record(answer, data, offset)
|
st, offset = get_answer_record(answer, data, offset)
|
||||||
if st then
|
if st then
|
||||||
if answer.domain then
|
if answer.domain then
|
||||||
tab.add(table, 1, answer.domain)
|
tab.add(table, 1, answer.domain)
|
||||||
end
|
end
|
||||||
if answer.ttype then
|
if answer.ttype then
|
||||||
tab.add(table, 2, answer.ttype)
|
tab.add(table, 2, answer.ttype)
|
||||||
end
|
end
|
||||||
if answer.rdata then
|
if answer.rdata then
|
||||||
tab.add(table, 3, answer.rdata)
|
tab.add(table, 3, answer.rdata)
|
||||||
end
|
end
|
||||||
tab.nextrow(table)
|
tab.nextrow(table)
|
||||||
end
|
|
||||||
number = number - 1
|
|
||||||
end
|
end
|
||||||
return offset
|
number = number - 1
|
||||||
|
end
|
||||||
|
return offset
|
||||||
end
|
end
|
||||||
|
|
||||||
-- An iterator that breaks up a concatentation of responses. In DNS over TCP,
|
-- An iterator that breaks up a concatentation of responses. In DNS over TCP,
|
||||||
-- each response is prefixed by a two-byte length (RFC 1035 section 4.2.2).
|
-- each response is prefixed by a two-byte length (RFC 1035 section 4.2.2).
|
||||||
-- Responses returned by this iterator include the two-byte length prefix.
|
-- Responses returned by this iterator include the two-byte length prefix.
|
||||||
function responses_iter(data)
|
function responses_iter(data)
|
||||||
local offset = 1
|
local offset = 1
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
local length, remaining, response
|
local length, remaining, response
|
||||||
|
|
||||||
remaining = #data - offset + 1
|
remaining = #data - offset + 1
|
||||||
if remaining == 0 then
|
if remaining == 0 then
|
||||||
return nil
|
return nil
|
||||||
end
|
|
||||||
assert(remaining >= 14 + 2)
|
|
||||||
length = bto16(data, offset)
|
|
||||||
assert(length <= remaining)
|
|
||||||
-- Skip over the length field.
|
|
||||||
offset = offset + 2
|
|
||||||
response = string.sub(data, offset, offset + length - 1)
|
|
||||||
offset = offset + length
|
|
||||||
return response
|
|
||||||
end
|
end
|
||||||
|
assert(remaining >= 14 + 2)
|
||||||
|
length = bto16(data, offset)
|
||||||
|
assert(length <= remaining)
|
||||||
|
-- Skip over the length field.
|
||||||
|
offset = offset + 2
|
||||||
|
response = string.sub(data, offset, offset + length - 1)
|
||||||
|
offset = offset + length
|
||||||
|
return response
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add axfr results to Nmap scan queue
|
-- add axfr results to Nmap scan queue
|
||||||
@@ -586,7 +586,7 @@ function add_zone_info(response)
|
|||||||
offset = offset + 12
|
offset = offset + 12
|
||||||
|
|
||||||
if questions > 1 then
|
if questions > 1 then
|
||||||
return false, 'More then 1 question record, something has gone wrong'
|
return false, 'More then 1 question record, something has gone wrong'
|
||||||
end
|
end
|
||||||
|
|
||||||
if answers == 0 then
|
if answers == 0 then
|
||||||
@@ -601,8 +601,8 @@ function add_zone_info(response)
|
|||||||
|
|
||||||
-- parse all available resource records
|
-- parse all available resource records
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Script %s: parsing ANCOUNT == %d, NSCOUNT == %d, ARCOUNT == %d",
|
"Script %s: parsing ANCOUNT == %d, NSCOUNT == %d, ARCOUNT == %d",
|
||||||
SCRIPT_NAME, answers, auth_answers, add_answers)
|
SCRIPT_NAME, answers, auth_answers, add_answers)
|
||||||
RR['Node Names'] = {}
|
RR['Node Names'] = {}
|
||||||
offset = parse_records(answers, data, RR, offset)
|
offset = parse_records(answers, data, RR, offset)
|
||||||
offset = parse_records(auth_answers, data, RR, offset)
|
offset = parse_records(auth_answers, data, RR, offset)
|
||||||
@@ -639,7 +639,7 @@ function add_zone_info(response)
|
|||||||
status, ret = target.add(rdata)
|
status, ret = target.add(rdata)
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Error: failed to add all 'A' records.")
|
"Error: failed to add all 'A' records.")
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
newhosts_count = newhosts_count + ret
|
newhosts_count = newhosts_count + ret
|
||||||
@@ -650,7 +650,7 @@ function add_zone_info(response)
|
|||||||
status, ret = target.add(rdata)
|
status, ret = target.add(rdata)
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Error: failed to add all '%s' records.", rectype)
|
"Error: failed to add all '%s' records.", rectype)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
newhosts_count = newhosts_count + ret
|
newhosts_count = newhosts_count + ret
|
||||||
@@ -672,8 +672,8 @@ function add_zone_info(response)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return true, tab.dump(outtab) .. "\n" ..
|
return true, tab.dump(outtab) .. "\n" ..
|
||||||
string.format("Total new targets added to Nmap scan queue: %d.",
|
string.format("Total new targets added to Nmap scan queue: %d.",
|
||||||
nhosts)
|
nhosts)
|
||||||
end
|
end
|
||||||
|
|
||||||
function dump_zone_info(table, response)
|
function dump_zone_info(table, response)
|
||||||
@@ -690,11 +690,11 @@ function dump_zone_info(table, response)
|
|||||||
offset = offset + 12
|
offset = offset + 12
|
||||||
|
|
||||||
if questions > 1 then
|
if questions > 1 then
|
||||||
return false, 'More then 1 question record, something has gone wrong'
|
return false, 'More then 1 question record, something has gone wrong'
|
||||||
end
|
end
|
||||||
|
|
||||||
if answers == 0 then
|
if answers == 0 then
|
||||||
return false, 'transfer successful but no records'
|
return false, 'transfer successful but no records'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- skip over the question section, we don't need it
|
-- skip over the question section, we don't need it
|
||||||
@@ -716,66 +716,66 @@ function dump_zone_info(table, response)
|
|||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
if not dns_opts.domain then
|
if not dns_opts.domain then
|
||||||
return stdnse.format_output(false,
|
return stdnse.format_output(false,
|
||||||
string.format("'%s' script needs a dnszonetransfer.domain argument.",
|
string.format("'%s' script needs a dnszonetransfer.domain argument.",
|
||||||
SCRIPT_TYPE))
|
SCRIPT_TYPE))
|
||||||
|
end
|
||||||
|
if not dns_opts.port then
|
||||||
|
dns_opts.port = 53
|
||||||
|
end
|
||||||
|
|
||||||
|
local soc = nmap.new_socket()
|
||||||
|
local catch = function() soc:close() end
|
||||||
|
local try = nmap.new_try(catch)
|
||||||
|
soc:set_timeout(4000)
|
||||||
|
try(soc:connect(dns_opts.server, dns_opts.port))
|
||||||
|
|
||||||
|
local req_id = '\222\173'
|
||||||
|
local offset = 1
|
||||||
|
local name = build_domain(string.lower(dns_opts.domain))
|
||||||
|
local pkt_len = #name + 16
|
||||||
|
|
||||||
|
-- build axfr request
|
||||||
|
local buf = strbuf.new()
|
||||||
|
buf = buf .. '\000' .. string.char(pkt_len) .. req_id
|
||||||
|
buf = buf .. '\000\000\000\001\000\000\000\000\000\000'
|
||||||
|
buf = buf .. name .. '\000\252\000\001'
|
||||||
|
try(soc:send(strbuf.dump(buf)))
|
||||||
|
|
||||||
|
-- read all data returned. Common to have
|
||||||
|
-- multiple packets from a single request
|
||||||
|
local response = strbuf.new()
|
||||||
|
while true do
|
||||||
|
local status, data = soc:receive_bytes(1)
|
||||||
|
if not status then break end
|
||||||
|
response = response .. data
|
||||||
|
end
|
||||||
|
soc:close()
|
||||||
|
|
||||||
|
local response_str = strbuf.dump(response)
|
||||||
|
local length = #response_str
|
||||||
|
|
||||||
|
-- check server response code
|
||||||
|
if length < 6 or
|
||||||
|
not (bit.band(string.byte(response_str, 6), 15) == 0) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add axfr results to Nmap scanning queue
|
||||||
|
if target.ALLOW_NEW_TARGETS then
|
||||||
|
local status, ret = add_zone_info(response_str)
|
||||||
|
if not status then
|
||||||
|
return stdnse.format_output(false, ret)
|
||||||
end
|
end
|
||||||
if not dns_opts.port then
|
return stdnse.format_output(true, ret)
|
||||||
dns_opts.port = 53
|
|
||||||
end
|
|
||||||
|
|
||||||
local soc = nmap.new_socket()
|
|
||||||
local catch = function() soc:close() end
|
|
||||||
local try = nmap.new_try(catch)
|
|
||||||
soc:set_timeout(4000)
|
|
||||||
try(soc:connect(dns_opts.server, dns_opts.port))
|
|
||||||
|
|
||||||
local req_id = '\222\173'
|
|
||||||
local offset = 1
|
|
||||||
local name = build_domain(string.lower(dns_opts.domain))
|
|
||||||
local pkt_len = #name + 16
|
|
||||||
|
|
||||||
-- build axfr request
|
|
||||||
local buf = strbuf.new()
|
|
||||||
buf = buf .. '\000' .. string.char(pkt_len) .. req_id
|
|
||||||
buf = buf .. '\000\000\000\001\000\000\000\000\000\000'
|
|
||||||
buf = buf .. name .. '\000\252\000\001'
|
|
||||||
try(soc:send(strbuf.dump(buf)))
|
|
||||||
|
|
||||||
-- read all data returned. Common to have
|
|
||||||
-- multiple packets from a single request
|
|
||||||
local response = strbuf.new()
|
|
||||||
while true do
|
|
||||||
local status, data = soc:receive_bytes(1)
|
|
||||||
if not status then break end
|
|
||||||
response = response .. data
|
|
||||||
end
|
|
||||||
soc:close()
|
|
||||||
|
|
||||||
local response_str = strbuf.dump(response)
|
|
||||||
local length = #response_str
|
|
||||||
|
|
||||||
-- check server response code
|
|
||||||
if length < 6 or
|
|
||||||
not (bit.band(string.byte(response_str, 6), 15) == 0) then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- add axfr results to Nmap scanning queue
|
|
||||||
if target.ALLOW_NEW_TARGETS then
|
|
||||||
local status, ret = add_zone_info(response_str)
|
|
||||||
if not status then
|
|
||||||
return stdnse.format_output(false, ret)
|
|
||||||
end
|
|
||||||
return stdnse.format_output(true, ret)
|
|
||||||
-- dump axfr results
|
-- dump axfr results
|
||||||
else
|
else
|
||||||
local table = tab.new()
|
local table = tab.new()
|
||||||
local status, ret = dump_zone_info(table, response_str)
|
local status, ret = dump_zone_info(table, response_str)
|
||||||
if not status then
|
if not status then
|
||||||
return stdnse.format_output(false, ret)
|
return stdnse.format_output(false, ret)
|
||||||
end
|
|
||||||
return '\n' .. tab.dump(table)
|
|
||||||
end
|
end
|
||||||
|
return '\n' .. tab.dump(table)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -283,9 +283,9 @@ action = function(host, port)
|
|||||||
local path = basepath .. probe['path']
|
local path = basepath .. probe['path']
|
||||||
|
|
||||||
if http.page_exists(results[j], result_404, known_404, path, true)
|
if http.page_exists(results[j], result_404, known_404, path, true)
|
||||||
and (not fingerprint.target_check
|
and (not fingerprint.target_check
|
||||||
or fingerprint.target_check(host, port, path, results[j]))
|
or fingerprint.target_check(host, port, path, results[j]))
|
||||||
then
|
then
|
||||||
for _, login_combo in ipairs(fingerprint.login_combos) do
|
for _, login_combo in ipairs(fingerprint.login_combos) do
|
||||||
stdnse.print_debug(2, "%s: Trying login combo -> %s:%s", SCRIPT_NAME, login_combo["username"], login_combo["password"])
|
stdnse.print_debug(2, "%s: Trying login combo -> %s:%s", SCRIPT_NAME, login_combo["username"], login_combo["password"])
|
||||||
--Check default credentials
|
--Check default credentials
|
||||||
|
|||||||
@@ -101,14 +101,14 @@ portrule = shortport.port_or_service({80, 443}, {"http","https"}, "tcp", "open")
|
|||||||
-- @param port table as received by the action function
|
-- @param port table as received by the action function
|
||||||
-- @param path against which to check if authentication is required
|
-- @param path against which to check if authentication is required
|
||||||
local function requiresAuth( host, port, path )
|
local function requiresAuth( host, port, path )
|
||||||
local result = http.get(host, port, "/names.nsf")
|
local result = http.get(host, port, "/names.nsf")
|
||||||
|
|
||||||
if ( result.status == 401 ) then
|
if ( result.status == 401 ) then
|
||||||
return true
|
return true
|
||||||
elseif ( result.status == 200 and result.body and result.body:match("<input.-type=[\"]*password[\"]*") ) then
|
elseif ( result.status == 200 and result.body and result.body:match("<input.-type=[\"]*password[\"]*") ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Checks if the credentials are valid and allow access to <code>path</code>
|
--- Checks if the credentials are valid and allow access to <code>path</code>
|
||||||
@@ -121,14 +121,14 @@ end
|
|||||||
-- @param pass the password used for authentication
|
-- @param pass the password used for authentication
|
||||||
-- @return true on valid access, false on failure
|
-- @return true on valid access, false on failure
|
||||||
local function isValidCredential( host, port, path, user, pass )
|
local function isValidCredential( host, port, path, user, pass )
|
||||||
-- we need to supply the no_cache directive, or else the http library
|
-- we need to supply the no_cache directive, or else the http library
|
||||||
-- incorrectly tells us that the authentication was successfull
|
-- incorrectly tells us that the authentication was successfull
|
||||||
local result = http.get( host, port, path, { auth = { username = user, password = pass }, no_cache = true })
|
local result = http.get( host, port, path, { auth = { username = user, password = pass }, no_cache = true })
|
||||||
|
|
||||||
if ( result.status == 401 ) then
|
if ( result.status == 401 ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Retrieves all uniq links in a pages
|
--- Retrieves all uniq links in a pages
|
||||||
@@ -138,28 +138,28 @@ end
|
|||||||
-- @param links [optional] table containing previousy retrieved links
|
-- @param links [optional] table containing previousy retrieved links
|
||||||
-- @return links table containing retrieved links
|
-- @return links table containing retrieved links
|
||||||
local function getLinks( body, filter, links )
|
local function getLinks( body, filter, links )
|
||||||
local tmp = {}
|
local tmp = {}
|
||||||
local links = links or {}
|
local links = links or {}
|
||||||
local filter = filter or ".*"
|
local filter = filter or ".*"
|
||||||
|
|
||||||
if ( not(body) ) then return end
|
if ( not(body) ) then return end
|
||||||
for _, v in ipairs( links ) do
|
for _, v in ipairs( links ) do
|
||||||
tmp[v] = true
|
tmp[v] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
for link in body:gmatch("<a href=\"([^\"]+)\"") do
|
for link in body:gmatch("<a href=\"([^\"]+)\"") do
|
||||||
-- use link as key in order to remove duplicates
|
-- use link as key in order to remove duplicates
|
||||||
if ( link:match(filter)) then
|
if ( link:match(filter)) then
|
||||||
tmp[link] = true
|
tmp[link] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
links = {}
|
links = {}
|
||||||
for k, _ in pairs(tmp) do
|
for k, _ in pairs(tmp) do
|
||||||
table.insert(links, k)
|
table.insert(links, k)
|
||||||
end
|
end
|
||||||
|
|
||||||
return links
|
return links
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Retrieves the "next page" path from the returned document
|
--- Retrieves the "next page" path from the returned document
|
||||||
@@ -167,7 +167,7 @@ end
|
|||||||
-- @param body the html content of the recieved page
|
-- @param body the html content of the recieved page
|
||||||
-- @return link to next page
|
-- @return link to next page
|
||||||
local function getPager( body )
|
local function getPager( body )
|
||||||
return body:match("<form.+action=\"(.+%?ReadForm)&" )
|
return body:match("<form.+action=\"(.+%?ReadForm)&" )
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Retrieves the username and passwords for a user
|
--- Retrieves the username and passwords for a user
|
||||||
@@ -177,19 +177,19 @@ end
|
|||||||
-- @return password the password hash for the user
|
-- @return password the password hash for the user
|
||||||
local function getUserDetails( body )
|
local function getUserDetails( body )
|
||||||
|
|
||||||
-- retrieve the details
|
-- retrieve the details
|
||||||
local full_name = body:match("<input name=\"FullName\".-value=\"(.-)\">")
|
local full_name = body:match("<input name=\"FullName\".-value=\"(.-)\">")
|
||||||
local http_passwd = body:match("<input name=\"HTTPPassword\".-value=\"(.-)\">")
|
local http_passwd = body:match("<input name=\"HTTPPassword\".-value=\"(.-)\">")
|
||||||
local dsp_http_passwd = body:match("<input name=\"dspHTTPPassword\".-value=\"(.-)\">")
|
local dsp_http_passwd = body:match("<input name=\"dspHTTPPassword\".-value=\"(.-)\">")
|
||||||
local id_file = body:match("<a href=\"(.-UserID)\">")
|
local id_file = body:match("<a href=\"(.-UserID)\">")
|
||||||
|
|
||||||
-- Remove the parenthesis around the password
|
-- Remove the parenthesis around the password
|
||||||
http_passwd = http_passwd:sub(2,-2)
|
http_passwd = http_passwd:sub(2,-2)
|
||||||
-- In case we have more than one full name, return only the last
|
-- In case we have more than one full name, return only the last
|
||||||
full_name = stdnse.strsplit(";%s*", full_name)
|
full_name = stdnse.strsplit(";%s*", full_name)
|
||||||
full_name = full_name[#full_name]
|
full_name = full_name[#full_name]
|
||||||
|
|
||||||
return { fullname = full_name, passwd = ( http_passwd or dsp_http_passwd ), idfile = id_file }
|
return { fullname = full_name, passwd = ( http_passwd or dsp_http_passwd ), idfile = id_file }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Saves the ID file to disk
|
--- Saves the ID file to disk
|
||||||
@@ -199,162 +199,162 @@ end
|
|||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return err string containing error message if status is false
|
-- @return err string containing error message if status is false
|
||||||
local function saveIDFile( filename, data )
|
local function saveIDFile( filename, data )
|
||||||
local f = io.open( filename, "w")
|
local f = io.open( filename, "w")
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return false, ("Failed to open file (%s)"):format(filename)
|
return false, ("Failed to open file (%s)"):format(filename)
|
||||||
end
|
end
|
||||||
if ( not(f:write( data ) ) ) then
|
if ( not(f:write( data ) ) ) then
|
||||||
return false, ("Failed to write file (%s)"):format(filename)
|
return false, ("Failed to write file (%s)"):format(filename)
|
||||||
end
|
end
|
||||||
f:close()
|
f:close()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local path = "/names.nsf"
|
local path = "/names.nsf"
|
||||||
local download_path = stdnse.get_script_args('domino-enum-passwords.idpath')
|
local download_path = stdnse.get_script_args('domino-enum-passwords.idpath')
|
||||||
local vhost= stdnse.get_script_args('domino-enum-passwords.hostname')
|
local vhost= stdnse.get_script_args('domino-enum-passwords.hostname')
|
||||||
local user = stdnse.get_script_args('domino-enum-passwords.username')
|
local user = stdnse.get_script_args('domino-enum-passwords.username')
|
||||||
local pass = stdnse.get_script_args('domino-enum-passwords.password')
|
local pass = stdnse.get_script_args('domino-enum-passwords.password')
|
||||||
local creds, pos, pager
|
local creds, pos, pager
|
||||||
local links, result, hashes,legacyHashes, id_files = {}, {}, {}, {},{}
|
local links, result, hashes,legacyHashes, id_files = {}, {}, {}, {},{}
|
||||||
local chunk_size = 30
|
local chunk_size = 30
|
||||||
local max_fetch = stdnse.get_script_args('domino-enum-passwords.count') and tonumber(stdnse.get_script_args('domino-enum-passwords.count')) or 10
|
local max_fetch = stdnse.get_script_args('domino-enum-passwords.count') and tonumber(stdnse.get_script_args('domino-enum-passwords.count')) or 10
|
||||||
local http_response
|
local http_response
|
||||||
|
|
||||||
if ( nmap.registry['credentials'] and nmap.registry['credentials']['http'] ) then
|
if ( nmap.registry['credentials'] and nmap.registry['credentials']['http'] ) then
|
||||||
creds = nmap.registry['credentials']['http']
|
creds = nmap.registry['credentials']['http']
|
||||||
end
|
end
|
||||||
|
|
||||||
-- authentication required?
|
-- authentication required?
|
||||||
if ( requiresAuth( vhost or host, port, path ) ) then
|
if ( requiresAuth( vhost or host, port, path ) ) then
|
||||||
if ( not(user) and not(creds) ) then
|
if ( not(user) and not(creds) ) then
|
||||||
return " \n ERROR: No credentials supplied (see domino-enum-passwords.username and domino-enum-passwords.password)"
|
return " \n ERROR: No credentials supplied (see domino-enum-passwords.username and domino-enum-passwords.password)"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- A user was provided, attempt to authenticate
|
-- A user was provided, attempt to authenticate
|
||||||
if ( user ) then
|
if ( user ) then
|
||||||
if (not(isValidCredential( vhost or host, port, path, user, pass )) ) then
|
if (not(isValidCredential( vhost or host, port, path, user, pass )) ) then
|
||||||
return " \n ERROR: The provided credentials where invalid"
|
return " \n ERROR: The provided credentials where invalid"
|
||||||
end
|
end
|
||||||
elseif ( creds ) then
|
elseif ( creds ) then
|
||||||
for _, cred in pairs(creds) do
|
for _, cred in pairs(creds) do
|
||||||
if ( isValidCredential( vhost or host, port, path, cred.username, cred.password ) ) then
|
if ( isValidCredential( vhost or host, port, path, cred.username, cred.password ) ) then
|
||||||
user = cred.username
|
user = cred.username
|
||||||
pass = cred.password
|
pass = cred.password
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(user) and not(pass) ) then
|
if ( not(user) and not(pass) ) then
|
||||||
return " \n ERROR: No valid credentials were found (see domino-enum-passwords.username and domino-enum-passwords.password)"
|
return " \n ERROR: No valid credentials were found (see domino-enum-passwords.username and domino-enum-passwords.password)"
|
||||||
end
|
end
|
||||||
|
|
||||||
path = "/names.nsf/People?OpenView"
|
path = "/names.nsf/People?OpenView"
|
||||||
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
|
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
|
||||||
pager = getPager( http_response.body )
|
pager = getPager( http_response.body )
|
||||||
if ( not(pager) ) then
|
if ( not(pager) ) then
|
||||||
if ( http_response.body and
|
if ( http_response.body and
|
||||||
http_response.body:match(".*<input type=\"submit\".* value=\"Sign In\">.*" ) ) then
|
http_response.body:match(".*<input type=\"submit\".* value=\"Sign In\">.*" ) ) then
|
||||||
return " \n ERROR: Failed to authenticate"
|
return " \n ERROR: Failed to authenticate"
|
||||||
else
|
else
|
||||||
return " \n ERROR: Failed to process results"
|
return " \n ERROR: Failed to process results"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
pos = 1
|
pos = 1
|
||||||
|
|
||||||
-- first collect all links
|
-- first collect all links
|
||||||
while( true ) do
|
while( true ) do
|
||||||
path = pager .. "&Start=" .. pos
|
path = pager .. "&Start=" .. pos
|
||||||
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
|
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
|
||||||
|
|
||||||
if ( http_response.status == 200 ) then
|
if ( http_response.status == 200 ) then
|
||||||
local size = #links
|
local size = #links
|
||||||
links = getLinks( http_response.body, "%?OpenDocument", links )
|
links = getLinks( http_response.body, "%?OpenDocument", links )
|
||||||
-- No additions were made
|
-- No additions were made
|
||||||
if ( size == #links ) then
|
if ( size == #links ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( max_fetch > 0 and max_fetch < #links ) then
|
if ( max_fetch > 0 and max_fetch < #links ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
pos = pos + chunk_size
|
pos = pos + chunk_size
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, link in ipairs(links) do
|
for _, link in ipairs(links) do
|
||||||
stdnse.print_debug(2, "Fetching link: %s", link)
|
stdnse.print_debug(2, "Fetching link: %s", link)
|
||||||
http_response = http.get( vhost or host, port, link, { auth = { username = user, password = pass }, no_cache = true })
|
http_response = http.get( vhost or host, port, link, { auth = { username = user, password = pass }, no_cache = true })
|
||||||
local u_details = getUserDetails( http_response.body )
|
local u_details = getUserDetails( http_response.body )
|
||||||
|
|
||||||
if ( max_fetch > 0 and (#hashes+#legacyHashes)>= max_fetch ) then
|
if ( max_fetch > 0 and (#hashes+#legacyHashes)>= max_fetch ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( u_details.fullname and u_details.passwd and #u_details.passwd > 0 ) then
|
if ( u_details.fullname and u_details.passwd and #u_details.passwd > 0 ) then
|
||||||
stdnse.print_debug(2, "Found Internet hash for: %s:%s", u_details.fullname, u_details.passwd)
|
stdnse.print_debug(2, "Found Internet hash for: %s:%s", u_details.fullname, u_details.passwd)
|
||||||
-- Old type are 32 bytes, new are 20
|
-- Old type are 32 bytes, new are 20
|
||||||
if #u_details.passwd == 32 then
|
if #u_details.passwd == 32 then
|
||||||
table.insert( legacyHashes, ("%s:%s"):format(u_details.fullname, u_details.passwd))
|
table.insert( legacyHashes, ("%s:%s"):format(u_details.fullname, u_details.passwd))
|
||||||
else
|
else
|
||||||
table.insert( hashes, ("%s:(%s)"):format(u_details.fullname, u_details.passwd))
|
table.insert( hashes, ("%s:(%s)"):format(u_details.fullname, u_details.passwd))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( u_details.idfile ) then
|
if ( u_details.idfile ) then
|
||||||
stdnse.print_debug(2, "Found ID file for user: %s", u_details.fullname)
|
stdnse.print_debug(2, "Found ID file for user: %s", u_details.fullname)
|
||||||
if ( download_path ) then
|
if ( download_path ) then
|
||||||
stdnse.print_debug(2, "Downloading ID file for user: %s", u_details.full_name)
|
stdnse.print_debug(2, "Downloading ID file for user: %s", u_details.full_name)
|
||||||
http_response = http.get( vhost or host, port, u_details.idfile, { auth = { username = user, password = pass }, no_cache = true })
|
http_response = http.get( vhost or host, port, u_details.idfile, { auth = { username = user, password = pass }, no_cache = true })
|
||||||
|
|
||||||
if ( http_response.status == 200 ) then
|
if ( http_response.status == 200 ) then
|
||||||
local filename = download_path .. "/" .. stdnse.filename_escape(u_details.fullname .. ".id")
|
local filename = download_path .. "/" .. stdnse.filename_escape(u_details.fullname .. ".id")
|
||||||
local status, err = saveIDFile( filename, http_response.body )
|
local status, err = saveIDFile( filename, http_response.body )
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
table.insert( id_files, ("%s ID File has been downloaded (%s)"):format(u_details.fullname, filename) )
|
table.insert( id_files, ("%s ID File has been downloaded (%s)"):format(u_details.fullname, filename) )
|
||||||
else
|
else
|
||||||
table.insert( id_files, ("%s ID File was not saved (error: %s)"):format(u_details.fullname, err ) )
|
table.insert( id_files, ("%s ID File was not saved (error: %s)"):format(u_details.fullname, err ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert( id_files, ("%s ID File was not saved (error: unexpected response from server)"):format( u_details.fullname ) )
|
table.insert( id_files, ("%s ID File was not saved (error: unexpected response from server)"):format( u_details.fullname ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert( id_files, ("%s has ID File available for download"):format(u_details.fullname) )
|
table.insert( id_files, ("%s has ID File available for download"):format(u_details.fullname) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if( #hashes + #legacyHashes > 0) then
|
if( #hashes + #legacyHashes > 0) then
|
||||||
table.insert( result, { name = "Information", [1] = ("Information retrieved as: \"%s\""):format(user) } )
|
table.insert( result, { name = "Information", [1] = ("Information retrieved as: \"%s\""):format(user) } )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( #hashes ) then
|
if ( #hashes ) then
|
||||||
hashes.name = "Internet hashes (salted, jtr: --format=DOMINOSEC)"
|
hashes.name = "Internet hashes (salted, jtr: --format=DOMINOSEC)"
|
||||||
table.insert( result, hashes )
|
table.insert( result, hashes )
|
||||||
end
|
end
|
||||||
if (#legacyHashes ) then
|
if (#legacyHashes ) then
|
||||||
legacyHashes.name = "Internet hashes (unsalted, jtr: --format=lotus5)"
|
legacyHashes.name = "Internet hashes (unsalted, jtr: --format=lotus5)"
|
||||||
table.insert( result, legacyHashes )
|
table.insert( result, legacyHashes )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( #id_files ) then
|
if ( #id_files ) then
|
||||||
id_files.name = "ID Files"
|
id_files.name = "ID Files"
|
||||||
table.insert( result, id_files )
|
table.insert( result, id_files )
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = stdnse.format_output(true, result)
|
local result = stdnse.format_output(true, result)
|
||||||
|
|
||||||
if ( max_fetch > 0 ) then
|
if ( max_fetch > 0 ) then
|
||||||
result = result .. (" \n Results limited to %d results (see domino-enum-passwords.count)"):format(max_fetch)
|
result = result .. (" \n Results limited to %d results (see domino-enum-passwords.count)"):format(max_fetch)
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -87,75 +87,75 @@ local common_ext = { 'php', 'asp', 'aspx', 'jsp', 'pl', 'cgi', 'css', 'js', 'htm
|
|||||||
--
|
--
|
||||||
-- At the time of the writing, these were all decided by me (Ron Bowes).
|
-- At the time of the writing, these were all decided by me (Ron Bowes).
|
||||||
local function get_variations(filename)
|
local function get_variations(filename)
|
||||||
local variations = {}
|
local variations = {}
|
||||||
|
|
||||||
if(filename == nil or filename == "" or filename == "/") then
|
if(filename == nil or filename == "" or filename == "/") then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
local is_directory = (string.sub(filename, #filename, #filename) == "/")
|
local is_directory = (string.sub(filename, #filename, #filename) == "/")
|
||||||
if(is_directory) then
|
if(is_directory) then
|
||||||
filename = string.sub(filename, 1, #filename - 1)
|
filename = string.sub(filename, 1, #filename - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try some extensions
|
-- Try some extensions
|
||||||
table.insert(variations, filename .. ".bak")
|
table.insert(variations, filename .. ".bak")
|
||||||
table.insert(variations, filename .. ".1")
|
table.insert(variations, filename .. ".1")
|
||||||
table.insert(variations, filename .. ".tmp")
|
table.insert(variations, filename .. ".tmp")
|
||||||
|
|
||||||
-- Strip off the extension, if it has one, and try it all again.
|
-- Strip off the extension, if it has one, and try it all again.
|
||||||
-- For now, just look for three-character extensions.
|
-- For now, just look for three-character extensions.
|
||||||
if(string.sub(filename, #filename - 3, #filename - 3) == '.') then
|
if(string.sub(filename, #filename - 3, #filename - 3) == '.') then
|
||||||
local bare = string.sub(filename, 1, #filename - 4)
|
local bare = string.sub(filename, 1, #filename - 4)
|
||||||
local extension = string.sub(filename, #filename - 3)
|
local extension = string.sub(filename, #filename - 3)
|
||||||
|
|
||||||
table.insert(variations, bare .. ".bak")
|
table.insert(variations, bare .. ".bak")
|
||||||
table.insert(variations, bare .. ".1")
|
table.insert(variations, bare .. ".1")
|
||||||
table.insert(variations, bare .. ".tmp")
|
table.insert(variations, bare .. ".tmp")
|
||||||
table.insert(variations, bare .. "_1" .. extension)
|
table.insert(variations, bare .. "_1" .. extension)
|
||||||
table.insert(variations, bare .. "2" .. extension)
|
table.insert(variations, bare .. "2" .. extension)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Some Windowsy things
|
-- Some Windowsy things
|
||||||
local onlyname = string.sub(filename, 2)
|
local onlyname = string.sub(filename, 2)
|
||||||
-- If the name contains a '/', forget it
|
-- If the name contains a '/', forget it
|
||||||
if(string.find(onlyname, "/") == nil) then
|
if(string.find(onlyname, "/") == nil) then
|
||||||
table.insert(variations, "/Copy of " .. onlyname)
|
table.insert(variations, "/Copy of " .. onlyname)
|
||||||
table.insert(variations, "/Copy (2) of " .. onlyname)
|
table.insert(variations, "/Copy (2) of " .. onlyname)
|
||||||
table.insert(variations, "/Copy of Copy of " .. onlyname)
|
table.insert(variations, "/Copy of Copy of " .. onlyname)
|
||||||
|
|
||||||
-- Word/Excel/etc replace the first two characters with '~$', it seems
|
-- Word/Excel/etc replace the first two characters with '~$', it seems
|
||||||
table.insert(variations, "/~$" .. string.sub(filename, 4))
|
table.insert(variations, "/~$" .. string.sub(filename, 4))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Some editors add a '~'
|
-- Some editors add a '~'
|
||||||
table.insert(variations, filename .. "~")
|
table.insert(variations, filename .. "~")
|
||||||
|
|
||||||
-- Try some directories
|
-- Try some directories
|
||||||
table.insert(variations, "/bak" .. filename)
|
table.insert(variations, "/bak" .. filename)
|
||||||
table.insert(variations, "/backup" .. filename)
|
table.insert(variations, "/backup" .. filename)
|
||||||
table.insert(variations, "/backups" .. filename)
|
table.insert(variations, "/backups" .. filename)
|
||||||
table.insert(variations, "/beta" .. filename)
|
table.insert(variations, "/beta" .. filename)
|
||||||
table.insert(variations, "/test" .. filename)
|
table.insert(variations, "/test" .. filename)
|
||||||
|
|
||||||
-- If it's a directory, add a '/' after every entry
|
-- If it's a directory, add a '/' after every entry
|
||||||
if(is_directory) then
|
if(is_directory) then
|
||||||
for i, v in ipairs(variations) do
|
for i, v in ipairs(variations) do
|
||||||
variations[i] = v .. "/"
|
variations[i] = v .. "/"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Some compressed formats (we don't want a trailing '/' on these, so they go after the loop)
|
-- Some compressed formats (we don't want a trailing '/' on these, so they go after the loop)
|
||||||
table.insert(variations, filename .. ".zip")
|
table.insert(variations, filename .. ".zip")
|
||||||
table.insert(variations, filename .. ".tar")
|
table.insert(variations, filename .. ".tar")
|
||||||
table.insert(variations, filename .. ".tar.gz")
|
table.insert(variations, filename .. ".tar.gz")
|
||||||
table.insert(variations, filename .. ".tgz")
|
table.insert(variations, filename .. ".tgz")
|
||||||
table.insert(variations, filename .. ".tar.bz2")
|
table.insert(variations, filename .. ".tar.bz2")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return variations
|
return variations
|
||||||
end
|
end
|
||||||
|
|
||||||
---Get the list of fingerprints from files. The files are defined in <code>fingerprint_files</code>. If category
|
---Get the list of fingerprints from files. The files are defined in <code>fingerprint_files</code>. If category
|
||||||
@@ -163,227 +163,227 @@ end
|
|||||||
--
|
--
|
||||||
--@return An array of entries, each of which have a <code>checkdir</code> field, and possibly a <code>checkdesc</code>.
|
--@return An array of entries, each of which have a <code>checkdir</code> field, and possibly a <code>checkdesc</code>.
|
||||||
local function get_fingerprints(fingerprint_file, category)
|
local function get_fingerprints(fingerprint_file, category)
|
||||||
local entries = {}
|
local entries = {}
|
||||||
local i
|
local i
|
||||||
local total_count = 0 -- Used for 'limit'
|
local total_count = 0 -- Used for 'limit'
|
||||||
|
|
||||||
-- Check if we've already read the file
|
-- Check if we've already read the file
|
||||||
-- There might be a race condition here, where multiple scripts will read the file and set this variable, but the impact
|
-- There might be a race condition here, where multiple scripts will read the file and set this variable, but the impact
|
||||||
-- of that would be minimal (and definitely isn't security)
|
-- of that would be minimal (and definitely isn't security)
|
||||||
if(nmap.registry.http_fingerprints ~= nil) then
|
if(nmap.registry.http_fingerprints ~= nil) then
|
||||||
stdnse.print_debug(1, "http-enum: Using cached HTTP fingerprints")
|
stdnse.print_debug(1, "http-enum: Using cached HTTP fingerprints")
|
||||||
return nmap.registry.http_fingerprints
|
return nmap.registry.http_fingerprints
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try and find the file; if it isn't in Nmap's directories, take it as a direct path
|
-- Try and find the file; if it isn't in Nmap's directories, take it as a direct path
|
||||||
local filename_full = nmap.fetchfile('nselib/data/' .. fingerprint_file)
|
local filename_full = nmap.fetchfile('nselib/data/' .. fingerprint_file)
|
||||||
if(not(filename_full)) then
|
if(not(filename_full)) then
|
||||||
filename_full = fingerprint_file
|
filename_full = fingerprint_file
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug("http-enum: Loading fingerprint database: %s", filename_full)
|
stdnse.print_debug("http-enum: Loading fingerprint database: %s", filename_full)
|
||||||
local env = setmetatable({fingerprints = {}}, {__index = _G})
|
local env = setmetatable({fingerprints = {}}, {__index = _G})
|
||||||
local file = loadfile(filename_full, "t", env)
|
local file = loadfile(filename_full, "t", env)
|
||||||
if(not(file)) then
|
if(not(file)) then
|
||||||
stdnse.print_debug("http-enum: Couldn't load configuration file: %s", filename_full)
|
stdnse.print_debug("http-enum: Couldn't load configuration file: %s", filename_full)
|
||||||
return false, "Couldn't load fingerprint file: " .. filename_full
|
return false, "Couldn't load fingerprint file: " .. filename_full
|
||||||
end
|
end
|
||||||
|
|
||||||
file()
|
file()
|
||||||
|
|
||||||
local fingerprints = env.fingerprints
|
local fingerprints = env.fingerprints
|
||||||
|
|
||||||
-- Sanity check our file to ensure that all the fields were good. If any are bad, we
|
-- Sanity check our file to ensure that all the fields were good. If any are bad, we
|
||||||
-- stop and don't load the file.
|
-- stop and don't load the file.
|
||||||
for i, fingerprint in pairs(fingerprints) do
|
for i, fingerprint in pairs(fingerprints) do
|
||||||
-- Make sure we have a valid index
|
-- Make sure we have a valid index
|
||||||
if(type(i) ~= 'number') then
|
if(type(i) ~= 'number') then
|
||||||
return false, "The 'fingerprints' table is an array, not a table; all indexes should be numeric"
|
return false, "The 'fingerprints' table is an array, not a table; all indexes should be numeric"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure they have either a string or a table of probes
|
-- Make sure they have either a string or a table of probes
|
||||||
if(not(fingerprint.probes) or
|
if(not(fingerprint.probes) or
|
||||||
(type(fingerprint.probes) ~= 'table' and type(fingerprint.probes) ~= 'string') or
|
(type(fingerprint.probes) ~= 'table' and type(fingerprint.probes) ~= 'string') or
|
||||||
(type(fingerprint.probes) == 'table' and #fingerprint.probes == 0)) then
|
(type(fingerprint.probes) == 'table' and #fingerprint.probes == 0)) then
|
||||||
return false, "Invalid path found for fingerprint " .. i
|
return false, "Invalid path found for fingerprint " .. i
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure fingerprint.path is a table
|
-- Make sure fingerprint.path is a table
|
||||||
if(type(fingerprint.probes) == 'string') then
|
if(type(fingerprint.probes) == 'string') then
|
||||||
fingerprint.probes = {fingerprint.probes}
|
fingerprint.probes = {fingerprint.probes}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure the elements in the probes array are strings or arrays
|
-- Make sure the elements in the probes array are strings or arrays
|
||||||
for i, probe in pairs(fingerprint.probes) do
|
for i, probe in pairs(fingerprint.probes) do
|
||||||
-- Make sure we have a valid index
|
-- Make sure we have a valid index
|
||||||
if(type(i) ~= 'number') then
|
if(type(i) ~= 'number') then
|
||||||
return false, "The 'probes' table is an array, not a table; all indexes should be numeric"
|
return false, "The 'probes' table is an array, not a table; all indexes should be numeric"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Convert the probe to a table if it's a string
|
-- Convert the probe to a table if it's a string
|
||||||
if(type(probe) == 'string') then
|
if(type(probe) == 'string') then
|
||||||
fingerprint.probes[i] = {path=fingerprint.probes[i]}
|
fingerprint.probes[i] = {path=fingerprint.probes[i]}
|
||||||
probe = fingerprint.probes[i]
|
probe = fingerprint.probes[i]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure the probes table has a 'path'
|
-- Make sure the probes table has a 'path'
|
||||||
if(not(probe['path'])) then
|
if(not(probe['path'])) then
|
||||||
return false, "The 'probes' table requires each element to have a 'path'."
|
return false, "The 'probes' table requires each element to have a 'path'."
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If they didn't set a method, set it to 'GET'
|
-- If they didn't set a method, set it to 'GET'
|
||||||
if(not(probe['method'])) then
|
if(not(probe['method'])) then
|
||||||
probe['method'] = 'GET'
|
probe['method'] = 'GET'
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure the method's a string
|
-- Make sure the method's a string
|
||||||
if(type(probe['method']) ~= 'string') then
|
if(type(probe['method']) ~= 'string') then
|
||||||
return false, "The 'method' in the probes file has to be a string"
|
return false, "The 'method' in the probes file has to be a string"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Ensure that matches is an array
|
-- Ensure that matches is an array
|
||||||
if(type(fingerprint.matches) ~= 'table') then
|
if(type(fingerprint.matches) ~= 'table') then
|
||||||
return false, "'matches' field has to be a table"
|
return false, "'matches' field has to be a table"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Loop through the matches
|
-- Loop through the matches
|
||||||
for i, match in pairs(fingerprint.matches) do
|
for i, match in pairs(fingerprint.matches) do
|
||||||
-- Make sure we have a valid index
|
-- Make sure we have a valid index
|
||||||
if(type(i) ~= 'number') then
|
if(type(i) ~= 'number') then
|
||||||
return false, "The 'matches' table is an array, not a table; all indexes should be numeric"
|
return false, "The 'matches' table is an array, not a table; all indexes should be numeric"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check that every element in the table is an array
|
-- Check that every element in the table is an array
|
||||||
if(type(match) ~= 'table') then
|
if(type(match) ~= 'table') then
|
||||||
return false, "Every element of 'matches' field has to be a table"
|
return false, "Every element of 'matches' field has to be a table"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check the output field
|
-- Check the output field
|
||||||
if(match['output'] == nil or type(match['output']) ~= 'string') then
|
if(match['output'] == nil or type(match['output']) ~= 'string') then
|
||||||
return false, "The 'output' field in 'matches' has to be present and a string"
|
return false, "The 'output' field in 'matches' has to be present and a string"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check the 'match' and 'dontmatch' fields, if present
|
-- Check the 'match' and 'dontmatch' fields, if present
|
||||||
if((match['match'] and type(match['match']) ~= 'string') or (match['dontmatch'] and type(match['dontmatch']) ~= 'string')) then
|
if((match['match'] and type(match['match']) ~= 'string') or (match['dontmatch'] and type(match['dontmatch']) ~= 'string')) then
|
||||||
return false, "The 'match' and 'dontmatch' fields in 'matches' have to be strings, if they exist"
|
return false, "The 'match' and 'dontmatch' fields in 'matches' have to be strings, if they exist"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Change blank 'match' strings to '.*' so they match everything
|
-- Change blank 'match' strings to '.*' so they match everything
|
||||||
if(not(match['match']) or match['match'] == '') then
|
if(not(match['match']) or match['match'] == '') then
|
||||||
match['match'] = '(.*)'
|
match['match'] = '(.*)'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure the severity is an integer between 1 and 4. Default it to 1.
|
-- Make sure the severity is an integer between 1 and 4. Default it to 1.
|
||||||
if(fingerprint.severity and (type(fingerprint.severity) ~= 'number' or fingerprint.severity < 1 or fingerprint.severity > 4)) then
|
if(fingerprint.severity and (type(fingerprint.severity) ~= 'number' or fingerprint.severity < 1 or fingerprint.severity > 4)) then
|
||||||
return false, "The 'severity' field has to be an integer between 1 and 4"
|
return false, "The 'severity' field has to be an integer between 1 and 4"
|
||||||
else
|
else
|
||||||
fingerprint.severity = 1
|
fingerprint.severity = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure ignore_404 is a boolean. Default it to false.
|
-- Make sure ignore_404 is a boolean. Default it to false.
|
||||||
if(fingerprint.ignore_404 and type(fingerprint.ignore_404) ~= 'boolean') then
|
if(fingerprint.ignore_404 and type(fingerprint.ignore_404) ~= 'boolean') then
|
||||||
return false, "The 'ignore_404' field has to be a boolean"
|
return false, "The 'ignore_404' field has to be a boolean"
|
||||||
else
|
else
|
||||||
fingerprint.ignore_404 = false
|
fingerprint.ignore_404 = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Make sure we have some fingerprints fingerprints
|
-- Make sure we have some fingerprints fingerprints
|
||||||
if(#fingerprints == 0) then
|
if(#fingerprints == 0) then
|
||||||
return false, "No fingerprints were loaded"
|
return false, "No fingerprints were loaded"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If the user wanted to filter by category, do it
|
-- If the user wanted to filter by category, do it
|
||||||
if(category) then
|
if(category) then
|
||||||
local filtered_fingerprints = {}
|
local filtered_fingerprints = {}
|
||||||
for _, fingerprint in pairs(fingerprints) do
|
for _, fingerprint in pairs(fingerprints) do
|
||||||
if(fingerprint.category == category) then
|
if(fingerprint.category == category) then
|
||||||
table.insert(filtered_fingerprints, fingerprint)
|
table.insert(filtered_fingerprints, fingerprint)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
fingerprints = filtered_fingerprints
|
fingerprints = filtered_fingerprints
|
||||||
|
|
||||||
-- Make sure we still have fingerprints after the category filter
|
-- Make sure we still have fingerprints after the category filter
|
||||||
if(#fingerprints == 0) then
|
if(#fingerprints == 0) then
|
||||||
return false, "No fingerprints matched the given category (" .. category .. ")"
|
return false, "No fingerprints matched the given category (" .. category .. ")"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- -- If the user wants to try variations, add them
|
-- -- If the user wants to try variations, add them
|
||||||
-- if(try_variations) then
|
-- if(try_variations) then
|
||||||
-- -- Get a list of all variations for this directory
|
-- -- Get a list of all variations for this directory
|
||||||
-- local variations = get_variations(entry['checkdir'])
|
-- local variations = get_variations(entry['checkdir'])
|
||||||
--
|
--
|
||||||
-- -- Make a copy of the entry for each of them
|
-- -- Make a copy of the entry for each of them
|
||||||
-- for _, variation in ipairs(variations) do
|
-- for _, variation in ipairs(variations) do
|
||||||
-- new_entry = {}
|
-- new_entry = {}
|
||||||
-- for k, v in pairs(entry) do
|
-- for k, v in pairs(entry) do
|
||||||
-- new_entry[k] = v
|
-- new_entry[k] = v
|
||||||
-- end
|
-- end
|
||||||
-- new_entry['checkdesc'] = new_entry['checkdesc'] .. " (variation)"
|
-- new_entry['checkdesc'] = new_entry['checkdesc'] .. " (variation)"
|
||||||
-- new_entry['checkdir'] = variation
|
-- new_entry['checkdir'] = variation
|
||||||
-- table.insert(entries, new_entry)
|
-- table.insert(entries, new_entry)
|
||||||
-- count = count + 1
|
-- count = count + 1
|
||||||
-- end
|
-- end
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
-- Cache the fingerprints for other scripts, so we aren't reading the files every time
|
-- Cache the fingerprints for other scripts, so we aren't reading the files every time
|
||||||
-- nmap.registry.http_fingerprints = fingerprints
|
-- nmap.registry.http_fingerprints = fingerprints
|
||||||
|
|
||||||
return true, fingerprints
|
return true, fingerprints
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- Read the script-args, keeping the old ones for reverse compatibility
|
-- Read the script-args, keeping the old ones for reverse compatibility
|
||||||
local basepath = stdnse.get_script_args({'http-enum.basepath', 'path'}) or '/'
|
local basepath = stdnse.get_script_args({'http-enum.basepath', 'path'}) or '/'
|
||||||
local displayall = stdnse.get_script_args({'http-enum.displayall', 'displayall'}) or false
|
local displayall = stdnse.get_script_args({'http-enum.displayall', 'displayall'}) or false
|
||||||
local fingerprint_file = stdnse.get_script_args({'http-enum.fingerprintfile', 'fingerprints'}) or 'http-fingerprints.lua'
|
local fingerprint_file = stdnse.get_script_args({'http-enum.fingerprintfile', 'fingerprints'}) or 'http-fingerprints.lua'
|
||||||
local category = stdnse.get_script_args('http-enum.category')
|
local category = stdnse.get_script_args('http-enum.category')
|
||||||
-- local try_variations = stdnse.get_script_args({'http-enum.tryvariations', 'variations'}) or false
|
-- local try_variations = stdnse.get_script_args({'http-enum.tryvariations', 'variations'}) or false
|
||||||
-- local limit = tonumber(stdnse.get_script_args({'http-enum.limit', 'limit'})) or -1
|
-- local limit = tonumber(stdnse.get_script_args({'http-enum.limit', 'limit'})) or -1
|
||||||
|
|
||||||
-- Add URLs from external files
|
-- Add URLs from external files
|
||||||
local status, fingerprints = get_fingerprints(fingerprint_file, category)
|
local status, fingerprints = get_fingerprints(fingerprint_file, category)
|
||||||
if(not(status)) then
|
if(not(status)) then
|
||||||
return stdnse.format_output(false, fingerprints)
|
return stdnse.format_output(false, fingerprints)
|
||||||
end
|
end
|
||||||
stdnse.print_debug(1, "http-enum: Loaded %d fingerprints", #fingerprints)
|
stdnse.print_debug(1, "http-enum: Loaded %d fingerprints", #fingerprints)
|
||||||
|
|
||||||
-- Check what response we get for a 404
|
-- Check what response we get for a 404
|
||||||
local result, result_404, known_404 = http.identify_404(host, port)
|
local result, result_404, known_404 = http.identify_404(host, port)
|
||||||
if(result == false) then
|
if(result == false) then
|
||||||
return stdnse.format_output(false, result_404)
|
return stdnse.format_output(false, result_404)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Queue up the checks
|
-- Queue up the checks
|
||||||
local all = {}
|
local all = {}
|
||||||
|
|
||||||
-- Remove trailing slash, if it exists
|
-- Remove trailing slash, if it exists
|
||||||
if(#basepath > 1 and string.sub(basepath, #basepath, #basepath) == '/') then
|
if(#basepath > 1 and string.sub(basepath, #basepath, #basepath) == '/') then
|
||||||
basepath = string.sub(basepath, 1, #basepath - 1)
|
basepath = string.sub(basepath, 1, #basepath - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add a leading slash, if it doesn't exist
|
-- Add a leading slash, if it doesn't exist
|
||||||
if(#basepath <= 1) then
|
if(#basepath <= 1) then
|
||||||
basepath = ''
|
basepath = ''
|
||||||
else
|
else
|
||||||
if(string.sub(basepath, 1, 1) ~= '/') then
|
if(string.sub(basepath, 1, 1) ~= '/') then
|
||||||
basepath = '/' .. basepath
|
basepath = '/' .. basepath
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local results_nopipeline = {}
|
local results_nopipeline = {}
|
||||||
-- Loop through the fingerprints
|
-- Loop through the fingerprints
|
||||||
stdnse.print_debug(1, "http-enum: Searching for entries under path '%s' (change with 'http-enum.basepath' argument)", basepath)
|
stdnse.print_debug(1, "http-enum: Searching for entries under path '%s' (change with 'http-enum.basepath' argument)", basepath)
|
||||||
for i = 1, #fingerprints, 1 do
|
for i = 1, #fingerprints, 1 do
|
||||||
-- Add each path. The order very much matters here.
|
-- Add each path. The order very much matters here.
|
||||||
for j = 1, #fingerprints[i].probes, 1 do
|
for j = 1, #fingerprints[i].probes, 1 do
|
||||||
if fingerprints[i].probes[j].nopipeline then
|
if fingerprints[i].probes[j].nopipeline then
|
||||||
local res = http.generic_request(host, port, fingerprints[i].probes[j].method or 'GET', basepath .. fingerprints[i].probes[j].path, nil)
|
local res = http.generic_request(host, port, fingerprints[i].probes[j].method or 'GET', basepath .. fingerprints[i].probes[j].path, nil)
|
||||||
if res.status then
|
if res.status then
|
||||||
@@ -395,25 +395,25 @@ action = function(host, port)
|
|||||||
all = http.pipeline_add(basepath .. fingerprints[i].probes[j].path, nil, all, fingerprints[i].probes[j].method or 'GET')
|
all = http.pipeline_add(basepath .. fingerprints[i].probes[j].path, nil, all, fingerprints[i].probes[j].method or 'GET')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Perform all the requests.
|
-- Perform all the requests.
|
||||||
local results = http.pipeline_go(host, port, all, nil)
|
local results = http.pipeline_go(host, port, all, nil)
|
||||||
|
|
||||||
-- Check for http.pipeline error
|
-- Check for http.pipeline error
|
||||||
if(results == nil) then
|
if(results == nil) then
|
||||||
stdnse.print_debug(1, "http-enum: http.pipeline_go encountered an error")
|
stdnse.print_debug(1, "http-enum: http.pipeline_go encountered an error")
|
||||||
return stdnse.format_output(false, "http.pipeline_go encountered an error")
|
return stdnse.format_output(false, "http.pipeline_go encountered an error")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Loop through the fingerprints. Note that for each fingerprint, we may have multiple results
|
-- Loop through the fingerprints. Note that for each fingerprint, we may have multiple results
|
||||||
local j = 1
|
local j = 1
|
||||||
local j_nopipeline = 1
|
local j_nopipeline = 1
|
||||||
for i, fingerprint in ipairs(fingerprints) do
|
for i, fingerprint in ipairs(fingerprints) do
|
||||||
|
|
||||||
-- Loop through the paths for each fingerprint in the same order we did the requests. Each of these will
|
-- Loop through the paths for each fingerprint in the same order we did the requests. Each of these will
|
||||||
-- have one result, so increment the result value at each iteration
|
-- have one result, so increment the result value at each iteration
|
||||||
for _, probe in ipairs(fingerprint.probes) do
|
for _, probe in ipairs(fingerprint.probes) do
|
||||||
local result
|
local result
|
||||||
if probe.nopipeline then
|
if probe.nopipeline then
|
||||||
result = results_nopipeline[j_nopipeline]
|
result = results_nopipeline[j_nopipeline]
|
||||||
@@ -422,66 +422,66 @@ action = function(host, port)
|
|||||||
result = results[j]
|
result = results[j]
|
||||||
j = j + 1
|
j = j + 1
|
||||||
end
|
end
|
||||||
if(result) then
|
if(result) then
|
||||||
local path = basepath .. probe['path']
|
local path = basepath .. probe['path']
|
||||||
local good = true
|
local good = true
|
||||||
local output = nil
|
local output = nil
|
||||||
-- Unless this check said to ignore 404 messages, check if we got a valid page back using a known 404 message.
|
-- Unless this check said to ignore 404 messages, check if we got a valid page back using a known 404 message.
|
||||||
if(fingerprint.ignore_404 ~= true and not(http.page_exists(result, result_404, known_404, path, displayall))) then
|
if(fingerprint.ignore_404 ~= true and not(http.page_exists(result, result_404, known_404, path, displayall))) then
|
||||||
good = false
|
good = false
|
||||||
else
|
else
|
||||||
-- Loop through our matches table and see if anything matches our result
|
-- Loop through our matches table and see if anything matches our result
|
||||||
for _, match in ipairs(fingerprint.matches) do
|
for _, match in ipairs(fingerprint.matches) do
|
||||||
if(match.match) then
|
if(match.match) then
|
||||||
local result, matches = http.response_contains(result, match.match)
|
local result, matches = http.response_contains(result, match.match)
|
||||||
if(result) then
|
if(result) then
|
||||||
output = match.output
|
output = match.output
|
||||||
good = true
|
good = true
|
||||||
for k, value in ipairs(matches) do
|
for k, value in ipairs(matches) do
|
||||||
output = string.gsub(output, '\\' .. k, matches[k])
|
output = string.gsub(output, '\\' .. k, matches[k])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
output = match.output
|
output = match.output
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If nothing matched, turn off the match
|
-- If nothing matched, turn off the match
|
||||||
if(not(output)) then
|
if(not(output)) then
|
||||||
good = false
|
good = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If we match the 'dontmatch' line, we're not getting a match
|
-- If we match the 'dontmatch' line, we're not getting a match
|
||||||
if(match.dontmatch and match.dontmatch ~= '' and http.response_contains(result, match.dontmatch)) then
|
if(match.dontmatch and match.dontmatch ~= '' and http.response_contains(result, match.dontmatch)) then
|
||||||
output = nil
|
output = nil
|
||||||
good = false
|
good = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Break the loop if we found it
|
-- Break the loop if we found it
|
||||||
if(output) then
|
if(output) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if(good) then
|
if(good) then
|
||||||
-- Save the path in the registry
|
-- Save the path in the registry
|
||||||
http.save_path(stdnse.get_hostname(host), port.number, path, result.status)
|
http.save_path(stdnse.get_hostname(host), port.number, path, result.status)
|
||||||
|
|
||||||
-- Add the path to the output
|
-- Add the path to the output
|
||||||
output = string.format("%s: %s", path, output)
|
output = string.format("%s: %s", path, output)
|
||||||
|
|
||||||
-- Build the status code, if it isn't a 200
|
-- Build the status code, if it isn't a 200
|
||||||
if(result.status ~= 200) then
|
if(result.status ~= 200) then
|
||||||
output = output .. " (" .. http.get_status_string(result) .. ")"
|
output = output .. " (" .. http.get_status_string(result) .. ")"
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug(1, "Found a valid page! %s", output)
|
stdnse.print_debug(1, "Found a valid page! %s", output)
|
||||||
|
|
||||||
table.insert(response, output)
|
table.insert(response, output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -500,27 +500,27 @@ function action(host, port)
|
|||||||
return string.match(url.file, "%.jpg") or string.match(url.file, "%.jpeg")
|
return string.match(url.file, "%.jpg") or string.match(url.file, "%.jpeg")
|
||||||
end
|
end
|
||||||
|
|
||||||
local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, whitelist = { whitelist }} )
|
local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, whitelist = { whitelist }} )
|
||||||
|
|
||||||
if ( not(crawler) ) then
|
if ( not(crawler) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
while(true) do
|
while(true) do
|
||||||
-- Begin the crawler
|
-- Begin the crawler
|
||||||
local status, r = crawler:crawl()
|
local status, r = crawler:crawl()
|
||||||
|
|
||||||
-- Make sure there's no error
|
-- Make sure there's no error
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
if ( r.err ) then
|
if ( r.err ) then
|
||||||
return stdnse.format_output(false, r.reason)
|
return stdnse.format_output(false, r.reason)
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we got a response, and the response is a .jpg file
|
-- Check if we got a response, and the response is a .jpg file
|
||||||
if r.response and r.response.body and r.response.status==200 and (string.match(r.url.path, ".jpg") or string.match(r.url.path, ".jpeg")) then
|
if r.response and r.response.body and r.response.status==200 and (string.match(r.url.path, ".jpg") or string.match(r.url.path, ".jpeg")) then
|
||||||
local status, result
|
local status, result
|
||||||
stdnse.print_debug(1, "Attempting to read exif data from %s", r.url.raw)
|
stdnse.print_debug(1, "Attempting to read exif data from %s", r.url.raw)
|
||||||
status, result = parse_jpeg(r.response.body)
|
status, result = parse_jpeg(r.response.body)
|
||||||
@@ -533,7 +533,7 @@ function action(host, port)
|
|||||||
table.insert(results, result)
|
table.insert(results, result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, results)
|
return stdnse.format_output(true, results)
|
||||||
|
|||||||
@@ -74,76 +74,76 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
|
|||||||
--
|
--
|
||||||
-- Note, that more payloads will slow down your scan significaly.
|
-- Note, that more payloads will slow down your scan significaly.
|
||||||
payloads = { { filename = "1.php", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
payloads = { { filename = "1.php", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
||||||
{ filename = "1.php3", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
{ filename = "1.php3", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
||||||
-- { filename = "1.php4", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
-- { filename = "1.php4", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
||||||
-- { filename = "1.shtml", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
-- { filename = "1.shtml", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
|
||||||
-- { filename = "1.py", content = "print 123456 + 654321", check = "777777" },
|
-- { filename = "1.py", content = "print 123456 + 654321", check = "777777" },
|
||||||
-- { filename = "1.pl", content = "print 123456 + 654321", check = "777777" },
|
-- { filename = "1.pl", content = "print 123456 + 654321", check = "777777" },
|
||||||
-- { filename = "1.sh", content = "echo 123456 + 654321", check = "777777" },
|
-- { filename = "1.sh", content = "echo 123456 + 654321", check = "777777" },
|
||||||
-- { filename = "1.jsp", content = "<%= 123456 + 654321 %>", check = "777777" },
|
-- { filename = "1.jsp", content = "<%= 123456 + 654321 %>", check = "777777" },
|
||||||
-- { filename = "1.asp", content = "<%= 123456 + 654321 %>", check = "777777" },
|
-- { filename = "1.asp", content = "<%= 123456 + 654321 %>", check = "777777" },
|
||||||
}
|
}
|
||||||
|
|
||||||
listofrequests = {}
|
listofrequests = {}
|
||||||
|
|
||||||
-- Escape for jsp and asp payloads.
|
-- Escape for jsp and asp payloads.
|
||||||
local escape = function(s)
|
local escape = function(s)
|
||||||
return (s:gsub('%%', '%%%%'))
|
return (s:gsub('%%', '%%%%'))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Represents an upload-request.
|
-- Represents an upload-request.
|
||||||
local function UploadRequest(host, port, submission, partofrequest, name, filename, mime, payload, check)
|
local function UploadRequest(host, port, submission, partofrequest, name, filename, mime, payload, check)
|
||||||
local request = {
|
local request = {
|
||||||
host = host;
|
host = host;
|
||||||
port = port;
|
port = port;
|
||||||
submission = submission;
|
submission = submission;
|
||||||
mime = mime;
|
mime = mime;
|
||||||
name = name;
|
name = name;
|
||||||
filename = filename;
|
filename = filename;
|
||||||
partofrequest = partofrequest;
|
partofrequest = partofrequest;
|
||||||
payload = payload;
|
payload = payload;
|
||||||
check = check;
|
check = check;
|
||||||
uploadedpaths = {};
|
uploadedpaths = {};
|
||||||
success = 0;
|
success = 0;
|
||||||
|
|
||||||
make = function(self)
|
make = function(self)
|
||||||
local options = { header={} }
|
local options = { header={} }
|
||||||
options['header']['Content-Type'] = "multipart/form-data; boundary=AaB03x"
|
options['header']['Content-Type'] = "multipart/form-data; boundary=AaB03x"
|
||||||
options['content'] = self.partofrequest .. '--AaB03x\nContent-Disposition: form-data; name="' .. self.name .. '"; filename="' .. self.filename .. '"\nContent-Type: ' .. self.mime .. '\n\n' .. self.payload .. '\n--AaB03x--'
|
options['content'] = self.partofrequest .. '--AaB03x\nContent-Disposition: form-data; name="' .. self.name .. '"; filename="' .. self.filename .. '"\nContent-Type: ' .. self.mime .. '\n\n' .. self.payload .. '\n--AaB03x--'
|
||||||
|
|
||||||
stdnse.print_debug(2, "Making a request: Header: " .. options['header']['Content-Type'] .. "\nContent: " .. escape(options['content']))
|
stdnse.print_debug(2, "Making a request: Header: " .. options['header']['Content-Type'] .. "\nContent: " .. escape(options['content']))
|
||||||
|
|
||||||
local response = http.post(self.host, self.port, self.submission, options, { no_cache = true })
|
local response = http.post(self.host, self.port, self.submission, options, { no_cache = true })
|
||||||
|
|
||||||
return response.body
|
return response.body
|
||||||
end;
|
end;
|
||||||
|
|
||||||
checkPayload = function(self, uploadspaths)
|
checkPayload = function(self, uploadspaths)
|
||||||
for _, uploadpath in ipairs(uploadspaths) do
|
for _, uploadpath in ipairs(uploadspaths) do
|
||||||
local response = http.get(host, port, uploadpath .. '/' .. filename, { no_cache = true } )
|
local response = http.get(host, port, uploadpath .. '/' .. filename, { no_cache = true } )
|
||||||
|
|
||||||
if response.status ~= 404 then
|
if response.status ~= 404 then
|
||||||
if (response.body:match(self.check)) then
|
if (response.body:match(self.check)) then
|
||||||
self.success = 1
|
self.success = 1
|
||||||
table.insert(self.uploadedpaths, uploadpath)
|
table.insert(self.uploadedpaths, uploadpath)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
}
|
}
|
||||||
table.insert(listofrequests, request)
|
table.insert(listofrequests, request)
|
||||||
return request
|
return request
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create customized requests for all of our payloads.
|
-- Create customized requests for all of our payloads.
|
||||||
local buildRequests = function(host, port, submission, name, mime, partofrequest, uploadspaths, image)
|
local buildRequests = function(host, port, submission, name, mime, partofrequest, uploadspaths, image)
|
||||||
|
|
||||||
for i, p in ipairs(payloads) do
|
for i, p in ipairs(payloads) do
|
||||||
if image then
|
if image then
|
||||||
p['content'] = string.gsub(image, '!!comment!!', escape(p['content']), 1, true)
|
p['content'] = string.gsub(image, '!!comment!!', escape(p['content']), 1, true)
|
||||||
end
|
|
||||||
UploadRequest(host, port, submission, partofrequest, name, p['filename'], mime, p['content'], p['check'])
|
|
||||||
end
|
end
|
||||||
|
UploadRequest(host, port, submission, partofrequest, name, p['filename'], mime, p['content'], p['check'])
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -151,179 +151,179 @@ end
|
|||||||
-- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array.
|
-- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array.
|
||||||
local makeAndCheckRequests = function(uploadspaths)
|
local makeAndCheckRequests = function(uploadspaths)
|
||||||
|
|
||||||
local exit = 0
|
local exit = 0
|
||||||
local output = {"Succesfully uploaded and executed payloads: "}
|
local output = {"Succesfully uploaded and executed payloads: "}
|
||||||
|
|
||||||
for i=1, #listofrequests, 1 do
|
for i=1, #listofrequests, 1 do
|
||||||
listofrequests[i]:make()
|
listofrequests[i]:make()
|
||||||
listofrequests[i]:checkPayload(uploadspaths)
|
listofrequests[i]:checkPayload(uploadspaths)
|
||||||
if (listofrequests[i].success == 1) then
|
if (listofrequests[i].success == 1) then
|
||||||
exit = 1
|
exit = 1
|
||||||
table.insert(output, " Filename: " .. listofrequests[i].filename .. ", MIME: " .. listofrequests[i].mime .. ", Uploaded on: ")
|
table.insert(output, " Filename: " .. listofrequests[i].filename .. ", MIME: " .. listofrequests[i].mime .. ", Uploaded on: ")
|
||||||
for _, uploadedpath in ipairs(listofrequests[i].uploadedpaths) do
|
for _, uploadedpath in ipairs(listofrequests[i].uploadedpaths) do
|
||||||
table.insert(output, uploadedpath .. "/" .. listofrequests[i].filename)
|
table.insert(output, uploadedpath .. "/" .. listofrequests[i].filename)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if exit == 1 then
|
if exit == 1 then
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
listofrequests = {}
|
listofrequests = {}
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local prepareRequest = function(fields, fieldvalues)
|
local prepareRequest = function(fields, fieldvalues)
|
||||||
|
|
||||||
local filefield = 0
|
local filefield = 0
|
||||||
local req = ""
|
local req = ""
|
||||||
local value
|
local value
|
||||||
|
|
||||||
for _, field in ipairs(fields) do
|
for _, field in ipairs(fields) do
|
||||||
if field["type"] == "file" then
|
if field["type"] == "file" then
|
||||||
filefield = field
|
filefield = field
|
||||||
elseif field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
|
elseif field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
|
||||||
if fieldvalues[field["name"]] ~= nil then
|
if fieldvalues[field["name"]] ~= nil then
|
||||||
value = fieldvalues[field["name"]]
|
value = fieldvalues[field["name"]]
|
||||||
else
|
else
|
||||||
value = "SampleData0"
|
value = "SampleData0"
|
||||||
end
|
end
|
||||||
req = req .. '--AaB03x\nContent-Disposition: form-data; name="' .. field["name"] .. '";\n\n' .. value .. '\n'
|
req = req .. '--AaB03x\nContent-Disposition: form-data; name="' .. field["name"] .. '";\n\n' .. value .. '\n'
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return req, filefield
|
return req, filefield
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local formpaths = stdnse.get_script_args("http-fileupload-exploiter.formpaths")
|
local formpaths = stdnse.get_script_args("http-fileupload-exploiter.formpaths")
|
||||||
local uploadspaths = stdnse.get_script_args("http-fileupload-exploiter.uploadspaths") or {'/uploads', '/upload', '/file', '/files', '/downloads'}
|
local uploadspaths = stdnse.get_script_args("http-fileupload-exploiter.uploadspaths") or {'/uploads', '/upload', '/file', '/files', '/downloads'}
|
||||||
local fieldvalues = stdnse.get_script_args("http-fileupload-exploiter.fieldvalues") or {}
|
local fieldvalues = stdnse.get_script_args("http-fileupload-exploiter.fieldvalues") or {}
|
||||||
|
|
||||||
local returntable = {}
|
local returntable = {}
|
||||||
|
|
||||||
local result
|
local result
|
||||||
local foundform = 0
|
local foundform = 0
|
||||||
local foundfield = 0
|
local foundfield = 0
|
||||||
local fail = 0
|
local fail = 0
|
||||||
|
|
||||||
|
|
||||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } )
|
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } )
|
||||||
|
|
||||||
if (not(crawler)) then
|
if (not(crawler)) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
crawler:set_timeout(10000)
|
crawler:set_timeout(10000)
|
||||||
|
|
||||||
local index, k, target, response
|
local index, k, target, response
|
||||||
|
|
||||||
while (true) do
|
while (true) do
|
||||||
|
|
||||||
if formpaths then
|
if formpaths then
|
||||||
k, target = next(formpaths, index)
|
k, target = next(formpaths, index)
|
||||||
if (k == nil) then
|
if (k == nil) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
response = http.get(host, port, target)
|
response = http.get(host, port, target)
|
||||||
|
else
|
||||||
|
|
||||||
|
local status, r = crawler:crawl()
|
||||||
|
-- if the crawler fails it can be due to a number of different reasons
|
||||||
|
-- most of them are "legitimate" and should not be reason to abort
|
||||||
|
if ( not(status) ) then
|
||||||
|
if ( r.err ) then
|
||||||
|
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||||
else
|
else
|
||||||
|
break
|
||||||
local status, r = crawler:crawl()
|
|
||||||
-- if the crawler fails it can be due to a number of different reasons
|
|
||||||
-- most of them are "legitimate" and should not be reason to abort
|
|
||||||
if ( not(status) ) then
|
|
||||||
if ( r.err ) then
|
|
||||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
|
||||||
else
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
target = tostring(r.url)
|
|
||||||
response = r.response
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
target = tostring(r.url)
|
||||||
|
response = r.response
|
||||||
|
|
||||||
if response.body then
|
|
||||||
|
|
||||||
local forms = http.grab_forms(response.body)
|
|
||||||
|
|
||||||
for i, form in ipairs(forms) do
|
|
||||||
|
|
||||||
form = http.parse_form(form)
|
|
||||||
|
|
||||||
if form then
|
|
||||||
|
|
||||||
local action_absolute = string.find(form["action"], "https*://")
|
|
||||||
|
|
||||||
-- Determine the path where the form needs to be submitted.
|
|
||||||
local submission
|
|
||||||
if action_absolute then
|
|
||||||
submission = form["action"]
|
|
||||||
else
|
|
||||||
local path_cropped = string.match(target, "(.*/).*")
|
|
||||||
path_cropped = path_cropped and path_cropped or ""
|
|
||||||
submission = path_cropped..form["action"]
|
|
||||||
end
|
|
||||||
|
|
||||||
foundform = 1
|
|
||||||
|
|
||||||
local partofrequest, filefield = prepareRequest(form["fields"], fieldvalues)
|
|
||||||
|
|
||||||
if filefield ~= 0 then
|
|
||||||
|
|
||||||
foundfield = 1
|
|
||||||
|
|
||||||
-- Method (1).
|
|
||||||
buildRequests(host, port, submission, filefield["name"], "text/plain", partofrequest, uploadspaths)
|
|
||||||
|
|
||||||
result = makeAndCheckRequests(uploadspaths)
|
|
||||||
if result then
|
|
||||||
table.insert(returntable, result)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Method (2).
|
|
||||||
buildRequests(host, port, submission, filefield["name"], "image/gif", partofrequest, uploadspaths)
|
|
||||||
buildRequests(host, port, submission, filefield["name"], "image/png", partofrequest, uploadspaths)
|
|
||||||
buildRequests(host, port, submission, filefield["name"], "image/jpeg", partofrequest, uploadspaths)
|
|
||||||
|
|
||||||
result = makeAndCheckRequests(uploadspaths)
|
|
||||||
if result then
|
|
||||||
table.insert(returntable, result)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Method (3).
|
|
||||||
local inp = assert(io.open("nselib/data/pixel.gif", "rb"))
|
|
||||||
local image = inp:read("*all")
|
|
||||||
|
|
||||||
buildRequests(host, port, submission, filefield["name"], "image/gif", partofrequest, uploadspaths, image)
|
|
||||||
|
|
||||||
result = makeAndCheckRequests(uploadspaths)
|
|
||||||
if result then
|
|
||||||
table.insert(returntable, result)
|
|
||||||
else
|
|
||||||
fail = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
table.insert(returntable, {"Couldn't find a file-type field."})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if fail == 1 then
|
|
||||||
table.insert(returntable, {"Failed to upload and execute a payload."})
|
|
||||||
end
|
|
||||||
if (index) then
|
|
||||||
index = index + 1
|
|
||||||
else
|
|
||||||
index = 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return returntable
|
|
||||||
|
|
||||||
|
if response.body then
|
||||||
|
|
||||||
|
local forms = http.grab_forms(response.body)
|
||||||
|
|
||||||
|
for i, form in ipairs(forms) do
|
||||||
|
|
||||||
|
form = http.parse_form(form)
|
||||||
|
|
||||||
|
if form then
|
||||||
|
|
||||||
|
local action_absolute = string.find(form["action"], "https*://")
|
||||||
|
|
||||||
|
-- Determine the path where the form needs to be submitted.
|
||||||
|
local submission
|
||||||
|
if action_absolute then
|
||||||
|
submission = form["action"]
|
||||||
|
else
|
||||||
|
local path_cropped = string.match(target, "(.*/).*")
|
||||||
|
path_cropped = path_cropped and path_cropped or ""
|
||||||
|
submission = path_cropped..form["action"]
|
||||||
|
end
|
||||||
|
|
||||||
|
foundform = 1
|
||||||
|
|
||||||
|
local partofrequest, filefield = prepareRequest(form["fields"], fieldvalues)
|
||||||
|
|
||||||
|
if filefield ~= 0 then
|
||||||
|
|
||||||
|
foundfield = 1
|
||||||
|
|
||||||
|
-- Method (1).
|
||||||
|
buildRequests(host, port, submission, filefield["name"], "text/plain", partofrequest, uploadspaths)
|
||||||
|
|
||||||
|
result = makeAndCheckRequests(uploadspaths)
|
||||||
|
if result then
|
||||||
|
table.insert(returntable, result)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Method (2).
|
||||||
|
buildRequests(host, port, submission, filefield["name"], "image/gif", partofrequest, uploadspaths)
|
||||||
|
buildRequests(host, port, submission, filefield["name"], "image/png", partofrequest, uploadspaths)
|
||||||
|
buildRequests(host, port, submission, filefield["name"], "image/jpeg", partofrequest, uploadspaths)
|
||||||
|
|
||||||
|
result = makeAndCheckRequests(uploadspaths)
|
||||||
|
if result then
|
||||||
|
table.insert(returntable, result)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Method (3).
|
||||||
|
local inp = assert(io.open("nselib/data/pixel.gif", "rb"))
|
||||||
|
local image = inp:read("*all")
|
||||||
|
|
||||||
|
buildRequests(host, port, submission, filefield["name"], "image/gif", partofrequest, uploadspaths, image)
|
||||||
|
|
||||||
|
result = makeAndCheckRequests(uploadspaths)
|
||||||
|
if result then
|
||||||
|
table.insert(returntable, result)
|
||||||
|
else
|
||||||
|
fail = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(returntable, {"Couldn't find a file-type field."})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if fail == 1 then
|
||||||
|
table.insert(returntable, {"Failed to upload and execute a payload."})
|
||||||
|
end
|
||||||
|
if (index) then
|
||||||
|
index = index + 1
|
||||||
|
else
|
||||||
|
index = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return returntable
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -85,240 +85,240 @@ local Bestopt
|
|||||||
|
|
||||||
-- get time (in miliseconds) when the script should finish
|
-- get time (in miliseconds) when the script should finish
|
||||||
local function get_end_time()
|
local function get_end_time()
|
||||||
if TimeLimit == nil then
|
if TimeLimit == nil then
|
||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
return 1000 * TimeLimit + nmap.clock_ms()
|
return 1000 * TimeLimit + nmap.clock_ms()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function set_parameters()
|
local function set_parameters()
|
||||||
SendInterval = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.send_interval') or '100s')
|
SendInterval = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.send_interval') or '100s')
|
||||||
if stdnse.get_script_args('http-slowloris.runforever') then
|
if stdnse.get_script_args('http-slowloris.runforever') then
|
||||||
TimeLimit = nil
|
TimeLimit = nil
|
||||||
else
|
else
|
||||||
TimeLimit = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.timelimit') or '30m')
|
TimeLimit = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.timelimit') or '30m')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function do_half_http(host, port, obj)
|
local function do_half_http(host, port, obj)
|
||||||
local condvar = nmap.condvar(obj)
|
local condvar = nmap.condvar(obj)
|
||||||
|
|
||||||
if StopAll then
|
if StopAll then
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create socket
|
-- Create socket
|
||||||
local slowloris = nmap.new_socket()
|
local slowloris = nmap.new_socket()
|
||||||
slowloris:set_timeout(200 * 1000) -- Set a long timeout so our socked doesn't timeout while it's waiting
|
slowloris:set_timeout(200 * 1000) -- Set a long timeout so our socked doesn't timeout while it's waiting
|
||||||
|
|
||||||
ThreadCount = ThreadCount + 1
|
ThreadCount = ThreadCount + 1
|
||||||
local catch = function()
|
local catch = function()
|
||||||
-- This connection is now dead
|
-- This connection is now dead
|
||||||
ThreadCount = ThreadCount - 1
|
ThreadCount = ThreadCount - 1
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [HALF HTTP]: lost connection")
|
stdnse.print_debug(SCRIPT_NAME .. " [HALF HTTP]: lost connection")
|
||||||
slowloris:close()
|
slowloris:close()
|
||||||
slowloris = nil
|
slowloris = nil
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
try(slowloris:connect(host.ip, port, Bestopt))
|
try(slowloris:connect(host.ip, port, Bestopt))
|
||||||
|
|
||||||
-- Build a half-http header.
|
-- Build a half-http header.
|
||||||
local half_http = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
|
local half_http = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
|
||||||
"Host: " .. host.ip .. "\r\n" ..
|
"Host: " .. host.ip .. "\r\n" ..
|
||||||
"User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; " ..
|
"User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; " ..
|
||||||
".NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n" ..
|
".NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n" ..
|
||||||
"Content-Length: 42\r\n"
|
"Content-Length: 42\r\n"
|
||||||
|
|
||||||
try(slowloris:send(half_http))
|
try(slowloris:send(half_http))
|
||||||
ServerNotice = " (attack against " .. host.ip .. "): HTTP stream started."
|
ServerNotice = " (attack against " .. host.ip .. "): HTTP stream started."
|
||||||
|
|
||||||
-- During the attack some connections will die and other will respawn.
|
-- During the attack some connections will die and other will respawn.
|
||||||
-- Here we keep in mind the maximum concurrent connections reached.
|
-- Here we keep in mind the maximum concurrent connections reached.
|
||||||
|
|
||||||
if Sockets <= ThreadCount then Sockets = ThreadCount end
|
if Sockets <= ThreadCount then Sockets = ThreadCount end
|
||||||
|
|
||||||
-- Maintain a pending HTTP request by adding a new line at a regular 'feed' interval
|
-- Maintain a pending HTTP request by adding a new line at a regular 'feed' interval
|
||||||
while true do
|
while true do
|
||||||
if StopAll then
|
if StopAll then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
stdnse.sleep(SendInterval)
|
stdnse.sleep(SendInterval)
|
||||||
try(slowloris:send("X-a: b\r\n"))
|
try(slowloris:send("X-a: b\r\n"))
|
||||||
ServerNotice = " (attack against " .. host.ip .. "): Feeding HTTP stream..."
|
ServerNotice = " (attack against " .. host.ip .. "): Feeding HTTP stream..."
|
||||||
Queries = Queries + 1
|
Queries = Queries + 1
|
||||||
ServerNotice = ServerNotice .. "\n(attack against " .. host.ip .. "): " .. Queries .. " queries sent using " .. ThreadCount .. " connections."
|
ServerNotice = ServerNotice .. "\n(attack against " .. host.ip .. "): " .. Queries .. " queries sent using " .. ThreadCount .. " connections."
|
||||||
end
|
end
|
||||||
slowloris:close()
|
slowloris:close()
|
||||||
ThreadCount = ThreadCount - 1
|
ThreadCount = ThreadCount - 1
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Monitor the web server
|
-- Monitor the web server
|
||||||
local function do_monitor(host, port)
|
local function do_monitor(host, port)
|
||||||
local general_faults = 0
|
local general_faults = 0
|
||||||
local request_faults = 0 -- keeps track of how many times we didn't get a reply from the server
|
local request_faults = 0 -- keeps track of how many times we didn't get a reply from the server
|
||||||
|
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: Monitoring " .. host.ip .. " started")
|
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: Monitoring " .. host.ip .. " started")
|
||||||
|
|
||||||
local request = "GET / HTTP/1.1\r\n" ..
|
local request = "GET / HTTP/1.1\r\n" ..
|
||||||
"Host: " .. host.ip ..
|
"Host: " .. host.ip ..
|
||||||
"\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; " ..
|
"\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; " ..
|
||||||
".NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n\r\n"
|
".NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n\r\n"
|
||||||
local opts = {}
|
local opts = {}
|
||||||
local _
|
local _
|
||||||
|
|
||||||
_, _, Bestopt = comm.tryssl(host, port, "GET / \r\n\r\n", opts) -- first determine if we need ssl
|
_, _, Bestopt = comm.tryssl(host, port, "GET / \r\n\r\n", opts) -- first determine if we need ssl
|
||||||
|
|
||||||
while not StopAll do
|
while not StopAll do
|
||||||
local monitor = nmap.new_socket()
|
local monitor = nmap.new_socket()
|
||||||
local status = monitor:connect(host.ip, port, Bestopt)
|
local status = monitor:connect(host.ip, port, Bestopt)
|
||||||
if not status then
|
if not status then
|
||||||
general_faults = general_faults + 1
|
general_faults = general_faults + 1
|
||||||
if general_faults > 3 then
|
if general_faults > 3 then
|
||||||
Reason = "not-slowloris"
|
Reason = "not-slowloris"
|
||||||
DOSed = true
|
DOSed = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
status = monitor:send(request)
|
status = monitor:send(request)
|
||||||
if not status then
|
if not status then
|
||||||
general_faults = general_faults + 1
|
general_faults = general_faults + 1
|
||||||
if general_faults > 3 then
|
if general_faults > 3 then
|
||||||
Reason = "not-slowloris"
|
Reason = "not-slowloris"
|
||||||
DOSed = true
|
DOSed = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
status, _ = monitor:receive_lines(1)
|
status, _ = monitor:receive_lines(1)
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: Didn't get a reply from " .. host.ip .. "." )
|
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: Didn't get a reply from " .. host.ip .. "." )
|
||||||
monitor:close()
|
monitor:close()
|
||||||
request_faults = request_faults +1
|
request_faults = request_faults +1
|
||||||
if request_faults > 3 then
|
if request_faults > 3 then
|
||||||
if TimeLimit then
|
if TimeLimit then
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: server " .. host.ip .. " is now unavailable. The attack worked.")
|
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: server " .. host.ip .. " is now unavailable. The attack worked.")
|
||||||
DOSed = true
|
DOSed = true
|
||||||
end
|
end
|
||||||
monitor:close()
|
monitor:close()
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
request_faults = 0
|
request_faults = 0
|
||||||
general_faults = 0
|
general_faults = 0
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: ".. host.ip .." still up, answer received.")
|
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: ".. host.ip .." still up, answer received.")
|
||||||
stdnse.sleep(10)
|
stdnse.sleep(10)
|
||||||
monitor:close()
|
monitor:close()
|
||||||
end
|
end
|
||||||
if StopAll then
|
if StopAll then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local Mutex = nmap.mutex("http-slowloris")
|
local Mutex = nmap.mutex("http-slowloris")
|
||||||
|
|
||||||
local function worker_scheduler(host, port)
|
local function worker_scheduler(host, port)
|
||||||
local Threads = {}
|
local Threads = {}
|
||||||
local obj = {}
|
local obj = {}
|
||||||
local condvar = nmap.condvar(obj)
|
local condvar = nmap.condvar(obj)
|
||||||
local i
|
local i
|
||||||
|
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
-- The real amount of sockets is triggered by the
|
-- The real amount of sockets is triggered by the
|
||||||
-- '--max-parallelism' option. The remaining threads will replace
|
-- '--max-parallelism' option. The remaining threads will replace
|
||||||
-- dead sockets during the attack
|
-- dead sockets during the attack
|
||||||
local co = stdnse.new_thread(do_half_http, host, port, obj)
|
local co = stdnse.new_thread(do_half_http, host, port, obj)
|
||||||
Threads[co] = true
|
Threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
while not DOSed and not StopAll do
|
while not DOSed and not StopAll do
|
||||||
-- keep creating new threads, in case we want to run the attack indefinitely
|
-- keep creating new threads, in case we want to run the attack indefinitely
|
||||||
repeat
|
repeat
|
||||||
if StopAll then
|
if StopAll then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for thread in pairs(Threads) do
|
for thread in pairs(Threads) do
|
||||||
if coroutine.status(thread) == "dead" then
|
if coroutine.status(thread) == "dead" then
|
||||||
Threads[thread] = nil
|
Threads[thread] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [SCHEDULER]: starting new thread")
|
stdnse.print_debug(SCRIPT_NAME .. " [SCHEDULER]: starting new thread")
|
||||||
local co = stdnse.new_thread(do_half_http, host, port, obj)
|
local co = stdnse.new_thread(do_half_http, host, port, obj)
|
||||||
Threads[co] = true
|
Threads[co] = true
|
||||||
if ( next(Threads) ) then
|
if ( next(Threads) ) then
|
||||||
condvar("wait")
|
condvar("wait")
|
||||||
end
|
end
|
||||||
until next(Threads) == nil;
|
until next(Threads) == nil;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
Mutex("lock") -- we want only one slowloris instance running at a single
|
Mutex("lock") -- we want only one slowloris instance running at a single
|
||||||
-- time even if multiple hosts are specified
|
-- time even if multiple hosts are specified
|
||||||
-- in order to have as many sockets as we can available to
|
-- in order to have as many sockets as we can available to
|
||||||
-- this script
|
-- this script
|
||||||
|
|
||||||
set_parameters()
|
set_parameters()
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
local start, stop, dos_time
|
local start, stop, dos_time
|
||||||
|
|
||||||
start = os.date("!*t")
|
start = os.date("!*t")
|
||||||
-- The first thread is for monitoring and is launched before the attack threads
|
-- The first thread is for monitoring and is launched before the attack threads
|
||||||
stdnse.new_thread(do_monitor, host, port)
|
stdnse.new_thread(do_monitor, host, port)
|
||||||
stdnse.sleep(2) -- let the monitor make the first request
|
stdnse.sleep(2) -- let the monitor make the first request
|
||||||
|
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: starting scheduler")
|
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: starting scheduler")
|
||||||
stdnse.new_thread(worker_scheduler, host, port)
|
stdnse.new_thread(worker_scheduler, host, port)
|
||||||
local end_time = get_end_time()
|
local end_time = get_end_time()
|
||||||
local last_message
|
local last_message
|
||||||
if TimeLimit == nil then
|
if TimeLimit == nil then
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: running forever!")
|
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: running forever!")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- return a live notice from time to time
|
-- return a live notice from time to time
|
||||||
while (nmap.clock_ms() < end_time or TimeLimit == nil) and not StopAll do
|
while (nmap.clock_ms() < end_time or TimeLimit == nil) and not StopAll do
|
||||||
if ServerNotice ~= last_message then
|
if ServerNotice ~= last_message then
|
||||||
-- don't flood the output by repeating the same info
|
-- don't flood the output by repeating the same info
|
||||||
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: " .. ServerNotice)
|
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: " .. ServerNotice)
|
||||||
last_message = ServerNotice
|
last_message = ServerNotice
|
||||||
end
|
end
|
||||||
if DOSed and TimeLimit ~= nil then
|
if DOSed and TimeLimit ~= nil then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
stdnse.sleep(10)
|
stdnse.sleep(10)
|
||||||
end
|
end
|
||||||
|
|
||||||
stop = os.date("!*t")
|
stop = os.date("!*t")
|
||||||
dos_time = stdnse.format_difftime(stop, start)
|
dos_time = stdnse.format_difftime(stop, start)
|
||||||
StopAll = true
|
StopAll = true
|
||||||
if DOSed then
|
if DOSed then
|
||||||
if Reason == "slowloris" then
|
if Reason == "slowloris" then
|
||||||
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped, building output")
|
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped, building output")
|
||||||
output = "Vulnerable:\n" ..
|
output = "Vulnerable:\n" ..
|
||||||
"the DoS attack took "..
|
"the DoS attack took "..
|
||||||
dos_time .. "\n" ..
|
dos_time .. "\n" ..
|
||||||
"with ".. Sockets .. " concurrent connections\n" ..
|
"with ".. Sockets .. " concurrent connections\n" ..
|
||||||
"and " .. Queries .." sent queries"
|
"and " .. Queries .." sent queries"
|
||||||
else
|
else
|
||||||
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped. Monitor couldn't communicate with the server.")
|
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped. Monitor couldn't communicate with the server.")
|
||||||
output = "Probably vulnerable:\n" ..
|
output = "Probably vulnerable:\n" ..
|
||||||
"the DoS attack took " .. dos_time .. "\n" ..
|
"the DoS attack took " .. dos_time .. "\n" ..
|
||||||
"with " .. Sockets .. " concurrent connections\n" ..
|
"with " .. Sockets .. " concurrent connections\n" ..
|
||||||
"and " .. Queries .. " sent queries\n" ..
|
"and " .. Queries .. " sent queries\n" ..
|
||||||
"Monitoring thread couldn't communicate with the server. " ..
|
"Monitoring thread couldn't communicate with the server. " ..
|
||||||
"This is probably due to max clients exhaustion or something similar but not due to slowloris attack."
|
"This is probably due to max clients exhaustion or something similar but not due to slowloris attack."
|
||||||
end
|
end
|
||||||
Mutex("done") -- release the mutex
|
Mutex("done") -- release the mutex
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
Mutex("done") -- release the mutex
|
Mutex("done") -- release the mutex
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,48 +33,48 @@ categories = {"discovery","external","safe"}
|
|||||||
|
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
local is_private, err = ipOps.isPrivate( host.ip )
|
local is_private, err = ipOps.isPrivate( host.ip )
|
||||||
if is_private == nil then
|
if is_private == nil then
|
||||||
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
|
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return not is_private
|
return not is_private
|
||||||
end
|
end
|
||||||
|
|
||||||
local MaxmindDef = {
|
local MaxmindDef = {
|
||||||
-- Database structure constants
|
-- Database structure constants
|
||||||
COUNTRY_BEGIN = 16776960,
|
COUNTRY_BEGIN = 16776960,
|
||||||
STATE_BEGIN_REV0 = 16700000,
|
STATE_BEGIN_REV0 = 16700000,
|
||||||
STATE_BEGIN_REV1 = 16000000,
|
STATE_BEGIN_REV1 = 16000000,
|
||||||
|
|
||||||
STRUCTURE_INFO_MAX_SIZE = 20,
|
STRUCTURE_INFO_MAX_SIZE = 20,
|
||||||
DATABASE_INFO_MAX_SIZE = 100,
|
DATABASE_INFO_MAX_SIZE = 100,
|
||||||
|
|
||||||
-- Database editions,
|
-- Database editions,
|
||||||
COUNTRY_EDITION = 1,
|
COUNTRY_EDITION = 1,
|
||||||
REGION_EDITION_REV0 = 7,
|
REGION_EDITION_REV0 = 7,
|
||||||
REGION_EDITION_REV1 = 3,
|
REGION_EDITION_REV1 = 3,
|
||||||
CITY_EDITION_REV0 = 6,
|
CITY_EDITION_REV0 = 6,
|
||||||
CITY_EDITION_REV1 = 2,
|
CITY_EDITION_REV1 = 2,
|
||||||
ORG_EDITION = 5,
|
ORG_EDITION = 5,
|
||||||
ISP_EDITION = 4,
|
ISP_EDITION = 4,
|
||||||
PROXY_EDITION = 8,
|
PROXY_EDITION = 8,
|
||||||
ASNUM_EDITION = 9,
|
ASNUM_EDITION = 9,
|
||||||
NETSPEED_EDITION = 11,
|
NETSPEED_EDITION = 11,
|
||||||
COUNTRY_EDITION_V6 = 12,
|
COUNTRY_EDITION_V6 = 12,
|
||||||
|
|
||||||
SEGMENT_RECORD_LENGTH = 3,
|
SEGMENT_RECORD_LENGTH = 3,
|
||||||
STANDARD_RECORD_LENGTH = 3,
|
STANDARD_RECORD_LENGTH = 3,
|
||||||
ORG_RECORD_LENGTH = 4,
|
ORG_RECORD_LENGTH = 4,
|
||||||
MAX_RECORD_LENGTH = 4,
|
MAX_RECORD_LENGTH = 4,
|
||||||
MAX_ORG_RECORD_LENGTH = 300,
|
MAX_ORG_RECORD_LENGTH = 300,
|
||||||
FULL_RECORD_LENGTH = 50,
|
FULL_RECORD_LENGTH = 50,
|
||||||
|
|
||||||
US_OFFSET = 1,
|
US_OFFSET = 1,
|
||||||
CANADA_OFFSET = 677,
|
CANADA_OFFSET = 677,
|
||||||
WORLD_OFFSET = 1353,
|
WORLD_OFFSET = 1353,
|
||||||
FIPS_RANGE = 360,
|
FIPS_RANGE = 360,
|
||||||
DMA_MAP = {
|
DMA_MAP = {
|
||||||
[500] = 'Portland-Auburn, ME',
|
[500] = 'Portland-Auburn, ME',
|
||||||
[501] = 'New York, NY',
|
[501] = 'New York, NY',
|
||||||
[502] = 'Binghamton, NY',
|
[502] = 'Binghamton, NY',
|
||||||
@@ -287,8 +287,8 @@ local MaxmindDef = {
|
|||||||
[866] = 'Fresno, CA',
|
[866] = 'Fresno, CA',
|
||||||
[868] = 'Chico-Redding, CA',
|
[868] = 'Chico-Redding, CA',
|
||||||
[881] = 'Spokane, WA'
|
[881] = 'Spokane, WA'
|
||||||
},
|
},
|
||||||
COUNTRY_CODES = {
|
COUNTRY_CODES = {
|
||||||
'', 'AP', 'EU', 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO', 'AQ',
|
'', 'AP', 'EU', 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AN', 'AO', 'AQ',
|
||||||
'AR', 'AS', 'AT', 'AU', 'AW', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH',
|
'AR', 'AS', 'AT', 'AU', 'AW', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH',
|
||||||
'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA',
|
'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA',
|
||||||
@@ -309,8 +309,8 @@ local MaxmindDef = {
|
|||||||
'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN',
|
'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN',
|
||||||
'VU', 'WF', 'WS', 'YE', 'YT', 'RS', 'ZA', 'ZM', 'ME', 'ZW', 'A1', 'A2', 'O1',
|
'VU', 'WF', 'WS', 'YE', 'YT', 'RS', 'ZA', 'ZM', 'ME', 'ZW', 'A1', 'A2', 'O1',
|
||||||
'AX', 'GG', 'IM', 'JE', 'BL', 'MF'
|
'AX', 'GG', 'IM', 'JE', 'BL', 'MF'
|
||||||
},
|
},
|
||||||
COUNTRY_CODES3 = {
|
COUNTRY_CODES3 = {
|
||||||
'','AP','EU','AND','ARE','AFG','ATG','AIA','ALB','ARM','ANT','AGO','AQ','ARG',
|
'','AP','EU','AND','ARE','AFG','ATG','AIA','ALB','ARM','ANT','AGO','AQ','ARG',
|
||||||
'ASM','AUT','AUS','ABW','AZE','BIH','BRB','BGD','BEL','BFA','BGR','BHR','BDI',
|
'ASM','AUT','AUS','ABW','AZE','BIH','BRB','BGD','BEL','BFA','BGR','BHR','BDI',
|
||||||
'BEN','BMU','BRN','BOL','BRA','BHS','BTN','BV','BWA','BLR','BLZ','CAN','CC',
|
'BEN','BMU','BRN','BOL','BRA','BHS','BTN','BV','BWA','BLR','BLZ','CAN','CC',
|
||||||
@@ -331,8 +331,8 @@ local MaxmindDef = {
|
|||||||
'UKR','UGA','UM','USA','URY','UZB','VAT','VCT','VEN','VGB','VIR','VNM','VUT',
|
'UKR','UGA','UM','USA','URY','UZB','VAT','VCT','VEN','VGB','VIR','VNM','VUT',
|
||||||
'WLF','WSM','YEM','YT','SRB','ZAF','ZMB','MNE','ZWE','A1','A2','O1',
|
'WLF','WSM','YEM','YT','SRB','ZAF','ZMB','MNE','ZWE','A1','A2','O1',
|
||||||
'ALA','GGY','IMN','JEY','BLM','MAF'
|
'ALA','GGY','IMN','JEY','BLM','MAF'
|
||||||
},
|
},
|
||||||
COUNTRY_NAMES = {
|
COUNTRY_NAMES = {
|
||||||
"", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates",
|
"", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates",
|
||||||
"Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia",
|
"Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia",
|
||||||
"Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa",
|
"Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa",
|
||||||
@@ -386,226 +386,226 @@ local MaxmindDef = {
|
|||||||
"Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe",
|
"Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe",
|
||||||
"Anonymous Proxy","Satellite Provider","Other",
|
"Anonymous Proxy","Satellite Provider","Other",
|
||||||
"Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin"
|
"Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local ip2long=function(ip_str)
|
local ip2long=function(ip_str)
|
||||||
local ip = stdnse.strsplit('%.',ip_str)
|
local ip = stdnse.strsplit('%.',ip_str)
|
||||||
local ip_num = (tonumber(ip[1])*16777216 + tonumber(ip[2])*65536
|
local ip_num = (tonumber(ip[1])*16777216 + tonumber(ip[2])*65536
|
||||||
+ tonumber(ip[3])*256 + tonumber(ip[4]))
|
+ tonumber(ip[3])*256 + tonumber(ip[4]))
|
||||||
return ip_num
|
return ip_num
|
||||||
end
|
end
|
||||||
|
|
||||||
local GeoIP = {
|
local GeoIP = {
|
||||||
new = function(self, filename)
|
new = function(self, filename)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o._filename=filename
|
o._filename=filename
|
||||||
local err
|
local err
|
||||||
o._filehandle= assert(io.open(filename,'rb'))
|
o._filehandle= assert(io.open(filename,'rb'))
|
||||||
o._databaseType = MaxmindDef.COUNTRY_EDITION
|
o._databaseType = MaxmindDef.COUNTRY_EDITION
|
||||||
o._recordLength = MaxmindDef.STANDARD_RECORD_LENGTH
|
o._recordLength = MaxmindDef.STANDARD_RECORD_LENGTH
|
||||||
|
|
||||||
local filepos = o._filehandle:seek()
|
local filepos = o._filehandle:seek()
|
||||||
o._filehandle:seek("end",-3)
|
o._filehandle:seek("end",-3)
|
||||||
|
|
||||||
for i=1,MaxmindDef.STRUCTURE_INFO_MAX_SIZE do
|
for i=1,MaxmindDef.STRUCTURE_INFO_MAX_SIZE do
|
||||||
local delim = o._filehandle:read(3)
|
local delim = o._filehandle:read(3)
|
||||||
|
|
||||||
if delim == '\255\255\255' then
|
if delim == '\255\255\255' then
|
||||||
o._databaseType = o._filehandle:read(1):byte()
|
o._databaseType = o._filehandle:read(1):byte()
|
||||||
-- backward compatibility with databases from April 2003 and earlier
|
-- backward compatibility with databases from April 2003 and earlier
|
||||||
if (o._databaseType >= 106) then
|
if (o._databaseType >= 106) then
|
||||||
o._databaseType = o._databaseType - 105
|
o._databaseType = o._databaseType - 105
|
||||||
end
|
end
|
||||||
|
|
||||||
local fast_combo1={[MaxmindDef.CITY_EDITION_REV0]=true,
|
local fast_combo1={[MaxmindDef.CITY_EDITION_REV0]=true,
|
||||||
[MaxmindDef.CITY_EDITION_REV1]=true,
|
[MaxmindDef.CITY_EDITION_REV1]=true,
|
||||||
[MaxmindDef.ORG_EDITION]=true,
|
[MaxmindDef.ORG_EDITION]=true,
|
||||||
[MaxmindDef.ISP_EDITION]=true,
|
[MaxmindDef.ISP_EDITION]=true,
|
||||||
[MaxmindDef.ASNUM_EDITION]=true}
|
[MaxmindDef.ASNUM_EDITION]=true}
|
||||||
|
|
||||||
if o._databaseType == MaxmindDef.REGION_EDITION_REV0 then
|
if o._databaseType == MaxmindDef.REGION_EDITION_REV0 then
|
||||||
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV0
|
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV0
|
||||||
elseif o._databaseType == MaxmindDef.REGION_EDITION_REV1 then
|
elseif o._databaseType == MaxmindDef.REGION_EDITION_REV1 then
|
||||||
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV1
|
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV1
|
||||||
elseif fast_combo1[o._databaseType] then
|
elseif fast_combo1[o._databaseType] then
|
||||||
o._databaseSegments = 0
|
o._databaseSegments = 0
|
||||||
local buf = o._filehandle:read(MaxmindDef.SEGMENT_RECORD_LENGTH)
|
local buf = o._filehandle:read(MaxmindDef.SEGMENT_RECORD_LENGTH)
|
||||||
|
|
||||||
-- the original representation in the MaxMind API is ANSI C integer
|
-- the original representation in the MaxMind API is ANSI C integer
|
||||||
-- which should not overflow the greatest value Lua can offer ;)
|
-- which should not overflow the greatest value Lua can offer ;)
|
||||||
for j=0,(MaxmindDef.SEGMENT_RECORD_LENGTH-1) do
|
for j=0,(MaxmindDef.SEGMENT_RECORD_LENGTH-1) do
|
||||||
o._databaseSegments = o._databaseSegments + bit.lshift( buf:byte(j+1), j*8)
|
o._databaseSegments = o._databaseSegments + bit.lshift( buf:byte(j+1), j*8)
|
||||||
end
|
end
|
||||||
|
|
||||||
if o._databaseType == MaxmindDef.ORG_EDITION or o._databaseType == MaxmindDef.ISP_EDITION then
|
if o._databaseType == MaxmindDef.ORG_EDITION or o._databaseType == MaxmindDef.ISP_EDITION then
|
||||||
o._recordLength = MaxmindDef.ORG_RECORD_LENGTH
|
o._recordLength = MaxmindDef.ORG_RECORD_LENGTH
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
o._filehandle:seek("cur",-4)
|
o._filehandle:seek("cur",-4)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if o._databaseType == MaxmindDef.COUNTRY_EDITION then
|
if o._databaseType == MaxmindDef.COUNTRY_EDITION then
|
||||||
o._databaseSegments = MaxmindDef.COUNTRY_BEGIN
|
o._databaseSegments = MaxmindDef.COUNTRY_BEGIN
|
||||||
end
|
end
|
||||||
o._filehandle:seek("set",filepos)
|
o._filehandle:seek("set",filepos)
|
||||||
|
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
output_record_by_addr = function(self,addr)
|
output_record_by_addr = function(self,addr)
|
||||||
local loc = self:record_by_addr(addr)
|
local loc = self:record_by_addr(addr)
|
||||||
if not loc then return nil end
|
if not loc then return nil end
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
--output.name = "Maxmind database"
|
--output.name = "Maxmind database"
|
||||||
table.insert(output, "coordinates (lat,lon): " .. loc.latitude .. "," .. loc.longitude)
|
table.insert(output, "coordinates (lat,lon): " .. loc.latitude .. "," .. loc.longitude)
|
||||||
|
|
||||||
local str = ""
|
local str = ""
|
||||||
if loc.city then
|
if loc.city then
|
||||||
str = str.."city: "..loc.city
|
str = str.."city: "..loc.city
|
||||||
end
|
end
|
||||||
if loc.metro_code then
|
if loc.metro_code then
|
||||||
str = str .. ", "..loc.metro_code
|
str = str .. ", "..loc.metro_code
|
||||||
end
|
end
|
||||||
if loc.country_name then
|
if loc.country_name then
|
||||||
str = str .. ", "..loc.country_name
|
str = str .. ", "..loc.country_name
|
||||||
end
|
end
|
||||||
table.insert(output,str)
|
table.insert(output,str)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end,
|
end,
|
||||||
|
|
||||||
record_by_addr=function(self,addr)
|
record_by_addr=function(self,addr)
|
||||||
local ipnum = ip2long(addr)
|
local ipnum = ip2long(addr)
|
||||||
return self:_get_record(ipnum)
|
return self:_get_record(ipnum)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_get_record=function(self,ipnum)
|
_get_record=function(self,ipnum)
|
||||||
local seek_country = self:_seek_country(ipnum)
|
local seek_country = self:_seek_country(ipnum)
|
||||||
if seek_country == self._databaseSegments then
|
if seek_country == self._databaseSegments then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments
|
local record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments
|
||||||
|
|
||||||
self._filehandle:seek("set",record_pointer)
|
self._filehandle:seek("set",record_pointer)
|
||||||
local record_buf = self._filehandle:read(MaxmindDef.FULL_RECORD_LENGTH)
|
local record_buf = self._filehandle:read(MaxmindDef.FULL_RECORD_LENGTH)
|
||||||
|
|
||||||
local record = {}
|
local record = {}
|
||||||
local start_pos = 1
|
local start_pos = 1
|
||||||
local char = record_buf:byte(start_pos)
|
local char = record_buf:byte(start_pos)
|
||||||
char=char+1
|
char=char+1
|
||||||
record.country_code = MaxmindDef.COUNTRY_CODES[char]
|
record.country_code = MaxmindDef.COUNTRY_CODES[char]
|
||||||
record.country_code3 = MaxmindDef.COUNTRY_CODES3[char]
|
record.country_code3 = MaxmindDef.COUNTRY_CODES3[char]
|
||||||
record.country_name = MaxmindDef.COUNTRY_NAMES[char]
|
record.country_name = MaxmindDef.COUNTRY_NAMES[char]
|
||||||
start_pos = start_pos + 1
|
start_pos = start_pos + 1
|
||||||
local end_pos = 0
|
local end_pos = 0
|
||||||
|
|
||||||
end_pos = record_buf:find("\0",start_pos)
|
end_pos = record_buf:find("\0",start_pos)
|
||||||
if start_pos ~= end_pos then
|
if start_pos ~= end_pos then
|
||||||
record.region_name = record_buf:sub(start_pos, end_pos-1)
|
record.region_name = record_buf:sub(start_pos, end_pos-1)
|
||||||
end
|
end
|
||||||
start_pos = end_pos + 1
|
start_pos = end_pos + 1
|
||||||
|
|
||||||
end_pos = record_buf:find("\0",start_pos)
|
end_pos = record_buf:find("\0",start_pos)
|
||||||
if start_pos ~= end_pos then
|
if start_pos ~= end_pos then
|
||||||
record.city = record_buf:sub(start_pos, end_pos-1)
|
record.city = record_buf:sub(start_pos, end_pos-1)
|
||||||
end
|
end
|
||||||
start_pos = end_pos + 1
|
start_pos = end_pos + 1
|
||||||
|
|
||||||
|
|
||||||
end_pos = record_buf:find("\0",start_pos)
|
end_pos = record_buf:find("\0",start_pos)
|
||||||
if start_pos ~= end_pos then
|
if start_pos ~= end_pos then
|
||||||
record.postal_code = record_buf:sub(start_pos, end_pos-1)
|
record.postal_code = record_buf:sub(start_pos, end_pos-1)
|
||||||
end
|
end
|
||||||
start_pos = end_pos + 1
|
start_pos = end_pos + 1
|
||||||
|
|
||||||
local c1,c2,c3=record_buf:byte(start_pos,start_pos+3)
|
local c1,c2,c3=record_buf:byte(start_pos,start_pos+3)
|
||||||
record.latitude = (( bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) )/10000) - 180
|
record.latitude = (( bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) )/10000) - 180
|
||||||
start_pos = start_pos +3
|
start_pos = start_pos +3
|
||||||
|
|
||||||
c1,c2,c3=record_buf:byte(start_pos,start_pos+3)
|
c1,c2,c3=record_buf:byte(start_pos,start_pos+3)
|
||||||
record.longitude = (( bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) )/10000) - 180
|
record.longitude = (( bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8) )/10000) - 180
|
||||||
start_pos = start_pos +3
|
start_pos = start_pos +3
|
||||||
|
|
||||||
if self._databaseType == MaxmindDef.CITY_EDITION_REV1 and record.country_code=='US' then
|
if self._databaseType == MaxmindDef.CITY_EDITION_REV1 and record.country_code=='US' then
|
||||||
c1,c2,c3=record_buf:byte(start_pos,start_pos+3)
|
c1,c2,c3=record_buf:byte(start_pos,start_pos+3)
|
||||||
local dmaarea_combo= bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8)
|
local dmaarea_combo= bit.lshift(c1,0*8) + bit.lshift(c2,1*8) + bit.lshift(c3,2*8)
|
||||||
record.dma_code = math.floor(dmaarea_combo/1000)
|
record.dma_code = math.floor(dmaarea_combo/1000)
|
||||||
record.area_code = dmaarea_combo % 1000
|
record.area_code = dmaarea_combo % 1000
|
||||||
else
|
else
|
||||||
record.dma_code = nil
|
record.dma_code = nil
|
||||||
record.area_code = nil
|
record.area_code = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if record.dma_code and MaxmindDef.DMA_MAP[record.dma_code] then
|
if record.dma_code and MaxmindDef.DMA_MAP[record.dma_code] then
|
||||||
record.metro_code = MaxmindDef.DMA_MAP[record.dma_code]
|
record.metro_code = MaxmindDef.DMA_MAP[record.dma_code]
|
||||||
else
|
else
|
||||||
record.metro_code = nil
|
record.metro_code = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return record
|
return record
|
||||||
end,
|
end,
|
||||||
|
|
||||||
_seek_country=function(self,ipnum)
|
_seek_country=function(self,ipnum)
|
||||||
local offset = 0
|
local offset = 0
|
||||||
for depth=31,0,-1 do
|
for depth=31,0,-1 do
|
||||||
self._filehandle:seek("set", 2 * self._recordLength * offset)
|
self._filehandle:seek("set", 2 * self._recordLength * offset)
|
||||||
local buf = self._filehandle:read(2*self._recordLength)
|
local buf = self._filehandle:read(2*self._recordLength)
|
||||||
|
|
||||||
local x = {}
|
local x = {}
|
||||||
x[0],x[1] = 0,0
|
x[0],x[1] = 0,0
|
||||||
|
|
||||||
for i=0,1 do
|
for i=0,1 do
|
||||||
for j=0,(self._recordLength-1) do
|
for j=0,(self._recordLength-1) do
|
||||||
x[i] = x[i] + bit.lshift(buf:byte((self._recordLength * i + j) +1 ), j*8)
|
x[i] = x[i] + bit.lshift(buf:byte((self._recordLength * i + j) +1 ), j*8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Gotta test this out thorougly because of the ipnum
|
-- Gotta test this out thorougly because of the ipnum
|
||||||
if bit.band(ipnum, bit.lshift(1,depth)) ~= 0 then
|
if bit.band(ipnum, bit.lshift(1,depth)) ~= 0 then
|
||||||
if x[1] >= self._databaseSegments then
|
if x[1] >= self._databaseSegments then
|
||||||
return x[1]
|
return x[1]
|
||||||
end
|
end
|
||||||
offset = x[1]
|
offset = x[1]
|
||||||
else
|
else
|
||||||
if x[0] >= self._databaseSegments then
|
if x[0] >= self._databaseSegments then
|
||||||
return x[0]
|
return x[0]
|
||||||
end
|
end
|
||||||
offset = x[0]
|
offset = x[0]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
stdnse.print_debug('Error traversing database - perhaps it is corrupt?')
|
stdnse.print_debug('Error traversing database - perhaps it is corrupt?')
|
||||||
return nil
|
return nil
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function(host,port)
|
action = function(host,port)
|
||||||
local f_maxmind = stdnse.get_script_args(SCRIPT_NAME .. ".maxmind_db")
|
local f_maxmind = stdnse.get_script_args(SCRIPT_NAME .. ".maxmind_db")
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
|
|
||||||
--if f_maxmind is a string, it should specify the filename of the database
|
--if f_maxmind is a string, it should specify the filename of the database
|
||||||
if f_maxmind then
|
if f_maxmind then
|
||||||
local gi = assert( GeoIP:new(f_maxmind), "Wrong file specified for a Maxmind database")
|
local gi = assert( GeoIP:new(f_maxmind), "Wrong file specified for a Maxmind database")
|
||||||
local out = gi:output_record_by_addr(host.ip)
|
local out = gi:output_record_by_addr(host.ip)
|
||||||
output = out
|
output = out
|
||||||
else
|
else
|
||||||
local gi = assert( GeoIP:new(nmap.fetchfile("nselib/data/GeoLiteCity.dat")), "Cannot read Maxmind database file in 'nselib/data/'.")
|
local gi = assert( GeoIP:new(nmap.fetchfile("nselib/data/GeoLiteCity.dat")), "Cannot read Maxmind database file in 'nselib/data/'.")
|
||||||
local out = gi:output_record_by_addr(host.ip)
|
local out = gi:output_record_by_addr(host.ip)
|
||||||
output = out
|
output = out
|
||||||
end
|
end
|
||||||
|
|
||||||
if(#output~=0) then
|
if(#output~=0) then
|
||||||
output.name = host.ip
|
output.name = host.ip
|
||||||
if host.targetname then
|
if host.targetname then
|
||||||
output.name = output.name.." ("..host.targetname..")"
|
output.name = output.name.." ("..host.targetname..")"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true,output)
|
return stdnse.format_output(true,output)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -52,142 +52,142 @@ local QTYPE_NODEADDRESSES = 3
|
|||||||
local QTYPE_NODEIPV4ADDRESSES = 4
|
local QTYPE_NODEIPV4ADDRESSES = 4
|
||||||
|
|
||||||
local QTYPE_STRINGS = {
|
local QTYPE_STRINGS = {
|
||||||
[QTYPE_NOOP] = "NOOP",
|
[QTYPE_NOOP] = "NOOP",
|
||||||
[QTYPE_NODENAME] = "Hostnames",
|
[QTYPE_NODENAME] = "Hostnames",
|
||||||
[QTYPE_NODEADDRESSES] = "IPv6 addresses",
|
[QTYPE_NODEADDRESSES] = "IPv6 addresses",
|
||||||
[QTYPE_NODEIPV4ADDRESSES] = "IPv4 addresses",
|
[QTYPE_NODEIPV4ADDRESSES] = "IPv4 addresses",
|
||||||
}
|
}
|
||||||
|
|
||||||
local function build_ni_query(src, dst, qtype)
|
local function build_ni_query(src, dst, qtype)
|
||||||
local payload, p, flags
|
local payload, p, flags
|
||||||
local nonce
|
local nonce
|
||||||
|
|
||||||
nonce = openssl.rand_pseudo_bytes(8)
|
nonce = openssl.rand_pseudo_bytes(8)
|
||||||
if qtype == QTYPE_NODENAME then
|
if qtype == QTYPE_NODENAME then
|
||||||
flags = 0x0000
|
flags = 0x0000
|
||||||
elseif qtype == QTYPE_NODEADDRESSES then
|
elseif qtype == QTYPE_NODEADDRESSES then
|
||||||
-- Set all the flags GSLCA (see RFC 4620, Figure 3).
|
-- Set all the flags GSLCA (see RFC 4620, Figure 3).
|
||||||
flags = 0x003E
|
flags = 0x003E
|
||||||
elseif qtype == QTYPE_NODEIPV4ADDRESSES then
|
elseif qtype == QTYPE_NODEIPV4ADDRESSES then
|
||||||
-- Set the A flag (see RFC 4620, Figure 4).
|
-- Set the A flag (see RFC 4620, Figure 4).
|
||||||
flags = 0x0002
|
flags = 0x0002
|
||||||
else
|
else
|
||||||
error("Unknown qtype " .. qtype)
|
error("Unknown qtype " .. qtype)
|
||||||
end
|
end
|
||||||
payload = bin.pack(">SSAA", qtype, flags, nonce, dst)
|
payload = bin.pack(">SSAA", qtype, flags, nonce, dst)
|
||||||
p = packet.Packet:new()
|
p = packet.Packet:new()
|
||||||
p:build_icmpv6_header(ICMPv6_NODEINFOQUERY, ICMPv6_NODEINFOQUERY_IPv6ADDR, payload, src, dst)
|
p:build_icmpv6_header(ICMPv6_NODEINFOQUERY, ICMPv6_NODEINFOQUERY_IPv6ADDR, payload, src, dst)
|
||||||
p:build_ipv6_packet(src, dst, packet.IPPROTO_ICMPV6)
|
p:build_ipv6_packet(src, dst, packet.IPPROTO_ICMPV6)
|
||||||
|
|
||||||
return p.buf
|
return p.buf
|
||||||
end
|
end
|
||||||
|
|
||||||
function hostrule(host)
|
function hostrule(host)
|
||||||
return nmap.is_privileged() and #host.bin_ip == 16 and host.interface
|
return nmap.is_privileged() and #host.bin_ip == 16 and host.interface
|
||||||
end
|
end
|
||||||
|
|
||||||
local function open_sniffer(host)
|
local function open_sniffer(host)
|
||||||
local bpf
|
local bpf
|
||||||
local s
|
local s
|
||||||
|
|
||||||
s = nmap.new_socket()
|
s = nmap.new_socket()
|
||||||
bpf = string.format("ip6 and src host %s", host.ip)
|
bpf = string.format("ip6 and src host %s", host.ip)
|
||||||
s:pcap_open(host.interface, 1500, false, bpf)
|
s:pcap_open(host.interface, 1500, false, bpf)
|
||||||
|
|
||||||
return s
|
return s
|
||||||
end
|
end
|
||||||
|
|
||||||
local function send_queries(host)
|
local function send_queries(host)
|
||||||
local dnet
|
local dnet
|
||||||
|
|
||||||
dnet = nmap.new_dnet()
|
dnet = nmap.new_dnet()
|
||||||
dnet:ip_open()
|
dnet:ip_open()
|
||||||
local p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEADDRESSES)
|
local p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEADDRESSES)
|
||||||
dnet:ip_send(p, host)
|
dnet:ip_send(p, host)
|
||||||
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODENAME)
|
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODENAME)
|
||||||
dnet:ip_send(p, host)
|
dnet:ip_send(p, host)
|
||||||
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEIPV4ADDRESSES)
|
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEIPV4ADDRESSES)
|
||||||
dnet:ip_send(p, host)
|
dnet:ip_send(p, host)
|
||||||
dnet:ip_close()
|
dnet:ip_close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function empty(t)
|
local function empty(t)
|
||||||
return not next(t)
|
return not next(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try to decode a Node Name reply data field. If successful, returns true and
|
-- Try to decode a Node Name reply data field. If successful, returns true and
|
||||||
-- a list of DNS names. In case of a parsing error, returns false and the
|
-- a list of DNS names. In case of a parsing error, returns false and the
|
||||||
-- partial list of names that were parsed prior to the error.
|
-- partial list of names that were parsed prior to the error.
|
||||||
local function try_decode_nodenames(data)
|
local function try_decode_nodenames(data)
|
||||||
local ttl
|
local ttl
|
||||||
local names = {}
|
local names = {}
|
||||||
local pos = nil
|
local pos = nil
|
||||||
|
|
||||||
pos, ttl = bin.unpack(">I", data, pos)
|
pos, ttl = bin.unpack(">I", data, pos)
|
||||||
if not ttl then
|
if not ttl then
|
||||||
return false, names
|
return false, names
|
||||||
end
|
end
|
||||||
while pos <= #data do
|
while pos <= #data do
|
||||||
local name
|
local name
|
||||||
|
|
||||||
pos, name = dns.decStr(data, pos)
|
pos, name = dns.decStr(data, pos)
|
||||||
if not name then
|
if not name then
|
||||||
return false, names
|
return false, names
|
||||||
end
|
end
|
||||||
-- Ignore empty names, such as those at the end.
|
-- Ignore empty names, such as those at the end.
|
||||||
if name ~= "" then
|
if name ~= "" then
|
||||||
names[#names + 1] = name
|
names[#names + 1] = name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, names
|
return true, names
|
||||||
end
|
end
|
||||||
|
|
||||||
local function stringify_noop(flags, data)
|
local function stringify_noop(flags, data)
|
||||||
return "replied"
|
return "replied"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RFC 4620, section 6.3.
|
-- RFC 4620, section 6.3.
|
||||||
local function stringify_nodename(flags, data)
|
local function stringify_nodename(flags, data)
|
||||||
local status, names
|
local status, names
|
||||||
local text
|
local text
|
||||||
|
|
||||||
status, names = try_decode_nodenames(data)
|
status, names = try_decode_nodenames(data)
|
||||||
if empty(names) then
|
if empty(names) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
text = stdnse.strjoin(", ", names)
|
text = stdnse.strjoin(", ", names)
|
||||||
if not status then
|
if not status then
|
||||||
text = text .. " (parsing error)"
|
text = text .. " (parsing error)"
|
||||||
end
|
end
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RFC 4620, section 6.3.
|
-- RFC 4620, section 6.3.
|
||||||
local function stringify_nodeaddresses(flags, data)
|
local function stringify_nodeaddresses(flags, data)
|
||||||
local ttl, binaddr
|
local ttl, binaddr
|
||||||
local text
|
local text
|
||||||
local addrs = {}
|
local addrs = {}
|
||||||
local pos = nil
|
local pos = nil
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
pos, ttl, binaddr = bin.unpack(">IA16", data, pos)
|
pos, ttl, binaddr = bin.unpack(">IA16", data, pos)
|
||||||
if not ttl then
|
if not ttl then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
addrs[#addrs + 1] = packet.toipv6(binaddr)
|
addrs[#addrs + 1] = packet.toipv6(binaddr)
|
||||||
end
|
end
|
||||||
if empty(addrs) then
|
if empty(addrs) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
text = stdnse.strjoin(", ", addrs)
|
text = stdnse.strjoin(", ", addrs)
|
||||||
if bit.band(flags, 0x01) ~= 0 then
|
if bit.band(flags, 0x01) ~= 0 then
|
||||||
text = text .. " (more omitted for space reasons)"
|
text = text .. " (more omitted for space reasons)"
|
||||||
end
|
end
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
-- RFC 4620, section 6.4.
|
-- RFC 4620, section 6.4.
|
||||||
@@ -200,133 +200,133 @@ end
|
|||||||
-- 00 00 00 00 0e 6d 61 63 2d 6d 69 6e 69 2e 6c 6f .....mac -mini.lo
|
-- 00 00 00 00 0e 6d 61 63 2d 6d 69 6e 69 2e 6c 6f .....mac -mini.lo
|
||||||
-- 63 61 6c cal
|
-- 63 61 6c cal
|
||||||
local function stringify_nodeipv4addresses(flags, data)
|
local function stringify_nodeipv4addresses(flags, data)
|
||||||
local status, names
|
local status, names
|
||||||
local ttl, binaddr
|
local ttl, binaddr
|
||||||
local text
|
local text
|
||||||
local addrs = {}
|
local addrs = {}
|
||||||
local pos = nil
|
local pos = nil
|
||||||
|
|
||||||
-- Check for DNS names.
|
-- Check for DNS names.
|
||||||
status, names = try_decode_nodenames(data .. "\0\0")
|
status, names = try_decode_nodenames(data .. "\0\0")
|
||||||
if status then
|
if status then
|
||||||
return "(actually hostnames) " .. stdnse.strjoin(", ", names)
|
return "(actually hostnames) " .. stdnse.strjoin(", ", names)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Okay, looks like it's really IP addresses.
|
-- Okay, looks like it's really IP addresses.
|
||||||
while true do
|
while true do
|
||||||
pos, ttl, binaddr = bin.unpack(">IA4", data, pos)
|
pos, ttl, binaddr = bin.unpack(">IA4", data, pos)
|
||||||
if not ttl then
|
if not ttl then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
addrs[#addrs + 1] = packet.toip(binaddr)
|
addrs[#addrs + 1] = packet.toip(binaddr)
|
||||||
end
|
end
|
||||||
if empty(addrs) then
|
if empty(addrs) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
text = stdnse.strjoin(", ", addrs)
|
text = stdnse.strjoin(", ", addrs)
|
||||||
if bit.band(flags, 0x01) ~= 0 then
|
if bit.band(flags, 0x01) ~= 0 then
|
||||||
text = text .. " (more omitted for space reasons)"
|
text = text .. " (more omitted for space reasons)"
|
||||||
end
|
end
|
||||||
|
|
||||||
return text
|
return text
|
||||||
end
|
end
|
||||||
|
|
||||||
local STRINGIFY = {
|
local STRINGIFY = {
|
||||||
[QTYPE_NOOP] = stringify_noop,
|
[QTYPE_NOOP] = stringify_noop,
|
||||||
[QTYPE_NODENAME] = stringify_nodename,
|
[QTYPE_NODENAME] = stringify_nodename,
|
||||||
[QTYPE_NODEADDRESSES] = stringify_nodeaddresses,
|
[QTYPE_NODEADDRESSES] = stringify_nodeaddresses,
|
||||||
[QTYPE_NODEIPV4ADDRESSES] = stringify_nodeipv4addresses,
|
[QTYPE_NODEIPV4ADDRESSES] = stringify_nodeipv4addresses,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function handle_received_packet(buf)
|
local function handle_received_packet(buf)
|
||||||
local p, qtype, flags, data
|
local p, qtype, flags, data
|
||||||
local text
|
local text
|
||||||
|
|
||||||
p = packet.Packet:new(buf)
|
p = packet.Packet:new(buf)
|
||||||
if p.icmpv6_type ~= ICMPv6_NODEINFORESP then
|
if p.icmpv6_type ~= ICMPv6_NODEINFORESP then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
qtype = packet.u16(p.buf, p.icmpv6_offset + 4)
|
qtype = packet.u16(p.buf, p.icmpv6_offset + 4)
|
||||||
flags = packet.u16(p.buf, p.icmpv6_offset + 6)
|
flags = packet.u16(p.buf, p.icmpv6_offset + 6)
|
||||||
data = string.sub(p.buf, p.icmpv6_offset + 16 + 1)
|
data = string.sub(p.buf, p.icmpv6_offset + 16 + 1)
|
||||||
|
|
||||||
if not STRINGIFY[qtype] then
|
if not STRINGIFY[qtype] then
|
||||||
-- This is a not a qtype we sent or know about.
|
-- This is a not a qtype we sent or know about.
|
||||||
stdnse.print_debug(1, "Got NI reply with unknown qtype %d from %s", qtype, p.ip6_src)
|
stdnse.print_debug(1, "Got NI reply with unknown qtype %d from %s", qtype, p.ip6_src)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if p.icmpv6_code == ICMPv6_NODEINFORESP_SUCCESS then
|
if p.icmpv6_code == ICMPv6_NODEINFORESP_SUCCESS then
|
||||||
text = STRINGIFY[qtype](flags, data)
|
text = STRINGIFY[qtype](flags, data)
|
||||||
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_REFUSED then
|
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_REFUSED then
|
||||||
text = "refused"
|
text = "refused"
|
||||||
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_UNKNOWN then
|
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_UNKNOWN then
|
||||||
text = string.format("target said: qtype %d is unknown", qtype)
|
text = string.format("target said: qtype %d is unknown", qtype)
|
||||||
else
|
else
|
||||||
text = string.format("unknown ICMPv6 code %d for qtype %d", p.icmpv6_code, qtype)
|
text = string.format("unknown ICMPv6 code %d for qtype %d", p.icmpv6_code, qtype)
|
||||||
end
|
end
|
||||||
|
|
||||||
return qtype, text
|
return qtype, text
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_results(results)
|
local function format_results(results)
|
||||||
local QTYPE_ORDER = {
|
local QTYPE_ORDER = {
|
||||||
QTYPE_NOOP,
|
QTYPE_NOOP,
|
||||||
QTYPE_NODENAME,
|
QTYPE_NODENAME,
|
||||||
QTYPE_NODEADDRESSES,
|
QTYPE_NODEADDRESSES,
|
||||||
QTYPE_NODEIPV4ADDRESSES,
|
QTYPE_NODEIPV4ADDRESSES,
|
||||||
}
|
}
|
||||||
local output
|
local output
|
||||||
|
|
||||||
output = {}
|
output = {}
|
||||||
for _, qtype in ipairs(QTYPE_ORDER) do
|
for _, qtype in ipairs(QTYPE_ORDER) do
|
||||||
if results[qtype] then
|
if results[qtype] then
|
||||||
output[#output + 1] = QTYPE_STRINGS[qtype] .. ": " .. results[qtype]
|
output[#output + 1] = QTYPE_STRINGS[qtype] .. ": " .. results[qtype]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|
||||||
function action(host)
|
function action(host)
|
||||||
local s
|
local s
|
||||||
local timeout, end_time, now
|
local timeout, end_time, now
|
||||||
local pending, results
|
local pending, results
|
||||||
|
|
||||||
timeout = host.times.timeout * 10
|
timeout = host.times.timeout * 10
|
||||||
|
|
||||||
s = open_sniffer(host)
|
s = open_sniffer(host)
|
||||||
|
|
||||||
send_queries(host)
|
send_queries(host)
|
||||||
|
|
||||||
pending = {
|
pending = {
|
||||||
[QTYPE_NODENAME] = true,
|
[QTYPE_NODENAME] = true,
|
||||||
[QTYPE_NODEADDRESSES] = true,
|
[QTYPE_NODEADDRESSES] = true,
|
||||||
[QTYPE_NODEIPV4ADDRESSES] = true,
|
[QTYPE_NODEIPV4ADDRESSES] = true,
|
||||||
}
|
}
|
||||||
results = {}
|
results = {}
|
||||||
|
|
||||||
now = nmap.clock_ms()
|
now = nmap.clock_ms()
|
||||||
end_time = now + timeout
|
end_time = now + timeout
|
||||||
repeat
|
repeat
|
||||||
local _, status, buf
|
local _, status, buf
|
||||||
|
|
||||||
s:set_timeout((end_time - now) * 1000)
|
s:set_timeout((end_time - now) * 1000)
|
||||||
|
|
||||||
status, _, _, buf = s:pcap_receive()
|
status, _, _, buf = s:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
local qtype, text = handle_received_packet(buf)
|
local qtype, text = handle_received_packet(buf)
|
||||||
if qtype then
|
if qtype then
|
||||||
results[qtype] = text
|
results[qtype] = text
|
||||||
pending[qtype] = nil
|
pending[qtype] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
now = nmap.clock_ms()
|
now = nmap.clock_ms()
|
||||||
until empty(pending) or now > end_time
|
until empty(pending) or now > end_time
|
||||||
|
|
||||||
s:pcap_close()
|
s:pcap_close()
|
||||||
|
|
||||||
return format_results(results)
|
return format_results(results)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -56,22 +56,22 @@ local RPL_LIST = "322"
|
|||||||
local RPL_LISTEND = "323"
|
local RPL_LISTEND = "323"
|
||||||
|
|
||||||
local DEFAULT_CHANNELS = {
|
local DEFAULT_CHANNELS = {
|
||||||
"loic",
|
"loic",
|
||||||
"Agobot",
|
"Agobot",
|
||||||
"Slackbot",
|
"Slackbot",
|
||||||
"Mytob",
|
"Mytob",
|
||||||
"Rbot",
|
"Rbot",
|
||||||
"SdBot",
|
"SdBot",
|
||||||
"poebot",
|
"poebot",
|
||||||
"IRCBot",
|
"IRCBot",
|
||||||
"VanBot",
|
"VanBot",
|
||||||
"MPack",
|
"MPack",
|
||||||
"Storm",
|
"Storm",
|
||||||
"GTbot",
|
"GTbot",
|
||||||
"Spybot",
|
"Spybot",
|
||||||
"Phatbot",
|
"Phatbot",
|
||||||
"Wargbot",
|
"Wargbot",
|
||||||
"RxBot",
|
"RxBot",
|
||||||
}
|
}
|
||||||
|
|
||||||
portrule = shortport.port_or_service({6666, 6667, 6697, 6679}, {"irc", "ircs"})
|
portrule = shortport.port_or_service({6666, 6667, 6697, 6679}, {"irc", "ircs"})
|
||||||
@@ -84,238 +84,238 @@ portrule = shortport.port_or_service({6666, 6667, 6697, 6679}, {"irc", "ircs"})
|
|||||||
--
|
--
|
||||||
-- See RFC 2812, section 2.3.1 for BNF of a message.
|
-- See RFC 2812, section 2.3.1 for BNF of a message.
|
||||||
local function irc_parse_message(s)
|
local function irc_parse_message(s)
|
||||||
local prefix, command, params
|
local prefix, command, params
|
||||||
local _, p, t
|
local _, p, t
|
||||||
|
|
||||||
s = string.gsub(s, "\r?\n$", "")
|
s = string.gsub(s, "\r?\n$", "")
|
||||||
if string.match(s, "^ *$") then
|
if string.match(s, "^ *$") then
|
||||||
return true, nil
|
return true, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
p = 0
|
p = 0
|
||||||
_, t, prefix = string.find(s, "^:([^ ]+) +", p + 1)
|
_, t, prefix = string.find(s, "^:([^ ]+) +", p + 1)
|
||||||
if t then
|
if t then
|
||||||
p = t
|
p = t
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We do not check for any special format of the command name or
|
-- We do not check for any special format of the command name or
|
||||||
-- number.
|
-- number.
|
||||||
_, p, command = string.find(s, "^([^ ]+)", p + 1)
|
_, p, command = string.find(s, "^([^ ]+)", p + 1)
|
||||||
if not p then
|
if not p then
|
||||||
return nil, "Presumed message is missing a command."
|
return nil, "Presumed message is missing a command."
|
||||||
end
|
end
|
||||||
|
|
||||||
params = {}
|
params = {}
|
||||||
while p + 1 <= #s do
|
while p + 1 <= #s do
|
||||||
local param
|
local param
|
||||||
|
|
||||||
_, p = string.find(s, "^ +", p + 1)
|
_, p = string.find(s, "^ +", p + 1)
|
||||||
if not p then
|
if not p then
|
||||||
return nil, "Missing a space before param."
|
return nil, "Missing a space before param."
|
||||||
end
|
end
|
||||||
-- We don't do any checks on the contents of params.
|
-- We don't do any checks on the contents of params.
|
||||||
if #params == 14 then
|
if #params == 14 then
|
||||||
params[#params + 1] = string.sub(s, p + 1)
|
params[#params + 1] = string.sub(s, p + 1)
|
||||||
break
|
break
|
||||||
elseif string.match(s, "^:", p + 1) then
|
elseif string.match(s, "^:", p + 1) then
|
||||||
params[#params + 1] = string.sub(s, p + 2)
|
params[#params + 1] = string.sub(s, p + 2)
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
_, p, param = string.find(s, "^([^ ]+)", p + 1)
|
_, p, param = string.find(s, "^([^ ]+)", p + 1)
|
||||||
if not p then
|
if not p then
|
||||||
return nil, "Missing a param."
|
return nil, "Missing a param."
|
||||||
end
|
end
|
||||||
params[#params + 1] = param
|
params[#params + 1] = param
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, prefix, command, params
|
return true, prefix, command, params
|
||||||
end
|
end
|
||||||
|
|
||||||
local function irc_compose_message(prefix, command, ...)
|
local function irc_compose_message(prefix, command, ...)
|
||||||
local parts, params
|
local parts, params
|
||||||
|
|
||||||
parts = {}
|
parts = {}
|
||||||
if prefix then
|
if prefix then
|
||||||
parts[#parts + 1] = prefix
|
parts[#parts + 1] = prefix
|
||||||
end
|
end
|
||||||
|
|
||||||
if string.match(command, "^:") then
|
if string.match(command, "^:") then
|
||||||
return nil, "Command may not begin with ':'."
|
return nil, "Command may not begin with ':'."
|
||||||
end
|
end
|
||||||
parts[#parts + 1] = command
|
parts[#parts + 1] = command
|
||||||
|
|
||||||
params = {...}
|
params = {...}
|
||||||
for i, param in ipairs(params) do
|
for i, param in ipairs(params) do
|
||||||
if not string.match(param, "^[^\0\r\n :][^\0\r\n ]*$") then
|
if not string.match(param, "^[^\0\r\n :][^\0\r\n ]*$") then
|
||||||
if i < #params then
|
if i < #params then
|
||||||
return nil, "Bad format for param."
|
return nil, "Bad format for param."
|
||||||
else
|
else
|
||||||
parts[#parts + 1] = ":" .. param
|
parts[#parts + 1] = ":" .. param
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
parts[#parts + 1] = param
|
parts[#parts + 1] = param
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.strjoin(" ", parts) .. "\r\n"
|
return stdnse.strjoin(" ", parts) .. "\r\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function random_nick()
|
local function random_nick()
|
||||||
local nick = {}
|
local nick = {}
|
||||||
|
|
||||||
for i = 1, 9 do
|
for i = 1, 9 do
|
||||||
nick[#nick + 1] = string.char(math.random(string.byte("a"), string.byte("z")))
|
nick[#nick + 1] = string.char(math.random(string.byte("a"), string.byte("z")))
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(nick)
|
return table.concat(nick)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function splitlines(s)
|
local function splitlines(s)
|
||||||
local lines = {}
|
local lines = {}
|
||||||
local _, i, j
|
local _, i, j
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
while i <= #s do
|
while i <= #s do
|
||||||
_, j = string.find(s, "\r?\n", i)
|
_, j = string.find(s, "\r?\n", i)
|
||||||
lines[#lines + 1] = string.sub(s, i, j)
|
lines[#lines + 1] = string.sub(s, i, j)
|
||||||
if not j then
|
if not j then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
i = j + 1
|
i = j + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
local function irc_connect(host, port, nick, user, pass)
|
local function irc_connect(host, port, nick, user, pass)
|
||||||
local commands = {}
|
local commands = {}
|
||||||
local irc = {}
|
local irc = {}
|
||||||
local banner
|
local banner
|
||||||
|
|
||||||
-- Section 3.1.1.
|
-- Section 3.1.1.
|
||||||
if pass then
|
if pass then
|
||||||
commands[#commands + 1] = irc_compose_message(nil, "PASS", pass)
|
commands[#commands + 1] = irc_compose_message(nil, "PASS", pass)
|
||||||
end
|
end
|
||||||
nick = nick or random_nick()
|
nick = nick or random_nick()
|
||||||
commands[#commands + 1] = irc_compose_message(nil, "NICK", nick)
|
commands[#commands + 1] = irc_compose_message(nil, "NICK", nick)
|
||||||
user = user or nick
|
user = user or nick
|
||||||
commands[#commands + 1] = irc_compose_message(nil, "USER", user, "8", "*", user)
|
commands[#commands + 1] = irc_compose_message(nil, "USER", user, "8", "*", user)
|
||||||
|
|
||||||
irc.sd, banner = comm.tryssl(host, port, table.concat(commands))
|
irc.sd, banner = comm.tryssl(host, port, table.concat(commands))
|
||||||
if not irc.sd then
|
if not irc.sd then
|
||||||
return nil, "Unable to open connection."
|
return nil, "Unable to open connection."
|
||||||
end
|
end
|
||||||
|
|
||||||
irc.sd:set_timeout(60 * 1000)
|
irc.sd:set_timeout(60 * 1000)
|
||||||
|
|
||||||
-- Buffer these initial lines for irc_readline.
|
-- Buffer these initial lines for irc_readline.
|
||||||
irc.linebuf = splitlines(banner)
|
irc.linebuf = splitlines(banner)
|
||||||
|
|
||||||
irc.buf = stdnse.make_buffer(irc.sd, "\r?\n")
|
irc.buf = stdnse.make_buffer(irc.sd, "\r?\n")
|
||||||
|
|
||||||
return irc
|
return irc
|
||||||
end
|
end
|
||||||
|
|
||||||
local function irc_disconnect(irc)
|
local function irc_disconnect(irc)
|
||||||
irc.sd:close()
|
irc.sd:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function irc_readline(irc)
|
local function irc_readline(irc)
|
||||||
local line
|
local line
|
||||||
|
|
||||||
if next(irc.linebuf) then
|
if next(irc.linebuf) then
|
||||||
line = table.remove(irc.linebuf, 1)
|
line = table.remove(irc.linebuf, 1)
|
||||||
if string.match(line, "\r?\n$") then
|
if string.match(line, "\r?\n$") then
|
||||||
return line
|
return line
|
||||||
else
|
else
|
||||||
-- We had only half a line buffered.
|
-- We had only half a line buffered.
|
||||||
return line .. irc.buf()
|
return line .. irc.buf()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return irc.buf()
|
return irc.buf()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function irc_read_message(irc)
|
local function irc_read_message(irc)
|
||||||
local line, err
|
local line, err
|
||||||
|
|
||||||
line, err = irc_readline(irc)
|
line, err = irc_readline(irc)
|
||||||
if not line then
|
if not line then
|
||||||
return nil, err
|
return nil, err
|
||||||
end
|
end
|
||||||
|
|
||||||
return irc_parse_message(line)
|
return irc_parse_message(line)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function irc_send_message(irc, prefix, command, ...)
|
local function irc_send_message(irc, prefix, command, ...)
|
||||||
local line
|
local line
|
||||||
|
|
||||||
line = irc_compose_message(prefix, command, ...)
|
line = irc_compose_message(prefix, command, ...)
|
||||||
irc.sd:send(line)
|
irc.sd:send(line)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Prefix channel names with '#' if necessary and concatenate into a
|
-- Prefix channel names with '#' if necessary and concatenate into a
|
||||||
-- comma-separated list.
|
-- comma-separated list.
|
||||||
local function concat_channel_list(channels)
|
local function concat_channel_list(channels)
|
||||||
local mod = {}
|
local mod = {}
|
||||||
|
|
||||||
for _, channel in ipairs(channels) do
|
for _, channel in ipairs(channels) do
|
||||||
if not string.match(channel, "^#") then
|
if not string.match(channel, "^#") then
|
||||||
channel = "#" .. channel
|
channel = "#" .. channel
|
||||||
end
|
end
|
||||||
mod[#mod + 1] = channel
|
mod[#mod + 1] = channel
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.strjoin(",", mod)
|
return stdnse.strjoin(",", mod)
|
||||||
end
|
end
|
||||||
|
|
||||||
function action(host, port)
|
function action(host, port)
|
||||||
local irc
|
local irc
|
||||||
local search_channels
|
local search_channels
|
||||||
local channels
|
local channels
|
||||||
local errorparams
|
local errorparams
|
||||||
|
|
||||||
search_channels = stdnse.get_script_args(SCRIPT_NAME .. ".channels")
|
search_channels = stdnse.get_script_args(SCRIPT_NAME .. ".channels")
|
||||||
if not search_channels then
|
if not search_channels then
|
||||||
search_channels = DEFAULT_CHANNELS
|
search_channels = DEFAULT_CHANNELS
|
||||||
elseif type(search_channels) == "string" then
|
elseif type(search_channels) == "string" then
|
||||||
search_channels = {search_channels}
|
search_channels = {search_channels}
|
||||||
end
|
end
|
||||||
|
|
||||||
irc = irc_connect(host, port)
|
irc = irc_connect(host, port)
|
||||||
irc_send_message(irc, "LIST", concat_channel_list(search_channels))
|
irc_send_message(irc, "LIST", concat_channel_list(search_channels))
|
||||||
|
|
||||||
channels = {}
|
channels = {}
|
||||||
while true do
|
while true do
|
||||||
local status, prefix, code, params
|
local status, prefix, code, params
|
||||||
|
|
||||||
status, prefix, code, params = irc_read_message(irc)
|
status, prefix, code, params = irc_read_message(irc)
|
||||||
if not status then
|
if not status then
|
||||||
-- Error message from irc_read_message.
|
-- Error message from irc_read_message.
|
||||||
errorparams = {prefix}
|
errorparams = {prefix}
|
||||||
break
|
break
|
||||||
elseif code == "ERROR" then
|
elseif code == "ERROR" then
|
||||||
errorparams = params
|
errorparams = params
|
||||||
break
|
break
|
||||||
elseif code == RPL_TRYAGAIN then
|
elseif code == RPL_TRYAGAIN then
|
||||||
errorparams = params
|
errorparams = params
|
||||||
break
|
break
|
||||||
elseif code == RPL_LIST then
|
elseif code == RPL_LIST then
|
||||||
if #params >= 2 then
|
if #params >= 2 then
|
||||||
channels[#channels + 1] = params[2]
|
channels[#channels + 1] = params[2]
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Got short " .. RPL_LIST .. "response.")
|
stdnse.print_debug("Got short " .. RPL_LIST .. "response.")
|
||||||
end
|
end
|
||||||
elseif code == RPL_LISTEND then
|
elseif code == RPL_LISTEND then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
irc_disconnect(irc)
|
irc_disconnect(irc)
|
||||||
|
|
||||||
if errorparams then
|
if errorparams then
|
||||||
channels[#channels + 1] = "ERROR: " .. stdnse.strjoin(" ", errorparams)
|
channels[#channels + 1] = "ERROR: " .. stdnse.strjoin(" ", errorparams)
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, channels)
|
return stdnse.format_output(true, channels)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -56,235 +56,235 @@ portrule = shortport.port_or_service( 88, {"kerberos-sec"}, {"udp","tcp"}, {"ope
|
|||||||
-- of it.
|
-- of it.
|
||||||
KRB5 = {
|
KRB5 = {
|
||||||
|
|
||||||
-- Valid Kerberos message types
|
-- Valid Kerberos message types
|
||||||
MessageType = {
|
MessageType = {
|
||||||
['AS-REQ'] = 10,
|
['AS-REQ'] = 10,
|
||||||
['AS-REP'] = 11,
|
['AS-REP'] = 11,
|
||||||
['KRB-ERROR'] = 30,
|
['KRB-ERROR'] = 30,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- Some of the used error messages
|
-- Some of the used error messages
|
||||||
ErrorMessages = {
|
ErrorMessages = {
|
||||||
['KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN'] = 6,
|
['KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN'] = 6,
|
||||||
['KRB5KDC_ERR_PREAUTH_REQUIRED'] = 25,
|
['KRB5KDC_ERR_PREAUTH_REQUIRED'] = 25,
|
||||||
['KDC_ERR_WRONG_REALM'] = 68,
|
['KDC_ERR_WRONG_REALM'] = 68,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- A list of some ot the encryption types
|
-- A list of some ot the encryption types
|
||||||
EncryptionTypes = {
|
EncryptionTypes = {
|
||||||
{ ['aes256-cts-hmac-sha1-96'] = 18 },
|
{ ['aes256-cts-hmac-sha1-96'] = 18 },
|
||||||
{ ['aes128-cts-hmac-sha1-96'] = 17 },
|
{ ['aes128-cts-hmac-sha1-96'] = 17 },
|
||||||
{ ['des3-cbc-sha1'] = 16 },
|
{ ['des3-cbc-sha1'] = 16 },
|
||||||
{ ['rc4-hmac'] = 23 },
|
{ ['rc4-hmac'] = 23 },
|
||||||
-- { ['des-cbc-crc'] = 1 },
|
-- { ['des-cbc-crc'] = 1 },
|
||||||
-- { ['des-cbc-md5'] = 3 },
|
-- { ['des-cbc-md5'] = 3 },
|
||||||
-- { ['des-cbc-md4'] = 2 }
|
-- { ['des-cbc-md4'] = 2 }
|
||||||
},
|
},
|
||||||
|
|
||||||
-- A list of principal name types
|
-- A list of principal name types
|
||||||
NameTypes = {
|
NameTypes = {
|
||||||
['NT-PRINCIPAL'] = 1,
|
['NT-PRINCIPAL'] = 1,
|
||||||
['NT-SRV-INST'] = 2,
|
['NT-SRV-INST'] = 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- Creates a new Krb5 instance
|
-- Creates a new Krb5 instance
|
||||||
-- @return o as the new instance
|
-- @return o as the new instance
|
||||||
new = function(self)
|
new = function(self)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
-- A number of custom ASN1 decoders needed to decode the response
|
||||||
|
tagDecoder = {
|
||||||
|
|
||||||
|
["18"] = function( self, encStr, elen, pos )
|
||||||
|
return bin.unpack("A" .. elen, encStr, pos)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- A number of custom ASN1 decoders needed to decode the response
|
["1B"] = function( ... ) return KRB5.tagDecoder["18"](...) end,
|
||||||
tagDecoder = {
|
|
||||||
|
|
||||||
["18"] = function( self, encStr, elen, pos )
|
["6B"] = function( self, encStr, elen, pos )
|
||||||
return bin.unpack("A" .. elen, encStr, pos)
|
local seq
|
||||||
end,
|
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||||
|
return pos, seq
|
||||||
|
end,
|
||||||
|
|
||||||
["1B"] = function( ... ) return KRB5.tagDecoder["18"](...) end,
|
-- Not really sure what these are, but they all decode sequences
|
||||||
|
["7E"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A0"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A1"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A2"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A3"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A4"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A5"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A6"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A7"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A8"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["A9"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["AA"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
["AC"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
||||||
|
|
||||||
["6B"] = function( self, encStr, elen, pos )
|
},
|
||||||
local seq
|
|
||||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
|
||||||
return pos, seq
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Not really sure what these are, but they all decode sequences
|
-- A few Kerberos ASN1 encoders
|
||||||
["7E"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
tagEncoder = {
|
||||||
["A0"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A1"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A2"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A3"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A4"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A5"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A6"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A7"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A8"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["A9"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["AA"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
["AC"] = function( ... ) return KRB5.tagDecoder["6B"](...) end,
|
|
||||||
|
|
||||||
},
|
['table'] = function(self, val)
|
||||||
|
|
||||||
-- A few Kerberos ASN1 encoders
|
local types = {
|
||||||
tagEncoder = {
|
['GeneralizedTime'] = 0x18,
|
||||||
|
['GeneralString'] = 0x1B,
|
||||||
|
}
|
||||||
|
|
||||||
['table'] = function(self, val)
|
local len = asn1.ASN1Encoder.encodeLength(#val[1])
|
||||||
|
|
||||||
local types = {
|
if ( val._type and types[val._type] ) then
|
||||||
['GeneralizedTime'] = 0x18,
|
return bin.pack("CAA", types[val._type], len, val[1])
|
||||||
['GeneralString'] = 0x1B,
|
elseif ( val._type and 'number' == type(val._type) ) then
|
||||||
}
|
return bin.pack("CAA", val._type, len, val[1])
|
||||||
|
end
|
||||||
|
|
||||||
local len = asn1.ASN1Encoder.encodeLength(#val[1])
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
if ( val._type and types[val._type] ) then
|
-- Encodes a sequence using a custom type
|
||||||
return bin.pack("CAA", types[val._type], len, val[1])
|
-- @param encoder class containing an instance of a ASN1Encoder
|
||||||
elseif ( val._type and 'number' == type(val._type) ) then
|
-- @param seqtype number the sequence type to encode
|
||||||
return bin.pack("CAA", val._type, len, val[1])
|
-- @param seq string containing the sequence to encode
|
||||||
end
|
encodeSequence = function(self, encoder, seqtype, seq)
|
||||||
|
return encoder:encode( { _type = seqtype, seq } )
|
||||||
|
end,
|
||||||
|
|
||||||
end,
|
-- Encodes a Kerberos Principal
|
||||||
},
|
-- @param encoder class containing an instance of ASN1Encoder
|
||||||
|
-- @param name_type number containing a valid Kerberos name type
|
||||||
|
-- @param names table containing a list of names to encode
|
||||||
|
-- @return princ string containing an encoded principal
|
||||||
|
encodePrincipal = function(self, encoder, name_type, names )
|
||||||
|
local princ = ""
|
||||||
|
|
||||||
-- Encodes a sequence using a custom type
|
for _, n in ipairs(names) do
|
||||||
-- @param encoder class containing an instance of a ASN1Encoder
|
princ = princ .. encoder:encode( { _type = 'GeneralString', n } )
|
||||||
-- @param seqtype number the sequence type to encode
|
end
|
||||||
-- @param seq string containing the sequence to encode
|
|
||||||
encodeSequence = function(self, encoder, seqtype, seq)
|
|
||||||
return encoder:encode( { _type = seqtype, seq } )
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Encodes a Kerberos Principal
|
princ = self:encodeSequence(encoder, 0x30, princ)
|
||||||
-- @param encoder class containing an instance of ASN1Encoder
|
princ = self:encodeSequence(encoder, 0xa1, princ)
|
||||||
-- @param name_type number containing a valid Kerberos name type
|
princ = encoder:encode( name_type ) .. princ
|
||||||
-- @param names table containing a list of names to encode
|
|
||||||
-- @return princ string containing an encoded principal
|
|
||||||
encodePrincipal = function(self, encoder, name_type, names )
|
|
||||||
local princ = ""
|
|
||||||
|
|
||||||
for _, n in ipairs(names) do
|
-- not sure about how this works, but apparently it does
|
||||||
princ = princ .. encoder:encode( { _type = 'GeneralString', n } )
|
princ = bin.pack("H", "A003") .. princ
|
||||||
end
|
princ = self:encodeSequence(encoder,0x30, princ)
|
||||||
|
|
||||||
princ = self:encodeSequence(encoder, 0x30, princ)
|
return princ
|
||||||
princ = self:encodeSequence(encoder, 0xa1, princ)
|
end,
|
||||||
princ = encoder:encode( name_type ) .. princ
|
|
||||||
|
|
||||||
-- not sure about how this works, but apparently it does
|
-- Encodes the Kerberos AS-REQ request
|
||||||
princ = bin.pack("H", "A003") .. princ
|
-- @param realm string containing the Kerberos REALM
|
||||||
princ = self:encodeSequence(encoder,0x30, princ)
|
-- @param user string containing the Kerberos principal name
|
||||||
|
-- @param protocol string containing either of "tcp" or "udp"
|
||||||
|
-- @return data string containing the encoded request
|
||||||
|
encodeASREQ = function(self, realm, user, protocol)
|
||||||
|
|
||||||
return princ
|
assert(protocol == "tcp" or protocol == "udp",
|
||||||
end,
|
"Protocol has to be either \"tcp\" or \"udp\"")
|
||||||
|
|
||||||
-- Encodes the Kerberos AS-REQ request
|
local encoder = asn1.ASN1Encoder:new()
|
||||||
-- @param realm string containing the Kerberos REALM
|
encoder:registerTagEncoders(KRB5.tagEncoder)
|
||||||
-- @param user string containing the Kerberos principal name
|
|
||||||
-- @param protocol string containing either of "tcp" or "udp"
|
|
||||||
-- @return data string containing the encoded request
|
|
||||||
encodeASREQ = function(self, realm, user, protocol)
|
|
||||||
|
|
||||||
assert(protocol == "tcp" or protocol == "udp",
|
local data = ""
|
||||||
"Protocol has to be either \"tcp\" or \"udp\"")
|
|
||||||
|
|
||||||
local encoder = asn1.ASN1Encoder:new()
|
-- encode encryption types
|
||||||
encoder:registerTagEncoders(KRB5.tagEncoder)
|
for _,enctype in ipairs(KRB5.EncryptionTypes) do
|
||||||
|
for k, v in pairs( enctype ) do
|
||||||
|
data = data .. encoder:encode(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local data = ""
|
data = self:encodeSequence(encoder, 0x30, data )
|
||||||
|
data = self:encodeSequence(encoder, 0xA8, data )
|
||||||
|
|
||||||
-- encode encryption types
|
-- encode nonce
|
||||||
for _,enctype in ipairs(KRB5.EncryptionTypes) do
|
local nonce = 155874945
|
||||||
for k, v in pairs( enctype ) do
|
data = self:encodeSequence(encoder, 0xA7, encoder:encode(nonce) ) .. data
|
||||||
data = data .. encoder:encode(v)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
data = self:encodeSequence(encoder, 0x30, data )
|
-- encode from/to
|
||||||
data = self:encodeSequence(encoder, 0xA8, data )
|
local fromdate = os.time() + 10 * 60 * 60
|
||||||
|
local from = os.date("%Y%m%d%H%M%SZ", fromdate)
|
||||||
|
data = self:encodeSequence(encoder, 0xA5, encoder:encode( { from, _type='GeneralizedTime' })) .. data
|
||||||
|
|
||||||
-- encode nonce
|
local names = { "krbtgt", realm }
|
||||||
local nonce = 155874945
|
local sname = self:encodePrincipal( encoder, KRB5.NameTypes['NT-SRV-INST'], names )
|
||||||
data = self:encodeSequence(encoder, 0xA7, encoder:encode(nonce) ) .. data
|
sname = self:encodeSequence(encoder, 0xA3, sname)
|
||||||
|
data = sname .. data
|
||||||
|
|
||||||
-- encode from/to
|
-- realm
|
||||||
local fromdate = os.time() + 10 * 60 * 60
|
data = self:encodeSequence(encoder, 0xA2, encoder:encode( { _type = 'GeneralString', realm })) .. data
|
||||||
local from = os.date("%Y%m%d%H%M%SZ", fromdate)
|
|
||||||
data = self:encodeSequence(encoder, 0xA5, encoder:encode( { from, _type='GeneralizedTime' })) .. data
|
|
||||||
|
|
||||||
local names = { "krbtgt", realm }
|
local cname = self:encodePrincipal(encoder, KRB5.NameTypes['NT-PRINCIPAL'], { user })
|
||||||
local sname = self:encodePrincipal( encoder, KRB5.NameTypes['NT-SRV-INST'], names )
|
cname = self:encodeSequence(encoder, 0xA1, cname)
|
||||||
sname = self:encodeSequence(encoder, 0xA3, sname)
|
data = cname .. data
|
||||||
data = sname .. data
|
|
||||||
|
|
||||||
-- realm
|
-- forwardable
|
||||||
data = self:encodeSequence(encoder, 0xA2, encoder:encode( { _type = 'GeneralString', realm })) .. data
|
local kdc_options = 0x40000000
|
||||||
|
data = bin.pack(">I", kdc_options) .. data
|
||||||
|
|
||||||
local cname = self:encodePrincipal(encoder, KRB5.NameTypes['NT-PRINCIPAL'], { user })
|
-- add padding
|
||||||
cname = self:encodeSequence(encoder, 0xA1, cname)
|
data = bin.pack("C", 0) .. data
|
||||||
data = cname .. data
|
|
||||||
|
|
||||||
-- forwardable
|
-- hmm, wonder what this is
|
||||||
local kdc_options = 0x40000000
|
data = bin.pack("H", "A0070305") .. data
|
||||||
data = bin.pack(">I", kdc_options) .. data
|
data = self:encodeSequence(encoder, 0x30, data)
|
||||||
|
data = self:encodeSequence(encoder, 0xA4, data)
|
||||||
|
data = self:encodeSequence(encoder, 0xA2, encoder:encode(KRB5.MessageType['AS-REQ'])) .. data
|
||||||
|
|
||||||
-- add padding
|
local pvno = 5
|
||||||
data = bin.pack("C", 0) .. data
|
data = self:encodeSequence(encoder, 0xA1, encoder:encode(pvno) ) .. data
|
||||||
|
|
||||||
-- hmm, wonder what this is
|
data = self:encodeSequence(encoder, 0x30, data)
|
||||||
data = bin.pack("H", "A0070305") .. data
|
data = self:encodeSequence(encoder, 0x6a, data)
|
||||||
data = self:encodeSequence(encoder, 0x30, data)
|
|
||||||
data = self:encodeSequence(encoder, 0xA4, data)
|
|
||||||
data = self:encodeSequence(encoder, 0xA2, encoder:encode(KRB5.MessageType['AS-REQ'])) .. data
|
|
||||||
|
|
||||||
local pvno = 5
|
if ( protocol == "tcp" ) then
|
||||||
data = self:encodeSequence(encoder, 0xA1, encoder:encode(pvno) ) .. data
|
data = bin.pack(">I", #data) .. data
|
||||||
|
end
|
||||||
|
|
||||||
data = self:encodeSequence(encoder, 0x30, data)
|
return data
|
||||||
data = self:encodeSequence(encoder, 0x6a, data)
|
end,
|
||||||
|
|
||||||
if ( protocol == "tcp" ) then
|
-- Parses the result from the AS-REQ
|
||||||
data = bin.pack(">I", #data) .. data
|
-- @param data string containing the raw unparsed data
|
||||||
end
|
-- @return status boolean true on success, false on failure
|
||||||
|
-- @return msg table containing the fields <code>type</code> and
|
||||||
|
-- <code>error_code</code> if the type is an error.
|
||||||
|
parseResult = function(self, data)
|
||||||
|
|
||||||
return data
|
local decoder = asn1.ASN1Decoder:new()
|
||||||
end,
|
decoder:registerTagDecoders(KRB5.tagDecoder)
|
||||||
|
decoder:setStopOnError(true)
|
||||||
-- Parses the result from the AS-REQ
|
local pos, result = decoder:decode(data)
|
||||||
-- @param data string containing the raw unparsed data
|
local msg = {}
|
||||||
-- @return status boolean true on success, false on failure
|
|
||||||
-- @return msg table containing the fields <code>type</code> and
|
|
||||||
-- <code>error_code</code> if the type is an error.
|
|
||||||
parseResult = function(self, data)
|
|
||||||
|
|
||||||
local decoder = asn1.ASN1Decoder:new()
|
|
||||||
decoder:registerTagDecoders(KRB5.tagDecoder)
|
|
||||||
decoder:setStopOnError(true)
|
|
||||||
local pos, result = decoder:decode(data)
|
|
||||||
local msg = {}
|
|
||||||
|
|
||||||
|
|
||||||
if ( #result == 0 or #result[1] < 2 or #result[1][2] < 1 ) then
|
if ( #result == 0 or #result[1] < 2 or #result[1][2] < 1 ) then
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
msg.type = result[1][2][1]
|
msg.type = result[1][2][1]
|
||||||
|
|
||||||
if ( msg.type == KRB5.MessageType['KRB-ERROR'] ) then
|
if ( msg.type == KRB5.MessageType['KRB-ERROR'] ) then
|
||||||
if ( #result[1] < 5 and #result[1][5] < 1 ) then
|
if ( #result[1] < 5 and #result[1][5] < 1 ) then
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
msg.error_code = result[1][5][1]
|
msg.error_code = result[1][5][1]
|
||||||
return true, msg
|
return true, msg
|
||||||
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
|
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
|
||||||
return true, msg
|
return true, msg
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,42 +297,42 @@ KRB5 = {
|
|||||||
-- @return state VALID or INVALID or error message if status was false
|
-- @return state VALID or INVALID or error message if status was false
|
||||||
local function checkUser( host, port, realm, user )
|
local function checkUser( host, port, realm, user )
|
||||||
|
|
||||||
local krb5 = KRB5:new()
|
local krb5 = KRB5:new()
|
||||||
local data = krb5:encodeASREQ(realm, user, port.protocol)
|
local data = krb5:encodeASREQ(realm, user, port.protocol)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local status = socket:connect(host, port)
|
local status = socket:connect(host, port)
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "ERROR: Failed to connect to Kerberos service"
|
return false, "ERROR: Failed to connect to Kerberos service"
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:send(data)
|
socket:send(data)
|
||||||
status, data = socket:receive()
|
status, data = socket:receive()
|
||||||
|
|
||||||
if ( port.protocol == 'tcp' ) then data = data:sub(5) end
|
if ( port.protocol == 'tcp' ) then data = data:sub(5) end
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "ERROR: Failed to receive result from Kerberos service"
|
return false, "ERROR: Failed to receive result from Kerberos service"
|
||||||
end
|
end
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
local msg
|
local msg
|
||||||
status, msg = krb5:parseResult(data)
|
status, msg = krb5:parseResult(data)
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "ERROR: Failed to parse the result returned from the Kerberos service"
|
return false, "ERROR: Failed to parse the result returned from the Kerberos service"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( msg and msg.error_code ) then
|
if ( msg and msg.error_code ) then
|
||||||
if ( msg.error_code == KRB5.ErrorMessages['KRB5KDC_ERR_PREAUTH_REQUIRED'] ) then
|
if ( msg.error_code == KRB5.ErrorMessages['KRB5KDC_ERR_PREAUTH_REQUIRED'] ) then
|
||||||
return true, "VALID"
|
return true, "VALID"
|
||||||
elseif ( msg.error_code == KRB5.ErrorMessages['KDC_ERR_WRONG_REALM'] ) then
|
elseif ( msg.error_code == KRB5.ErrorMessages['KDC_ERR_WRONG_REALM'] ) then
|
||||||
return false, "Invalid Kerberos REALM"
|
return false, "Invalid Kerberos REALM"
|
||||||
end
|
end
|
||||||
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
|
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
|
||||||
return true, "VALID"
|
return true, "VALID"
|
||||||
end
|
end
|
||||||
return true, "INVALID"
|
return true, "INVALID"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Checks whether the Kerberos REALM exists or not
|
-- Checks whether the Kerberos REALM exists or not
|
||||||
@@ -341,7 +341,7 @@ end
|
|||||||
-- @param realm string containing the Kerberos REALM
|
-- @param realm string containing the Kerberos REALM
|
||||||
-- @return status boolean, true on success, false on failure
|
-- @return status boolean, true on success, false on failure
|
||||||
local function isValidRealm( host, port, realm )
|
local function isValidRealm( host, port, realm )
|
||||||
return checkUser( host, port, realm, "nmap")
|
return checkUser( host, port, realm, "nmap")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Wraps the checkUser function so that it is suitable to be called from
|
-- Wraps the checkUser function so that it is suitable to be called from
|
||||||
@@ -352,53 +352,53 @@ end
|
|||||||
-- @param user string containing the Kerberos principal
|
-- @param user string containing the Kerberos principal
|
||||||
-- @param result table to which all discovered users are added
|
-- @param result table to which all discovered users are added
|
||||||
local function checkUserThread( host, port, realm, user, result )
|
local function checkUserThread( host, port, realm, user, result )
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
local status, state = checkUser(host, port, realm, user)
|
local status, state = checkUser(host, port, realm, user)
|
||||||
if ( status and state == "VALID" ) then
|
if ( status and state == "VALID" ) then
|
||||||
table.insert(result, ("%s@%s"):format(user,realm))
|
table.insert(result, ("%s@%s"):format(user,realm))
|
||||||
end
|
end
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local realm = stdnse.get_script_args("krb5-enum-users.realm")
|
local realm = stdnse.get_script_args("krb5-enum-users.realm")
|
||||||
local result = {}
|
local result = {}
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
|
|
||||||
-- did the user supply a realm
|
-- did the user supply a realm
|
||||||
if ( not(realm) ) then
|
if ( not(realm) ) then
|
||||||
return "ERROR: No Kerberos REALM was supplied, aborting ..."
|
return "ERROR: No Kerberos REALM was supplied, aborting ..."
|
||||||
end
|
end
|
||||||
|
|
||||||
-- does the realm appear to exist
|
-- does the realm appear to exist
|
||||||
if ( not(isValidRealm(host, port, realm)) ) then
|
if ( not(isValidRealm(host, port, realm)) ) then
|
||||||
return "ERROR: Invalid Kerberos REALM, aborting ..."
|
return "ERROR: Invalid Kerberos REALM, aborting ..."
|
||||||
end
|
end
|
||||||
|
|
||||||
-- load our user database from unpwdb
|
-- load our user database from unpwdb
|
||||||
local status, usernames = unpwdb.usernames()
|
local status, usernames = unpwdb.usernames()
|
||||||
if( not(status) ) then return "ERROR: Failed to load unpwdb usernames" end
|
if( not(status) ) then return "ERROR: Failed to load unpwdb usernames" end
|
||||||
|
|
||||||
-- start as many threads as there are names in the list
|
-- start as many threads as there are names in the list
|
||||||
local threads = {}
|
local threads = {}
|
||||||
for user in usernames do
|
for user in usernames do
|
||||||
local co = stdnse.new_thread( checkUserThread, host, port, realm, user, result )
|
local co = stdnse.new_thread( checkUserThread, host, port, realm, user, result )
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- wait for all threads to finish up
|
-- wait for all threads to finish up
|
||||||
repeat
|
repeat
|
||||||
for t in pairs(threads) do
|
for t in pairs(threads) do
|
||||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until( next(threads) == nil )
|
until( next(threads) == nil )
|
||||||
|
|
||||||
if ( #result > 0 ) then
|
if ( #result > 0 ) then
|
||||||
result = { name = "Discovered Kerberos principals", result }
|
result = { name = "Discovered Kerberos principals", result }
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -98,25 +98,25 @@ portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"})
|
|||||||
-- @return string containing a valid naming context
|
-- @return string containing a valid naming context
|
||||||
function get_naming_context( socket )
|
function get_naming_context( socket )
|
||||||
|
|
||||||
local req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
|
local req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
|
||||||
local status, searchResEntries = ldap.searchRequest( socket, req )
|
local status, searchResEntries = ldap.searchRequest( socket, req )
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
|
local contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
|
||||||
|
|
||||||
-- OpenLDAP does not have a defaultNamingContext
|
-- OpenLDAP does not have a defaultNamingContext
|
||||||
if not contexts then
|
if not contexts then
|
||||||
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
|
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
|
||||||
end
|
end
|
||||||
|
|
||||||
if contexts and #contexts > 0 then
|
if contexts and #contexts > 0 then
|
||||||
return contexts[1]
|
return contexts[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Attempts to validate the credentials by requesting the base object of the supplied context
|
--- Attempts to validate the credentials by requesting the base object of the supplied context
|
||||||
@@ -125,194 +125,194 @@ end
|
|||||||
-- @param context string containing the context to search
|
-- @param context string containing the context to search
|
||||||
-- @return true if credentials are valid and search was a success, false if not.
|
-- @return true if credentials are valid and search was a success, false if not.
|
||||||
function is_valid_credential( socket, context )
|
function is_valid_credential( socket, context )
|
||||||
local req = { baseObject = context, scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = nil }
|
local req = { baseObject = context, scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = nil }
|
||||||
local status, searchResEntries = ldap.searchRequest( socket, req )
|
local status, searchResEntries = ldap.searchRequest( socket, req )
|
||||||
|
|
||||||
return status
|
return status
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local result, response, status, err, context, output, valid_accounts = {}, nil, nil, nil, nil, nil, {}
|
local result, response, status, err, context, output, valid_accounts = {}, nil, nil, nil, nil, nil, {}
|
||||||
local usernames, passwords, username, password, fq_username
|
local usernames, passwords, username, password, fq_username
|
||||||
local user_cnt, invalid_account_cnt, tot_tries = 0, 0, 0
|
local user_cnt, invalid_account_cnt, tot_tries = 0, 0, 0
|
||||||
|
|
||||||
local clock_start = nmap.clock_ms()
|
local clock_start = nmap.clock_ms()
|
||||||
|
|
||||||
local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 )
|
local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 )
|
||||||
local socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil )
|
local socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil )
|
||||||
|
|
||||||
local base_dn = stdnse.get_script_args('ldap.base')
|
local base_dn = stdnse.get_script_args('ldap.base')
|
||||||
local upn_suffix = stdnse.get_script_args('ldap.upnsuffix')
|
local upn_suffix = stdnse.get_script_args('ldap.upnsuffix')
|
||||||
|
|
||||||
local output_type = stdnse.get_script_args('ldap.savetype')
|
local output_type = stdnse.get_script_args('ldap.savetype')
|
||||||
|
|
||||||
local output_prefix = nil
|
local output_prefix = nil
|
||||||
if ( stdnse.get_script_args('ldap.saveprefix') ) then
|
if ( stdnse.get_script_args('ldap.saveprefix') ) then
|
||||||
output_prefix = stdnse.get_script_args('ldap.saveprefix')
|
output_prefix = stdnse.get_script_args('ldap.saveprefix')
|
||||||
elseif ( output_type ) then
|
elseif ( output_type ) then
|
||||||
output_prefix = "ldap-brute"
|
output_prefix = "ldap-brute"
|
||||||
end
|
end
|
||||||
|
|
||||||
local credTable = creds.Credentials:new(SCRIPT_NAME, host, port)
|
local credTable = creds.Credentials:new(SCRIPT_NAME, host, port)
|
||||||
|
|
||||||
if not socket then
|
if not socket then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We close and re-open the socket so that the anonymous bind does not distract us
|
-- We close and re-open the socket so that the anonymous bind does not distract us
|
||||||
socket:close()
|
socket:close()
|
||||||
-- set a reasonable timeout value
|
-- set a reasonable timeout value
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
status = socket:connect(host, port, opt)
|
status = socket:connect(host, port, opt)
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
context = get_naming_context(socket)
|
context = get_naming_context(socket)
|
||||||
|
|
||||||
if not context then
|
if not context then
|
||||||
stdnse.print_debug("Failed to retrieve namingContext")
|
stdnse.print_debug("Failed to retrieve namingContext")
|
||||||
socket:close()
|
socket:close()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
status, usernames = unpwdb.usernames()
|
status, usernames = unpwdb.usernames()
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
status, passwords = unpwdb.passwords()
|
status, passwords = unpwdb.passwords()
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
for username in usernames do
|
for username in usernames do
|
||||||
-- if a base DN was set append our username (CN) to the base
|
-- if a base DN was set append our username (CN) to the base
|
||||||
if base_dn then
|
if base_dn then
|
||||||
fq_username = ("cn=%s,%s"):format(username, base_dn)
|
fq_username = ("cn=%s,%s"):format(username, base_dn)
|
||||||
elseif upn_suffix then
|
elseif upn_suffix then
|
||||||
fq_username = ("%s@%s"):format(username, upn_suffix)
|
fq_username = ("%s@%s"):format(username, upn_suffix)
|
||||||
else
|
else
|
||||||
fq_username = username
|
fq_username = username
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
user_cnt = user_cnt + 1
|
user_cnt = user_cnt + 1
|
||||||
for password in passwords do
|
for password in passwords do
|
||||||
tot_tries = tot_tries + 1
|
tot_tries = tot_tries + 1
|
||||||
|
|
||||||
-- handle special case where we want to guess the username as password
|
-- handle special case where we want to guess the username as password
|
||||||
if password == "%username%" then
|
if password == "%username%" then
|
||||||
password = username
|
password = username
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug( "Trying %s/%s ...", fq_username, password )
|
stdnse.print_debug( "Trying %s/%s ...", fq_username, password )
|
||||||
status, response = ldap.bindRequest( socket, { version=3, ['username']=fq_username, ['password']=password} )
|
status, response = ldap.bindRequest( socket, { version=3, ['username']=fq_username, ['password']=password} )
|
||||||
|
|
||||||
-- if the DN (username) does not exist, break loop
|
-- if the DN (username) does not exist, break loop
|
||||||
if not status and response:match("invalid DN") then
|
if not status and response:match("invalid DN") then
|
||||||
stdnse.print_debug( "%s returned: \"Invalid DN\"", fq_username )
|
stdnse.print_debug( "%s returned: \"Invalid DN\"", fq_username )
|
||||||
invalid_account_cnt = invalid_account_cnt + 1
|
invalid_account_cnt = invalid_account_cnt + 1
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Is AD telling us the account does not exist?
|
-- Is AD telling us the account does not exist?
|
||||||
if not status and response:match("AcceptSecurityContext error, data 525,") then
|
if not status and response:match("AcceptSecurityContext error, data 525,") then
|
||||||
invalid_account_cnt = invalid_account_cnt + 1
|
invalid_account_cnt = invalid_account_cnt + 1
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Account Locked Out
|
-- Account Locked Out
|
||||||
if not status and response:match("AcceptSecurityContext error, data 775,") then
|
if not status and response:match("AcceptSecurityContext error, data 775,") then
|
||||||
table.insert( valid_accounts, string.format("%s => Valid credentials, account locked", fq_username ) )
|
table.insert( valid_accounts, string.format("%s => Valid credentials, account locked", fq_username ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s => Valid credentials, account locked", fq_username ))
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s => Valid credentials, account locked", fq_username ))
|
||||||
credTable:add(fq_username,password, creds.State.LOCKED_VALID)
|
credTable:add(fq_username,password, creds.State.LOCKED_VALID)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Login correct, account disabled
|
-- Login correct, account disabled
|
||||||
if not status and response:match("AcceptSecurityContext error, data 533,") then
|
if not status and response:match("AcceptSecurityContext error, data 533,") then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account disabled", fq_username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account disabled", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account disabled", fq_username, password:len()>0 and password or "<empty>" ))
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account disabled", fq_username, password:len()>0 and password or "<empty>" ))
|
||||||
credTable:add(fq_username,password, creds.State.DISABLED_VALID)
|
credTable:add(fq_username,password, creds.State.DISABLED_VALID)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Login correct, user must change password
|
-- Login correct, user must change password
|
||||||
if not status and response:match("AcceptSecurityContext error, data 773,") then
|
if not status and response:match("AcceptSecurityContext error, data 773,") then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, password must be changed at next logon", fq_username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, password must be changed at next logon", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, password must be changed at next logon", fq_username, password:len()>0 and password or "<empty>" ))
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, password must be changed at next logon", fq_username, password:len()>0 and password or "<empty>" ))
|
||||||
credTable:add(fq_username,password, creds.State.CHANGEPW)
|
credTable:add(fq_username,password, creds.State.CHANGEPW)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Login correct, user account expired
|
-- Login correct, user account expired
|
||||||
if not status and response:match("AcceptSecurityContext error, data 701,") then
|
if not status and response:match("AcceptSecurityContext error, data 701,") then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account expired", fq_username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account expired", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account expired", fq_username, password:len()>0 and password or "<empty>" ))
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account expired", fq_username, password:len()>0 and password or "<empty>" ))
|
||||||
credTable:add(fq_username,password, creds.State.EXPIRED)
|
credTable:add(fq_username,password, creds.State.EXPIRED)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Login correct, user account logon time restricted
|
-- Login correct, user account logon time restricted
|
||||||
if not status and response:match("AcceptSecurityContext error, data 530,") then
|
if not status and response:match("AcceptSecurityContext error, data 530,") then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>" ))
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>" ))
|
||||||
credTable:add(fq_username,password, creds.State.TIME_RESTRICTED)
|
credTable:add(fq_username,password, creds.State.TIME_RESTRICTED)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Login correct, user account can only log in from certain workstations
|
-- Login correct, user account can only log in from certain workstations
|
||||||
if not status and response:match("AcceptSecurityContext error, data 531,") then
|
if not status and response:match("AcceptSecurityContext error, data 531,") then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>" ))
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>" ))
|
||||||
credTable:add(fq_username,password, creds.State.HOST_RESTRICTED)
|
credTable:add(fq_username,password, creds.State.HOST_RESTRICTED)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
--Login, correct
|
--Login, correct
|
||||||
if status then
|
if status then
|
||||||
status = is_valid_credential( socket, context )
|
status = is_valid_credential( socket, context )
|
||||||
if status then
|
if status then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", fq_username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials", fq_username, password:len()>0 and password or "<empty>" ) )
|
stdnse.print_verbose(2, string.format(" ldap-brute: %s:%s => Valid credentials", fq_username, password:len()>0 and password or "<empty>" ) )
|
||||||
-- Add credentials for other ldap scripts to use
|
-- Add credentials for other ldap scripts to use
|
||||||
if nmap.registry.ldapaccounts == nil then
|
if nmap.registry.ldapaccounts == nil then
|
||||||
nmap.registry.ldapaccounts = {}
|
nmap.registry.ldapaccounts = {}
|
||||||
end
|
end
|
||||||
nmap.registry.ldapaccounts[fq_username]=password
|
nmap.registry.ldapaccounts[fq_username]=password
|
||||||
credTable:add(fq_username,password, creds.State.VALID)
|
credTable:add(fq_username,password, creds.State.VALID)
|
||||||
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
passwords("reset")
|
passwords("reset")
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug( "Finished brute against LDAP, total tries: %d, tps: %d", tot_tries, ( tot_tries / ( ( nmap.clock_ms() - clock_start ) / 1000 ) ) )
|
stdnse.print_debug( "Finished brute against LDAP, total tries: %d, tps: %d", tot_tries, ( tot_tries / ( ( nmap.clock_ms() - clock_start ) / 1000 ) ) )
|
||||||
|
|
||||||
if ( invalid_account_cnt == user_cnt and base_dn ~= nil ) then
|
if ( invalid_account_cnt == user_cnt and base_dn ~= nil ) then
|
||||||
return "WARNING: All usernames were invalid. Invalid LDAP base?"
|
return "WARNING: All usernames were invalid. Invalid LDAP base?"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if output_prefix then
|
if output_prefix then
|
||||||
local output_file = output_prefix .. "_" .. host.ip .. "_" .. port.number
|
local output_file = output_prefix .. "_" .. host.ip .. "_" .. port.number
|
||||||
status, err = credTable:saveToFile(output_file,output_type)
|
status, err = credTable:saveToFile(output_file,output_type)
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(err)
|
stdnse.print_debug(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if err then
|
if err then
|
||||||
output = stdnse.format_output(true, valid_accounts ) .. stdnse.format_output(true, err) or stdnse.format_output(true, err)
|
output = stdnse.format_output(true, valid_accounts ) .. stdnse.format_output(true, err) or stdnse.format_output(true, err)
|
||||||
else
|
else
|
||||||
output = stdnse.format_output(true, valid_accounts) or ""
|
output = stdnse.format_output(true, valid_accounts) or ""
|
||||||
end
|
end
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -103,198 +103,198 @@ portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"})
|
|||||||
|
|
||||||
function action(host,port)
|
function action(host,port)
|
||||||
|
|
||||||
local status
|
local status
|
||||||
local socket, opt
|
local socket, opt
|
||||||
local args = nmap.registry.args
|
local args = nmap.registry.args
|
||||||
local username = stdnse.get_script_args('ldap.username')
|
local username = stdnse.get_script_args('ldap.username')
|
||||||
local password = stdnse.get_script_args('ldap.password')
|
local password = stdnse.get_script_args('ldap.password')
|
||||||
local qfilter = stdnse.get_script_args('ldap.qfilter')
|
local qfilter = stdnse.get_script_args('ldap.qfilter')
|
||||||
local searchAttrib = stdnse.get_script_args('ldap.searchattrib')
|
local searchAttrib = stdnse.get_script_args('ldap.searchattrib')
|
||||||
local searchValue = stdnse.get_script_args('ldap.searchvalue')
|
local searchValue = stdnse.get_script_args('ldap.searchvalue')
|
||||||
local base = stdnse.get_script_args('ldap.base')
|
local base = stdnse.get_script_args('ldap.base')
|
||||||
local attribs = stdnse.get_script_args('ldap.attrib')
|
local attribs = stdnse.get_script_args('ldap.attrib')
|
||||||
local saveFile = stdnse.get_script_args('ldap.savesearch')
|
local saveFile = stdnse.get_script_args('ldap.savesearch')
|
||||||
local accounts
|
local accounts
|
||||||
local objCount = 0
|
local objCount = 0
|
||||||
local maxObjects = stdnse.get_script_args('ldap.maxobjects') and tonumber(stdnse.get_script_args('ldap.maxobjects')) or 20
|
local maxObjects = stdnse.get_script_args('ldap.maxobjects') and tonumber(stdnse.get_script_args('ldap.maxobjects')) or 20
|
||||||
|
|
||||||
-- In order to discover what protocol to use (SSL/TCP) we need to send a few bytes to the server
|
-- In order to discover what protocol to use (SSL/TCP) we need to send a few bytes to the server
|
||||||
-- An anonymous bind should do it
|
-- An anonymous bind should do it
|
||||||
local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 )
|
local ldap_anonymous_bind = string.char( 0x30, 0x0c, 0x02, 0x01, 0x01, 0x60, 0x07, 0x02, 0x01, 0x03, 0x04, 0x00, 0x80, 0x00 )
|
||||||
local _
|
local _
|
||||||
socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil )
|
socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil )
|
||||||
|
|
||||||
if not socket then
|
if not socket then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if ldap-brute stored us some credentials
|
-- Check if ldap-brute stored us some credentials
|
||||||
if ( not(username) and nmap.registry.ldapaccounts~=nil ) then
|
if ( not(username) and nmap.registry.ldapaccounts~=nil ) then
|
||||||
accounts = nmap.registry.ldapaccounts
|
accounts = nmap.registry.ldapaccounts
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We close and re-open the socket so that the anonymous bind does not distract us
|
-- We close and re-open the socket so that the anonymous bind does not distract us
|
||||||
socket:close()
|
socket:close()
|
||||||
status = socket:connect(host, port, opt)
|
status = socket:connect(host, port, opt)
|
||||||
socket:set_timeout(10000)
|
socket:set_timeout(10000)
|
||||||
|
|
||||||
local req
|
local req
|
||||||
local searchResEntries
|
local searchResEntries
|
||||||
local contexts = {}
|
local contexts = {}
|
||||||
local result = {}
|
local result = {}
|
||||||
local filter
|
local filter
|
||||||
|
|
||||||
if base == nil then
|
if base == nil then
|
||||||
req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
|
req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
|
||||||
status, searchResEntries = ldap.searchRequest( socket, req )
|
status, searchResEntries = ldap.searchRequest( socket, req )
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
socket:close()
|
socket:close()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
|
contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
|
||||||
|
|
||||||
-- OpenLDAP does not have a defaultNamingContext
|
-- OpenLDAP does not have a defaultNamingContext
|
||||||
if not contexts then
|
if not contexts then
|
||||||
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
|
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(contexts, base)
|
table.insert(contexts, base)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(contexts) or #contexts == 0 ) then
|
if ( not(contexts) or #contexts == 0 ) then
|
||||||
stdnse.print_debug( "Failed to retrieve namingContexts" )
|
stdnse.print_debug( "Failed to retrieve namingContexts" )
|
||||||
contexts = {""}
|
contexts = {""}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- perform a bind only if we have valid credentials
|
-- perform a bind only if we have valid credentials
|
||||||
if ( username ) then
|
if ( username ) then
|
||||||
local bindParam = { version=3, ['username']=username, ['password']=password}
|
local bindParam = { version=3, ['username']=username, ['password']=password}
|
||||||
local status, errmsg = ldap.bindRequest( socket, bindParam )
|
local status, errmsg = ldap.bindRequest( socket, bindParam )
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug( string.format("ldap-search failed to bind: %s", errmsg) )
|
stdnse.print_debug( string.format("ldap-search failed to bind: %s", errmsg) )
|
||||||
return " \n ERROR: Authentication failed"
|
return " \n ERROR: Authentication failed"
|
||||||
end
|
end
|
||||||
-- or if ldap-brute found us something
|
-- or if ldap-brute found us something
|
||||||
elseif ( accounts ) then
|
elseif ( accounts ) then
|
||||||
for username, password in pairs(accounts) do
|
for username, password in pairs(accounts) do
|
||||||
local bindParam = { version=3, ['username']=username, ['password']=password}
|
local bindParam = { version=3, ['username']=username, ['password']=password}
|
||||||
local status, errmsg = ldap.bindRequest( socket, bindParam )
|
local status, errmsg = ldap.bindRequest( socket, bindParam )
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if qfilter == "users" then
|
if qfilter == "users" then
|
||||||
filter = { op=ldap.FILTER._or, val=
|
filter = { op=ldap.FILTER._or, val=
|
||||||
{
|
{
|
||||||
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='user' },
|
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='user' },
|
||||||
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='posixAccount' },
|
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='posixAccount' },
|
||||||
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='person' }
|
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='person' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elseif qfilter == "computers" or qfilter == "computer" then
|
elseif qfilter == "computers" or qfilter == "computer" then
|
||||||
filter = { op=ldap.FILTER.equalityMatch, obj='objectClass', val='computer' }
|
filter = { op=ldap.FILTER.equalityMatch, obj='objectClass', val='computer' }
|
||||||
|
|
||||||
elseif qfilter == "ad_dcs" then
|
elseif qfilter == "ad_dcs" then
|
||||||
filter = { op=ldap.FILTER.extensibleMatch, obj='userAccountControl', val='1.2.840.113556.1.4.803:=8192' }
|
filter = { op=ldap.FILTER.extensibleMatch, obj='userAccountControl', val='1.2.840.113556.1.4.803:=8192' }
|
||||||
|
|
||||||
elseif qfilter == "custom" then
|
elseif qfilter == "custom" then
|
||||||
if searchAttrib == nil or searchValue == nil then
|
if searchAttrib == nil or searchValue == nil then
|
||||||
return "\n\nERROR: Please specify both ldap.searchAttrib and ldap.searchValue using using the custom qfilter."
|
return "\n\nERROR: Please specify both ldap.searchAttrib and ldap.searchValue using using the custom qfilter."
|
||||||
end
|
end
|
||||||
if string.find(searchValue, '*') == nil then
|
if string.find(searchValue, '*') == nil then
|
||||||
filter = { op=ldap.FILTER.equalityMatch, obj=searchAttrib, val=searchValue }
|
filter = { op=ldap.FILTER.equalityMatch, obj=searchAttrib, val=searchValue }
|
||||||
else
|
else
|
||||||
filter = { op=ldap.FILTER.substrings, obj=searchAttrib, val=searchValue }
|
filter = { op=ldap.FILTER.substrings, obj=searchAttrib, val=searchValue }
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif qfilter == "all" or qfilter == nil then
|
elseif qfilter == "all" or qfilter == nil then
|
||||||
filter = nil -- { op=ldap.FILTER}
|
filter = nil -- { op=ldap.FILTER}
|
||||||
else
|
else
|
||||||
return " \n\nERROR: Unsupported Quick Filter: " .. qfilter
|
return " \n\nERROR: Unsupported Quick Filter: " .. qfilter
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(attribs) == 'string' then
|
if type(attribs) == 'string' then
|
||||||
local tmp = attribs
|
local tmp = attribs
|
||||||
attribs = {}
|
attribs = {}
|
||||||
table.insert(attribs, tmp)
|
table.insert(attribs, tmp)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, context in ipairs(contexts) do
|
for _, context in ipairs(contexts) do
|
||||||
|
|
||||||
req = {
|
req = {
|
||||||
baseObject = context,
|
baseObject = context,
|
||||||
scope = ldap.SCOPE.sub,
|
scope = ldap.SCOPE.sub,
|
||||||
derefPolicy = ldap.DEREFPOLICY.default,
|
derefPolicy = ldap.DEREFPOLICY.default,
|
||||||
filter = filter,
|
filter = filter,
|
||||||
attributes = attribs,
|
attributes = attribs,
|
||||||
['maxObjects'] = maxObjects }
|
['maxObjects'] = maxObjects }
|
||||||
status, searchResEntries = ldap.searchRequest( socket, req )
|
status, searchResEntries = ldap.searchRequest( socket, req )
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
if ( searchResEntries:match("DSID[-]0C090627") and not(username) ) then
|
if ( searchResEntries:match("DSID[-]0C090627") and not(username) ) then
|
||||||
return "ERROR: Failed to bind as the anonymous user"
|
return "ERROR: Failed to bind as the anonymous user"
|
||||||
else
|
else
|
||||||
stdnse.print_debug( string.format( "ldap.searchRequest returned: %s", searchResEntries ) )
|
stdnse.print_debug( string.format( "ldap.searchRequest returned: %s", searchResEntries ) )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local result_part = ldap.searchResultToTable( searchResEntries )
|
local result_part = ldap.searchResultToTable( searchResEntries )
|
||||||
|
|
||||||
if saveFile then
|
if saveFile then
|
||||||
local output_file = saveFile .. "_" .. host.ip .. "_" .. port.number .. ".csv"
|
local output_file = saveFile .. "_" .. host.ip .. "_" .. port.number .. ".csv"
|
||||||
local save_status, save_err = ldap.searchResultToFile(searchResEntries,output_file)
|
local save_status, save_err = ldap.searchResultToFile(searchResEntries,output_file)
|
||||||
if not save_status then
|
if not save_status then
|
||||||
stdnse.print_debug(save_err)
|
stdnse.print_debug(save_err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
objCount = objCount + (result_part and #result_part or 0)
|
objCount = objCount + (result_part and #result_part or 0)
|
||||||
result_part.name = ""
|
result_part.name = ""
|
||||||
|
|
||||||
if ( context ) then
|
if ( context ) then
|
||||||
result_part.name = ("Context: %s"):format(#context > 0 and context or "<empty>")
|
result_part.name = ("Context: %s"):format(#context > 0 and context or "<empty>")
|
||||||
end
|
end
|
||||||
if ( qfilter ) then
|
if ( qfilter ) then
|
||||||
result_part.name = result_part.name .. ("; QFilter: %s"):format(qfilter)
|
result_part.name = result_part.name .. ("; QFilter: %s"):format(qfilter)
|
||||||
end
|
end
|
||||||
if ( attribs ) then
|
if ( attribs ) then
|
||||||
result_part.name = result_part.name .. ("; Attributes: %s"):format(stdnse.strjoin(",", attribs))
|
result_part.name = result_part.name .. ("; Attributes: %s"):format(stdnse.strjoin(",", attribs))
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert( result, result_part )
|
table.insert( result, result_part )
|
||||||
|
|
||||||
-- catch any softerrors
|
-- catch any softerrors
|
||||||
if searchResEntries.resultCode ~= 0 then
|
if searchResEntries.resultCode ~= 0 then
|
||||||
local output = stdnse.format_output(true, result )
|
local output = stdnse.format_output(true, result )
|
||||||
output = output .. string.format("\n\n\n=========== %s ===========", searchResEntries.errorMessage )
|
output = output .. string.format("\n\n\n=========== %s ===========", searchResEntries.errorMessage )
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- perform a unbind only if we have valid credentials
|
-- perform a unbind only if we have valid credentials
|
||||||
if ( username ) then
|
if ( username ) then
|
||||||
status = ldap.unbindRequest( socket )
|
status = ldap.unbindRequest( socket )
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
-- if taken a way and ldap returns a single result, it ain't shown....
|
-- if taken a way and ldap returns a single result, it ain't shown....
|
||||||
--result.name = "LDAP Results"
|
--result.name = "LDAP Results"
|
||||||
|
|
||||||
local output = stdnse.format_output(true, result )
|
local output = stdnse.format_output(true, result )
|
||||||
|
|
||||||
if ( maxObjects ~= -1 and objCount == maxObjects ) then
|
if ( maxObjects ~= -1 and objCount == maxObjects ) then
|
||||||
output = output .. ("\n\nResult limited to %d objects (see ldap.maxobjects)"):format(maxObjects)
|
output = output .. ("\n\nResult limited to %d objects (see ldap.maxobjects)"):format(maxObjects)
|
||||||
end
|
end
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -45,296 +45,296 @@ categories = {"broadcast","discovery","safe"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
if not nmap.registry[SCRIPT_NAME].rootfail then
|
if not nmap.registry[SCRIPT_NAME].rootfail then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Converts a 6 byte string into the familiar MAC address formatting
|
--- Converts a 6 byte string into the familiar MAC address formatting
|
||||||
-- @param mac string containing the MAC address
|
-- @param mac string containing the MAC address
|
||||||
-- @return formatted string suitable for printing
|
-- @return formatted string suitable for printing
|
||||||
local function get_mac_addr( mac )
|
local function get_mac_addr( mac )
|
||||||
local catch = function() return end
|
local catch = function() return end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
||||||
|
|
||||||
if mac:len() ~= 6 then
|
if mac:len() ~= 6 then
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
else
|
else
|
||||||
local prefix = string.upper(string.format("%02x%02x%02x", mac:byte(1), mac:byte(2), mac:byte(3)))
|
local prefix = string.upper(string.format("%02x%02x%02x", mac:byte(1), mac:byte(2), mac:byte(3)))
|
||||||
local manuf = mac_prefixes[prefix] or "Unknown"
|
local manuf = mac_prefixes[prefix] or "Unknown"
|
||||||
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
|
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets a raw ethernet buffer with LLTD information and returns the responding host's IP and MAC
|
--- Gets a raw ethernet buffer with LLTD information and returns the responding host's IP and MAC
|
||||||
local parseHello = function(data)
|
local parseHello = function(data)
|
||||||
-- HelloMsg = [
|
-- HelloMsg = [
|
||||||
-- ethernet_hdr = [mac_dst(6), mac_src(6), protocol(2)],
|
-- ethernet_hdr = [mac_dst(6), mac_src(6), protocol(2)],
|
||||||
-- lltd_demultiplex_hdr = [version(1), type_of_service(1), reserved(1), function(1)],
|
-- lltd_demultiplex_hdr = [version(1), type_of_service(1), reserved(1), function(1)],
|
||||||
-- base_hdr = [mac_dst(6), mac_src(6), seq_no(2)],
|
-- base_hdr = [mac_dst(6), mac_src(6), seq_no(2)],
|
||||||
-- up_hello_hdr = [ generation_number(2), current_mapper_address(6), apparent_mapper_address(6), tlv_list(var) ]
|
-- up_hello_hdr = [ generation_number(2), current_mapper_address(6), apparent_mapper_address(6), tlv_list(var) ]
|
||||||
--]
|
--]
|
||||||
|
|
||||||
--HelloStruct = {
|
--HelloStruct = {
|
||||||
-- mac_src,
|
-- mac_src,
|
||||||
-- sequence_number,
|
-- sequence_number,
|
||||||
-- generation_number,
|
-- generation_number,
|
||||||
-- tlv_list(dict)
|
-- tlv_list(dict)
|
||||||
--}
|
--}
|
||||||
local types = {"Host ID", "Characteristics", "Physical Medium", "Wireless Mode", "802.11 BSSID",
|
local types = {"Host ID", "Characteristics", "Physical Medium", "Wireless Mode", "802.11 BSSID",
|
||||||
"802.11 SSID", "IPv4 Address", "IPv6 Address", "802.11 Max Operational Rate",
|
"802.11 SSID", "IPv4 Address", "IPv6 Address", "802.11 Max Operational Rate",
|
||||||
"Performance Counter Frequency", nil, "Link Speed", "802.11 RSSI", "Icon Image", "Machine Name",
|
"Performance Counter Frequency", nil, "Link Speed", "802.11 RSSI", "Icon Image", "Machine Name",
|
||||||
"Support Information", "Friendly Name", "Device UUID", "Hardware ID", "QoS Characteristics",
|
"Support Information", "Friendly Name", "Device UUID", "Hardware ID", "QoS Characteristics",
|
||||||
"802.11 Physical Medium", "AP Association Table", "Detailed Icon Image", "Sees-List Working Set",
|
"802.11 Physical Medium", "AP Association Table", "Detailed Icon Image", "Sees-List Working Set",
|
||||||
"Component Table", "Repeater AP Lineage", "Repeater AP Table"}
|
"Component Table", "Repeater AP Lineage", "Repeater AP Table"}
|
||||||
local mac = nil
|
local mac = nil
|
||||||
local ipv4 = nil
|
local ipv4 = nil
|
||||||
local ipv6 = nil
|
local ipv6 = nil
|
||||||
local hostname = nil
|
local hostname = nil
|
||||||
|
|
||||||
local pos = 1
|
local pos = 1
|
||||||
pos = pos + 6
|
pos = pos + 6
|
||||||
local mac_src = data:sub(pos,pos+5)
|
local mac_src = data:sub(pos,pos+5)
|
||||||
|
|
||||||
pos = pos + 24
|
pos = pos + 24
|
||||||
local seq_no = data:sub(pos,pos+1)
|
local seq_no = data:sub(pos,pos+1)
|
||||||
|
|
||||||
pos = pos + 2
|
pos = pos + 2
|
||||||
local generation_no = data:sub(pos,pos+1)
|
local generation_no = data:sub(pos,pos+1)
|
||||||
|
|
||||||
pos = pos + 14
|
pos = pos + 14
|
||||||
local tlv = data:sub(pos)
|
local tlv = data:sub(pos)
|
||||||
|
|
||||||
local tlv_list = {}
|
local tlv_list = {}
|
||||||
local p = 1
|
local p = 1
|
||||||
while p < #tlv do
|
while p < #tlv do
|
||||||
local t = tlv:byte(p)
|
local t = tlv:byte(p)
|
||||||
if t == 0x00 then
|
if t == 0x00 then
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
p = p + 1
|
p = p + 1
|
||||||
local l = tlv:byte(p)
|
local l = tlv:byte(p)
|
||||||
|
|
||||||
p = p + 1
|
p = p + 1
|
||||||
local v = tlv:sub(p,p+l)
|
local v = tlv:sub(p,p+l)
|
||||||
|
|
||||||
if t == 0x01 then
|
if t == 0x01 then
|
||||||
-- Host ID (MAC Address)
|
-- Host ID (MAC Address)
|
||||||
mac = get_mac_addr(v:sub(1,6))
|
mac = get_mac_addr(v:sub(1,6))
|
||||||
elseif t == 0x08 then
|
elseif t == 0x08 then
|
||||||
ipv6 = string.format(
|
ipv6 = string.format(
|
||||||
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
|
||||||
v:byte(1), v:byte(2), v:byte(3), v:byte(4),
|
v:byte(1), v:byte(2), v:byte(3), v:byte(4),
|
||||||
v:byte(5), v:byte(6), v:byte(7), v:byte(8),
|
v:byte(5), v:byte(6), v:byte(7), v:byte(8),
|
||||||
v:byte(9), v:byte(10), v:byte(11), v:byte(12),
|
v:byte(9), v:byte(10), v:byte(11), v:byte(12),
|
||||||
v:byte(13), v:byte(14), v:byte(15), v:byte(16))
|
v:byte(13), v:byte(14), v:byte(15), v:byte(16))
|
||||||
elseif t == 0x07 then
|
elseif t == 0x07 then
|
||||||
-- IPv4 address
|
-- IPv4 address
|
||||||
ipv4 = string.format("%d.%d.%d.%d",v:byte(1),v:byte(2),v:byte(3),v:byte(4)), mac
|
ipv4 = string.format("%d.%d.%d.%d",v:byte(1),v:byte(2),v:byte(3),v:byte(4)), mac
|
||||||
|
|
||||||
-- Machine Name (Hostname)
|
-- Machine Name (Hostname)
|
||||||
elseif t == 0x0f then
|
elseif t == 0x0f then
|
||||||
hostname = ''
|
hostname = ''
|
||||||
-- Hostname is returned in unicode, but Lua doesn't support that,
|
-- Hostname is returned in unicode, but Lua doesn't support that,
|
||||||
-- so we skip 00 values.
|
-- so we skip 00 values.
|
||||||
for i=1, #v-1, 2 do
|
for i=1, #v-1, 2 do
|
||||||
hostname = hostname .. string.char(v:byte(i))
|
hostname = hostname .. string.char(v:byte(i))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
p = p + l
|
p = p + l
|
||||||
|
|
||||||
if ipv4 and ipv6 and mac and hostname then
|
if ipv4 and ipv6 and mac and hostname then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ipv4, mac, ipv6, hostname
|
return ipv4, mac, ipv6, hostname
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates an LLTD Quick Discovery packet with the source MAC address
|
--- Creates an LLTD Quick Discovery packet with the source MAC address
|
||||||
-- @param mac_src - six byte long binary string
|
-- @param mac_src - six byte long binary string
|
||||||
local QuickDiscoveryPacket = function(mac_src)
|
local QuickDiscoveryPacket = function(mac_src)
|
||||||
local ethernet_hdr, demultiplex_hdr, base_hdr, discover_up_lev_hdr
|
local ethernet_hdr, demultiplex_hdr, base_hdr, discover_up_lev_hdr
|
||||||
|
|
||||||
-- set up ethernet header = [ mac_dst, mac_src, protocol ]
|
-- set up ethernet header = [ mac_dst, mac_src, protocol ]
|
||||||
local mac_dst = "FF FF FF FF FF FF" -- broadcast
|
local mac_dst = "FF FF FF FF FF FF" -- broadcast
|
||||||
local protocol = "88 d9" -- LLTD protocol number
|
local protocol = "88 d9" -- LLTD protocol number
|
||||||
|
|
||||||
ethernet_hdr = bin.pack("HAH",mac_dst, mac_src, protocol)
|
ethernet_hdr = bin.pack("HAH",mac_dst, mac_src, protocol)
|
||||||
|
|
||||||
-- set up LLTD demultiplex header = [ version, type_of_service, reserved, function ]
|
-- set up LLTD demultiplex header = [ version, type_of_service, reserved, function ]
|
||||||
local lltd_version = "01" -- Fixed Value
|
local lltd_version = "01" -- Fixed Value
|
||||||
local lltd_type_of_service = "01" -- Type Of Service = Quick Discovery(0x01)
|
local lltd_type_of_service = "01" -- Type Of Service = Quick Discovery(0x01)
|
||||||
local lltd_reserved = "00" -- Fixed value
|
local lltd_reserved = "00" -- Fixed value
|
||||||
local lltd_function = "00" -- Function = QuickDiscovery->Discover (0x00)
|
local lltd_function = "00" -- Function = QuickDiscovery->Discover (0x00)
|
||||||
|
|
||||||
demultiplex_hdr = bin.pack("HHHH", lltd_version, lltd_type_of_service, lltd_reserved, lltd_function )
|
demultiplex_hdr = bin.pack("HHHH", lltd_version, lltd_type_of_service, lltd_reserved, lltd_function )
|
||||||
|
|
||||||
-- set up LLTD base header = [ mac_dst, mac_src, seq_num(xid) ]
|
-- set up LLTD base header = [ mac_dst, mac_src, seq_num(xid) ]
|
||||||
local lltd_seq_num = openssl.rand_bytes(2)
|
local lltd_seq_num = openssl.rand_bytes(2)
|
||||||
|
|
||||||
base_hdr = bin.pack("HHA", mac_dst, mac_src, lltd_seq_num)
|
base_hdr = bin.pack("HHA", mac_dst, mac_src, lltd_seq_num)
|
||||||
|
|
||||||
-- set up LLTD Upper Level Header = [ generation_number, number_of_stations, station_list ]
|
-- set up LLTD Upper Level Header = [ generation_number, number_of_stations, station_list ]
|
||||||
local generation_number = openssl.rand_bytes(2)
|
local generation_number = openssl.rand_bytes(2)
|
||||||
local number_of_stations = "00 00"
|
local number_of_stations = "00 00"
|
||||||
local station_list = "00 00 00 00 00 00 " .. "00 00 00 00 00 00 " ..
|
local station_list = "00 00 00 00 00 00 " .. "00 00 00 00 00 00 " ..
|
||||||
"00 00 00 00 00 00 " .."00 00 00 00 00 00 "
|
"00 00 00 00 00 00 " .."00 00 00 00 00 00 "
|
||||||
|
|
||||||
discover_up_lev_hdr = bin.pack("AHH", generation_number, number_of_stations, station_list)
|
discover_up_lev_hdr = bin.pack("AHH", generation_number, number_of_stations, station_list)
|
||||||
|
|
||||||
-- put them all together and return
|
-- put them all together and return
|
||||||
return bin.pack("AAAA", ethernet_hdr, demultiplex_hdr, base_hdr, discover_up_lev_hdr)
|
return bin.pack("AAAA", ethernet_hdr, demultiplex_hdr, base_hdr, discover_up_lev_hdr)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Runs a thread which discovers LLTD Responders on a certain interface
|
--- Runs a thread which discovers LLTD Responders on a certain interface
|
||||||
local LLTDDiscover = function(if_table, lltd_responders, timeout)
|
local LLTDDiscover = function(if_table, lltd_responders, timeout)
|
||||||
local timeout_s = 3
|
local timeout_s = 3
|
||||||
local condvar = nmap.condvar(lltd_responders)
|
local condvar = nmap.condvar(lltd_responders)
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
pcap:set_timeout(5000)
|
pcap:set_timeout(5000)
|
||||||
|
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
local try = nmap.new_try(function() dnet:ethernet_close() pcap:close() end)
|
local try = nmap.new_try(function() dnet:ethernet_close() pcap:close() end)
|
||||||
|
|
||||||
pcap:pcap_open(if_table.device, 256, false, "")
|
pcap:pcap_open(if_table.device, 256, false, "")
|
||||||
try(dnet:ethernet_open(if_table.device))
|
try(dnet:ethernet_open(if_table.device))
|
||||||
|
|
||||||
local packet = QuickDiscoveryPacket(if_table.mac)
|
local packet = QuickDiscoveryPacket(if_table.mac)
|
||||||
try( dnet:ethernet_send(packet) )
|
try( dnet:ethernet_send(packet) )
|
||||||
stdnse.sleep(0.5)
|
stdnse.sleep(0.5)
|
||||||
try( dnet:ethernet_send(packet) )
|
try( dnet:ethernet_send(packet) )
|
||||||
|
|
||||||
local start = os.time()
|
local start = os.time()
|
||||||
local start_s = os.time()
|
local start_s = os.time()
|
||||||
while true do
|
while true do
|
||||||
local status, plen, l2, l3, _ = pcap:pcap_receive()
|
local status, plen, l2, l3, _ = pcap:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
local packet = l2..l3
|
local packet = l2..l3
|
||||||
if stdnse.tohex(packet:sub(13,14)) == "88d9" then
|
if stdnse.tohex(packet:sub(13,14)) == "88d9" then
|
||||||
start_s = os.time()
|
start_s = os.time()
|
||||||
|
|
||||||
local ipv4, mac, ipv6, hostname = parseHello(packet)
|
local ipv4, mac, ipv6, hostname = parseHello(packet)
|
||||||
|
|
||||||
if ipv4 then
|
if ipv4 then
|
||||||
if not lltd_responders[ipv4] then
|
if not lltd_responders[ipv4] then
|
||||||
lltd_responders[ipv4] = {}
|
lltd_responders[ipv4] = {}
|
||||||
lltd_responders[ipv4].hostname = hostname
|
lltd_responders[ipv4].hostname = hostname
|
||||||
lltd_responders[ipv4].mac = mac
|
lltd_responders[ipv4].mac = mac
|
||||||
lltd_responders[ipv4].ipv6 = ipv6
|
lltd_responders[ipv4].ipv6 = ipv6
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if os.time() - start_s > timeout_s then
|
if os.time() - start_s > timeout_s then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if os.time() - start > timeout then
|
if os.time() - start > timeout then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
dnet:ethernet_close()
|
dnet:ethernet_close()
|
||||||
pcap:close()
|
pcap:close()
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||||
timeout = timeout or 30
|
timeout = timeout or 30
|
||||||
|
|
||||||
--get interface script-args, if any
|
--get interface script-args, if any
|
||||||
local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
local interface_opt = nmap.get_interface()
|
local interface_opt = nmap.get_interface()
|
||||||
|
|
||||||
-- interfaces list (decide which interfaces to broadcast on)
|
-- interfaces list (decide which interfaces to broadcast on)
|
||||||
local interfaces ={}
|
local interfaces ={}
|
||||||
if interface_opt or interface_arg then
|
if interface_opt or interface_arg then
|
||||||
-- single interface defined
|
-- single interface defined
|
||||||
local interface = interface_opt or interface_arg
|
local interface = interface_opt or interface_arg
|
||||||
local if_table = nmap.get_interface_info(interface)
|
local if_table = nmap.get_interface_info(interface)
|
||||||
if not if_table or not if_table.address or not if_table.link=="ethernet" then
|
if not if_table or not if_table.address or not if_table.link=="ethernet" then
|
||||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
table.insert(interfaces, if_table)
|
table.insert(interfaces, if_table)
|
||||||
else
|
else
|
||||||
local tmp_ifaces = nmap.list_interfaces()
|
local tmp_ifaces = nmap.list_interfaces()
|
||||||
for _, if_table in ipairs(tmp_ifaces) do
|
for _, if_table in ipairs(tmp_ifaces) do
|
||||||
if if_table.address and
|
if if_table.address and
|
||||||
if_table.link=="ethernet" and
|
if_table.link=="ethernet" and
|
||||||
if_table.address:match("%d+%.%d+%.%d+%.%d+") then
|
if_table.address:match("%d+%.%d+%.%d+%.%d+") then
|
||||||
|
|
||||||
table.insert(interfaces, if_table)
|
table.insert(interfaces, if_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #interfaces == 0 then
|
if #interfaces == 0 then
|
||||||
stdnse.print_debug("No interfaces found.")
|
stdnse.print_debug("No interfaces found.")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local lltd_responders={}
|
local lltd_responders={}
|
||||||
local threads ={}
|
local threads ={}
|
||||||
local condvar = nmap.condvar(lltd_responders)
|
local condvar = nmap.condvar(lltd_responders)
|
||||||
|
|
||||||
-- party time
|
-- party time
|
||||||
for _, if_table in ipairs(interfaces) do
|
for _, if_table in ipairs(interfaces) do
|
||||||
-- create a thread for each interface
|
-- create a thread for each interface
|
||||||
local co = stdnse.new_thread(LLTDDiscover, if_table, lltd_responders, timeout)
|
local co = stdnse.new_thread(LLTDDiscover, if_table, lltd_responders, timeout)
|
||||||
threads[co]=true
|
threads[co]=true
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
-- generate output
|
-- generate output
|
||||||
local output = {}
|
local output = {}
|
||||||
for ip_addr, info in pairs(lltd_responders) do
|
for ip_addr, info in pairs(lltd_responders) do
|
||||||
if target.ALLOW_NEW_TARGETS then target.add(ip_addr) end
|
if target.ALLOW_NEW_TARGETS then target.add(ip_addr) end
|
||||||
|
|
||||||
local s = {}
|
local s = {}
|
||||||
s.name = ip_addr
|
s.name = ip_addr
|
||||||
if info.hostname then
|
if info.hostname then
|
||||||
table.insert(s, "Hostname: " .. info.hostname)
|
table.insert(s, "Hostname: " .. info.hostname)
|
||||||
end
|
end
|
||||||
if info.mac then
|
if info.mac then
|
||||||
table.insert(s, "Mac: " .. info.mac)
|
table.insert(s, "Mac: " .. info.mac)
|
||||||
end
|
end
|
||||||
if info.ipv6 then
|
if info.ipv6 then
|
||||||
table.insert(s, "IPv6: " .. info.ipv6)
|
table.insert(s, "IPv6: " .. info.ipv6)
|
||||||
end
|
end
|
||||||
table.insert(output,s)
|
table.insert(output,s)
|
||||||
end
|
end
|
||||||
|
|
||||||
if #output>0 and not target.ALLOW_NEW_TARGETS then
|
if #output>0 and not target.ALLOW_NEW_TARGETS then
|
||||||
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
||||||
end
|
end
|
||||||
return stdnse.format_output( (#output>0), output )
|
return stdnse.format_output( (#output>0), output )
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -45,243 +45,243 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|||||||
categories = {"intrusive","safe"}
|
categories = {"intrusive","safe"}
|
||||||
|
|
||||||
portrule = shortport.port_or_service(55553,"metasploit-msgrpc")
|
portrule = shortport.port_or_service(55553,"metasploit-msgrpc")
|
||||||
local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
|
local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
|
||||||
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
|
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
|
||||||
local arg_command = stdnse.get_script_args(SCRIPT_NAME .. ".command")
|
local arg_command = stdnse.get_script_args(SCRIPT_NAME .. ".command")
|
||||||
local os_type
|
local os_type
|
||||||
|
|
||||||
-- returns a "prefix" that msgpack uses for strings
|
-- returns a "prefix" that msgpack uses for strings
|
||||||
local get_prefix = function(data)
|
local get_prefix = function(data)
|
||||||
if string.len(data) <= 31 then
|
if string.len(data) <= 31 then
|
||||||
return bin.pack("C",0xa0 + string.len(data))
|
return bin.pack("C",0xa0 + string.len(data))
|
||||||
else
|
else
|
||||||
return bin.pack("C",0xda) .. bin.pack("s",string.len(data))
|
return bin.pack("C",0xda) .. bin.pack("s",string.len(data))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns a msgpacked data for console.read
|
-- returns a msgpacked data for console.read
|
||||||
local encode_console_read = function(method,token, console_id)
|
local encode_console_read = function(method,token, console_id)
|
||||||
return bin.pack("C",0x93) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token .. get_prefix(console_id) .. console_id
|
return bin.pack("C",0x93) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token .. get_prefix(console_id) .. console_id
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns a msgpacked data for console.write
|
-- returns a msgpacked data for console.write
|
||||||
local encode_console_write = function(method, token, console_id, command)
|
local encode_console_write = function(method, token, console_id, command)
|
||||||
return bin.pack("C",0x94) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token .. get_prefix(console_id) .. console_id .. get_prefix(command) .. command
|
return bin.pack("C",0x94) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token .. get_prefix(console_id) .. console_id .. get_prefix(command) .. command
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns a msgpacked data for auth.login
|
-- returns a msgpacked data for auth.login
|
||||||
local encode_auth = function(username, password)
|
local encode_auth = function(username, password)
|
||||||
local method = "auth.login"
|
local method = "auth.login"
|
||||||
return bin.pack("C",0x93) .. bin.pack("C",0xaa) .. method .. get_prefix(username) .. username .. get_prefix(password) .. password
|
return bin.pack("C",0x93) .. bin.pack("C",0xaa) .. method .. get_prefix(username) .. username .. get_prefix(password) .. password
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns a msgpacked data for any method without exstra parameters
|
-- returns a msgpacked data for any method without exstra parameters
|
||||||
local encode_noparam = function(token,method)
|
local encode_noparam = function(token,method)
|
||||||
-- token is always the same length
|
-- token is always the same length
|
||||||
return bin.pack("C",0x92) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token
|
return bin.pack("C",0x92) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token
|
||||||
end
|
end
|
||||||
|
|
||||||
-- does the actuall call with specified, pre-packed data
|
-- does the actuall call with specified, pre-packed data
|
||||||
-- and returns the response
|
-- and returns the response
|
||||||
local msgrpc_call = function(host, port, msg)
|
local msgrpc_call = function(host, port, msg)
|
||||||
local data
|
local data
|
||||||
local options = {
|
local options = {
|
||||||
header = {
|
header = {
|
||||||
["Content-Type"] = "binary/message-pack"
|
["Content-Type"] = "binary/message-pack"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data = http.post(host,port, "/api/",options, nil , msg)
|
data = http.post(host,port, "/api/",options, nil , msg)
|
||||||
if data and data.status and tostring( data.status ):match( "200" ) then
|
if data and data.status and tostring( data.status ):match( "200" ) then
|
||||||
return data.body
|
return data.body
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- auth.login wraper, returns the auth token
|
-- auth.login wraper, returns the auth token
|
||||||
local login = function(username, password,host,port)
|
local login = function(username, password,host,port)
|
||||||
|
|
||||||
local data = msgrpc_call(host, port, encode_auth(username,password))
|
local data = msgrpc_call(host, port, encode_auth(username,password))
|
||||||
|
|
||||||
if data then
|
if data then
|
||||||
local start = string.find(data,"success")
|
local start = string.find(data,"success")
|
||||||
if start > -1 then
|
if start > -1 then
|
||||||
-- get token
|
-- get token
|
||||||
local token = string.sub(string.sub(data,start),17) -- "manualy" unpack token
|
local token = string.sub(string.sub(data,start),17) -- "manualy" unpack token
|
||||||
return true, token
|
return true, token
|
||||||
else
|
else
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
stdnse.print_debug("something is wrong:" .. data )
|
stdnse.print_debug("something is wrong:" .. data )
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- core.version wraper, returns version info, and sets the OS type
|
-- core.version wraper, returns version info, and sets the OS type
|
||||||
-- so we can decide which commands to send later
|
-- so we can decide which commands to send later
|
||||||
local get_version = function(host, port, token)
|
local get_version = function(host, port, token)
|
||||||
local msg = encode_noparam(token,"core.version")
|
local msg = encode_noparam(token,"core.version")
|
||||||
|
|
||||||
local data = msgrpc_call(host, port, msg)
|
local data = msgrpc_call(host, port, msg)
|
||||||
-- unpack data
|
-- unpack data
|
||||||
if data then
|
if data then
|
||||||
-- get version, ruby version, api version
|
-- get version, ruby version, api version
|
||||||
local start = string.find(data,"version")
|
local start = string.find(data,"version")
|
||||||
local metasploit_version
|
local metasploit_version
|
||||||
local ruby_version
|
local ruby_version
|
||||||
local api_version
|
local api_version
|
||||||
if start then
|
if start then
|
||||||
metasploit_version = string.sub(string.sub(data,start),9)
|
metasploit_version = string.sub(string.sub(data,start),9)
|
||||||
start = string.find(metasploit_version,"ruby")
|
start = string.find(metasploit_version,"ruby")
|
||||||
start = start - 2
|
start = start - 2
|
||||||
metasploit_version = string.sub(metasploit_version,1,start)
|
metasploit_version = string.sub(metasploit_version,1,start)
|
||||||
start = string.find(data,"ruby")
|
start = string.find(data,"ruby")
|
||||||
ruby_version = string.sub(string.sub(data,start),6)
|
ruby_version = string.sub(string.sub(data,start),6)
|
||||||
start = string.find(ruby_version,"api")
|
start = string.find(ruby_version,"api")
|
||||||
start = start - 2
|
start = start - 2
|
||||||
ruby_version = string.sub(ruby_version,1,start)
|
ruby_version = string.sub(ruby_version,1,start)
|
||||||
start = string.find(data,"api")
|
start = string.find(data,"api")
|
||||||
api_version = string.sub(string.sub(data,start),5)
|
api_version = string.sub(string.sub(data,start),5)
|
||||||
-- put info in a table and parse for OS detection and other info
|
-- put info in a table and parse for OS detection and other info
|
||||||
port.version.name = "metasploit-msgrpc"
|
port.version.name = "metasploit-msgrpc"
|
||||||
port.version.product = metasploit_version
|
port.version.product = metasploit_version
|
||||||
port.version.name_confidence = 10
|
port.version.name_confidence = 10
|
||||||
nmap.set_port_version(host,port)
|
nmap.set_port_version(host,port)
|
||||||
local info = "Metasploit version: " .. metasploit_version .. " Ruby version: " .. ruby_version .. " API version: " .. api_version
|
local info = "Metasploit version: " .. metasploit_version .. " Ruby version: " .. ruby_version .. " API version: " .. api_version
|
||||||
if string.find(ruby_version,"mingw") < 0 then
|
if string.find(ruby_version,"mingw") < 0 then
|
||||||
os_type = "linux" -- assume linux for now
|
os_type = "linux" -- assume linux for now
|
||||||
else -- mingw compiler means it's a windows build
|
else -- mingw compiler means it's a windows build
|
||||||
os_type = "windows"
|
os_type = "windows"
|
||||||
end
|
end
|
||||||
stdnse.print_debug(info)
|
stdnse.print_debug(info)
|
||||||
return info
|
return info
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- console.create wraper, returns console_id
|
-- console.create wraper, returns console_id
|
||||||
-- which we can use to interact with metasploit further
|
-- which we can use to interact with metasploit further
|
||||||
local create_console = function(host,port,token)
|
local create_console = function(host,port,token)
|
||||||
local msg = encode_noparam(token,"console.create")
|
local msg = encode_noparam(token,"console.create")
|
||||||
local data = msgrpc_call(host, port, msg)
|
local data = msgrpc_call(host, port, msg)
|
||||||
-- unpack data
|
-- unpack data
|
||||||
if data then
|
if data then
|
||||||
--get console id
|
--get console id
|
||||||
local start = string.find(data,"id")
|
local start = string.find(data,"id")
|
||||||
local console_id
|
local console_id
|
||||||
if start then
|
if start then
|
||||||
console_id = string.sub(string.sub(data,start),4)
|
console_id = string.sub(string.sub(data,start),4)
|
||||||
local next_token = string.find(console_id,"prompt")
|
local next_token = string.find(console_id,"prompt")
|
||||||
console_id = string.sub(console_id,1,next_token-2)
|
console_id = string.sub(console_id,1,next_token-2)
|
||||||
return console_id
|
return console_id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- console.read wraper
|
-- console.read wraper
|
||||||
local read_console = function(host,port,token,console_id)
|
local read_console = function(host,port,token,console_id)
|
||||||
local msg = encode_console_read("console.read",token,console_id)
|
local msg = encode_console_read("console.read",token,console_id)
|
||||||
local data = msgrpc_call(host, port, msg)
|
local data = msgrpc_call(host, port, msg)
|
||||||
-- unpack data
|
-- unpack data
|
||||||
if data then
|
if data then
|
||||||
-- check if busy
|
-- check if busy
|
||||||
while string.byte(data,string.len(data)) == 0xc3 do
|
while string.byte(data,string.len(data)) == 0xc3 do
|
||||||
-- console is busy , let's retry in one second
|
-- console is busy , let's retry in one second
|
||||||
stdnse.sleep(1)
|
stdnse.sleep(1)
|
||||||
data = msgrpc_call(host, port, msg)
|
data = msgrpc_call(host, port, msg)
|
||||||
end
|
end
|
||||||
local start = string.find(data,"data")
|
local start = string.find(data,"data")
|
||||||
local read_data
|
local read_data
|
||||||
if start then
|
if start then
|
||||||
read_data = string.sub(string.sub(data,start),8)
|
read_data = string.sub(string.sub(data,start),8)
|
||||||
local next_token = string.find(read_data,"prompt")
|
local next_token = string.find(read_data,"prompt")
|
||||||
read_data = string.sub(read_data,1,next_token-2)
|
read_data = string.sub(read_data,1,next_token-2)
|
||||||
return read_data
|
return read_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- console.write wraper
|
-- console.write wraper
|
||||||
local write_console = function(host,port,token,console_id,command)
|
local write_console = function(host,port,token,console_id,command)
|
||||||
local msg = encode_console_write("console.write",token,console_id,command .. "\n")
|
local msg = encode_console_write("console.write",token,console_id,command .. "\n")
|
||||||
local data = msgrpc_call(host, port, msg)
|
local data = msgrpc_call(host, port, msg)
|
||||||
-- unpack data
|
-- unpack data
|
||||||
if data then
|
if data then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- console.destroy wraper, just to be nice, we don't want console to hang ...
|
-- console.destroy wraper, just to be nice, we don't want console to hang ...
|
||||||
local destroy_console = function(host,port,token,console_id)
|
local destroy_console = function(host,port,token,console_id)
|
||||||
local msg = encode_console_read("console.destroy",token,console_id)
|
local msg = encode_console_read("console.destroy",token,console_id)
|
||||||
local data = msgrpc_call(host, port, msg)
|
local data = msgrpc_call(host, port, msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- write command and read result helper
|
-- write command and read result helper
|
||||||
local write_read_console = function(host,port,token, console_id,command)
|
local write_read_console = function(host,port,token, console_id,command)
|
||||||
if write_console(host,port,token,console_id, command) then
|
if write_console(host,port,token,console_id, command) then
|
||||||
local read_data = read_console(host,port,token,console_id)
|
local read_data = read_console(host,port,token,console_id)
|
||||||
if read_data then
|
if read_data then
|
||||||
read_data = string.sub(read_data,string.find(read_data,"\n")+1) -- skip command echo
|
read_data = string.sub(read_data,string.find(read_data,"\n")+1) -- skip command echo
|
||||||
return read_data
|
return read_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
if not arg_username or not arg_password then
|
if not arg_username or not arg_password then
|
||||||
stdnse.print_debug("This script requires username and password supplied as arguments")
|
stdnse.print_debug("This script requires username and password supplied as arguments")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- authenticate
|
-- authenticate
|
||||||
local status, token = login(arg_username,arg_password,host,port)
|
local status, token = login(arg_username,arg_password,host,port)
|
||||||
if status then
|
if status then
|
||||||
-- get version info
|
-- get version info
|
||||||
local info = get_version(host,port,token)
|
local info = get_version(host,port,token)
|
||||||
local console_id = create_console(host,port,token)
|
local console_id = create_console(host,port,token)
|
||||||
if console_id then
|
if console_id then
|
||||||
local read_data = read_console(host,port,token,console_id) -- first read the banner/ascii art
|
local read_data = read_console(host,port,token,console_id) -- first read the banner/ascii art
|
||||||
stdnse.print_debug(2,read_data) -- print the nice looking banner if dbg level high enough :)
|
stdnse.print_debug(2,read_data) -- print the nice looking banner if dbg level high enough :)
|
||||||
if read_data then
|
if read_data then
|
||||||
if os_type == "linux" then
|
if os_type == "linux" then
|
||||||
read_data = write_read_console(host,port,token,console_id, "uname -a")
|
read_data = write_read_console(host,port,token,console_id, "uname -a")
|
||||||
if read_data then
|
if read_data then
|
||||||
info = info .. "\nAdditional info: " .. read_data
|
info = info .. "\nAdditional info: " .. read_data
|
||||||
end
|
end
|
||||||
read_data = write_read_console(host,port,token,console_id, "id")
|
read_data = write_read_console(host,port,token,console_id, "id")
|
||||||
if read_data then
|
if read_data then
|
||||||
info = info .. read_data
|
info = info .. read_data
|
||||||
end
|
end
|
||||||
elseif os_type == "windows" then
|
elseif os_type == "windows" then
|
||||||
read_data = write_read_console(host,port,token,console_id, "systeminfo")
|
read_data = write_read_console(host,port,token,console_id, "systeminfo")
|
||||||
if read_data then
|
if read_data then
|
||||||
stdnse.print_debug(2,read_data) -- print whole info if dbg level high enough
|
stdnse.print_debug(2,read_data) -- print whole info if dbg level high enough
|
||||||
local stop = string.find(read_data,"Hotfix") -- trim data down , systeminfo return A LOT
|
local stop = string.find(read_data,"Hotfix") -- trim data down , systeminfo return A LOT
|
||||||
read_data = string.sub(read_data,1,stop-2)
|
read_data = string.sub(read_data,1,stop-2)
|
||||||
info = info .. "\nAdditional info: \n" .. read_data
|
info = info .. "\nAdditional info: \n" .. read_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if arg_command then
|
if arg_command then
|
||||||
read_data = write_read_console(host,port,token,console_id, arg_command)
|
read_data = write_read_console(host,port,token,console_id, arg_command)
|
||||||
if read_data then
|
if read_data then
|
||||||
info = info .. "\nCustom command output: " .. read_data
|
info = info .. "\nCustom command output: " .. read_data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if read_data then
|
if read_data then
|
||||||
-- let's be nice and close the console
|
-- let's be nice and close the console
|
||||||
destroy_console(host,port,token,console_id)
|
destroy_console(host,port,token,console_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if info then
|
if info then
|
||||||
return stdnse.format_output(true,info)
|
return stdnse.format_output(true,info)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -71,62 +71,62 @@ categories = {"discovery", "safe", "broadcast"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parses a DVMRP Ask Neighbor 2 raw data and returns
|
-- Parses a DVMRP Ask Neighbor 2 raw data and returns
|
||||||
-- a structured response.
|
-- a structured response.
|
||||||
-- @param data raw data.
|
-- @param data raw data.
|
||||||
local mrinfoParse = function(data)
|
local mrinfoParse = function(data)
|
||||||
local index, address, neighbor
|
local index, address, neighbor
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- first byte should be IGMP type == 0x13 (DVMRP)
|
-- first byte should be IGMP type == 0x13 (DVMRP)
|
||||||
if data:byte(1) ~= 0x13 then return end
|
if data:byte(1) ~= 0x13 then return end
|
||||||
|
|
||||||
-- DVMRP Code
|
-- DVMRP Code
|
||||||
index, response.code = bin.unpack(">C", data, 2)
|
index, response.code = bin.unpack(">C", data, 2)
|
||||||
-- Checksum
|
-- Checksum
|
||||||
index, response.checksum = bin.unpack(">S", data, index)
|
index, response.checksum = bin.unpack(">S", data, index)
|
||||||
-- Capabilities (Skip one reserved byte)
|
-- Capabilities (Skip one reserved byte)
|
||||||
index, response.capabilities = bin.unpack(">C", data, index + 1)
|
index, response.capabilities = bin.unpack(">C", data, index + 1)
|
||||||
-- Major and minor version
|
-- Major and minor version
|
||||||
index, response.minver = bin.unpack(">C", data, index)
|
index, response.minver = bin.unpack(">C", data, index)
|
||||||
index, response.majver = bin.unpack(">C", data, index)
|
index, response.majver = bin.unpack(">C", data, index)
|
||||||
response.addresses = {}
|
response.addresses = {}
|
||||||
-- Iterate over target local addresses (interfaces)
|
-- Iterate over target local addresses (interfaces)
|
||||||
while index < #data do
|
while index < #data do
|
||||||
if data:byte(index) == 0x00 then break end
|
if data:byte(index) == 0x00 then break end
|
||||||
address = {}
|
address = {}
|
||||||
-- Local address
|
-- Local address
|
||||||
index, address.ip = bin.unpack("<I", data, index)
|
index, address.ip = bin.unpack("<I", data, index)
|
||||||
address.ip = ipOps.fromdword(address.ip)
|
address.ip = ipOps.fromdword(address.ip)
|
||||||
-- Link metric
|
-- Link metric
|
||||||
index, address.metric = bin.unpack(">C", data, index)
|
index, address.metric = bin.unpack(">C", data, index)
|
||||||
-- Treshold
|
-- Treshold
|
||||||
index, address.treshold= bin.unpack(">C", data, index)
|
index, address.treshold= bin.unpack(">C", data, index)
|
||||||
-- Flags
|
-- Flags
|
||||||
index, address.flags = bin.unpack(">C", data, index)
|
index, address.flags = bin.unpack(">C", data, index)
|
||||||
-- Number of neighbors
|
-- Number of neighbors
|
||||||
index, address.ncount = bin.unpack(">C", data, index)
|
index, address.ncount = bin.unpack(">C", data, index)
|
||||||
|
|
||||||
address.neighbors = {}
|
address.neighbors = {}
|
||||||
-- Iterate over neighbors
|
-- Iterate over neighbors
|
||||||
for i = 1, address.ncount do
|
for i = 1, address.ncount do
|
||||||
index, neighbor = bin.unpack("<I", data, index)
|
index, neighbor = bin.unpack("<I", data, index)
|
||||||
table.insert(address.neighbors, ipOps.fromdword(neighbor))
|
table.insert(address.neighbors, ipOps.fromdword(neighbor))
|
||||||
end
|
|
||||||
table.insert(response.addresses, address)
|
|
||||||
end
|
end
|
||||||
return response
|
table.insert(response.addresses, address)
|
||||||
|
end
|
||||||
|
return response
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listens for DVMRP Ask Neighbors 2 responses
|
-- Listens for DVMRP Ask Neighbors 2 responses
|
||||||
@@ -134,161 +134,161 @@ end
|
|||||||
--@param timeout Time to listen for a response.
|
--@param timeout Time to listen for a response.
|
||||||
--@param responses table to insert responses into.
|
--@param responses table to insert responses into.
|
||||||
local mrinfoListen = function(interface, timeout, responses)
|
local mrinfoListen = function(interface, timeout, responses)
|
||||||
local condvar = nmap.condvar(responses)
|
local condvar = nmap.condvar(responses)
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
local p, mrinfo_raw, status, l3data, response, _
|
local p, mrinfo_raw, status, l3data, response, _
|
||||||
|
|
||||||
-- IGMP packets that are sent to our host
|
-- IGMP packets that are sent to our host
|
||||||
local filter = 'ip proto 2 and dst host ' .. interface.address
|
local filter = 'ip proto 2 and dst host ' .. interface.address
|
||||||
listener:set_timeout(100)
|
listener:set_timeout(100)
|
||||||
listener:pcap_open(interface.device, 1024, true, filter)
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
|
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
p = packet.Packet:new(l3data, #l3data)
|
p = packet.Packet:new(l3data, #l3data)
|
||||||
mrinfo_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
mrinfo_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||||
if p then
|
if p then
|
||||||
-- Check that IGMP Type == DVMRP (0x13) and DVMRP code == Neighbor 2 (0x06)
|
-- Check that IGMP Type == DVMRP (0x13) and DVMRP code == Neighbor 2 (0x06)
|
||||||
if mrinfo_raw:byte(1) == 0x13 and mrinfo_raw:byte(2) == 0x06 then
|
if mrinfo_raw:byte(1) == 0x13 and mrinfo_raw:byte(2) == 0x06 then
|
||||||
response = mrinfoParse(mrinfo_raw)
|
response = mrinfoParse(mrinfo_raw)
|
||||||
if response then
|
if response then
|
||||||
response.srcip = p.ip_src
|
response.srcip = p.ip_src
|
||||||
table.insert(responses, response)
|
table.insert(responses, response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Function that generates a raw DVMRP Ask Neighbors 2 request.
|
-- Function that generates a raw DVMRP Ask Neighbors 2 request.
|
||||||
local mrinfoRaw = function()
|
local mrinfoRaw = function()
|
||||||
-- Type: DVMRP
|
-- Type: DVMRP
|
||||||
local mrinfo_raw = bin.pack(">C", 0x13)
|
local mrinfo_raw = bin.pack(">C", 0x13)
|
||||||
-- Code: Ask Neighbor v2
|
-- Code: Ask Neighbor v2
|
||||||
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x05)
|
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x05)
|
||||||
-- Checksum: Calculated later
|
-- Checksum: Calculated later
|
||||||
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x0000)
|
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x0000)
|
||||||
-- Reserved
|
-- Reserved
|
||||||
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x000a)
|
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x000a)
|
||||||
-- Version == Cisco IOS 12.4
|
-- Version == Cisco IOS 12.4
|
||||||
-- Minor version: 4
|
-- Minor version: 4
|
||||||
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x04)
|
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x04)
|
||||||
-- Major version: 12
|
-- Major version: 12
|
||||||
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x0c)
|
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x0c)
|
||||||
-- Calculate checksum
|
-- Calculate checksum
|
||||||
mrinfo_raw = mrinfo_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(mrinfo_raw)) .. mrinfo_raw:sub(5)
|
mrinfo_raw = mrinfo_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(mrinfo_raw)) .. mrinfo_raw:sub(5)
|
||||||
|
|
||||||
return mrinfo_raw
|
return mrinfo_raw
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Function that sends a DVMRP query.
|
-- Function that sends a DVMRP query.
|
||||||
--@param interface Network interface to use.
|
--@param interface Network interface to use.
|
||||||
--@param dstip Destination IP to send to.
|
--@param dstip Destination IP to send to.
|
||||||
local mrinfoQuery = function(interface, dstip)
|
local mrinfoQuery = function(interface, dstip)
|
||||||
local mrinfo_packet, sock, eth_hdr
|
local mrinfo_packet, sock, eth_hdr
|
||||||
local srcip = interface.address
|
local srcip = interface.address
|
||||||
|
|
||||||
local mrinfo_raw = mrinfoRaw()
|
local mrinfo_raw = mrinfoRaw()
|
||||||
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw
|
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw
|
||||||
mrinfo_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
mrinfo_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
||||||
mrinfo_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
mrinfo_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
||||||
mrinfo_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
mrinfo_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
||||||
mrinfo_packet:ip_set_len(ip_raw:len())
|
mrinfo_packet:ip_set_len(ip_raw:len())
|
||||||
if dstip == "224.0.0.1" then
|
if dstip == "224.0.0.1" then
|
||||||
-- Doesn't affect results, but we should respect RFC 3171 :)
|
-- Doesn't affect results, but we should respect RFC 3171 :)
|
||||||
mrinfo_packet:ip_set_ttl(1)
|
mrinfo_packet:ip_set_ttl(1)
|
||||||
end
|
end
|
||||||
mrinfo_packet:ip_count_checksum()
|
mrinfo_packet:ip_count_checksum()
|
||||||
|
|
||||||
sock = nmap.new_dnet()
|
sock = nmap.new_dnet()
|
||||||
if dstip == "224.0.0.1" then
|
if dstip == "224.0.0.1" then
|
||||||
sock:ethernet_open(interface.device)
|
sock:ethernet_open(interface.device)
|
||||||
-- Ethernet IPv4 multicast, our ethernet address and packet type IP
|
-- Ethernet IPv4 multicast, our ethernet address and packet type IP
|
||||||
eth_hdr = bin.pack("HAH", "01 00 5e 00 00 01", interface.mac, "08 00")
|
eth_hdr = bin.pack("HAH", "01 00 5e 00 00 01", interface.mac, "08 00")
|
||||||
sock:ethernet_send(eth_hdr .. mrinfo_packet.buf)
|
sock:ethernet_send(eth_hdr .. mrinfo_packet.buf)
|
||||||
sock:ethernet_close()
|
sock:ethernet_close()
|
||||||
else
|
else
|
||||||
sock:ip_open()
|
sock:ip_open()
|
||||||
sock:ip_send(mrinfo_packet.buf, dstip)
|
sock:ip_send(mrinfo_packet.buf, dstip)
|
||||||
sock:ip_close()
|
sock:ip_close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the network interface used to send packets to a target host.
|
-- Returns the network interface used to send packets to a target host.
|
||||||
--@param target host to which the interface is used.
|
--@param target host to which the interface is used.
|
||||||
--@return interface Network interface used for target host.
|
--@return interface Network interface used for target host.
|
||||||
local getInterface = function(target)
|
local getInterface = function(target)
|
||||||
-- First, create dummy UDP connection to get interface
|
-- First, create dummy UDP connection to get interface
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local status, err = sock:connect(target, "12345", "udp")
|
local status, err = sock:connect(target, "12345", "udp")
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local status, address, _, _, _ = sock:get_info()
|
local status, address, _, _, _ = sock:get_info()
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for _, interface in pairs(nmap.list_interfaces()) do
|
for _, interface in pairs(nmap.list_interfaces()) do
|
||||||
if interface.address == address then
|
if interface.address == address then
|
||||||
return interface
|
return interface
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
timeout = (timeout or 5) * 1000
|
timeout = (timeout or 5) * 1000
|
||||||
local target = stdnse.get_script_args(SCRIPT_NAME .. ".target") or "224.0.0.1"
|
local target = stdnse.get_script_args(SCRIPT_NAME .. ".target") or "224.0.0.1"
|
||||||
local responses = {}
|
local responses = {}
|
||||||
local interface, result
|
local interface, result
|
||||||
|
|
||||||
interface = nmap.get_interface()
|
interface = nmap.get_interface()
|
||||||
if interface then
|
if interface then
|
||||||
interface = nmap.get_interface_info(interface)
|
interface = nmap.get_interface_info(interface)
|
||||||
else
|
else
|
||||||
interface = getInterface(target)
|
interface = getInterface(target)
|
||||||
|
end
|
||||||
|
if not interface then
|
||||||
|
return ("\n ERROR: Couldn't get interface for %s"):format(target)
|
||||||
|
end
|
||||||
|
|
||||||
|
stdnse.print_debug("%s: will send to %s via %s interface.", SCRIPT_NAME, target, interface.shortname)
|
||||||
|
|
||||||
|
-- Thread that listens for responses
|
||||||
|
stdnse.new_thread(mrinfoListen, interface, timeout, responses)
|
||||||
|
|
||||||
|
-- Send request after small wait to let Listener start
|
||||||
|
stdnse.sleep(0.1)
|
||||||
|
mrinfoQuery(interface, target)
|
||||||
|
local condvar = nmap.condvar(responses)
|
||||||
|
condvar("wait")
|
||||||
|
|
||||||
|
if #responses > 0 then
|
||||||
|
local output, ifoutput = {}
|
||||||
|
for _, response in pairs(responses) do
|
||||||
|
result = {}
|
||||||
|
result.name = "Source: " .. response.srcip
|
||||||
|
table.insert(result, ("Version %s.%s"):format(response.majver, response.minver))
|
||||||
|
for _, address in pairs(response.addresses) do
|
||||||
|
ifoutput = {}
|
||||||
|
ifoutput.name = "Local address: " .. address.ip
|
||||||
|
for _, neighbor in pairs(address.neighbors) do
|
||||||
|
if target.ALLOW_NEW_TARGETS then target.add(neighbor) end
|
||||||
|
table.insert(ifoutput, "Neighbor: " .. neighbor)
|
||||||
|
end
|
||||||
|
table.insert(result, ifoutput)
|
||||||
|
end
|
||||||
|
table.insert(output, result)
|
||||||
end
|
end
|
||||||
if not interface then
|
if not target.ALLOW_NEW_TARGETS then
|
||||||
return ("\n ERROR: Couldn't get interface for %s"):format(target)
|
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
||||||
end
|
|
||||||
|
|
||||||
stdnse.print_debug("%s: will send to %s via %s interface.", SCRIPT_NAME, target, interface.shortname)
|
|
||||||
|
|
||||||
-- Thread that listens for responses
|
|
||||||
stdnse.new_thread(mrinfoListen, interface, timeout, responses)
|
|
||||||
|
|
||||||
-- Send request after small wait to let Listener start
|
|
||||||
stdnse.sleep(0.1)
|
|
||||||
mrinfoQuery(interface, target)
|
|
||||||
local condvar = nmap.condvar(responses)
|
|
||||||
condvar("wait")
|
|
||||||
|
|
||||||
if #responses > 0 then
|
|
||||||
local output, ifoutput = {}
|
|
||||||
for _, response in pairs(responses) do
|
|
||||||
result = {}
|
|
||||||
result.name = "Source: " .. response.srcip
|
|
||||||
table.insert(result, ("Version %s.%s"):format(response.majver, response.minver))
|
|
||||||
for _, address in pairs(response.addresses) do
|
|
||||||
ifoutput = {}
|
|
||||||
ifoutput.name = "Local address: " .. address.ip
|
|
||||||
for _, neighbor in pairs(address.neighbors) do
|
|
||||||
if target.ALLOW_NEW_TARGETS then target.add(neighbor) end
|
|
||||||
table.insert(ifoutput, "Neighbor: " .. neighbor)
|
|
||||||
end
|
|
||||||
table.insert(result, ifoutput)
|
|
||||||
end
|
|
||||||
table.insert(output, result)
|
|
||||||
end
|
|
||||||
if not target.ALLOW_NEW_TARGETS then
|
|
||||||
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
|
||||||
end
|
|
||||||
return stdnse.format_output(true, output)
|
|
||||||
end
|
end
|
||||||
|
return stdnse.format_output(true, output)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
|||||||
--
|
--
|
||||||
----
|
----
|
||||||
-- @args ms-sql-brute.ignore-lockout WARNING! Including this argument will cause
|
-- @args ms-sql-brute.ignore-lockout WARNING! Including this argument will cause
|
||||||
-- the script to continue attempting to brute-forcing passwords for users
|
-- the script to continue attempting to brute-forcing passwords for users
|
||||||
-- even after a user has been locked out. This may result in many SQL
|
-- even after a user has been locked out. This may result in many SQL
|
||||||
-- Server logins being locked out!
|
-- Server logins being locked out!
|
||||||
--
|
--
|
||||||
-- @args ms-sql-brute.brute-windows-accounts Enable targeting Windows accounts
|
-- @args ms-sql-brute.brute-windows-accounts Enable targeting Windows accounts
|
||||||
-- as part of the brute force attack. This should be used in conjunction
|
-- as part of the brute force attack. This should be used in conjunction
|
||||||
@@ -66,10 +66,10 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
|||||||
|
|
||||||
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 02/01/2011 - v0.2 (Chris Woodbury)
|
-- Revised 02/01/2011 - v0.2 (Chris Woodbury)
|
||||||
-- - Added ability to run against all instances on a host;
|
-- - Added ability to run against all instances on a host;
|
||||||
-- - Added recognition of account-locked out and password-expired error codes;
|
-- - Added recognition of account-locked out and password-expired error codes;
|
||||||
-- - Added storage of credentials on a per-instance basis
|
-- - Added storage of credentials on a per-instance basis
|
||||||
-- - Added compatibility with changes in mssql.lua
|
-- - Added compatibility with changes in mssql.lua
|
||||||
|
|
||||||
author = "Patrik Karlsson"
|
author = "Patrik Karlsson"
|
||||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
@@ -86,220 +86,220 @@ portrule = mssql.Helper.GetPortrule_Standard()
|
|||||||
--- Returns formatted output for the given instance
|
--- Returns formatted output for the given instance
|
||||||
local function create_instance_output_table( instance )
|
local function create_instance_output_table( instance )
|
||||||
|
|
||||||
local instanceOutput = {}
|
local instanceOutput = {}
|
||||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||||
if ( instance.ms_sql_brute.credentials ) then
|
if ( instance.ms_sql_brute.credentials ) then
|
||||||
local credsOutput = {}
|
local credsOutput = {}
|
||||||
credsOutput["name"] = "Credentials found:"
|
credsOutput["name"] = "Credentials found:"
|
||||||
table.insert( instanceOutput, credsOutput )
|
table.insert( instanceOutput, credsOutput )
|
||||||
|
|
||||||
for username, result in pairs( instance.ms_sql_brute.credentials ) do
|
for username, result in pairs( instance.ms_sql_brute.credentials ) do
|
||||||
local password = result[1]
|
local password = result[1]
|
||||||
local errorCode = result[2]
|
local errorCode = result[2]
|
||||||
password = password:len()>0 and password or "<empty>"
|
password = password:len()>0 and password or "<empty>"
|
||||||
if errorCode then
|
if errorCode then
|
||||||
local errorMessage = mssql.LoginErrorMessage[ errorCode ] or "unknown error"
|
local errorMessage = mssql.LoginErrorMessage[ errorCode ] or "unknown error"
|
||||||
table.insert( credsOutput, string.format( "%s:%s => %s", username, password, errorMessage ) )
|
table.insert( credsOutput, string.format( "%s:%s => %s", username, password, errorMessage ) )
|
||||||
else
|
else
|
||||||
table.insert( credsOutput, string.format( "%s:%s => Login Success", username, password ) )
|
table.insert( credsOutput, string.format( "%s:%s => Login Success", username, password ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( #credsOutput == 0 ) then
|
if ( #credsOutput == 0 ) then
|
||||||
table.insert( instanceOutput, "No credentials found" )
|
table.insert( instanceOutput, "No credentials found" )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( instance.ms_sql_brute.warnings ) then
|
if ( instance.ms_sql_brute.warnings ) then
|
||||||
local warningsOutput = {}
|
local warningsOutput = {}
|
||||||
warningsOutput["name"] = "Warnings:"
|
warningsOutput["name"] = "Warnings:"
|
||||||
table.insert( instanceOutput, warningsOutput )
|
table.insert( instanceOutput, warningsOutput )
|
||||||
|
|
||||||
for _, warning in ipairs( instance.ms_sql_brute.warnings ) do
|
for _, warning in ipairs( instance.ms_sql_brute.warnings ) do
|
||||||
table.insert( warningsOutput, warning )
|
table.insert( warningsOutput, warning )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( instance.ms_sql_brute.errors ) then
|
if ( instance.ms_sql_brute.errors ) then
|
||||||
local errorsOutput = {}
|
local errorsOutput = {}
|
||||||
errorsOutput["name"] = "Errors:"
|
errorsOutput["name"] = "Errors:"
|
||||||
table.insert( instanceOutput, errorsOutput )
|
table.insert( instanceOutput, errorsOutput )
|
||||||
|
|
||||||
for _, error in ipairs( instance.ms_sql_brute.errors ) do
|
for _, error in ipairs( instance.ms_sql_brute.errors ) do
|
||||||
table.insert( errorsOutput, error )
|
table.insert( errorsOutput, error )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return instanceOutput
|
return instanceOutput
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function test_credentials( instance, helper, username, password )
|
local function test_credentials( instance, helper, username, password )
|
||||||
local database = "tempdb"
|
local database = "tempdb"
|
||||||
local stopUser, stopInstance = false, false
|
local stopUser, stopInstance = false, false
|
||||||
|
|
||||||
local status, result = helper:ConnectEx( instance )
|
local status, result = helper:ConnectEx( instance )
|
||||||
local loginErrorCode
|
local loginErrorCode
|
||||||
if( status ) then
|
if( status ) then
|
||||||
stdnse.print_debug( 2, "%s: Attempting login to %s as %s/%s", SCRIPT_NAME, instance:GetName(), username, password )
|
stdnse.print_debug( 2, "%s: Attempting login to %s as %s/%s", SCRIPT_NAME, instance:GetName(), username, password )
|
||||||
status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip )
|
status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip )
|
||||||
end
|
end
|
||||||
helper:Disconnect()
|
helper:Disconnect()
|
||||||
|
|
||||||
local passwordIsGood, canLogin
|
local passwordIsGood, canLogin
|
||||||
if status then
|
if status then
|
||||||
passwordIsGood = true
|
passwordIsGood = true
|
||||||
canLogin = true
|
canLogin = true
|
||||||
elseif ( loginErrorCode ) then
|
elseif ( loginErrorCode ) then
|
||||||
if ( ( loginErrorCode ~= mssql.LoginErrorType.InvalidUsernameOrPassword ) and
|
if ( ( loginErrorCode ~= mssql.LoginErrorType.InvalidUsernameOrPassword ) and
|
||||||
( loginErrorCode ~= mssql.LoginErrorType.NotAssociatedWithTrustedConnection ) ) then
|
( loginErrorCode ~= mssql.LoginErrorType.NotAssociatedWithTrustedConnection ) ) then
|
||||||
stopUser = true
|
stopUser = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true
|
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true
|
||||||
elseif ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true
|
elseif ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true
|
||||||
elseif ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
|
elseif ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
|
||||||
stdnse.print_debug( 1, "%s: Account %s locked out on %s", SCRIPT_NAME, username, instance:GetName() )
|
stdnse.print_debug( 1, "%s: Account %s locked out on %s", SCRIPT_NAME, username, instance:GetName() )
|
||||||
table.insert( instance.ms_sql_brute.warnings, string.format( "%s: Account is locked out.", username ) )
|
table.insert( instance.ms_sql_brute.warnings, string.format( "%s: Account is locked out.", username ) )
|
||||||
if ( not stdnse.get_script_args( "ms-sql-brute.ignore-lockout" ) ) then
|
if ( not stdnse.get_script_args( "ms-sql-brute.ignore-lockout" ) ) then
|
||||||
stopInstance = true
|
stopInstance = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
|
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
|
||||||
stdnse.print_debug( 2, "%s: Attemping login to %s as (%s/%s): Unknown login error number: %s",
|
stdnse.print_debug( 2, "%s: Attemping login to %s as (%s/%s): Unknown login error number: %s",
|
||||||
SCRIPT_NAME, instance:GetName(), username, password, loginErrorCode )
|
SCRIPT_NAME, instance:GetName(), username, password, loginErrorCode )
|
||||||
table.insert( instance.ms_sql_brute.warnings, string.format( "Unknown login error number: %s", loginErrorCode ) )
|
table.insert( instance.ms_sql_brute.warnings, string.format( "Unknown login error number: %s", loginErrorCode ) )
|
||||||
end
|
end
|
||||||
stdnse.print_debug( 3, "%s: Attempt to login to %s as (%s/%s): %d (%s)",
|
stdnse.print_debug( 3, "%s: Attempt to login to %s as (%s/%s): %d (%s)",
|
||||||
SCRIPT_NAME, instance:GetName(), username, password, loginErrorCode, tostring( mssql.LoginErrorMessage[ loginErrorCode ] ) )
|
SCRIPT_NAME, instance:GetName(), username, password, loginErrorCode, tostring( mssql.LoginErrorMessage[ loginErrorCode ] ) )
|
||||||
else
|
else
|
||||||
table.insert( instance.ms_sql_brute.errors, string.format("Network error. Skipping instance. Error: %s", result ) )
|
table.insert( instance.ms_sql_brute.errors, string.format("Network error. Skipping instance. Error: %s", result ) )
|
||||||
stopUser = true
|
stopUser = true
|
||||||
stopInstance = true
|
stopInstance = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( passwordIsGood ) then
|
if ( passwordIsGood ) then
|
||||||
stopUser = true
|
stopUser = true
|
||||||
|
|
||||||
instance.ms_sql_brute.credentials[ username ] = { password, loginErrorCode }
|
instance.ms_sql_brute.credentials[ username ] = { password, loginErrorCode }
|
||||||
-- Add credentials for other ms-sql scripts to use but don't
|
-- Add credentials for other ms-sql scripts to use but don't
|
||||||
-- add accounts that need to change passwords
|
-- add accounts that need to change passwords
|
||||||
if ( canLogin ) then
|
if ( canLogin ) then
|
||||||
instance.credentials[ username ] = password
|
instance.credentials[ username ] = password
|
||||||
-- Legacy storage method (does not distinguish between instances)
|
-- Legacy storage method (does not distinguish between instances)
|
||||||
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
||||||
nmap.registry.mssqlusers[username]=password
|
nmap.registry.mssqlusers[username]=password
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stopUser, stopInstance
|
return stopUser, stopInstance
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Processes a single instance, attempting to detect an empty password for "sa"
|
--- Processes a single instance, attempting to detect an empty password for "sa"
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
-- One of this script's features is that it will report an instance's
|
-- One of this script's features is that it will report an instance's
|
||||||
-- in both the port-script results and the host-script results. In order to
|
-- in both the port-script results and the host-script results. In order to
|
||||||
-- avoid redundant login attempts on an instance, we will just make the
|
-- avoid redundant login attempts on an instance, we will just make the
|
||||||
-- attempt once and then re-use the results. We'll use a mutex to make sure
|
-- attempt once and then re-use the results. We'll use a mutex to make sure
|
||||||
-- that multiple script instances (e.g. a host-script and a port-script)
|
-- that multiple script instances (e.g. a host-script and a port-script)
|
||||||
-- working on the same SQL Server instance can only enter this block one at
|
-- working on the same SQL Server instance can only enter this block one at
|
||||||
-- a time.
|
-- a time.
|
||||||
local mutex = nmap.mutex( instance )
|
local mutex = nmap.mutex( instance )
|
||||||
mutex( "lock" )
|
mutex( "lock" )
|
||||||
|
|
||||||
-- If this instance has already been tested (e.g. if we got to it by both the
|
-- If this instance has already been tested (e.g. if we got to it by both the
|
||||||
-- hostrule and the portrule), don't test it again.
|
-- hostrule and the portrule), don't test it again.
|
||||||
if ( instance.tested_brute ~= true ) then
|
if ( instance.tested_brute ~= true ) then
|
||||||
instance.tested_brute = true
|
instance.tested_brute = true
|
||||||
|
|
||||||
instance.credentials = instance.credentials or {}
|
instance.credentials = instance.credentials or {}
|
||||||
instance.ms_sql_brute = instance.ms_sql_brute or {}
|
instance.ms_sql_brute = instance.ms_sql_brute or {}
|
||||||
instance.ms_sql_brute.credentials = instance.ms_sql_brute.credentials or {}
|
instance.ms_sql_brute.credentials = instance.ms_sql_brute.credentials or {}
|
||||||
instance.ms_sql_brute.warnings = instance.ms_sql_brute.warnings or {}
|
instance.ms_sql_brute.warnings = instance.ms_sql_brute.warnings or {}
|
||||||
instance.ms_sql_brute.errors = instance.ms_sql_brute.errors or {}
|
instance.ms_sql_brute.errors = instance.ms_sql_brute.errors or {}
|
||||||
|
|
||||||
local result, status
|
local result, status
|
||||||
local stopUser, stopInstance
|
local stopUser, stopInstance
|
||||||
local usernames, passwords, username, password
|
local usernames, passwords, username, password
|
||||||
local helper = mssql.Helper:new()
|
local helper = mssql.Helper:new()
|
||||||
|
|
||||||
if ( not instance:HasNetworkProtocols() ) then
|
if ( not instance:HasNetworkProtocols() ) then
|
||||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||||
table.insert( instance.ms_sql_brute.errors, "No network protocols enabled." )
|
table.insert( instance.ms_sql_brute.errors, "No network protocols enabled." )
|
||||||
stopInstance = true
|
stopInstance = true
|
||||||
end
|
end
|
||||||
|
|
||||||
status, usernames = unpwdb.usernames()
|
status, usernames = unpwdb.usernames()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug( 1, "%s: Failed to load usernames list.", SCRIPT_NAME )
|
stdnse.print_debug( 1, "%s: Failed to load usernames list.", SCRIPT_NAME )
|
||||||
table.insert( instance.ms_sql_brute.errors, "Failed to load usernames list." )
|
table.insert( instance.ms_sql_brute.errors, "Failed to load usernames list." )
|
||||||
stopInstance = true
|
stopInstance = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
status, passwords = unpwdb.passwords()
|
status, passwords = unpwdb.passwords()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug( 1, "%s: Failed to load passwords list.", SCRIPT_NAME )
|
stdnse.print_debug( 1, "%s: Failed to load passwords list.", SCRIPT_NAME )
|
||||||
table.insert( instance.ms_sql_brute.errors, "Failed to load passwords list." )
|
table.insert( instance.ms_sql_brute.errors, "Failed to load passwords list." )
|
||||||
stopInstance = true
|
stopInstance = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
for username in usernames do
|
for username in usernames do
|
||||||
if stopInstance then break end
|
if stopInstance then break end
|
||||||
|
|
||||||
-- See if the password is the same as the username (which may not
|
-- See if the password is the same as the username (which may not
|
||||||
-- be in the password list)
|
-- be in the password list)
|
||||||
stopUser, stopInstance = test_credentials( instance, helper, username, username )
|
stopUser, stopInstance = test_credentials( instance, helper, username, username )
|
||||||
|
|
||||||
for password in passwords do
|
for password in passwords do
|
||||||
if stopUser then break end
|
if stopUser then break end
|
||||||
|
|
||||||
stopUser, stopInstance = test_credentials( instance, helper, username, password )
|
stopUser, stopInstance = test_credentials( instance, helper, username, password )
|
||||||
end
|
end
|
||||||
|
|
||||||
passwords("reset")
|
passwords("reset")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The password testing has been finished. Unlock the mutex.
|
-- The password testing has been finished. Unlock the mutex.
|
||||||
mutex( "done" )
|
mutex( "done" )
|
||||||
|
|
||||||
return create_instance_output_table( instance )
|
return create_instance_output_table( instance )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
local scriptOutput = {}
|
local scriptOutput = {}
|
||||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||||
|
|
||||||
local domain, bruteWindows = stdnse.get_script_args("mssql.domain", "ms-sql-brute.brute-windows-accounts")
|
local domain, bruteWindows = stdnse.get_script_args("mssql.domain", "ms-sql-brute.brute-windows-accounts")
|
||||||
|
|
||||||
if ( domain and not(bruteWindows) ) then
|
if ( domain and not(bruteWindows) ) then
|
||||||
local ret = "\n " ..
|
local ret = "\n " ..
|
||||||
"Windows authentication was enabled but the argument\n " ..
|
"Windows authentication was enabled but the argument\n " ..
|
||||||
"ms-sql-brute.brute-windows-accounts was not given. As there is currently no\n " ..
|
"ms-sql-brute.brute-windows-accounts was not given. As there is currently no\n " ..
|
||||||
"way of detecting accounts being locked out when Windows authentication is \n " ..
|
"way of detecting accounts being locked out when Windows authentication is \n " ..
|
||||||
"used, make sure that the amount entries in the password list\n " ..
|
"used, make sure that the amount entries in the password list\n " ..
|
||||||
"(passdb argument) are at least 2 entries below the lockout threshold."
|
"(passdb argument) are at least 2 entries below the lockout threshold."
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not status ) then
|
if ( not status ) then
|
||||||
return stdnse.format_output( false, instanceList )
|
return stdnse.format_output( false, instanceList )
|
||||||
else
|
else
|
||||||
for _, instance in pairs( instanceList ) do
|
for _, instance in pairs( instanceList ) do
|
||||||
local instanceOutput = process_instance( instance )
|
local instanceOutput = process_instance( instance )
|
||||||
if instanceOutput then
|
if instanceOutput then
|
||||||
table.insert( scriptOutput, instanceOutput )
|
table.insert( scriptOutput, instanceOutput )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output( true, scriptOutput )
|
return stdnse.format_output( true, scriptOutput )
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -69,47 +69,47 @@ categories = {"discovery", "safe", "broadcast"}
|
|||||||
|
|
||||||
-- From: https://tools.ietf.org/id/draft-ietf-idmr-traceroute-ipm-07.txt
|
-- From: https://tools.ietf.org/id/draft-ietf-idmr-traceroute-ipm-07.txt
|
||||||
PROTO = {
|
PROTO = {
|
||||||
[0x01] = "DVMRP",
|
[0x01] = "DVMRP",
|
||||||
[0x02] = "MOSPF",
|
[0x02] = "MOSPF",
|
||||||
[0x03] = "PIM",
|
[0x03] = "PIM",
|
||||||
[0x04] = "CBT",
|
[0x04] = "CBT",
|
||||||
[0x05] = "PIM / Special table",
|
[0x05] = "PIM / Special table",
|
||||||
[0x06] = "PIM / Static",
|
[0x06] = "PIM / Static",
|
||||||
[0x07] = "DVMRP / Static",
|
[0x07] = "DVMRP / Static",
|
||||||
[0x08] = "PIM / MBGP",
|
[0x08] = "PIM / MBGP",
|
||||||
[0x09] = "CBT / Special table",
|
[0x09] = "CBT / Special table",
|
||||||
[0x10] = "CBT / Static",
|
[0x10] = "CBT / Static",
|
||||||
[0x11] = "PIM / state created by Assert processing",
|
[0x11] = "PIM / state created by Assert processing",
|
||||||
}
|
}
|
||||||
|
|
||||||
FWD_CODE = {
|
FWD_CODE = {
|
||||||
[0x00] = "NO_ERROR",
|
[0x00] = "NO_ERROR",
|
||||||
[0x01] = "WRONG_IF",
|
[0x01] = "WRONG_IF",
|
||||||
[0x02] = "PRUNE_SENT",
|
[0x02] = "PRUNE_SENT",
|
||||||
[0x03] = "PRUNE_RCVD",
|
[0x03] = "PRUNE_RCVD",
|
||||||
[0x04] = "SCOPED",
|
[0x04] = "SCOPED",
|
||||||
[0x05] = "NO_ROUTE",
|
[0x05] = "NO_ROUTE",
|
||||||
[0x06] = "WRONG_LAST_HOP",
|
[0x06] = "WRONG_LAST_HOP",
|
||||||
[0x07] = "NOT_FORWARDING",
|
[0x07] = "NOT_FORWARDING",
|
||||||
[0x08] = "REACHED_RP",
|
[0x08] = "REACHED_RP",
|
||||||
[0x09] = "RPF_IF",
|
[0x09] = "RPF_IF",
|
||||||
[0x0A] = "NO_MULTICAST",
|
[0x0A] = "NO_MULTICAST",
|
||||||
[0x0B] = "INFO_HIDDEN",
|
[0x0B] = "INFO_HIDDEN",
|
||||||
[0x81] = "NO_SPACE",
|
[0x81] = "NO_SPACE",
|
||||||
[0x82] = "OLD_ROUTER",
|
[0x82] = "OLD_ROUTER",
|
||||||
[0x83] = "ADMIN_PROHIB",
|
[0x83] = "ADMIN_PROHIB",
|
||||||
}
|
}
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Generates a raw IGMP Traceroute Query.
|
--- Generates a raw IGMP Traceroute Query.
|
||||||
@@ -119,19 +119,19 @@ end
|
|||||||
--@param receiver Receiver of the response.
|
--@param receiver Receiver of the response.
|
||||||
--@return data Raw Traceroute Query.
|
--@return data Raw Traceroute Query.
|
||||||
local traceRaw = function(fromip, toip, group, receiver)
|
local traceRaw = function(fromip, toip, group, receiver)
|
||||||
local data = bin.pack(">C", 0x1f) -- Type: Traceroute Query
|
local data = bin.pack(">C", 0x1f) -- Type: Traceroute Query
|
||||||
local data = data .. bin.pack(">C", 0x20) -- Hops: 32
|
local data = data .. bin.pack(">C", 0x20) -- Hops: 32
|
||||||
local data = data .. bin.pack(">S", 0x0000) -- Checksum: To be set later
|
local data = data .. bin.pack(">S", 0x0000) -- Checksum: To be set later
|
||||||
local data = data .. bin.pack(">I", ipOps.todword(group)) -- Multicast group
|
local data = data .. bin.pack(">I", ipOps.todword(group)) -- Multicast group
|
||||||
local data = data .. bin.pack(">I", ipOps.todword(fromip)) -- Source
|
local data = data .. bin.pack(">I", ipOps.todword(fromip)) -- Source
|
||||||
local data = data .. bin.pack(">I", ipOps.todword(toip)) -- Destination
|
local data = data .. bin.pack(">I", ipOps.todword(toip)) -- Destination
|
||||||
local data = data .. bin.pack(">I", ipOps.todword(receiver)) -- Receiver
|
local data = data .. bin.pack(">I", ipOps.todword(receiver)) -- Receiver
|
||||||
local data = data .. bin.pack(">C", 0x40) -- TTL
|
local data = data .. bin.pack(">C", 0x40) -- TTL
|
||||||
local data = data .. bin.pack(">CS", 0x00, math.random(123456)) -- Query ID
|
local data = data .. bin.pack(">CS", 0x00, math.random(123456)) -- Query ID
|
||||||
|
|
||||||
-- We calculate checksum
|
-- We calculate checksum
|
||||||
data = data:sub(1,2) .. bin.pack(">S", packet.in_cksum(data)) .. data:sub(5)
|
data = data:sub(1,2) .. bin.pack(">S", packet.in_cksum(data)) .. data:sub(5)
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a raw IGMP Traceroute Query.
|
--- Sends a raw IGMP Traceroute Query.
|
||||||
@@ -139,125 +139,125 @@ end
|
|||||||
--@param destination Target host to which the packet is sent.
|
--@param destination Target host to which the packet is sent.
|
||||||
--@param trace_raw Traceroute raw Query.
|
--@param trace_raw Traceroute raw Query.
|
||||||
local traceSend = function(interface, destination, trace_raw)
|
local traceSend = function(interface, destination, trace_raw)
|
||||||
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. trace_raw
|
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. trace_raw
|
||||||
local trace_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
local trace_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
||||||
trace_packet:ip_set_bin_src(ipOps.ip_to_str(interface.address))
|
trace_packet:ip_set_bin_src(ipOps.ip_to_str(interface.address))
|
||||||
trace_packet:ip_set_bin_dst(ipOps.ip_to_str(destination))
|
trace_packet:ip_set_bin_dst(ipOps.ip_to_str(destination))
|
||||||
trace_packet:ip_set_len(#trace_packet.buf)
|
trace_packet:ip_set_len(#trace_packet.buf)
|
||||||
trace_packet:ip_count_checksum()
|
trace_packet:ip_count_checksum()
|
||||||
|
|
||||||
if destination == "224.0.0.2" then
|
if destination == "224.0.0.2" then
|
||||||
-- Doesn't affect results as it is ignored but most routers, but RFC
|
-- Doesn't affect results as it is ignored but most routers, but RFC
|
||||||
-- 3171 should be respected.
|
-- 3171 should be respected.
|
||||||
trace_packet:ip_set_ttl(1)
|
trace_packet:ip_set_ttl(1)
|
||||||
end
|
end
|
||||||
trace_packet:ip_count_checksum()
|
trace_packet:ip_count_checksum()
|
||||||
|
|
||||||
local sock = nmap.new_dnet()
|
local sock = nmap.new_dnet()
|
||||||
if destination == "224.0.0.2" then
|
if destination == "224.0.0.2" then
|
||||||
sock:ethernet_open(interface.device)
|
sock:ethernet_open(interface.device)
|
||||||
-- Ethernet IPv4 multicast, our ethernet address and packet type IP
|
-- Ethernet IPv4 multicast, our ethernet address and packet type IP
|
||||||
local eth_hdr = bin.pack("HAH", "01 00 5e 00 00 02", interface.mac, "08 00")
|
local eth_hdr = bin.pack("HAH", "01 00 5e 00 00 02", interface.mac, "08 00")
|
||||||
sock:ethernet_send(eth_hdr .. trace_packet.buf)
|
sock:ethernet_send(eth_hdr .. trace_packet.buf)
|
||||||
sock:ethernet_close()
|
sock:ethernet_close()
|
||||||
else
|
else
|
||||||
sock:ip_open()
|
sock:ip_open()
|
||||||
sock:ip_send(trace_packet.buf, destination)
|
sock:ip_send(trace_packet.buf, destination)
|
||||||
sock:ip_close()
|
sock:ip_close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Parses an IGMP Traceroute Response and returns it in structured form.
|
--- Parses an IGMP Traceroute Response and returns it in structured form.
|
||||||
--@param data Raw Traceroute Response.
|
--@param data Raw Traceroute Response.
|
||||||
--@return response Structured Traceroute Response.
|
--@return response Structured Traceroute Response.
|
||||||
local traceParse = function(data)
|
local traceParse = function(data)
|
||||||
local index
|
local index
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- first byte should be IGMP type == 0x1e (Traceroute Response)
|
-- first byte should be IGMP type == 0x1e (Traceroute Response)
|
||||||
if data:byte(1) ~= 0x1e then return end
|
if data:byte(1) ~= 0x1e then return end
|
||||||
|
|
||||||
-- Hops
|
-- Hops
|
||||||
index, response.hops = bin.unpack(">C", data, 2)
|
index, response.hops = bin.unpack(">C", data, 2)
|
||||||
|
|
||||||
-- Checksum
|
-- Checksum
|
||||||
index, response.checksum = bin.unpack(">S", data, index)
|
index, response.checksum = bin.unpack(">S", data, index)
|
||||||
|
|
||||||
-- Group
|
-- Group
|
||||||
index, response.group = bin.unpack("<I", data, index)
|
index, response.group = bin.unpack("<I", data, index)
|
||||||
response.group = ipOps.fromdword(response.group)
|
response.group = ipOps.fromdword(response.group)
|
||||||
|
|
||||||
-- Source address
|
-- Source address
|
||||||
index, response.source = bin.unpack("<I", data, index)
|
index, response.source = bin.unpack("<I", data, index)
|
||||||
response.source = ipOps.fromdword(response.source)
|
response.source = ipOps.fromdword(response.source)
|
||||||
|
|
||||||
-- Destination address
|
-- Destination address
|
||||||
index, response.destination = bin.unpack("<I", data, index)
|
index, response.destination = bin.unpack("<I", data, index)
|
||||||
response.receiver = ipOps.fromdword(response.destination)
|
response.receiver = ipOps.fromdword(response.destination)
|
||||||
|
|
||||||
-- Response address
|
-- Response address
|
||||||
index, response.response = bin.unpack("<I", data, index)
|
index, response.response = bin.unpack("<I", data, index)
|
||||||
response.response = ipOps.fromdword(response.response)
|
response.response = ipOps.fromdword(response.response)
|
||||||
|
|
||||||
-- Response TTL
|
-- Response TTL
|
||||||
index, response.ttl = bin.unpack(">C", data, index)
|
index, response.ttl = bin.unpack(">C", data, index)
|
||||||
|
|
||||||
-- Query ID
|
-- Query ID
|
||||||
index, response.qid = bin.unpack(">C", data, index)
|
index, response.qid = bin.unpack(">C", data, index)
|
||||||
index, response.qid = response.qid * 2^16 + bin.unpack(">S", data, index)
|
index, response.qid = response.qid * 2^16 + bin.unpack(">S", data, index)
|
||||||
|
|
||||||
local block
|
local block
|
||||||
response.blocks = {}
|
response.blocks = {}
|
||||||
-- Now, parse data blocks
|
-- Now, parse data blocks
|
||||||
while true do
|
while true do
|
||||||
-- To end parsing and not get stuck in infinite loops.
|
-- To end parsing and not get stuck in infinite loops.
|
||||||
if index >= #data then
|
if index >= #data then
|
||||||
break
|
break
|
||||||
elseif #data - index < 31 then
|
elseif #data - index < 31 then
|
||||||
stdnse.print_verbose("%s malformated traceroute response.", SCRIPT_NAME)
|
stdnse.print_verbose("%s malformated traceroute response.", SCRIPT_NAME)
|
||||||
return
|
return
|
||||||
end
|
|
||||||
|
|
||||||
block = {}
|
|
||||||
-- Query Arrival
|
|
||||||
index, block.query = bin.unpack(">I", data, index)
|
|
||||||
|
|
||||||
-- In itf address
|
|
||||||
index, block.inaddr = bin.unpack("<I", data, index)
|
|
||||||
block.inaddr = ipOps.fromdword(block.inaddr)
|
|
||||||
|
|
||||||
-- Out itf address
|
|
||||||
index, block.outaddr = bin.unpack("<I", data, index)
|
|
||||||
block.outaddr = ipOps.fromdword(block.outaddr)
|
|
||||||
|
|
||||||
-- Previous rtr address
|
|
||||||
index, block.prevaddr = bin.unpack("<I", data, index)
|
|
||||||
block.prevaddr = ipOps.fromdword(block.prevaddr)
|
|
||||||
|
|
||||||
-- In packets
|
|
||||||
index, block.inpkts = bin.unpack(">I", data, index)
|
|
||||||
|
|
||||||
-- Out packets
|
|
||||||
index, block.outpkts = bin.unpack(">I", data, index)
|
|
||||||
|
|
||||||
-- S,G pkt count
|
|
||||||
index, block.sgpkt = bin.unpack(">I", data, index)
|
|
||||||
|
|
||||||
-- Protocol
|
|
||||||
index, block.proto = bin.unpack(">C", data, index)
|
|
||||||
|
|
||||||
-- Forward TTL
|
|
||||||
index, block.fwdttl = bin.unpack(">C", data, index)
|
|
||||||
|
|
||||||
-- Options
|
|
||||||
index, block.options = bin.unpack(">C", data, index)
|
|
||||||
|
|
||||||
-- Forwarding Code
|
|
||||||
index, block.code = bin.unpack(">C", data, index)
|
|
||||||
|
|
||||||
table.insert(response.blocks, block)
|
|
||||||
end
|
end
|
||||||
return response
|
|
||||||
|
block = {}
|
||||||
|
-- Query Arrival
|
||||||
|
index, block.query = bin.unpack(">I", data, index)
|
||||||
|
|
||||||
|
-- In itf address
|
||||||
|
index, block.inaddr = bin.unpack("<I", data, index)
|
||||||
|
block.inaddr = ipOps.fromdword(block.inaddr)
|
||||||
|
|
||||||
|
-- Out itf address
|
||||||
|
index, block.outaddr = bin.unpack("<I", data, index)
|
||||||
|
block.outaddr = ipOps.fromdword(block.outaddr)
|
||||||
|
|
||||||
|
-- Previous rtr address
|
||||||
|
index, block.prevaddr = bin.unpack("<I", data, index)
|
||||||
|
block.prevaddr = ipOps.fromdword(block.prevaddr)
|
||||||
|
|
||||||
|
-- In packets
|
||||||
|
index, block.inpkts = bin.unpack(">I", data, index)
|
||||||
|
|
||||||
|
-- Out packets
|
||||||
|
index, block.outpkts = bin.unpack(">I", data, index)
|
||||||
|
|
||||||
|
-- S,G pkt count
|
||||||
|
index, block.sgpkt = bin.unpack(">I", data, index)
|
||||||
|
|
||||||
|
-- Protocol
|
||||||
|
index, block.proto = bin.unpack(">C", data, index)
|
||||||
|
|
||||||
|
-- Forward TTL
|
||||||
|
index, block.fwdttl = bin.unpack(">C", data, index)
|
||||||
|
|
||||||
|
-- Options
|
||||||
|
index, block.options = bin.unpack(">C", data, index)
|
||||||
|
|
||||||
|
-- Forwarding Code
|
||||||
|
index, block.code = bin.unpack(">C", data, index)
|
||||||
|
|
||||||
|
table.insert(response.blocks, block)
|
||||||
|
end
|
||||||
|
return response
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listens for IGMP Traceroute responses
|
-- Listens for IGMP Traceroute responses
|
||||||
@@ -265,129 +265,129 @@ end
|
|||||||
--@param timeout Amount of time to listen for in seconds.
|
--@param timeout Amount of time to listen for in seconds.
|
||||||
--@param responses table to insert responses into.
|
--@param responses table to insert responses into.
|
||||||
local traceListener = function(interface, timeout, responses)
|
local traceListener = function(interface, timeout, responses)
|
||||||
local condvar = nmap.condvar(responses)
|
local condvar = nmap.condvar(responses)
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
local p, trace_raw, status, l3data, response, _
|
local p, trace_raw, status, l3data, response, _
|
||||||
|
|
||||||
-- IGMP packets that are sent to our host
|
-- IGMP packets that are sent to our host
|
||||||
local filter = 'ip proto 2 and dst host ' .. interface.address
|
local filter = 'ip proto 2 and dst host ' .. interface.address
|
||||||
listener:set_timeout(100)
|
listener:set_timeout(100)
|
||||||
listener:pcap_open(interface.device, 1024, true, filter)
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
|
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
p = packet.Packet:new(l3data, #l3data)
|
p = packet.Packet:new(l3data, #l3data)
|
||||||
trace_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
trace_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||||
if p then
|
if p then
|
||||||
-- Check that IGMP Type == 0x1e (Traceroute Response)
|
-- Check that IGMP Type == 0x1e (Traceroute Response)
|
||||||
if trace_raw:byte(1) == 0x1e then
|
if trace_raw:byte(1) == 0x1e then
|
||||||
response = traceParse(trace_raw)
|
response = traceParse(trace_raw)
|
||||||
if response then
|
if response then
|
||||||
response.srcip = p.ip_src
|
response.srcip = p.ip_src
|
||||||
table.insert(responses, response)
|
table.insert(responses, response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the network interface used to send packets to a target host.
|
-- Returns the network interface used to send packets to a target host.
|
||||||
--@param target host to which the interface is used.
|
--@param target host to which the interface is used.
|
||||||
--@return interface Network interface used for target host.
|
--@return interface Network interface used for target host.
|
||||||
local getInterface = function(target)
|
local getInterface = function(target)
|
||||||
-- First, create dummy UDP connection to get interface
|
-- First, create dummy UDP connection to get interface
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local status, err = sock:connect(target, "12345", "udp")
|
local status, err = sock:connect(target, "12345", "udp")
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local status, address, _, _, _ = sock:get_info()
|
local status, address, _, _, _ = sock:get_info()
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for _, interface in pairs(nmap.list_interfaces()) do
|
for _, interface in pairs(nmap.list_interfaces()) do
|
||||||
if interface.address == address then
|
if interface.address == address then
|
||||||
return interface
|
return interface
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local fromip = stdnse.get_script_args(SCRIPT_NAME .. ".fromip")
|
local fromip = stdnse.get_script_args(SCRIPT_NAME .. ".fromip")
|
||||||
local toip = stdnse.get_script_args(SCRIPT_NAME .. ".toip")
|
local toip = stdnse.get_script_args(SCRIPT_NAME .. ".toip")
|
||||||
local group = stdnse.get_script_args(SCRIPT_NAME .. ".group") or "0.0.0.0"
|
local group = stdnse.get_script_args(SCRIPT_NAME .. ".group") or "0.0.0.0"
|
||||||
local firsthop = stdnse.get_script_args(SCRIPT_NAME .. ".firsthop") or "224.0.0.2"
|
local firsthop = stdnse.get_script_args(SCRIPT_NAME .. ".firsthop") or "224.0.0.2"
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
local responses = {}
|
local responses = {}
|
||||||
timeout = (timeout or 7) * 1000
|
timeout = (timeout or 7) * 1000
|
||||||
|
|
||||||
-- Source address from which to traceroute
|
-- Source address from which to traceroute
|
||||||
if not fromip then
|
if not fromip then
|
||||||
stdnse.print_verbose("%s: A source IP must be provided through fromip argument.", SCRIPT_NAME)
|
stdnse.print_verbose("%s: A source IP must be provided through fromip argument.", SCRIPT_NAME)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get network interface to use
|
-- Get network interface to use
|
||||||
local interface = nmap.get_interface()
|
local interface = nmap.get_interface()
|
||||||
if interface then
|
if interface then
|
||||||
interface = nmap.get_interface_info(interface)
|
interface = nmap.get_interface_info(interface)
|
||||||
else
|
else
|
||||||
interface = getInterface(firsthop)
|
interface = getInterface(firsthop)
|
||||||
end
|
end
|
||||||
if not interface then
|
if not interface then
|
||||||
return ("\n ERROR: Couldn't get interface for %s"):format(firsthop)
|
return ("\n ERROR: Couldn't get interface for %s"):format(firsthop)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Destination defaults to our own host
|
-- Destination defaults to our own host
|
||||||
toip = toip or interface.address
|
toip = toip or interface.address
|
||||||
|
|
||||||
stdnse.print_debug("%s: Traceroute group %s from %s to %s.", SCRIPT_NAME, group, fromip, toip)
|
stdnse.print_debug("%s: Traceroute group %s from %s to %s.", SCRIPT_NAME, group, fromip, toip)
|
||||||
stdnse.print_debug("%s: will send to %s via %s interface.", SCRIPT_NAME, firsthop, interface.shortname)
|
stdnse.print_debug("%s: will send to %s via %s interface.", SCRIPT_NAME, firsthop, interface.shortname)
|
||||||
|
|
||||||
-- Thread that listens for responses
|
-- Thread that listens for responses
|
||||||
stdnse.new_thread(traceListener, interface, timeout, responses)
|
stdnse.new_thread(traceListener, interface, timeout, responses)
|
||||||
|
|
||||||
-- Send request after small wait to let Listener start
|
-- Send request after small wait to let Listener start
|
||||||
stdnse.sleep(0.1)
|
stdnse.sleep(0.1)
|
||||||
local trace_raw = traceRaw(fromip, toip, group, interface.address)
|
local trace_raw = traceRaw(fromip, toip, group, interface.address)
|
||||||
traceSend(interface, firsthop, trace_raw)
|
traceSend(interface, firsthop, trace_raw)
|
||||||
|
|
||||||
local condvar = nmap.condvar(responses)
|
local condvar = nmap.condvar(responses)
|
||||||
condvar("wait")
|
condvar("wait")
|
||||||
if #responses > 0 then
|
if #responses > 0 then
|
||||||
local outresp
|
local outresp
|
||||||
local output, outblock = {}
|
local output, outblock = {}
|
||||||
table.insert(output, ("Group %s from %s to %s"):format(group, fromip, toip))
|
table.insert(output, ("Group %s from %s to %s"):format(group, fromip, toip))
|
||||||
for _, response in pairs(responses) do
|
for _, response in pairs(responses) do
|
||||||
outresp = {}
|
outresp = {}
|
||||||
outresp.name = "Source: " .. response.srcip
|
outresp.name = "Source: " .. response.srcip
|
||||||
for _, block in pairs(response.blocks) do
|
for _, block in pairs(response.blocks) do
|
||||||
outblock = {}
|
outblock = {}
|
||||||
outblock.name = "In address: " .. block.inaddr
|
outblock.name = "In address: " .. block.inaddr
|
||||||
table.insert(outblock, "Out address: " .. block.outaddr)
|
table.insert(outblock, "Out address: " .. block.outaddr)
|
||||||
-- Protocol
|
-- Protocol
|
||||||
if PROTO[block.proto] then
|
if PROTO[block.proto] then
|
||||||
table.insert(outblock, "Protocol: " .. PROTO[block.proto])
|
table.insert(outblock, "Protocol: " .. PROTO[block.proto])
|
||||||
else
|
else
|
||||||
table.insert(outblock, "Protocol: Unknown")
|
table.insert(outblock, "Protocol: Unknown")
|
||||||
end
|
end
|
||||||
-- Error Code, we ignore NO_ERROR which is the normal case.
|
-- Error Code, we ignore NO_ERROR which is the normal case.
|
||||||
if FWD_CODE[block.code] and block.code ~= 0x00 then
|
if FWD_CODE[block.code] and block.code ~= 0x00 then
|
||||||
table.insert(outblock, "Error code: " .. FWD_CODE[block.code])
|
table.insert(outblock, "Error code: " .. FWD_CODE[block.code])
|
||||||
elseif block.code ~= 0x00 then
|
elseif block.code ~= 0x00 then
|
||||||
table.insert(outblock, "Error code: Unknown")
|
table.insert(outblock, "Error code: Unknown")
|
||||||
end
|
end
|
||||||
table.insert(outresp, outblock)
|
table.insert(outresp, outblock)
|
||||||
end
|
end
|
||||||
table.insert(output, outresp)
|
table.insert(output, outresp)
|
||||||
end
|
|
||||||
return stdnse.format_output(true, output)
|
|
||||||
end
|
end
|
||||||
|
return stdnse.format_output(true, output)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -89,31 +89,31 @@ local MAX_PACKET = 0x2000
|
|||||||
-- Flags
|
-- Flags
|
||||||
local mode_flags =
|
local mode_flags =
|
||||||
{
|
{
|
||||||
FLAG_MODE = bit.lshift(1, 0),
|
FLAG_MODE = bit.lshift(1, 0),
|
||||||
FLAG_LOCAL_ACK = bit.lshift(1, 1),
|
FLAG_LOCAL_ACK = bit.lshift(1, 1),
|
||||||
FLAG_IS_TCP = bit.lshift(1, 2),
|
FLAG_IS_TCP = bit.lshift(1, 2),
|
||||||
FLAG_IP_INCLUDED = bit.lshift(1, 3),
|
FLAG_IP_INCLUDED = bit.lshift(1, 3),
|
||||||
FLAG_UNKNOWN0_INCLUDED = bit.lshift(1, 4),
|
FLAG_UNKNOWN0_INCLUDED = bit.lshift(1, 4),
|
||||||
FLAG_UNKNOWN1_INCLUDED = bit.lshift(1, 5),
|
FLAG_UNKNOWN1_INCLUDED = bit.lshift(1, 5),
|
||||||
FLAG_DATA_INCLUDED = bit.lshift(1, 6),
|
FLAG_DATA_INCLUDED = bit.lshift(1, 6),
|
||||||
FLAG_SYSINFO_INCLUDED = bit.lshift(1, 7),
|
FLAG_SYSINFO_INCLUDED = bit.lshift(1, 7),
|
||||||
FLAG_ENCODED = bit.lshift(1, 15)
|
FLAG_ENCODED = bit.lshift(1, 15)
|
||||||
}
|
}
|
||||||
|
|
||||||
---For a hostrule, simply use the 'smb' ports as an indicator, unless the user overrides it
|
---For a hostrule, simply use the 'smb' ports as an indicator, unless the user overrides it
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
if ( nmap.address_family() ~= 'inet' ) then
|
if ( nmap.address_family() ~= 'inet' ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if(smb.get_port(host) ~= nil) then
|
if(smb.get_port(host) ~= nil) then
|
||||||
return true
|
return true
|
||||||
elseif(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
|
elseif(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
|
||||||
return true
|
return true
|
||||||
elseif(nmap.registry.args.checkconficker == "true" or nmap.registry.args.checkconficker == "1") then
|
elseif(nmap.registry.args.checkconficker == "true" or nmap.registry.args.checkconficker == "1") then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Multiply two 32-bit integers and return a 64-bit product. The first return
|
-- Multiply two 32-bit integers and return a 64-bit product. The first return
|
||||||
@@ -124,21 +124,21 @@ end
|
|||||||
--@param v Second number (0 <= v <= 0xFFFFFFFF)
|
--@param v Second number (0 <= v <= 0xFFFFFFFF)
|
||||||
--@return 64-bit product of u*v, as a pair of 32-bit integers.
|
--@return 64-bit product of u*v, as a pair of 32-bit integers.
|
||||||
local function mul64(u, v)
|
local function mul64(u, v)
|
||||||
-- This is based on formula (2) from section 4.3.3 of The Art of
|
-- This is based on formula (2) from section 4.3.3 of The Art of
|
||||||
-- Computer Programming. We split u and v into upper and lower 16-bit
|
-- Computer Programming. We split u and v into upper and lower 16-bit
|
||||||
-- chunks, such that
|
-- chunks, such that
|
||||||
-- u = 2**16 u1 + u0 and v = 2**16 v1 + v0
|
-- u = 2**16 u1 + u0 and v = 2**16 v1 + v0
|
||||||
-- Then
|
-- Then
|
||||||
-- u v = (2**16 u1 + u0) * (2**16 v1 + v0)
|
-- u v = (2**16 u1 + u0) * (2**16 v1 + v0)
|
||||||
-- = 2**32 u1 v1 + 2**16 (u0 v1 + u1 v0) + u0 v0
|
-- = 2**32 u1 v1 + 2**16 (u0 v1 + u1 v0) + u0 v0
|
||||||
assert(0 <= u and u <= 0xFFFFFFFF)
|
assert(0 <= u and u <= 0xFFFFFFFF)
|
||||||
assert(0 <= v and v <= 0xFFFFFFFF)
|
assert(0 <= v and v <= 0xFFFFFFFF)
|
||||||
local u0, u1 = bit.band(u, 0xFFFF), bit.rshift(u, 16)
|
local u0, u1 = bit.band(u, 0xFFFF), bit.rshift(u, 16)
|
||||||
local v0, v1 = bit.band(v, 0xFFFF), bit.rshift(v, 16)
|
local v0, v1 = bit.band(v, 0xFFFF), bit.rshift(v, 16)
|
||||||
-- t uses at most 49 bits, which is within the range of exact integer
|
-- t uses at most 49 bits, which is within the range of exact integer
|
||||||
-- precision of a Lua number.
|
-- precision of a Lua number.
|
||||||
local t = u0 * v0 + (u0 * v1 + u1 * v0) * 65536
|
local t = u0 * v0 + (u0 * v1 + u1 * v0) * 65536
|
||||||
return bit.band(t, 0xFFFFFFFF), u1 * v1 + bit.rshift(t, 32)
|
return bit.band(t, 0xFFFFFFFF), u1 * v1 + bit.rshift(t, 32)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Rotates the 64-bit integer defined by h:l left by one bit.
|
---Rotates the 64-bit integer defined by h:l left by one bit.
|
||||||
@@ -147,23 +147,23 @@ end
|
|||||||
--@param l The low-order 32 bits
|
--@param l The low-order 32 bits
|
||||||
--@return 64-bit rotated integer, as a pair of 32-bit integers.
|
--@return 64-bit rotated integer, as a pair of 32-bit integers.
|
||||||
local function rot64(h, l)
|
local function rot64(h, l)
|
||||||
local i
|
local i
|
||||||
|
|
||||||
assert(0 <= h and h <= 0xFFFFFFFF)
|
assert(0 <= h and h <= 0xFFFFFFFF)
|
||||||
assert(0 <= l and l <= 0xFFFFFFFF)
|
assert(0 <= l and l <= 0xFFFFFFFF)
|
||||||
|
|
||||||
local tmp = bit.band(h, 0x80000000) -- tmp = h & 0x80000000
|
local tmp = bit.band(h, 0x80000000) -- tmp = h & 0x80000000
|
||||||
h = bit.lshift(h, 1) -- h = h << 1
|
h = bit.lshift(h, 1) -- h = h << 1
|
||||||
h = bit.bor(h, bit.rshift(l, 31)) -- h = h | (l >> 31)
|
h = bit.bor(h, bit.rshift(l, 31)) -- h = h | (l >> 31)
|
||||||
l = bit.lshift(l, 1)
|
l = bit.lshift(l, 1)
|
||||||
if(tmp ~= 0) then
|
if(tmp ~= 0) then
|
||||||
l = bit.bor(l, 1)
|
l = bit.bor(l, 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
h = bit.band(h, 0xFFFFFFFF)
|
h = bit.band(h, 0xFFFFFFFF)
|
||||||
l = bit.band(l, 0xFFFFFFFF)
|
l = bit.band(l, 0xFFFFFFFF)
|
||||||
|
|
||||||
return h, l
|
return h, l
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -177,15 +177,15 @@ end
|
|||||||
--@param port The port to check
|
--@param port The port to check
|
||||||
--@return true if the port is blacklisted, false otherwise
|
--@return true if the port is blacklisted, false otherwise
|
||||||
local function is_blacklisted_port(port)
|
local function is_blacklisted_port(port)
|
||||||
local r, l
|
local r, l
|
||||||
|
|
||||||
local blacklist = { 0xFFFFFFFF, 0xFFFFFFFF, 0xF0F6BFBB, 0xBB5A5FF3, 0xF3977011, 0xEB67BFBF, 0x5F9BFAC8, 0x34D88091, 0x1E2282DF, 0x573402C4, 0xC0000084, 0x03000209, 0x01600002, 0x00005000, 0x801000C0, 0x00500040, 0x000000A1, 0x01000000, 0x01000000, 0x00022A20, 0x00000080, 0x04000000, 0x40020000, 0x88000000, 0x00000180, 0x00081000, 0x08801900, 0x00800B81, 0x00000280, 0x080002C0, 0x00A80000, 0x00008000, 0x00100040, 0x00100000, 0x00000000, 0x00000000, 0x10000008, 0x00000000, 0x00000000, 0x00000004, 0x00000002, 0x00000000, 0x00040000, 0x00000000, 0x00000000, 0x00000000, 0x00410000, 0x82000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008, 0x80000000, };
|
local blacklist = { 0xFFFFFFFF, 0xFFFFFFFF, 0xF0F6BFBB, 0xBB5A5FF3, 0xF3977011, 0xEB67BFBF, 0x5F9BFAC8, 0x34D88091, 0x1E2282DF, 0x573402C4, 0xC0000084, 0x03000209, 0x01600002, 0x00005000, 0x801000C0, 0x00500040, 0x000000A1, 0x01000000, 0x01000000, 0x00022A20, 0x00000080, 0x04000000, 0x40020000, 0x88000000, 0x00000180, 0x00081000, 0x08801900, 0x00800B81, 0x00000280, 0x080002C0, 0x00A80000, 0x00008000, 0x00100040, 0x00100000, 0x00000000, 0x00000000, 0x10000008, 0x00000000, 0x00000000, 0x00000004, 0x00000002, 0x00000000, 0x00040000, 0x00000000, 0x00000000, 0x00000000, 0x00410000, 0x82000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000008, 0x80000000, };
|
||||||
|
|
||||||
r = bit.rshift(port, 5)
|
r = bit.rshift(port, 5)
|
||||||
l = bit.lshift(1, bit.band(r, 0x1f))
|
l = bit.lshift(1, bit.band(r, 0x1f))
|
||||||
r = bit.rshift(r, 5)
|
r = bit.rshift(r, 5)
|
||||||
|
|
||||||
return (bit.band(blacklist[r + 1], l) ~= 0)
|
return (bit.band(blacklist[r + 1], l) ~= 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Generates the four random ports that Conficker uses, based on the current time and the IP address.
|
---Generates the four random ports that Conficker uses, based on the current time and the IP address.
|
||||||
@@ -194,57 +194,57 @@ end
|
|||||||
--@param seed The seed, based on the time (<code>floor((time - 345600) / 604800)</code>)
|
--@param seed The seed, based on the time (<code>floor((time - 345600) / 604800)</code>)
|
||||||
--@return An array of four ports; the first and third are TCP, and the second and fourth are UDP.
|
--@return An array of four ports; the first and third are TCP, and the second and fourth are UDP.
|
||||||
local function prng_generate_ports(ip, seed)
|
local function prng_generate_ports(ip, seed)
|
||||||
local ports = {0, 0, 0, 0}
|
local ports = {0, 0, 0, 0}
|
||||||
local v1, v2
|
local v1, v2
|
||||||
local port1, port2, shift1, shift2
|
local port1, port2, shift1, shift2
|
||||||
local i
|
local i
|
||||||
local magic = 0x015A4E35
|
local magic = 0x015A4E35
|
||||||
|
|
||||||
stdnse.print_debug(1, "Conficker: Generating ports based on ip (0x%08x) and seed (%d)", ip, seed)
|
stdnse.print_debug(1, "Conficker: Generating ports based on ip (0x%08x) and seed (%d)", ip, seed)
|
||||||
|
|
||||||
v1 = -(ip + 1)
|
v1 = -(ip + 1)
|
||||||
repeat
|
repeat
|
||||||
-- Loop 10 times to generate the first pair of ports
|
-- Loop 10 times to generate the first pair of ports
|
||||||
for i = 0, 9, 1 do
|
for i = 0, 9, 1 do
|
||||||
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
|
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
|
||||||
|
|
||||||
-- Add 1 to v1, handling overflows
|
-- Add 1 to v1, handling overflows
|
||||||
if(v1 ~= 0xFFFFFFFF) then
|
if(v1 ~= 0xFFFFFFFF) then
|
||||||
v1 = v1 + 1
|
v1 = v1 + 1
|
||||||
else
|
else
|
||||||
v1 = 0
|
v1 = 0
|
||||||
v2 = v2 + 1
|
v2 = v2 + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
v2 = bit.rshift(v2, i)
|
v2 = bit.rshift(v2, i)
|
||||||
|
|
||||||
ports[(i % 2) + 1] = bit.bxor(bit.band(v2, 0xFFFF), ports[(i % 2) + 1])
|
ports[(i % 2) + 1] = bit.bxor(bit.band(v2, 0xFFFF), ports[(i % 2) + 1])
|
||||||
end
|
end
|
||||||
until(is_blacklisted_port(ports[1]) == false and is_blacklisted_port(ports[2]) == false and ports[1] ~= ports[2])
|
until(is_blacklisted_port(ports[1]) == false and is_blacklisted_port(ports[2]) == false and ports[1] ~= ports[2])
|
||||||
|
|
||||||
-- Update the accumlator with the seed
|
-- Update the accumlator with the seed
|
||||||
v1 = bit.bxor(v1, seed)
|
v1 = bit.bxor(v1, seed)
|
||||||
|
|
||||||
-- Loop 10 more times to generate the second pair of ports
|
-- Loop 10 more times to generate the second pair of ports
|
||||||
repeat
|
repeat
|
||||||
for i = 0, 9, 1 do
|
for i = 0, 9, 1 do
|
||||||
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
|
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
|
||||||
|
|
||||||
-- Add 1 to v1, handling overflows
|
-- Add 1 to v1, handling overflows
|
||||||
if(v1 ~= 0xFFFFFFFF) then
|
if(v1 ~= 0xFFFFFFFF) then
|
||||||
v1 = v1 + 1
|
v1 = v1 + 1
|
||||||
else
|
else
|
||||||
v1 = 0
|
v1 = 0
|
||||||
v2 = v2 + 1
|
v2 = v2 + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
v2 = bit.rshift(v2, i)
|
v2 = bit.rshift(v2, i)
|
||||||
|
|
||||||
ports[(i % 2) + 3] = bit.bxor(bit.band(v2, 0xFFFF), ports[(i % 2) + 3])
|
ports[(i % 2) + 3] = bit.bxor(bit.band(v2, 0xFFFF), ports[(i % 2) + 3])
|
||||||
end
|
end
|
||||||
until(is_blacklisted_port(ports[3]) == false and is_blacklisted_port(ports[4]) == false and ports[3] ~= ports[4])
|
until(is_blacklisted_port(ports[3]) == false and is_blacklisted_port(ports[4]) == false and ports[3] ~= ports[4])
|
||||||
|
|
||||||
return {ports[1], ports[2], ports[3], ports[4]}
|
return {ports[1], ports[2], ports[3], ports[4]}
|
||||||
end
|
end
|
||||||
|
|
||||||
---Calculate a checksum for the data. This checksum is appended to every Conficker packet before the random noise.
|
---Calculate a checksum for the data. This checksum is appended to every Conficker packet before the random noise.
|
||||||
@@ -253,24 +253,24 @@ end
|
|||||||
--@param data The data to create a checksum for.
|
--@param data The data to create a checksum for.
|
||||||
--@return An integer representing the checksum.
|
--@return An integer representing the checksum.
|
||||||
local function p2p_checksum(data)
|
local function p2p_checksum(data)
|
||||||
local pos, i
|
local pos, i
|
||||||
local hash = #data
|
local hash = #data
|
||||||
|
|
||||||
stdnse.print_debug(2, "Conficker: Calculating checksum for %d-byte buffer", #data)
|
stdnse.print_debug(2, "Conficker: Calculating checksum for %d-byte buffer", #data)
|
||||||
|
|
||||||
-- Get the first character
|
-- Get the first character
|
||||||
pos, i = bin.unpack("<C", data)
|
pos, i = bin.unpack("<C", data)
|
||||||
while i ~= nil do
|
while i ~= nil do
|
||||||
local h = bit.bxor(hash, i)
|
local h = bit.bxor(hash, i)
|
||||||
-- Incorporate the current character into the checksum
|
-- Incorporate the current character into the checksum
|
||||||
hash = bit.bor((h + h), bit.rshift(h, 31))
|
hash = bit.bor((h + h), bit.rshift(h, 31))
|
||||||
hash = bit.band(hash, 0xFFFFFFFF)
|
hash = bit.band(hash, 0xFFFFFFFF)
|
||||||
|
|
||||||
-- Get the next character
|
-- Get the next character
|
||||||
pos, i = bin.unpack("<C", data, pos)
|
pos, i = bin.unpack("<C", data, pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
return hash
|
return hash
|
||||||
end
|
end
|
||||||
|
|
||||||
---Encrypt/decrypt the buffer with a simple xor-based symmetric encryption. It uses a 64-bit key, represented
|
---Encrypt/decrypt the buffer with a simple xor-based symmetric encryption. It uses a 64-bit key, represented
|
||||||
@@ -282,30 +282,30 @@ end
|
|||||||
--@param key2 The high-order 32 bits in the key.
|
--@param key2 The high-order 32 bits in the key.
|
||||||
--@return The encrypted (or decrypted) data.
|
--@return The encrypted (or decrypted) data.
|
||||||
local function p2p_cipher(packet, key1, key2)
|
local function p2p_cipher(packet, key1, key2)
|
||||||
local i
|
local i
|
||||||
local buf = ""
|
local buf = ""
|
||||||
|
|
||||||
for i = 1, #packet, 1 do
|
for i = 1, #packet, 1 do
|
||||||
-- Do a 64-bit rotate on key1:key2
|
-- Do a 64-bit rotate on key1:key2
|
||||||
key2, key1 = rot64(key2, key1)
|
key2, key1 = rot64(key2, key1)
|
||||||
|
|
||||||
-- Generate the key (the right-most byte)
|
-- Generate the key (the right-most byte)
|
||||||
local k = bit.band(key1, 0x0FF)
|
local k = bit.band(key1, 0x0FF)
|
||||||
|
|
||||||
-- Xor the current character and add it to the encrypted buffer
|
-- Xor the current character and add it to the encrypted buffer
|
||||||
buf = buf .. string.char(bit.bxor(string.byte(packet, i), k))
|
buf = buf .. string.char(bit.bxor(string.byte(packet, i), k))
|
||||||
|
|
||||||
-- Update the key with 'k'
|
-- Update the key with 'k'
|
||||||
key1 = key1 + k
|
key1 = key1 + k
|
||||||
if(key1 > 0xFFFFFFFF) then
|
if(key1 > 0xFFFFFFFF) then
|
||||||
-- Handle overflows
|
-- Handle overflows
|
||||||
key2 = key2 + (bit.rshift(key1, 32))
|
key2 = key2 + (bit.rshift(key1, 32))
|
||||||
key2 = bit.band(key2, 0xFFFFFFFF)
|
key2 = bit.band(key2, 0xFFFFFFFF)
|
||||||
key1 = bit.band(key1, 0xFFFFFFFF)
|
key1 = bit.band(key1, 0xFFFFFFFF)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return buf
|
return buf
|
||||||
end
|
end
|
||||||
|
|
||||||
---Decrypt the packet, verify it, and parse it. This function will fail with an error if the packet can't be
|
---Decrypt the packet, verify it, and parse it. This function will fail with an error if the packet can't be
|
||||||
@@ -317,96 +317,96 @@ end
|
|||||||
--@return (status, result) If status is true, result is a table (including 'hash' and 'real_hash'). If status
|
--@return (status, result) If status is true, result is a table (including 'hash' and 'real_hash'). If status
|
||||||
-- is false, result is a string that indicates why the parse failed.
|
-- is false, result is a string that indicates why the parse failed.
|
||||||
function p2p_parse(packet)
|
function p2p_parse(packet)
|
||||||
local pos = 1
|
local pos = 1
|
||||||
local data = {}
|
local data = {}
|
||||||
|
|
||||||
-- Get the key
|
-- Get the key
|
||||||
pos, data['key1'], data['key2'] = bin.unpack("<II", packet, pos)
|
pos, data['key1'], data['key2'] = bin.unpack("<II", packet, pos)
|
||||||
if(data['key2'] == nil) then
|
if(data['key2'] == nil) then
|
||||||
return false, "Packet was too short [1]"
|
return false, "Packet was too short [1]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Decrypt the second half of the packet using the key
|
-- Decrypt the second half of the packet using the key
|
||||||
packet = string.sub(packet, 1, pos - 1) .. p2p_cipher(string.sub(packet, pos), data['key1'], data['key2'])
|
packet = string.sub(packet, 1, pos - 1) .. p2p_cipher(string.sub(packet, pos), data['key1'], data['key2'])
|
||||||
|
|
||||||
-- Parse the flags
|
-- Parse the flags
|
||||||
pos, data['flags'] = bin.unpack("<S", packet, pos)
|
pos, data['flags'] = bin.unpack("<S", packet, pos)
|
||||||
if(data['flags'] == nil) then
|
if(data['flags'] == nil) then
|
||||||
return false, "Packet was too short [2]"
|
return false, "Packet was too short [2]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the IP, if it's present
|
-- Get the IP, if it's present
|
||||||
if(bit.band(data['flags'], mode_flags.FLAG_IP_INCLUDED) ~= 0) then
|
if(bit.band(data['flags'], mode_flags.FLAG_IP_INCLUDED) ~= 0) then
|
||||||
pos, data['ip'], data['port'] = bin.unpack("<IS", packet, pos)
|
pos, data['ip'], data['port'] = bin.unpack("<IS", packet, pos)
|
||||||
if(data['ip'] == nil) then
|
if(data['ip'] == nil) then
|
||||||
return false, "Packet was too short [3]"
|
return false, "Packet was too short [3]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read the first unknown value, if present
|
-- Read the first unknown value, if present
|
||||||
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN0_INCLUDED) ~= 0) then
|
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN0_INCLUDED) ~= 0) then
|
||||||
pos, data['unknown0'] = bin.unpack("<I", packet, pos)
|
pos, data['unknown0'] = bin.unpack("<I", packet, pos)
|
||||||
if(data['unknown0'] == nil) then
|
if(data['unknown0'] == nil) then
|
||||||
return false, "Packet was too short [3]"
|
return false, "Packet was too short [3]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read the second unknown value, if present
|
-- Read the second unknown value, if present
|
||||||
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN1_INCLUDED) ~= 0) then
|
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN1_INCLUDED) ~= 0) then
|
||||||
pos, data['unknown1'] = bin.unpack("<I", packet, pos)
|
pos, data['unknown1'] = bin.unpack("<I", packet, pos)
|
||||||
if(data['unknown1'] == nil) then
|
if(data['unknown1'] == nil) then
|
||||||
return false, "Packet was too short [4]"
|
return false, "Packet was too short [4]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read the data, if present
|
-- Read the data, if present
|
||||||
if(bit.band(data['flags'], mode_flags.FLAG_DATA_INCLUDED) ~= 0) then
|
if(bit.band(data['flags'], mode_flags.FLAG_DATA_INCLUDED) ~= 0) then
|
||||||
pos, data['data_flags'], data['data_length'] = bin.unpack("<CS", packet, pos)
|
pos, data['data_flags'], data['data_length'] = bin.unpack("<CS", packet, pos)
|
||||||
if(data['data_length'] == nil) then
|
if(data['data_length'] == nil) then
|
||||||
return false, "Packet was too short [5]"
|
return false, "Packet was too short [5]"
|
||||||
end
|
end
|
||||||
pos, data['data'] = bin.unpack(string.format("A%d", data['data_length']), packet, pos)
|
pos, data['data'] = bin.unpack(string.format("A%d", data['data_length']), packet, pos)
|
||||||
if(data['data'] == nil) then
|
if(data['data'] == nil) then
|
||||||
return false, "Packet was too short [6]"
|
return false, "Packet was too short [6]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Read the sysinfo, if present
|
-- Read the sysinfo, if present
|
||||||
if(bit.band(data['flags'], mode_flags.FLAG_SYSINFO_INCLUDED) ~= 0) then
|
if(bit.band(data['flags'], mode_flags.FLAG_SYSINFO_INCLUDED) ~= 0) then
|
||||||
pos, data['sysinfo_systemtestflags'],
|
pos, data['sysinfo_systemtestflags'],
|
||||||
data['sysinfo_os_major'],
|
data['sysinfo_os_major'],
|
||||||
data['sysinfo_os_minor'],
|
data['sysinfo_os_minor'],
|
||||||
data['sysinfo_os_build'],
|
data['sysinfo_os_build'],
|
||||||
data['sysinfo_os_servicepack_major'],
|
data['sysinfo_os_servicepack_major'],
|
||||||
data['sysinfo_os_servicepack_minor'],
|
data['sysinfo_os_servicepack_minor'],
|
||||||
data['sysinfo_ntdll_translation_file_information'],
|
data['sysinfo_ntdll_translation_file_information'],
|
||||||
data['sysinfo_prng_sample'],
|
data['sysinfo_prng_sample'],
|
||||||
data['sysinfo_unknown0'],
|
data['sysinfo_unknown0'],
|
||||||
data['sysinfo_unknown1'],
|
data['sysinfo_unknown1'],
|
||||||
data['sysinfo_unknown2'],
|
data['sysinfo_unknown2'],
|
||||||
data['sysinfo_unknown3'],
|
data['sysinfo_unknown3'],
|
||||||
data['sysinfo_unknown4'] = bin.unpack("<SCCSCCSISSISS", packet, pos)
|
data['sysinfo_unknown4'] = bin.unpack("<SCCSCCSISSISS", packet, pos)
|
||||||
if(data['sysinfo_unknown4'] == nil) then
|
if(data['sysinfo_unknown4'] == nil) then
|
||||||
return false, "Packet was too short [7]"
|
return false, "Packet was too short [7]"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Pull out the data that's used in the hash
|
-- Pull out the data that's used in the hash
|
||||||
data['hash_data'] = string.sub(packet, 1, pos - 1)
|
data['hash_data'] = string.sub(packet, 1, pos - 1)
|
||||||
|
|
||||||
-- Read the hash
|
-- Read the hash
|
||||||
pos, data['hash'] = bin.unpack("<I", packet, pos)
|
pos, data['hash'] = bin.unpack("<I", packet, pos)
|
||||||
if(data['hash'] == nil) then
|
if(data['hash'] == nil) then
|
||||||
return false, "Packet was too short [8]"
|
return false, "Packet was too short [8]"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Record the noise
|
-- Record the noise
|
||||||
data['noise'] = string.sub(packet, pos)
|
data['noise'] = string.sub(packet, pos)
|
||||||
|
|
||||||
-- Generate the actual hash (we're going to ignore it for now, but it can be checked higher up)
|
-- Generate the actual hash (we're going to ignore it for now, but it can be checked higher up)
|
||||||
data['real_hash'] = p2p_checksum(data['hash_data'])
|
data['real_hash'] = p2p_checksum(data['hash_data'])
|
||||||
|
|
||||||
return true, data
|
return true, data
|
||||||
end
|
end
|
||||||
|
|
||||||
---Create a peer to peer packet for the given protocol.
|
---Create a peer to peer packet for the given protocol.
|
||||||
@@ -416,46 +416,46 @@ end
|
|||||||
--@param do_encryption (optional) If set to false, packets aren't encrypted (the key '0' is used). Useful
|
--@param do_encryption (optional) If set to false, packets aren't encrypted (the key '0' is used). Useful
|
||||||
-- for testing. Default: true.
|
-- for testing. Default: true.
|
||||||
local function p2p_create_packet(protocol, do_encryption)
|
local function p2p_create_packet(protocol, do_encryption)
|
||||||
assert(protocol == "tcp" or protocol == "udp")
|
assert(protocol == "tcp" or protocol == "udp")
|
||||||
|
|
||||||
local key1 = math.random(1, 0x7FFFFFFF)
|
local key1 = math.random(1, 0x7FFFFFFF)
|
||||||
local key2 = math.random(1, 0x7FFFFFFF)
|
local key2 = math.random(1, 0x7FFFFFFF)
|
||||||
|
|
||||||
-- A key of 0 disables the encryption
|
-- A key of 0 disables the encryption
|
||||||
if(do_encryption == false) then
|
if(do_encryption == false) then
|
||||||
key1 = 0
|
key1 = 0
|
||||||
key2 = 0
|
key2 = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local flags = 0
|
local flags = 0
|
||||||
|
|
||||||
-- Set a couple flags that we need (we don't send any optional data)
|
-- Set a couple flags that we need (we don't send any optional data)
|
||||||
flags = bit.bor(flags, mode_flags.FLAG_MODE)
|
flags = bit.bor(flags, mode_flags.FLAG_MODE)
|
||||||
flags = bit.bor(flags, mode_flags.FLAG_ENCODED)
|
flags = bit.bor(flags, mode_flags.FLAG_ENCODED)
|
||||||
-- flags = bit.bor(flags, mode_flags.FLAG_LOCAL_ACK)
|
-- flags = bit.bor(flags, mode_flags.FLAG_LOCAL_ACK)
|
||||||
-- Set the special TCP flag
|
-- Set the special TCP flag
|
||||||
if(protocol == "tcp") then
|
if(protocol == "tcp") then
|
||||||
flags = bit.bor(flags, mode_flags.FLAG_IS_TCP)
|
flags = bit.bor(flags, mode_flags.FLAG_IS_TCP)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add the key and flags that are always present (and skip over the boring stuff)
|
-- Add the key and flags that are always present (and skip over the boring stuff)
|
||||||
local packet = ""
|
local packet = ""
|
||||||
packet = packet .. bin.pack("<II", key1, key2)
|
packet = packet .. bin.pack("<II", key1, key2)
|
||||||
packet = packet .. bin.pack("<S", flags)
|
packet = packet .. bin.pack("<S", flags)
|
||||||
|
|
||||||
-- Generate the checksum for the packet
|
-- Generate the checksum for the packet
|
||||||
local hash = p2p_checksum(packet)
|
local hash = p2p_checksum(packet)
|
||||||
packet = packet .. bin.pack("<I", hash)
|
packet = packet .. bin.pack("<I", hash)
|
||||||
|
|
||||||
-- Encrypt the full packet, except for the key and optional length
|
-- Encrypt the full packet, except for the key and optional length
|
||||||
packet = string.sub(packet, 1, 8) .. p2p_cipher(string.sub(packet, 9), key1, key2)
|
packet = string.sub(packet, 1, 8) .. p2p_cipher(string.sub(packet, 9), key1, key2)
|
||||||
|
|
||||||
-- Add the length in front if it's TCP
|
-- Add the length in front if it's TCP
|
||||||
if(protocol == "tcp") then
|
if(protocol == "tcp") then
|
||||||
packet = bin.pack("<SA", #packet, packet)
|
packet = bin.pack("<SA", #packet, packet)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, packet
|
return true, packet
|
||||||
end
|
end
|
||||||
|
|
||||||
---Checks if conficker is present on the given port/protocol. The ports Conficker uses are fairly standard, so
|
---Checks if conficker is present on the given port/protocol. The ports Conficker uses are fairly standard, so
|
||||||
@@ -467,171 +467,171 @@ end
|
|||||||
-- Conficker, <code>false</code> = no Conficker). If status is true, data is the table of information returned by
|
-- Conficker, <code>false</code> = no Conficker). If status is true, data is the table of information returned by
|
||||||
-- Conficker.
|
-- Conficker.
|
||||||
local function conficker_check(ip, port, protocol)
|
local function conficker_check(ip, port, protocol)
|
||||||
local status, packet
|
local status, packet
|
||||||
local socket
|
local socket
|
||||||
local response
|
local response
|
||||||
|
|
||||||
status, packet = p2p_create_packet(protocol)
|
status, packet = p2p_create_packet(protocol)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, packet
|
return false, packet
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try to connect to the first socket
|
-- Try to connect to the first socket
|
||||||
socket = nmap.new_socket()
|
socket = nmap.new_socket()
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
status, response = socket:connect(ip, port, protocol)
|
status, response = socket:connect(ip, port, protocol)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, "Couldn't establish connection (" .. response .. ")"
|
return false, "Couldn't establish connection (" .. response .. ")"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send the packet
|
-- Send the packet
|
||||||
socket:send(packet)
|
socket:send(packet)
|
||||||
|
|
||||||
-- Read a response (2 bytes minimum, because that's the TCP length)
|
-- Read a response (2 bytes minimum, because that's the TCP length)
|
||||||
status, response = socket:receive_bytes(2)
|
status, response = socket:receive_bytes(2)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, "Couldn't receive bytes: " .. response
|
return false, "Couldn't receive bytes: " .. response
|
||||||
elseif(response == "ERROR") then
|
elseif(response == "ERROR") then
|
||||||
return false, "Failed to receive data"
|
return false, "Failed to receive data"
|
||||||
elseif(response == "TIMEOUT") then
|
elseif(response == "TIMEOUT") then
|
||||||
return false, "Timeout"
|
return false, "Timeout"
|
||||||
elseif(response == "EOF") then
|
elseif(response == "EOF") then
|
||||||
return false, "Couldn't connect"
|
return false, "Couldn't connect"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If it's TCP, get the length and make sure we have the full packet
|
-- If it's TCP, get the length and make sure we have the full packet
|
||||||
if(protocol == "tcp") then
|
if(protocol == "tcp") then
|
||||||
local _, length = bin.unpack("<S", response, 1)
|
local _, length = bin.unpack("<S", response, 1)
|
||||||
|
|
||||||
while length > (#response - 2) do
|
while length > (#response - 2) do
|
||||||
local response2
|
local response2
|
||||||
|
|
||||||
status, response2 = socket:receive_bytes(2)
|
status, response2 = socket:receive_bytes(2)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, "Couldn't receive bytes: " .. response2
|
return false, "Couldn't receive bytes: " .. response2
|
||||||
elseif(response2 == "ERROR") then
|
elseif(response2 == "ERROR") then
|
||||||
return false, "Failed to receive data"
|
return false, "Failed to receive data"
|
||||||
elseif(response2 == "TIMEOUT") then
|
elseif(response2 == "TIMEOUT") then
|
||||||
return false, "Timeout"
|
return false, "Timeout"
|
||||||
elseif(response2 == "EOF") then
|
elseif(response2 == "EOF") then
|
||||||
return false, "Couldn't connect"
|
return false, "Couldn't connect"
|
||||||
end
|
end
|
||||||
|
|
||||||
response = response .. response2
|
response = response .. response2
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Remove the 'length' bytes
|
-- Remove the 'length' bytes
|
||||||
response = string.sub(response, 3)
|
response = string.sub(response, 3)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Close the socket
|
-- Close the socket
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
local status, result = p2p_parse(response)
|
local status, result = p2p_parse(response)
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, "Data received, but wasn't Conficker data: " .. result
|
return false, "Data received, but wasn't Conficker data: " .. result
|
||||||
end
|
end
|
||||||
|
|
||||||
if(result['hash'] ~= result['real_hash']) then
|
if(result['hash'] ~= result['real_hash']) then
|
||||||
return false, "Data received, but checksum was invalid (possibly INFECTED)"
|
return false, "Data received, but checksum was invalid (possibly INFECTED)"
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, "Received valid data", result
|
return true, "Received valid data", result
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local tcp_ports = {}
|
local tcp_ports = {}
|
||||||
local udp_ports = {}
|
local udp_ports = {}
|
||||||
local response = {}
|
local response = {}
|
||||||
local i
|
local i
|
||||||
local port, protocol
|
local port, protocol
|
||||||
local count = 0
|
local count = 0
|
||||||
local checks = 0
|
local checks = 0
|
||||||
|
|
||||||
-- Generate a complete list of valid ports
|
-- Generate a complete list of valid ports
|
||||||
if(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
|
if(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
|
||||||
for i = 1, 65535, 1 do
|
for i = 1, 65535, 1 do
|
||||||
if(not(is_blacklisted_port(i))) then
|
if(not(is_blacklisted_port(i))) then
|
||||||
local tcp = nmap.get_port_state(host, {number=i, protocol="tcp"})
|
local tcp = nmap.get_port_state(host, {number=i, protocol="tcp"})
|
||||||
if(tcp ~= nil and tcp.state == "open") then
|
if(tcp ~= nil and tcp.state == "open") then
|
||||||
tcp_ports[i] = true
|
tcp_ports[i] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local udp = nmap.get_port_state(host, {number=i, protocol="udp"})
|
local udp = nmap.get_port_state(host, {number=i, protocol="udp"})
|
||||||
if(udp ~= nil and (udp.state == "open" or udp.state == "open|filtered")) then
|
if(udp ~= nil and (udp.state == "open" or udp.state == "open|filtered")) then
|
||||||
udp_ports[i] = true
|
udp_ports[i] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Generate ports based on the ip and time
|
-- Generate ports based on the ip and time
|
||||||
local seed = math.floor((os.time() - 345600) / 604800)
|
local seed = math.floor((os.time() - 345600) / 604800)
|
||||||
local ip = host.ip
|
local ip = host.ip
|
||||||
|
|
||||||
-- Use the provided IP, if it exists
|
-- Use the provided IP, if it exists
|
||||||
if(nmap.registry.args.realip ~= nil) then
|
if(nmap.registry.args.realip ~= nil) then
|
||||||
ip = nmap.registry.args.realip
|
ip = nmap.registry.args.realip
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Reverse the IP's endianness
|
-- Reverse the IP's endianness
|
||||||
ip = ipOps.todword(ip)
|
ip = ipOps.todword(ip)
|
||||||
ip = bin.pack(">I", ip)
|
ip = bin.pack(">I", ip)
|
||||||
local _
|
local _
|
||||||
_, ip = bin.unpack("<I", ip)
|
_, ip = bin.unpack("<I", ip)
|
||||||
|
|
||||||
-- Generate the ports
|
-- Generate the ports
|
||||||
local generated_ports = prng_generate_ports(ip, seed)
|
local generated_ports = prng_generate_ports(ip, seed)
|
||||||
tcp_ports[generated_ports[1]] = true
|
tcp_ports[generated_ports[1]] = true
|
||||||
tcp_ports[generated_ports[3]] = true
|
tcp_ports[generated_ports[3]] = true
|
||||||
udp_ports[generated_ports[2]] = true
|
udp_ports[generated_ports[2]] = true
|
||||||
udp_ports[generated_ports[4]] = true
|
udp_ports[generated_ports[4]] = true
|
||||||
|
|
||||||
table.insert(response, string.format("Checking for Conficker.C or higher..."))
|
table.insert(response, string.format("Checking for Conficker.C or higher..."))
|
||||||
|
|
||||||
-- Check the TCP ports
|
-- Check the TCP ports
|
||||||
for port in pairs(tcp_ports) do
|
for port in pairs(tcp_ports) do
|
||||||
local status, reason
|
local status, reason
|
||||||
|
|
||||||
status, reason = conficker_check(host.ip, port, "tcp")
|
status, reason = conficker_check(host.ip, port, "tcp")
|
||||||
checks = checks + 1
|
checks = checks + 1
|
||||||
|
|
||||||
if(status == true) then
|
if(status == true) then
|
||||||
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "tcp", reason))
|
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "tcp", reason))
|
||||||
count = count + 1
|
count = count + 1
|
||||||
else
|
else
|
||||||
table.insert(response, string.format("Check %d (port %d/%s): CLEAN (%s)", checks, port, "tcp", reason))
|
table.insert(response, string.format("Check %d (port %d/%s): CLEAN (%s)", checks, port, "tcp", reason))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check the UDP ports
|
-- Check the UDP ports
|
||||||
for port in pairs(udp_ports) do
|
for port in pairs(udp_ports) do
|
||||||
local status, reason
|
local status, reason
|
||||||
|
|
||||||
status, reason = conficker_check(host.ip, port, "udp")
|
status, reason = conficker_check(host.ip, port, "udp")
|
||||||
checks = checks + 1
|
checks = checks + 1
|
||||||
|
|
||||||
if(status == true) then
|
if(status == true) then
|
||||||
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "udp", reason))
|
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "udp", reason))
|
||||||
count = count + 1
|
count = count + 1
|
||||||
else
|
else
|
||||||
table.insert(response, string.format("| Check %d (port %d/%s): CLEAN (%s)", checks, port, "udp", reason))
|
table.insert(response, string.format("| Check %d (port %d/%s): CLEAN (%s)", checks, port, "udp", reason))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check how many INFECTED hits we got
|
-- Check how many INFECTED hits we got
|
||||||
if(count == 0) then
|
if(count == 0) then
|
||||||
if (nmap.verbosity() > 1) then
|
if (nmap.verbosity() > 1) then
|
||||||
table.insert(response, string.format("%d/%d checks are positive: Host is CLEAN or ports are blocked", count, checks))
|
table.insert(response, string.format("%d/%d checks are positive: Host is CLEAN or ports are blocked", count, checks))
|
||||||
else
|
else
|
||||||
response = ''
|
response = ''
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(response, string.format("%d/%d checks are positive: Host is likely INFECTED", count, checks))
|
table.insert(response, string.format("%d/%d checks are positive: Host is likely INFECTED", count, checks))
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -57,346 +57,346 @@ local RETRIES = 1
|
|||||||
-- here since we skip down the list based on the outgoing interface
|
-- here since we skip down the list based on the outgoing interface
|
||||||
-- so its no harm.
|
-- so its no harm.
|
||||||
local MTUS = {
|
local MTUS = {
|
||||||
65535,
|
65535,
|
||||||
32000,
|
32000,
|
||||||
17914,
|
17914,
|
||||||
8166,
|
8166,
|
||||||
4352,
|
4352,
|
||||||
2002,
|
2002,
|
||||||
1492,
|
1492,
|
||||||
1006,
|
1006,
|
||||||
508,
|
508,
|
||||||
296,
|
296,
|
||||||
68
|
68
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Find the index in MTUS{} to use based on the MTU +new+. If +new+ is in
|
-- Find the index in MTUS{} to use based on the MTU +new+. If +new+ is in
|
||||||
-- between values in MTUS, then insert it into the table appropriately.
|
-- between values in MTUS, then insert it into the table appropriately.
|
||||||
local searchmtu = function(cidx, new)
|
local searchmtu = function(cidx, new)
|
||||||
if new == 0 then
|
if new == 0 then
|
||||||
return cidx
|
return cidx
|
||||||
end
|
end
|
||||||
|
|
||||||
while cidx <= #MTUS do
|
while cidx <= #MTUS do
|
||||||
if new >= MTUS[cidx] then
|
if new >= MTUS[cidx] then
|
||||||
if new ~= MTUS[cidx] then
|
if new ~= MTUS[cidx] then
|
||||||
table.insert(MTUS, cidx, new)
|
table.insert(MTUS, cidx, new)
|
||||||
end
|
end
|
||||||
return cidx
|
return cidx
|
||||||
end
|
end
|
||||||
cidx = cidx + 1
|
cidx = cidx + 1
|
||||||
end
|
end
|
||||||
return cidx
|
return cidx
|
||||||
end
|
end
|
||||||
|
|
||||||
local dport = function(ip)
|
local dport = function(ip)
|
||||||
if ip.ip_p == IPPROTO_TCP then
|
if ip.ip_p == IPPROTO_TCP then
|
||||||
return ip.tcp_dport
|
return ip.tcp_dport
|
||||||
elseif ip.ip_p == IPPROTO_UDP then
|
elseif ip.ip_p == IPPROTO_UDP then
|
||||||
return ip.udp_dport
|
return ip.udp_dport
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local sport = function(ip)
|
local sport = function(ip)
|
||||||
if ip.ip_p == IPPROTO_TCP then
|
if ip.ip_p == IPPROTO_TCP then
|
||||||
return ip.tcp_sport
|
return ip.tcp_sport
|
||||||
elseif ip.ip_p == IPPROTO_UDP then
|
elseif ip.ip_p == IPPROTO_UDP then
|
||||||
return ip.udp_sport
|
return ip.udp_sport
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Checks how we should react to this packet
|
-- Checks how we should react to this packet
|
||||||
local checkpkt = function(reply, orig)
|
local checkpkt = function(reply, orig)
|
||||||
local ip = packet.Packet:new(reply, reply:len())
|
local ip = packet.Packet:new(reply, reply:len())
|
||||||
|
|
||||||
if ip.ip_p == IPPROTO_ICMP then
|
if ip.ip_p == IPPROTO_ICMP then
|
||||||
if ip.icmp_type ~= 3 then
|
if ip.icmp_type ~= 3 then
|
||||||
return "recap"
|
return "recap"
|
||||||
end
|
end
|
||||||
-- Port Unreachable
|
-- Port Unreachable
|
||||||
if ip.icmp_code == 3 then
|
if ip.icmp_code == 3 then
|
||||||
local is = ip.buf:sub(ip.icmp_offset + 9)
|
local is = ip.buf:sub(ip.icmp_offset + 9)
|
||||||
local ip2 = packet.Packet:new(is, is:len())
|
local ip2 = packet.Packet:new(is, is:len())
|
||||||
|
|
||||||
-- Check sent packet against ICMP payload
|
-- Check sent packet against ICMP payload
|
||||||
if ip2.ip_p ~= IPPROTO_UDP or
|
if ip2.ip_p ~= IPPROTO_UDP or
|
||||||
ip2.ip_p ~= orig.ip_p or
|
ip2.ip_p ~= orig.ip_p or
|
||||||
ip2.ip_bin_src ~= orig.ip_bin_src or
|
ip2.ip_bin_src ~= orig.ip_bin_src or
|
||||||
ip2.ip_bin_dst ~= orig.ip_bin_dst or
|
ip2.ip_bin_dst ~= orig.ip_bin_dst or
|
||||||
sport(ip2) ~= sport(orig) or
|
sport(ip2) ~= sport(orig) or
|
||||||
dport(ip2) ~= dport(orig) then
|
dport(ip2) ~= dport(orig) then
|
||||||
return "recap"
|
return "recap"
|
||||||
end
|
end
|
||||||
|
|
||||||
return "gotreply"
|
return "gotreply"
|
||||||
end
|
end
|
||||||
-- Frag needed, DF set
|
-- Frag needed, DF set
|
||||||
if ip.icmp_code == 4 then
|
if ip.icmp_code == 4 then
|
||||||
local val = ip:u16(ip.icmp_offset + 6)
|
local val = ip:u16(ip.icmp_offset + 6)
|
||||||
return "nextmtu", val
|
return "nextmtu", val
|
||||||
end
|
end
|
||||||
return "recap"
|
return "recap"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ip.ip_p ~= orig.ip_p or
|
if ip.ip_p ~= orig.ip_p or
|
||||||
ip.ip_bin_src ~= orig.ip_bin_dst or
|
ip.ip_bin_src ~= orig.ip_bin_dst or
|
||||||
ip.ip_bin_dst ~= orig.ip_bin_src or
|
ip.ip_bin_dst ~= orig.ip_bin_src or
|
||||||
dport(ip) ~= sport(orig) or
|
dport(ip) ~= sport(orig) or
|
||||||
sport(ip) ~= dport(orig) then
|
sport(ip) ~= dport(orig) then
|
||||||
return "recap"
|
return "recap"
|
||||||
end
|
end
|
||||||
|
|
||||||
return "gotreply"
|
return "gotreply"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- This is all we can use since we can get various protocols back from
|
-- This is all we can use since we can get various protocols back from
|
||||||
-- different hosts
|
-- different hosts
|
||||||
local check = function(layer3)
|
local check = function(layer3)
|
||||||
local ip = packet.Packet:new(layer3, layer3:len())
|
local ip = packet.Packet:new(layer3, layer3:len())
|
||||||
return bin.pack('A', ip.ip_bin_dst)
|
return bin.pack('A', ip.ip_bin_dst)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Updates a packet's info and calculates checksum
|
-- Updates a packet's info and calculates checksum
|
||||||
local updatepkt = function(ip)
|
local updatepkt = function(ip)
|
||||||
if ip.ip_p == IPPROTO_TCP then
|
if ip.ip_p == IPPROTO_TCP then
|
||||||
ip:tcp_set_sport(math.random(0x401, 0xffff))
|
ip:tcp_set_sport(math.random(0x401, 0xffff))
|
||||||
ip:tcp_set_seq(math.random(1, 0x7fffffff))
|
ip:tcp_set_seq(math.random(1, 0x7fffffff))
|
||||||
ip:tcp_count_checksum()
|
ip:tcp_count_checksum()
|
||||||
elseif ip.ip_p == IPPROTO_UDP then
|
elseif ip.ip_p == IPPROTO_UDP then
|
||||||
ip:udp_set_sport(math.random(0x401, 0xffff))
|
ip:udp_set_sport(math.random(0x401, 0xffff))
|
||||||
ip:udp_set_length(ip.ip_len - ip.ip_hl * 4)
|
ip:udp_set_length(ip.ip_len - ip.ip_hl * 4)
|
||||||
ip:udp_count_checksum()
|
ip:udp_count_checksum()
|
||||||
end
|
end
|
||||||
ip:ip_count_checksum()
|
ip:ip_count_checksum()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set up packet header and data to satisfy a certain MTU
|
-- Set up packet header and data to satisfy a certain MTU
|
||||||
local setmtu = function(pkt, mtu)
|
local setmtu = function(pkt, mtu)
|
||||||
if pkt.ip_len < mtu then
|
if pkt.ip_len < mtu then
|
||||||
pkt.buf = pkt.buf .. string.rep("\0", mtu - pkt.ip_len)
|
pkt.buf = pkt.buf .. string.rep("\0", mtu - pkt.ip_len)
|
||||||
else
|
else
|
||||||
pkt.buf = pkt.buf:sub(1, mtu)
|
pkt.buf = pkt.buf:sub(1, mtu)
|
||||||
end
|
end
|
||||||
|
|
||||||
pkt:ip_set_len(mtu)
|
pkt:ip_set_len(mtu)
|
||||||
pkt.packet_length = mtu
|
pkt.packet_length = mtu
|
||||||
updatepkt(pkt)
|
updatepkt(pkt)
|
||||||
end
|
end
|
||||||
|
|
||||||
local basepkt = function(proto)
|
local basepkt = function(proto)
|
||||||
local ibin = bin.pack("H",
|
local ibin = bin.pack("H",
|
||||||
"4500 0014 0000 4000 8000 0000 0000 0000 0000 0000"
|
"4500 0014 0000 4000 8000 0000 0000 0000 0000 0000"
|
||||||
)
|
)
|
||||||
local tbin = bin.pack("H",
|
local tbin = bin.pack("H",
|
||||||
"0000 0000 0000 0000 0000 0000 6002 0c00 0000 0000 0204 05b4"
|
"0000 0000 0000 0000 0000 0000 6002 0c00 0000 0000 0204 05b4"
|
||||||
)
|
)
|
||||||
local ubin = bin.pack("H",
|
local ubin = bin.pack("H",
|
||||||
"0000 0000 0800 0000"
|
"0000 0000 0800 0000"
|
||||||
)
|
)
|
||||||
|
|
||||||
if proto == IPPROTO_TCP then
|
if proto == IPPROTO_TCP then
|
||||||
return ibin .. tbin
|
return ibin .. tbin
|
||||||
elseif proto == IPPROTO_UDP then
|
elseif proto == IPPROTO_UDP then
|
||||||
return ibin .. ubin
|
return ibin .. ubin
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Creates a Packet object for the given proto and port
|
-- Creates a Packet object for the given proto and port
|
||||||
local genericpkt = function(host, proto, port)
|
local genericpkt = function(host, proto, port)
|
||||||
local pkt = basepkt(proto)
|
local pkt = basepkt(proto)
|
||||||
local ip = packet.Packet:new(pkt, pkt:len())
|
local ip = packet.Packet:new(pkt, pkt:len())
|
||||||
|
|
||||||
ip:ip_set_bin_src(host.bin_ip_src)
|
ip:ip_set_bin_src(host.bin_ip_src)
|
||||||
ip:ip_set_bin_dst(host.bin_ip)
|
ip:ip_set_bin_dst(host.bin_ip)
|
||||||
|
|
||||||
ip:set_u8(ip.ip_offset + 9, proto)
|
ip:set_u8(ip.ip_offset + 9, proto)
|
||||||
ip.ip_p = proto
|
ip.ip_p = proto
|
||||||
|
|
||||||
ip:ip_set_len(pkt:len())
|
ip:ip_set_len(pkt:len())
|
||||||
|
|
||||||
if proto == IPPROTO_TCP then
|
if proto == IPPROTO_TCP then
|
||||||
ip:tcp_parse(false)
|
ip:tcp_parse(false)
|
||||||
ip:tcp_set_dport(port)
|
ip:tcp_set_dport(port)
|
||||||
elseif proto == IPPROTO_UDP then
|
elseif proto == IPPROTO_UDP then
|
||||||
ip:udp_parse(false)
|
ip:udp_parse(false)
|
||||||
ip:udp_set_dport(port)
|
ip:udp_set_dport(port)
|
||||||
end
|
end
|
||||||
|
|
||||||
updatepkt(ip)
|
updatepkt(ip)
|
||||||
|
|
||||||
return ip
|
return ip
|
||||||
end
|
end
|
||||||
|
|
||||||
local ipproto = function(p)
|
local ipproto = function(p)
|
||||||
if p == "tcp" then
|
if p == "tcp" then
|
||||||
return IPPROTO_TCP
|
return IPPROTO_TCP
|
||||||
elseif p == "udp" then
|
elseif p == "udp" then
|
||||||
return IPPROTO_UDP
|
return IPPROTO_UDP
|
||||||
end
|
end
|
||||||
return -1
|
return -1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Determines how to probe
|
-- Determines how to probe
|
||||||
local getprobe = function(host)
|
local getprobe = function(host)
|
||||||
local combos = {
|
local combos = {
|
||||||
{ "tcp", "open" },
|
{ "tcp", "open" },
|
||||||
{ "tcp", "closed" },
|
{ "tcp", "closed" },
|
||||||
-- udp/open probably only happens when Nmap sends proper
|
-- udp/open probably only happens when Nmap sends proper
|
||||||
-- payloads, which doesn't happen in here
|
-- payloads, which doesn't happen in here
|
||||||
{ "udp", "closed" }
|
{ "udp", "closed" }
|
||||||
}
|
}
|
||||||
local proto = nil
|
local proto = nil
|
||||||
local port = nil
|
local port = nil
|
||||||
|
|
||||||
for _, c in ipairs(combos) do
|
for _, c in ipairs(combos) do
|
||||||
port = nmap.get_ports(host, nil, c[1], c[2])
|
port = nmap.get_ports(host, nil, c[1], c[2])
|
||||||
if port then
|
if port then
|
||||||
proto = c[1]
|
proto = c[1]
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return proto, port
|
return proto, port
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sets necessary probe data in registry
|
-- Sets necessary probe data in registry
|
||||||
local setreg = function(host, proto, port)
|
local setreg = function(host, proto, port)
|
||||||
host.registry['pathmtuprobe'] = {
|
host.registry['pathmtuprobe'] = {
|
||||||
['proto'] = proto,
|
['proto'] = proto,
|
||||||
['port'] = port
|
['port'] = port
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
if not nmap.registry[SCRIPT_NAME].rootfail then
|
if not nmap.registry[SCRIPT_NAME].rootfail then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not (host.interface and host.interface_mtu) then
|
if not (host.interface and host.interface_mtu) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local proto, port = getprobe(host)
|
local proto, port = getprobe(host)
|
||||||
if not (proto and port) then
|
if not (proto and port) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
setreg(host, proto, port.number)
|
setreg(host, proto, port.number)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local m, r
|
local m, r
|
||||||
local gotit = false
|
local gotit = false
|
||||||
local mtuset
|
local mtuset
|
||||||
local sock = nmap.new_dnet()
|
local sock = nmap.new_dnet()
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
local proto = host.registry['pathmtuprobe']['proto']
|
local proto = host.registry['pathmtuprobe']['proto']
|
||||||
local port = host.registry['pathmtuprobe']['port']
|
local port = host.registry['pathmtuprobe']['port']
|
||||||
local saddr = packet.toip(host.bin_ip_src)
|
local saddr = packet.toip(host.bin_ip_src)
|
||||||
local daddr = packet.toip(host.bin_ip)
|
local daddr = packet.toip(host.bin_ip)
|
||||||
local try = nmap.new_try()
|
local try = nmap.new_try()
|
||||||
local status, pkt, ip
|
local status, pkt, ip
|
||||||
|
|
||||||
try(sock:ip_open())
|
try(sock:ip_open())
|
||||||
|
|
||||||
try = nmap.new_try(function() sock:ip_close() end)
|
try = nmap.new_try(function() sock:ip_close() end)
|
||||||
|
|
||||||
pcap:pcap_open(host.interface, 104, false, "dst host " .. saddr .. " and (icmp or (" .. proto .. " and src host " .. daddr .. " and src port " .. port .. "))")
|
pcap:pcap_open(host.interface, 104, false, "dst host " .. saddr .. " and (icmp or (" .. proto .. " and src host " .. daddr .. " and src port " .. port .. "))")
|
||||||
|
|
||||||
-- Since we're sending potentially large amounts of data per packet,
|
-- Since we're sending potentially large amounts of data per packet,
|
||||||
-- simply bump up the host's calculated timeout value. Most replies
|
-- simply bump up the host's calculated timeout value. Most replies
|
||||||
-- should come from routers along the path, fragmentation reassembly
|
-- should come from routers along the path, fragmentation reassembly
|
||||||
-- times isn't an issue and the large amount of data is only travelling
|
-- times isn't an issue and the large amount of data is only travelling
|
||||||
-- in one direction; still, we want a response from the target so call
|
-- in one direction; still, we want a response from the target so call
|
||||||
-- it 1.5*timeout to play it safer.
|
-- it 1.5*timeout to play it safer.
|
||||||
pcap:set_timeout(1.5 * host.times.timeout * 1000)
|
pcap:set_timeout(1.5 * host.times.timeout * 1000)
|
||||||
|
|
||||||
m = searchmtu(1, host.interface_mtu)
|
m = searchmtu(1, host.interface_mtu)
|
||||||
|
|
||||||
mtuset = MTUS[m]
|
mtuset = MTUS[m]
|
||||||
|
|
||||||
local pkt = genericpkt(host, ipproto(proto), port)
|
local pkt = genericpkt(host, ipproto(proto), port)
|
||||||
|
|
||||||
while m <= #MTUS do
|
while m <= #MTUS do
|
||||||
setmtu(pkt, MTUS[m])
|
setmtu(pkt, MTUS[m])
|
||||||
|
|
||||||
r = 0
|
r = 0
|
||||||
status = false
|
status = false
|
||||||
while true do
|
while true do
|
||||||
if not status then
|
if not status then
|
||||||
if not sock:ip_send(pkt.buf, host) then
|
if not sock:ip_send(pkt.buf, host) then
|
||||||
-- Got a send error, perhaps EMSGSIZE
|
-- Got a send error, perhaps EMSGSIZE
|
||||||
-- when we don't know our interface's
|
-- when we don't know our interface's
|
||||||
-- MTU. Drop an MTU and keep trying.
|
-- MTU. Drop an MTU and keep trying.
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local test = bin.pack('A', pkt.ip_bin_src)
|
local test = bin.pack('A', pkt.ip_bin_src)
|
||||||
local status, length, _, layer3 = pcap:pcap_receive()
|
local status, length, _, layer3 = pcap:pcap_receive()
|
||||||
while status and test ~= check(layer3) do
|
while status and test ~= check(layer3) do
|
||||||
status, length, _, layer3 = pcap:pcap_receive()
|
status, length, _, layer3 = pcap:pcap_receive()
|
||||||
end
|
end
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
local t, v = checkpkt(layer3, pkt)
|
local t, v = checkpkt(layer3, pkt)
|
||||||
if t == "gotreply" then
|
if t == "gotreply" then
|
||||||
gotit = true
|
gotit = true
|
||||||
break
|
break
|
||||||
elseif t == "recap" then
|
elseif t == "recap" then
|
||||||
elseif t == "nextmtu" then
|
elseif t == "nextmtu" then
|
||||||
if v == 0 then
|
if v == 0 then
|
||||||
-- Router didn't send its
|
-- Router didn't send its
|
||||||
-- next-hop MTU. Just drop
|
-- next-hop MTU. Just drop
|
||||||
-- a level.
|
-- a level.
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
-- Lua's lack of a continue statement
|
-- Lua's lack of a continue statement
|
||||||
-- for loop control sucks, so dec m
|
-- for loop control sucks, so dec m
|
||||||
-- here as it's inc'd below. Ugh.
|
-- here as it's inc'd below. Ugh.
|
||||||
m = searchmtu(m, v) - 1
|
m = searchmtu(m, v) - 1
|
||||||
mtuset = v
|
mtuset = v
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if r >= RETRIES then
|
if r >= RETRIES then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
r = r + 1
|
r = r + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if gotit then
|
if gotit then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
m = m + 1
|
m = m + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
pcap:close()
|
pcap:close()
|
||||||
sock:ip_close()
|
sock:ip_close()
|
||||||
|
|
||||||
if not gotit then
|
if not gotit then
|
||||||
if nmap.debugging() > 0 then
|
if nmap.debugging() > 0 then
|
||||||
return "Error: Unable to determine PMTU (no replies)"
|
return "Error: Unable to determine PMTU (no replies)"
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if MTUS[m] == mtuset then
|
if MTUS[m] == mtuset then
|
||||||
return "PMTU == " .. MTUS[m]
|
return "PMTU == " .. MTUS[m]
|
||||||
elseif m == 1 then
|
elseif m == 1 then
|
||||||
return "PMTU >= " .. MTUS[m]
|
return "PMTU >= " .. MTUS[m]
|
||||||
else
|
else
|
||||||
return "" .. MTUS[m] .. " <= PMTU < " .. MTUS[m - 1]
|
return "" .. MTUS[m] .. " <= PMTU < " .. MTUS[m - 1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,22 +8,22 @@ local tab = require "tab"
|
|||||||
local table = require "table"
|
local table = require "table"
|
||||||
|
|
||||||
description = [[
|
description = [[
|
||||||
Repeatedly probe open and/or closed ports on a host to obtain a series
|
Repeatedly probe open and/or closed ports on a host to obtain a series
|
||||||
of round-trip time values for each port. These values are used to
|
of round-trip time values for each port. These values are used to
|
||||||
group collections of ports which are statistically different from other
|
group collections of ports which are statistically different from other
|
||||||
groups. Ports being in different groups (or "families") may be due to
|
groups. Ports being in different groups (or "families") may be due to
|
||||||
network mechanisms such as port forwarding to machines behind a NAT.
|
network mechanisms such as port forwarding to machines behind a NAT.
|
||||||
|
|
||||||
In order to group these ports into different families, some statistical
|
In order to group these ports into different families, some statistical
|
||||||
values must be computed. Among these values are the mean and standard
|
values must be computed. Among these values are the mean and standard
|
||||||
deviation of the round-trip times for each port. Once all of the times
|
deviation of the round-trip times for each port. Once all of the times
|
||||||
have been recorded and these values have been computed, the Student's
|
have been recorded and these values have been computed, the Student's
|
||||||
t-test is used to test the statistical significance of the differences
|
t-test is used to test the statistical significance of the differences
|
||||||
between each port's data. Ports which have round-trip times that are
|
between each port's data. Ports which have round-trip times that are
|
||||||
statistically the same are grouped together in the same family.
|
statistically the same are grouped together in the same family.
|
||||||
|
|
||||||
This script is based on Doug Hoyte's Qscan documentation and patches
|
This script is based on Doug Hoyte's Qscan documentation and patches
|
||||||
for Nmap.
|
for Nmap.
|
||||||
]]
|
]]
|
||||||
|
|
||||||
-- See http://hcsw.org/nmap/QSCAN for more on Doug's research
|
-- See http://hcsw.org/nmap/QSCAN for more on Doug's research
|
||||||
@@ -69,38 +69,38 @@ local NUMCLOSED = 1
|
|||||||
-- The following tdist{} and tinv() are based off of
|
-- The following tdist{} and tinv() are based off of
|
||||||
-- http://www.owlnet.rice.edu/~elec428/projects/tinv.c
|
-- http://www.owlnet.rice.edu/~elec428/projects/tinv.c
|
||||||
local tdist = {
|
local tdist = {
|
||||||
-- 75% 90% 95% 97.5% 99% 99.5% 99.95%
|
-- 75% 90% 95% 97.5% 99% 99.5% 99.95%
|
||||||
{ 1.0000, 3.0777, 6.3138, 12.7062, 31.8207, 63.6574, 636.6192 }, -- 1
|
{ 1.0000, 3.0777, 6.3138, 12.7062, 31.8207, 63.6574, 636.6192 }, -- 1
|
||||||
{ 0.8165, 1.8856, 2.9200, 4.3027, 6.9646, 9.9248, 31.5991 }, -- 2
|
{ 0.8165, 1.8856, 2.9200, 4.3027, 6.9646, 9.9248, 31.5991 }, -- 2
|
||||||
{ 0.7649, 1.6377, 2.3534, 3.1824, 4.5407, 5.8409, 12.9240 }, -- 3
|
{ 0.7649, 1.6377, 2.3534, 3.1824, 4.5407, 5.8409, 12.9240 }, -- 3
|
||||||
{ 0.7407, 1.5332, 2.1318, 2.7764, 3.7649, 4.6041, 8.6103 }, -- 4
|
{ 0.7407, 1.5332, 2.1318, 2.7764, 3.7649, 4.6041, 8.6103 }, -- 4
|
||||||
{ 0.7267, 1.4759, 2.0150, 2.5706, 3.3649, 4.0322, 6.8688 }, -- 5
|
{ 0.7267, 1.4759, 2.0150, 2.5706, 3.3649, 4.0322, 6.8688 }, -- 5
|
||||||
{ 0.7176, 1.4398, 1.9432, 2.4469, 3.1427, 3.7074, 5.9588 }, -- 6
|
{ 0.7176, 1.4398, 1.9432, 2.4469, 3.1427, 3.7074, 5.9588 }, -- 6
|
||||||
{ 0.7111, 1.4149, 1.8946, 2.3646, 2.9980, 3.4995, 5.4079 }, -- 7
|
{ 0.7111, 1.4149, 1.8946, 2.3646, 2.9980, 3.4995, 5.4079 }, -- 7
|
||||||
{ 0.7064, 1.3968, 1.8595, 3.3060, 2.8965, 3.3554, 5.0413 }, -- 8
|
{ 0.7064, 1.3968, 1.8595, 3.3060, 2.8965, 3.3554, 5.0413 }, -- 8
|
||||||
{ 0.7027, 1.3830, 1.8331, 2.2622, 2.8214, 3.2498, 4.7809 }, -- 9
|
{ 0.7027, 1.3830, 1.8331, 2.2622, 2.8214, 3.2498, 4.7809 }, -- 9
|
||||||
{ 0.6998, 1.3722, 1.8125, 2.2281, 2.7638, 1.1693, 4.5869 }, -- 10
|
{ 0.6998, 1.3722, 1.8125, 2.2281, 2.7638, 1.1693, 4.5869 }, -- 10
|
||||||
{ 0.6974, 1.3634, 1.7959, 2.2010, 2.7181, 3.1058, 4.4370 }, -- 11
|
{ 0.6974, 1.3634, 1.7959, 2.2010, 2.7181, 3.1058, 4.4370 }, -- 11
|
||||||
{ 0.6955, 1.3562, 1.7823, 2.1788, 2.6810, 3.0545, 4.3178 }, -- 12
|
{ 0.6955, 1.3562, 1.7823, 2.1788, 2.6810, 3.0545, 4.3178 }, -- 12
|
||||||
{ 0.6938, 1.3502, 1.7709, 2.1604, 2.6403, 3.0123, 4.2208 }, -- 13
|
{ 0.6938, 1.3502, 1.7709, 2.1604, 2.6403, 3.0123, 4.2208 }, -- 13
|
||||||
{ 0.6924, 1.3450, 1.7613, 2.1448, 2.6245, 2.9768, 4.1405 }, -- 14
|
{ 0.6924, 1.3450, 1.7613, 2.1448, 2.6245, 2.9768, 4.1405 }, -- 14
|
||||||
{ 0.6912, 1.3406, 1.7531, 2.1315, 2.6025, 2.9467, 4.0728 }, -- 15
|
{ 0.6912, 1.3406, 1.7531, 2.1315, 2.6025, 2.9467, 4.0728 }, -- 15
|
||||||
{ 0.6901, 1.3368, 1.7459, 2.1199, 2.5835, 2.9208, 4.0150 }, -- 16
|
{ 0.6901, 1.3368, 1.7459, 2.1199, 2.5835, 2.9208, 4.0150 }, -- 16
|
||||||
{ 0.6892, 1.3334, 1.7396, 2.1098, 2.5669, 2.8982, 3.9651 }, -- 17
|
{ 0.6892, 1.3334, 1.7396, 2.1098, 2.5669, 2.8982, 3.9651 }, -- 17
|
||||||
{ 0.6884, 1.3304, 1.7341, 2.1009, 2.5524, 2.8784, 3.9216 }, -- 18
|
{ 0.6884, 1.3304, 1.7341, 2.1009, 2.5524, 2.8784, 3.9216 }, -- 18
|
||||||
{ 0.6876, 1.3277, 1.7291, 2.0930, 2.5395, 2.8609, 3.8834 }, -- 19
|
{ 0.6876, 1.3277, 1.7291, 2.0930, 2.5395, 2.8609, 3.8834 }, -- 19
|
||||||
{ 0.6870, 1.3253, 1.7247, 2.0860, 2.5280, 2.8453, 3.8495 }, -- 20
|
{ 0.6870, 1.3253, 1.7247, 2.0860, 2.5280, 2.8453, 3.8495 }, -- 20
|
||||||
{ 0.6844, 1.3163, 1.7081, 2.0595, 2.4851, 2.7874, 3.7251 }, -- 25
|
{ 0.6844, 1.3163, 1.7081, 2.0595, 2.4851, 2.7874, 3.7251 }, -- 25
|
||||||
{ 0.6828, 1.3104, 1.6973, 2.0423, 2.4573, 2.7500, 3.6460 }, -- 30
|
{ 0.6828, 1.3104, 1.6973, 2.0423, 2.4573, 2.7500, 3.6460 }, -- 30
|
||||||
{ 0.6816, 1.3062, 1.6896, 2.0301, 2.4377, 2.7238, 3.5911 }, -- 35
|
{ 0.6816, 1.3062, 1.6896, 2.0301, 2.4377, 2.7238, 3.5911 }, -- 35
|
||||||
{ 0.6807, 1.3031, 1.6839, 2.0211, 2.4233, 2.7045, 3.5510 }, -- 40
|
{ 0.6807, 1.3031, 1.6839, 2.0211, 2.4233, 2.7045, 3.5510 }, -- 40
|
||||||
{ 0.6800, 1.3006, 1.6794, 2.0141, 2.4121, 2.6896, 3.5203 }, -- 45
|
{ 0.6800, 1.3006, 1.6794, 2.0141, 2.4121, 2.6896, 3.5203 }, -- 45
|
||||||
{ 0.6794, 1.2987, 1.6759, 2.0086, 2.4033, 2.6778, 3.4960 }, -- 50
|
{ 0.6794, 1.2987, 1.6759, 2.0086, 2.4033, 2.6778, 3.4960 }, -- 50
|
||||||
{ 0.6786, 1.2958, 1.6706, 2.0003, 2.3901, 2.6603, 3.4602 }, -- 60
|
{ 0.6786, 1.2958, 1.6706, 2.0003, 2.3901, 2.6603, 3.4602 }, -- 60
|
||||||
{ 0.6780, 1.2938, 1.6669, 1.9944, 2.3808, 2.6479, 3.4350 }, -- 70
|
{ 0.6780, 1.2938, 1.6669, 1.9944, 2.3808, 2.6479, 3.4350 }, -- 70
|
||||||
{ 0.6776, 1.2922, 1.6641, 1.9901, 2.3739, 2.6387, 3.4163 }, -- 80
|
{ 0.6776, 1.2922, 1.6641, 1.9901, 2.3739, 2.6387, 3.4163 }, -- 80
|
||||||
{ 0.6772, 1.2910, 1.6620, 1.9867, 2.3685, 2.6316, 3.4019 }, -- 90
|
{ 0.6772, 1.2910, 1.6620, 1.9867, 2.3685, 2.6316, 3.4019 }, -- 90
|
||||||
{ 0.6770, 1.2901, 1.6602, 1.9840, 2.3642, 2.6259, 3.3905 } -- 100
|
{ 0.6770, 1.2901, 1.6602, 1.9840, 2.3642, 2.6259, 3.3905 } -- 100
|
||||||
}
|
}
|
||||||
|
|
||||||
-- cache ports to probe between the hostrule and the action function
|
-- cache ports to probe between the hostrule and the action function
|
||||||
@@ -108,391 +108,391 @@ local qscanports
|
|||||||
|
|
||||||
|
|
||||||
local tinv = function(p, dof)
|
local tinv = function(p, dof)
|
||||||
local din, pin
|
local din, pin
|
||||||
|
|
||||||
if dof >= 1 and dof <= 20 then
|
if dof >= 1 and dof <= 20 then
|
||||||
din = dof
|
din = dof
|
||||||
elseif dof < 25 then
|
elseif dof < 25 then
|
||||||
din = 20
|
din = 20
|
||||||
elseif dof < 30 then
|
elseif dof < 30 then
|
||||||
din = 21
|
din = 21
|
||||||
elseif dof < 35 then
|
elseif dof < 35 then
|
||||||
din = 22
|
din = 22
|
||||||
elseif dof < 40 then
|
elseif dof < 40 then
|
||||||
din = 23
|
din = 23
|
||||||
elseif dof < 45 then
|
elseif dof < 45 then
|
||||||
din = 24
|
din = 24
|
||||||
elseif dof < 50 then
|
elseif dof < 50 then
|
||||||
din = 25
|
din = 25
|
||||||
elseif dof < 60 then
|
elseif dof < 60 then
|
||||||
din = 26
|
din = 26
|
||||||
elseif dof < 70 then
|
elseif dof < 70 then
|
||||||
din = 27
|
din = 27
|
||||||
elseif dof < 80 then
|
elseif dof < 80 then
|
||||||
din = 28
|
din = 28
|
||||||
elseif dof < 90 then
|
elseif dof < 90 then
|
||||||
din = 29
|
din = 29
|
||||||
elseif dof < 100 then
|
elseif dof < 100 then
|
||||||
din = 30
|
din = 30
|
||||||
elseif dof >= 100 then
|
elseif dof >= 100 then
|
||||||
din = 31
|
din = 31
|
||||||
end
|
end
|
||||||
|
|
||||||
if p == 0.75 then
|
if p == 0.75 then
|
||||||
pin = 1
|
pin = 1
|
||||||
elseif p == 0.9 then
|
elseif p == 0.9 then
|
||||||
pin = 2
|
pin = 2
|
||||||
elseif p == 0.95 then
|
elseif p == 0.95 then
|
||||||
pin = 3
|
pin = 3
|
||||||
elseif p == 0.975 then
|
elseif p == 0.975 then
|
||||||
pin = 4
|
pin = 4
|
||||||
elseif p == 0.99 then
|
elseif p == 0.99 then
|
||||||
pin = 5
|
pin = 5
|
||||||
elseif p == 0.995 then
|
elseif p == 0.995 then
|
||||||
pin = 6
|
pin = 6
|
||||||
elseif p == 0.9995 then
|
elseif p == 0.9995 then
|
||||||
pin = 7
|
pin = 7
|
||||||
end
|
end
|
||||||
|
|
||||||
return tdist[din][pin]
|
return tdist[din][pin]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculates intermediate t statistic
|
--- Calculates intermediate t statistic
|
||||||
local tstat = function(n1, n2, u1, u2, v1, v2)
|
local tstat = function(n1, n2, u1, u2, v1, v2)
|
||||||
local dof = n1 + n2 - 2
|
local dof = n1 + n2 - 2
|
||||||
local a = (n1 + n2) / (n1 * n2)
|
local a = (n1 + n2) / (n1 * n2)
|
||||||
--local b = ((n1 - 1) * (s1 * s1) + (n2 - 1) * (s2 * s2))
|
--local b = ((n1 - 1) * (s1 * s1) + (n2 - 1) * (s2 * s2))
|
||||||
local b = ((n1 - 1) * v1) + ((n2 - 1) * v2)
|
local b = ((n1 - 1) * v1) + ((n2 - 1) * v2)
|
||||||
return math.abs(u1 - u2) / math.sqrt(a * (b / dof))
|
return math.abs(u1 - u2) / math.sqrt(a * (b / dof))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Pcap check
|
--- Pcap check
|
||||||
-- @return Destination and source IP addresses and TCP ports
|
-- @return Destination and source IP addresses and TCP ports
|
||||||
local check = function(layer3)
|
local check = function(layer3)
|
||||||
local ip = packet.Packet:new(layer3, layer3:len())
|
local ip = packet.Packet:new(layer3, layer3:len())
|
||||||
return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport)
|
return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Updates a TCP Packet object
|
--- Updates a TCP Packet object
|
||||||
-- @param tcp The TCP object
|
-- @param tcp The TCP object
|
||||||
local updatepkt = function(tcp, dport)
|
local updatepkt = function(tcp, dport)
|
||||||
tcp:tcp_set_sport(math.random(0x401, 0xffff))
|
tcp:tcp_set_sport(math.random(0x401, 0xffff))
|
||||||
tcp:tcp_set_dport(dport)
|
tcp:tcp_set_dport(dport)
|
||||||
tcp:tcp_set_seq(math.random(1, 0x7fffffff))
|
tcp:tcp_set_seq(math.random(1, 0x7fffffff))
|
||||||
tcp:tcp_count_checksum(tcp.ip_len)
|
tcp:tcp_count_checksum(tcp.ip_len)
|
||||||
tcp:ip_count_checksum()
|
tcp:ip_count_checksum()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a TCP Packet object
|
--- Create a TCP Packet object
|
||||||
-- @param host Host object
|
-- @param host Host object
|
||||||
-- @return TCP Packet object
|
-- @return TCP Packet object
|
||||||
local genericpkt = function(host)
|
local genericpkt = function(host)
|
||||||
local pkt = bin.pack("H",
|
local pkt = bin.pack("H",
|
||||||
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
|
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
|
||||||
"0000 0000 0000 0000 0000 0000 0000 0000" ..
|
"0000 0000 0000 0000 0000 0000 0000 0000" ..
|
||||||
"6002 0c00 0000 0000 0204 05b4"
|
"6002 0c00 0000 0000 0204 05b4"
|
||||||
)
|
)
|
||||||
|
|
||||||
local tcp = packet.Packet:new(pkt, pkt:len())
|
local tcp = packet.Packet:new(pkt, pkt:len())
|
||||||
|
|
||||||
tcp:ip_set_bin_src(host.bin_ip_src)
|
tcp:ip_set_bin_src(host.bin_ip_src)
|
||||||
tcp:ip_set_bin_dst(host.bin_ip)
|
tcp:ip_set_bin_dst(host.bin_ip)
|
||||||
|
|
||||||
updatepkt(tcp, 0)
|
updatepkt(tcp, 0)
|
||||||
|
|
||||||
return tcp
|
return tcp
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculates "family" values for grouping
|
--- Calculates "family" values for grouping
|
||||||
-- @param stats Statistics table
|
-- @param stats Statistics table
|
||||||
-- @param conf Confidence level
|
-- @param conf Confidence level
|
||||||
local calcfamilies = function(stats, conf)
|
local calcfamilies = function(stats, conf)
|
||||||
local i, j
|
local i, j
|
||||||
local famidx = 0
|
local famidx = 0
|
||||||
local stat
|
local stat
|
||||||
local crit
|
local crit
|
||||||
|
|
||||||
for _, i in pairs(stats) do repeat
|
for _, i in pairs(stats) do repeat
|
||||||
if i.fam ~= -1 then
|
if i.fam ~= -1 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
i.fam = famidx
|
i.fam = famidx
|
||||||
famidx = famidx + 1
|
famidx = famidx + 1
|
||||||
|
|
||||||
for _, j in pairs(stats) do repeat
|
for _, j in pairs(stats) do repeat
|
||||||
if j.port == i.port or j.fam ~= -1 then
|
if j.port == i.port or j.fam ~= -1 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
stat = tstat(i.num, j.num, i.mean, j.mean, i.K / (i.num - 1), j.K / (j.num - 1))
|
stat = tstat(i.num, j.num, i.mean, j.mean, i.K / (i.num - 1), j.K / (j.num - 1))
|
||||||
crit = tinv(conf, i.num + j.num - 2)
|
crit = tinv(conf, i.num + j.num - 2)
|
||||||
|
|
||||||
if stat < crit then
|
if stat < crit then
|
||||||
j.fam = i.fam
|
j.fam = i.fam
|
||||||
end
|
end
|
||||||
until true end
|
until true end
|
||||||
until true end
|
until true end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Builds report for output
|
--- Builds report for output
|
||||||
-- @param stats Array of port statistics
|
-- @param stats Array of port statistics
|
||||||
-- @return Output report
|
-- @return Output report
|
||||||
local report = function(stats)
|
local report = function(stats)
|
||||||
local j
|
local j
|
||||||
local outtab = tab.new()
|
local outtab = tab.new()
|
||||||
|
|
||||||
tab.add(outtab, 1, "PORT")
|
tab.add(outtab, 1, "PORT")
|
||||||
tab.add(outtab, 2, "FAMILY")
|
tab.add(outtab, 2, "FAMILY")
|
||||||
tab.add(outtab, 3, "MEAN (us)")
|
tab.add(outtab, 3, "MEAN (us)")
|
||||||
tab.add(outtab, 4, "STDDEV")
|
tab.add(outtab, 4, "STDDEV")
|
||||||
tab.add(outtab, 5, "LOSS (%)")
|
tab.add(outtab, 5, "LOSS (%)")
|
||||||
tab.nextrow(outtab)
|
tab.nextrow(outtab)
|
||||||
local port, fam, mean, stddev, loss
|
local port, fam, mean, stddev, loss
|
||||||
for _, j in pairs(stats) do
|
for _, j in pairs(stats) do
|
||||||
port = tostring(j.port)
|
port = tostring(j.port)
|
||||||
fam = tostring(j.fam)
|
fam = tostring(j.fam)
|
||||||
mean = string.format("%.2f", j.mean)
|
mean = string.format("%.2f", j.mean)
|
||||||
stddev = string.format("%.2f", math.sqrt(j.K / (j.num - 1)))
|
stddev = string.format("%.2f", math.sqrt(j.K / (j.num - 1)))
|
||||||
loss = string.format("%.1f%%", 100 * (1 - j.num / j.sent))
|
loss = string.format("%.1f%%", 100 * (1 - j.num / j.sent))
|
||||||
|
|
||||||
tab.add(outtab, 1, port)
|
tab.add(outtab, 1, port)
|
||||||
tab.add(outtab, 2, fam)
|
tab.add(outtab, 2, fam)
|
||||||
tab.add(outtab, 3, mean)
|
tab.add(outtab, 3, mean)
|
||||||
tab.add(outtab, 4, stddev)
|
tab.add(outtab, 4, stddev)
|
||||||
tab.add(outtab, 5, loss)
|
tab.add(outtab, 5, loss)
|
||||||
tab.nextrow(outtab)
|
tab.nextrow(outtab)
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab.dump(outtab)
|
return tab.dump(outtab)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns option values based on script arguments and defaults
|
--- Returns option values based on script arguments and defaults
|
||||||
-- @return Confidence level, delay and number of trips
|
-- @return Confidence level, delay and number of trips
|
||||||
local getopts = function()
|
local getopts = function()
|
||||||
local conf, delay, numtrips = CONF, DELAY, NUMTRIPS
|
local conf, delay, numtrips = CONF, DELAY, NUMTRIPS
|
||||||
local bool, err
|
local bool, err
|
||||||
local k
|
local k
|
||||||
|
|
||||||
for _, k in ipairs({"qscan.confidence", "confidence"}) do
|
for _, k in ipairs({"qscan.confidence", "confidence"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
conf = tonumber(nmap.registry.args[k])
|
conf = tonumber(nmap.registry.args[k])
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, k in ipairs({"qscan.delay", "delay"}) do
|
for _, k in ipairs({"qscan.delay", "delay"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
delay = stdnse.parse_timespec(nmap.registry.args[k])
|
delay = stdnse.parse_timespec(nmap.registry.args[k])
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, k in ipairs({"qscan.numtrips", "numtrips"}) do
|
for _, k in ipairs({"qscan.numtrips", "numtrips"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
numtrips = tonumber(nmap.registry.args[k])
|
numtrips = tonumber(nmap.registry.args[k])
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
bool = true
|
bool = true
|
||||||
|
|
||||||
if conf ~= 0.75 and conf ~= 0.9 and
|
if conf ~= 0.75 and conf ~= 0.9 and
|
||||||
conf ~= 0.95 and conf ~= 0.975 and
|
conf ~= 0.95 and conf ~= 0.975 and
|
||||||
conf ~= 0.99 and conf ~= 0.995 and conf ~= 0.9995 then
|
conf ~= 0.99 and conf ~= 0.995 and conf ~= 0.9995 then
|
||||||
bool = false
|
bool = false
|
||||||
err = "Invalid confidence level"
|
err = "Invalid confidence level"
|
||||||
end
|
end
|
||||||
|
|
||||||
if not delay then
|
if not delay then
|
||||||
bool = false
|
bool = false
|
||||||
err = "Invalid delay"
|
err = "Invalid delay"
|
||||||
end
|
end
|
||||||
|
|
||||||
if numtrips < 3 then
|
if numtrips < 3 then
|
||||||
bool = false
|
bool = false
|
||||||
err = "Invalid number of trips (should be >= 3)"
|
err = "Invalid number of trips (should be >= 3)"
|
||||||
end
|
end
|
||||||
|
|
||||||
if bool then
|
if bool then
|
||||||
return bool, conf, delay, numtrips
|
return bool, conf, delay, numtrips
|
||||||
else
|
else
|
||||||
return bool, err
|
return bool, err
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local table_extend = function(a, b)
|
local table_extend = function(a, b)
|
||||||
local t = {}
|
local t = {}
|
||||||
|
|
||||||
for _, v in ipairs(a) do
|
for _, v in ipairs(a) do
|
||||||
t[#t + 1] = v
|
t[#t + 1] = v
|
||||||
end
|
end
|
||||||
for _, v in ipairs(b) do
|
for _, v in ipairs(b) do
|
||||||
t[#t + 1] = v
|
t[#t + 1] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
return t
|
return t
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get ports to probe
|
--- Get ports to probe
|
||||||
-- @param host Host object
|
-- @param host Host object
|
||||||
local getports = function(host, numopen, numclosed)
|
local getports = function(host, numopen, numclosed)
|
||||||
local open = {}
|
local open = {}
|
||||||
local closed = {}
|
local closed = {}
|
||||||
local port
|
local port
|
||||||
|
|
||||||
port = nil
|
port = nil
|
||||||
while numopen < 0 or #open < numopen do
|
while numopen < 0 or #open < numopen do
|
||||||
port = nmap.get_ports(host, port, "tcp", "open")
|
port = nmap.get_ports(host, port, "tcp", "open")
|
||||||
if not port then
|
if not port then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
open[#open + 1] = port.number
|
open[#open + 1] = port.number
|
||||||
end
|
end
|
||||||
port = nil
|
port = nil
|
||||||
while numclosed < 0 or #closed < numclosed do
|
while numclosed < 0 or #closed < numclosed do
|
||||||
port = nmap.get_ports(host, port, "tcp", "closed")
|
port = nmap.get_ports(host, port, "tcp", "closed")
|
||||||
if not port then
|
if not port then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
closed[#closed + 1] = port.number
|
closed[#closed + 1] = port.number
|
||||||
end
|
end
|
||||||
|
|
||||||
return table_extend(open, closed)
|
return table_extend(open, closed)
|
||||||
end
|
end
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
if not nmap.registry[SCRIPT_NAME].rootfail then
|
if not nmap.registry[SCRIPT_NAME].rootfail then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local numopen, numclosed = NUMOPEN, NUMCLOSED
|
local numopen, numclosed = NUMOPEN, NUMCLOSED
|
||||||
|
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not host.interface then
|
if not host.interface then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, k in ipairs({"qscan.numopen", "numopen"}) do
|
for _, k in ipairs({"qscan.numopen", "numopen"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
numopen = tonumber(nmap.registry.args[k])
|
numopen = tonumber(nmap.registry.args[k])
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, k in ipairs({"qscan.numclosed", "numclosed"}) do
|
for _, k in ipairs({"qscan.numclosed", "numclosed"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
numclosed = tonumber(nmap.registry.args[k])
|
numclosed = tonumber(nmap.registry.args[k])
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
qscanports = getports(host, numopen, numclosed)
|
qscanports = getports(host, numopen, numclosed)
|
||||||
return (#qscanports > 1)
|
return (#qscanports > 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local sock = nmap.new_dnet()
|
local sock = nmap.new_dnet()
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
local saddr = packet.toip(host.bin_ip_src)
|
local saddr = packet.toip(host.bin_ip_src)
|
||||||
local daddr = packet.toip(host.bin_ip)
|
local daddr = packet.toip(host.bin_ip)
|
||||||
local start
|
local start
|
||||||
local rtt
|
local rtt
|
||||||
local stats = {}
|
local stats = {}
|
||||||
local try = nmap.new_try()
|
local try = nmap.new_try()
|
||||||
|
|
||||||
local conf, delay, numtrips = try(getopts())
|
local conf, delay, numtrips = try(getopts())
|
||||||
|
|
||||||
pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr)
|
pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr)
|
||||||
|
|
||||||
try(sock:ip_open())
|
try(sock:ip_open())
|
||||||
|
|
||||||
try = nmap.new_try(function() sock:ip_close() end)
|
try = nmap.new_try(function() sock:ip_close() end)
|
||||||
|
|
||||||
-- Simply double the calculated host timeout to account for possible
|
-- Simply double the calculated host timeout to account for possible
|
||||||
-- extra time due to port forwarding or whathaveyou. Nmap has all
|
-- extra time due to port forwarding or whathaveyou. Nmap has all
|
||||||
-- ready scanned this host, so the timing should have taken into
|
-- ready scanned this host, so the timing should have taken into
|
||||||
-- account some of the RTT differences, but I think it really depends
|
-- account some of the RTT differences, but I think it really depends
|
||||||
-- on how many ports were scanned and how many were forwarded where.
|
-- on how many ports were scanned and how many were forwarded where.
|
||||||
-- Play it safer here.
|
-- Play it safer here.
|
||||||
pcap:set_timeout(2 * host.times.timeout * 1000)
|
pcap:set_timeout(2 * host.times.timeout * 1000)
|
||||||
|
|
||||||
local tcp = genericpkt(host)
|
local tcp = genericpkt(host)
|
||||||
|
|
||||||
for i = 1, numtrips do
|
for i = 1, numtrips do
|
||||||
for j, port in ipairs(qscanports) do
|
for j, port in ipairs(qscanports) do
|
||||||
|
|
||||||
updatepkt(tcp, port)
|
updatepkt(tcp, port)
|
||||||
|
|
||||||
if not stats[j] then
|
if not stats[j] then
|
||||||
stats[j] = {}
|
stats[j] = {}
|
||||||
stats[j].port = port
|
stats[j].port = port
|
||||||
stats[j].num = 0
|
stats[j].num = 0
|
||||||
stats[j].sent = 0
|
stats[j].sent = 0
|
||||||
stats[j].mean = 0
|
stats[j].mean = 0
|
||||||
stats[j].K = 0
|
stats[j].K = 0
|
||||||
stats[j].fam = -1
|
stats[j].fam = -1
|
||||||
end
|
end
|
||||||
|
|
||||||
start = stdnse.clock_us()
|
start = stdnse.clock_us()
|
||||||
|
|
||||||
try(sock:ip_send(tcp.buf, host))
|
try(sock:ip_send(tcp.buf, host))
|
||||||
|
|
||||||
stats[j].sent = stats[j].sent + 1
|
stats[j].sent = stats[j].sent + 1
|
||||||
|
|
||||||
local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)
|
local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)
|
||||||
local status, length, _, layer3, stop = pcap:pcap_receive()
|
local status, length, _, layer3, stop = pcap:pcap_receive()
|
||||||
while status and test ~= check(layer3) do
|
while status and test ~= check(layer3) do
|
||||||
status, length, _, layer3, stop = pcap:pcap_receive()
|
status, length, _, layer3, stop = pcap:pcap_receive()
|
||||||
end
|
end
|
||||||
|
|
||||||
if not stop then
|
if not stop then
|
||||||
-- probably a timeout, just grab current time
|
-- probably a timeout, just grab current time
|
||||||
stop = stdnse.clock_us()
|
stop = stdnse.clock_us()
|
||||||
else
|
else
|
||||||
-- we use usecs
|
-- we use usecs
|
||||||
stop = stop * 1000000
|
stop = stop * 1000000
|
||||||
end
|
end
|
||||||
|
|
||||||
rtt = stop - start
|
rtt = stop - start
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
-- update more stats on the port, Knuth-style
|
-- update more stats on the port, Knuth-style
|
||||||
local delta
|
local delta
|
||||||
stats[j].num = stats[j].num + 1
|
stats[j].num = stats[j].num + 1
|
||||||
delta = rtt - stats[j].mean
|
delta = rtt - stats[j].mean
|
||||||
stats[j].mean = stats[j].mean + delta / stats[j].num
|
stats[j].mean = stats[j].mean + delta / stats[j].num
|
||||||
stats[j].K = stats[j].K + delta * (rtt - stats[j].mean)
|
stats[j].K = stats[j].K + delta * (rtt - stats[j].mean)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Unlike qscan.cc which loops around while waiting for
|
-- Unlike qscan.cc which loops around while waiting for
|
||||||
-- the delay, I just sleep here (depending on rtt)
|
-- the delay, I just sleep here (depending on rtt)
|
||||||
if rtt < (3 * delay) / 2 then
|
if rtt < (3 * delay) / 2 then
|
||||||
if rtt < (delay / 2) then
|
if rtt < (delay / 2) then
|
||||||
stdnse.sleep(((delay / 2) + math.random(0, delay) - rtt))
|
stdnse.sleep(((delay / 2) + math.random(0, delay) - rtt))
|
||||||
else
|
else
|
||||||
stdnse.sleep(math.random((3 * delay) / 2 - rtt))
|
stdnse.sleep(math.random((3 * delay) / 2 - rtt))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sock:ip_close()
|
sock:ip_close()
|
||||||
pcap:pcap_close()
|
pcap:pcap_close()
|
||||||
|
|
||||||
-- sort by port number
|
-- sort by port number
|
||||||
table.sort(stats, function(t1, t2) return t1.port < t2.port end)
|
table.sort(stats, function(t1, t2) return t1.port < t2.port end)
|
||||||
|
|
||||||
calcfamilies(stats, conf)
|
calcfamilies(stats, conf)
|
||||||
|
|
||||||
return "\n" .. report(stats)
|
return "\n" .. report(stats)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -119,7 +119,7 @@ dependencies = {
|
|||||||
|
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local VULNERABLE = 1
|
local VULNERABLE = 1
|
||||||
@@ -149,88 +149,88 @@ local NOTUP = 8
|
|||||||
-- <code>UNKNOWN</code> if there was an error (likely vulnerable), <code>NOTRUN</code>
|
-- <code>UNKNOWN</code> if there was an error (likely vulnerable), <code>NOTRUN</code>
|
||||||
-- if this check was disabled, and <code>INFECTED</code> if it was patched by Conficker.
|
-- if this check was disabled, and <code>INFECTED</code> if it was patched by Conficker.
|
||||||
function check_ms08_067(host)
|
function check_ms08_067(host)
|
||||||
if(nmap.registry.args.safe ~= nil) then
|
if(nmap.registry.args.safe ~= nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
if(nmap.registry.args.unsafe == nil) then
|
if(nmap.registry.args.unsafe == nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
local status, smbstate
|
local status, smbstate
|
||||||
local bind_result, netpathcompare_result
|
local bind_result, netpathcompare_result
|
||||||
|
|
||||||
-- Create the SMB session
|
-- Create the SMB session
|
||||||
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER")
|
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER")
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to SRVSVC service
|
-- Bind to SRVSVC service
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Call netpathcanonicalize
|
-- Call netpathcanonicalize
|
||||||
-- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\")
|
-- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\")
|
||||||
|
|
||||||
local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n"
|
local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n"
|
||||||
local path2 = "\\n"
|
local path2 = "\\n"
|
||||||
status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0)
|
status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0)
|
||||||
|
|
||||||
-- Stop the SMB session
|
-- Stop the SMB session
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
if(string.find(netpathcompare_result, "WERR_INVALID_PARAMETER") ~= nil) then
|
if(string.find(netpathcompare_result, "WERR_INVALID_PARAMETER") ~= nil) then
|
||||||
return true, INFECTED
|
return true, INFECTED
|
||||||
elseif(string.find(netpathcompare_result, "INVALID_NAME") ~= nil) then
|
elseif(string.find(netpathcompare_result, "INVALID_NAME") ~= nil) then
|
||||||
return true, PATCHED
|
return true, PATCHED
|
||||||
else
|
else
|
||||||
return true, UNKNOWN, netpathcompare_result
|
return true, UNKNOWN, netpathcompare_result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return true, VULNERABLE
|
return true, VULNERABLE
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Help messages for the more common errors seen by the Conficker check.
|
-- Help messages for the more common errors seen by the Conficker check.
|
||||||
CONFICKER_ERROR_HELP = {
|
CONFICKER_ERROR_HELP = {
|
||||||
["NT_STATUS_BAD_NETWORK_NAME"] =
|
["NT_STATUS_BAD_NETWORK_NAME"] =
|
||||||
[[UNKNOWN; Network name not found (required service has crashed). (Error NT_STATUS_BAD_NETWORK_NAME)]],
|
[[UNKNOWN; Network name not found (required service has crashed). (Error NT_STATUS_BAD_NETWORK_NAME)]],
|
||||||
-- http://seclists.org/nmap-dev/2009/q1/0918.html "non-Windows boxes (Samba on Linux/OS X, or a printer)"
|
-- http://seclists.org/nmap-dev/2009/q1/0918.html "non-Windows boxes (Samba on Linux/OS X, or a printer)"
|
||||||
-- http://www.skullsecurity.org/blog/?p=209#comment-156
|
-- http://www.skullsecurity.org/blog/?p=209#comment-156
|
||||||
-- "That means either it isn’t a Windows machine, or the service is
|
-- "That means either it isn’t a Windows machine, or the service is
|
||||||
-- either crashed or not running. That may indicate a failed (or
|
-- either crashed or not running. That may indicate a failed (or
|
||||||
-- successful) exploit attempt, or just a locked down system.
|
-- successful) exploit attempt, or just a locked down system.
|
||||||
-- NT_STATUS_OBJECT_NAME_NOT_FOUND can be returned if the browser
|
-- NT_STATUS_OBJECT_NAME_NOT_FOUND can be returned if the browser
|
||||||
-- service is disabled. There are at least two ways that can happen:
|
-- service is disabled. There are at least two ways that can happen:
|
||||||
-- 1) The service itself is disabled in the services list.
|
-- 1) The service itself is disabled in the services list.
|
||||||
-- 2) The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters\MaintainServerList
|
-- 2) The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters\MaintainServerList
|
||||||
-- is set to Off/False/No rather than Auto or yes.
|
-- is set to Off/False/No rather than Auto or yes.
|
||||||
-- On these systems, if you reenable the browser service, then the
|
-- On these systems, if you reenable the browser service, then the
|
||||||
-- test will complete."
|
-- test will complete."
|
||||||
["NT_STATUS_OBJECT_NAME_NOT_FOUND"] =
|
["NT_STATUS_OBJECT_NAME_NOT_FOUND"] =
|
||||||
[[UNKNOWN; not Windows, or Windows with disabled browser service (CLEAN); or Windows with crashed browser service (possibly INFECTED).
|
[[UNKNOWN; not Windows, or Windows with disabled browser service (CLEAN); or Windows with crashed browser service (possibly INFECTED).
|
||||||
| If you know the remote system is Windows, try rebooting it and scanning
|
| If you know the remote system is Windows, try rebooting it and scanning
|
||||||
|_ again. (Error NT_STATUS_OBJECT_NAME_NOT_FOUND)]],
|
|_ again. (Error NT_STATUS_OBJECT_NAME_NOT_FOUND)]],
|
||||||
-- http://www.skullsecurity.org/blog/?p=209#comment-100
|
-- http://www.skullsecurity.org/blog/?p=209#comment-100
|
||||||
-- "That likely means that the server has been locked down, so we
|
-- "That likely means that the server has been locked down, so we
|
||||||
-- don’t have access to the necessary pipe. Fortunately, that means
|
-- don’t have access to the necessary pipe. Fortunately, that means
|
||||||
-- that neither does Conficker — NT_STATUS_ACCESS_DENIED probably
|
-- that neither does Conficker — NT_STATUS_ACCESS_DENIED probably
|
||||||
-- means you’re ok."
|
-- means you’re ok."
|
||||||
["NT_STATUS_ACCESS_DENIED"] =
|
["NT_STATUS_ACCESS_DENIED"] =
|
||||||
[[Likely CLEAN; access was denied.
|
[[Likely CLEAN; access was denied.
|
||||||
| If you have a login, try using --script-args=smbuser=xxx,smbpass=yyy
|
| If you have a login, try using --script-args=smbuser=xxx,smbpass=yyy
|
||||||
| (replace xxx and yyy with your username and password). Also try
|
| (replace xxx and yyy with your username and password). Also try
|
||||||
|_ smbdomain=zzz if you know the domain. (Error NT_STATUS_ACCESS_DENIED)]],
|
|_ smbdomain=zzz if you know the domain. (Error NT_STATUS_ACCESS_DENIED)]],
|
||||||
-- The cause of these two is still unknown.
|
-- The cause of these two is still unknown.
|
||||||
-- ["NT_STATUS_NOT_SUPPORTED"] =
|
-- ["NT_STATUS_NOT_SUPPORTED"] =
|
||||||
-- [[]]
|
-- [[]]
|
||||||
-- http://thatsbroken.com/?cat=5 (doesn't seem common)
|
-- http://thatsbroken.com/?cat=5 (doesn't seem common)
|
||||||
-- ["NT_STATUS_REQUEST_NOT_ACCEPTED"] =
|
-- ["NT_STATUS_REQUEST_NOT_ACCEPTED"] =
|
||||||
-- [[]]
|
-- [[]]
|
||||||
}
|
}
|
||||||
|
|
||||||
---Check if the server is infected with Conficker. This can be detected by a modified MS08-067 patch,
|
---Check if the server is infected with Conficker. This can be detected by a modified MS08-067 patch,
|
||||||
@@ -245,50 +245,50 @@ CONFICKER_ERROR_HELP = {
|
|||||||
--@return (status, result) If status is false, result is an error code; otherwise, result is either
|
--@return (status, result) If status is false, result is an error code; otherwise, result is either
|
||||||
-- <code>INFECTED</code> for infected or <code>CLEAN</code> for not infected.
|
-- <code>INFECTED</code> for infected or <code>CLEAN</code> for not infected.
|
||||||
function check_conficker(host)
|
function check_conficker(host)
|
||||||
local status, smbstate
|
local status, smbstate
|
||||||
local bind_result, netpathcompare_result
|
local bind_result, netpathcompare_result
|
||||||
|
|
||||||
-- Create the SMB session
|
-- Create the SMB session
|
||||||
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER", true)
|
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER", true)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to SRVSVC service
|
-- Bind to SRVSVC service
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try checking a valid string to find Conficker.D
|
-- Try checking a valid string to find Conficker.D
|
||||||
local netpathcanonicalize_result, error_result
|
local netpathcanonicalize_result, error_result
|
||||||
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\")
|
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\")
|
||||||
if(status == true and netpathcanonicalize_result['can_path'] == 0x5c45005c) then
|
if(status == true and netpathcanonicalize_result['can_path'] == 0x5c45005c) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return true, INFECTED2
|
return true, INFECTED2
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try checking an illegal string ("\..\") to find Conficker.C and earlier
|
-- Try checking an illegal string ("\..\") to find Conficker.C and earlier
|
||||||
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\..\\")
|
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\..\\")
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then
|
if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return true, CLEAN
|
return true, CLEAN
|
||||||
elseif(string.find(netpathcanonicalize_result, "WERR_INVALID_PARAMETER") ~= nil) then
|
elseif(string.find(netpathcanonicalize_result, "WERR_INVALID_PARAMETER") ~= nil) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return true, INFECTED
|
return true, INFECTED
|
||||||
else
|
else
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, netpathcanonicalize_result
|
return false, netpathcanonicalize_result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Stop the SMB session
|
-- Stop the SMB session
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
return true, CLEAN
|
return true, CLEAN
|
||||||
end
|
end
|
||||||
|
|
||||||
---While writing <code>smb-enum-sessions</code> I discovered a repeatable null-pointer dereference
|
---While writing <code>smb-enum-sessions</code> I discovered a repeatable null-pointer dereference
|
||||||
@@ -303,130 +303,130 @@ end
|
|||||||
-- <code>VULNERABLE</code> for vulnerable or <code>PATCHED</code> for not vulnerable. If the check
|
-- <code>VULNERABLE</code> for vulnerable or <code>PATCHED</code> for not vulnerable. If the check
|
||||||
-- was skipped, <code>NOTRUN</code> is returned.
|
-- was skipped, <code>NOTRUN</code> is returned.
|
||||||
function check_winreg_Enum_crash(host)
|
function check_winreg_Enum_crash(host)
|
||||||
if(nmap.registry.args.safe ~= nil) then
|
if(nmap.registry.args.safe ~= nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
if(nmap.registry.args.unsafe == nil) then
|
if(nmap.registry.args.unsafe == nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
|
|
||||||
local i, j
|
local i, j
|
||||||
local elements = {}
|
local elements = {}
|
||||||
local status, bind_result, smbstate
|
local status, bind_result, smbstate
|
||||||
|
|
||||||
-- Create the SMB session
|
-- Create the SMB session
|
||||||
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to WINREG service
|
-- Bind to WINREG service
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
local openhku_result
|
local openhku_result
|
||||||
status, openhku_result = msrpc.winreg_openhku(smbstate)
|
status, openhku_result = msrpc.winreg_openhku(smbstate)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, openhku_result
|
return false, openhku_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Loop through the keys under HKEY_USERS and grab the names
|
-- Loop through the keys under HKEY_USERS and grab the names
|
||||||
local enumkey_result
|
local enumkey_result
|
||||||
status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil)
|
status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil)
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return true, VULNERABLE
|
return true, VULNERABLE
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, PATCHED
|
return true, PATCHED
|
||||||
end
|
end
|
||||||
|
|
||||||
local function check_smbv2_dos(host)
|
local function check_smbv2_dos(host)
|
||||||
local status, result
|
local status, result
|
||||||
|
|
||||||
if(nmap.registry.args.safe ~= nil) then
|
if(nmap.registry.args.safe ~= nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
if(nmap.registry.args.unsafe == nil) then
|
if(nmap.registry.args.unsafe == nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
|
|
||||||
-- From http://seclists.org/fulldisclosure/2009/Sep/0039.html with one change on the last line.
|
-- From http://seclists.org/fulldisclosure/2009/Sep/0039.html with one change on the last line.
|
||||||
local buf = string.char(0x00, 0x00, 0x00, 0x90) .. -- Begin SMB header: Session message
|
local buf = string.char(0x00, 0x00, 0x00, 0x90) .. -- Begin SMB header: Session message
|
||||||
string.char(0xff, 0x53, 0x4d, 0x42) .. -- Server Component: SMB
|
string.char(0xff, 0x53, 0x4d, 0x42) .. -- Server Component: SMB
|
||||||
string.char(0x72, 0x00, 0x00, 0x00) .. -- Negociate Protocol
|
string.char(0x72, 0x00, 0x00, 0x00) .. -- Negociate Protocol
|
||||||
string.char(0x00, 0x18, 0x53, 0xc8) .. -- Operation 0x18 & sub 0xc853
|
string.char(0x00, 0x18, 0x53, 0xc8) .. -- Operation 0x18 & sub 0xc853
|
||||||
string.char(0x00, 0x26) .. -- Process ID High: --> :) normal value should be ", 0x00, 0x00"
|
string.char(0x00, 0x26) .. -- Process ID High: --> :) normal value should be ", 0x00, 0x00"
|
||||||
string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe) ..
|
string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe) ..
|
||||||
string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54) ..
|
string.char(0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54) ..
|
||||||
string.char(0x57, 0x4f, 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x20, 0x31) ..
|
string.char(0x57, 0x4f, 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x20, 0x31) ..
|
||||||
string.char(0x2e, 0x30, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30, 0x00) ..
|
string.char(0x2e, 0x30, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30, 0x00) ..
|
||||||
string.char(0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57) ..
|
string.char(0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57) ..
|
||||||
string.char(0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61) ..
|
string.char(0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61) ..
|
||||||
string.char(0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x4c) ..
|
string.char(0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30, 0x32, 0x00, 0x02, 0x4c) ..
|
||||||
string.char(0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c) ..
|
string.char(0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54, 0x20, 0x4c) ..
|
||||||
string.char(0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e) ..
|
string.char(0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e) ..
|
||||||
string.char(0x30, 0x30, 0x32, 0x00)
|
string.char(0x30, 0x30, 0x32, 0x00)
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
if(socket == nil) then
|
if(socket == nil) then
|
||||||
return false, "Couldn't create socket"
|
return false, "Couldn't create socket"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, result = socket:connect(host, 445)
|
status, result = socket:connect(host, 445)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
socket:close()
|
socket:close()
|
||||||
return false, "Couldn't connect to host: " .. result
|
return false, "Couldn't connect to host: " .. result
|
||||||
end
|
end
|
||||||
|
|
||||||
status, result = socket:send(buf)
|
status, result = socket:send(buf)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
socket:close()
|
socket:close()
|
||||||
return false, "Couldn't send the buffer: " .. result
|
return false, "Couldn't send the buffer: " .. result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Close the socket
|
-- Close the socket
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
-- Give it some time to crash
|
-- Give it some time to crash
|
||||||
stdnse.print_debug(1, "smb-check-vulns: Waiting 5 seconds to see if Windows crashed")
|
stdnse.print_debug(1, "smb-check-vulns: Waiting 5 seconds to see if Windows crashed")
|
||||||
stdnse.sleep(5)
|
stdnse.sleep(5)
|
||||||
|
|
||||||
-- Create a new socket
|
-- Create a new socket
|
||||||
socket = nmap.new_socket()
|
socket = nmap.new_socket()
|
||||||
if(socket == nil) then
|
if(socket == nil) then
|
||||||
return false, "Couldn't create socket"
|
return false, "Couldn't create socket"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try and do something simple
|
-- Try and do something simple
|
||||||
stdnse.print_debug(1, "smb-check-vulns: Attempting to connect to the host")
|
stdnse.print_debug(1, "smb-check-vulns: Attempting to connect to the host")
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
status, result = socket:connect(host, 445)
|
status, result = socket:connect(host, 445)
|
||||||
|
|
||||||
-- Check the result
|
-- Check the result
|
||||||
if(status == false or status == nil) then
|
if(status == false or status == nil) then
|
||||||
stdnse.print_debug(1, "smb-check-vulns: Connect failed, host is likely vulnerable!")
|
stdnse.print_debug(1, "smb-check-vulns: Connect failed, host is likely vulnerable!")
|
||||||
socket:close()
|
socket:close()
|
||||||
return true, VULNERABLE
|
return true, VULNERABLE
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try sending something
|
-- Try sending something
|
||||||
stdnse.print_debug(1, "smb-check-vulns: Attempting to send data to the host")
|
stdnse.print_debug(1, "smb-check-vulns: Attempting to send data to the host")
|
||||||
status, result = socket:send("AAAA")
|
status, result = socket:send("AAAA")
|
||||||
if(status == false or status == nil) then
|
if(status == false or status == nil) then
|
||||||
stdnse.print_debug(1, "smb-check-vulns: Send failed, host is likely vulnerable!")
|
stdnse.print_debug(1, "smb-check-vulns: Send failed, host is likely vulnerable!")
|
||||||
socket:close()
|
socket:close()
|
||||||
return true, VULNERABLE
|
return true, VULNERABLE
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug(1, "smb-check-vulns: Checks finished; host is likely not vulnerable.")
|
stdnse.print_debug(1, "smb-check-vulns: Checks finished; host is likely not vulnerable.")
|
||||||
socket:close()
|
socket:close()
|
||||||
return true, PATCHED
|
return true, PATCHED
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -442,56 +442,56 @@ end
|
|||||||
-- ** <code>result == PATCHED</code> for not vulnerable.
|
-- ** <code>result == PATCHED</code> for not vulnerable.
|
||||||
-- ** <code>result == NOTRUN</code> if check skipped.
|
-- ** <code>result == NOTRUN</code> if check skipped.
|
||||||
function check_ms06_025(host)
|
function check_ms06_025(host)
|
||||||
--check for safety flag
|
--check for safety flag
|
||||||
if(nmap.registry.args.safe ~= nil) then
|
if(nmap.registry.args.safe ~= nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
if(nmap.registry.args.unsafe == nil) then
|
if(nmap.registry.args.unsafe == nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
end
|
end
|
||||||
--create the SMB session
|
--create the SMB session
|
||||||
--first we try with the "\router" pipe, then the "\srvsvc" pipe.
|
--first we try with the "\router" pipe, then the "\srvsvc" pipe.
|
||||||
local status, smb_result, smbstate, err_msg
|
local status, smb_result, smbstate, err_msg
|
||||||
status, smb_result = msrpc.start_smb(host, msrpc.ROUTER_PATH)
|
status, smb_result = msrpc.start_smb(host, msrpc.ROUTER_PATH)
|
||||||
|
if(status == false) then
|
||||||
|
err_msg = smb_result
|
||||||
|
status, smb_result = msrpc.start_smb(host, msrpc.SRVSVC_PATH) --rras is also accessible across SRVSVC pipe
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
err_msg = smb_result
|
return false, NOTUP --if not accessible across both pipes then service is inactive
|
||||||
status, smb_result = msrpc.start_smb(host, msrpc.SRVSVC_PATH) --rras is also accessible across SRVSVC pipe
|
|
||||||
if(status == false) then
|
|
||||||
return false, NOTUP --if not accessible across both pipes then service is inactive
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
smbstate = smb_result
|
end
|
||||||
--bind to RRAS service
|
smbstate = smb_result
|
||||||
local bind_result
|
--bind to RRAS service
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.RASRPC_UUID, msrpc.RASRPC_VERSION, nil)
|
local bind_result
|
||||||
if(status == false) then
|
status, bind_result = msrpc.bind(smbstate, msrpc.RASRPC_UUID, msrpc.RASRPC_VERSION, nil)
|
||||||
msrpc.stop_smb(smbstate)
|
if(status == false) then
|
||||||
return false, UNKNOWN --if bind operation results with a false status we can't conclude anything.
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, UNKNOWN --if bind operation results with a false status we can't conclude anything.
|
||||||
|
end
|
||||||
|
if(bind_result['ack_result'] == 0x02) then --0x02 == PROVIDER_REJECTION
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, NOTUP --if bind operation results with true but PROVIDER_REJECTION, then the service is inactive.
|
||||||
|
end
|
||||||
|
local req, buff, sr_result
|
||||||
|
req = msrpc.RRAS_marshall_RequestBuffer(
|
||||||
|
0x01,
|
||||||
|
msrpc.RRAS_RegTypes['GETDEVCONFIG'],
|
||||||
|
msrpc.random_crap(3000))
|
||||||
|
status, sr_result = msrpc.RRAS_SubmitRequest(smbstate, req)
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
--sanity check
|
||||||
|
if(status == false) then
|
||||||
|
stdnse.print_debug(
|
||||||
|
3,
|
||||||
|
"check_ms06_025: RRAS_SubmitRequest failed")
|
||||||
|
if(sr_result == "NT_STATUS_PIPE_BROKEN") then
|
||||||
|
return true, VULNERABLE
|
||||||
|
else
|
||||||
|
return true, PATCHED
|
||||||
end
|
end
|
||||||
if(bind_result['ack_result'] == 0x02) then --0x02 == PROVIDER_REJECTION
|
else
|
||||||
msrpc.stop_smb(smbstate)
|
return true, PATCHED
|
||||||
return false, NOTUP --if bind operation results with true but PROVIDER_REJECTION, then the service is inactive.
|
end
|
||||||
end
|
|
||||||
local req, buff, sr_result
|
|
||||||
req = msrpc.RRAS_marshall_RequestBuffer(
|
|
||||||
0x01,
|
|
||||||
msrpc.RRAS_RegTypes['GETDEVCONFIG'],
|
|
||||||
msrpc.random_crap(3000))
|
|
||||||
status, sr_result = msrpc.RRAS_SubmitRequest(smbstate, req)
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
--sanity check
|
|
||||||
if(status == false) then
|
|
||||||
stdnse.print_debug(
|
|
||||||
3,
|
|
||||||
"check_ms06_025: RRAS_SubmitRequest failed")
|
|
||||||
if(sr_result == "NT_STATUS_PIPE_BROKEN") then
|
|
||||||
return true, VULNERABLE
|
|
||||||
else
|
|
||||||
return true, PATCHED
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return true, PATCHED
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Check the existence of ms07_029 vulnerability in Microsoft Dns Server service.
|
---Check the existence of ms07_029 vulnerability in Microsoft Dns Server service.
|
||||||
@@ -505,47 +505,47 @@ end
|
|||||||
-- ** <code>result == PATCHED</code> for not vulnerable.
|
-- ** <code>result == PATCHED</code> for not vulnerable.
|
||||||
-- ** <code>result == NOTRUN</code> if check skipped.
|
-- ** <code>result == NOTRUN</code> if check skipped.
|
||||||
function check_ms07_029(host)
|
function check_ms07_029(host)
|
||||||
--check for safety flag
|
--check for safety flag
|
||||||
if(nmap.registry.args.safe ~= nil) then
|
if(nmap.registry.args.safe ~= nil) then
|
||||||
return true, NOTRUN
|
return true, NOTRUN
|
||||||
|
end
|
||||||
|
if(nmap.registry.args.unsafe == nil) then
|
||||||
|
return true, NOTRUN
|
||||||
|
end
|
||||||
|
--create the SMB session
|
||||||
|
local status, smbstate
|
||||||
|
status, smbstate = msrpc.start_smb(host, msrpc.DNSSERVER_PATH)
|
||||||
|
if(status == false) then
|
||||||
|
return false, NOTUP --if not accessible across pipe then the service is inactive
|
||||||
|
end
|
||||||
|
--bind to DNSSERVER service
|
||||||
|
local bind_result
|
||||||
|
status, bind_result = msrpc.bind(smbstate, msrpc.DNSSERVER_UUID, msrpc.DNSSERVER_VERSION)
|
||||||
|
if(status == false) then
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
return false, UNKNOWN --if bind operation results with a false status we can't conclude anything.
|
||||||
|
end
|
||||||
|
--call
|
||||||
|
local req_blob, q_result
|
||||||
|
status, q_result = msrpc.DNSSERVER_Query(
|
||||||
|
smbstate,
|
||||||
|
"VULNSRV",
|
||||||
|
string.rep("\\\13", 1000),
|
||||||
|
1)--any op num will do
|
||||||
|
--sanity check
|
||||||
|
msrpc.stop_smb(smbstate)
|
||||||
|
if(status == false) then
|
||||||
|
stdnse.print_debug(
|
||||||
|
3,
|
||||||
|
"check_ms07_029: DNSSERVER_Query failed")
|
||||||
|
if(q_result == "NT_STATUS_PIPE_BROKEN") then
|
||||||
|
return true, VULNERABLE
|
||||||
|
else
|
||||||
|
return true, PATCHED
|
||||||
end
|
end
|
||||||
if(nmap.registry.args.unsafe == nil) then
|
else
|
||||||
return true, NOTRUN
|
return true, PATCHED
|
||||||
end
|
end
|
||||||
--create the SMB session
|
|
||||||
local status, smbstate
|
|
||||||
status, smbstate = msrpc.start_smb(host, msrpc.DNSSERVER_PATH)
|
|
||||||
if(status == false) then
|
|
||||||
return false, NOTUP --if not accessible across pipe then the service is inactive
|
|
||||||
end
|
|
||||||
--bind to DNSSERVER service
|
|
||||||
local bind_result
|
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.DNSSERVER_UUID, msrpc.DNSSERVER_VERSION)
|
|
||||||
if(status == false) then
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
return false, UNKNOWN --if bind operation results with a false status we can't conclude anything.
|
|
||||||
end
|
|
||||||
--call
|
|
||||||
local req_blob, q_result
|
|
||||||
status, q_result = msrpc.DNSSERVER_Query(
|
|
||||||
smbstate,
|
|
||||||
"VULNSRV",
|
|
||||||
string.rep("\\\13", 1000),
|
|
||||||
1)--any op num will do
|
|
||||||
--sanity check
|
|
||||||
msrpc.stop_smb(smbstate)
|
|
||||||
if(status == false) then
|
|
||||||
stdnse.print_debug(
|
|
||||||
3,
|
|
||||||
"check_ms07_029: DNSSERVER_Query failed")
|
|
||||||
if(q_result == "NT_STATUS_PIPE_BROKEN") then
|
|
||||||
return true, VULNERABLE
|
|
||||||
else
|
|
||||||
return true, PATCHED
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return true, PATCHED
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---Returns the appropriate text to display, if any.
|
---Returns the appropriate text to display, if any.
|
||||||
@@ -557,129 +557,129 @@ end
|
|||||||
--@param minimum_debug [optional] The minimum debug level required before the message is displayed (default: 0).
|
--@param minimum_debug [optional] The minimum debug level required before the message is displayed (default: 0).
|
||||||
--@return A string with a textual representation of the error (or empty string, if it was determined that the message shouldn't be displayed).
|
--@return A string with a textual representation of the error (or empty string, if it was determined that the message shouldn't be displayed).
|
||||||
local function get_response(check, message, description, minimum_verbosity, minimum_debug)
|
local function get_response(check, message, description, minimum_verbosity, minimum_debug)
|
||||||
if(minimum_debug == nil) then
|
if(minimum_debug == nil) then
|
||||||
minimum_debug = 0
|
minimum_debug = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we have appropriate verbosity/debug
|
-- Check if we have appropriate verbosity/debug
|
||||||
if(nmap.verbosity() >= minimum_verbosity and nmap.debugging() >= minimum_debug) then
|
if(nmap.verbosity() >= minimum_verbosity and nmap.debugging() >= minimum_debug) then
|
||||||
if(description == nil or description == '') then
|
if(description == nil or description == '') then
|
||||||
return string.format("%s: %s", check, message)
|
return string.format("%s: %s", check, message)
|
||||||
else
|
else
|
||||||
return string.format("%s: %s (%s)", check, message, description)
|
return string.format("%s: %s (%s)", check, message, description)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local status, result, message
|
local status, result, message
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- Check for ms08-067
|
-- Check for ms08-067
|
||||||
status, result, message = check_ms08_067(host)
|
status, result, message = check_ms08_067(host)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
table.insert(response, get_response("MS08-067", "ERROR", result, 0, 1))
|
table.insert(response, get_response("MS08-067", "ERROR", result, 0, 1))
|
||||||
else
|
else
|
||||||
if(result == VULNERABLE) then
|
if(result == VULNERABLE) then
|
||||||
table.insert(response, get_response("MS08-067", "VULNERABLE", nil, 0))
|
table.insert(response, get_response("MS08-067", "VULNERABLE", nil, 0))
|
||||||
elseif(result == UNKNOWN) then
|
elseif(result == UNKNOWN) then
|
||||||
table.insert(response, get_response("MS08-067", "LIKELY VULNERABLE", "host stopped responding", 1)) -- TODO: this isn't very accurate
|
table.insert(response, get_response("MS08-067", "LIKELY VULNERABLE", "host stopped responding", 1)) -- TODO: this isn't very accurate
|
||||||
elseif(result == NOTRUN) then
|
elseif(result == NOTRUN) then
|
||||||
table.insert(response, get_response("MS08-067", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
table.insert(response, get_response("MS08-067", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
||||||
elseif(result == INFECTED) then
|
elseif(result == INFECTED) then
|
||||||
table.insert(response, get_response("MS08-067", "NOT VULNERABLE", "likely by Conficker", 0))
|
table.insert(response, get_response("MS08-067", "NOT VULNERABLE", "likely by Conficker", 0))
|
||||||
else
|
|
||||||
table.insert(response, get_response("MS08-067", "NOT VULNERABLE", nil, 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for Conficker
|
|
||||||
status, result = check_conficker(host)
|
|
||||||
if(status == false) then
|
|
||||||
local msg = CONFICKER_ERROR_HELP[result] or "UNKNOWN; got error " .. result
|
|
||||||
table.insert(response, get_response("Conficker", msg, nil, 1)) -- Only set verbosity for this, since it might be an error or it might be UNKNOWN
|
|
||||||
else
|
|
||||||
if(result == CLEAN) then
|
|
||||||
table.insert(response, get_response("Conficker", "Likely CLEAN", nil, 1))
|
|
||||||
elseif(result == INFECTED) then
|
|
||||||
table.insert(response, get_response("Conficker", "Likely INFECTED", "by Conficker.C or lower", 0))
|
|
||||||
elseif(result == INFECTED2) then
|
|
||||||
table.insert(response, get_response("Conficker", "Likely INFECTED", "by Conficker.D or higher", 0))
|
|
||||||
else
|
|
||||||
table.insert(response, get_response("Conficker", "UNKNOWN", result, 0, 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for a winreg_Enum crash
|
|
||||||
status, result = check_winreg_Enum_crash(host)
|
|
||||||
if(status == false) then
|
|
||||||
table.insert(response, get_response("regsvc DoS", "ERROR", result, 0, 1))
|
|
||||||
else
|
|
||||||
if(result == VULNERABLE) then
|
|
||||||
table.insert(response, get_response("regsvc DoS", "VULNERABLE", nil, 0))
|
|
||||||
elseif(result == NOTRUN) then
|
|
||||||
table.insert(response, get_response("regsvc DoS", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
|
||||||
else
|
|
||||||
table.insert(response, get_response("regsvc DoS", "NOT VULNERABLE", nil, 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for SMBv2 vulnerablity
|
|
||||||
status, result = check_smbv2_dos(host)
|
|
||||||
if(status == false) then
|
|
||||||
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "ERROR", result, 0, 1))
|
|
||||||
else
|
|
||||||
if(result == VULNERABLE) then
|
|
||||||
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "VULNERABLE", nil, 0))
|
|
||||||
elseif(result == NOTRUN) then
|
|
||||||
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
|
||||||
else
|
|
||||||
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "NOT VULNERABLE", nil, 1))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for ms06-025
|
|
||||||
status, result = check_ms06_025(host)
|
|
||||||
if(status == false) then
|
|
||||||
if(result == NOTUP) then
|
|
||||||
table.insert(response, get_response("MS06-025", "NO SERVICE", "the Ras RPC service is inactive", 1))
|
|
||||||
else
|
|
||||||
table.insert(response, get_response("MS06-025", "ERROR", result, 0, 1))
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
if(result == VULNERABLE) then
|
table.insert(response, get_response("MS08-067", "NOT VULNERABLE", nil, 1))
|
||||||
table.insert(response, get_response("MS06-025", "VULNERABLE", nil, 0))
|
|
||||||
elseif(result == NOTRUN) then
|
|
||||||
table.insert(response, get_response("MS06-025", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
|
||||||
elseif(result == NOTUP) then
|
|
||||||
table.insert(response, get_response("MS06-025", "NO SERVICE", "the Ras RPC service is inactive", 1))
|
|
||||||
else
|
|
||||||
table.insert(response, get_response("MS06-025", "NOT VULNERABLE", nil, 1))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Check for ms07-029
|
-- Check for Conficker
|
||||||
status, result = check_ms07_029(host)
|
status, result = check_conficker(host)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
if(result == NOTUP) then
|
local msg = CONFICKER_ERROR_HELP[result] or "UNKNOWN; got error " .. result
|
||||||
table.insert(response, get_response("MS07-029", "NO SERVICE", "the Dns Server RPC service is inactive", 1))
|
table.insert(response, get_response("Conficker", msg, nil, 1)) -- Only set verbosity for this, since it might be an error or it might be UNKNOWN
|
||||||
else
|
else
|
||||||
table.insert(response, get_response("MS07-029", "ERROR", result, 0, 1))
|
if(result == CLEAN) then
|
||||||
end
|
table.insert(response, get_response("Conficker", "Likely CLEAN", nil, 1))
|
||||||
|
elseif(result == INFECTED) then
|
||||||
|
table.insert(response, get_response("Conficker", "Likely INFECTED", "by Conficker.C or lower", 0))
|
||||||
|
elseif(result == INFECTED2) then
|
||||||
|
table.insert(response, get_response("Conficker", "Likely INFECTED", "by Conficker.D or higher", 0))
|
||||||
else
|
else
|
||||||
if(result == VULNERABLE) then
|
table.insert(response, get_response("Conficker", "UNKNOWN", result, 0, 1))
|
||||||
table.insert(response, get_response("MS07-029", "VULNERABLE", nil, 0))
|
|
||||||
elseif(result == NOTRUN) then
|
|
||||||
table.insert(response, get_response("MS07-029", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
|
||||||
else
|
|
||||||
table.insert(response, get_response("MS07-029", "NOT VULNERABLE", nil, 1))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
-- Check for a winreg_Enum crash
|
||||||
|
status, result = check_winreg_Enum_crash(host)
|
||||||
|
if(status == false) then
|
||||||
|
table.insert(response, get_response("regsvc DoS", "ERROR", result, 0, 1))
|
||||||
|
else
|
||||||
|
if(result == VULNERABLE) then
|
||||||
|
table.insert(response, get_response("regsvc DoS", "VULNERABLE", nil, 0))
|
||||||
|
elseif(result == NOTRUN) then
|
||||||
|
table.insert(response, get_response("regsvc DoS", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
||||||
|
else
|
||||||
|
table.insert(response, get_response("regsvc DoS", "NOT VULNERABLE", nil, 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for SMBv2 vulnerablity
|
||||||
|
status, result = check_smbv2_dos(host)
|
||||||
|
if(status == false) then
|
||||||
|
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "ERROR", result, 0, 1))
|
||||||
|
else
|
||||||
|
if(result == VULNERABLE) then
|
||||||
|
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "VULNERABLE", nil, 0))
|
||||||
|
elseif(result == NOTRUN) then
|
||||||
|
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
||||||
|
else
|
||||||
|
table.insert(response, get_response("SMBv2 DoS (CVE-2009-3103)", "NOT VULNERABLE", nil, 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for ms06-025
|
||||||
|
status, result = check_ms06_025(host)
|
||||||
|
if(status == false) then
|
||||||
|
if(result == NOTUP) then
|
||||||
|
table.insert(response, get_response("MS06-025", "NO SERVICE", "the Ras RPC service is inactive", 1))
|
||||||
|
else
|
||||||
|
table.insert(response, get_response("MS06-025", "ERROR", result, 0, 1))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if(result == VULNERABLE) then
|
||||||
|
table.insert(response, get_response("MS06-025", "VULNERABLE", nil, 0))
|
||||||
|
elseif(result == NOTRUN) then
|
||||||
|
table.insert(response, get_response("MS06-025", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
||||||
|
elseif(result == NOTUP) then
|
||||||
|
table.insert(response, get_response("MS06-025", "NO SERVICE", "the Ras RPC service is inactive", 1))
|
||||||
|
else
|
||||||
|
table.insert(response, get_response("MS06-025", "NOT VULNERABLE", nil, 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check for ms07-029
|
||||||
|
status, result = check_ms07_029(host)
|
||||||
|
if(status == false) then
|
||||||
|
if(result == NOTUP) then
|
||||||
|
table.insert(response, get_response("MS07-029", "NO SERVICE", "the Dns Server RPC service is inactive", 1))
|
||||||
|
else
|
||||||
|
table.insert(response, get_response("MS07-029", "ERROR", result, 0, 1))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if(result == VULNERABLE) then
|
||||||
|
table.insert(response, get_response("MS07-029", "VULNERABLE", nil, 0))
|
||||||
|
elseif(result == NOTRUN) then
|
||||||
|
table.insert(response, get_response("MS07-029", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
|
||||||
|
else
|
||||||
|
table.insert(response, get_response("MS07-029", "NOT VULNERABLE", nil, 1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -87,206 +87,206 @@ dependencies = {"smb-brute"}
|
|||||||
|
|
||||||
|
|
||||||
function psl_mode (list, i)
|
function psl_mode (list, i)
|
||||||
local mode
|
local mode
|
||||||
|
|
||||||
-- Decide connector for process.
|
-- Decide connector for process.
|
||||||
if #list == 1 then
|
if #list == 1 then
|
||||||
mode = "only"
|
mode = "only"
|
||||||
elseif i == 1 then
|
elseif i == 1 then
|
||||||
mode = "first"
|
mode = "first"
|
||||||
elseif i < #list then
|
elseif i < #list then
|
||||||
mode = "middle"
|
mode = "middle"
|
||||||
else
|
else
|
||||||
mode = "last"
|
mode = "last"
|
||||||
end
|
end
|
||||||
|
|
||||||
return mode
|
return mode
|
||||||
end
|
end
|
||||||
|
|
||||||
function psl_print (psl, lvl)
|
function psl_print (psl, lvl)
|
||||||
-- Print out table header.
|
-- Print out table header.
|
||||||
local result = ""
|
local result = ""
|
||||||
if lvl == 2 then
|
if lvl == 2 then
|
||||||
result = result .. " PID PPID Priority Threads Handles\n"
|
result = result .. " PID PPID Priority Threads Handles\n"
|
||||||
result = result .. "----- ----- -------- ------- -------\n"
|
result = result .. "----- ----- -------- ------- -------\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Find how many root processes there are.
|
-- Find how many root processes there are.
|
||||||
local roots = {}
|
local roots = {}
|
||||||
for i, ps in pairs(psl) do
|
for i, ps in pairs(psl) do
|
||||||
if psl[ps.ppid] == nil or ps.ppid == ps.pid then
|
if psl[ps.ppid] == nil or ps.ppid == ps.pid then
|
||||||
table.insert(roots, i)
|
table.insert(roots, i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(roots)
|
table.sort(roots)
|
||||||
|
|
||||||
-- Create vertical sibling bars.
|
-- Create vertical sibling bars.
|
||||||
local bars = {}
|
local bars = {}
|
||||||
if #roots ~= 1 then
|
if #roots ~= 1 then
|
||||||
table.insert(bars, 2)
|
table.insert(bars, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Print out each root of the tree.
|
-- Print out each root of the tree.
|
||||||
for i, root in ipairs(roots) do
|
for i, root in ipairs(roots) do
|
||||||
local mode = psl_mode(roots, i)
|
local mode = psl_mode(roots, i)
|
||||||
result = result .. psl_tree(psl, root, 0, bars, mode, lvl)
|
result = result .. psl_tree(psl, root, 0, bars, mode, lvl)
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function psl_tree (psl, pid, column, bars, mode, lvl)
|
function psl_tree (psl, pid, column, bars, mode, lvl)
|
||||||
local ps = psl[pid]
|
local ps = psl[pid]
|
||||||
|
|
||||||
-- Delete vertical sibling link.
|
-- Delete vertical sibling link.
|
||||||
if mode == "last" then
|
if mode == "last" then
|
||||||
table.remove(bars)
|
table.remove(bars)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Print information table.
|
-- Print information table.
|
||||||
local info = ""
|
local info = ""
|
||||||
if lvl == 2 then
|
if lvl == 2 then
|
||||||
info = info .. string.format("% 5d ", ps.pid)
|
info = info .. string.format("% 5d ", ps.pid)
|
||||||
info = info .. string.format("% 5d ", ps.ppid)
|
info = info .. string.format("% 5d ", ps.ppid)
|
||||||
info = info .. string.format("% 8d ", ps.prio)
|
info = info .. string.format("% 8d ", ps.prio)
|
||||||
info = info .. string.format("% 7d ", ps.thrd)
|
info = info .. string.format("% 7d ", ps.thrd)
|
||||||
info = info .. string.format("% 7d ", ps.hndl)
|
info = info .. string.format("% 7d ", ps.hndl)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Print vertical sibling bars.
|
-- Print vertical sibling bars.
|
||||||
local prefix = ""
|
local prefix = ""
|
||||||
local i = 1
|
local i = 1
|
||||||
for j = 1, column do
|
for j = 1, column do
|
||||||
if bars[i] == j then
|
if bars[i] == j then
|
||||||
prefix = prefix .. "|"
|
prefix = prefix .. "|"
|
||||||
i = i + 1
|
i = i + 1
|
||||||
else
|
else
|
||||||
prefix = prefix .. " "
|
prefix = prefix .. " "
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Strings used to separate processes from one another.
|
-- Strings used to separate processes from one another.
|
||||||
local separators = {
|
local separators = {
|
||||||
first = "`+-";
|
first = "`+-";
|
||||||
last = " `-";
|
last = " `-";
|
||||||
middle = " +-";
|
middle = " +-";
|
||||||
only = "`-";
|
only = "`-";
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Format process itself.
|
-- Format process itself.
|
||||||
local result = "\n" .. info .. prefix .. separators[mode] .. ps.name
|
local result = "\n" .. info .. prefix .. separators[mode] .. ps.name
|
||||||
|
|
||||||
-- Find children of the process.
|
-- Find children of the process.
|
||||||
local children = {}
|
local children = {}
|
||||||
for child_pid, child in pairs(psl) do
|
for child_pid, child in pairs(psl) do
|
||||||
if child_pid ~= pid and child.ppid == pid then
|
if child_pid ~= pid and child.ppid == pid then
|
||||||
table.insert(children, child_pid)
|
table.insert(children, child_pid)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(children)
|
table.sort(children)
|
||||||
|
|
||||||
-- Add vertical sibling link between children.
|
-- Add vertical sibling link between children.
|
||||||
column = column + #separators[mode]
|
column = column + #separators[mode]
|
||||||
if #children > 1 then
|
if #children > 1 then
|
||||||
table.insert(bars, column + 2)
|
table.insert(bars, column + 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Format process's children.
|
-- Format process's children.
|
||||||
for i, pid in ipairs(children) do
|
for i, pid in ipairs(children) do
|
||||||
local mode = psl_mode(children, i)
|
local mode = psl_mode(children, i)
|
||||||
result = result .. psl_tree(psl, pid, column, bars, mode, lvl)
|
result = result .. psl_tree(psl, pid, column, bars, mode, lvl)
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
-- Get the process list
|
-- Get the process list
|
||||||
local status, result = msrpcperformance.get_performance_data(host, "230")
|
local status, result = msrpcperformance.get_performance_data(host, "230")
|
||||||
if status == false then
|
if status == false then
|
||||||
if nmap.debugging() > 0 then
|
if nmap.debugging() > 0 then
|
||||||
return "ERROR: " .. result
|
return "ERROR: " .. result
|
||||||
else
|
else
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the process table
|
-- Get the process table
|
||||||
local process = result["Process"]
|
local process = result["Process"]
|
||||||
|
|
||||||
-- Put the processes into an array, and sort them by pid.
|
-- Put the processes into an array, and sort them by pid.
|
||||||
local names = {}
|
local names = {}
|
||||||
for i, v in pairs(process) do
|
for i, v in pairs(process) do
|
||||||
if i ~= "_Total" then
|
if i ~= "_Total" then
|
||||||
names[#names + 1] = i
|
names[#names + 1] = i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(names, function (a, b) return process[a]["ID Process"] < process[b]["ID Process"] end)
|
table.sort(names, function (a, b) return process[a]["ID Process"] < process[b]["ID Process"] end)
|
||||||
|
|
||||||
-- Put the processes into an array indexed by pid and with a value equal
|
-- Put the processes into an array indexed by pid and with a value equal
|
||||||
-- to the name (so we can look it up easily when we need to).
|
-- to the name (so we can look it up easily when we need to).
|
||||||
local process_id = {}
|
local process_id = {}
|
||||||
for i, v in pairs(process) do
|
for i, v in pairs(process) do
|
||||||
process_id[v["ID Process"]] = i
|
process_id[v["ID Process"]] = i
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Fill the process list table.
|
-- Fill the process list table.
|
||||||
--
|
--
|
||||||
-- Used fields:
|
-- Used fields:
|
||||||
-- Creating Process ID
|
-- Creating Process ID
|
||||||
-- Handle Count
|
-- Handle Count
|
||||||
-- ID Process
|
-- ID Process
|
||||||
-- Priority Base
|
-- Priority Base
|
||||||
-- Thread Count
|
-- Thread Count
|
||||||
--
|
--
|
||||||
-- Unused fields:
|
-- Unused fields:
|
||||||
-- % Privileged Time
|
-- % Privileged Time
|
||||||
-- % Processor Time
|
-- % Processor Time
|
||||||
-- % User Time
|
-- % User Time
|
||||||
-- Elapsed Time
|
-- Elapsed Time
|
||||||
-- IO Data Bytes/sec
|
-- IO Data Bytes/sec
|
||||||
-- IO Data Operations/sec
|
-- IO Data Operations/sec
|
||||||
-- IO Other Bytes/sec
|
-- IO Other Bytes/sec
|
||||||
-- IO Other Operations/sec
|
-- IO Other Operations/sec
|
||||||
-- IO Read Bytes/sec
|
-- IO Read Bytes/sec
|
||||||
-- IO Read Operations/sec
|
-- IO Read Operations/sec
|
||||||
-- IO Write Bytes/sec
|
-- IO Write Bytes/sec
|
||||||
-- IO Write Operations/sec
|
-- IO Write Operations/sec
|
||||||
-- Page Faults/sec
|
-- Page Faults/sec
|
||||||
-- Page File Bytes
|
-- Page File Bytes
|
||||||
-- Page File Bytes Peak
|
-- Page File Bytes Peak
|
||||||
-- Pool Nonpaged Bytes
|
-- Pool Nonpaged Bytes
|
||||||
-- Pool Paged Bytes
|
-- Pool Paged Bytes
|
||||||
-- Private Bytes
|
-- Private Bytes
|
||||||
-- Virtual Bytes
|
-- Virtual Bytes
|
||||||
-- Virtual Bytes Peak
|
-- Virtual Bytes Peak
|
||||||
-- Working Set
|
-- Working Set
|
||||||
-- Working Set Peak
|
-- Working Set Peak
|
||||||
local psl = {}
|
local psl = {}
|
||||||
for i, name in ipairs(names) do
|
for i, name in ipairs(names) do
|
||||||
if name ~= "_Total" then
|
if name ~= "_Total" then
|
||||||
psl[process[name]["ID Process"]] = {
|
psl[process[name]["ID Process"]] = {
|
||||||
name = name;
|
name = name;
|
||||||
pid = process[name]["ID Process"];
|
pid = process[name]["ID Process"];
|
||||||
ppid = process[name]["Creating Process ID"];
|
ppid = process[name]["Creating Process ID"];
|
||||||
prio = process[name]["Priority Base"];
|
prio = process[name]["Priority Base"];
|
||||||
thrd = process[name]["Thread Count"];
|
thrd = process[name]["Thread Count"];
|
||||||
hndl = process[name]["Handle Count"];
|
hndl = process[name]["Handle Count"];
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Produce final output.
|
-- Produce final output.
|
||||||
local response
|
local response
|
||||||
if nmap.verbosity() == 0 then
|
if nmap.verbosity() == 0 then
|
||||||
response = "|_ " .. stdnse.strjoin(", ", names)
|
response = "|_ " .. stdnse.strjoin(", ", names)
|
||||||
else
|
else
|
||||||
response = "\n" .. psl_print(psl, nmap.verbosity())
|
response = "\n" .. psl_print(psl, nmap.verbosity())
|
||||||
end
|
end
|
||||||
|
|
||||||
return response
|
return response
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ dependencies = {"smb-brute"}
|
|||||||
|
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Attempts to enumerate the sessions on a remote system using MSRPC calls. This will likely fail
|
---Attempts to enumerate the sessions on a remote system using MSRPC calls. This will likely fail
|
||||||
@@ -78,34 +78,34 @@ end
|
|||||||
--@return Status (true or false).
|
--@return Status (true or false).
|
||||||
--@return List of sessions (if status is true) or an an error string (if status is false).
|
--@return List of sessions (if status is true) or an an error string (if status is false).
|
||||||
local function srvsvc_enum_sessions(host)
|
local function srvsvc_enum_sessions(host)
|
||||||
local i
|
local i
|
||||||
local status, smbstate
|
local status, smbstate
|
||||||
local bind_result, netsessenum_result
|
local bind_result, netsessenum_result
|
||||||
|
|
||||||
-- Create the SMB session
|
-- Create the SMB session
|
||||||
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to SRVSVC service
|
-- Bind to SRVSVC service
|
||||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Call netsessenum
|
-- Call netsessenum
|
||||||
status, netsessenum_result = msrpc.srvsvc_netsessenum(smbstate, host.ip)
|
status, netsessenum_result = msrpc.srvsvc_netsessenum(smbstate, host.ip)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, netsessenum_result
|
return false, netsessenum_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Stop the SMB session
|
-- Stop the SMB session
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
return true, netsessenum_result['ctr']['array']
|
return true, netsessenum_result['ctr']['array']
|
||||||
end
|
end
|
||||||
|
|
||||||
---Enumerates the users logged in locally (or through terminal services) by using functions
|
---Enumerates the users logged in locally (or through terminal services) by using functions
|
||||||
@@ -118,218 +118,218 @@ end
|
|||||||
--@return An array of user tables, each with the keys <code>name</code>, <code>domain</code>, and <code>changed_date</code> (representing
|
--@return An array of user tables, each with the keys <code>name</code>, <code>domain</code>, and <code>changed_date</code> (representing
|
||||||
-- when they logged in).
|
-- when they logged in).
|
||||||
local function winreg_enum_rids(host)
|
local function winreg_enum_rids(host)
|
||||||
local i, j
|
local i, j
|
||||||
local elements = {}
|
local elements = {}
|
||||||
|
|
||||||
-- Create the SMB session
|
-- Create the SMB session
|
||||||
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to WINREG service
|
-- Bind to WINREG service
|
||||||
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, openhku_result = msrpc.winreg_openhku(smbstate)
|
local status, openhku_result = msrpc.winreg_openhku(smbstate)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, openhku_result
|
return false, openhku_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Loop through the keys under HKEY_USERS and grab the names
|
-- Loop through the keys under HKEY_USERS and grab the names
|
||||||
i = 0
|
i = 0
|
||||||
repeat
|
repeat
|
||||||
local status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i, "")
|
local status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i, "")
|
||||||
|
|
||||||
if(status == true) then
|
if(status == true) then
|
||||||
local status, openkey_result
|
local status, openkey_result
|
||||||
|
|
||||||
local element = {}
|
local element = {}
|
||||||
element['name'] = enumkey_result['name']
|
element['name'] = enumkey_result['name']
|
||||||
|
|
||||||
-- To get the time the user logged in, we check the 'Volatile Environment' key
|
-- To get the time the user logged in, we check the 'Volatile Environment' key
|
||||||
-- This can fail with the 'guest' account due to access restrictions
|
-- This can fail with the 'guest' account due to access restrictions
|
||||||
local status, openkey_result = msrpc.winreg_openkey(smbstate, openhku_result['handle'], element['name'] .. "\\Volatile Environment")
|
local status, openkey_result = msrpc.winreg_openkey(smbstate, openhku_result['handle'], element['name'] .. "\\Volatile Environment")
|
||||||
if(status ~= false) then
|
if(status ~= false) then
|
||||||
local queryinfokey_result, closekey_result
|
local queryinfokey_result, closekey_result
|
||||||
|
|
||||||
-- Query the info about this key. The response will tell us when the user logged into the server.
|
-- Query the info about this key. The response will tell us when the user logged into the server.
|
||||||
local status, queryinfokey_result = msrpc.winreg_queryinfokey(smbstate, openkey_result['handle'])
|
local status, queryinfokey_result = msrpc.winreg_queryinfokey(smbstate, openkey_result['handle'])
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, queryinfokey_result
|
return false, queryinfokey_result
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'])
|
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'])
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, closekey_result
|
return false, closekey_result
|
||||||
end
|
end
|
||||||
|
|
||||||
element['changed_date'] = queryinfokey_result['last_changed_date']
|
element['changed_date'] = queryinfokey_result['last_changed_date']
|
||||||
else
|
else
|
||||||
-- Getting extra details failed, but we can still handle this
|
-- Getting extra details failed, but we can still handle this
|
||||||
element['changed_date'] = "<unknown>"
|
element['changed_date'] = "<unknown>"
|
||||||
end
|
end
|
||||||
elements[#elements + 1] = element
|
elements[#elements + 1] = element
|
||||||
end
|
end
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
until status ~= true
|
until status ~= true
|
||||||
|
|
||||||
local status, closekey_result = msrpc.winreg_closekey(smbstate, openhku_result['handle'])
|
local status, closekey_result = msrpc.winreg_closekey(smbstate, openhku_result['handle'])
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, closekey_result
|
return false, closekey_result
|
||||||
end
|
end
|
||||||
|
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
-- Start a new SMB session
|
-- Start a new SMB session
|
||||||
local status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
local status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to LSA service
|
-- Bind to LSA service
|
||||||
local status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
local status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get a policy handle
|
-- Get a policy handle
|
||||||
local status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
local status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, openpolicy2_result
|
return false, openpolicy2_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Convert the SID to the name of the user
|
-- Convert the SID to the name of the user
|
||||||
local results = {}
|
local results = {}
|
||||||
stdnse.print_debug(3, "MSRPC: Found %d SIDs that might be logged in", #elements)
|
stdnse.print_debug(3, "MSRPC: Found %d SIDs that might be logged in", #elements)
|
||||||
for i = 1, #elements, 1 do
|
for i = 1, #elements, 1 do
|
||||||
if(elements[i]['name'] ~= nil) then
|
if(elements[i]['name'] ~= nil) then
|
||||||
local sid = elements[i]['name']
|
local sid = elements[i]['name']
|
||||||
if(string.find(sid, "^S%-") ~= nil and string.find(sid, "%-%d+$") ~= nil) then
|
if(string.find(sid, "^S%-") ~= nil and string.find(sid, "%-%d+$") ~= nil) then
|
||||||
-- The rid is the last digits before the end of the string
|
-- The rid is the last digits before the end of the string
|
||||||
local rid = string.sub(sid, string.find(sid, "%d+$"))
|
local rid = string.sub(sid, string.find(sid, "%d+$"))
|
||||||
|
|
||||||
local status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], {elements[i]['name']})
|
local status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], {elements[i]['name']})
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
-- It may not succeed, if it doesn't that's ok
|
-- It may not succeed, if it doesn't that's ok
|
||||||
stdnse.print_debug(3, "MSRPC: Lookup failed")
|
stdnse.print_debug(3, "MSRPC: Lookup failed")
|
||||||
else
|
else
|
||||||
-- Create the result array
|
-- Create the result array
|
||||||
local result = {}
|
local result = {}
|
||||||
result['changed_date'] = elements[i]['changed_date']
|
result['changed_date'] = elements[i]['changed_date']
|
||||||
result['rid'] = rid
|
result['rid'] = rid
|
||||||
|
|
||||||
-- Fill in the result from the response
|
-- Fill in the result from the response
|
||||||
if(lookupsids2_result['names']['names'][1] == nil) then
|
if(lookupsids2_result['names']['names'][1] == nil) then
|
||||||
result['name'] = "<unknown>"
|
result['name'] = "<unknown>"
|
||||||
result['type'] = "<unknown>"
|
result['type'] = "<unknown>"
|
||||||
result['domain'] = ""
|
result['domain'] = ""
|
||||||
else
|
else
|
||||||
result['name'] = lookupsids2_result['names']['names'][1]['name']
|
result['name'] = lookupsids2_result['names']['names'][1]['name']
|
||||||
result['type'] = lookupsids2_result['names']['names'][1]['sid_type']
|
result['type'] = lookupsids2_result['names']['names'][1]['sid_type']
|
||||||
if(lookupsids2_result['domains'] ~= nil and lookupsids2_result['domains']['domains'] ~= nil and lookupsids2_result['domains']['domains'][1] ~= nil) then
|
if(lookupsids2_result['domains'] ~= nil and lookupsids2_result['domains']['domains'] ~= nil and lookupsids2_result['domains']['domains'][1] ~= nil) then
|
||||||
result['domain'] = lookupsids2_result['domains']['domains'][1]['name']
|
result['domain'] = lookupsids2_result['domains']['domains'][1]['name']
|
||||||
else
|
else
|
||||||
result['domain'] = ""
|
result['domain'] = ""
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if(result['type'] ~= "SID_NAME_WKN_GRP") then -- Don't show "well known" accounts
|
if(result['type'] ~= "SID_NAME_WKN_GRP") then -- Don't show "well known" accounts
|
||||||
-- Add it to the results
|
-- Add it to the results
|
||||||
results[#results + 1] = result
|
results[#results + 1] = result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Close the policy
|
-- Close the policy
|
||||||
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
||||||
|
|
||||||
-- Stop the session
|
-- Stop the session
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
return true, results
|
return true, results
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--_G.TRACEBACK = TRACEBACK or {}
|
--_G.TRACEBACK = TRACEBACK or {}
|
||||||
action = function(host)
|
action = function(host)
|
||||||
-- TRACEBACK[coroutine.running()] = true;
|
-- TRACEBACK[coroutine.running()] = true;
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- Enumerate the logged in users
|
-- Enumerate the logged in users
|
||||||
local logged_in = {}
|
local logged_in = {}
|
||||||
local status1, users = winreg_enum_rids(host)
|
local status1, users = winreg_enum_rids(host)
|
||||||
if(status1 == false) then
|
if(status1 == false) then
|
||||||
logged_in['warning'] = "Couldn't enumerate login sessions: " .. users
|
logged_in['warning'] = "Couldn't enumerate login sessions: " .. users
|
||||||
else
|
else
|
||||||
logged_in['name'] = "Users logged in"
|
logged_in['name'] = "Users logged in"
|
||||||
if(#users == 0) then
|
if(#users == 0) then
|
||||||
table.insert(response, "<nobody>")
|
table.insert(response, "<nobody>")
|
||||||
else
|
else
|
||||||
for i = 1, #users, 1 do
|
for i = 1, #users, 1 do
|
||||||
if(users[i]['name'] ~= nil) then
|
if(users[i]['name'] ~= nil) then
|
||||||
table.insert(logged_in, string.format("%s\\%s since %s", users[i]['domain'], users[i]['name'], users[i]['changed_date']))
|
table.insert(logged_in, string.format("%s\\%s since %s", users[i]['domain'], users[i]['name'], users[i]['changed_date']))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(response, logged_in)
|
table.insert(response, logged_in)
|
||||||
|
|
||||||
-- Get the connected sessions
|
-- Get the connected sessions
|
||||||
local sessions_output = {}
|
local sessions_output = {}
|
||||||
local status2, sessions = srvsvc_enum_sessions(host)
|
local status2, sessions = srvsvc_enum_sessions(host)
|
||||||
if(status2 == false) then
|
if(status2 == false) then
|
||||||
sessions_output['warning'] = "Couldn't enumerate SMB sessions: " .. sessions
|
sessions_output['warning'] = "Couldn't enumerate SMB sessions: " .. sessions
|
||||||
else
|
else
|
||||||
sessions_output['name'] = "Active SMB sessions"
|
sessions_output['name'] = "Active SMB sessions"
|
||||||
if(#sessions == 0) then
|
if(#sessions == 0) then
|
||||||
table.insert(sessions_output, "<none>")
|
table.insert(sessions_output, "<none>")
|
||||||
else
|
else
|
||||||
-- Format the result
|
-- Format the result
|
||||||
for i = 1, #sessions, 1 do
|
for i = 1, #sessions, 1 do
|
||||||
local time = sessions[i]['time']
|
local time = sessions[i]['time']
|
||||||
if(time == 0) then
|
if(time == 0) then
|
||||||
time = "[just logged in, it's probably you]"
|
time = "[just logged in, it's probably you]"
|
||||||
elseif(time > 60 * 60 * 24) then
|
elseif(time > 60 * 60 * 24) then
|
||||||
time = string.format("%dd%dh%02dm%02ds", time / (60*60*24), (time % (60*60*24)) / 3600, (time % 3600) / 60, time % 60)
|
time = string.format("%dd%dh%02dm%02ds", time / (60*60*24), (time % (60*60*24)) / 3600, (time % 3600) / 60, time % 60)
|
||||||
elseif(time > 60 * 60) then
|
elseif(time > 60 * 60) then
|
||||||
time = string.format("%dh%02dm%02ds", time / 3600, (time % 3600) / 60, time % 60)
|
time = string.format("%dh%02dm%02ds", time / 3600, (time % 3600) / 60, time % 60)
|
||||||
else
|
else
|
||||||
time = string.format("%02dm%02ds", time / 60, time % 60)
|
time = string.format("%02dm%02ds", time / 60, time % 60)
|
||||||
end
|
end
|
||||||
|
|
||||||
local idle_time = sessions[i]['idle_time']
|
local idle_time = sessions[i]['idle_time']
|
||||||
if(idle_time == 0) then
|
if(idle_time == 0) then
|
||||||
idle_time = "[not idle]"
|
idle_time = "[not idle]"
|
||||||
elseif(idle_time > 60 * 60 * 24) then
|
elseif(idle_time > 60 * 60 * 24) then
|
||||||
idle_time = string.format("%dd%dh%02dm%02ds", idle_time / (60*60*24), (idle_time % (60*60*24)) / 3600, (idle_time % 3600) / 60, idle_time % 60)
|
idle_time = string.format("%dd%dh%02dm%02ds", idle_time / (60*60*24), (idle_time % (60*60*24)) / 3600, (idle_time % 3600) / 60, idle_time % 60)
|
||||||
elseif(idle_time > 60 * 60) then
|
elseif(idle_time > 60 * 60) then
|
||||||
idle_time = string.format("%dh%02dm%02ds", idle_time / 3600, (idle_time % 3600) / 60, idle_time % 60)
|
idle_time = string.format("%dh%02dm%02ds", idle_time / 3600, (idle_time % 3600) / 60, idle_time % 60)
|
||||||
else
|
else
|
||||||
idle_time = string.format("%02dm%02ds", idle_time / 60, idle_time % 60)
|
idle_time = string.format("%02dm%02ds", idle_time / 60, idle_time % 60)
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(sessions_output, string.format("%s is connected from %s for %s, idle for %s", sessions[i]['user'], sessions[i]['client'], time, idle_time))
|
table.insert(sessions_output, string.format("%s is connected from %s for %s, idle for %s", sessions[i]['user'], sessions[i]['client'], time, idle_time))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(response, sessions_output)
|
table.insert(response, sessions_output)
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -60,234 +60,234 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
|||||||
local communitiestable = {}
|
local communitiestable = {}
|
||||||
|
|
||||||
local filltable = function(filename, table)
|
local filltable = function(filename, table)
|
||||||
if #table ~= 0 then
|
if #table ~= 0 then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local file = io.open(filename, "r")
|
local file = io.open(filename, "r")
|
||||||
|
|
||||||
if not file then
|
if not file then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
for l in file:lines() do
|
for l in file:lines() do
|
||||||
-- Comments takes up a whole line
|
-- Comments takes up a whole line
|
||||||
if not l:match("#!comment:") then
|
if not l:match("#!comment:") then
|
||||||
table[#table + 1] = l
|
table[#table + 1] = l
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
file:close()
|
file:close()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local closure = function(table)
|
local closure = function(table)
|
||||||
local i = 1
|
local i = 1
|
||||||
|
|
||||||
return function(cmd)
|
return function(cmd)
|
||||||
if cmd == "reset" then
|
if cmd == "reset" then
|
||||||
i = 1
|
i = 1
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local elem = table[i]
|
local elem = table[i]
|
||||||
if elem then i = i + 1 end
|
if elem then i = i + 1 end
|
||||||
return elem
|
return elem
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local communities_raw = function(path)
|
local communities_raw = function(path)
|
||||||
if not path then
|
if not path then
|
||||||
return false, "Cannot find communities list"
|
return false, "Cannot find communities list"
|
||||||
end
|
end
|
||||||
|
|
||||||
if not filltable(path, communitiestable) then
|
if not filltable(path, communitiestable) then
|
||||||
return false, "Error parsing communities list"
|
return false, "Error parsing communities list"
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, closure(communitiestable)
|
return true, closure(communitiestable)
|
||||||
end
|
end
|
||||||
|
|
||||||
local communities = function()
|
local communities = function()
|
||||||
local communities_file = stdnse.get_script_args('snmp-brute.communitiesdb') or
|
local communities_file = stdnse.get_script_args('snmp-brute.communitiesdb') or
|
||||||
nmap.fetchfile("nselib/data/snmpcommunities.lst")
|
nmap.fetchfile("nselib/data/snmpcommunities.lst")
|
||||||
|
|
||||||
if communities_file then
|
if communities_file then
|
||||||
stdnse.print_debug(1, "%s: Using the %s as the communities file",
|
stdnse.print_debug(1, "%s: Using the %s as the communities file",
|
||||||
SCRIPT_NAME, communities_file)
|
SCRIPT_NAME, communities_file)
|
||||||
|
|
||||||
local status, iterator = communities_raw(communities_file)
|
local status, iterator = communities_raw(communities_file)
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return false, iterator
|
return false, iterator
|
||||||
end
|
end
|
||||||
|
|
||||||
local time_limit = unpwdb.timelimit()
|
local time_limit = unpwdb.timelimit()
|
||||||
local count_limit = 0
|
local count_limit = 0
|
||||||
|
|
||||||
if stdnse.get_script_args("unpwdb.passlimit") then
|
if stdnse.get_script_args("unpwdb.passlimit") then
|
||||||
count_limit = tonumber(stdnse.get_script_args("unpwdb.passlimit"))
|
count_limit = tonumber(stdnse.get_script_args("unpwdb.passlimit"))
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, unpwdb.limited_iterator(iterator, time_limit, count_limit)
|
return true, unpwdb.limited_iterator(iterator, time_limit, count_limit)
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "%s: Cannot read the communities file, using the nmap username/password database instead",
|
stdnse.print_debug(1, "%s: Cannot read the communities file, using the nmap username/password database instead",
|
||||||
SCRIPT_NAME)
|
SCRIPT_NAME)
|
||||||
|
|
||||||
return unpwdb.passwords()
|
return unpwdb.passwords()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local send_snmp_queries = function(socket, result, nextcommunity)
|
local send_snmp_queries = function(socket, result, nextcommunity)
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
|
|
||||||
local request = snmp.buildGetRequest({}, "1.3.6.1.2.1.1.3.0")
|
local request = snmp.buildGetRequest({}, "1.3.6.1.2.1.1.3.0")
|
||||||
|
|
||||||
local payload, status, response, err
|
local payload, status, response, err
|
||||||
local community = nextcommunity()
|
local community = nextcommunity()
|
||||||
|
|
||||||
while community do
|
while community do
|
||||||
if result.status == false then
|
if result.status == false then
|
||||||
--in case the sniff_snmp_responses thread was shut down
|
--in case the sniff_snmp_responses thread was shut down
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
payload = snmp.encode(snmp.buildPacket(request, 0, community))
|
payload = snmp.encode(snmp.buildPacket(request, 0, community))
|
||||||
status, err = socket:send(payload)
|
status, err = socket:send(payload)
|
||||||
if not status then
|
if not status then
|
||||||
result.status = false
|
result.status = false
|
||||||
result.msg = "Could not send SNMP probe"
|
result.msg = "Could not send SNMP probe"
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
community = nextcommunity()
|
community = nextcommunity()
|
||||||
end
|
end
|
||||||
|
|
||||||
result.sent = true
|
result.sent = true
|
||||||
condvar("signal")
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
local sniff_snmp_responses = function(host, port, lport, result)
|
local sniff_snmp_responses = function(host, port, lport, result)
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
|
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
pcap:set_timeout(host.times.timeout * 1000 * 3)
|
pcap:set_timeout(host.times.timeout * 1000 * 3)
|
||||||
local ip = host.bin_ip_src
|
local ip = host.bin_ip_src
|
||||||
ip = string.format("%d.%d.%d.%d",ip:byte(1),ip:byte(2),ip:byte(3),ip:byte(4))
|
ip = string.format("%d.%d.%d.%d",ip:byte(1),ip:byte(2),ip:byte(3),ip:byte(4))
|
||||||
pcap:pcap_open(host.interface, 104, false,"dst host " .. ip .. " and udp and src port 161 and dst port " .. lport)
|
pcap:pcap_open(host.interface, 104, false,"dst host " .. ip .. " and udp and src port 161 and dst port " .. lport)
|
||||||
|
|
||||||
-- last_run indicated whether there will be only one more receive
|
-- last_run indicated whether there will be only one more receive
|
||||||
local last_run = false
|
local last_run = false
|
||||||
|
|
||||||
-- receive even when status=false untill all the probes are sent
|
-- receive even when status=false untill all the probes are sent
|
||||||
while true do
|
while true do
|
||||||
local status, plen, l2, l3, _ = pcap:pcap_receive()
|
local status, plen, l2, l3, _ = pcap:pcap_receive()
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
local p = packet.Packet:new(l3,#l3)
|
local p = packet.Packet:new(l3,#l3)
|
||||||
if not p:udp_parse() then
|
if not p:udp_parse() then
|
||||||
--shouldn't happen
|
--shouldn't happen
|
||||||
result.status = false
|
result.status = false
|
||||||
result.msg = "Wrong type of packet received"
|
result.msg = "Wrong type of packet received"
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local response = p:raw(28, #p.buf)
|
local response = p:raw(28, #p.buf)
|
||||||
local res
|
local res
|
||||||
_, res = snmp.decode(response)
|
_, res = snmp.decode(response)
|
||||||
|
|
||||||
if type(res) == "table" then
|
if type(res) == "table" then
|
||||||
result.communities[ #(result.communities) + 1 ] = res[2]
|
result.communities[ #(result.communities) + 1 ] = res[2]
|
||||||
else
|
else
|
||||||
result.status = false
|
result.status = false
|
||||||
result.msg = "Wrong type of SNMP response received"
|
result.msg = "Wrong type of SNMP response received"
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if last_run then
|
if last_run then
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
if result.sent then
|
if result.sent then
|
||||||
last_run = true
|
last_run = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
pcap:close()
|
pcap:close()
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, nextcommunity = communities()
|
local status, nextcommunity = communities()
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return "\n ERROR: Failed to read the communities database"
|
return "\n ERROR: Failed to read the communities database"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
local threads = {}
|
local threads = {}
|
||||||
|
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
|
|
||||||
result.sent = false --whether the probes are sent
|
result.sent = false --whether the probes are sent
|
||||||
result.communities = {} -- list of valid community strings
|
result.communities = {} -- list of valid community strings
|
||||||
result.msg = "" -- Error/Status msg
|
result.msg = "" -- Error/Status msg
|
||||||
result.status = true -- Status (is everything ok)
|
result.status = true -- Status (is everything ok)
|
||||||
|
|
||||||
local socket = nmap.new_socket("udp")
|
local socket = nmap.new_socket("udp")
|
||||||
status = socket:connect(host, port)
|
status = socket:connect(host, port)
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "\n ERROR: Failed to connect to server"
|
return "\n ERROR: Failed to connect to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, _, lport = socket:get_info()
|
local status, _, lport = socket:get_info()
|
||||||
if( not(status) ) then
|
if( not(status) ) then
|
||||||
return "\n ERROR: Failed to retrieve local port"
|
return "\n ERROR: Failed to retrieve local port"
|
||||||
end
|
end
|
||||||
|
|
||||||
local recv_co = stdnse.new_thread(sniff_snmp_responses, host, port, lport, result)
|
local recv_co = stdnse.new_thread(sniff_snmp_responses, host, port, lport, result)
|
||||||
local send_co = stdnse.new_thread(send_snmp_queries, socket, result, nextcommunity)
|
local send_co = stdnse.new_thread(send_snmp_queries, socket, result, nextcommunity)
|
||||||
|
|
||||||
local recv_dead, send_dead
|
local recv_dead, send_dead
|
||||||
while true do
|
while true do
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
recv_dead = (coroutine.status(recv_co) == "dead")
|
recv_dead = (coroutine.status(recv_co) == "dead")
|
||||||
send_dead = (coroutine.status(send_co) == "dead")
|
send_dead = (coroutine.status(send_co) == "dead")
|
||||||
if recv_dead then break end
|
if recv_dead then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
if result.status then
|
if result.status then
|
||||||
-- add the community strings to the creds database
|
-- add the community strings to the creds database
|
||||||
local c = creds.Credentials:new(SCRIPT_NAME, host, port)
|
local c = creds.Credentials:new(SCRIPT_NAME, host, port)
|
||||||
for _, community_string in ipairs(result.communities) do
|
for _, community_string in ipairs(result.communities) do
|
||||||
c:add("",community_string, creds.State.VALID)
|
c:add("",community_string, creds.State.VALID)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- insert the first community string as a snmpcommunity registry field
|
-- insert the first community string as a snmpcommunity registry field
|
||||||
local creds_iter = c:getCredentials()
|
local creds_iter = c:getCredentials()
|
||||||
if creds_iter then
|
if creds_iter then
|
||||||
local account = creds_iter()
|
local account = creds_iter()
|
||||||
if account then
|
if account then
|
||||||
if account.pass == "<empty>" then
|
if account.pass == "<empty>" then
|
||||||
nmap.registry.snmpcommunity = ""
|
nmap.registry.snmpcommunity = ""
|
||||||
else
|
else
|
||||||
nmap.registry.snmpcommunity = account.pass
|
nmap.registry.snmpcommunity = account.pass
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- return output
|
-- return output
|
||||||
return tostring(c)
|
return tostring(c)
|
||||||
else
|
else
|
||||||
stdnse.print_debug("An error occured: "..result.msg)
|
stdnse.print_debug("An error occured: "..result.msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -51,14 +51,14 @@ dependencies = {"snmp-brute"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not stdnse.get_script_args({"snmp-interfaces.host", "host"}) then
|
if not stdnse.get_script_args({"snmp-interfaces.host", "host"}) then
|
||||||
stdnse.print_debug(3,
|
stdnse.print_debug(3,
|
||||||
"Skipping '%s' %s, 'snmp-interfaces.host' argument is missing.",
|
"Skipping '%s' %s, 'snmp-interfaces.host' argument is missing.",
|
||||||
SCRIPT_NAME, SCRIPT_TYPE)
|
SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||||
@@ -68,43 +68,43 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
|||||||
-- Available at http://www.iana.org/assignments/ianaiftype-mib
|
-- Available at http://www.iana.org/assignments/ianaiftype-mib
|
||||||
-- REVISION "201002110000Z"
|
-- REVISION "201002110000Z"
|
||||||
local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", "ethernetCsmacd",
|
local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", "ethernetCsmacd",
|
||||||
"iso88023Csmacd", "iso88024TokenBus", "iso88025TokenRing", "iso88026Man", "starLan",
|
"iso88023Csmacd", "iso88024TokenBus", "iso88025TokenRing", "iso88026Man", "starLan",
|
||||||
"proteon10Mbit", "proteon80Mbit", "hyperchannel", "fddi", "lapb", "sdlc", "ds1", "e1",
|
"proteon10Mbit", "proteon80Mbit", "hyperchannel", "fddi", "lapb", "sdlc", "ds1", "e1",
|
||||||
"basicISDN", "primaryISDN", "propPointToPointSerial", "ppp", "softwareLoopback", "eon",
|
"basicISDN", "primaryISDN", "propPointToPointSerial", "ppp", "softwareLoopback", "eon",
|
||||||
"ethernet3Mbit", "nsip", "slip", "ultra", "ds3", "sip", "frameRelay", "rs232", "para",
|
"ethernet3Mbit", "nsip", "slip", "ultra", "ds3", "sip", "frameRelay", "rs232", "para",
|
||||||
"arcnet", "arcnetPlus", "atm", "miox25", "sonet", "x25ple", "iso88022llc", "localTalk",
|
"arcnet", "arcnetPlus", "atm", "miox25", "sonet", "x25ple", "iso88022llc", "localTalk",
|
||||||
"smdsDxi", "frameRelayService", "v35", "hssi", "hippi", "modem", "aal5", "sonetPath",
|
"smdsDxi", "frameRelayService", "v35", "hssi", "hippi", "modem", "aal5", "sonetPath",
|
||||||
"sonetVT", "smdsIcip", "propVirtual", "propMultiplexor", "ieee80212", "fibreChannel",
|
"sonetVT", "smdsIcip", "propVirtual", "propMultiplexor", "ieee80212", "fibreChannel",
|
||||||
"hippiInterface", "frameRelayInterconnect", "aflane8023", "aflane8025", "cctEmul",
|
"hippiInterface", "frameRelayInterconnect", "aflane8023", "aflane8025", "cctEmul",
|
||||||
"fastEther", "isdn", "v11", "v36", "g703at64k", "g703at2mb", "qllc", "fastEtherFX",
|
"fastEther", "isdn", "v11", "v36", "g703at64k", "g703at2mb", "qllc", "fastEtherFX",
|
||||||
"channel", "ieee80211", "ibm370parChan", "escon", "dlsw", "isdns", "isdnu", "lapd",
|
"channel", "ieee80211", "ibm370parChan", "escon", "dlsw", "isdns", "isdnu", "lapd",
|
||||||
"ipSwitch", "rsrb", "atmLogical", "ds0", "ds0Bundle", "bsc", "async", "cnr",
|
"ipSwitch", "rsrb", "atmLogical", "ds0", "ds0Bundle", "bsc", "async", "cnr",
|
||||||
"iso88025Dtr", "eplrs", "arap", "propCnls", "hostPad", "termPad", "frameRelayMPI",
|
"iso88025Dtr", "eplrs", "arap", "propCnls", "hostPad", "termPad", "frameRelayMPI",
|
||||||
"x213", "adsl", "radsl", "sdsl", "vdsl", "iso88025CRFPInt", "myrinet", "voiceEM",
|
"x213", "adsl", "radsl", "sdsl", "vdsl", "iso88025CRFPInt", "myrinet", "voiceEM",
|
||||||
"voiceFXO", "voiceFXS", "voiceEncap", "voiceOverIp", "atmDxi", "atmFuni", "atmIma",
|
"voiceFXO", "voiceFXS", "voiceEncap", "voiceOverIp", "atmDxi", "atmFuni", "atmIma",
|
||||||
"pppMultilinkBundle", "ipOverCdlc", "ipOverClaw", "stackToStack", "virtualIpAddress",
|
"pppMultilinkBundle", "ipOverCdlc", "ipOverClaw", "stackToStack", "virtualIpAddress",
|
||||||
"mpc", "ipOverAtm", "iso88025Fiber", "tdlc", "gigabitEthernet", "hdlc", "lapf", "v37",
|
"mpc", "ipOverAtm", "iso88025Fiber", "tdlc", "gigabitEthernet", "hdlc", "lapf", "v37",
|
||||||
"x25mlp", "x25huntGroup", "trasnpHdlc", "interleave", "fast", "ip", "docsCableMaclayer",
|
"x25mlp", "x25huntGroup", "trasnpHdlc", "interleave", "fast", "ip", "docsCableMaclayer",
|
||||||
"docsCableDownstream", "docsCableUpstream", "a12MppSwitch", "tunnel", "coffee", "ces",
|
"docsCableDownstream", "docsCableUpstream", "a12MppSwitch", "tunnel", "coffee", "ces",
|
||||||
"atmSubInterface", "l2vlan", "l3ipvlan", "l3ipxvlan", "digitalPowerlinev", "mediaMailOverIp",
|
"atmSubInterface", "l2vlan", "l3ipvlan", "l3ipxvlan", "digitalPowerlinev", "mediaMailOverIp",
|
||||||
"dtm", "dcn", "ipForward", "msdsl", "ieee1394", "if-gsn", "dvbRccMacLayer", "dvbRccDownstream",
|
"dtm", "dcn", "ipForward", "msdsl", "ieee1394", "if-gsn", "dvbRccMacLayer", "dvbRccDownstream",
|
||||||
"dvbRccUpstream", "atmVirtual", "mplsTunnel", "srp", "voiceOverAtm", "voiceOverFrameRelay",
|
"dvbRccUpstream", "atmVirtual", "mplsTunnel", "srp", "voiceOverAtm", "voiceOverFrameRelay",
|
||||||
"idsl", "compositeLink", "ss7SigLink", "propWirelessP2P", "frForward", "rfc1483", "usb",
|
"idsl", "compositeLink", "ss7SigLink", "propWirelessP2P", "frForward", "rfc1483", "usb",
|
||||||
"ieee8023adLag", "bgppolicyaccounting", "frf16MfrBundle", "h323Gatekeeper", "h323Proxy",
|
"ieee8023adLag", "bgppolicyaccounting", "frf16MfrBundle", "h323Gatekeeper", "h323Proxy",
|
||||||
"mpls", "mfSigLink", "hdsl2", "shdsl", "ds1FDL", "pos", "dvbAsiIn", "dvbAsiOut", "plc",
|
"mpls", "mfSigLink", "hdsl2", "shdsl", "ds1FDL", "pos", "dvbAsiIn", "dvbAsiOut", "plc",
|
||||||
"nfas", "tr008", "gr303RDT", "gr303IDT", "isup", "propDocsWirelessMaclayer",
|
"nfas", "tr008", "gr303RDT", "gr303IDT", "isup", "propDocsWirelessMaclayer",
|
||||||
"propDocsWirelessDownstream", "propDocsWirelessUpstream", "hiperlan2", "propBWAp2Mp",
|
"propDocsWirelessDownstream", "propDocsWirelessUpstream", "hiperlan2", "propBWAp2Mp",
|
||||||
"sonetOverheadChannel", "digitalWrapperOverheadChannel", "aal2", "radioMAC", "atmRadio",
|
"sonetOverheadChannel", "digitalWrapperOverheadChannel", "aal2", "radioMAC", "atmRadio",
|
||||||
"imt", "mvl", "reachDSL", "frDlciEndPt", "atmVciEndPt", "opticalChannel", "opticalTransport",
|
"imt", "mvl", "reachDSL", "frDlciEndPt", "atmVciEndPt", "opticalChannel", "opticalTransport",
|
||||||
"propAtm", "voiceOverCable", "infiniband", "teLink", "q2931", "virtualTg", "sipTg", "sipSig",
|
"propAtm", "voiceOverCable", "infiniband", "teLink", "q2931", "virtualTg", "sipTg", "sipSig",
|
||||||
"docsCableUpstreamChannel", "econet", "pon155", "pon622", "bridge", "linegroup", "voiceEMFGD",
|
"docsCableUpstreamChannel", "econet", "pon155", "pon622", "bridge", "linegroup", "voiceEMFGD",
|
||||||
"voiceFGDEANA", "voiceDID", "mpegTransport", "sixToFour", "gtp", "pdnEtherLoop1",
|
"voiceFGDEANA", "voiceDID", "mpegTransport", "sixToFour", "gtp", "pdnEtherLoop1",
|
||||||
"pdnEtherLoop2", "opticalChannelGroup", "homepna", "gfp", "ciscoISLvlan", "actelisMetaLOOP",
|
"pdnEtherLoop2", "opticalChannelGroup", "homepna", "gfp", "ciscoISLvlan", "actelisMetaLOOP",
|
||||||
"fcipLink", "rpr", "qam", "lmp", "cblVectaStar", "docsCableMCmtsDownstream", "adsl2",
|
"fcipLink", "rpr", "qam", "lmp", "cblVectaStar", "docsCableMCmtsDownstream", "adsl2",
|
||||||
"macSecControlledIF", "macSecUncontrolledIF", "aviciOpticalEther", "atmbond", "voiceFGDOS",
|
"macSecControlledIF", "macSecUncontrolledIF", "aviciOpticalEther", "atmbond", "voiceFGDOS",
|
||||||
"mocaVersion1", "ieee80216WMAN", "adsl2plus", "dvbRcsMacLayer", "dvbTdm", "dvbRcsTdma",
|
"mocaVersion1", "ieee80216WMAN", "adsl2plus", "dvbRcsMacLayer", "dvbTdm", "dvbRcsTdma",
|
||||||
"x86Laps", "wwanPP", "wwanPP2", "voiceEBS", "ifPwType", "ilan", "pip", "aluELP", "gpon",
|
"x86Laps", "wwanPP", "wwanPP2", "voiceEBS", "ifPwType", "ilan", "pip", "aluELP", "gpon",
|
||||||
"vdsl2", "capwapDot11Profile", "capwapDot11Bss", "capwapWtpVirtualRadio" }
|
"vdsl2", "capwapDot11Profile", "capwapDot11Bss", "capwapWtpVirtualRadio" }
|
||||||
|
|
||||||
--- Gets a value for the specified oid
|
--- Gets a value for the specified oid
|
||||||
--
|
--
|
||||||
@@ -113,13 +113,13 @@ local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", "
|
|||||||
-- @return value of relevant type or nil if oid was not found
|
-- @return value of relevant type or nil if oid was not found
|
||||||
function get_value_from_table( tbl, oid )
|
function get_value_from_table( tbl, oid )
|
||||||
|
|
||||||
for _, v in ipairs( tbl ) do
|
for _, v in ipairs( tbl ) do
|
||||||
if v.oid == oid then
|
if v.oid == oid then
|
||||||
return v.value
|
return v.value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Gets the network interface type from a list of IANA approved types
|
--- Gets the network interface type from a list of IANA approved types
|
||||||
@@ -127,13 +127,13 @@ end
|
|||||||
-- @param iana integer interface type returned from snmp result
|
-- @param iana integer interface type returned from snmp result
|
||||||
-- @return string description of interface type, or "Unknown" if type not found
|
-- @return string description of interface type, or "Unknown" if type not found
|
||||||
function get_iana_type( iana )
|
function get_iana_type( iana )
|
||||||
-- 254 types are currently defined
|
-- 254 types are currently defined
|
||||||
-- if the requested type falls outside that range, reset to "other"
|
-- if the requested type falls outside that range, reset to "other"
|
||||||
if iana > 254 or iana < 1 then
|
if iana > 254 or iana < 1 then
|
||||||
iana = 1
|
iana = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
return iana_types[iana]
|
return iana_types[iana]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculates the speed of the interface based on the snmp value
|
--- Calculates the speed of the interface based on the snmp value
|
||||||
@@ -141,20 +141,20 @@ end
|
|||||||
-- @param speed value from IF-MIB::ifSpeed
|
-- @param speed value from IF-MIB::ifSpeed
|
||||||
-- @return string description of speed
|
-- @return string description of speed
|
||||||
function get_if_speed( speed )
|
function get_if_speed( speed )
|
||||||
local result
|
local result
|
||||||
|
|
||||||
-- GigE or 10GigE speeds
|
-- GigE or 10GigE speeds
|
||||||
if speed >= 1000000000 then
|
if speed >= 1000000000 then
|
||||||
result = string.format( "%d Gbps", speed / 1000000000)
|
result = string.format( "%d Gbps", speed / 1000000000)
|
||||||
-- Common for 10 or 100 Mbit ethernet
|
-- Common for 10 or 100 Mbit ethernet
|
||||||
elseif speed >= 1000000 then
|
elseif speed >= 1000000 then
|
||||||
result = string.format( "%d Mbps", speed / 1000000)
|
result = string.format( "%d Mbps", speed / 1000000)
|
||||||
-- Anything slower report in Kbps
|
-- Anything slower report in Kbps
|
||||||
else
|
else
|
||||||
result = string.format( "%d Kbps", speed / 1000)
|
result = string.format( "%d Kbps", speed / 1000)
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Calculates the amount of traffic passed through an interface based on the snmp value
|
--- Calculates the amount of traffic passed through an interface based on the snmp value
|
||||||
@@ -162,20 +162,20 @@ end
|
|||||||
-- @param amount value from IF-MIB::ifInOctets or IF-MIB::ifOutOctets
|
-- @param amount value from IF-MIB::ifInOctets or IF-MIB::ifOutOctets
|
||||||
-- @return string description of traffic amount
|
-- @return string description of traffic amount
|
||||||
function get_traffic( amount )
|
function get_traffic( amount )
|
||||||
local result
|
local result
|
||||||
|
|
||||||
-- Gigabytes
|
-- Gigabytes
|
||||||
if amount >= 1000000000 then
|
if amount >= 1000000000 then
|
||||||
result = string.format( "%.2f Gb", amount / 1000000000)
|
result = string.format( "%.2f Gb", amount / 1000000000)
|
||||||
-- Megabytes
|
-- Megabytes
|
||||||
elseif amount >= 1000000 then
|
elseif amount >= 1000000 then
|
||||||
result = string.format( "%.2f Mb", amount / 1000000)
|
result = string.format( "%.2f Mb", amount / 1000000)
|
||||||
-- Anything lower report in kb
|
-- Anything lower report in kb
|
||||||
else
|
else
|
||||||
result = string.format( "%.2f Kb", amount / 1000)
|
result = string.format( "%.2f Kb", amount / 1000)
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Converts a 6 byte string into the familiar MAC address formatting
|
--- Converts a 6 byte string into the familiar MAC address formatting
|
||||||
@@ -183,17 +183,17 @@ end
|
|||||||
-- @param mac string containing the MAC address
|
-- @param mac string containing the MAC address
|
||||||
-- @return formatted string suitable for printing
|
-- @return formatted string suitable for printing
|
||||||
function get_mac_addr( mac )
|
function get_mac_addr( mac )
|
||||||
local catch = function() return end
|
local catch = function() return end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
||||||
|
|
||||||
if mac:len() ~= 6 then
|
if mac:len() ~= 6 then
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
else
|
else
|
||||||
local prefix = string.upper(string.format("%02x%02x%02x", mac:byte(1), mac:byte(2), mac:byte(3)))
|
local prefix = string.upper(string.format("%02x%02x%02x", mac:byte(1), mac:byte(2), mac:byte(3)))
|
||||||
local manuf = mac_prefixes[prefix] or "Unknown"
|
local manuf = mac_prefixes[prefix] or "Unknown"
|
||||||
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
|
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Processes the list of network interfaces
|
--- Processes the list of network interfaces
|
||||||
@@ -202,87 +202,87 @@ end
|
|||||||
-- @return table with network interfaces described in key / value pairs
|
-- @return table with network interfaces described in key / value pairs
|
||||||
function process_interfaces( tbl )
|
function process_interfaces( tbl )
|
||||||
|
|
||||||
-- Add the %. escape character to prevent matching the index on e.g. "1.3.6.1.2.1.2.2.1.10."
|
-- Add the %. escape character to prevent matching the index on e.g. "1.3.6.1.2.1.2.2.1.10."
|
||||||
local if_index = "1.3.6.1.2.1.2.2.1.1%."
|
local if_index = "1.3.6.1.2.1.2.2.1.1%."
|
||||||
local if_descr = "1.3.6.1.2.1.2.2.1.2."
|
local if_descr = "1.3.6.1.2.1.2.2.1.2."
|
||||||
local if_type = "1.3.6.1.2.1.2.2.1.3."
|
local if_type = "1.3.6.1.2.1.2.2.1.3."
|
||||||
local if_speed = "1.3.6.1.2.1.2.2.1.5."
|
local if_speed = "1.3.6.1.2.1.2.2.1.5."
|
||||||
local if_phys_addr = "1.3.6.1.2.1.2.2.1.6."
|
local if_phys_addr = "1.3.6.1.2.1.2.2.1.6."
|
||||||
local if_status = "1.3.6.1.2.1.2.2.1.8."
|
local if_status = "1.3.6.1.2.1.2.2.1.8."
|
||||||
local if_in_octets = "1.3.6.1.2.1.2.2.1.10."
|
local if_in_octets = "1.3.6.1.2.1.2.2.1.10."
|
||||||
local if_out_octets = "1.3.6.1.2.1.2.2.1.16."
|
local if_out_octets = "1.3.6.1.2.1.2.2.1.16."
|
||||||
local new_tbl = {}
|
local new_tbl = {}
|
||||||
|
|
||||||
-- Some operating systems (such as MS Windows) don't list interfaces with consecutive indexes
|
-- Some operating systems (such as MS Windows) don't list interfaces with consecutive indexes
|
||||||
-- Therefore we keep an index list so we can iterate over the indexes later on
|
-- Therefore we keep an index list so we can iterate over the indexes later on
|
||||||
new_tbl.index_list = {}
|
new_tbl.index_list = {}
|
||||||
|
|
||||||
for _, v in ipairs( tbl ) do
|
for _, v in ipairs( tbl ) do
|
||||||
|
|
||||||
if ( v.oid:match("^" .. if_index) ) then
|
if ( v.oid:match("^" .. if_index) ) then
|
||||||
local item = {}
|
local item = {}
|
||||||
item.index = get_value_from_table( tbl, v.oid )
|
item.index = get_value_from_table( tbl, v.oid )
|
||||||
|
|
||||||
local objid = v.oid:gsub( "^" .. if_index, if_descr)
|
local objid = v.oid:gsub( "^" .. if_index, if_descr)
|
||||||
local value = get_value_from_table( tbl, objid )
|
local value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value and value:len() > 0 then
|
if value and value:len() > 0 then
|
||||||
item.descr = value
|
item.descr = value
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. if_index, if_type )
|
objid = v.oid:gsub( "^" .. if_index, if_type )
|
||||||
value = get_value_from_table( tbl, objid )
|
value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
item.type = get_iana_type(value)
|
item.type = get_iana_type(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. if_index, if_speed )
|
objid = v.oid:gsub( "^" .. if_index, if_speed )
|
||||||
value = get_value_from_table( tbl, objid )
|
value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
item.speed = get_if_speed( value )
|
item.speed = get_if_speed( value )
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. if_index, if_phys_addr )
|
objid = v.oid:gsub( "^" .. if_index, if_phys_addr )
|
||||||
value = get_value_from_table( tbl, objid )
|
value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value and value:len() > 0 then
|
if value and value:len() > 0 then
|
||||||
item.phys_addr = get_mac_addr( value )
|
item.phys_addr = get_mac_addr( value )
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. if_index, if_status )
|
objid = v.oid:gsub( "^" .. if_index, if_status )
|
||||||
value = get_value_from_table( tbl, objid )
|
value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value == 1 then
|
if value == 1 then
|
||||||
item.status = "up"
|
item.status = "up"
|
||||||
elseif value == 2 then
|
elseif value == 2 then
|
||||||
item.status = "down"
|
item.status = "down"
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. if_index, if_in_octets )
|
objid = v.oid:gsub( "^" .. if_index, if_in_octets )
|
||||||
value = get_value_from_table( tbl, objid )
|
value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
item.received = get_traffic( value )
|
item.received = get_traffic( value )
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. if_index, if_out_octets )
|
objid = v.oid:gsub( "^" .. if_index, if_out_octets )
|
||||||
value = get_value_from_table( tbl, objid )
|
value = get_value_from_table( tbl, objid )
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
item.sent = get_traffic( value )
|
item.sent = get_traffic( value )
|
||||||
end
|
end
|
||||||
|
|
||||||
new_tbl[item.index] = item
|
new_tbl[item.index] = item
|
||||||
-- Add this interface index to our master list
|
-- Add this interface index to our master list
|
||||||
table.insert( new_tbl.index_list, item.index )
|
table.insert( new_tbl.index_list, item.index )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_tbl
|
return new_tbl
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -292,34 +292,34 @@ end
|
|||||||
-- @param ip_tbl table containing <code>oid</code> and <code>value</code> pairs from IP::MIB
|
-- @param ip_tbl table containing <code>oid</code> and <code>value</code> pairs from IP::MIB
|
||||||
-- @return table with network interfaces described in key / value pairs
|
-- @return table with network interfaces described in key / value pairs
|
||||||
function process_ips( if_tbl, ip_tbl )
|
function process_ips( if_tbl, ip_tbl )
|
||||||
local ip_index = "1.3.6.1.2.1.4.20.1.2."
|
local ip_index = "1.3.6.1.2.1.4.20.1.2."
|
||||||
local ip_addr = "1.3.6.1.2.1.4.20.1.1."
|
local ip_addr = "1.3.6.1.2.1.4.20.1.1."
|
||||||
local ip_netmask = "1.3.6.1.2.1.4.20.1.3."
|
local ip_netmask = "1.3.6.1.2.1.4.20.1.3."
|
||||||
local index
|
local index
|
||||||
local item
|
local item
|
||||||
|
|
||||||
for _, v in ipairs( ip_tbl ) do
|
for _, v in ipairs( ip_tbl ) do
|
||||||
if ( v.oid:match("^" .. ip_index) ) then
|
if ( v.oid:match("^" .. ip_index) ) then
|
||||||
index = get_value_from_table( ip_tbl, v.oid )
|
index = get_value_from_table( ip_tbl, v.oid )
|
||||||
item = if_tbl[index]
|
item = if_tbl[index]
|
||||||
|
|
||||||
local objid = v.oid:gsub( "^" .. ip_index, ip_addr )
|
local objid = v.oid:gsub( "^" .. ip_index, ip_addr )
|
||||||
local value = get_value_from_table( ip_tbl, objid )
|
local value = get_value_from_table( ip_tbl, objid )
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
item.ip_addr = value
|
item.ip_addr = value
|
||||||
end
|
end
|
||||||
|
|
||||||
objid = v.oid:gsub( "^" .. ip_index, ip_netmask )
|
objid = v.oid:gsub( "^" .. ip_index, ip_netmask )
|
||||||
value = get_value_from_table( ip_tbl, objid )
|
value = get_value_from_table( ip_tbl, objid )
|
||||||
|
|
||||||
if value then
|
if value then
|
||||||
item.netmask = value
|
item.netmask = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return if_tbl
|
return if_tbl
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates a table of IP addresses from the table of network interfaces
|
--- Creates a table of IP addresses from the table of network interfaces
|
||||||
@@ -327,16 +327,16 @@ end
|
|||||||
-- @param tbl table containing network interfaces
|
-- @param tbl table containing network interfaces
|
||||||
-- @return table containing only IP addresses
|
-- @return table containing only IP addresses
|
||||||
function list_addrs( tbl )
|
function list_addrs( tbl )
|
||||||
local new_tbl = {}
|
local new_tbl = {}
|
||||||
|
|
||||||
for _, index in ipairs( tbl.index_list ) do
|
for _, index in ipairs( tbl.index_list ) do
|
||||||
local interface = tbl[index]
|
local interface = tbl[index]
|
||||||
if interface.ip_addr then
|
if interface.ip_addr then
|
||||||
table.insert( new_tbl, interface.ip_addr )
|
table.insert( new_tbl, interface.ip_addr )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_tbl
|
return new_tbl
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Process the table of network interfaces for reporting
|
--- Process the table of network interfaces for reporting
|
||||||
@@ -344,132 +344,132 @@ end
|
|||||||
-- @param tbl table containing network interfaces
|
-- @param tbl table containing network interfaces
|
||||||
-- @return table suitable for <code>stdnse.format_output</code>
|
-- @return table suitable for <code>stdnse.format_output</code>
|
||||||
function build_results( tbl )
|
function build_results( tbl )
|
||||||
local new_tbl = {}
|
local new_tbl = {}
|
||||||
local verbose = nmap.verbosity()
|
local verbose = nmap.verbosity()
|
||||||
|
|
||||||
-- For each interface index previously discovered, format the relevant information for output
|
-- For each interface index previously discovered, format the relevant information for output
|
||||||
for _, index in ipairs( tbl.index_list ) do
|
for _, index in ipairs( tbl.index_list ) do
|
||||||
local interface = tbl[index]
|
local interface = tbl[index]
|
||||||
local item = {}
|
local item = {}
|
||||||
local status = interface.status
|
local status = interface.status
|
||||||
local if_type = interface.type
|
local if_type = interface.type
|
||||||
|
|
||||||
if interface.descr then
|
if interface.descr then
|
||||||
item.name = interface.descr
|
item.name = interface.descr
|
||||||
else
|
else
|
||||||
item.name = string.format("Interface %d", index)
|
item.name = string.format("Interface %d", index)
|
||||||
end
|
end
|
||||||
|
|
||||||
if interface.ip_addr and interface.netmask then
|
if interface.ip_addr and interface.netmask then
|
||||||
table.insert( item, ("IP address: %s Netmask: %s"):format( interface.ip_addr, interface.netmask ) )
|
table.insert( item, ("IP address: %s Netmask: %s"):format( interface.ip_addr, interface.netmask ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
if interface.phys_addr then
|
if interface.phys_addr then
|
||||||
table.insert( item, ("MAC address: %s"):format( interface.phys_addr ) )
|
table.insert( item, ("MAC address: %s"):format( interface.phys_addr ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
if interface.type and interface.speed then
|
if interface.type and interface.speed then
|
||||||
table.insert( item, ("Type: %s Speed: %s"):format( interface.type, interface.speed ) )
|
table.insert( item, ("Type: %s Speed: %s"):format( interface.type, interface.speed ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( verbose > 0 ) and interface.status then
|
if ( verbose > 0 ) and interface.status then
|
||||||
table.insert( item, ("Status: %s"):format( interface.status ) )
|
table.insert( item, ("Status: %s"):format( interface.status ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
if interface.sent and interface.received then
|
if interface.sent and interface.received then
|
||||||
table.insert( item, ("Traffic stats: %s sent, %s received"):format( interface.sent, interface.received ) )
|
table.insert( item, ("Traffic stats: %s sent, %s received"):format( interface.sent, interface.received ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( verbose > 0 ) or status == "up" then
|
if ( verbose > 0 ) or status == "up" then
|
||||||
table.insert( new_tbl, item )
|
table.insert( new_tbl, item )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_tbl
|
return new_tbl
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local catch = function() socket:close() end
|
local catch = function() socket:close() end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
-- IF-MIB - used to look up network interfaces
|
-- IF-MIB - used to look up network interfaces
|
||||||
local if_oid = "1.3.6.1.2.1.2.2.1"
|
local if_oid = "1.3.6.1.2.1.2.2.1"
|
||||||
-- IP-MIB - used to determine IP address information
|
-- IP-MIB - used to determine IP address information
|
||||||
local ip_oid = "1.3.6.1.2.1.4.20"
|
local ip_oid = "1.3.6.1.2.1.4.20"
|
||||||
local interfaces = {}
|
local interfaces = {}
|
||||||
local ips = {}
|
local ips = {}
|
||||||
local status
|
local status
|
||||||
local srvhost, srvport
|
local srvhost, srvport
|
||||||
|
|
||||||
if SCRIPT_TYPE == "prerule" then
|
if SCRIPT_TYPE == "prerule" then
|
||||||
srvhost = stdnse.get_script_args({"snmp-interfaces.host", "host"})
|
srvhost = stdnse.get_script_args({"snmp-interfaces.host", "host"})
|
||||||
if not srvhost then
|
if not srvhost then
|
||||||
-- Shouldn't happen; checked in prerule.
|
-- Shouldn't happen; checked in prerule.
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
srvport = stdnse.get_script_args({"snmp-interfaces.port", "port"})
|
srvport = stdnse.get_script_args({"snmp-interfaces.port", "port"})
|
||||||
if srvport then
|
if srvport then
|
||||||
srvport = tonumber(srvport)
|
srvport = tonumber(srvport)
|
||||||
else
|
else
|
||||||
srvport = 161
|
srvport = 161
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
srvhost = host.ip
|
srvhost = host.ip
|
||||||
srvport = port.number
|
srvport = port.number
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
try(socket:connect(srvhost, srvport, "udp"))
|
try(socket:connect(srvhost, srvport, "udp"))
|
||||||
|
|
||||||
-- retreive network interface information from IF-MIB
|
-- retreive network interface information from IF-MIB
|
||||||
status, interfaces = snmp.snmpWalk( socket, if_oid )
|
status, interfaces = snmp.snmpWalk( socket, if_oid )
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
if (not(status)) or ( interfaces == nil ) or ( #interfaces == 0 ) then
|
if (not(status)) or ( interfaces == nil ) or ( #interfaces == 0 ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug("SNMP walk of IF-MIB returned %d lines", #interfaces)
|
stdnse.print_debug("SNMP walk of IF-MIB returned %d lines", #interfaces)
|
||||||
|
|
||||||
-- build a table of network interfaces from the IF-MIB table
|
-- build a table of network interfaces from the IF-MIB table
|
||||||
interfaces = process_interfaces( interfaces )
|
interfaces = process_interfaces( interfaces )
|
||||||
|
|
||||||
-- retreive IP address information from IP-MIB
|
-- retreive IP address information from IP-MIB
|
||||||
try(socket:connect(srvhost, srvport, "udp"))
|
try(socket:connect(srvhost, srvport, "udp"))
|
||||||
status, ips = snmp.snmpWalk( socket, ip_oid )
|
status, ips = snmp.snmpWalk( socket, ip_oid )
|
||||||
|
|
||||||
-- associate that IP address information with the correct interface
|
-- associate that IP address information with the correct interface
|
||||||
if (not(status)) or ( ips ~= nil ) and ( #ips ~= 0 ) then
|
if (not(status)) or ( ips ~= nil ) and ( #ips ~= 0 ) then
|
||||||
interfaces = process_ips( interfaces, ips )
|
interfaces = process_ips( interfaces, ips )
|
||||||
end
|
end
|
||||||
|
|
||||||
local output = stdnse.format_output( true, build_results(interfaces) )
|
local output = stdnse.format_output( true, build_results(interfaces) )
|
||||||
|
|
||||||
if SCRIPT_TYPE == "prerule" and target.ALLOW_NEW_TARGETS then
|
if SCRIPT_TYPE == "prerule" and target.ALLOW_NEW_TARGETS then
|
||||||
local sum = 0
|
local sum = 0
|
||||||
|
|
||||||
ips = list_addrs(interfaces)
|
ips = list_addrs(interfaces)
|
||||||
|
|
||||||
-- Could add all of the addresses at once, but count
|
-- Could add all of the addresses at once, but count
|
||||||
-- successful additions instead for script output
|
-- successful additions instead for script output
|
||||||
for _, i in ipairs(ips) do
|
for _, i in ipairs(ips) do
|
||||||
local st, err = target.add(i)
|
local st, err = target.add(i)
|
||||||
if st then
|
if st then
|
||||||
sum = sum + 1
|
sum = sum + 1
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Couldn't add target " .. i .. ": " .. err)
|
stdnse.print_debug("Couldn't add target " .. i .. ": " .. err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if sum ~= 0 then
|
if sum ~= 0 then
|
||||||
output = output .. "\nSuccessfully added " .. tostring(sum) .. " new targets"
|
output = output .. "\nSuccessfully added " .. tostring(sum) .. " new targets"
|
||||||
end
|
end
|
||||||
elseif SCRIPT_TYPE == "portrule" then
|
elseif SCRIPT_TYPE == "portrule" then
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
end
|
end
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ local pcreptn = {} -- cache of compiled PCRE patterns
|
|||||||
-- @param fmt Format string.
|
-- @param fmt Format string.
|
||||||
-- @param ... Arguments to format.
|
-- @param ... Arguments to format.
|
||||||
local print_debug = function (level, fmt, ...)
|
local print_debug = function (level, fmt, ...)
|
||||||
stdnse.print_debug(level, "%s: " .. fmt, SCRIPT_NAME, ...)
|
stdnse.print_debug(level, "%s: " .. fmt, SCRIPT_NAME, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -73,10 +73,10 @@ end
|
|||||||
-- @param str The string to analyze
|
-- @param str The string to analyze
|
||||||
-- @return Verdict (true or false)
|
-- @return Verdict (true or false)
|
||||||
local is_username_prompt = function (str)
|
local is_username_prompt = function (str)
|
||||||
pcreptn.username_prompt = pcreptn.username_prompt
|
pcreptn.username_prompt = pcreptn.username_prompt
|
||||||
or pcre.new("\\b(?:username|login)\\s*:\\s*$",
|
or pcre.new("\\b(?:username|login)\\s*:\\s*$",
|
||||||
pcre.flags().CASELESS, "C")
|
pcre.flags().CASELESS, "C")
|
||||||
return pcreptn.username_prompt:match(str)
|
return pcreptn.username_prompt:match(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -87,10 +87,10 @@ end
|
|||||||
-- @param str The string to analyze
|
-- @param str The string to analyze
|
||||||
-- @return Verdict (true or false)
|
-- @return Verdict (true or false)
|
||||||
local is_password_prompt = function (str)
|
local is_password_prompt = function (str)
|
||||||
pcreptn.password_prompt = pcreptn.password_prompt
|
pcreptn.password_prompt = pcreptn.password_prompt
|
||||||
or pcre.new("\\bpass(?:word|code)\\s*:\\s*$",
|
or pcre.new("\\bpass(?:word|code)\\s*:\\s*$",
|
||||||
pcre.flags().CASELESS, "C")
|
pcre.flags().CASELESS, "C")
|
||||||
return pcreptn.password_prompt:match(str)
|
return pcreptn.password_prompt:match(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -101,14 +101,14 @@ end
|
|||||||
-- @param str The string to analyze
|
-- @param str The string to analyze
|
||||||
-- @return Verdict (true or false)
|
-- @return Verdict (true or false)
|
||||||
local is_login_success = function (str)
|
local is_login_success = function (str)
|
||||||
pcreptn.login_success = pcreptn.login_success
|
pcreptn.login_success = pcreptn.login_success
|
||||||
or pcre.new("[/>%$#]\\s*$" -- general prompt
|
or pcre.new("[/>%$#]\\s*$" -- general prompt
|
||||||
.. "|^Last login\\s*:" -- linux telnetd
|
.. "|^Last login\\s*:" -- linux telnetd
|
||||||
.. "|^(?-i:[A-Z]):\\\\" -- Windows telnet
|
.. "|^(?-i:[A-Z]):\\\\" -- Windows telnet
|
||||||
.. "|Main(?:\\s|\\x1B\\[\\d+;\\d+H)Menu\\b" -- Netgear RM356
|
.. "|Main(?:\\s|\\x1B\\[\\d+;\\d+H)Menu\\b" -- Netgear RM356
|
||||||
.. "|^Enter Terminal Emulation:\\s*$", -- Hummingbird telnetd
|
.. "|^Enter Terminal Emulation:\\s*$", -- Hummingbird telnetd
|
||||||
pcre.flags().CASELESS, "C")
|
pcre.flags().CASELESS, "C")
|
||||||
return pcreptn.login_success:match(str)
|
return pcreptn.login_success:match(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -119,10 +119,10 @@ end
|
|||||||
-- @param str The string to analyze
|
-- @param str The string to analyze
|
||||||
-- @return Verdict (true or false)
|
-- @return Verdict (true or false)
|
||||||
local is_login_failure = function (str)
|
local is_login_failure = function (str)
|
||||||
pcreptn.login_failure = pcreptn.login_failure
|
pcreptn.login_failure = pcreptn.login_failure
|
||||||
or pcre.new("\\b(?:incorrect|failed|denied|invalid|bad)\\b",
|
or pcre.new("\\b(?:incorrect|failed|denied|invalid|bad)\\b",
|
||||||
pcre.flags().CASELESS, "C")
|
pcre.flags().CASELESS, "C")
|
||||||
return pcreptn.login_failure:match(str)
|
return pcreptn.login_failure:match(str)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -138,21 +138,21 @@ local Connection = { methods = {} }
|
|||||||
-- @param port Telnet port
|
-- @param port Telnet port
|
||||||
-- @return Connection object or nil (if the operation failed)
|
-- @return Connection object or nil (if the operation failed)
|
||||||
Connection.new = function (host, port, proto)
|
Connection.new = function (host, port, proto)
|
||||||
local soc = nmap.new_socket(proto)
|
local soc = nmap.new_socket(proto)
|
||||||
if not soc then return nil end
|
if not soc then return nil end
|
||||||
return setmetatable( {
|
return setmetatable( {
|
||||||
socket = soc,
|
socket = soc,
|
||||||
isopen = false,
|
isopen = false,
|
||||||
buffer = nil,
|
buffer = nil,
|
||||||
error = nil,
|
error = nil,
|
||||||
host = host,
|
host = host,
|
||||||
port = port,
|
port = port,
|
||||||
proto = proto
|
proto = proto
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__index = Connection.methods,
|
__index = Connection.methods,
|
||||||
__gc = Connection.methods.close
|
__gc = Connection.methods.close
|
||||||
} )
|
} )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -163,21 +163,21 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Connection.methods.connect = function (self)
|
Connection.methods.connect = function (self)
|
||||||
local status
|
local status
|
||||||
local wait = 1
|
local wait = 1
|
||||||
|
|
||||||
self.buffer = ""
|
self.buffer = ""
|
||||||
|
|
||||||
for tries = 0, conn_retries do
|
for tries = 0, conn_retries do
|
||||||
self.socket:set_timeout(telnet_timeout)
|
self.socket:set_timeout(telnet_timeout)
|
||||||
status, self.error = self.socket:connect(self.host, self.port, self.proto)
|
status, self.error = self.socket:connect(self.host, self.port, self.proto)
|
||||||
if status then break end
|
if status then break end
|
||||||
stdnse.sleep(wait)
|
stdnse.sleep(wait)
|
||||||
wait = 2 * wait
|
wait = 2 * wait
|
||||||
end
|
end
|
||||||
|
|
||||||
self.isopen = status
|
self.isopen = status
|
||||||
return status, self.error
|
return status, self.error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -188,12 +188,12 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Connection.methods.close = function (self)
|
Connection.methods.close = function (self)
|
||||||
if not self.isopen then return true, nil end
|
if not self.isopen then return true, nil end
|
||||||
local status
|
local status
|
||||||
self.isopen = false
|
self.isopen = false
|
||||||
self.buffer = nil
|
self.buffer = nil
|
||||||
status, self.error = self.socket:close()
|
status, self.error = self.socket:close()
|
||||||
return status, self.error
|
return status, self.error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -205,9 +205,9 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Connection.methods.send_line = function (self, line)
|
Connection.methods.send_line = function (self, line)
|
||||||
local status
|
local status
|
||||||
status, self.error = self.socket:send(line .. telnet_eol)
|
status, self.error = self.socket:send(line .. telnet_eol)
|
||||||
return status, self.error
|
return status, self.error
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -219,40 +219,40 @@ end
|
|||||||
-- @param data Data string to add to the buffer
|
-- @param data Data string to add to the buffer
|
||||||
-- @return Number of characters in the connection buffer
|
-- @return Number of characters in the connection buffer
|
||||||
Connection.methods.fill_buffer = function (self, data)
|
Connection.methods.fill_buffer = function (self, data)
|
||||||
local outbuf = strbuf.new(self.buffer)
|
local outbuf = strbuf.new(self.buffer)
|
||||||
local optbuf = strbuf.new()
|
local optbuf = strbuf.new()
|
||||||
local oldpos = 0
|
local oldpos = 0
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
-- look for IAC (Interpret As Command)
|
-- look for IAC (Interpret As Command)
|
||||||
local newpos = data:find('\255', oldpos)
|
local newpos = data:find('\255', oldpos)
|
||||||
if not newpos then break end
|
if not newpos then break end
|
||||||
|
|
||||||
outbuf = outbuf .. data:sub(oldpos, newpos - 1)
|
outbuf = outbuf .. data:sub(oldpos, newpos - 1)
|
||||||
local opttype = data:byte(newpos + 1)
|
local opttype = data:byte(newpos + 1)
|
||||||
local opt = data:byte(newpos + 2)
|
local opt = data:byte(newpos + 2)
|
||||||
|
|
||||||
if opttype == 251 or opttype == 252 then
|
if opttype == 251 or opttype == 252 then
|
||||||
-- Telnet Will / Will Not
|
-- Telnet Will / Will Not
|
||||||
-- regarding ECHO or GO-AHEAD, agree with whatever the
|
-- regarding ECHO or GO-AHEAD, agree with whatever the
|
||||||
-- server wants (or not) to do; otherwise respond with
|
-- server wants (or not) to do; otherwise respond with
|
||||||
-- "don't"
|
-- "don't"
|
||||||
opttype = (opt == 1 or opt == 3) and opttype + 2 or 254
|
opttype = (opt == 1 or opt == 3) and opttype + 2 or 254
|
||||||
elseif opttype == 253 or opttype == 254 then
|
elseif opttype == 253 or opttype == 254 then
|
||||||
-- Telnet Do / Do not
|
-- Telnet Do / Do not
|
||||||
-- I will not do whatever the server wants me to
|
-- I will not do whatever the server wants me to
|
||||||
opttype = 252
|
opttype = 252
|
||||||
end
|
end
|
||||||
|
|
||||||
optbuf = optbuf .. string.char(255)
|
optbuf = optbuf .. string.char(255)
|
||||||
.. string.char(opttype)
|
.. string.char(opttype)
|
||||||
.. string.char(opt)
|
.. string.char(opt)
|
||||||
oldpos = newpos + 3
|
oldpos = newpos + 3
|
||||||
end
|
end
|
||||||
|
|
||||||
self.buffer = strbuf.dump(outbuf) .. data:sub(oldpos)
|
self.buffer = strbuf.dump(outbuf) .. data:sub(oldpos)
|
||||||
self.socket:send(strbuf.dump(optbuf))
|
self.socket:send(strbuf.dump(optbuf))
|
||||||
return self.buffer:len()
|
return self.buffer:len()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -264,18 +264,18 @@ end
|
|||||||
-- @param normalize whether the returned line is normalized (default: false)
|
-- @param normalize whether the returned line is normalized (default: false)
|
||||||
-- @return String representing the first line in the buffer
|
-- @return String representing the first line in the buffer
|
||||||
Connection.methods.get_line = function (self)
|
Connection.methods.get_line = function (self)
|
||||||
if self.buffer:len() == 0 then
|
if self.buffer:len() == 0 then
|
||||||
-- refill the buffer
|
-- refill the buffer
|
||||||
local status, data = self.socket:receive_buf("[\r\n:>%%%$#\255].*", true)
|
local status, data = self.socket:receive_buf("[\r\n:>%%%$#\255].*", true)
|
||||||
if not status then
|
if not status then
|
||||||
-- connection error
|
-- connection error
|
||||||
self.error = data
|
self.error = data
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
self:fill_buffer(data)
|
self:fill_buffer(data)
|
||||||
end
|
end
|
||||||
return self.buffer:match('^[^\r\n]*')
|
return self.buffer:match('^[^\r\n]*')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -286,8 +286,8 @@ end
|
|||||||
-- @param self Connection object
|
-- @param self Connection object
|
||||||
-- @return Number of characters remaining in the connection buffer
|
-- @return Number of characters remaining in the connection buffer
|
||||||
Connection.methods.discard_line = function (self)
|
Connection.methods.discard_line = function (self)
|
||||||
self.buffer = self.buffer:gsub('^[^\r\n]*[\r\n]*', '', 1)
|
self.buffer = self.buffer:gsub('^[^\r\n]*[\r\n]*', '', 1)
|
||||||
return self.buffer:len()
|
return self.buffer:len()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -309,16 +309,16 @@ local Target = { methods = {} }
|
|||||||
-- @param port Telnet port
|
-- @param port Telnet port
|
||||||
-- @return Target object or nil (if the operation failed)
|
-- @return Target object or nil (if the operation failed)
|
||||||
Target.new = function (host, port)
|
Target.new = function (host, port)
|
||||||
local soc, _, proto = comm.tryssl(host, port, "\n", {timeout=telnet_timeout})
|
local soc, _, proto = comm.tryssl(host, port, "\n", {timeout=telnet_timeout})
|
||||||
if not soc then return nil end
|
if not soc then return nil end
|
||||||
soc:close()
|
soc:close()
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
host = host,
|
host = host,
|
||||||
port = port,
|
port = port,
|
||||||
proto = proto,
|
proto = proto,
|
||||||
workers = setmetatable({}, { __mode = "k" })
|
workers = setmetatable({}, { __mode = "k" })
|
||||||
},
|
},
|
||||||
{ __index = Target.methods } )
|
{ __index = Target.methods } )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -327,8 +327,8 @@ end
|
|||||||
--
|
--
|
||||||
-- @param self Target object
|
-- @param self Target object
|
||||||
Target.methods.worker = function (self)
|
Target.methods.worker = function (self)
|
||||||
local thread = coroutine.running()
|
local thread = coroutine.running()
|
||||||
self.workers[thread] = self.workers[thread] or {}
|
self.workers[thread] = self.workers[thread] or {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -340,19 +340,19 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return Connection if the operation was successful; error code otherwise
|
-- @return Connection if the operation was successful; error code otherwise
|
||||||
Target.methods.attach = function (self)
|
Target.methods.attach = function (self)
|
||||||
local worker = self.workers[coroutine.running()]
|
local worker = self.workers[coroutine.running()]
|
||||||
local conn = worker.conn
|
local conn = worker.conn
|
||||||
or Connection.new(self.host, self.port, self.proto)
|
or Connection.new(self.host, self.port, self.proto)
|
||||||
if not conn then return false, "Unable to allocate connection" end
|
if not conn then return false, "Unable to allocate connection" end
|
||||||
worker.conn = conn
|
worker.conn = conn
|
||||||
|
|
||||||
if conn.error then conn:close() end
|
if conn.error then conn:close() end
|
||||||
if not conn.isopen then
|
if not conn.isopen then
|
||||||
local status, err = conn:connect()
|
local status, err = conn:connect()
|
||||||
if not status then return false, err end
|
if not status then return false, err end
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, conn
|
return true, conn
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -363,10 +363,10 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Target.methods.detach = function (self)
|
Target.methods.detach = function (self)
|
||||||
local conn = self.workers[coroutine.running()].conn
|
local conn = self.workers[coroutine.running()].conn
|
||||||
local status, response = true, nil
|
local status, response = true, nil
|
||||||
if conn and conn.error then status, response = conn:close() end
|
if conn and conn.error then status, response = conn:close() end
|
||||||
return status, response
|
return status, response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -377,8 +377,8 @@ end
|
|||||||
-- @param inuse Whether the worker is in use (true or false)
|
-- @param inuse Whether the worker is in use (true or false)
|
||||||
-- @return inuse
|
-- @return inuse
|
||||||
Target.methods.inuse = function (self, inuse)
|
Target.methods.inuse = function (self, inuse)
|
||||||
self.workers[coroutine.running()].inuse = inuse
|
self.workers[coroutine.running()].inuse = inuse
|
||||||
return inuse
|
return inuse
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -388,11 +388,11 @@ end
|
|||||||
-- @param self Target object
|
-- @param self Target object
|
||||||
-- @return Verdict (true or false)
|
-- @return Verdict (true or false)
|
||||||
Target.methods.idle = function (self)
|
Target.methods.idle = function (self)
|
||||||
local idle = true
|
local idle = true
|
||||||
for t, w in pairs(self.workers) do
|
for t, w in pairs(self.workers) do
|
||||||
idle = idle and (not w.inuse or coroutine.status(t) == "dead")
|
idle = idle and (not w.inuse or coroutine.status(t) == "dead")
|
||||||
end
|
end
|
||||||
return idle
|
return idle
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -409,16 +409,16 @@ local Driver = { methods = {} }
|
|||||||
-- @param target instance of a Target class
|
-- @param target instance of a Target class
|
||||||
-- @return Driver object or nil (if the operation failed)
|
-- @return Driver object or nil (if the operation failed)
|
||||||
Driver.new = function (self, host, port, target)
|
Driver.new = function (self, host, port, target)
|
||||||
assert(host == target.host and port == target.port, "Target mismatch")
|
assert(host == target.host and port == target.port, "Target mismatch")
|
||||||
target:worker()
|
target:worker()
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
target = target,
|
target = target,
|
||||||
connect = telnet_autosize
|
connect = telnet_autosize
|
||||||
and Driver.methods.connect_autosize
|
and Driver.methods.connect_autosize
|
||||||
or Driver.methods.connect_simple,
|
or Driver.methods.connect_simple,
|
||||||
thread_exit = nmap.condvar(target)
|
thread_exit = nmap.condvar(target)
|
||||||
},
|
},
|
||||||
{ __index = Driver.methods } )
|
{ __index = Driver.methods } )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -429,13 +429,13 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Driver.methods.connect_simple = function (self)
|
Driver.methods.connect_simple = function (self)
|
||||||
assert(not self.conn, "Multiple connections attempted")
|
assert(not self.conn, "Multiple connections attempted")
|
||||||
local status, response = self.target:attach()
|
local status, response = self.target:attach()
|
||||||
if status then
|
if status then
|
||||||
self.conn = response
|
self.conn = response
|
||||||
response = nil
|
response = nil
|
||||||
end
|
end
|
||||||
return status, response
|
return status, response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -446,26 +446,26 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Driver.methods.connect_autosize = function (self)
|
Driver.methods.connect_autosize = function (self)
|
||||||
assert(not self.conn, "Multiple connections attempted")
|
assert(not self.conn, "Multiple connections attempted")
|
||||||
self.target:inuse(true)
|
self.target:inuse(true)
|
||||||
local status, response = self.target:attach()
|
local status, response = self.target:attach()
|
||||||
if status then
|
if status then
|
||||||
-- connected to the target
|
-- connected to the target
|
||||||
self.conn = response
|
self.conn = response
|
||||||
if self:prompt() then
|
if self:prompt() then
|
||||||
-- successfully reached login prompt
|
-- successfully reached login prompt
|
||||||
return true, nil
|
return true, nil
|
||||||
end
|
end
|
||||||
-- connected but turned away
|
-- connected but turned away
|
||||||
self.target:detach()
|
self.target:detach()
|
||||||
end
|
end
|
||||||
-- let's park the thread here till all the functioning threads finish
|
-- let's park the thread here till all the functioning threads finish
|
||||||
self.target:inuse(false)
|
self.target:inuse(false)
|
||||||
print_debug(detail_debug, "Retiring %s", tostring(coroutine.running()))
|
print_debug(detail_debug, "Retiring %s", tostring(coroutine.running()))
|
||||||
while not self.target:idle() do self.thread_exit("wait") end
|
while not self.target:idle() do self.thread_exit("wait") end
|
||||||
-- pretend that it connected
|
-- pretend that it connected
|
||||||
self.conn = Connection.GHOST
|
self.conn = Connection.GHOST
|
||||||
return true, nil
|
return true, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -476,13 +476,13 @@ end
|
|||||||
-- @return Status (true or false)
|
-- @return Status (true or false)
|
||||||
-- @return nil if the operation was successful; error code otherwise
|
-- @return nil if the operation was successful; error code otherwise
|
||||||
Driver.methods.disconnect = function (self)
|
Driver.methods.disconnect = function (self)
|
||||||
assert(self.conn, "Attempt to disconnect non-existing connection")
|
assert(self.conn, "Attempt to disconnect non-existing connection")
|
||||||
if self.conn.isopen and not self.conn.error then
|
if self.conn.isopen and not self.conn.error then
|
||||||
-- try to reach new login prompt
|
-- try to reach new login prompt
|
||||||
self:prompt()
|
self:prompt()
|
||||||
end
|
end
|
||||||
self.conn = nil
|
self.conn = nil
|
||||||
return self.target:detach()
|
return self.target:detach()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -492,16 +492,16 @@ end
|
|||||||
-- @param self Driver object
|
-- @param self Driver object
|
||||||
-- @return line Reached prompt or nil
|
-- @return line Reached prompt or nil
|
||||||
Driver.methods.prompt = function (self)
|
Driver.methods.prompt = function (self)
|
||||||
assert(self.conn, "Attempt to use disconnected driver")
|
assert(self.conn, "Attempt to use disconnected driver")
|
||||||
local conn = self.conn
|
local conn = self.conn
|
||||||
local line
|
local line
|
||||||
repeat
|
repeat
|
||||||
line = conn:get_line()
|
line = conn:get_line()
|
||||||
until not line
|
until not line
|
||||||
or is_username_prompt(line)
|
or is_username_prompt(line)
|
||||||
or is_password_prompt(line)
|
or is_password_prompt(line)
|
||||||
or not conn:discard_line()
|
or not conn:discard_line()
|
||||||
return line
|
return line
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -513,181 +513,181 @@ end
|
|||||||
-- @return instance of brute.Account if the operation was successful;
|
-- @return instance of brute.Account if the operation was successful;
|
||||||
-- instance of brute.Error otherwise
|
-- instance of brute.Error otherwise
|
||||||
Driver.methods.login = function (self, username, password)
|
Driver.methods.login = function (self, username, password)
|
||||||
assert(self.conn, "Attempt to use disconnected driver")
|
assert(self.conn, "Attempt to use disconnected driver")
|
||||||
local sent_username = self.target.passonly
|
local sent_username = self.target.passonly
|
||||||
local sent_password = false
|
local sent_password = false
|
||||||
local conn = self.conn
|
local conn = self.conn
|
||||||
|
|
||||||
local loc = " in " .. tostring(coroutine.running())
|
local loc = " in " .. tostring(coroutine.running())
|
||||||
|
|
||||||
local connection_error = function (msg)
|
local connection_error = function (msg)
|
||||||
print_debug(detail_debug, msg .. loc)
|
print_debug(detail_debug, msg .. loc)
|
||||||
local err = brute.Error:new(msg)
|
local err = brute.Error:new(msg)
|
||||||
err:setRetry(true)
|
err:setRetry(true)
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
local passonly_error = function ()
|
local passonly_error = function ()
|
||||||
local msg = "Password prompt encountered"
|
local msg = "Password prompt encountered"
|
||||||
print_debug(critical_debug, msg .. loc)
|
print_debug(critical_debug, msg .. loc)
|
||||||
local err = brute.Error:new(msg)
|
local err = brute.Error:new(msg)
|
||||||
err:setAbort(true)
|
err:setAbort(true)
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
local username_error = function ()
|
local username_error = function ()
|
||||||
local msg = "Invalid username encountered"
|
local msg = "Invalid username encountered"
|
||||||
print_debug(detail_debug, msg .. loc)
|
print_debug(detail_debug, msg .. loc)
|
||||||
local err = brute.Error:new(msg)
|
local err = brute.Error:new(msg)
|
||||||
err:setInvalidAccount(username)
|
err:setInvalidAccount(username)
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
local login_error = function ()
|
local login_error = function ()
|
||||||
local msg = "Login failed"
|
local msg = "Login failed"
|
||||||
print_debug(detail_debug, msg .. loc)
|
print_debug(detail_debug, msg .. loc)
|
||||||
return false, brute.Error:new(msg)
|
return false, brute.Error:new(msg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local login_success = function ()
|
local login_success = function ()
|
||||||
local msg = "Login succeeded"
|
local msg = "Login succeeded"
|
||||||
print_debug(detail_debug, msg .. loc)
|
print_debug(detail_debug, msg .. loc)
|
||||||
return true, brute.Account:new(username, password, "OPEN")
|
return true, brute.Account:new(username, password, "OPEN")
|
||||||
end
|
end
|
||||||
|
|
||||||
local login_no_password = function ()
|
local login_no_password = function ()
|
||||||
local msg = "Login succeeded without password"
|
local msg = "Login succeeded without password"
|
||||||
print_debug(detail_debug, msg .. loc)
|
print_debug(detail_debug, msg .. loc)
|
||||||
return true, brute.Account:new(username, "<none>", "OPEN")
|
return true, brute.Account:new(username, "<none>", "OPEN")
|
||||||
end
|
end
|
||||||
|
|
||||||
print_debug(detail_debug, "Login attempt %s:%s%s", username, password, loc)
|
print_debug(detail_debug, "Login attempt %s:%s%s", username, password, loc)
|
||||||
|
|
||||||
if conn == Connection.GHOST then
|
if conn == Connection.GHOST then
|
||||||
-- reached when auto-sizing is enabled and all worker threads
|
-- reached when auto-sizing is enabled and all worker threads
|
||||||
-- failed
|
-- failed
|
||||||
return connection_error("Service unreachable")
|
return connection_error("Service unreachable")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- username has not yet been sent
|
-- username has not yet been sent
|
||||||
while not sent_username do
|
while not sent_username do
|
||||||
local line = conn:get_line()
|
local line = conn:get_line()
|
||||||
if not line then
|
if not line then
|
||||||
-- stopped receiving data
|
-- stopped receiving data
|
||||||
return connection_error("Login prompt not reached")
|
return connection_error("Login prompt not reached")
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_username_prompt(line) then
|
if is_username_prompt(line) then
|
||||||
-- being prompted for a username
|
-- being prompted for a username
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
print_debug(detail_debug, "Sending username" .. loc)
|
print_debug(detail_debug, "Sending username" .. loc)
|
||||||
if not conn:send_line(username) then
|
if not conn:send_line(username) then
|
||||||
return connection_error(conn.error)
|
return connection_error(conn.error)
|
||||||
end
|
end
|
||||||
sent_username = true
|
sent_username = true
|
||||||
if conn:get_line() == username then
|
if conn:get_line() == username then
|
||||||
-- ignore; remote echo of the username in effect
|
-- ignore; remote echo of the username in effect
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif is_password_prompt(line) then
|
elseif is_password_prompt(line) then
|
||||||
-- looks like 'password only' support
|
-- looks like 'password only' support
|
||||||
return passonly_error()
|
return passonly_error()
|
||||||
|
|
||||||
else
|
else
|
||||||
-- ignore; insignificant response line
|
-- ignore; insignificant response line
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- username has been already sent
|
-- username has been already sent
|
||||||
while not sent_password do
|
while not sent_password do
|
||||||
local line = conn:get_line()
|
local line = conn:get_line()
|
||||||
if not line then
|
if not line then
|
||||||
-- remote host disconnected
|
-- remote host disconnected
|
||||||
return connection_error("Password prompt not reached")
|
return connection_error("Password prompt not reached")
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_login_success(line) then
|
if is_login_success(line) then
|
||||||
-- successful login without a password
|
-- successful login without a password
|
||||||
conn:close()
|
conn:close()
|
||||||
return login_no_password()
|
return login_no_password()
|
||||||
|
|
||||||
elseif is_password_prompt(line) then
|
elseif is_password_prompt(line) then
|
||||||
-- being prompted for a password
|
-- being prompted for a password
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
print_debug(detail_debug, "Sending password" .. loc)
|
print_debug(detail_debug, "Sending password" .. loc)
|
||||||
if not conn:send_line(password) then
|
if not conn:send_line(password) then
|
||||||
return connection_error(conn.error)
|
return connection_error(conn.error)
|
||||||
end
|
end
|
||||||
sent_password = true
|
sent_password = true
|
||||||
|
|
||||||
elseif is_login_failure(line) then
|
elseif is_login_failure(line) then
|
||||||
-- failed login without a password; explicitly told so
|
-- failed login without a password; explicitly told so
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
return username_error()
|
return username_error()
|
||||||
|
|
||||||
elseif is_username_prompt(line) then
|
elseif is_username_prompt(line) then
|
||||||
-- failed login without a password; prompted again for a username
|
-- failed login without a password; prompted again for a username
|
||||||
return username_error()
|
return username_error()
|
||||||
|
|
||||||
else
|
else
|
||||||
-- ignore; insignificant response line
|
-- ignore; insignificant response line
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- password has been already sent
|
-- password has been already sent
|
||||||
while true do
|
while true do
|
||||||
local line = conn:get_line()
|
local line = conn:get_line()
|
||||||
if not line then
|
if not line then
|
||||||
-- remote host disconnected
|
-- remote host disconnected
|
||||||
return connection_error("Login not completed")
|
return connection_error("Login not completed")
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_login_success(line) then
|
if is_login_success(line) then
|
||||||
-- successful login
|
-- successful login
|
||||||
conn:close()
|
conn:close()
|
||||||
return login_success()
|
return login_success()
|
||||||
|
|
||||||
elseif is_login_failure(line) then
|
elseif is_login_failure(line) then
|
||||||
-- failed login; explicitly told so
|
-- failed login; explicitly told so
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
return login_error()
|
return login_error()
|
||||||
|
|
||||||
elseif is_password_prompt(line) or is_username_prompt(line) then
|
elseif is_password_prompt(line) or is_username_prompt(line) then
|
||||||
-- failed login; prompted again for credentials
|
-- failed login; prompted again for credentials
|
||||||
return login_error()
|
return login_error()
|
||||||
|
|
||||||
else
|
else
|
||||||
-- ignore; insignificant response line
|
-- ignore; insignificant response line
|
||||||
conn:discard_line()
|
conn:discard_line()
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- unreachable code
|
-- unreachable code
|
||||||
assert(false, "Reached unreachable code")
|
assert(false, "Reached unreachable code")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function (host, port)
|
action = function (host, port)
|
||||||
local ts, tserror = stdnse.parse_timespec(arg_timeout)
|
local ts, tserror = stdnse.parse_timespec(arg_timeout)
|
||||||
if not ts then
|
if not ts then
|
||||||
return stdnse.format_output(false, "Invalid timeout value: " .. tserror)
|
return stdnse.format_output(false, "Invalid timeout value: " .. tserror)
|
||||||
end
|
end
|
||||||
telnet_timeout = 1000 * ts
|
telnet_timeout = 1000 * ts
|
||||||
telnet_autosize = arg_autosize:lower() == "true"
|
telnet_autosize = arg_autosize:lower() == "true"
|
||||||
|
|
||||||
local target = Target.new(host, port)
|
local target = Target.new(host, port)
|
||||||
if not target then
|
if not target then
|
||||||
return stdnse.format_output(false, "Unable to connect to the target")
|
return stdnse.format_output(false, "Unable to connect to the target")
|
||||||
end
|
end
|
||||||
|
|
||||||
local engine = brute.Engine:new(Driver, host, port, target)
|
local engine = brute.Engine:new(Driver, host, port, target)
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
target.passonly = engine.options.passonly
|
target.passonly = engine.options.passonly
|
||||||
local _, result = engine:start()
|
local _, result = engine:start()
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user