mirror of
https://github.com/nmap/nmap.git
synced 2025-12-15 12:19:02 +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
|
-- @return table suitable for stdnse.format_output
|
||||||
local function processResponse( tbl, max_count, out, count )
|
local function processResponse( tbl, max_count, out, count )
|
||||||
|
|
||||||
local out = out or {}
|
local out = out or {}
|
||||||
local count = count or 0
|
local count = count or 0
|
||||||
|
|
||||||
for _, v in ipairs(tbl) do
|
for _, v in ipairs(tbl) do
|
||||||
if ( max_count and max_count > 0 and max_count <= count ) then
|
if ( max_count and max_count > 0 and max_count <= count ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if ( v.name ) then
|
if ( v.name ) then
|
||||||
local sfx = ( v.type == 0x80 ) and "/" or ""
|
local sfx = ( v.type == 0x80 ) and "/" or ""
|
||||||
table.insert(out, v.name .. sfx )
|
table.insert(out, v.name .. sfx )
|
||||||
count = count + 1
|
count = count + 1
|
||||||
elseif( type(v) == 'table' ) then
|
elseif( type(v) == 'table' ) then
|
||||||
local tmp = {}
|
local tmp = {}
|
||||||
table.insert( out, tmp )
|
table.insert( out, tmp )
|
||||||
processResponse( v, max_count, tmp, count )
|
processResponse( v, max_count, tmp, count )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- strip the outer table
|
-- strip the outer table
|
||||||
return out[1]
|
return out[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This function simply checks if the table contains a Directory Id (DID) of 2
|
--- 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
|
-- @param tbl table containing the table as return from the Dir method
|
||||||
-- @return true if host is vulnerable, false otherwise
|
-- @return true if host is vulnerable, false otherwise
|
||||||
local function isVulnerable( tbl )
|
local function isVulnerable( tbl )
|
||||||
for _, v in ipairs(tbl) do
|
for _, v in ipairs(tbl) do
|
||||||
-- if we got no v.id it's probably a container table
|
-- if we got no v.id it's probably a container table
|
||||||
if ( not(v.id) ) then
|
if ( not(v.id) ) then
|
||||||
if ( isVulnerable(v) ) then
|
if ( isVulnerable(v) ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( v.id == 2 ) then
|
if ( v.id == 2 ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local status, response, shares
|
local status, response, shares
|
||||||
local afp_helper = afp.Helper:new()
|
local afp_helper = afp.Helper:new()
|
||||||
local args = nmap.registry.args
|
local args = nmap.registry.args
|
||||||
local users = nmap.registry.afp or { ['nil'] = 'nil' }
|
local users = nmap.registry.afp or { ['nil'] = 'nil' }
|
||||||
local vulnerable = false
|
local vulnerable = false
|
||||||
|
|
||||||
local MAX_FILES = 5
|
local MAX_FILES = 5
|
||||||
|
|
||||||
local afp_vuln = {
|
local afp_vuln = {
|
||||||
title = "Apple Mac OS X AFP server directory traversal",
|
title = "Apple Mac OS X AFP server directory traversal",
|
||||||
IDS = {CVE = 'CVE-2010-0533'},
|
IDS = {CVE = 'CVE-2010-0533'},
|
||||||
risk_factor = "High",
|
risk_factor = "High",
|
||||||
scores = {
|
scores = {
|
||||||
CVSSv2 = "7.5 (HIGH) (AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
CVSSv2 = "7.5 (HIGH) (AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||||
},
|
},
|
||||||
description = [[
|
description = [[
|
||||||
Directory traversal vulnerability in AFP Server in Apple Mac OS X before
|
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.]],
|
10.6.3 allows remote attackers to list a share root's parent directory.]],
|
||||||
references = {
|
references = {
|
||||||
'http://www.cqure.net/wp/2010/03/detecting-apple-mac-os-x-afp-vulnerability-cve-2010-0533-with-nmap',
|
'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',
|
'http://support.apple.com/kb/HT1222',
|
||||||
},
|
},
|
||||||
dates = {
|
dates = {
|
||||||
disclosure = {year = '2010', month = '03', day = '29'},
|
disclosure = {year = '2010', month = '03', day = '29'},
|
||||||
},
|
},
|
||||||
exploit_results = {},
|
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
|
if ( args['afp.username'] ) then
|
||||||
users = {}
|
users = {}
|
||||||
users[args['afp.username']] = args['afp.password']
|
users[args['afp.username']] = args['afp.password']
|
||||||
end
|
end
|
||||||
|
|
||||||
for username, password in pairs(users) do
|
for username, password in pairs(users) do
|
||||||
|
|
||||||
status, response = afp_helper:OpenSession(host, port)
|
status, response = afp_helper:OpenSession(host, port)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(response)
|
stdnse.print_debug(response)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Attempt to use No User Authentication?
|
-- Attempt to use No User Authentication?
|
||||||
if ( username ~= 'nil' ) then
|
if ( username ~= 'nil' ) then
|
||||||
status, response = afp_helper:Login(username, password)
|
status, response = afp_helper:Login(username, password)
|
||||||
else
|
else
|
||||||
status, response = afp_helper:Login(nil, nil)
|
status, response = afp_helper:Login(nil, nil)
|
||||||
end
|
end
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug("afp-path-vuln: Login failed", response)
|
stdnse.print_debug("afp-path-vuln: Login failed", response)
|
||||||
stdnse.print_debug(3, "afp-path-vuln: Login error: %s", response)
|
stdnse.print_debug(3, "afp-path-vuln: Login error: %s", response)
|
||||||
return
|
return
|
||||||
end
|
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
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(3, "afp-path-vuln: %s", response)
|
stdnse.print_debug(3, "afp-path-vuln: %s", response)
|
||||||
else
|
else
|
||||||
if ( isVulnerable( response ) ) then
|
if ( isVulnerable( response ) ) then
|
||||||
vulnerable = true
|
vulnerable = true
|
||||||
if(nmap.verbosity() > 1) then
|
if(nmap.verbosity() > 1) then
|
||||||
response = processResponse( response )
|
response = processResponse( response )
|
||||||
local name = share .. "/../"
|
local name = share .. "/../"
|
||||||
table.insert(afp_vuln.exploit_results,
|
table.insert(afp_vuln.exploit_results,
|
||||||
name)
|
name)
|
||||||
else
|
else
|
||||||
response = processResponse( response, MAX_FILES )
|
response = processResponse( response, MAX_FILES )
|
||||||
local name = share .. ("/../ (%d first items)"):format(MAX_FILES)
|
local name = share .. ("/../ (%d first items)"):format(MAX_FILES)
|
||||||
table.insert(afp_vuln.exploit_results,
|
table.insert(afp_vuln.exploit_results,
|
||||||
name)
|
name)
|
||||||
end
|
end
|
||||||
table.insert(afp_vuln.exploit_results,
|
table.insert(afp_vuln.exploit_results,
|
||||||
response)
|
response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( vulnerable ) then
|
if ( vulnerable ) then
|
||||||
afp_vuln.state = vulns.STATE.EXPLOIT
|
afp_vuln.state = vulns.STATE.EXPLOIT
|
||||||
else
|
else
|
||||||
afp_vuln.state = vulns.STATE.NOT_VULN
|
afp_vuln.state = vulns.STATE.NOT_VULN
|
||||||
end
|
end
|
||||||
|
|
||||||
return report:make_output(afp_vuln)
|
return report:make_output(afp_vuln)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -90,41 +90,41 @@ http://sourceforge.net/projects/gameq/
|
|||||||
-- <elem key="num players">2</elem>
|
-- <elem key="num players">2</elem>
|
||||||
-- <elem key="max players">16</elem>
|
-- <elem key="max players">16</elem>
|
||||||
-- <table key="settings">
|
-- <table key="settings">
|
||||||
-- <elem key="Dedicated">No</elem>
|
-- <elem key="Dedicated">No</elem>
|
||||||
-- <elem key="Password Required">No</elem>
|
-- <elem key="Password Required">No</elem>
|
||||||
-- <elem key="Time Limit">30</elem>
|
-- <elem key="Time Limit">30</elem>
|
||||||
-- <elem key="Points Limit">200 min.</elem>
|
-- <elem key="Points Limit">200 min.</elem>
|
||||||
-- <elem key="Respawns Limit">unlimited</elem>
|
-- <elem key="Respawns Limit">unlimited</elem>
|
||||||
-- <elem key="Respawn Delay">10 sec.</elem>
|
-- <elem key="Respawn Delay">10 sec.</elem>
|
||||||
-- <elem key="Enemies Visible On Map">No</elem>
|
-- <elem key="Enemies Visible On Map">No</elem>
|
||||||
-- <elem key="Available Inventory Room">Yes</elem>
|
-- <elem key="Available Inventory Room">Yes</elem>
|
||||||
-- <elem key="Identify Enemy Players">No</elem>
|
-- <elem key="Identify Enemy Players">No</elem>
|
||||||
-- <elem key="Available Vehicles">Yes</elem>
|
-- <elem key="Available Vehicles">Yes</elem>
|
||||||
-- <elem key="Vehicle Respaws Limit">unlimited</elem>
|
-- <elem key="Vehicle Respaws Limit">unlimited</elem>
|
||||||
-- <elem key="Vehicle Respawn Delay">30 sec.</elem>
|
-- <elem key="Vehicle Respawn Delay">30 sec.</elem>
|
||||||
-- <elem key="Vehicle Auto Return Time">90 sec.</elem>
|
-- <elem key="Vehicle Auto Return Time">90 sec.</elem>
|
||||||
-- <elem key="Vehicles Visible On Map">Yes</elem>
|
-- <elem key="Vehicles Visible On Map">Yes</elem>
|
||||||
-- <elem key="Team Balance">Off</elem>
|
-- <elem key="Team Balance">Off</elem>
|
||||||
-- <elem key="Friendly Fire">On</elem>
|
-- <elem key="Friendly Fire">On</elem>
|
||||||
-- <elem key="Friends Visible On Map">Yes</elem>
|
-- <elem key="Friends Visible On Map">Yes</elem>
|
||||||
-- </table>
|
-- </table>
|
||||||
-- <table key="players">
|
-- <table key="players">
|
||||||
-- <table key="player 0">
|
-- <table key="player 0">
|
||||||
-- <elem key="name">NoVoDondo</elem>
|
-- <elem key="name">NoVoDondo</elem>
|
||||||
-- <elem key="team">BLUE</elem>
|
-- <elem key="team">BLUE</elem>
|
||||||
-- <elem key="skin"></elem>
|
-- <elem key="skin"></elem>
|
||||||
-- <elem key="score">71</elem>
|
-- <elem key="score">71</elem>
|
||||||
-- <elem key="ping">0</elem>
|
-- <elem key="ping">0</elem>
|
||||||
-- <elem key="time"></elem>
|
-- <elem key="time"></elem>
|
||||||
-- </table>
|
-- </table>
|
||||||
-- <table key="player 1">
|
-- <table key="player 1">
|
||||||
-- <elem key="name">HeroX</elem>
|
-- <elem key="name">HeroX</elem>
|
||||||
-- <elem key="team">RED</elem>
|
-- <elem key="team">RED</elem>
|
||||||
-- <elem key="skin"></elem>
|
-- <elem key="skin"></elem>
|
||||||
-- <elem key="score">0</elem>
|
-- <elem key="score">0</elem>
|
||||||
-- <elem key="ping">11</elem>
|
-- <elem key="ping">11</elem>
|
||||||
-- <elem key="time"></elem>
|
-- <elem key="time"></elem>
|
||||||
-- </table>
|
-- </table>
|
||||||
-- </table>
|
-- </table>
|
||||||
|
|
||||||
author = "Marin Maržić"
|
author = "Marin Maržić"
|
||||||
@@ -139,94 +139,94 @@ categories = { "discovery", "safe", "version" }
|
|||||||
-- @return ret_pos the position after the last unpacked byte
|
-- @return ret_pos the position after the last unpacked byte
|
||||||
-- @return string the unpacked string
|
-- @return string the unpacked string
|
||||||
local unpack_str = function(str, pos)
|
local unpack_str = function(str, pos)
|
||||||
local ret_pos = pos + str:byte(pos)
|
local ret_pos = pos + str:byte(pos)
|
||||||
return ret_pos, string.sub(str, pos + 1, ret_pos - 1)
|
return ret_pos, string.sub(str, pos + 1, ret_pos - 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = shortport.version_port_or_service({1258,2126,3123,12444,13200,23196,26000,27138,27244,27777,28138}, "allseeingeye", "udp")
|
portrule = shortport.version_port_or_service({1258,2126,3123,12444,13200,23196,26000,27138,27244,27777,28138}, "allseeingeye", "udp")
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, data = comm.exchange(host, port.number, "s", { proto = "udp", timeout = 3000 })
|
local status, data = comm.exchange(host, port.number, "s", { proto = "udp", timeout = 3000 })
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- UDP port is open
|
-- UDP port is open
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
|
|
||||||
if not string.match(data, "^EYE1") then
|
if not string.match(data, "^EYE1") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Detected; extract fields
|
-- Detected; extract fields
|
||||||
local o = stdnse.output_table()
|
local o = stdnse.output_table()
|
||||||
local pos = 5
|
local pos = 5
|
||||||
|
|
||||||
pos, o["game"] = unpack_str(data, pos)
|
pos, o["game"] = unpack_str(data, pos)
|
||||||
pos, o["port"] = unpack_str(data, pos)
|
pos, o["port"] = unpack_str(data, pos)
|
||||||
pos, o["server name"] = unpack_str(data, pos)
|
pos, o["server name"] = unpack_str(data, pos)
|
||||||
pos, o["game type"] = unpack_str(data, pos)
|
pos, o["game type"] = unpack_str(data, pos)
|
||||||
pos, o["map"] = unpack_str(data, pos)
|
pos, o["map"] = unpack_str(data, pos)
|
||||||
pos, o["version"] = unpack_str(data, pos)
|
pos, o["version"] = unpack_str(data, pos)
|
||||||
pos, o["passworded"] = unpack_str(data, pos)
|
pos, o["passworded"] = unpack_str(data, pos)
|
||||||
pos, o["num players"] = unpack_str(data, pos)
|
pos, o["num players"] = unpack_str(data, pos)
|
||||||
pos, o["max players"] = unpack_str(data, pos)
|
pos, o["max players"] = unpack_str(data, pos)
|
||||||
|
|
||||||
-- extract the key-value pairs
|
-- extract the key-value pairs
|
||||||
local kv = stdnse.output_table()
|
local kv = stdnse.output_table()
|
||||||
o["settings"] = kv
|
o["settings"] = kv
|
||||||
while data:byte(pos) ~= 1 do
|
while data:byte(pos) ~= 1 do
|
||||||
local key, value
|
local key, value
|
||||||
pos, key = unpack_str(data, pos)
|
pos, key = unpack_str(data, pos)
|
||||||
pos, value = unpack_str(data, pos)
|
pos, value = unpack_str(data, pos)
|
||||||
kv[key] = value
|
kv[key] = value
|
||||||
end
|
end
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
|
|
||||||
-- extract player info
|
-- extract player info
|
||||||
local players = stdnse.output_table()
|
local players = stdnse.output_table()
|
||||||
o["players"] = players
|
o["players"] = players
|
||||||
local playernum = 0
|
local playernum = 0
|
||||||
while pos <= #data do
|
while pos <= #data do
|
||||||
local flags = data:byte(pos)
|
local flags = data:byte(pos)
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
|
|
||||||
local player = stdnse.output_table()
|
local player = stdnse.output_table()
|
||||||
if bit.band(flags, 1) ~= 0 then
|
if bit.band(flags, 1) ~= 0 then
|
||||||
pos, player.name = unpack_str(data, pos)
|
pos, player.name = unpack_str(data, pos)
|
||||||
end
|
end
|
||||||
if bit.band(flags, 2) ~= 0 then
|
if bit.band(flags, 2) ~= 0 then
|
||||||
pos, player.team = unpack_str(data, pos)
|
pos, player.team = unpack_str(data, pos)
|
||||||
end
|
end
|
||||||
if bit.band(flags, 4) ~= 0 then
|
if bit.band(flags, 4) ~= 0 then
|
||||||
pos, player.skin = unpack_str(data, pos)
|
pos, player.skin = unpack_str(data, pos)
|
||||||
end
|
end
|
||||||
if bit.band(flags, 8) ~= 0 then
|
if bit.band(flags, 8) ~= 0 then
|
||||||
pos, player.score = unpack_str(data, pos)
|
pos, player.score = unpack_str(data, pos)
|
||||||
end
|
end
|
||||||
if bit.band(flags, 16) ~= 0 then
|
if bit.band(flags, 16) ~= 0 then
|
||||||
pos, player.ping = unpack_str(data, pos)
|
pos, player.ping = unpack_str(data, pos)
|
||||||
end
|
end
|
||||||
if bit.band(flags, 32) ~= 0 then
|
if bit.band(flags, 32) ~= 0 then
|
||||||
pos, player.time = unpack_str(data, pos)
|
pos, player.time = unpack_str(data, pos)
|
||||||
end
|
end
|
||||||
|
|
||||||
players["player " .. playernum] = player
|
players["player " .. playernum] = player
|
||||||
playernum = playernum + 1
|
playernum = playernum + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
port.version.name = "ase"
|
port.version.name = "ase"
|
||||||
port.version.name_confidence = 10
|
port.version.name_confidence = 10
|
||||||
port.version.product = "All-Seeing Eye"
|
port.version.product = "All-Seeing Eye"
|
||||||
local passworded_string
|
local passworded_string
|
||||||
if o["passworded"] == "0" then
|
if o["passworded"] == "0" then
|
||||||
passworded_string = "; no password"
|
passworded_string = "; no password"
|
||||||
else
|
else
|
||||||
passworded_string = "; has password"
|
passworded_string = "; has password"
|
||||||
end
|
end
|
||||||
port.version.extrainfo = "game: " .. o["game"] .. " " .. o["version"] .. "; port: " .. o["port"] .. passworded_string
|
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
|
return o
|
||||||
end
|
end
|
||||||
@@ -51,27 +51,27 @@ categories = {"broadcast", "safe"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Creates a random MAC address
|
-- Creates a random MAC address
|
||||||
--
|
--
|
||||||
-- @return mac_addr string containing a random MAC
|
-- @return mac_addr string containing a random MAC
|
||||||
local function randomizeMAC()
|
local function randomizeMAC()
|
||||||
local mac_addr = ""
|
local mac_addr = ""
|
||||||
for j=1, 6 do
|
for j=1, 6 do
|
||||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||||
end
|
end
|
||||||
return mac_addr
|
return mac_addr
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Gets a list of available interfaces based on link and up filters
|
-- 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
|
-- @param up string containing the interface status to filter
|
||||||
-- @return result table containing the matching interfaces
|
-- @return result table containing the matching interfaces
|
||||||
local function getInterfaces(link, up)
|
local function getInterfaces(link, up)
|
||||||
if( not(nmap.list_interfaces) ) then return end
|
if( not(nmap.list_interfaces) ) then return end
|
||||||
local interfaces, err = nmap.list_interfaces()
|
local interfaces, err = nmap.list_interfaces()
|
||||||
local result
|
local result
|
||||||
if ( not(err) ) then
|
if ( not(err) ) then
|
||||||
for _, iface in ipairs(interfaces) do
|
for _, iface in ipairs(interfaces) do
|
||||||
if ( iface.link == link and iface.up == up ) then
|
if ( iface.link == link and iface.up == up ) then
|
||||||
result = result or {}
|
result = result or {}
|
||||||
result[iface.device] = true
|
result[iface.device] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listens for an incoming dhcp response
|
-- Listens for an incoming dhcp response
|
||||||
@@ -101,113 +101,113 @@ end
|
|||||||
-- @param xid the DHCP transaction id
|
-- @param xid the DHCP transaction id
|
||||||
-- @param result a table to which the result is written
|
-- @param result a table to which the result is written
|
||||||
local function dhcp_listener(sock, timeout, xid, result)
|
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()
|
local start_time = nmap.clock_ms()
|
||||||
while( nmap.clock_ms() - start_time < timeout ) do
|
while( nmap.clock_ms() - start_time < timeout ) do
|
||||||
local status, _, _, data = sock:pcap_receive()
|
local status, _, _, data = sock:pcap_receive()
|
||||||
-- abort, once another thread has picked up our response
|
-- abort, once another thread has picked up our response
|
||||||
if ( #result > 0 ) then
|
if ( #result > 0 ) then
|
||||||
sock:close()
|
sock:close()
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local p = packet.Packet:new( data, #data )
|
local p = packet.Packet:new( data, #data )
|
||||||
if ( p and p.udp_dport ) then
|
if ( p and p.udp_dport ) then
|
||||||
local data = data:sub(p.udp_offset + 9)
|
local data = data:sub(p.udp_offset + 9)
|
||||||
local status, response = dhcp.dhcp_parse(data, xid)
|
local status, response = dhcp.dhcp_parse(data, xid)
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
table.insert( result, response )
|
table.insert( result, response )
|
||||||
sock:close()
|
sock:close()
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sock:close()
|
sock:close()
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local host, port = "255.255.255.255", 67
|
local host, port = "255.255.255.255", 67
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-dhcp-discover.timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args("broadcast-dhcp-discover.timeout"))
|
||||||
timeout = (timeout or 10) * 1000
|
timeout = (timeout or 10) * 1000
|
||||||
|
|
||||||
-- randomizing the MAC could exhaust dhcp servers with small scopes
|
-- randomizing the MAC could exhaust dhcp servers with small scopes
|
||||||
-- if ran multiple times, so we should probably refrain from doing
|
-- if ran multiple times, so we should probably refrain from doing
|
||||||
-- this?
|
-- this?
|
||||||
local mac = string.char(0xDE,0xAD,0xC0,0xDE,0xCA,0xFE)--randomizeMAC()
|
local mac = string.char(0xDE,0xAD,0xC0,0xDE,0xCA,0xFE)--randomizeMAC()
|
||||||
|
|
||||||
local interfaces
|
local interfaces
|
||||||
|
|
||||||
-- first check if the user supplied an interface
|
-- first check if the user supplied an interface
|
||||||
if ( nmap.get_interface() ) then
|
if ( nmap.get_interface() ) then
|
||||||
interfaces = { [nmap.get_interface()] = true }
|
interfaces = { [nmap.get_interface()] = true }
|
||||||
else
|
else
|
||||||
-- As the response will be sent to the "offered" ip address we need
|
-- 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
|
-- 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
|
-- 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.
|
-- run pcap on all of them, if they're a) up and b) ethernet.
|
||||||
interfaces = getInterfaces("ethernet", "up")
|
interfaces = getInterfaces("ethernet", "up")
|
||||||
end
|
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 transaction_id = bin.pack("<I", math.random(0, 0x7FFFFFFF))
|
||||||
local request_type = dhcp.request_types["DHCPDISCOVER"]
|
local request_type = dhcp.request_types["DHCPDISCOVER"]
|
||||||
local ip_address = bin.pack(">I", ipOps.todword("0.0.0.0"))
|
local ip_address = bin.pack(">I", ipOps.todword("0.0.0.0"))
|
||||||
|
|
||||||
-- we nead to set the flags to broadcast
|
-- we nead to set the flags to broadcast
|
||||||
local request_options, overrides, lease_time = nil, { flags = 0x8000 }, nil
|
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)
|
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
|
if (not(status)) then return "\n ERROR: Failed to build packet" end
|
||||||
|
|
||||||
local threads = {}
|
local threads = {}
|
||||||
local result = {}
|
local result = {}
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
|
|
||||||
-- start a listening thread for each interface
|
-- start a listening thread for each interface
|
||||||
for iface, _ in pairs(interfaces) do
|
for iface, _ in pairs(interfaces) do
|
||||||
local sock, co
|
local sock, co
|
||||||
sock = nmap.new_socket()
|
sock = nmap.new_socket()
|
||||||
sock:pcap_open(iface, 1500, false, "ip && udp && port 68")
|
sock:pcap_open(iface, 1500, false, "ip && udp && port 68")
|
||||||
co = stdnse.new_thread( dhcp_listener, sock, timeout, transaction_id, result )
|
co = stdnse.new_thread( dhcp_listener, sock, timeout, transaction_id, result )
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local socket = nmap.new_socket("udp")
|
local socket = nmap.new_socket("udp")
|
||||||
socket:bind(nil, 68)
|
socket:bind(nil, 68)
|
||||||
socket:sendto( host, port, packet )
|
socket:sendto( host, port, packet )
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
-- wait until all threads are done
|
-- wait until all threads are done
|
||||||
repeat
|
repeat
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
-- Display the results
|
-- Display the results
|
||||||
for i, r in ipairs(result) do
|
for i, r in ipairs(result) do
|
||||||
table.insert(response, string.format("IP Offered: %s", r.yiaddr_str))
|
table.insert(response, string.format("IP Offered: %s", r.yiaddr_str))
|
||||||
for _, v in ipairs(r.options) do
|
for _, v in ipairs(r.options) do
|
||||||
if(type(v['value']) == 'table') then
|
if(type(v['value']) == 'table') then
|
||||||
table.insert(response, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
table.insert(response, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||||
else
|
else
|
||||||
table.insert(response, string.format("%s: %s\n", v['name'], v['value']))
|
table.insert(response, string.format("%s: %s\n", v['name'], v['value']))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -41,61 +41,61 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|||||||
categories = {"discovery", "safe", "broadcast"}
|
categories = {"discovery", "safe", "broadcast"}
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
stdnse.print_verbose("%s is IPv4 only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Generates a raw PIM Hello message.
|
-- Generates a raw PIM Hello message.
|
||||||
--@return hello Raw PIM Hello message
|
--@return hello Raw PIM Hello message
|
||||||
local helloRaw = function()
|
local helloRaw = function()
|
||||||
-- Version: 2, Type: Hello (0)
|
-- Version: 2, Type: Hello (0)
|
||||||
local hello_raw = bin.pack(">C", 0x20)
|
local hello_raw = bin.pack(">C", 0x20)
|
||||||
-- Reserved
|
-- Reserved
|
||||||
hello_raw = hello_raw.. bin.pack(">C", 0x00)
|
hello_raw = hello_raw.. bin.pack(">C", 0x00)
|
||||||
-- Checksum: Calculated later
|
-- Checksum: Calculated later
|
||||||
hello_raw = hello_raw.. bin.pack(">S", 0x0000)
|
hello_raw = hello_raw.. bin.pack(">S", 0x0000)
|
||||||
-- Options (TLVs)
|
-- Options (TLVs)
|
||||||
-- Hold time 1 second
|
-- Hold time 1 second
|
||||||
hello_raw = hello_raw.. bin.pack(">SSS", 0x01, 0x02, 0x01)
|
hello_raw = hello_raw.. bin.pack(">SSS", 0x01, 0x02, 0x01)
|
||||||
-- Generation ID: Random
|
-- Generation ID: Random
|
||||||
hello_raw = hello_raw.. bin.pack(">SSI", 0x14, 0x04, math.random(23456))
|
hello_raw = hello_raw.. bin.pack(">SSI", 0x14, 0x04, math.random(23456))
|
||||||
-- DR Priority: 1
|
-- DR Priority: 1
|
||||||
hello_raw = hello_raw.. bin.pack(">SSI", 0x13, 0x04, 0x01)
|
hello_raw = hello_raw.. bin.pack(">SSI", 0x13, 0x04, 0x01)
|
||||||
-- State fresh capable: Version = 1, interval = 0, Reserved
|
-- State fresh capable: Version = 1, interval = 0, Reserved
|
||||||
hello_raw = hello_raw.. bin.pack(">SSCCS", 0x15, 0x04, 0x01, 0x00, 0x00)
|
hello_raw = hello_raw.. bin.pack(">SSCCS", 0x15, 0x04, 0x01, 0x00, 0x00)
|
||||||
-- Calculate checksum
|
-- Calculate checksum
|
||||||
hello_raw = hello_raw:sub(1,2) .. bin.pack(">S", packet.in_cksum(hello_raw)) .. hello_raw:sub(5)
|
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
|
end
|
||||||
|
|
||||||
-- Sends a PIM Hello message.
|
-- Sends a PIM Hello message.
|
||||||
--@param interface Network interface to use.
|
--@param interface Network interface to use.
|
||||||
--@param dstip Destination IP to which send the Hello.
|
--@param dstip Destination IP to which send the Hello.
|
||||||
local helloQuery = function(interface, dstip)
|
local helloQuery = function(interface, dstip)
|
||||||
local hello_packet, sock, eth_hdr
|
local hello_packet, sock, eth_hdr
|
||||||
local srcip = interface.address
|
local srcip = interface.address
|
||||||
|
|
||||||
local hello_raw = helloRaw()
|
local hello_raw = helloRaw()
|
||||||
local ip_raw = bin.pack("H", "45c00040ed780000016718bc0a00c8750a00c86b") .. hello_raw
|
local ip_raw = bin.pack("H", "45c00040ed780000016718bc0a00c8750a00c86b") .. hello_raw
|
||||||
hello_packet = packet.Packet:new(ip_raw, ip_raw:len())
|
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_src(ipOps.ip_to_str(srcip))
|
||||||
hello_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
hello_packet:ip_set_bin_dst(ipOps.ip_to_str(dstip))
|
||||||
hello_packet:ip_set_len(ip_raw:len()) hello_packet:ip_count_checksum()
|
hello_packet:ip_set_len(ip_raw:len()) hello_packet:ip_count_checksum()
|
||||||
|
|
||||||
sock = nmap.new_dnet()
|
sock = nmap.new_dnet()
|
||||||
sock:ethernet_open(interface.device)
|
sock:ethernet_open(interface.device)
|
||||||
-- Ethernet multicast for PIM, our ethernet address and packet type IP
|
-- 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)
|
eth_hdr = bin.pack(">HAS", "01 00 5e 00 00 0d", interface.mac, 0x0800)
|
||||||
sock:ethernet_send(eth_hdr .. hello_packet.buf)
|
sock:ethernet_send(eth_hdr .. hello_packet.buf)
|
||||||
sock:ethernet_close()
|
sock:ethernet_close()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listens for PIM Hello messages.
|
-- Listens for PIM Hello messages.
|
||||||
@@ -103,90 +103,90 @@ end
|
|||||||
--@param timeout Time to listen for a response.
|
--@param timeout Time to listen for a response.
|
||||||
--@param responses table to insert responders' IPs into.
|
--@param responses table to insert responders' IPs into.
|
||||||
local helloListen = function(interface, timeout, responses)
|
local helloListen = function(interface, timeout, responses)
|
||||||
local condvar = nmap.condvar(responses)
|
local condvar = nmap.condvar(responses)
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
local p, hello_raw, status, l3data, _
|
local p, hello_raw, status, l3data, _
|
||||||
|
|
||||||
-- PIM packets that are sent to 224.0.0.13 and not coming from our host
|
-- 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
|
local filter = 'ip proto 103 and dst host 224.0.0.13 and src host not ' .. interface.address
|
||||||
listener:set_timeout(100)
|
listener:set_timeout(100)
|
||||||
listener:pcap_open(interface.device, 1024, true, filter)
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
|
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
p = packet.Packet:new(l3data, #l3data)
|
p = packet.Packet:new(l3data, #l3data)
|
||||||
hello_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
hello_raw = string.sub(l3data, p.ip_hl*4 + 1)
|
||||||
-- Check that PIM Type is Hello
|
-- Check that PIM Type is Hello
|
||||||
if p and hello_raw:byte(1) == 0x20 then
|
if p and hello_raw:byte(1) == 0x20 then
|
||||||
table.insert(responses, p.ip_src)
|
table.insert(responses, p.ip_src)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the network interface used to send packets to the destination host.
|
--- Returns the network interface used to send packets to the destination host.
|
||||||
--@param destination host to which the interface is used.
|
--@param destination host to which the interface is used.
|
||||||
--@return interface Network interface used for destination host.
|
--@return interface Network interface used for destination host.
|
||||||
local getInterface = function(destination)
|
local getInterface = function(destination)
|
||||||
-- First, create dummy UDP connection to get interface
|
-- First, create dummy UDP connection to get interface
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local status, err = sock:connect(destination, "12345", "udp")
|
local status, err = sock:connect(destination, "12345", "udp")
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local status, address, _, _, _ = sock:get_info()
|
local status, address, _, _, _ = sock:get_info()
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for _, interface in pairs(nmap.list_interfaces()) do
|
for _, interface in pairs(nmap.list_interfaces()) do
|
||||||
if interface.address == address then
|
if interface.address == address then
|
||||||
return interface
|
return interface
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
local responses = {}
|
local responses = {}
|
||||||
timeout = (timeout or 5) * 1000
|
timeout = (timeout or 5) * 1000
|
||||||
local mcast = "224.0.0.13"
|
local mcast = "224.0.0.13"
|
||||||
|
|
||||||
-- Get the network interface to use
|
-- Get the network interface to use
|
||||||
local interface = nmap.get_interface()
|
local interface = nmap.get_interface()
|
||||||
if interface then
|
if interface then
|
||||||
interface = nmap.get_interface_info(interface)
|
interface = nmap.get_interface_info(interface)
|
||||||
|
else
|
||||||
|
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
|
else
|
||||||
interface = getInterface(mcast)
|
table.insert(responses,"Use the newtargets script-arg to add the results as targets")
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
return stdnse.format_output(true, responses)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -60,21 +60,21 @@ categories = {"discovery","safe","broadcast"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
if not nmap.registry[SCRIPT_NAME].rootfail then
|
if not nmap.registry[SCRIPT_NAME].rootfail then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -85,195 +85,195 @@ end
|
|||||||
-- @param ttl number containing value for the TTL (time to live) field in IP header
|
-- @param ttl number containing value for the TTL (time to live) field in IP header
|
||||||
-- @param data_length number value of ICMP payload length
|
-- @param data_length number value of ICMP payload length
|
||||||
local icmp_packet = function(srcIP, dstIP, ttl, data_length, mtu, seqNo, icmp_id)
|
local icmp_packet = function(srcIP, dstIP, ttl, data_length, mtu, seqNo, icmp_id)
|
||||||
-- A couple of checks first
|
-- A couple of checks first
|
||||||
assert((seqNo and seqNo>0 and seqNo<=0xffff),"ICMP Sequence number: Value out of range(1-65535).")
|
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).")
|
assert((ttl and ttl>0 and ttl<0xff),"TTL(time-to-live): Value out of range(1-256).")
|
||||||
-- MTU values should be considered here!
|
-- 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).")
|
assert((data_length and data_length>=0 and data_length<mtu),"ICMP Payload length: Value out of range(0-mtu).")
|
||||||
|
|
||||||
-- ICMP Message
|
-- ICMP Message
|
||||||
local icmp_payload = nil
|
local icmp_payload = nil
|
||||||
if data_length and data_length>0 then
|
if data_length and data_length>0 then
|
||||||
icmp_payload = openssl.rand_bytes(data_length)
|
icmp_payload = openssl.rand_bytes(data_length)
|
||||||
else
|
else
|
||||||
icmp_payload = ""
|
icmp_payload = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
local seqNo_hex = stdnse.tohex(seqNo)
|
local seqNo_hex = stdnse.tohex(seqNo)
|
||||||
local icmp_seqNo = bin.pack(">H", string.rep("0",(4-seqNo_hex))..seqNo_hex)
|
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);
|
-- 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_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
|
--IP Total Length
|
||||||
local length_hex = stdnse.tohex(20 + #icmp_msg)
|
local length_hex = stdnse.tohex(20 + #icmp_msg)
|
||||||
local ip_length = bin.pack(">H", string.rep("0",(4-#length_hex))..length_hex)
|
local ip_length = bin.pack(">H", string.rep("0",(4-#length_hex))..length_hex)
|
||||||
|
|
||||||
--TTL
|
--TTL
|
||||||
local ttl_hex = stdnse.tohex(ttl)
|
local ttl_hex = stdnse.tohex(ttl)
|
||||||
local ip_ttl = bin.pack(">H", string.rep("0",(2-ttl_hex))..ttl_hex)
|
local ip_ttl = bin.pack(">H", string.rep("0",(2-ttl_hex))..ttl_hex)
|
||||||
|
|
||||||
--IP header
|
--IP header
|
||||||
local ip_bin = bin.pack(">HAHAH","4500",ip_length, "0000 4000", ip_ttl,
|
local ip_bin = bin.pack(">HAHAH","4500",ip_length, "0000 4000", ip_ttl,
|
||||||
"01 0000 0000 0000 0000 0000")
|
"01 0000 0000 0000 0000 0000")
|
||||||
|
|
||||||
-- IP+ICMP; Addresses and checksum need to be filled
|
-- IP+ICMP; Addresses and checksum need to be filled
|
||||||
local icmp_bin = bin.pack(">AA",ip_bin, icmp_msg)
|
local icmp_bin = bin.pack(">AA",ip_bin, icmp_msg)
|
||||||
|
|
||||||
--Packet
|
--Packet
|
||||||
local icmp = packet.Packet:new(icmp_bin,#icmp_bin)
|
local icmp = packet.Packet:new(icmp_bin,#icmp_bin)
|
||||||
assert(icmp,"Mistake during ICMP packet parsing")
|
assert(icmp,"Mistake during ICMP packet parsing")
|
||||||
|
|
||||||
icmp:ip_set_bin_src(packet.iptobin(srcIP))
|
icmp:ip_set_bin_src(packet.iptobin(srcIP))
|
||||||
icmp:ip_set_bin_dst(packet.iptobin(dstIP))
|
icmp:ip_set_bin_dst(packet.iptobin(dstIP))
|
||||||
icmp:ip_count_checksum()
|
icmp:ip_count_checksum()
|
||||||
|
|
||||||
return icmp
|
return icmp
|
||||||
end
|
end
|
||||||
|
|
||||||
local broadcast_if = function(if_table,icmp_responders)
|
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"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
timeout = (timeout or 3) * 1000
|
timeout = (timeout or 3) * 1000
|
||||||
|
|
||||||
local ttl = nmap.get_ttl()
|
local ttl = nmap.get_ttl()
|
||||||
|
|
||||||
local data_length = nmap.get_payload_length()
|
local data_length = nmap.get_payload_length()
|
||||||
local sequence_number = 1
|
local sequence_number = 1
|
||||||
local destination_IP = "255.255.255.255"
|
local destination_IP = "255.255.255.255"
|
||||||
|
|
||||||
-- raw IPv4 socket
|
-- raw IPv4 socket
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
local try = nmap.new_try()
|
local try = nmap.new_try()
|
||||||
try = nmap.new_try(function() dnet:ethernet_close() end)
|
try = nmap.new_try(function() dnet:ethernet_close() end)
|
||||||
|
|
||||||
-- raw sniffing socket (icmp echoreply style)
|
-- raw sniffing socket (icmp echoreply style)
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
pcap:set_timeout(timeout)
|
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 ..
|
pcap:pcap_open(if_table.device, 104, false, "dst host ".. if_table.address ..
|
||||||
" and icmp[icmptype]==icmp-echoreply")
|
" and icmp[icmptype]==icmp-echoreply")
|
||||||
try(dnet:ethernet_open(if_table.device))
|
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
|
for i = 1, num_probes do
|
||||||
-- ICMP packet
|
-- ICMP packet
|
||||||
local icmp_id = openssl.rand_bytes(2)
|
local icmp_id = openssl.rand_bytes(2)
|
||||||
icmp_ids[icmp_id]=true
|
icmp_ids[icmp_id]=true
|
||||||
local icmp = icmp_packet( source_IP, destination_IP, ttl,
|
local icmp = icmp_packet( source_IP, destination_IP, ttl,
|
||||||
data_length, mtu, sequence_number, icmp_id)
|
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) )
|
try( dnet:ethernet_send(ethernet_icmp) )
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local status, plen, l2, l3data, _ = pcap:pcap_receive()
|
local status, plen, l2, l3data, _ = pcap:pcap_receive()
|
||||||
if not status then break end
|
if not status then break end
|
||||||
|
|
||||||
-- Do stuff with packet
|
-- Do stuff with packet
|
||||||
local icmpreply = packet.Packet:new(l3data,plen,false)
|
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
|
-- 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
|
-- 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)
|
local icmp_id = icmpreply:raw(icmpreply.icmp_offset+4,2)
|
||||||
if icmpreply:ip_parse() and icmp_ids[icmp_id] then
|
if icmpreply:ip_parse() and icmp_ids[icmp_id] then
|
||||||
if not icmp_responders[icmpreply.ip_src] then
|
if not icmp_responders[icmpreply.ip_src] then
|
||||||
-- [key = IP]=MAC
|
-- [key = IP]=MAC
|
||||||
local mac_pretty = stdnse.format_mac(l2:sub(7,12))
|
local mac_pretty = stdnse.format_mac(l2:sub(7,12))
|
||||||
icmp_responders[icmpreply.ip_src] = mac_pretty
|
icmp_responders[icmpreply.ip_src] = mac_pretty
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Erroneous ICMP packet received; Cannot parse IP header.")
|
stdnse.print_debug("Erroneous ICMP packet received; Cannot parse IP header.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
pcap:close()
|
pcap:close()
|
||||||
dnet:ethernet_close()
|
dnet:ethernet_close()
|
||||||
|
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
--get interface script-args, if any
|
--get interface script-args, if any
|
||||||
local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local interface_arg = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
local interface_opt = nmap.get_interface()
|
local interface_opt = nmap.get_interface()
|
||||||
|
|
||||||
-- interfaces list (decide which interfaces to broadcast on)
|
-- interfaces list (decide which interfaces to broadcast on)
|
||||||
local interfaces ={}
|
local interfaces ={}
|
||||||
if interface_opt or interface_arg then
|
if interface_opt or interface_arg then
|
||||||
-- single interface defined
|
-- single interface defined
|
||||||
local interface = interface_opt or interface_arg
|
local interface = interface_opt or interface_arg
|
||||||
local if_table = nmap.get_interface_info(interface)
|
local if_table = nmap.get_interface_info(interface)
|
||||||
if not if_table or not if_table.address or not if_table.link=="ethernet" then
|
if not if_table or not if_table.address or not if_table.link=="ethernet" then
|
||||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
table.insert(interfaces, if_table)
|
table.insert(interfaces, if_table)
|
||||||
else
|
else
|
||||||
local tmp_ifaces = nmap.list_interfaces()
|
local tmp_ifaces = nmap.list_interfaces()
|
||||||
for _, if_table in ipairs(tmp_ifaces) do
|
for _, if_table in ipairs(tmp_ifaces) do
|
||||||
if if_table.address and
|
if if_table.address and
|
||||||
if_table.link=="ethernet" and
|
if_table.link=="ethernet" and
|
||||||
if_table.address:match("%d+%.%d+%.%d+%.%d+") then
|
if_table.address:match("%d+%.%d+%.%d+%.%d+") then
|
||||||
table.insert(interfaces, if_table)
|
table.insert(interfaces, if_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #interfaces == 0 then
|
if #interfaces == 0 then
|
||||||
stdnse.print_debug("No interfaces found.")
|
stdnse.print_debug("No interfaces found.")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local icmp_responders={}
|
local icmp_responders={}
|
||||||
local threads ={}
|
local threads ={}
|
||||||
local condvar = nmap.condvar(icmp_responders)
|
local condvar = nmap.condvar(icmp_responders)
|
||||||
|
|
||||||
-- party time
|
-- party time
|
||||||
for _, if_table in ipairs(interfaces) do
|
for _, if_table in ipairs(interfaces) do
|
||||||
-- create a thread for each interface
|
-- create a thread for each interface
|
||||||
local co = stdnse.new_thread(broadcast_if, if_table, icmp_responders)
|
local co = stdnse.new_thread(broadcast_if, if_table, icmp_responders)
|
||||||
threads[co]=true
|
threads[co]=true
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
-- generate output
|
-- generate output
|
||||||
local output = tab.new()
|
local output = tab.new()
|
||||||
for ip_addr, mac_addr in pairs(icmp_responders) do
|
for ip_addr, mac_addr in pairs(icmp_responders) do
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
target.add(ip_addr)
|
target.add(ip_addr)
|
||||||
end
|
end
|
||||||
tab.addrow(output, "IP: " .. ip_addr, "MAC: " .. mac_addr)
|
tab.addrow(output, "IP: " .. ip_addr, "MAC: " .. mac_addr)
|
||||||
end
|
end
|
||||||
if #output > 0 then
|
if #output > 0 then
|
||||||
output = { tab.dump(output) }
|
output = { tab.dump(output) }
|
||||||
if not target.ALLOW_NEW_TARGETS then
|
if not target.ALLOW_NEW_TARGETS then
|
||||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -34,117 +34,117 @@ prerule = function() return ( nmap.address_family() == "inet6" ) end
|
|||||||
|
|
||||||
RIPng = {
|
RIPng = {
|
||||||
|
|
||||||
-- Supported RIPng commands
|
-- Supported RIPng commands
|
||||||
Command = {
|
Command = {
|
||||||
Request = 1,
|
Request = 1,
|
||||||
Response = 2,
|
Response = 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- Route table entry
|
-- Route table entry
|
||||||
RTE = {
|
RTE = {
|
||||||
|
|
||||||
-- Creates a new Route Table Entry
|
-- Creates a new Route Table Entry
|
||||||
-- @param prefix string containing the ipv6 route prefix
|
-- @param prefix string containing the ipv6 route prefix
|
||||||
-- @param tag number containing the route tag
|
-- @param tag number containing the route tag
|
||||||
-- @param prefix_len number containing the length in bits of the
|
-- @param prefix_len number containing the length in bits of the
|
||||||
-- signifcant part of the prefix
|
-- signifcant part of the prefix
|
||||||
-- @param metric number containing the current metric for the
|
-- @param metric number containing the current metric for the
|
||||||
-- destination
|
-- destination
|
||||||
new = function(self, prefix, tag, prefix_len, metric)
|
new = function(self, prefix, tag, prefix_len, metric)
|
||||||
local o = {
|
local o = {
|
||||||
prefix = prefix,
|
prefix = prefix,
|
||||||
tag = tag,
|
tag = tag,
|
||||||
prefix_len = prefix_len,
|
prefix_len = prefix_len,
|
||||||
metric = metric
|
metric = metric
|
||||||
}
|
}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Parses a byte string and creates an instance of RTE
|
-- Parses a byte string and creates an instance of RTE
|
||||||
-- @param data string of bytes
|
-- @param data string of bytes
|
||||||
-- @return rte instance of RTE
|
-- @return rte instance of RTE
|
||||||
parse = function(data)
|
parse = function(data)
|
||||||
local rte = RIPng.RTE:new()
|
local rte = RIPng.RTE:new()
|
||||||
local pos, ip
|
local pos, ip
|
||||||
|
|
||||||
pos, ip, rte.tag, rte.prefix_len, rte.metric = bin.unpack(">A16SCC", data)
|
pos, ip, rte.tag, rte.prefix_len, rte.metric = bin.unpack(">A16SCC", data)
|
||||||
ip = select(2, bin.unpack("B" .. #ip, ip))
|
ip = select(2, bin.unpack("B" .. #ip, ip))
|
||||||
rte.prefix = ipOps.bin_to_ip(ip)
|
rte.prefix = ipOps.bin_to_ip(ip)
|
||||||
return rte
|
return rte
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Converts a RTE instance to string
|
-- Converts a RTE instance to string
|
||||||
-- @return string of bytes to send to the server
|
-- @return string of bytes to send to the server
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
local ipstr = ipOps.ip_to_str(self.prefix)
|
local ipstr = ipOps.ip_to_str(self.prefix)
|
||||||
assert(16 == #ipstr, "Invalid IPv6 address encountered")
|
assert(16 == #ipstr, "Invalid IPv6 address encountered")
|
||||||
return bin.pack(">ASCC", ipstr, self.tag, self.prefix_len, self.metric)
|
return bin.pack(">ASCC", ipstr, self.tag, self.prefix_len, self.metric)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
-- The Request class contains functions to build a RIPv2 Request
|
-- The Request class contains functions to build a RIPv2 Request
|
||||||
Request = {
|
Request = {
|
||||||
|
|
||||||
-- Creates a new Request instance
|
-- Creates a new Request instance
|
||||||
--
|
--
|
||||||
-- @param command number containing the RIPv2 Command to use
|
-- @param command number containing the RIPv2 Command to use
|
||||||
-- @return o instance of request
|
-- @return o instance of request
|
||||||
new = function(self, entries)
|
new = function(self, entries)
|
||||||
local o = {
|
local o = {
|
||||||
command = 1,
|
command = 1,
|
||||||
version = 1,
|
version = 1,
|
||||||
entries = entries,
|
entries = entries,
|
||||||
}
|
}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Converts the whole request to a string
|
-- Converts the whole request to a string
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
local RESERVED = 0
|
local RESERVED = 0
|
||||||
local str = bin.pack(">CCS", self.command, self.version, RESERVED)
|
local str = bin.pack(">CCS", self.command, self.version, RESERVED)
|
||||||
for _, rte in ipairs(self.entries) do
|
for _, rte in ipairs(self.entries) do
|
||||||
str = str .. tostring(rte)
|
str = str .. tostring(rte)
|
||||||
end
|
end
|
||||||
return str
|
return str
|
||||||
end,
|
end,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
-- A RIPng Response
|
-- A RIPng Response
|
||||||
Response = {
|
Response = {
|
||||||
|
|
||||||
-- Creates a new Response instance
|
-- Creates a new Response instance
|
||||||
-- @return o new instance of Response
|
-- @return o new instance of Response
|
||||||
new = function(self)
|
new = function(self)
|
||||||
local o = { }
|
local o = { }
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Creates a new Response instance based on a string of bytes
|
-- Creates a new Response instance based on a string of bytes
|
||||||
-- @return resp new instance of Response
|
-- @return resp new instance of Response
|
||||||
parse = function(data)
|
parse = function(data)
|
||||||
local resp = RIPng.Response:new()
|
local resp = RIPng.Response:new()
|
||||||
local pos, _
|
local pos, _
|
||||||
|
|
||||||
pos, resp.command, resp.version, _ = bin.unpack(">CCS", data)
|
pos, resp.command, resp.version, _ = bin.unpack(">CCS", data)
|
||||||
resp.entries = {}
|
resp.entries = {}
|
||||||
while( pos < #data ) do
|
while( pos < #data ) do
|
||||||
local e = RIPng.RTE.parse(data:sub(pos))
|
local e = RIPng.RTE.parse(data:sub(pos))
|
||||||
table.insert(resp.entries, e)
|
table.insert(resp.entries, e)
|
||||||
pos = pos + 20
|
pos = pos + 20
|
||||||
end
|
end
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
@@ -152,65 +152,65 @@ local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
|||||||
-- Parses a RIPng response
|
-- Parses a RIPng response
|
||||||
-- @return ret string containing the routing table
|
-- @return ret string containing the routing table
|
||||||
local function parse_response(resp)
|
local function parse_response(resp)
|
||||||
local next_hop
|
local next_hop
|
||||||
local result = tab.new(3)
|
local result = tab.new(3)
|
||||||
tab.addrow(result, "route", "metric", "next hop")
|
tab.addrow(result, "route", "metric", "next hop")
|
||||||
for _, rte in pairs(resp.entries or {}) do
|
for _, rte in pairs(resp.entries or {}) do
|
||||||
-- next hop information is specified in a separate RTE according to
|
-- next hop information is specified in a separate RTE according to
|
||||||
-- RFC 2080 section 2.1.1
|
-- RFC 2080 section 2.1.1
|
||||||
if ( 0xFF == rte.metric ) then
|
if ( 0xFF == rte.metric ) then
|
||||||
next_hop = rte.prefix
|
next_hop = rte.prefix
|
||||||
else
|
else
|
||||||
tab.addrow(result, ("%s/%d"):format(rte.prefix, rte.prefix_len), rte.metric, next_hop or "")
|
tab.addrow(result, ("%s/%d"):format(rte.prefix, rte.prefix_len), rte.metric, next_hop or "")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tab.dump(result)
|
return tab.dump(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local req = RIPng.Request:new( { RIPng.RTE:new("0::", 0, 0, 16) } )
|
local req = RIPng.Request:new( { RIPng.RTE:new("0::", 0, 0, 16) } )
|
||||||
local host, port = "FF02::9", { number = 521, protocol = "udp" }
|
local host, port = "FF02::9", { number = 521, protocol = "udp" }
|
||||||
local iface = nmap.get_interface()
|
local iface = nmap.get_interface()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||||
timeout = (timeout or 5) * 1000
|
timeout = (timeout or 5) * 1000
|
||||||
|
|
||||||
local sock = nmap.new_socket("udp")
|
local sock = nmap.new_socket("udp")
|
||||||
sock:bind(nil, 521)
|
sock:bind(nil, 521)
|
||||||
sock:set_timeout(timeout)
|
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?
|
-- do we need to add the interface name to the address?
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
if ( not(iface) ) then
|
if ( not(iface) ) then
|
||||||
return fail("Couldn't determine what interface to use, try supplying it with -e")
|
return fail("Couldn't determine what interface to use, try supplying it with -e")
|
||||||
end
|
end
|
||||||
status = sock:sendto(host .. "%" .. iface, port, tostring(req))
|
status = sock:sendto(host .. "%" .. iface, port, tostring(req))
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail("Failed to send request to server")
|
return fail("Failed to send request to server")
|
||||||
end
|
end
|
||||||
|
|
||||||
local responses = {}
|
local responses = {}
|
||||||
while(true) do
|
while(true) do
|
||||||
local status, data = sock:receive()
|
local status, data = sock:receive()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
local status, _, _, rhost = sock:get_info()
|
local status, _, _, rhost = sock:get_info()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
rhost = "unknown"
|
rhost = "unknown"
|
||||||
end
|
end
|
||||||
responses[rhost] = RIPng.Response.parse(data)
|
responses[rhost] = RIPng.Response.parse(data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for ip, resp in pairs(responses) do
|
for ip, resp in pairs(responses) do
|
||||||
stdnse.print_debug(ip, resp)
|
stdnse.print_debug(ip, resp)
|
||||||
table.insert(result, { name = ip, parse_response(resp) } )
|
table.insert(result, { name = ip, parse_response(resp) } )
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
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 arg_getwpad= stdnse.get_script_args(SCRIPT_NAME .. ".getwpad")
|
||||||
|
|
||||||
local function createRequestList(req_list)
|
local function createRequestList(req_list)
|
||||||
local output = ""
|
local output = ""
|
||||||
for _, v in ipairs(req_list) do
|
for _, v in ipairs(req_list) do
|
||||||
output = output .. string.char(v)
|
output = output .. string.char(v)
|
||||||
end
|
end
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -58,175 +58,175 @@ end
|
|||||||
-- @param up string containing the interface status to filter
|
-- @param up string containing the interface status to filter
|
||||||
-- @return result table containing the matching interfaces
|
-- @return result table containing the matching interfaces
|
||||||
local function getInterfaces(link, up)
|
local function getInterfaces(link, up)
|
||||||
if( not(nmap.list_interfaces) ) then return end
|
if( not(nmap.list_interfaces) ) then return end
|
||||||
local interfaces, err = nmap.list_interfaces()
|
local interfaces, err = nmap.list_interfaces()
|
||||||
local result
|
local result
|
||||||
if ( not(err) ) then
|
if ( not(err) ) then
|
||||||
for _, iface in ipairs(interfaces) do
|
for _, iface in ipairs(interfaces) do
|
||||||
if ( iface.link == link and iface.up == up ) then
|
if ( iface.link == link and iface.up == up ) then
|
||||||
result = result or {}
|
result = result or {}
|
||||||
result[iface.device] = true
|
result[iface.device] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local function parseDHCPResponse(response)
|
local function parseDHCPResponse(response)
|
||||||
for _, v in ipairs(response.options) do
|
for _, v in ipairs(response.options) do
|
||||||
if ( "WPAD" == v.name ) then
|
if ( "WPAD" == v.name ) then
|
||||||
return true, v.value
|
return true, v.value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getWPAD(u)
|
local function getWPAD(u)
|
||||||
local u_parsed = url.parse(u)
|
local u_parsed = url.parse(u)
|
||||||
|
|
||||||
if ( not(u_parsed) ) then
|
if ( not(u_parsed) ) then
|
||||||
return false, ("Failed to parse url: %s"):format(u)
|
return false, ("Failed to parse url: %s"):format(u)
|
||||||
end
|
end
|
||||||
|
|
||||||
local response = http.get(u_parsed.host, u_parsed.port or 80, u_parsed.path)
|
local response = http.get(u_parsed.host, u_parsed.port or 80, u_parsed.path)
|
||||||
if ( response and response.status == 200 ) then
|
if ( response and response.status == 200 ) then
|
||||||
return true, response.body
|
return true, response.body
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
local function parseWPAD(wpad)
|
local function parseWPAD(wpad)
|
||||||
local proxies = {}
|
local proxies = {}
|
||||||
for proxy in wpad:gmatch("PROXY%s*([^\";%s]*)") do
|
for proxy in wpad:gmatch("PROXY%s*([^\";%s]*)") do
|
||||||
table.insert(proxies, proxy)
|
table.insert(proxies, proxy)
|
||||||
end
|
end
|
||||||
return proxies
|
return proxies
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dnsDiscover()
|
local function dnsDiscover()
|
||||||
|
|
||||||
-- tries to discover WPAD for all domains and sub-domains
|
-- tries to discover WPAD for all domains and sub-domains
|
||||||
local function enumWPADNames(domain)
|
local function enumWPADNames(domain)
|
||||||
local d = domain
|
local d = domain
|
||||||
-- reduce domain until we only have a single dot left
|
-- reduce domain until we only have a single dot left
|
||||||
-- there is a security problem in querying for wpad.tld like eg
|
-- 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
|
-- 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.
|
-- account for domains with tld's containing two parts e.g. co.uk.
|
||||||
-- However, as the script just attempts to download and parse the
|
-- However, as the script just attempts to download and parse the
|
||||||
-- proxy values in the WPAD there should be no real harm here.
|
-- proxy values in the WPAD there should be no real harm here.
|
||||||
repeat
|
repeat
|
||||||
local name = ("wpad.%s"):format(d)
|
local name = ("wpad.%s"):format(d)
|
||||||
d = d:match("^[^%.]-%.(.*)$")
|
d = d:match("^[^%.]-%.(.*)$")
|
||||||
local status, response = dns.query(name, { dtype = 'A', retAll = true })
|
local status, response = dns.query(name, { dtype = 'A', retAll = true })
|
||||||
|
|
||||||
-- get the first entry and return
|
-- get the first entry and return
|
||||||
if ( status and response[1] ) then
|
if ( status and response[1] ) then
|
||||||
return true, { name = name, ip = response[1] }
|
return true, { name = name, ip = response[1] }
|
||||||
end
|
end
|
||||||
until( not(d) or not(d:match("%.")) )
|
until( not(d) or not(d:match("%.")) )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- first try a domain if it was supplied
|
-- first try a domain if it was supplied
|
||||||
if ( arg_domain ) then
|
if ( arg_domain ) then
|
||||||
local status, response = enumWPADNames(arg_domain)
|
local status, response = enumWPADNames(arg_domain)
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
return status, response
|
return status, response
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- if no domain was supplied, attempt to reverse lookup every ip on each
|
-- 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
|
-- interface to find our FQDN hostname, once we do, try to query for WPAD
|
||||||
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
||||||
local iface, err = nmap.get_interface_info(i)
|
local iface, err = nmap.get_interface_info(i)
|
||||||
if ( iface ) then
|
if ( iface ) then
|
||||||
local status, response = dns.query( dns.reverse(iface.address), { dtype = 'PTR', retAll = true } )
|
local status, response = dns.query( dns.reverse(iface.address), { dtype = 'PTR', retAll = true } )
|
||||||
|
|
||||||
-- did we get a name back from dns?
|
-- did we get a name back from dns?
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local domains = {}
|
local domains = {}
|
||||||
for _, name in ipairs(response) do
|
for _, name in ipairs(response) do
|
||||||
-- first get all unique domain names
|
-- first get all unique domain names
|
||||||
if ( not(name:match("in%-addr.arpa$")) ) then
|
if ( not(name:match("in%-addr.arpa$")) ) then
|
||||||
local domain = name:match("^[^%.]-%.(.*)$")
|
local domain = name:match("^[^%.]-%.(.*)$")
|
||||||
domains[domain] = true
|
domains[domain] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- attempt to discover the ip for WPAD in all domains
|
-- attempt to discover the ip for WPAD in all domains
|
||||||
-- each domain is processed and reduced and ones the first
|
-- each domain is processed and reduced and ones the first
|
||||||
-- match is received it returns an IP
|
-- match is received it returns an IP
|
||||||
for domain in pairs(domains) do
|
for domain in pairs(domains) do
|
||||||
status, response = enumWPADNames(domain)
|
status, response = enumWPADNames(domain)
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
return true, response
|
return true, response
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
local function dhcpDiscover()
|
local function dhcpDiscover()
|
||||||
|
|
||||||
-- send a DHCP discover on all ethernet interfaces that are up
|
-- send a DHCP discover on all ethernet interfaces that are up
|
||||||
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
for i in pairs(getInterfaces("ethernet", "up") or {}) do
|
||||||
local iface, err = nmap.get_interface_info(i)
|
local iface, err = nmap.get_interface_info(i)
|
||||||
if ( iface ) then
|
if ( iface ) then
|
||||||
local req_list = createRequestList( { 1, 15, 3, 6, 44, 46, 47, 31, 33, 249, 43, 252 } )
|
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 } )
|
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 we got a response, we're happy and don't need to continue
|
||||||
if (status) then
|
if (status) then
|
||||||
return status, response
|
return status, response
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local status, response, wpad
|
local status, response, wpad
|
||||||
|
|
||||||
if ( arg_nodhcp and arg_nodns ) then
|
if ( arg_nodhcp and arg_nodns ) then
|
||||||
return "\n ERROR: Both nodns and nodhcp arguments were supplied"
|
return "\n ERROR: Both nodns and nodhcp arguments were supplied"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( nmap.is_privileged() and not(arg_nodhcp) ) then
|
if ( nmap.is_privileged() and not(arg_nodhcp) ) then
|
||||||
status, response = dhcpDiscover()
|
status, response = dhcpDiscover()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
status, wpad = parseDHCPResponse(response)
|
status, wpad = parseDHCPResponse(response)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if the DHCP did not get a result, fallback to DNS
|
-- if the DHCP did not get a result, fallback to DNS
|
||||||
if (not(status) and not(arg_nodns) ) then
|
if (not(status) and not(arg_nodns) ) then
|
||||||
status, response = dnsDiscover()
|
status, response = dnsDiscover()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
local services = "DNS" .. ( nmap.is_privileged() and "/DHCP" or "" )
|
local services = "DNS" .. ( nmap.is_privileged() and "/DHCP" or "" )
|
||||||
return ("\n ERROR: Could not find WPAD using %s"):format(services)
|
return ("\n ERROR: Could not find WPAD using %s"):format(services)
|
||||||
end
|
end
|
||||||
wpad = ("http://%s/wpad.dat"):format( response.name )
|
wpad = ("http://%s/wpad.dat"):format( response.name )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
status, response = getWPAD(wpad)
|
status, response = getWPAD(wpad)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return status, response
|
return status, response
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
@@ -78,56 +78,56 @@ local TIMED_MULTIPLIER = 1.0
|
|||||||
-- This list is the first 50 entries of
|
-- This list is the first 50 entries of
|
||||||
-- http://s3.amazonaws.com/alexa-static/top-1m.csv.zip on 2013-08-08.
|
-- http://s3.amazonaws.com/alexa-static/top-1m.csv.zip on 2013-08-08.
|
||||||
local ALEXA_DOMAINS = {
|
local ALEXA_DOMAINS = {
|
||||||
"google.com",
|
"google.com",
|
||||||
"facebook.com",
|
"facebook.com",
|
||||||
"youtube.com",
|
"youtube.com",
|
||||||
"yahoo.com",
|
"yahoo.com",
|
||||||
"baidu.com",
|
"baidu.com",
|
||||||
"wikipedia.org",
|
"wikipedia.org",
|
||||||
"amazon.com",
|
"amazon.com",
|
||||||
"qq.com",
|
"qq.com",
|
||||||
"live.com",
|
"live.com",
|
||||||
"linkedin.com",
|
"linkedin.com",
|
||||||
"twitter.com",
|
"twitter.com",
|
||||||
"blogspot.com",
|
"blogspot.com",
|
||||||
"taobao.com",
|
"taobao.com",
|
||||||
"google.co.in",
|
"google.co.in",
|
||||||
"bing.com",
|
"bing.com",
|
||||||
"yahoo.co.jp",
|
"yahoo.co.jp",
|
||||||
"yandex.ru",
|
"yandex.ru",
|
||||||
"wordpress.com",
|
"wordpress.com",
|
||||||
"sina.com.cn",
|
"sina.com.cn",
|
||||||
"vk.com",
|
"vk.com",
|
||||||
"ebay.com",
|
"ebay.com",
|
||||||
"google.de",
|
"google.de",
|
||||||
"tumblr.com",
|
"tumblr.com",
|
||||||
"msn.com",
|
"msn.com",
|
||||||
"google.co.uk",
|
"google.co.uk",
|
||||||
"googleusercontent.com",
|
"googleusercontent.com",
|
||||||
"ask.com",
|
"ask.com",
|
||||||
"mail.ru",
|
"mail.ru",
|
||||||
"google.com.br",
|
"google.com.br",
|
||||||
"163.com",
|
"163.com",
|
||||||
"google.fr",
|
"google.fr",
|
||||||
"pinterest.com",
|
"pinterest.com",
|
||||||
"google.com.hk",
|
"google.com.hk",
|
||||||
"hao123.com",
|
"hao123.com",
|
||||||
"microsoft.com",
|
"microsoft.com",
|
||||||
"google.co.jp",
|
"google.co.jp",
|
||||||
"xvideos.com",
|
"xvideos.com",
|
||||||
"google.ru",
|
"google.ru",
|
||||||
"weibo.com",
|
"weibo.com",
|
||||||
"craigslist.org",
|
"craigslist.org",
|
||||||
"paypal.com",
|
"paypal.com",
|
||||||
"instagram.com",
|
"instagram.com",
|
||||||
"amazon.co.jp",
|
"amazon.co.jp",
|
||||||
"google.it",
|
"google.it",
|
||||||
"imdb.com",
|
"imdb.com",
|
||||||
"blogger.com",
|
"blogger.com",
|
||||||
"google.es",
|
"google.es",
|
||||||
"apple.com",
|
"apple.com",
|
||||||
"conduit.com",
|
"conduit.com",
|
||||||
"sohu.com",
|
"sohu.com",
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Construct the default list of domains.
|
-- Construct the default list of domains.
|
||||||
|
|||||||
@@ -63,149 +63,149 @@ postrule = function() return true end
|
|||||||
|
|
||||||
local function processSSLCerts(tab)
|
local function processSSLCerts(tab)
|
||||||
|
|
||||||
-- Handle SSL-certificates
|
-- Handle SSL-certificates
|
||||||
-- We create a new table using the SHA1 digest as index
|
-- We create a new table using the SHA1 digest as index
|
||||||
local ssl_certs = {}
|
local ssl_certs = {}
|
||||||
for host, v in pairs(tab) do
|
for host, v in pairs(tab) do
|
||||||
for port, sha1 in pairs(v) do
|
for port, sha1 in pairs(v) do
|
||||||
ssl_certs[sha1] = ssl_certs[sha1] or {}
|
ssl_certs[sha1] = ssl_certs[sha1] or {}
|
||||||
if ( not stdnse.contains(ssl_certs[sha1], host.ip) ) then
|
if ( not stdnse.contains(ssl_certs[sha1], host.ip) ) then
|
||||||
table.insert(ssl_certs[sha1], host.ip)
|
table.insert(ssl_certs[sha1], host.ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local results = {}
|
local results = {}
|
||||||
for sha1, hosts in pairs(ssl_certs) do
|
for sha1, hosts in pairs(ssl_certs) do
|
||||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||||
if ( #hosts > 1 ) then
|
if ( #hosts > 1 ) then
|
||||||
table.insert(results, { name = ("Certficate (%s)"):format(sha1), hosts } )
|
table.insert(results, { name = ("Certficate (%s)"):format(sha1), hosts } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processSSHKeys(tab)
|
local function processSSHKeys(tab)
|
||||||
|
|
||||||
local hostkeys = {}
|
local hostkeys = {}
|
||||||
|
|
||||||
-- create a reverse mapping key_fingerprint -> host(s)
|
-- create a reverse mapping key_fingerprint -> host(s)
|
||||||
for ip, keys in pairs(tab) do
|
for ip, keys in pairs(tab) do
|
||||||
for _, key in ipairs(keys) do
|
for _, key in ipairs(keys) do
|
||||||
local fp = ssh1.fingerprint_hex(key.fingerprint, key.algorithm, key.bits)
|
local fp = ssh1.fingerprint_hex(key.fingerprint, key.algorithm, key.bits)
|
||||||
if not hostkeys[fp] then
|
if not hostkeys[fp] then
|
||||||
hostkeys[fp] = {}
|
hostkeys[fp] = {}
|
||||||
end
|
end
|
||||||
-- discard duplicate IPs
|
-- discard duplicate IPs
|
||||||
if not stdnse.contains(hostkeys[fp], ip) then
|
if not stdnse.contains(hostkeys[fp], ip) then
|
||||||
table.insert(hostkeys[fp], ip)
|
table.insert(hostkeys[fp], ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- look for hosts using the same hostkey
|
-- look for hosts using the same hostkey
|
||||||
local results = {}
|
local results = {}
|
||||||
for key, hosts in pairs(hostkeys) do
|
for key, hosts in pairs(hostkeys) do
|
||||||
if #hostkeys[key] > 1 then
|
if #hostkeys[key] > 1 then
|
||||||
table.sort(hostkeys[key], function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
table.sort(hostkeys[key], function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||||
local str = 'Key ' .. key .. ':'
|
local str = 'Key ' .. key .. ':'
|
||||||
table.insert( results, { name = str, hostkeys[key] } )
|
table.insert( results, { name = str, hostkeys[key] } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processNBStat(tab)
|
local function processNBStat(tab)
|
||||||
|
|
||||||
local results, mac_table, name_table = {}, {}, {}
|
local results, mac_table, name_table = {}, {}, {}
|
||||||
for host, v in pairs(tab) do
|
for host, v in pairs(tab) do
|
||||||
mac_table[v.mac] = mac_table[v.mac] or {}
|
mac_table[v.mac] = mac_table[v.mac] or {}
|
||||||
if ( not(stdnse.contains(mac_table[v.mac], host.ip)) ) then
|
if ( not(stdnse.contains(mac_table[v.mac], host.ip)) ) then
|
||||||
table.insert(mac_table[v.mac], host.ip)
|
table.insert(mac_table[v.mac], host.ip)
|
||||||
end
|
end
|
||||||
|
|
||||||
name_table[v.server_name] = name_table[v.server_name] or {}
|
name_table[v.server_name] = name_table[v.server_name] or {}
|
||||||
if ( not(stdnse.contains(name_table[v.server_name], host.ip)) ) then
|
if ( not(stdnse.contains(name_table[v.server_name], host.ip)) ) then
|
||||||
table.insert(name_table[v.server_name], host.ip)
|
table.insert(name_table[v.server_name], host.ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for mac, hosts in pairs(mac_table) do
|
for mac, hosts in pairs(mac_table) do
|
||||||
if ( #hosts > 1 ) then
|
if ( #hosts > 1 ) then
|
||||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||||
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for srvname, hosts in pairs(name_table) do
|
for srvname, hosts in pairs(name_table) do
|
||||||
if ( #hosts > 1 ) then
|
if ( #hosts > 1 ) then
|
||||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||||
table.insert(results, { name = ("Server Name: %s"):format(srvname), hosts })
|
table.insert(results, { name = ("Server Name: %s"):format(srvname), hosts })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processMAC(tab)
|
local function processMAC(tab)
|
||||||
|
|
||||||
local mac
|
local mac
|
||||||
local mac_table = {}
|
local mac_table = {}
|
||||||
|
|
||||||
for host in pairs(tab) do
|
for host in pairs(tab) do
|
||||||
if ( host.mac_addr ) then
|
if ( host.mac_addr ) then
|
||||||
mac = stdnse.format_mac(host.mac_addr)
|
mac = stdnse.format_mac(host.mac_addr)
|
||||||
mac_table[mac] = mac_table[mac] or {}
|
mac_table[mac] = mac_table[mac] or {}
|
||||||
if ( not(stdnse.contains(mac_table[mac], host.ip)) ) then
|
if ( not(stdnse.contains(mac_table[mac], host.ip)) ) then
|
||||||
table.insert(mac_table[mac], host.ip)
|
table.insert(mac_table[mac], host.ip)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local results = {}
|
local results = {}
|
||||||
for mac, hosts in pairs(mac_table) do
|
for mac, hosts in pairs(mac_table) do
|
||||||
if ( #hosts > 1 ) then
|
if ( #hosts > 1 ) then
|
||||||
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
||||||
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
postaction = function()
|
postaction = function()
|
||||||
|
|
||||||
local handlers = {
|
local handlers = {
|
||||||
['ssl-cert'] = { func = processSSLCerts, name = "SSL" },
|
['ssl-cert'] = { func = processSSLCerts, name = "SSL" },
|
||||||
['sshhostkey'] = { func = processSSHKeys, name = "SSH" },
|
['sshhostkey'] = { func = processSSHKeys, name = "SSH" },
|
||||||
['nbstat'] = { func = processNBStat, name = "Netbios" },
|
['nbstat'] = { func = processNBStat, name = "Netbios" },
|
||||||
['mac'] = { func = processMAC, name = "ARP" }
|
['mac'] = { func = processMAC, name = "ARP" }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- temporary re-allocation code for SSH keys
|
-- temporary re-allocation code for SSH keys
|
||||||
for k, v in pairs(nmap.registry.sshhostkey or {}) do
|
for k, v in pairs(nmap.registry.sshhostkey or {}) do
|
||||||
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
||||||
nmap.registry['duplicates']['sshhostkey'] = nmap.registry['duplicates']['sshhostkey'] or {}
|
nmap.registry['duplicates']['sshhostkey'] = nmap.registry['duplicates']['sshhostkey'] or {}
|
||||||
nmap.registry['duplicates']['sshhostkey'][k] = v
|
nmap.registry['duplicates']['sshhostkey'][k] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(nmap.registry['duplicates']) ) then
|
if ( not(nmap.registry['duplicates']) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local results = {}
|
local results = {}
|
||||||
for key, handler in pairs(handlers) do
|
for key, handler in pairs(handlers) do
|
||||||
if ( nmap.registry['duplicates'][key] ) then
|
if ( nmap.registry['duplicates'][key] ) then
|
||||||
local result_part = handler.func( nmap.registry['duplicates'][key] )
|
local result_part = handler.func( nmap.registry['duplicates'][key] )
|
||||||
if ( result_part and #result_part > 0 ) then
|
if ( result_part and #result_part > 0 ) then
|
||||||
table.insert(results, { name = handler.name, result_part } )
|
table.insert(results, { name = handler.name, result_part } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, results)
|
return stdnse.format_output(true, results)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we have no real action in here. In essence we move information from the
|
-- we have no real action in here. In essence we move information from the
|
||||||
@@ -213,25 +213,25 @@ end
|
|||||||
-- it when we need it.
|
-- it when we need it.
|
||||||
hostaction = function(host)
|
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
|
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'] = 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] = nmap.registry['duplicates']['ssl-cert'][host] or {}
|
||||||
nmap.registry['duplicates']['ssl-cert'][host][port] = stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
|
nmap.registry['duplicates']['ssl-cert'][host][port] = stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( host.registry['nbstat'] ) then
|
if ( host.registry['nbstat'] ) then
|
||||||
nmap.registry['duplicates']['nbstat'] = nmap.registry['duplicates']['nbstat'] or {}
|
nmap.registry['duplicates']['nbstat'] = nmap.registry['duplicates']['nbstat'] or {}
|
||||||
nmap.registry['duplicates']['nbstat'][host] = host.registry['nbstat']
|
nmap.registry['duplicates']['nbstat'][host] = host.registry['nbstat']
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( host.mac_addr_src ) then
|
if ( host.mac_addr_src ) then
|
||||||
nmap.registry['duplicates']['mac'] = nmap.registry['duplicates']['mac'] or {}
|
nmap.registry['duplicates']['mac'] = nmap.registry['duplicates']['mac'] or {}
|
||||||
nmap.registry['duplicates']['mac'][host] = true
|
nmap.registry['duplicates']['mac'][host] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local Actions = {
|
local Actions = {
|
||||||
|
|||||||
@@ -36,159 +36,159 @@ categories = { "broadcast", "safe" }
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
return nmap.is_privileged()
|
return nmap.is_privileged()
|
||||||
end
|
end
|
||||||
|
|
||||||
local default_scan = {
|
local default_scan = {
|
||||||
eap.eap_t.TLS,
|
eap.eap_t.TLS,
|
||||||
eap.eap_t.TTLS,
|
eap.eap_t.TTLS,
|
||||||
eap.eap_t.PEAP,
|
eap.eap_t.PEAP,
|
||||||
eap.eap_t.MSCHAP,
|
eap.eap_t.MSCHAP,
|
||||||
}
|
}
|
||||||
|
|
||||||
local UNKNOWN = "unknown"
|
local UNKNOWN = "unknown"
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local arg_interface = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
local arg_identity = stdnse.get_script_args(SCRIPT_NAME .. ".identity")
|
local arg_identity = stdnse.get_script_args(SCRIPT_NAME .. ".identity")
|
||||||
local arg_scan = stdnse.get_script_args(SCRIPT_NAME .. ".scan")
|
local arg_scan = stdnse.get_script_args(SCRIPT_NAME .. ".scan")
|
||||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
local iface
|
local iface
|
||||||
|
|
||||||
-- trying with provided interface name
|
-- trying with provided interface name
|
||||||
if arg_interface then
|
if arg_interface then
|
||||||
iface = nmap.get_interface_info(arg_interface)
|
iface = nmap.get_interface_info(arg_interface)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- trying with default nmap interface
|
-- trying with default nmap interface
|
||||||
if not iface then
|
if not iface then
|
||||||
local iname = nmap.get_interface()
|
local iname = nmap.get_interface()
|
||||||
if iname then
|
if iname then
|
||||||
iface = nmap.get_interface_info(iname)
|
iface = nmap.get_interface_info(iname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- failed
|
-- failed
|
||||||
if not iface then
|
if not iface then
|
||||||
return "please specify an interface with -e"
|
return "please specify an interface with -e"
|
||||||
end
|
end
|
||||||
stdnse.print_debug(1, "iface: %s", iface.device)
|
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()
|
local pcap = nmap.new_socket()
|
||||||
pcap:pcap_open(iface.device, 512, true, "ether proto 0x888e")
|
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
|
if arg_identity then
|
||||||
identity.name = tostring(arg_identity)
|
identity.name = tostring(arg_identity)
|
||||||
end
|
end
|
||||||
|
|
||||||
local scan
|
local scan
|
||||||
if arg_scan == nil or type(arg_scan) ~= "table" or #arg_scan == 0 then
|
if arg_scan == nil or type(arg_scan) ~= "table" or #arg_scan == 0 then
|
||||||
scan = default_scan
|
scan = default_scan
|
||||||
else
|
else
|
||||||
scan = arg_scan
|
scan = arg_scan
|
||||||
end
|
end
|
||||||
|
|
||||||
local valid = false
|
local valid = false
|
||||||
for i,v in ipairs(scan) do
|
for i,v in ipairs(scan) do
|
||||||
v = tonumber(v)
|
v = tonumber(v)
|
||||||
if v ~= nil and v < 256 and v > 3 then
|
if v ~= nil and v < 256 and v > 3 then
|
||||||
stdnse.print_debug(1, "selected: %s", eap.eap_str[v] or "unassigned" )
|
stdnse.print_debug(1, "selected: %s", eap.eap_str[v] or "unassigned" )
|
||||||
identity.auth[v] = UNKNOWN
|
identity.auth[v] = UNKNOWN
|
||||||
valid = true
|
valid = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not valid then
|
if not valid then
|
||||||
return "no valid scan methods provided"
|
return "no valid scan methods provided"
|
||||||
end
|
end
|
||||||
|
|
||||||
local tried_all = false
|
local tried_all = false
|
||||||
|
|
||||||
local start_time = nmap.clock_ms()
|
local start_time = nmap.clock_ms()
|
||||||
eap.send_start(iface)
|
eap.send_start(iface)
|
||||||
|
|
||||||
while(nmap.clock_ms() - start_time < timeout) and not tried_all do
|
while(nmap.clock_ms() - start_time < timeout) and not tried_all do
|
||||||
local status, plen, l2_data, l3_data, time = pcap:pcap_receive()
|
local status, plen, l2_data, l3_data, time = pcap:pcap_receive()
|
||||||
if (status) then
|
if (status) then
|
||||||
stdnse.print_debug(2, "packet size: 0x%x", plen )
|
stdnse.print_debug(2, "packet size: 0x%x", plen )
|
||||||
local packet = eap.parse(l2_data .. l3_data)
|
local packet = eap.parse(l2_data .. l3_data)
|
||||||
|
|
||||||
if packet then
|
if packet then
|
||||||
stdnse.print_debug(2, "packet valid")
|
stdnse.print_debug(2, "packet valid")
|
||||||
|
|
||||||
-- respond to identity requests, using the same session id
|
-- 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
|
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)
|
stdnse.print_debug(1, "server identity: %s",packet.eap.body.identity)
|
||||||
eap.send_identity_response(iface, packet.eap.id, identity.name)
|
eap.send_identity_response(iface, packet.eap.id, identity.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- respond with NAK to every auth request to enumerate them until we get a failure
|
-- 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
|
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])
|
stdnse.print_debug(1, "auth request: %s",eap.eap_str[packet.eap.type])
|
||||||
identity.auth[packet.eap.type] = true
|
identity.auth[packet.eap.type] = true
|
||||||
|
|
||||||
identity.probe = -1
|
identity.probe = -1
|
||||||
for i,v in pairs(identity.auth) do
|
for i,v in pairs(identity.auth) do
|
||||||
stdnse.print_debug(1, "identity.auth: %d %s",i,tostring(v))
|
stdnse.print_debug(1, "identity.auth: %d %s",i,tostring(v))
|
||||||
if v == UNKNOWN then
|
if v == UNKNOWN then
|
||||||
identity.probe = i
|
identity.probe = i
|
||||||
eap.send_nak_response(iface, packet.eap.id, i)
|
eap.send_nak_response(iface, packet.eap.id, i)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if identity.probe == -1 then tried_all = true end
|
if identity.probe == -1 then tried_all = true end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- retry on failure
|
-- retry on failure
|
||||||
if packet.eap.code == eap.code_t.FAILURE then
|
if packet.eap.code == eap.code_t.FAILURE then
|
||||||
stdnse.print_debug(1, "auth failure")
|
stdnse.print_debug(1, "auth failure")
|
||||||
identity.auth[identity.probe] = false
|
identity.auth[identity.probe] = false
|
||||||
|
|
||||||
-- don't give up at the first failure!
|
-- don't give up at the first failure!
|
||||||
-- mac spoofing to avoid to wait too much
|
-- mac spoofing to avoid to wait too much
|
||||||
local d = string.byte(iface.mac,6)
|
local d = string.byte(iface.mac,6)
|
||||||
d = (d + 1) % 256
|
d = (d + 1) % 256
|
||||||
iface.mac = iface.mac:sub(1,5) .. bin.pack("C",d)
|
iface.mac = iface.mac:sub(1,5) .. bin.pack("C",d)
|
||||||
|
|
||||||
tried_all = true
|
tried_all = true
|
||||||
for i,v in pairs(identity.auth) do
|
for i,v in pairs(identity.auth) do
|
||||||
if v == UNKNOWN then
|
if v == UNKNOWN then
|
||||||
tried_all = false
|
tried_all = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not tried_all then
|
if not tried_all then
|
||||||
eap.send_start(iface)
|
eap.send_start(iface)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "packet invalid! wrong filter?")
|
stdnse.print_debug(1, "packet invalid! wrong filter?")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local results = { ["name"] = ("Available authentication methods with identity=\"%s\" on interface %s"):format(identity.name, iface.device) }
|
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
|
for i,v in pairs(identity.auth) do
|
||||||
if v== true then
|
if v== true then
|
||||||
table.insert(results, 1, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
table.insert(results, 1, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
||||||
else
|
else
|
||||||
table.insert(results, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
table.insert(results, ("%-8s %s"):format(tostring(v), eap.eap_str[i] or "unassigned" ))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for i,v in ipairs(results) do
|
for i,v in ipairs(results) do
|
||||||
stdnse.print_debug(1, "%s", tostring(v))
|
stdnse.print_debug(1, "%s", tostring(v))
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, results)
|
return stdnse.format_output(true, results)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -43,234 +43,234 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|||||||
categories = {"vuln", "intrusive"}
|
categories = {"vuln", "intrusive"}
|
||||||
|
|
||||||
ftp_helper = {
|
ftp_helper = {
|
||||||
should_run = function(host, helperport)
|
should_run = function(host, helperport)
|
||||||
local helperport = helperport or 21
|
local helperport = helperport or 21
|
||||||
-- IPv4 and IPv6 are supported
|
-- IPv4 and IPv6 are supported
|
||||||
if nmap.address_family() ~= 'inet' and nmap.address_family() ~= 'inet6' then
|
if nmap.address_family() ~= 'inet' and nmap.address_family() ~= 'inet6' then
|
||||||
return false
|
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
|
||||||
|
end
|
||||||
|
|
||||||
-- Test if helper port is open
|
-- Get ethernet values
|
||||||
local testsock = nmap.new_socket()
|
local f = packet.Frame:new(l2data)
|
||||||
testsock:set_timeout(1000)
|
f:ether_parse()
|
||||||
local status, _ = testsock:connect(host.ip, helperport)
|
|
||||||
testsock:close()
|
local p = packet.Packet:new(l3data, #l3data)
|
||||||
if not status then
|
if isIp4 then
|
||||||
stdnse.print_debug("%s Unable to connect to %s helper port.", SCRIPT_NAME, helperport)
|
if not p:ip_parse() then
|
||||||
return false
|
-- An error happened
|
||||||
|
stdnse.print_debug("%s Couldn't parse IPv4 sniffed packet.", SCRIPT_NAME)
|
||||||
|
sniffer:pcap_close()
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
return true
|
else
|
||||||
end,
|
if not p:ip6_parse() then
|
||||||
|
-- An error happened
|
||||||
attack = function(host, helperport, targetport)
|
stdnse.print_debug("%s Couldn't parse IPv6 sniffed packet.", SCRIPT_NAME)
|
||||||
local ethertype, payload
|
sniffer:pcap_close()
|
||||||
local isIp4 = nmap.address_family() == 'inet' -- True if we are using IPv4. Otherwise, it is IPv6
|
return false
|
||||||
|
|
||||||
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
|
|
||||||
end
|
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
|
-- 2. Modify packet payload
|
||||||
stdnse.sleep(1)
|
p.buf = string.sub(p.buf, 1, p.tcp_data_offset) .. payload
|
||||||
-- Make connection to the target while packet the spoofing thread is sniffing for packets
|
-- 3. Increment IP ID field (IPv4 packets)
|
||||||
local socket = nmap.new_socket()
|
if isIp4 then
|
||||||
socket:set_timeout(3000)
|
p:ip_set_id(p.ip_id + 1)
|
||||||
local status, _ = socket:connect(host.ip, helperport)
|
end
|
||||||
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
|
-- 4. Set TCP sequence number correctly using traffic data
|
||||||
stdnse.sleep(1.5)
|
p:tcp_set_seq(p.tcp_seq + p.tcp_data_length)
|
||||||
socket:close()
|
|
||||||
return
|
-- 5. Update all checksums and lengths
|
||||||
end,
|
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
|
-- List of helpers
|
||||||
local helpers = {
|
local helpers = {
|
||||||
ftp = ftp_helper, -- FTP (IPv4 and IPv6)
|
ftp = ftp_helper, -- FTP (IPv4 and IPv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
local helper
|
local helper
|
||||||
|
|
||||||
hostrule = function(host)
|
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
|
if not nmap.is_privileged() then
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
if not nmap.registry[SCRIPT_NAME].rootfail then
|
if not nmap.registry[SCRIPT_NAME].rootfail then
|
||||||
stdnse.print_verbose("%s lacks privileges.", SCRIPT_NAME )
|
stdnse.print_verbose("%s lacks privileges.", SCRIPT_NAME )
|
||||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
if not host.interface then
|
if not host.interface then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if helper and not helpers[helper] then
|
if helper and not helpers[helper] then
|
||||||
stdnse.print_debug("%s %s helper not supported at the moment.", SCRIPT_NAME, helper)
|
stdnse.print_debug("%s %s helper not supported at the moment.", SCRIPT_NAME, helper)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local helperport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".helperport"))
|
local helperport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".helperport"))
|
||||||
local targetport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".targetport"))
|
local targetport = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".targetport"))
|
||||||
local helpername
|
local helpername
|
||||||
|
|
||||||
if targetport then
|
if targetport then
|
||||||
-- We should check if target port is not already open
|
-- 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.
|
|
||||||
local testsock = nmap.new_socket()
|
local testsock = nmap.new_socket()
|
||||||
testsock:set_timeout(1000)
|
testsock:set_timeout(1000)
|
||||||
local status, _ = testsock:connect(host.ip, targetport)
|
local status, _ = testsock:connect(host.ip, targetport)
|
||||||
testsock:close()
|
|
||||||
if status then
|
if status then
|
||||||
-- If we could connect, then port is open and firewall is vulnerable.
|
stdnse.print_debug("%s %s target port already open.", SCRIPT_NAME, targetport)
|
||||||
local vulnstring = "Firewall vulnerable to bypass through " .. (helper or helpername) .. " helper. "
|
return nil
|
||||||
.. (nmap.address_family() == 'inet' and "(IPv4)" or "(IPv6)")
|
|
||||||
|
|
||||||
return stdnse.format_output(true, vulnstring)
|
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
@@ -86,131 +86,131 @@ categories = {"default", "discovery", "safe"}
|
|||||||
|
|
||||||
|
|
||||||
portrule = function(host, port)
|
portrule = function(host, port)
|
||||||
-- Run for the special port number, or for any HTTP-like service that is
|
-- Run for the special port number, or for any HTTP-like service that is
|
||||||
-- not on a usual HTTP port.
|
-- not on a usual HTTP port.
|
||||||
return shortport.port_or_service ({35871}, "flume-master")(host, 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))
|
or (shortport.service(shortport.LIKELY_HTTP_SERVICES)(host, port) and not shortport.portnumber(shortport.LIKELY_HTTP_PORTS)(host, port))
|
||||||
end
|
end
|
||||||
|
|
||||||
function add_target(hostname)
|
function add_target(hostname)
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, hostname))
|
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, hostname))
|
||||||
local status,err = target.add(hostname)
|
local status,err = target.add(hostname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ref: http://lua-users.org/wiki/TableUtils
|
-- ref: http://lua-users.org/wiki/TableUtils
|
||||||
function table_count(tt, item)
|
function table_count(tt, item)
|
||||||
local count
|
local count
|
||||||
count = 0
|
count = 0
|
||||||
for ii,xx in pairs(tt) do
|
for ii,xx in pairs(tt) do
|
||||||
if item == xx then count = count + 1 end
|
if item == xx then count = count + 1 end
|
||||||
end
|
end
|
||||||
return count
|
return count
|
||||||
end
|
end
|
||||||
|
|
||||||
parse_page = function( host, port, uri, intresting_keys )
|
parse_page = function( host, port, uri, intresting_keys )
|
||||||
local result = {}
|
local result = {}
|
||||||
local response = http.get( host, port, uri )
|
local response = http.get( host, port, uri )
|
||||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response" ))
|
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
|
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||||
local body = response['body']:gsub("%%","%%%%")
|
local body = response['body']:gsub("%%","%%%%")
|
||||||
for name,value in string.gmatch(body,"<tr><th>([^][<]+)</th>%s*<td><div%sclass=[^][>]+>([^][<]+)") do
|
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")))
|
stdnse.print_debug(1, ("%s: %s=%s "):format(SCRIPT_NAME,name,value:gsub("^%s*(.-)%s*$", "%1")))
|
||||||
if nmap.verbosity() > 1 then
|
if nmap.verbosity() > 1 then
|
||||||
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
||||||
else
|
else
|
||||||
for i,v in ipairs(intresting_keys) do
|
for i,v in ipairs(intresting_keys) do
|
||||||
if name:match(("^%s"):format(v)) then
|
if name:match(("^%s"):format(v)) then
|
||||||
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
result[#result+1] = ("%s: %s"):format(name,value:gsub("^%s*(.-)%s*$", "%1"))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
local uri = "/flumemaster.jsp"
|
local uri = "/flumemaster.jsp"
|
||||||
local env_uri = "/masterenv.jsp"
|
local env_uri = "/masterenv.jsp"
|
||||||
local config_uri = "/masterstaticconfig.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 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 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 nodes = { }
|
||||||
local zookeepers = { }
|
local zookeepers = { }
|
||||||
local hbasemasters = { }
|
local hbasemasters = { }
|
||||||
stdnse.print_debug(1, ("%s:HTTP GET %s:%s%s"):format(SCRIPT_NAME, host.targetname or host.ip, port.number, uri))
|
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 )
|
local response = http.get( host, port, uri )
|
||||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
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
|
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||||
local body = response['body']:gsub("%%","%%%%")
|
local body = response['body']:gsub("%%","%%%%")
|
||||||
local capacity = {}
|
local capacity = {}
|
||||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
||||||
if body:match("Version:%s*</b>([^][,]+)") then
|
if body:match("Version:%s*</b>([^][,]+)") then
|
||||||
local version = body:match("Version:%s*</b>([^][,]+)")
|
local version = body:match("Version:%s*</b>([^][,]+)")
|
||||||
stdnse.print_debug(1, ("%s: Version %s"):format(SCRIPT_NAME,version))
|
stdnse.print_debug(1, ("%s: Version %s"):format(SCRIPT_NAME,version))
|
||||||
result[#result+1] = ("Version: %s"):format(version)
|
result[#result+1] = ("Version: %s"):format(version)
|
||||||
port.version.version = version
|
port.version.version = version
|
||||||
end
|
end
|
||||||
if body:match("Compiled:%s*</b>([^][<]+)") then
|
if body:match("Compiled:%s*</b>([^][<]+)") then
|
||||||
local compiled = body:match("Compiled:%s*</b>([^][<]+)")
|
local compiled = body:match("Compiled:%s*</b>([^][<]+)")
|
||||||
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
||||||
result[#result+1] = ("Compiled: %s"):format(compiled)
|
result[#result+1] = ("Compiled: %s"):format(compiled)
|
||||||
end
|
end
|
||||||
if body:match("ServerID:%s*([^][<]+)") then
|
if body:match("ServerID:%s*([^][<]+)") then
|
||||||
local upgrades = body:match("ServerID:%s*([^][<]+)")
|
local upgrades = body:match("ServerID:%s*([^][<]+)")
|
||||||
stdnse.print_debug(1, ("%s: ServerID %s"):format(SCRIPT_NAME,upgrades))
|
stdnse.print_debug(1, ("%s: ServerID %s"):format(SCRIPT_NAME,upgrades))
|
||||||
result[#result] = ("ServerID: %s"):format(upgrades)
|
result[#result] = ("ServerID: %s"):format(upgrades)
|
||||||
end
|
end
|
||||||
for logical,physical,hostname in string.gmatch(body,"<tr><td>([%w%.-_:]+)</td><td>([%w%.]+)</td><td>([%w%.]+)</td>") do
|
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))
|
stdnse.print_debug(2, ("%s: %s (%s) %s"):format(SCRIPT_NAME,physical,logical,hostname))
|
||||||
if (table_count(nodes, hostname) == 0) then
|
if (table_count(nodes, hostname) == 0) then
|
||||||
nodes[#nodes+1] = hostname
|
nodes[#nodes+1] = hostname
|
||||||
add_target(hostname)
|
add_target(hostname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if next(nodes) ~= nil then
|
if next(nodes) ~= nil then
|
||||||
table.insert(result, "Flume nodes:")
|
table.insert(result, "Flume nodes:")
|
||||||
result[#result+1] = nodes
|
result[#result+1] = nodes
|
||||||
end
|
end
|
||||||
for zookeeper in string.gmatch(body,"Dhbase.zookeeper.quorum=([^][\"]+)") do
|
for zookeeper in string.gmatch(body,"Dhbase.zookeeper.quorum=([^][\"]+)") do
|
||||||
if (table_count(zookeepers, zookeeper) == 0) then
|
if (table_count(zookeepers, zookeeper) == 0) then
|
||||||
zookeepers[#zookeepers+1] = zookeeper
|
zookeepers[#zookeepers+1] = zookeeper
|
||||||
add_target(zookeeper)
|
add_target(zookeeper)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if next(zookeepers) ~= nil then
|
if next(zookeepers) ~= nil then
|
||||||
result[#result+1] = "Zookeeper Master:"
|
result[#result+1] = "Zookeeper Master:"
|
||||||
result[#result+1] = zookeepers
|
result[#result+1] = zookeepers
|
||||||
end
|
end
|
||||||
for hbasemaster in string.gmatch(body,"Dhbase.rootdir=([^][\"]+)") do
|
for hbasemaster in string.gmatch(body,"Dhbase.rootdir=([^][\"]+)") do
|
||||||
if (table_count(hbasemasters, hbasemaster) == 0) then
|
if (table_count(hbasemasters, hbasemaster) == 0) then
|
||||||
hbasemasters[#hbasemasters+1] = hbasemaster
|
hbasemasters[#hbasemasters+1] = hbasemaster
|
||||||
add_target(hbasemaster)
|
add_target(hbasemaster)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if next(hbasemasters) ~= nil then
|
if next(hbasemasters) ~= nil then
|
||||||
result[#result+1] = "Hbase Master Master:"
|
result[#result+1] = "Hbase Master Master:"
|
||||||
result[#result+1] = hbasemasters
|
result[#result+1] = hbasemasters
|
||||||
end
|
end
|
||||||
local vars = parse_page(host, port, env_uri, env_keys )
|
local vars = parse_page(host, port, env_uri, env_keys )
|
||||||
if next(vars) ~= nil then
|
if next(vars) ~= nil then
|
||||||
result[#result+1] = "Enviroment: "
|
result[#result+1] = "Enviroment: "
|
||||||
result[#result+1] = vars
|
result[#result+1] = vars
|
||||||
end
|
end
|
||||||
local vars = parse_page(host, port, config_uri, config_keys )
|
local vars = parse_page(host, port, config_uri, config_keys )
|
||||||
if next(vars) ~= nil then
|
if next(vars) ~= nil then
|
||||||
result[#result+1] = "Config: "
|
result[#result+1] = "Config: "
|
||||||
result[#result+1] = vars
|
result[#result+1] = vars
|
||||||
end
|
end
|
||||||
if #result > 0 then
|
if #result > 0 then
|
||||||
port.version.name = "flume-master"
|
port.version.name = "flume-master"
|
||||||
port.version.product = "Apache Flume"
|
port.version.product = "Apache Flume"
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -34,191 +34,191 @@ categories = {"default", "safe"}
|
|||||||
portrule = shortport.service("ftp")
|
portrule = shortport.service("ftp")
|
||||||
|
|
||||||
line_iterate = function(s)
|
line_iterate = function(s)
|
||||||
local line
|
local line
|
||||||
for line in string.gmatch(s, "([^\n$]*)") do
|
for line in string.gmatch(s, "([^\n$]*)") do
|
||||||
if #line > 0 then
|
if #line > 0 then
|
||||||
coroutine.yield(line)
|
coroutine.yield(line)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- returns last ftp code read, or 000 on timeout
|
-- returns last ftp code read, or 000 on timeout
|
||||||
get_ftp_code = function(socket)
|
get_ftp_code = function(socket)
|
||||||
local fcode = 000
|
local fcode = 000
|
||||||
local code = 0
|
local code = 0
|
||||||
local status
|
local status
|
||||||
local result
|
local result
|
||||||
local co
|
local co
|
||||||
local line
|
local line
|
||||||
local err
|
local err
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
status, result = socket:receive()
|
status, result = socket:receive()
|
||||||
if not status then
|
if not status then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
-- read okay!
|
-- read okay!
|
||||||
co = coroutine.create(line_iterate)
|
co = coroutine.create(line_iterate)
|
||||||
while coroutine.status(co) ~= 'dead' do
|
while coroutine.status(co) ~= 'dead' do
|
||||||
err, line = coroutine.resume(co, result)
|
err, line = coroutine.resume(co, result)
|
||||||
if line then
|
if line then
|
||||||
code = string.match(line, "^(%d%d%d) ")
|
code = string.match(line, "^(%d%d%d) ")
|
||||||
if not code then
|
if not code then
|
||||||
code = "-1"
|
code = "-1"
|
||||||
end
|
end
|
||||||
-- io.write(">" .. code .. ":".. line .. "<\n")
|
-- io.write(">" .. code .. ":".. line .. "<\n")
|
||||||
if tonumber(code) > 0 then
|
if tonumber(code) > 0 then
|
||||||
fcode = tonumber(code)
|
fcode = tonumber(code)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- not received good ftp code, try again
|
-- not received good ftp code, try again
|
||||||
if fcode ~= 0 then
|
if fcode ~= 0 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- io.write("## " .. fcode .. "\n");
|
-- io.write("## " .. fcode .. "\n");
|
||||||
return fcode
|
return fcode
|
||||||
end
|
end
|
||||||
|
|
||||||
local get_login = function()
|
local get_login = function()
|
||||||
local user, pass
|
local user, pass
|
||||||
local k
|
local k
|
||||||
|
|
||||||
for _, k in ipairs({"ftp-bounce.username", "username"}) do
|
for _, k in ipairs({"ftp-bounce.username", "username"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
user = nmap.registry.args[k]
|
user = nmap.registry.args[k]
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, k in ipairs({"ftp-bounce.password", "password"}) do
|
for _, k in ipairs({"ftp-bounce.password", "password"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
pass = nmap.registry.args[k]
|
pass = nmap.registry.args[k]
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return user or "anonymous", pass or "IEUser@"
|
return user or "anonymous", pass or "IEUser@"
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local result;
|
local result;
|
||||||
local status = true
|
local status = true
|
||||||
local isAnon = false
|
local isAnon = false
|
||||||
local isOk = false
|
local isOk = false
|
||||||
local sendPass = true
|
local sendPass = true
|
||||||
local user, pass = get_login()
|
local user, pass = get_login()
|
||||||
local fc
|
local fc
|
||||||
|
|
||||||
socket:set_timeout(10000)
|
socket:set_timeout(10000)
|
||||||
socket:connect(host, port)
|
socket:connect(host, port)
|
||||||
|
|
||||||
-- BANNER
|
-- BANNER
|
||||||
fc = get_ftp_code(socket)
|
fc = get_ftp_code(socket)
|
||||||
if fc == 0 then
|
if fc == 0 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- no banner
|
-- no banner
|
||||||
return "no banner"
|
return "no banner"
|
||||||
end
|
end
|
||||||
if fc == 421 or (fc >= 500 and fc <= 599) then
|
if fc == 421 or (fc >= 500 and fc <= 599) then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "server says you are not allowed to create connection"
|
-- return "server says you are not allowed to create connection"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc < 200 or fc > 299 then
|
if fc < 200 or fc > 299 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- bad code
|
-- bad code
|
||||||
-- return "bad banner (code " .. fc .. ")"
|
-- return "bad banner (code " .. fc .. ")"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
-- USER
|
-- USER
|
||||||
socket:send("USER " .. user .. "\r\n")
|
socket:send("USER " .. user .. "\r\n")
|
||||||
fc = get_ftp_code(socket)
|
fc = get_ftp_code(socket)
|
||||||
if (fc >= 400 and fc <= 499) or (fc >= 500 and fc <= 599) then
|
if (fc >= 400 and fc <= 499) or (fc >= 500 and fc <= 599) then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- bad code
|
-- bad code
|
||||||
--return "anonymous user not allowed"
|
--return "anonymous user not allowed"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc == 0 then
|
if fc == 0 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "anonymous user timeouted"
|
-- return "anonymous user timeouted"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc ~= 230 and fc ~= 331 then
|
if fc ~= 230 and fc ~= 331 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- bad code
|
-- bad code
|
||||||
-- return "bad response for anonymous user (code " .. fc .. ")"
|
-- return "bad response for anonymous user (code " .. fc .. ")"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc == 230 then
|
if fc == 230 then
|
||||||
sendPass = false
|
sendPass = false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PASS
|
-- PASS
|
||||||
if sendPass then
|
if sendPass then
|
||||||
socket:send("PASS " .. pass .. "\r\n")
|
socket:send("PASS " .. pass .. "\r\n")
|
||||||
fc = get_ftp_code(socket)
|
fc = get_ftp_code(socket)
|
||||||
if (fc >= 500 and fc <= 599) or (fc >= 400 and fc <= 499) then
|
if (fc >= 500 and fc <= 599) or (fc >= 400 and fc <= 499) then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- bad code
|
-- bad code
|
||||||
-- return "anonymous user/pass rejected"
|
-- return "anonymous user/pass rejected"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc == 0 then
|
if fc == 0 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "anonymous pass timeouted"
|
-- return "anonymous pass timeouted"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc ~= 230 and fc ~= 200 then
|
if fc ~= 230 and fc ~= 200 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "answer to PASS not understood (code " .. fc .. ")"
|
-- return "answer to PASS not understood (code " .. fc .. ")"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PORT scanme.nmap.com:highport
|
-- PORT scanme.nmap.com:highport
|
||||||
socket:send("PORT 205,217,153,62,80,80\r\n")
|
socket:send("PORT 205,217,153,62,80,80\r\n")
|
||||||
fc = get_ftp_code(socket)
|
fc = get_ftp_code(socket)
|
||||||
if (fc >= 500 and fc <= 599) then
|
if (fc >= 500 and fc <= 599) then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "server forbids bouncing"
|
-- return "server forbids bouncing"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if fc == 0 then
|
if fc == 0 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "PORT command timeouted"
|
-- return "PORT command timeouted"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not (fc >= 200 and fc<=299) then
|
if not (fc >= 200 and fc<=299) then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "PORT response not understood (code " .. fc .. ")"
|
-- return "PORT response not understood (code " .. fc .. ")"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PORT scanme.nmap.com:lowport
|
-- PORT scanme.nmap.com:lowport
|
||||||
socket:send("PORT 205,217,153,62,0,80\r\n")
|
socket:send("PORT 205,217,153,62,0,80\r\n")
|
||||||
fc = get_ftp_code(socket)
|
fc = get_ftp_code(socket)
|
||||||
if (fc >= 500 and fc <= 599) then
|
if (fc >= 500 and fc <= 599) then
|
||||||
socket:close()
|
socket:close()
|
||||||
return "server forbids bouncing to low ports <1025"
|
return "server forbids bouncing to low ports <1025"
|
||||||
end
|
end
|
||||||
if fc == 0 then
|
if fc == 0 then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "PORT command timeouted for low port"
|
-- return "PORT command timeouted for low port"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if not (fc >= 200 and fc<=299) then
|
if not (fc >= 200 and fc<=299) then
|
||||||
socket:close()
|
socket:close()
|
||||||
-- return "PORT response not understood for low port (code " .. fc .. ")"
|
-- return "PORT response not understood for low port (code " .. fc .. ")"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
return "bounce working!"
|
return "bounce working!"
|
||||||
end
|
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 function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
|
|
||||||
local long_names = {
|
local long_names = {
|
||||||
["fs_mounts"] = "Mounts",
|
["fs_mounts"] = "Mounts",
|
||||||
["net"] = "Network",
|
["net"] = "Network",
|
||||||
["hostname"] = "Hostname",
|
["hostname"] = "Hostname",
|
||||||
["sysname"] = "System",
|
["sysname"] = "System",
|
||||||
["version"] = "Version",
|
["version"] = "Version",
|
||||||
["uptime"] = "Uptime",
|
["uptime"] = "Uptime",
|
||||||
["mem"] = "Memory",
|
["mem"] = "Memory",
|
||||||
["proc"] = "Processes",
|
["proc"] = "Processes",
|
||||||
}
|
}
|
||||||
|
|
||||||
local order = {
|
local order = {
|
||||||
"Hostname", "System", "Version", "Uptime", "Processes", "Memory", "Network", "Mounts"
|
"Hostname", "System", "Version", "Uptime", "Processes", "Memory", "Network", "Mounts"
|
||||||
}
|
}
|
||||||
|
|
||||||
local function getOrderPos(tag)
|
local function getOrderPos(tag)
|
||||||
for i=1, #order do
|
for i=1, #order do
|
||||||
if ( tag.name == order[i] ) then
|
if ( tag.name == order[i] ) then
|
||||||
return i
|
return i
|
||||||
elseif ( "string" == type(tag) and tag:match("^([^:]*)") == order[i] ) then
|
elseif ( "string" == type(tag) and tag:match("^([^:]*)") == order[i] ) then
|
||||||
return i
|
return i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local function minutesToUptime(value)
|
local function minutesToUptime(value)
|
||||||
local days = math.floor(value / (60 * 24))
|
local days = math.floor(value / (60 * 24))
|
||||||
local htime = math.fmod(value, (60 * 24))
|
local htime = math.fmod(value, (60 * 24))
|
||||||
local hours = math.floor(htime / 60)
|
local hours = math.floor(htime / 60)
|
||||||
local mtime = math.fmod(htime, 60)
|
local mtime = math.fmod(htime, 60)
|
||||||
local minutes = math.floor(mtime)
|
local minutes = math.floor(mtime)
|
||||||
local output = ""
|
local output = ""
|
||||||
if ( days > 0 ) then
|
if ( days > 0 ) then
|
||||||
output = output .. ("%d days, "):format(days)
|
output = output .. ("%d days, "):format(days)
|
||||||
end
|
end
|
||||||
if ( hours > 0 ) then
|
if ( hours > 0 ) then
|
||||||
output = output .. ("%d hours, "):format(hours)
|
output = output .. ("%d hours, "):format(hours)
|
||||||
end
|
end
|
||||||
if ( minutes > 0 ) then
|
if ( minutes > 0 ) then
|
||||||
output = output .. ("%d minutes"):format(minutes)
|
output = output .. ("%d minutes"):format(minutes)
|
||||||
end
|
end
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
local function decodeTag(tag, lines)
|
local function decodeTag(tag, lines)
|
||||||
local result = { name = long_names[tag] }
|
local result = { name = long_names[tag] }
|
||||||
local order
|
local order
|
||||||
|
|
||||||
if ( "fs_mounts" == tag ) then
|
if ( "fs_mounts" == tag ) then
|
||||||
local fs_tab = tab.new(4)
|
local fs_tab = tab.new(4)
|
||||||
tab.addrow(fs_tab, "Mount point", "Fs type", "Size", "Available")
|
tab.addrow(fs_tab, "Mount point", "Fs type", "Size", "Available")
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
if ( ".clear" ~= line ) then
|
if ( ".clear" ~= line ) then
|
||||||
local mount, prefix, fstype, size, free, used, bs = table.unpack(stdnse.strsplit("%s", line))
|
local mount, prefix, fstype, size, free, used, bs = table.unpack(stdnse.strsplit("%s", line))
|
||||||
if ( size and free and mount and fstype ) then
|
if ( size and free and mount and fstype ) then
|
||||||
size = ("%dM"):format(math.ceil(tonumber(size) * tonumber(bs) / 1048576))
|
size = ("%dM"):format(math.ceil(tonumber(size) * tonumber(bs) / 1048576))
|
||||||
free = ("%dM"):format(math.ceil(tonumber(free) * tonumber(bs) / 1048576))
|
free = ("%dM"):format(math.ceil(tonumber(free) * tonumber(bs) / 1048576))
|
||||||
tab.addrow(fs_tab, mount, fstype, size, free)
|
tab.addrow(fs_tab, mount, fstype, size, free)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(result, tab.dump(fs_tab))
|
table.insert(result, tab.dump(fs_tab))
|
||||||
elseif ( "net" == tag ) then
|
elseif ( "net" == tag ) then
|
||||||
local net_tab = tab.new(3)
|
local net_tab = tab.new(3)
|
||||||
tab.addrow(net_tab, "Interface", "Received", "Transmitted")
|
tab.addrow(net_tab, "Interface", "Received", "Transmitted")
|
||||||
for _, line in ipairs(lines) do
|
for _, line in ipairs(lines) do
|
||||||
local name, rx, tx = line:match("^([^%s]*)%s([^%s]*)%s([^%s]*)$")
|
local name, rx, tx = line:match("^([^%s]*)%s([^%s]*)%s([^%s]*)$")
|
||||||
rx = ("%dM"):format(math.ceil(tonumber(rx) / 1048576))
|
rx = ("%dM"):format(math.ceil(tonumber(rx) / 1048576))
|
||||||
tx = ("%dM"):format(math.ceil(tonumber(tx) / 1048576))
|
tx = ("%dM"):format(math.ceil(tonumber(tx) / 1048576))
|
||||||
tab.addrow(net_tab, name, rx, tx)
|
tab.addrow(net_tab, name, rx, tx)
|
||||||
end
|
end
|
||||||
table.insert(result, tab.dump(net_tab))
|
table.insert(result, tab.dump(net_tab))
|
||||||
elseif ( "hostname" == tag or "sysname" == tag or
|
elseif ( "hostname" == tag or "sysname" == tag or
|
||||||
"version" == tag ) then
|
"version" == tag ) then
|
||||||
return ("%s: %s"):format(long_names[tag], lines[1])
|
return ("%s: %s"):format(long_names[tag], lines[1])
|
||||||
elseif ( "uptime" == tag ) then
|
elseif ( "uptime" == tag ) then
|
||||||
return ("%s: %s"):format(long_names[tag], minutesToUptime(lines[1]))
|
return ("%s: %s"):format(long_names[tag], minutesToUptime(lines[1]))
|
||||||
elseif ( "mem" == tag ) then
|
elseif ( "mem" == tag ) then
|
||||||
local total, used = table.unpack(stdnse.strsplit("%s", lines[1]))
|
local total, used = table.unpack(stdnse.strsplit("%s", lines[1]))
|
||||||
if ( not(total) or not(used) ) then
|
if ( not(total) or not(used) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local free = math.ceil((total - used)/1048576)
|
local free = math.ceil((total - used)/1048576)
|
||||||
total = math.ceil(tonumber(total)/1048576)
|
total = math.ceil(tonumber(total)/1048576)
|
||||||
return ("%s: Total %dM, Free %dM"):format(long_names[tag], total, free)
|
return ("%s: Total %dM, Free %dM"):format(long_names[tag], total, free)
|
||||||
elseif ( "proc" == tag ) then
|
elseif ( "proc" == tag ) then
|
||||||
local procs, _, forks, load, users = table.unpack(stdnse.strsplit("%s", lines[1]))
|
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
|
if ( not(procs) or not(forks) or not(load) or not(users) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return ("%s: Processes %d, Load %.2f, Users %d"):format(long_names[tag], procs, load, users)
|
return ("%s: Processes %d, Load %.2f, Users %d"):format(long_names[tag], procs, load, users)
|
||||||
end
|
end
|
||||||
return ( #result > 0 and result or nil )
|
return ( #result > 0 and result or nil )
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
|
|
||||||
if ( not(socket:connect(host, port)) ) then
|
if ( not(socket:connect(host, port)) ) then
|
||||||
return fail("Failed to connect to the server")
|
return fail("Failed to connect to the server")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If there's an error we get a response back, and only then
|
-- If there's an error we get a response back, and only then
|
||||||
local status, data = socket:receive_buf("\n", false)
|
local status, data = socket:receive_buf("\n", false)
|
||||||
if( status and data ~= "<error>" ) then
|
if( status and data ~= "<error>" ) then
|
||||||
return fail("An unknown error occured, aborting ...")
|
return fail("An unknown error occured, aborting ...")
|
||||||
elseif ( status ) then
|
elseif ( status ) then
|
||||||
status, data = socket:receive_buf("\n", false)
|
status, data = socket:receive_buf("\n", false)
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
return fail(data)
|
return fail(data)
|
||||||
else
|
else
|
||||||
return fail("Failed to receive error message from server")
|
return fail("Failed to receive error message from server")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(socket:send("gkrellm 2.3.4\n")) ) then
|
if ( not(socket:send("gkrellm 2.3.4\n")) ) then
|
||||||
return fail("Failed to send data to the server")
|
return fail("Failed to send data to the server")
|
||||||
end
|
end
|
||||||
|
|
||||||
local tags = {}
|
local tags = {}
|
||||||
local status, tag = socket:receive_buf("\n", false)
|
local status, tag = socket:receive_buf("\n", false)
|
||||||
while(true) do
|
while(true) do
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if ( not(tag:match("^<.*>$")) ) then
|
if ( not(tag:match("^<.*>$")) ) then
|
||||||
stdnse.print_debug(2, "Expected tag, got: %s", tag)
|
stdnse.print_debug(2, "Expected tag, got: %s", tag)
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
tag = tag:match("^<(.*)>$")
|
tag = tag:match("^<(.*)>$")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( tags[tag] ) then
|
if ( tags[tag] ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
while(true) do
|
while(true) do
|
||||||
local data
|
local data
|
||||||
status, data = socket:receive_buf("\n", false)
|
status, data = socket:receive_buf("\n", false)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if ( status and data:match("^<.*>$") ) then
|
if ( status and data:match("^<.*>$") ) then
|
||||||
tag = data
|
tag = data
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
tags[tag] = tags[tag] or {}
|
tags[tag] = tags[tag] or {}
|
||||||
table.insert(tags[tag], data)
|
table.insert(tags[tag], data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
for tag in pairs(tags) do
|
for tag in pairs(tags) do
|
||||||
local result, order = decodeTag(tag, tags[tag])
|
local result, order = decodeTag(tag, tags[tag])
|
||||||
if ( result ) then
|
if ( result ) then
|
||||||
table.insert(output, result)
|
table.insert(output, result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.sort(output, function(a,b) return getOrderPos(a) < getOrderPos(b) end)
|
table.sort(output, function(a,b) return getOrderPos(a) < getOrderPos(b) end)
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
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
|
-- analyzes the response and attempt to determine whether authentication was
|
||||||
-- successful or not. The script analyzes this by checking the response using
|
-- successful or not. The script analyzes this by checking the response using
|
||||||
-- the following rules:
|
-- the following rules:
|
||||||
-- 1. If the response was empty the authentication was successful
|
-- 1. If the response was empty the authentication was successful
|
||||||
-- 2. If the response contains the message passed in the onsuccess
|
-- 2. If the response contains the message passed in the onsuccess
|
||||||
-- argument the authentication was successful
|
-- argument the authentication was successful
|
||||||
-- 3. If no onsuccess argument was passed, and if the response
|
-- 3. If no onsuccess argument was passed, and if the response
|
||||||
-- does not contain the message passed in the onfailure argument the
|
-- does not contain the message passed in the onfailure argument the
|
||||||
-- authentication was successful
|
-- authentication was successful
|
||||||
-- 4. If neither the onsuccess or onfailure argument was passed and the
|
-- 4. If neither the onsuccess or onfailure argument was passed and the
|
||||||
-- response does not contain a password form field authentication
|
-- response does not contain a password form field authentication
|
||||||
-- was successful
|
-- was successful
|
||||||
-- 5. Authentication failed
|
-- 5. Authentication failed
|
||||||
--
|
--
|
||||||
-- @output
|
-- @output
|
||||||
-- PORT STATE SERVICE REASON
|
-- 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
|
-- holds the username used to authenticate. A simple autodetection of
|
||||||
-- this variable is attempted.
|
-- this variable is attempted.
|
||||||
-- @args http-form-brute.passvar sets the http-variable name that holds the
|
-- @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.
|
-- is attempted.
|
||||||
-- @args http-form-brute.onsuccess (optional) sets the message to expect on
|
-- @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
|
-- @args http-form-brute.onfailure (optional) sets the message to expect on
|
||||||
-- unsuccessful authentication
|
-- unsuccessful authentication
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Version 0.3
|
-- Version 0.3
|
||||||
-- Created 07/30/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- 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 05/23/2011 - v0.2 - changed so that uservar is optional
|
||||||
-- Revised 06/05/2011 - v0.3 - major re-write, added onsucces, onfailure and
|
-- Revised 06/05/2011 - v0.3 - major re-write, added onsucces, onfailure and
|
||||||
-- support for redirects
|
-- support for redirects
|
||||||
--
|
--
|
||||||
|
|
||||||
author = "Patrik Karlsson"
|
author = "Patrik Karlsson"
|
||||||
@@ -85,166 +85,166 @@ local form_params = {}
|
|||||||
|
|
||||||
Driver = {
|
Driver = {
|
||||||
|
|
||||||
new = function(self, host, port, options)
|
new = function(self, host, port, options)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.host = nmap.registry.args['http-form-brute.hostname'] or host
|
o.host = nmap.registry.args['http-form-brute.hostname'] or host
|
||||||
o.port = port
|
o.port = port
|
||||||
o.options = options
|
o.options = options
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
connect = function( self )
|
connect = function( self )
|
||||||
-- This will cause problems, as ther is no way for us to "reserve"
|
-- 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
|
-- 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.
|
-- which won't be guessed until the end, due to socket exhaustion.
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
login = function( self, username, password )
|
login = function( self, username, password )
|
||||||
-- we need to supply the no_cache directive, or else the http library
|
-- we need to supply the no_cache directive, or else the http library
|
||||||
-- incorrectly tells us that the authentication was successfull
|
-- incorrectly tells us that the authentication was successfull
|
||||||
local postparams = { [self.options.passvar] = password }
|
local postparams = { [self.options.passvar] = password }
|
||||||
if ( self.options.uservar ) then postparams[self.options.uservar] = username end
|
if ( self.options.uservar ) then postparams[self.options.uservar] = username end
|
||||||
|
|
||||||
local response = Driver.postRequest(self.host, self.port, self.options.path, postparams)
|
local response = Driver.postRequest(self.host, self.port, self.options.path, postparams)
|
||||||
local success = false
|
local success = false
|
||||||
|
|
||||||
-- if we have no response, we were successful
|
-- if we have no response, we were successful
|
||||||
if ( not(response.body) ) then
|
if ( not(response.body) ) then
|
||||||
success = true
|
success = true
|
||||||
-- if we have a response and it matches our onsuccess match, login was successful
|
-- if we have a response and it matches our onsuccess match, login was successful
|
||||||
elseif ( response.body and
|
elseif ( response.body and
|
||||||
self.options.onsuccess and
|
self.options.onsuccess and
|
||||||
response.body:match(self.options.onsuccess) ) then
|
response.body:match(self.options.onsuccess) ) then
|
||||||
success = true
|
success = true
|
||||||
-- if we have a response and it does not match our onfailure, login was successful
|
-- if we have a response and it does not match our onfailure, login was successful
|
||||||
elseif ( response.body and
|
elseif ( response.body and
|
||||||
not(self.options.onsuccess) and
|
not(self.options.onsuccess) and
|
||||||
self.options.onfailure and
|
self.options.onfailure and
|
||||||
not(response.body:match(self.options.onfailure))) then
|
not(response.body:match(self.options.onfailure))) then
|
||||||
success = true
|
success = true
|
||||||
-- if we have a response and no onfailure or onsuccess match defined
|
-- if we have a response and no onfailure or onsuccess match defined
|
||||||
-- and can't find a password field, login was successful
|
-- and can't find a password field, login was successful
|
||||||
elseif ( response.body and
|
elseif ( response.body and
|
||||||
not(self.options.onfailure) and
|
not(self.options.onfailure) and
|
||||||
not(self.options.onsuccess) and
|
not(self.options.onsuccess) and
|
||||||
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*"))
|
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*"))
|
||||||
) then
|
) then
|
||||||
success = true
|
success = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- We check whether the body was empty or that we have a body without our user- and pass-var
|
-- We check whether the body was empty or that we have a body without our user- and pass-var
|
||||||
if ( success ) then
|
if ( success ) then
|
||||||
nmap.registry['credentials'] = nmap.registry['credentials'] or {}
|
nmap.registry['credentials'] = nmap.registry['credentials'] or {}
|
||||||
nmap.registry.credentials['http'] = nmap.registry.credentials['http'] or {}
|
nmap.registry.credentials['http'] = nmap.registry.credentials['http'] or {}
|
||||||
table.insert( nmap.registry.credentials.http, { username = username, password = password } )
|
table.insert( nmap.registry.credentials.http, { username = username, password = password } )
|
||||||
return true, brute.Account:new( username, password, creds.State.VALID)
|
return true, brute.Account:new( username, password, creds.State.VALID)
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, brute.Error:new( "Incorrect password" )
|
return false, brute.Error:new( "Incorrect password" )
|
||||||
end,
|
end,
|
||||||
|
|
||||||
disconnect = function( self )
|
disconnect = function( self )
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
check = function( self )
|
check = function( self )
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
postRequest = function( host, port, path, options )
|
postRequest = function( host, port, path, options )
|
||||||
local response = http.post( host, port, path, { no_cache = true }, nil, options )
|
local response = http.post( host, port, path, { no_cache = true }, nil, options )
|
||||||
local status = ( response and tonumber(response.status) ) or 0
|
local status = ( response and tonumber(response.status) ) or 0
|
||||||
if ( status > 300 and status < 400 ) then
|
if ( status > 300 and status < 400 ) then
|
||||||
local new_path = url.absolute(path, response.header.location)
|
local new_path = url.absolute(path, response.header.location)
|
||||||
response = http.get( host, port, new_path, { no_cache = true } )
|
response = http.get( host, port, new_path, { no_cache = true } )
|
||||||
end
|
end
|
||||||
return response
|
return response
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Attempts to auto-detect known form-fields
|
--- Attempts to auto-detect known form-fields
|
||||||
--
|
--
|
||||||
local function detectFormFields( host, port, path )
|
local function detectFormFields( host, port, path )
|
||||||
local response = http.get( host, port, path )
|
local response = http.get( host, port, path )
|
||||||
local user_field, pass_field
|
local user_field, pass_field
|
||||||
|
|
||||||
if ( response.status == 200 ) then
|
if ( response.status == 200 ) then
|
||||||
user_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]*([^\"]-[Uu][Ss][Ee][Rr].-)[\"]*.->")
|
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].-)[\"]*.->")
|
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]*([Pp][Aa][Ss][Ss].-)[\"]*.->")
|
||||||
|
|
||||||
if ( not(pass_field) ) then
|
if ( not(pass_field) ) then
|
||||||
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]-([^\"]-[Kk][Ee][Yy].-)[\"].->")
|
pass_field = response.body:match("<[Ii][Nn][Pp][Uu][Tt].-name=[\"]-([^\"]-[Kk][Ee][Yy].-)[\"].->")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return user_field, pass_field
|
return user_field, pass_field
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
local uservar = stdnse.get_script_args('http-form-brute.uservar')
|
local uservar = stdnse.get_script_args('http-form-brute.uservar')
|
||||||
local passvar = stdnse.get_script_args('http-form-brute.passvar')
|
local passvar = stdnse.get_script_args('http-form-brute.passvar')
|
||||||
local path = stdnse.get_script_args('http-form-brute.path') or "/"
|
local path = stdnse.get_script_args('http-form-brute.path') or "/"
|
||||||
local onsuccess = stdnse.get_script_args("http-form-brute.onsuccess")
|
local onsuccess = stdnse.get_script_args("http-form-brute.onsuccess")
|
||||||
local onfailure = stdnse.get_script_args("http-form-brute.onfailure")
|
local onfailure = stdnse.get_script_args("http-form-brute.onfailure")
|
||||||
|
|
||||||
local _
|
local _
|
||||||
|
|
||||||
-- if now fields were given attempt to autodetect
|
-- if now fields were given attempt to autodetect
|
||||||
if ( not(uservar) and not(passvar) ) then
|
if ( not(uservar) and not(passvar) ) then
|
||||||
uservar, passvar = detectFormFields( host, port, path )
|
uservar, passvar = detectFormFields( host, port, path )
|
||||||
-- if now passvar was detected attempt to autodetect
|
-- if now passvar was detected attempt to autodetect
|
||||||
elseif ( not(passvar) ) then
|
elseif ( not(passvar) ) then
|
||||||
_, passvar = detectFormFields( host, port, path )
|
_, passvar = detectFormFields( host, port, path )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- uservar is optional, so only make sure we have a passvar
|
-- uservar is optional, so only make sure we have a passvar
|
||||||
if ( not( passvar ) ) then
|
if ( not( passvar ) ) then
|
||||||
return "\n ERROR: No passvar was specified (see http-form-brute.passvar)"
|
return "\n ERROR: No passvar was specified (see http-form-brute.passvar)"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(path) ) then
|
if ( not(path) ) then
|
||||||
return "\n ERROR: No path was specified (see http-form-brute.path)"
|
return "\n ERROR: No path was specified (see http-form-brute.path)"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( onsuccess and onfailure ) then
|
if ( onsuccess and onfailure ) then
|
||||||
return "\n ERROR: Either the onsuccess or onfailure argument should be passed, not both."
|
return "\n ERROR: Either the onsuccess or onfailure argument should be passed, not both."
|
||||||
end
|
end
|
||||||
|
|
||||||
local options = { [passvar] = "this_is_not_a_valid_password" }
|
local options = { [passvar] = "this_is_not_a_valid_password" }
|
||||||
if ( uservar ) then options[uservar] = "this_is_not_a_valid_user" end
|
if ( uservar ) then options[uservar] = "this_is_not_a_valid_user" end
|
||||||
|
|
||||||
local response = Driver.postRequest( host, port, path, options )
|
local response = Driver.postRequest( host, port, path, options )
|
||||||
if ( not(response) or not(response.body) or response.status ~= 200 ) then
|
if ( not(response) or not(response.body) or response.status ~= 200 ) then
|
||||||
return ("\n ERROR: Failed to retrieve path (%s) from server"):format(path)
|
return ("\n ERROR: Failed to retrieve path (%s) from server"):format(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- try to detect onfailure match
|
-- try to detect onfailure match
|
||||||
if ( onfailure and not(response.body:match(onfailure)) ) then
|
if ( onfailure and not(response.body:match(onfailure)) ) then
|
||||||
return ("\n ERROR: Failed to match password failure message (%s)"):format(onfailure)
|
return ("\n ERROR: Failed to match password failure message (%s)"):format(onfailure)
|
||||||
elseif ( not(onfailure) and
|
elseif ( not(onfailure) and
|
||||||
not(onsuccess) and
|
not(onsuccess) and
|
||||||
not(response.body:match("input.-type=[\"]*[Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd][\"]*")) ) then
|
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)")
|
return ("\n ERROR: Failed to detect password form field see (http-form-brute.onsuccess or http-form-brute.onfailure)")
|
||||||
end
|
end
|
||||||
|
|
||||||
local engine = brute.Engine:new( Driver, host, port, {
|
local engine = brute.Engine:new( Driver, host, port, {
|
||||||
uservar = uservar, passvar = passvar,
|
uservar = uservar, passvar = passvar,
|
||||||
path = path, onsuccess = onsuccess, onfailure = onfailure
|
path = path, onsuccess = onsuccess, onfailure = onfailure
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
-- there's a bug in http.lua that does not allow it to be called by
|
-- there's a bug in http.lua that does not allow it to be called by
|
||||||
-- multiple threads
|
-- multiple threads
|
||||||
engine:setMaxThreads(1)
|
engine:setMaxThreads(1)
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
|
|
||||||
if ( not(uservar) ) then
|
if ( not(uservar) ) then
|
||||||
engine.options:setOption( "passonly", true )
|
engine.options:setOption( "passonly", true )
|
||||||
end
|
end
|
||||||
local status, result = engine:start()
|
local status, result = engine:start()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -43,170 +43,170 @@ portrule = shortport.http
|
|||||||
---Enumeration for results
|
---Enumeration for results
|
||||||
local enum_results =
|
local enum_results =
|
||||||
{
|
{
|
||||||
VULNERABLE = 1,
|
VULNERABLE = 1,
|
||||||
NOT_VULNERABLE = 2,
|
NOT_VULNERABLE = 2,
|
||||||
UNKNOWN = 3
|
UNKNOWN = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
---Sends a PROPFIND request to the given host, and for the given folder. Returns a table reprenting a response.
|
---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 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 = {
|
local options = {
|
||||||
header = {
|
header = {
|
||||||
Host = host.ip,
|
Host = host.ip,
|
||||||
Connection = "close",
|
Connection = "close",
|
||||||
["User-Agent"] = "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)",
|
["User-Agent"] = "Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)",
|
||||||
["Content-Type"] = "application/xml",
|
["Content-Type"] = "application/xml",
|
||||||
},
|
},
|
||||||
content = webdav_req
|
content = webdav_req
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.generic_request(host, port, "PROPFIND", folder, options)
|
return http.generic_request(host, port, "PROPFIND", folder, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
---Check a single folder on a single host for the vulnerability. Returns one of the enum_results codes.
|
---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 function go_single(host, port, folder)
|
||||||
local response
|
local response
|
||||||
|
|
||||||
response = get_response(host, port, folder)
|
response = get_response(host, port, folder)
|
||||||
if(response.status == 401) then
|
if(response.status == 401) then
|
||||||
local vuln_response
|
local vuln_response
|
||||||
local check_folder
|
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
|
-- check for IIS 6.0 and 5.1
|
||||||
-- doesn't appear to work on 5.0
|
-- doesn't appear to work on 5.0
|
||||||
-- /secret/ becomes /s%c0%afecret/
|
-- /secret/ becomes /s%c0%afecret/
|
||||||
check_folder = string.sub(folder, 1, 2) .. "%c0%af" .. string.sub(folder, 3)
|
check_folder = string.sub(folder, 1, 2) .. "%c0%af" .. string.sub(folder, 3)
|
||||||
vuln_response = get_response(host, port, check_folder)
|
vuln_response = get_response(host, port, check_folder)
|
||||||
if(vuln_response.status == 207) then
|
if(vuln_response.status == 207) then
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder seems vulnerable: %s", folder)
|
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder seems vulnerable: %s", folder)
|
||||||
return enum_results.VULNERABLE
|
return enum_results.VULNERABLE
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder does not seem vulnerable: %s", folder)
|
stdnse.print_debug(1, "http-iis-webdav-vuln: Folder does not seem vulnerable: %s", folder)
|
||||||
return enum_results.NOT_VULNERABLE
|
return enum_results.NOT_VULNERABLE
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if(response['status-line'] ~= nil) then
|
if(response['status-line'] ~= nil) then
|
||||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status-line'], folder)
|
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status-line'], folder)
|
||||||
elseif(response['status'] ~= nil) then
|
elseif(response['status'] ~= nil) then
|
||||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status'], folder)
|
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder (%s): %s", response['status'], folder)
|
||||||
else
|
else
|
||||||
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder: %s",folder)
|
stdnse.print_debug(3, "http-iis-webdav-vuln: Not a protected folder: %s",folder)
|
||||||
end
|
end
|
||||||
return enum_results.UNKNOWN
|
return enum_results.UNKNOWN
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---Checks a list of possible folders for the vulnerability. Returns a list of vulnerable folders.
|
---Checks a list of possible folders for the vulnerability. Returns a list of vulnerable folders.
|
||||||
local function go(host, port)
|
local function go(host, port)
|
||||||
local status, folder
|
local status, folder
|
||||||
local results = {}
|
local results = {}
|
||||||
local is_vulnerable = true
|
local is_vulnerable = true
|
||||||
|
|
||||||
local folder_file
|
local folder_file
|
||||||
local farg = nmap.registry.args.folderdb
|
local farg = nmap.registry.args.folderdb
|
||||||
folder_file = farg and (nmap.fetchfile(farg) or farg) or nmap.fetchfile('nselib/data/http-folders.txt')
|
folder_file = farg and (nmap.fetchfile(farg) or farg) or nmap.fetchfile('nselib/data/http-folders.txt')
|
||||||
|
|
||||||
if(folder_file == nil) then
|
if(folder_file == nil) then
|
||||||
return false, "Couldn't find http-folders.txt (should be in nselib/data)"
|
return false, "Couldn't find http-folders.txt (should be in nselib/data)"
|
||||||
end
|
end
|
||||||
|
|
||||||
local file = io.open(folder_file, "r")
|
local file = io.open(folder_file, "r")
|
||||||
if not file then
|
if not file then
|
||||||
return false, ("Couldn't find or open %s"):format(folder_file)
|
return false, ("Couldn't find or open %s"):format(folder_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local result
|
local result
|
||||||
local line = file:read()
|
local line = file:read()
|
||||||
if not line then
|
if not line then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if(nmap.registry.args.basefolder ~= nil) then
|
if(nmap.registry.args.basefolder ~= nil) then
|
||||||
line = "/" .. nmap.registry.args.basefolder .. "/" .. line
|
line = "/" .. nmap.registry.args.basefolder .. "/" .. line
|
||||||
else
|
else
|
||||||
line = "/" .. line
|
line = "/" .. line
|
||||||
end
|
end
|
||||||
|
|
||||||
result = go_single(host, port, line)
|
result = go_single(host, port, line)
|
||||||
if(result == enum_results.VULNERABLE) then
|
if(result == enum_results.VULNERABLE) then
|
||||||
results[#results + 1] = line
|
results[#results + 1] = line
|
||||||
elseif(result == enum_results.NOT_VULNERABLE) then
|
elseif(result == enum_results.NOT_VULNERABLE) then
|
||||||
is_vulnerable = false
|
is_vulnerable = false
|
||||||
else
|
else
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
file:close()
|
file:close()
|
||||||
|
|
||||||
return true, results, is_vulnerable
|
return true, results, is_vulnerable
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
-- Start by checking if '/' is protected -- if it is, we can't do the tests
|
-- Start by checking if '/' is protected -- if it is, we can't do the tests
|
||||||
local result = go_single(host, port, "/")
|
local result = go_single(host, port, "/")
|
||||||
if(result == enum_results.NOT_VULNERABLE) then
|
if(result == enum_results.NOT_VULNERABLE) then
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: Root folder is password protected, aborting.")
|
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
|
return nmap.verbosity() > 0 and "Could not determine vulnerability, since root folder is password protected" or nil
|
||||||
end
|
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, "/")
|
local response = get_response(host, port, "/")
|
||||||
if(response.status == 501) then
|
if(response.status == 501) then
|
||||||
-- WebDAV is disabled
|
-- WebDAV is disabled
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is DISABLED (PROPFIND failed).")
|
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
|
return nmap.verbosity() > 0 and "WebDAV is DISABLED. Server is not currently vulnerable." or nil
|
||||||
else
|
else
|
||||||
if(response.status == 207) then
|
if(response.status == 207) then
|
||||||
-- PROPFIND works, WebDAV is enabled
|
-- PROPFIND works, WebDAV is enabled
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is ENABLED (PROPFIND was successful).")
|
stdnse.print_debug(1, "http-iis-webdav-vuln: WebDAV is ENABLED (PROPFIND was successful).")
|
||||||
else
|
else
|
||||||
-- probably not running IIS 5.0/5.1/6.0
|
-- probably not running IIS 5.0/5.1/6.0
|
||||||
if(response['status-line'] ~= nil) then
|
if(response['status-line'] ~= nil) then
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status-line'])
|
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status-line'])
|
||||||
elseif(response['status'] ~= nil) then
|
elseif(response['status'] ~= nil) then
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status'])
|
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed with \"%s\".", response['status'])
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed.")
|
stdnse.print_debug(1, "http-iis-webdav-vuln: PROPFIND request failed.")
|
||||||
end
|
end
|
||||||
return nmap.verbosity() > 0 and "ERROR: This web server is not supported." or nil
|
return nmap.verbosity() > 0 and "ERROR: This web server is not supported." or nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if(nmap.registry.args.webdavfolder ~= nil) then
|
if(nmap.registry.args.webdavfolder ~= nil) then
|
||||||
local folder = nmap.registry.args.webdavfolder
|
local folder = nmap.registry.args.webdavfolder
|
||||||
local result = go_single(host, port, "/" .. folder)
|
local result = go_single(host, port, "/" .. folder)
|
||||||
|
|
||||||
if(result == enum_results.VULNERABLE) then
|
if(result == enum_results.VULNERABLE) then
|
||||||
return string.format("WebDAV is ENABLED. Folder is vulnerable: %s", folder)
|
return string.format("WebDAV is ENABLED. Folder is vulnerable: %s", folder)
|
||||||
elseif(result == enum_results.NOT_VULNERABLE) then
|
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
|
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Folder is NOT vulnerable: %s", folder) or nil
|
||||||
else
|
else
|
||||||
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Could not determine vulnerability of folder: %s", folder) or nil
|
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Could not determine vulnerability of folder: %s", folder) or nil
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
local status, results, is_vulnerable = go(host, port)
|
local status, results, is_vulnerable = go(host, port)
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return nmap.verbosity() > 0 and "ERROR: " .. results or nil
|
return nmap.verbosity() > 0 and "ERROR: " .. results or nil
|
||||||
else
|
else
|
||||||
if(#results == 0) then
|
if(#results == 0) then
|
||||||
if(is_vulnerable == false) 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
|
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
|
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
|
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
|
end
|
||||||
else
|
else
|
||||||
return "WebDAV is ENABLED. Vulnerable folders discovered: " .. stdnse.strjoin(", ", results)
|
return "WebDAV is ENABLED. Vulnerable folders discovered: " .. stdnse.strjoin(", ", results)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -72,19 +72,19 @@ categories = {"intrusive", "vuln"}
|
|||||||
--@param response The HTTP response from the server.
|
--@param response The HTTP response from the server.
|
||||||
--@return The body of the HTTP response.
|
--@return The body of the HTTP response.
|
||||||
local validate = function(response)
|
local validate = function(response)
|
||||||
if not response.status then
|
if not response.status then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if response.status ~= 200 then
|
if response.status ~= 200 then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if response.body:match("^[^:]+:[^:]*:[0-9]+:[0-9]+:") or response.body:match("%[boot loader%]") then
|
if response.body:match("^[^:]+:[^:]*:[0-9]+:[0-9]+:") or response.body:match("%[boot loader%]") then
|
||||||
return response.body
|
return response.body
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Transforms a string with ".", "/" and "\" converted to their URL-formatted
|
--- Transforms a string with ".", "/" and "\" converted to their URL-formatted
|
||||||
@@ -92,19 +92,19 @@ end
|
|||||||
--@param str String to hexify.
|
--@param str String to hexify.
|
||||||
--@return Transformed string.
|
--@return Transformed string.
|
||||||
local hexify = function(str)
|
local hexify = function(str)
|
||||||
local ret
|
local ret
|
||||||
ret = str:gsub("%.", "%%2E")
|
ret = str:gsub("%.", "%%2E")
|
||||||
ret = ret:gsub("/", "%%2F")
|
ret = ret:gsub("/", "%%2F")
|
||||||
ret = ret:gsub("\\", "%%5C")
|
ret = ret:gsub("\\", "%%5C")
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Truncates the <code>passwd</code> or <code>boot.ini</code> file.
|
--- Truncates the <code>passwd</code> or <code>boot.ini</code> file.
|
||||||
--@param passwd <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.
|
--@return Truncated passwd file and truncated length.
|
||||||
local truncatePasswd = function(passwd)
|
local truncatePasswd = function(passwd)
|
||||||
local len = 250
|
local len = 250
|
||||||
return passwd:sub(1, len), len
|
return passwd:sub(1, len), len
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Formats output.
|
--- Formats output.
|
||||||
@@ -112,83 +112,83 @@ end
|
|||||||
--@param dir Formatted request which elicited the good reponse.
|
--@param dir Formatted request which elicited the good reponse.
|
||||||
--@return String description for output
|
--@return String description for output
|
||||||
local output = function(passwd, dir)
|
local output = function(passwd, dir)
|
||||||
local trunc, len = truncatePasswd(passwd)
|
local trunc, len = truncatePasswd(passwd)
|
||||||
local out = ""
|
local out = ""
|
||||||
out = out .. "Directory traversal found.\nPayload: \"" .. dir .. "\"\n"
|
out = out .. "Directory traversal found.\nPayload: \"" .. dir .. "\"\n"
|
||||||
out = out .. "Printing first " .. len .. " bytes:\n"
|
out = out .. "Printing first " .. len .. " bytes:\n"
|
||||||
out = out .. trunc
|
out = out .. trunc
|
||||||
return out
|
return out
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = shortport.http
|
portrule = shortport.http
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local dirs = {
|
local dirs = {
|
||||||
hexify("//etc/passwd"),
|
hexify("//etc/passwd"),
|
||||||
hexify(string.rep("../", 10) .. "etc/passwd"),
|
hexify(string.rep("../", 10) .. "etc/passwd"),
|
||||||
hexify(string.rep("../", 10) .. "boot.ini"),
|
hexify(string.rep("../", 10) .. "boot.ini"),
|
||||||
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"),
|
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
|
-- These don't get hexified because they are targeted at
|
||||||
-- specific known vulnerabilities.
|
-- specific known vulnerabilities.
|
||||||
'..\\\\..\\\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
'..\\\\..\\\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
||||||
--miniwebsvr
|
--miniwebsvr
|
||||||
'%c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./boot.ini',
|
'%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',
|
'%c0%2e%c0%2e/%c0%2e%c0%2e/%c0%2e%c0%2e/%c0%2e%c0%2e/boot.ini',
|
||||||
--Acritum Femitter Server
|
--Acritum Femitter Server
|
||||||
'\\\\..%2f..%2f..%2f..%2fboot.ini% ../',
|
'\\\\..%2f..%2f..%2f..%2fboot.ini% ../',
|
||||||
--zervit Web Server and several others
|
--zervit Web Server and several others
|
||||||
'index.html?../../../../../boot.ini',
|
'index.html?../../../../../boot.ini',
|
||||||
'index.html?..\\..\\..\\..\\..\\boot.ini',
|
'index.html?..\\..\\..\\..\\..\\boot.ini',
|
||||||
--Mongoose Web Server
|
--Mongoose Web Server
|
||||||
'///..%2f..%2f..%2f..%2fboot.ini',
|
'///..%2f..%2f..%2f..%2fboot.ini',
|
||||||
'/..%5C..%5C%5C..%5C..%5C%5C..%5C..%5C%5C..%5C..%5Cboot.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',
|
'/%c0%2e%c0%2e\\%c0%2e%c0%2e\\%c0%2e%c0%2e\\boot.ini',
|
||||||
-- Yaws 1.89
|
-- Yaws 1.89
|
||||||
'/..\\/..\\/..\\/boot.ini',
|
'/..\\/..\\/..\\/boot.ini',
|
||||||
'/..\\/\\..\\/\\..\\/\\boot.ini',
|
'/..\\/\\..\\/\\..\\/\\boot.ini',
|
||||||
'/\\../\\../\\../boot.ini',
|
'/\\../\\../\\../boot.ini',
|
||||||
'////..\\..\\..\\boot.ini',
|
'////..\\..\\..\\boot.ini',
|
||||||
--MultiThreaded HTTP Server v1.1
|
--MultiThreaded HTTP Server v1.1
|
||||||
'/..\\..\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
'/..\\..\\..\\..\\\\..\\..\\\\..\\..\\\\\\boot.ini',
|
||||||
--uHttp Server
|
--uHttp Server
|
||||||
'/../../../../../../../etc/passwd',
|
'/../../../../../../../etc/passwd',
|
||||||
--Java Mini Web Server
|
--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%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',
|
'/%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
|
for _, dir in ipairs(dirs) do
|
||||||
local response = http.get(host, port, dir)
|
local response = http.get(host, port, dir)
|
||||||
|
|
||||||
if validate(response) then
|
if validate(response) then
|
||||||
return output(response.body, dir)
|
return output(response.body, dir)
|
||||||
end
|
end
|
||||||
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
|
-- 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
|
-- "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
|
-- vectors. Add an encoded null byte at the end to bypass some checks; see
|
||||||
-- http://insecure.org/news/P55-01.txt.
|
-- http://insecure.org/news/P55-01.txt.
|
||||||
local response = http.get(host, port, root)
|
local response = http.get(host, port, root)
|
||||||
if response.body then
|
if response.body then
|
||||||
local page_var = response.body:match ("[%?%&](%a-)=%a-%.%a")
|
local page_var = response.body:match ("[%?%&](%a-)=%a-%.%a")
|
||||||
if page_var then
|
if page_var then
|
||||||
local query_base = root .. "?" .. page_var .. "="
|
local query_base = root .. "?" .. page_var .. "="
|
||||||
stdnse.print_debug(1, "%s: testing with query %s.", SCRIPT_NAME, query_base .. "...")
|
stdnse.print_debug(1, "%s: testing with query %s.", SCRIPT_NAME, query_base .. "...")
|
||||||
|
|
||||||
for _, dir in ipairs(dirs) do
|
for _, dir in ipairs(dirs) do
|
||||||
local response = http.get(host, port, query_base .. dir .. "%00")
|
local response = http.get(host, port, query_base .. dir .. "%00")
|
||||||
|
|
||||||
if validate(response) then
|
if validate(response) then
|
||||||
return output(response.body, dir)
|
return output(response.body, dir)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
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
|
check_response = function(body) return string.find(body, pattern_to_search) end
|
||||||
|
|
||||||
-- create a new crawler instance
|
-- 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
|
if ( not(crawler) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local return_table = {}
|
local return_table = {}
|
||||||
|
|
||||||
while(true) do
|
while(true) do
|
||||||
local status, r = crawler:crawl()
|
local status, r = crawler:crawl()
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
if ( r.err ) then
|
if ( r.err ) then
|
||||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- first we try rfi on forms
|
-- first we try rfi 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)
|
local all_forms = http.grab_forms(r.response.body)
|
||||||
for _,form_plain in ipairs(all_forms) do
|
for _,form_plain in ipairs(all_forms) do
|
||||||
local form = http.parse_form(form_plain)
|
local form = http.parse_form(form_plain)
|
||||||
@@ -206,7 +206,7 @@ function action(host, port)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end --for
|
end --for
|
||||||
end --if
|
end --if
|
||||||
|
|
||||||
-- now try inclusion by parameters
|
-- now try inclusion by parameters
|
||||||
local injectable = {}
|
local injectable = {}
|
||||||
@@ -232,7 +232,7 @@ function action(host, port)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, return_table)
|
return stdnse.format_output(true, return_table)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -108,8 +108,8 @@ local function build_injection_vector(urls)
|
|||||||
urlstr = url.build(utab)
|
urlstr = url.build(utab)
|
||||||
table.insert(all, urlstr)
|
table.insert(all, urlstr)
|
||||||
|
|
||||||
qtab[k] = old_qtab
|
qtab[k] = old_qtab
|
||||||
utab.query = url.build_query(qtab)
|
utab.query = url.build_query(qtab)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -242,7 +242,7 @@ action = function(host, port)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- first we try sqli on forms
|
-- 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)
|
local all_forms = http.grab_forms(r.response.body)
|
||||||
for _,form_plain in ipairs(all_forms) do
|
for _,form_plain in ipairs(all_forms) do
|
||||||
local form = http.parse_form(form_plain)
|
local form = http.parse_form(form_plain)
|
||||||
@@ -255,7 +255,7 @@ action = function(host, port)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end --for
|
end --for
|
||||||
end --if
|
end --if
|
||||||
local links = {}
|
local links = {}
|
||||||
if r.response.status and r.response.body then
|
if r.response.status and r.response.body then
|
||||||
links = httpspider.LinkExtractor:new(r.url, r.response.body, crawler.options):getLinks()
|
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.
|
-- Note, that more payloads will slow down your scan.
|
||||||
payloads = {
|
payloads = {
|
||||||
|
|
||||||
-- Basic vectors. Each one is 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 = '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 = '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." },
|
{ vector = 'zxc\'xcv', description = "Unfiltered ' (apostrophe). An indication of potential XSS vulnerability." },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
-- Create customized requests for all of our payloads.
|
-- Create customized requests for all of our payloads.
|
||||||
local makeRequests = function(host, port, submission, fields, fieldvalues)
|
local makeRequests = function(host, port, submission, fields, fieldvalues)
|
||||||
|
|
||||||
local postdata = {}
|
local postdata = {}
|
||||||
for _, p in ipairs(payloads) do
|
for _, p in ipairs(payloads) do
|
||||||
for __, field in ipairs(fields) do
|
for __, field in ipairs(fields) do
|
||||||
if field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
|
if field["type"] == "text" or field["type"] == "textarea" or field["type"] == "radio" or field["type"] == "checkbox" then
|
||||||
|
|
||||||
local value = fieldvalues[field["name"]]
|
local value = fieldvalues[field["name"]]
|
||||||
if value == nil then
|
if value == nil then
|
||||||
value = p.vector
|
value = p.vector
|
||||||
end
|
|
||||||
|
|
||||||
postdata[field["name"]] = value
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
postdata[field["name"]] = value
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
stdnse.print_debug(2, "Making a POST request to " .. submission .. ": ")
|
stdnse.print_debug(2, "Making a POST request to " .. submission .. ": ")
|
||||||
for i, content in pairs(postdata) do
|
for i, content in pairs(postdata) do
|
||||||
stdnse.print_debug(2, i .. ": " .. content)
|
stdnse.print_debug(2, i .. ": " .. content)
|
||||||
end
|
end
|
||||||
local response = http.post(host, port, submission, { no_cache = true }, nil, postdata)
|
local response = http.post(host, port, submission, { no_cache = true }, nil, postdata)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local checkPayload = function(body, p)
|
local checkPayload = function(body, p)
|
||||||
|
|
||||||
if (body:match(p)) then
|
if (body:match(p)) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array.
|
-- Check if the payloads were succesfull by checking the content of pages in the uploadspaths array.
|
||||||
local checkRequests = function(body, target)
|
local checkRequests = function(body, target)
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
for _, p in ipairs(payloads) do
|
for _, p in ipairs(payloads) do
|
||||||
if checkPayload(body, p.vector) then
|
if checkPayload(body, p.vector) then
|
||||||
local report = " Payload: " .. p.vector .. "\n\t Uploaded on: " .. target
|
local report = " Payload: " .. p.vector .. "\n\t Uploaded on: " .. target
|
||||||
if p.description then
|
if p.description then
|
||||||
report = report .. "\n\t Description: " .. p.description
|
report = report .. "\n\t Description: " .. p.description
|
||||||
end
|
end
|
||||||
table.insert(output, report)
|
table.insert(output, report)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return output
|
end
|
||||||
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
local readFromFile = function(filename)
|
local readFromFile = function(filename)
|
||||||
local database = { }
|
local database = { }
|
||||||
for l in io.lines(filename) do
|
for l in io.lines(filename) do
|
||||||
table.insert(payloads, { vector = l })
|
table.insert(payloads, { vector = l })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local formpaths = stdnse.get_script_args("http-stored-xss.formpaths")
|
local formpaths = stdnse.get_script_args("http-stored-xss.formpaths")
|
||||||
local uploadspaths = stdnse.get_script_args("http-stored-xss.uploadspaths")
|
local uploadspaths = stdnse.get_script_args("http-stored-xss.uploadspaths")
|
||||||
local fieldvalues = stdnse.get_script_args("http-stored-xss.fieldvalues") or {}
|
local fieldvalues = stdnse.get_script_args("http-stored-xss.fieldvalues") or {}
|
||||||
local dbfile = stdnse.get_script_args("http-stored-xss.dbfile")
|
local dbfile = stdnse.get_script_args("http-stored-xss.dbfile")
|
||||||
|
|
||||||
if dbfile then
|
if dbfile then
|
||||||
readFromFile(dbfile)
|
readFromFile(dbfile)
|
||||||
end
|
end
|
||||||
|
|
||||||
local returntable = {}
|
local returntable = {}
|
||||||
local result
|
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
|
if (not(crawler)) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
crawler:set_timeout(10000)
|
crawler:set_timeout(10000)
|
||||||
|
|
||||||
local index, k, target, response
|
local index, k, target, response
|
||||||
|
|
||||||
-- Phase 1. Crawls through the website and POSTs malicious payloads.
|
-- Phase 1. Crawls through the website and POSTs malicious payloads.
|
||||||
while (true) do
|
while (true) do
|
||||||
|
|
||||||
if formpaths then
|
if formpaths then
|
||||||
|
|
||||||
k, target = next(formpaths, index)
|
k, target = next(formpaths, index)
|
||||||
if (k == nil) then
|
if (k == nil) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
response = http.get(host, port, target, { no_cache = true })
|
response = http.get(host, port, target, { no_cache = true })
|
||||||
target = host.name .. target
|
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
|
|
||||||
else
|
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
|
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
|
end
|
||||||
|
|||||||
@@ -102,154 +102,154 @@ local arg_checksum = stdnse.get_script_args(SCRIPT_NAME .. ".checksum")
|
|||||||
prerule = function() return true end
|
prerule = function() return true end
|
||||||
|
|
||||||
local function readFile(filename)
|
local function readFile(filename)
|
||||||
local f = io.open(filename, "r")
|
local f = io.open(filename, "r")
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return false, ("Failed to open file: %s"):format(filename)
|
return false, ("Failed to open file: %s"):format(filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
local str = f:read("*all")
|
local str = f:read("*all")
|
||||||
if ( not(str) ) then
|
if ( not(str) ) then
|
||||||
f:close()
|
f:close()
|
||||||
return false, "Failed to read file contents"
|
return false, "Failed to read file contents"
|
||||||
end
|
end
|
||||||
f:close()
|
f:close()
|
||||||
return true, str
|
return true, str
|
||||||
end
|
end
|
||||||
|
|
||||||
local function requestFileScan(filename)
|
local function requestFileScan(filename)
|
||||||
local status, str = readFile(filename)
|
local status, str = readFile(filename)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, str
|
return false, str
|
||||||
end
|
end
|
||||||
|
|
||||||
local shortfile = filename:match("^.*[\\/](.*)$")
|
local shortfile = filename:match("^.*[\\/](.*)$")
|
||||||
local boundary = "----------------------------nmapboundary"
|
local boundary = "----------------------------nmapboundary"
|
||||||
local header = { ["Content-Type"] = ("multipart/form-data; boundary=%s"):format(boundary) }
|
local header = { ["Content-Type"] = ("multipart/form-data; boundary=%s"):format(boundary) }
|
||||||
local postdata = ("--%s\r\n"):format(boundary)
|
local postdata = ("--%s\r\n"):format(boundary)
|
||||||
postdata = postdata .. "Content-Disposition: form-data; name=\"apikey\"\r\n\r\n"
|
postdata = postdata .. "Content-Disposition: form-data; name=\"apikey\"\r\n\r\n"
|
||||||
postdata = postdata .. arg_apiKey .. "\r\n"
|
postdata = postdata .. arg_apiKey .. "\r\n"
|
||||||
postdata = postdata .. ("--%s\r\n" ..
|
postdata = postdata .. ("--%s\r\n" ..
|
||||||
"Content-Disposition: form-data; name=\"file\"; filename=\"%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)
|
"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 host = "www.virustotal.com"
|
||||||
local port = { number = 80, protocol = "tcp" }
|
local port = { number = 80, protocol = "tcp" }
|
||||||
local path = "/vtapi/v2/file/scan"
|
local path = "/vtapi/v2/file/scan"
|
||||||
|
|
||||||
local response = http.post( host, port, path, { header = header }, nil, postdata )
|
local response = http.post( host, port, path, { header = header }, nil, postdata )
|
||||||
if ( not(response) or response.status ~= 200 ) then
|
if ( not(response) or response.status ~= 200 ) then
|
||||||
return false, "Failed to request file scan"
|
return false, "Failed to request file scan"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, json_data = json.parse(response.body)
|
local status, json_data = json.parse(response.body)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to parse JSON response"
|
return false, "Failed to parse JSON response"
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, json_data
|
return true, json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getFileScanReport(resource)
|
local function getFileScanReport(resource)
|
||||||
|
|
||||||
local host = "www.virustotal.com"
|
local host = "www.virustotal.com"
|
||||||
local port = { number = 80, protocol = "tcp" }
|
local port = { number = 80, protocol = "tcp" }
|
||||||
local path = "/vtapi/v2/file/report"
|
local path = "/vtapi/v2/file/report"
|
||||||
|
|
||||||
|
|
||||||
local response = http.post(host, port, path, nil, nil, { ["apikey"] = arg_apiKey, ["resource"] = resource })
|
local response = http.post(host, port, path, nil, nil, { ["apikey"] = arg_apiKey, ["resource"] = resource })
|
||||||
if ( not(response) or response.status ~= 200 ) then
|
if ( not(response) or response.status ~= 200 ) then
|
||||||
return false, "Failed to retrieve scan report"
|
return false, "Failed to retrieve scan report"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, json_data = json.parse(response.body)
|
local status, json_data = json.parse(response.body)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to parse JSON response"
|
return false, "Failed to parse JSON response"
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, json_data
|
return true, json_data
|
||||||
end
|
end
|
||||||
|
|
||||||
local function calcSHA256(filename)
|
local function calcSHA256(filename)
|
||||||
|
|
||||||
local status, str = readFile(filename)
|
local status, str = readFile(filename)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, str
|
return false, str
|
||||||
end
|
end
|
||||||
return true, stdnse.tohex(openssl.digest("sha256", str))
|
return true, stdnse.tohex(openssl.digest("sha256", str))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parseScanReport(report)
|
local function parseScanReport(report)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
table.insert(result, ("Permalink: %s"):format(report.permalink))
|
table.insert(result, ("Permalink: %s"):format(report.permalink))
|
||||||
table.insert(result, ("Scan date: %s"):format(report.scan_date))
|
table.insert(result, ("Scan date: %s"):format(report.scan_date))
|
||||||
table.insert(result, ("Positives: %s"):format(report.positives))
|
table.insert(result, ("Positives: %s"):format(report.positives))
|
||||||
table.insert(result, {
|
table.insert(result, {
|
||||||
name = "digests",
|
name = "digests",
|
||||||
("SHA1: %s"):format(report.sha1),
|
("SHA1: %s"):format(report.sha1),
|
||||||
("SHA256: %s"):format(report.sha256),
|
("SHA256: %s"):format(report.sha256),
|
||||||
("MD5: %s"):format(report.md5)
|
("MD5: %s"):format(report.md5)
|
||||||
})
|
})
|
||||||
|
|
||||||
local tmp = {}
|
local tmp = {}
|
||||||
for name, scanres in pairs(report.scans) do
|
for name, scanres in pairs(report.scans) do
|
||||||
local res = ( scanres.detected ) and scanres.result or "-"
|
local res = ( scanres.detected ) and scanres.result or "-"
|
||||||
table.insert(tmp, { name = name, result = res, update = scanres.update, version = scanres.version })
|
table.insert(tmp, { name = name, result = res, update = scanres.update, version = scanres.version })
|
||||||
end
|
end
|
||||||
table.sort(tmp, function(a,b) return a.name:upper()<b.name:upper() end)
|
table.sort(tmp, function(a,b) return a.name:upper()<b.name:upper() end)
|
||||||
|
|
||||||
local scan_tbl = tab.new(4)
|
local scan_tbl = tab.new(4)
|
||||||
tab.addrow(scan_tbl, "name", "result", "date", "version")
|
tab.addrow(scan_tbl, "name", "result", "date", "version")
|
||||||
for _, v in ipairs(tmp) do
|
for _, v in ipairs(tmp) do
|
||||||
tab.addrow(scan_tbl, v.name, v.result, v.update, v.version)
|
tab.addrow(scan_tbl, v.name, v.result, v.update, v.version)
|
||||||
end
|
end
|
||||||
table.insert(result, { name = "Results", tab.dump(scan_tbl) })
|
table.insert(result, { name = "Results", tab.dump(scan_tbl) })
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
if ( not(arg_apiKey) ) then
|
if ( not(arg_apiKey) ) then
|
||||||
return fail("An API key is required in order to use this script (see description)")
|
return fail("An API key is required in order to use this script (see description)")
|
||||||
end
|
end
|
||||||
|
|
||||||
local resource
|
local resource
|
||||||
if ( arg_upload == "true" and arg_filename ) then
|
if ( arg_upload == "true" and arg_filename ) then
|
||||||
local status, json_data = requestFileScan(arg_filename, arg_apiKey)
|
local status, json_data = requestFileScan(arg_filename, arg_apiKey)
|
||||||
if ( not(status) or not(json_data['resource']) ) then
|
if ( not(status) or not(json_data['resource']) ) then
|
||||||
return fail(json_data)
|
return fail(json_data)
|
||||||
end
|
end
|
||||||
resource = json_data['resource']
|
resource = json_data['resource']
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
table.insert(output, "Your file was succesfully uploaded and placed in the scanning queue.")
|
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'] })
|
table.insert(output, { name = "To check the current status visit:", json_data['permalink'] })
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
elseif ( arg_filename ) then
|
elseif ( arg_filename ) then
|
||||||
local status, sha256 = calcSHA256(arg_filename)
|
local status, sha256 = calcSHA256(arg_filename)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail("Failed to calculate SHA256 checksum for file")
|
return fail("Failed to calculate SHA256 checksum for file")
|
||||||
end
|
end
|
||||||
resource = sha256
|
resource = sha256
|
||||||
elseif ( arg_checksum ) then
|
elseif ( arg_checksum ) then
|
||||||
resource = arg_checksum
|
resource = arg_checksum
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, response
|
local status, response
|
||||||
|
|
||||||
local status, response = getFileScanReport(resource)
|
local status, response = getFileScanReport(resource)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail("Failed to retrieve file scan report")
|
return fail("Failed to retrieve file scan report")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(response.response_code) or 0 == tonumber(response.response_code) ) then
|
if ( not(response.response_code) or 0 == tonumber(response.response_code) ) then
|
||||||
return fail(("Failed to retreive scan report for resource: %s"):format(resource))
|
return fail(("Failed to retreive scan report for resource: %s"):format(resource))
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, parseScanReport(response))
|
return stdnse.format_output(true, parseScanReport(response))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -43,17 +43,17 @@ local ipidseqport
|
|||||||
--- Pcap check function
|
--- Pcap check function
|
||||||
-- @return Destination and source IP addresses and TCP ports
|
-- @return Destination and source IP addresses and TCP ports
|
||||||
local check = function(layer3)
|
local check = function(layer3)
|
||||||
local ip = packet.Packet:new(layer3, layer3:len())
|
local ip = packet.Packet:new(layer3, layer3:len())
|
||||||
return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport)
|
return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Updates a TCP Packet object
|
--- Updates a TCP Packet object
|
||||||
-- @param tcp The TCP object
|
-- @param tcp The TCP object
|
||||||
local updatepkt = function(tcp)
|
local updatepkt = function(tcp)
|
||||||
tcp:tcp_set_sport(math.random(0x401, 0xffff))
|
tcp:tcp_set_sport(math.random(0x401, 0xffff))
|
||||||
tcp:tcp_set_seq(math.random(1, 0x7fffffff))
|
tcp:tcp_set_seq(math.random(1, 0x7fffffff))
|
||||||
tcp:tcp_count_checksum(tcp.ip_len)
|
tcp:tcp_count_checksum(tcp.ip_len)
|
||||||
tcp:ip_count_checksum()
|
tcp:ip_count_checksum()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Create a TCP Packet object
|
--- Create a TCP Packet object
|
||||||
@@ -61,192 +61,192 @@ end
|
|||||||
-- @param port Port number
|
-- @param port Port number
|
||||||
-- @return TCP Packet object
|
-- @return TCP Packet object
|
||||||
local genericpkt = function(host, port)
|
local genericpkt = function(host, port)
|
||||||
local pkt = bin.pack("H",
|
local pkt = bin.pack("H",
|
||||||
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
|
"4500 002c 55d1 0000 8006 0000 0000 0000" ..
|
||||||
"0000 0000 0000 0000 0000 0000 0000 0000" ..
|
"0000 0000 0000 0000 0000 0000 0000 0000" ..
|
||||||
"6002 0c00 0000 0000 0204 05b4"
|
"6002 0c00 0000 0000 0204 05b4"
|
||||||
)
|
)
|
||||||
|
|
||||||
local tcp = packet.Packet:new(pkt, pkt:len())
|
local tcp = packet.Packet:new(pkt, pkt:len())
|
||||||
|
|
||||||
tcp:ip_set_bin_src(host.bin_ip_src)
|
tcp:ip_set_bin_src(host.bin_ip_src)
|
||||||
tcp:ip_set_bin_dst(host.bin_ip)
|
tcp:ip_set_bin_dst(host.bin_ip)
|
||||||
tcp:tcp_set_dport(port)
|
tcp:tcp_set_dport(port)
|
||||||
|
|
||||||
updatepkt(tcp)
|
updatepkt(tcp)
|
||||||
|
|
||||||
return tcp
|
return tcp
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Classifies a series of IP ID numbers like get_ipid_sequence() in osscan2.cc
|
--- Classifies a series of IP ID numbers like get_ipid_sequence() in osscan2.cc
|
||||||
-- @param ipids Table of IP IDs
|
-- @param ipids Table of IP IDs
|
||||||
local ipidseqclass = function(ipids)
|
local ipidseqclass = function(ipids)
|
||||||
local diffs = {}
|
local diffs = {}
|
||||||
local allzeros = true
|
local allzeros = true
|
||||||
local allsame = true
|
local allsame = true
|
||||||
local mul256 = true
|
local mul256 = true
|
||||||
local inc = true
|
local inc = true
|
||||||
|
|
||||||
if #ipids < 2 then
|
if #ipids < 2 then
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
end
|
end
|
||||||
|
|
||||||
local i = 2
|
local i = 2
|
||||||
|
|
||||||
while i <= #ipids do
|
while i <= #ipids do
|
||||||
if ipids[i-1] ~= 0 or ipids[i] ~= 0 then
|
if ipids[i-1] ~= 0 or ipids[i] ~= 0 then
|
||||||
allzeros = false
|
allzeros = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if ipids[i-1] <= ipids[i] then
|
if ipids[i-1] <= ipids[i] then
|
||||||
diffs[i-1] = ipids[i] - ipids[i-1]
|
diffs[i-1] = ipids[i] - ipids[i-1]
|
||||||
else
|
else
|
||||||
diffs[i-1] = ipids[i] - ipids[i-1] + 65536
|
diffs[i-1] = ipids[i] - ipids[i-1] + 65536
|
||||||
end
|
end
|
||||||
|
|
||||||
if #ipids > 2 and diffs[i-1] > 20000 then
|
if #ipids > 2 and diffs[i-1] > 20000 then
|
||||||
return "Randomized"
|
return "Randomized"
|
||||||
end
|
end
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if allzeros then
|
if allzeros then
|
||||||
return "All zeros"
|
return "All zeros"
|
||||||
end
|
end
|
||||||
|
|
||||||
i = 1
|
i = 1
|
||||||
|
|
||||||
while i <= #diffs do
|
while i <= #diffs do
|
||||||
if diffs[i] ~= 0 then
|
if diffs[i] ~= 0 then
|
||||||
allsame = false
|
allsame = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if (diffs[i] > 1000) and ((diffs[i] % 256) ~= 0 or
|
if (diffs[i] > 1000) and ((diffs[i] % 256) ~= 0 or
|
||||||
((diffs[i] % 256) == 0 and diffs[i] > 25600)) then
|
((diffs[i] % 256) == 0 and diffs[i] > 25600)) then
|
||||||
return "Random Positive Increments"
|
return "Random Positive Increments"
|
||||||
end
|
end
|
||||||
|
|
||||||
if diffs[i] > 5120 or (diffs[i] % 256) ~= 0 then
|
if diffs[i] > 5120 or (diffs[i] % 256) ~= 0 then
|
||||||
mul256 = false
|
mul256 = false
|
||||||
end
|
end
|
||||||
|
|
||||||
if diffs[i] >= 10 then
|
if diffs[i] >= 10 then
|
||||||
inc = false
|
inc = false
|
||||||
end
|
end
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if allsame then
|
if allsame then
|
||||||
return "Constant"
|
return "Constant"
|
||||||
end
|
end
|
||||||
|
|
||||||
if mul256 then
|
if mul256 then
|
||||||
return "Broken incremental!"
|
return "Broken incremental!"
|
||||||
end
|
end
|
||||||
|
|
||||||
if inc then
|
if inc then
|
||||||
return "Incremental!"
|
return "Incremental!"
|
||||||
end
|
end
|
||||||
|
|
||||||
return "Unknown"
|
return "Unknown"
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Determines what port to probe
|
--- Determines what port to probe
|
||||||
-- @param host Host object
|
-- @param host Host object
|
||||||
local getport = function(host)
|
local getport = function(host)
|
||||||
for _, k in ipairs({"ipidseq.probeport", "probeport"}) do
|
for _, k in ipairs({"ipidseq.probeport", "probeport"}) do
|
||||||
if nmap.registry.args[k] then
|
if nmap.registry.args[k] then
|
||||||
return tonumber(nmap.registry.args[k])
|
return tonumber(nmap.registry.args[k])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--local states = { "open", "closed", "unfiltered", "open|filtered", "closed|filtered" }
|
--local states = { "open", "closed", "unfiltered", "open|filtered", "closed|filtered" }
|
||||||
local states = { "open", "closed" }
|
local states = { "open", "closed" }
|
||||||
local port = nil
|
local port = nil
|
||||||
|
|
||||||
for _, s in ipairs(states) do
|
for _, s in ipairs(states) do
|
||||||
port = nmap.get_ports(host, nil, "tcp", s)
|
port = nmap.get_ports(host, nil, "tcp", s)
|
||||||
if port then
|
if port then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not port then
|
if not port then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return port.number
|
return port.number
|
||||||
end
|
end
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
if not nmap.registry[SCRIPT_NAME].rootfail then
|
if not nmap.registry[SCRIPT_NAME].rootfail then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
nmap.registry[SCRIPT_NAME].rootfail = true
|
nmap.registry[SCRIPT_NAME].rootfail = true
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if nmap.address_family() ~= 'inet' then
|
if nmap.address_family() ~= 'inet' then
|
||||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if not host.interface then
|
if not host.interface then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
ipidseqport = getport(host)
|
ipidseqport = getport(host)
|
||||||
return (ipidseqport ~= nil)
|
return (ipidseqport ~= nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local i = 1
|
local i = 1
|
||||||
local ipids = {}
|
local ipids = {}
|
||||||
local sock = nmap.new_dnet()
|
local sock = nmap.new_dnet()
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
local saddr = packet.toip(host.bin_ip_src)
|
local saddr = packet.toip(host.bin_ip_src)
|
||||||
local daddr = packet.toip(host.bin_ip)
|
local daddr = packet.toip(host.bin_ip)
|
||||||
local try = nmap.new_try()
|
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
|
while i <= NUMPROBES do
|
||||||
try(sock:ip_send(tcp.buf, host))
|
try(sock:ip_send(tcp.buf, host))
|
||||||
|
|
||||||
local status, len, _, layer3 = pcap:pcap_receive()
|
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)
|
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
|
while status and test ~= check(layer3) do
|
||||||
status, len, _, layer3 = pcap:pcap_receive()
|
status, len, _, layer3 = pcap:pcap_receive()
|
||||||
end
|
end
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
table.insert(ipids, packet.u16(layer3, 4))
|
table.insert(ipids, packet.u16(layer3, 4))
|
||||||
end
|
end
|
||||||
|
|
||||||
updatepkt(tcp)
|
updatepkt(tcp)
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
pcap:close()
|
pcap:close()
|
||||||
sock:ip_close()
|
sock:ip_close()
|
||||||
|
|
||||||
local output = ipidseqclass(ipids)
|
local output = ipidseqclass(ipids)
|
||||||
|
|
||||||
if nmap.debugging() > 0 then
|
if nmap.debugging() > 0 then
|
||||||
output = output .. " [used port " .. ipidseqport .. "]"
|
output = output .. " [used port " .. ipidseqport .. "]"
|
||||||
end
|
end
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -43,50 +43,50 @@ try = nmap.new_try()
|
|||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if nmap.address_family() ~= "inet6" then
|
if nmap.address_family() ~= "inet6" then
|
||||||
stdnse.print_debug("%s is IPv6 compatible only.", SCRIPT_NAME)
|
stdnse.print_debug("%s is IPv6 compatible only.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_debug("Running %s needs root privileges.", SCRIPT_NAME)
|
stdnse.print_debug("Running %s needs root privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not stdnse.get_script_args(SCRIPT_NAME .. ".interface") and not nmap.get_interface() then
|
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)
|
stdnse.print_debug("No interface was selected, aborting...", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_interface()
|
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
|
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||||
return if_table.device
|
return if_table.device
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Interface %s not supported or not properly configured, exiting...", arg_interface)
|
stdnse.print_debug("Interface %s not supported or not properly configured, exiting...", arg_interface)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Generates random MAC address
|
--- Generates random MAC address
|
||||||
-- @return mac string containing random MAC address
|
-- @return mac string containing random MAC address
|
||||||
local function random_mac()
|
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)
|
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
|
return mac
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Generates random IPv6 prefix
|
--- Generates random IPv6 prefix
|
||||||
-- @return prefix string containing random IPv6 /64 prefix
|
-- @return prefix string containing random IPv6 /64 prefix
|
||||||
local function get_random_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
|
end
|
||||||
|
|
||||||
--- Build an ICMPv6 payload of Router Advertisement.
|
--- Build an ICMPv6 payload of Router Advertisement.
|
||||||
@@ -99,94 +99,94 @@ end
|
|||||||
-- @return icmpv6_payload string representing ICMPv6 RA payload
|
-- @return icmpv6_payload string representing ICMPv6 RA payload
|
||||||
|
|
||||||
local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferred_time, mtu)
|
local function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferred_time, mtu)
|
||||||
local ra_msg = string.char(0x0, --cur hop limit
|
local ra_msg = string.char(0x0, --cur hop limit
|
||||||
0x08, --flags
|
0x08, --flags
|
||||||
0x00,0x00, --router lifetime
|
0x00,0x00, --router lifetime
|
||||||
0x00,0x00,0x00,0x00, --reachable time
|
0x00,0x00,0x00,0x00, --reachable time
|
||||||
0x00,0x00,0x00,0x00) --retrans timer
|
0x00,0x00,0x00,0x00) --retrans timer
|
||||||
|
|
||||||
local mtu_option_msg = string.char(0x00, 0x00) .. -- reserved
|
local mtu_option_msg = string.char(0x00, 0x00) .. -- reserved
|
||||||
packet.numtostr32(mtu) -- MTU
|
packet.numtostr32(mtu) -- MTU
|
||||||
|
|
||||||
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
||||||
packet.set_u32("....", 0, valid_time) .. -- valid lifetime
|
packet.set_u32("....", 0, valid_time) .. -- valid lifetime
|
||||||
packet.set_u32("....", 0, preferred_time) .. -- preffered lifetime
|
packet.set_u32("....", 0, preferred_time) .. -- preffered lifetime
|
||||||
string.char(0,0,0,0) .. --unknown
|
string.char(0,0,0,0) .. --unknown
|
||||||
prefix
|
prefix
|
||||||
|
|
||||||
local icmpv6_mtu_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_MTU, mtu_option_msg)
|
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_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_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
|
end
|
||||||
|
|
||||||
--- Broadcasting on the selected interface
|
--- Broadcasting on the selected interface
|
||||||
-- @param iface table containing interface information
|
-- @param iface table containing interface information
|
||||||
local function broadcast_on_interface(iface)
|
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
|
-- packet counter
|
||||||
local counter = 0
|
local counter = 0
|
||||||
|
|
||||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME..".timeout"))
|
||||||
arg_timeout = arg_timeout or 30
|
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_mac = packet.mactobin("33:33:00:00:00:01")
|
||||||
local dst_ip6_addr = packet.ip6tobin("ff02::1")
|
local dst_ip6_addr = packet.ip6tobin("ff02::1")
|
||||||
|
|
||||||
local prefix_len = 64
|
local prefix_len = 64
|
||||||
|
|
||||||
--- maximum possible value of 4-byte integer
|
--- maximum possible value of 4-byte integer
|
||||||
local valid_time = tonumber(0xffffffff)
|
local valid_time = tonumber(0xffffffff)
|
||||||
local preffered_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_mac = packet.mactobin(random_mac())
|
||||||
local src_ip6_addr = packet.mac_to_lladdr(src_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_src = src_mac
|
||||||
packet.mac_dst = dst_mac
|
packet.mac_dst = dst_mac
|
||||||
packet.ip_bin_src = src_ip6_addr
|
packet.ip_bin_src = src_ip6_addr
|
||||||
packet.ip_bin_dst = dst_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)
|
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_icmpv6_header(134, 0, icmpv6_payload)
|
||||||
packet:build_ipv6_packet()
|
packet:build_ipv6_packet()
|
||||||
packet:build_ether_frame()
|
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
|
if arg_timeout and arg_timeout > 0 and arg_timeout <= os.time() - start then
|
||||||
stop = os.time()
|
stop = os.time()
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if counter > 0 then
|
if counter > 0 then
|
||||||
stdnse.print_debug("%s generated %d packets in %d seconds.", SCRIPT_NAME, counter, stop - start)
|
stdnse.print_debug("%s generated %d packets in %d seconds.", SCRIPT_NAME, counter, stop - start)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function action()
|
function action()
|
||||||
local interface = get_interface()
|
local interface = get_interface()
|
||||||
|
|
||||||
broadcast_on_interface(interface)
|
broadcast_on_interface(interface)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,159 +58,159 @@ portrule = shortport.port_or_service({6666,6667,6697,6679,8067},{"irc","ircs"})
|
|||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local code, message
|
local code, message
|
||||||
local status, err
|
local status, err
|
||||||
local data
|
local data
|
||||||
-- Wait up to this long for the server to send its startup messages and
|
-- 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.
|
-- 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
|
-- Usually we don't have to wait the full time because we can detect
|
||||||
-- the response to noop_command.
|
-- the response to noop_command.
|
||||||
local banner_timeout = 60
|
local banner_timeout = 60
|
||||||
-- Send a command to sleep this long. This just has to be long enough
|
-- Send a command to sleep this long. This just has to be long enough
|
||||||
-- to remove confusion from network delay.
|
-- to remove confusion from network delay.
|
||||||
local delay = 8
|
local delay = 8
|
||||||
|
|
||||||
-- If the command takes (delay - delay_fudge) or more seconds, the server is vulnerable.
|
-- 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,
|
-- 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.
|
-- the actual delay should never be shorter than the given delay, only longer.
|
||||||
local delay_fudge = 1
|
local delay_fudge = 1
|
||||||
|
|
||||||
-- We send this command on connection because comm.tryssl needs to send
|
-- We send this command on connection because comm.tryssl needs to send
|
||||||
-- something; it also allows us to detect the end of server
|
-- something; it also allows us to detect the end of server
|
||||||
-- initialization.
|
-- initialization.
|
||||||
local noop_command = "TIME"
|
local noop_command = "TIME"
|
||||||
|
|
||||||
-- The 'AB' sequence triggers the backdoor to run a command.
|
-- The 'AB' sequence triggers the backdoor to run a command.
|
||||||
local trigger = "AB"
|
local trigger = "AB"
|
||||||
|
|
||||||
-- We define a highly unique variable as a type of 'ping' -- it lets us see when our
|
-- 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
|
-- command returns. Typically, asynchronous data will be received after the initial
|
||||||
-- connection -- this lets us ignore that extra data.
|
-- connection -- this lets us ignore that extra data.
|
||||||
local unique = "SOMETHINGUNIQUE"
|
local unique = "SOMETHINGUNIQUE"
|
||||||
|
|
||||||
-- On Linux, do a simple sleep command.
|
-- On Linux, do a simple sleep command.
|
||||||
local command_linux = "sleep " .. delay
|
local command_linux = "sleep " .. delay
|
||||||
|
|
||||||
-- Set up an extra command, if the user requested one
|
-- Set up an extra command, if the user requested one
|
||||||
local command_extra = ""
|
local command_extra = ""
|
||||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.command')) then
|
if(stdnse.get_script_args('irc-unrealircd-backdoor.command')) then
|
||||||
command_extra = stdnse.get_script_args('irc-unrealircd-backdoor.command')
|
command_extra = stdnse.get_script_args('irc-unrealircd-backdoor.command')
|
||||||
-- Replace "%IP%" with the ip address
|
-- Replace "%IP%" with the ip address
|
||||||
command_extra = string.gsub(command_extra, '%%IP%%', host.ip)
|
command_extra = string.gsub(command_extra, '%%IP%%', host.ip)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Windows, unfortunately, doesn't have a sleep command. Instead, we use 'ping' to
|
-- 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
|
-- 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.
|
-- to add 1 to the delay because the first ping happens instantly.
|
||||||
--
|
--
|
||||||
-- This is likely unnecessary, because the Windows version of UnrealIRCd is reportedly
|
-- 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
|
-- not vulnerable. However, it's possible that some odd person may have compiled it
|
||||||
-- from the vulnerable sourcecode, so we check for it anyways.
|
-- from the vulnerable sourcecode, so we check for it anyways.
|
||||||
local command_windows = "ping -n " .. (delay + 1) .. " 127.0.0.1"
|
local command_windows = "ping -n " .. (delay + 1) .. " 127.0.0.1"
|
||||||
|
|
||||||
-- Put together the full command
|
-- Put together the full command
|
||||||
local full_command = string.format("%s;%s;%s;%s;%s", trigger, unique, command_linux, command_windows, command_extra)
|
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
|
-- wait time: get rid of fast reconnecting annoyance
|
||||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.wait')) then
|
if(stdnse.get_script_args('irc-unrealircd-backdoor.wait')) then
|
||||||
local waittime = stdnse.get_script_args('irc-unrealircd-backdoor.wait')
|
local waittime = stdnse.get_script_args('irc-unrealircd-backdoor.wait')
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: waiting for %i seconds", waittime)
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: waiting for %i seconds", waittime)
|
||||||
stdnse.sleep(waittime)
|
stdnse.sleep(waittime)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send an innocuous command as fodder for tryssl.
|
-- Send an innocuous command as fodder for tryssl.
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", noop_command);
|
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})
|
local socket, response = comm.tryssl(host, port, noop_command .. "\n", {recv_before=false})
|
||||||
|
|
||||||
-- Make sure the socket worked
|
-- Make sure the socket worked
|
||||||
if(not(socket) or not(response)) then
|
if(not(socket) or not(response)) then
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Couldn't connect to remote host")
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: Couldn't connect to remote host")
|
||||||
return nil
|
return nil
|
||||||
end
|
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
|
-- Look for the end of initial server messages. This allows reverse DNS
|
||||||
-- resolution and ident lookups to time out and not interfere with our
|
-- resolution and ident lookups to time out and not interfere with our
|
||||||
-- timing measurement.
|
-- timing measurement.
|
||||||
status = true
|
status = true
|
||||||
data = response
|
data = response
|
||||||
while status and not (string.find(data, noop_command) or string.find(data, " 451 ")) do
|
while status and not (string.find(data, noop_command) or string.find(data, " 451 ")) do
|
||||||
status, response = socket:receive_bytes(0)
|
status, response = socket:receive_bytes(0)
|
||||||
if status then
|
if status then
|
||||||
data = data .. response
|
data = data .. response
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed after %s: %s", noop_command, response)
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed after %s: %s", noop_command, response)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send the backdoor command.
|
-- Send the backdoor command.
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", full_command);
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: Sending command: %s", full_command);
|
||||||
status, err = socket:send(full_command .. "\n")
|
status, err = socket:send(full_command .. "\n")
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Send failed: %s", err)
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: Send failed: %s", err)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the current time so we can measure the delay
|
-- Get the current time so we can measure the delay
|
||||||
local time = os.time(os.date('*t'))
|
local time = os.time(os.date('*t'))
|
||||||
socket:set_timeout((delay + 5) * 1000)
|
socket:set_timeout((delay + 5) * 1000)
|
||||||
|
|
||||||
-- Accumulate the response in the 'data' string
|
-- Accumulate the response in the 'data' string
|
||||||
status = true
|
status = true
|
||||||
data = ""
|
data = ""
|
||||||
while not string.find(data, unique) do
|
while not string.find(data, unique) do
|
||||||
status, response = socket:receive_bytes(0)
|
status, response = socket:receive_bytes(0)
|
||||||
if status then
|
if status then
|
||||||
data = data .. response
|
data = data .. response
|
||||||
else
|
else
|
||||||
-- If the server unexpectedly closes the connection, it
|
-- If the server unexpectedly closes the connection, it
|
||||||
-- is usually related to throttling. Therefore, we
|
-- is usually related to throttling. Therefore, we
|
||||||
-- print a throttling warning.
|
-- print a throttling warning.
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed: %s", response)
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: Receive failed: %s", response)
|
||||||
socket:close()
|
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)."
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Determine the elapsed time
|
-- Determine the elapsed time
|
||||||
local elapsed = os.time(os.date('*t')) - time
|
local elapsed = os.time(os.date('*t')) - time
|
||||||
|
|
||||||
-- Let the user know that everything's working
|
-- Let the user know that everything's working
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Received a response to our command in " .. elapsed .. " seconds")
|
stdnse.print_debug(1, "irc-unrealircd-backdoor: Received a response to our command in " .. elapsed .. " seconds")
|
||||||
|
|
||||||
-- Determine whether or not the vulnerability is present
|
-- Determine whether or not the vulnerability is present
|
||||||
if(elapsed > (delay - delay_fudge)) then
|
if(elapsed > (delay - delay_fudge)) then
|
||||||
-- Check if the user wants to kill the server.
|
-- Check if the user wants to kill the server.
|
||||||
if(stdnse.get_script_args('irc-unrealircd-backdoor.kill')) then
|
if(stdnse.get_script_args('irc-unrealircd-backdoor.kill')) then
|
||||||
stdnse.print_debug(1, "irc-unrealircd-backdoor: Attempting to kill the Trojanned UnrealIRCd server...")
|
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 linux_kill = "kill `ps -e | grep ircd | awk '{ print $1 }'`"
|
||||||
local windows_kill = 'wmic process where "name like \'%ircd%\'" delete'
|
local windows_kill = 'wmic process where "name like \'%ircd%\'" delete'
|
||||||
local kill_command = string.format("%s||%s||%s", trigger, linux_kill, windows_kill)
|
local kill_command = string.format("%s||%s||%s", trigger, linux_kill, windows_kill)
|
||||||
|
|
||||||
-- Kill the process
|
-- Kill the process
|
||||||
stdnse.print_debug(1, "Running kill command: %s", kill_command)
|
stdnse.print_debug(1, "Running kill command: %s", kill_command)
|
||||||
socket:send(kill_command .. "\n")
|
socket:send(kill_command .. "\n")
|
||||||
end
|
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
|
-- Close the socket
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
return "Looks like trojaned version of unrealircd. See http://seclists.org/fulldisclosure/2010/Jun/277"
|
return "Looks like trojaned version of unrealircd. See http://seclists.org/fulldisclosure/2010/Jun/277"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Close the socket
|
-- Close the socket
|
||||||
socket:close()
|
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
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ For more information, see:
|
|||||||
--
|
--
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if not nmap.is_privileged() then
|
if not nmap.is_privileged() then
|
||||||
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
author = "Hani Benhabiles"
|
author = "Hani Benhabiles"
|
||||||
@@ -55,30 +55,30 @@ categories = {"discovery", "safe", "broadcast"}
|
|||||||
-- @param hostname Hostname to query for.
|
-- @param hostname Hostname to query for.
|
||||||
-- @return query Raw llmnr query.
|
-- @return query Raw llmnr query.
|
||||||
local llmnrQuery = function(hostname)
|
local llmnrQuery = function(hostname)
|
||||||
local query = bin.pack(">S", math.random(0,65535)) -- transaction ID
|
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", 0x0000) -- Flags: Standard Query
|
||||||
query = query .. bin.pack(">S", 0x0001) -- Questions = 1
|
query = query .. bin.pack(">S", 0x0001) -- Questions = 1
|
||||||
query = query .. bin.pack(">S", 0x0000) -- Answer RRs = 0
|
query = query .. bin.pack(">S", 0x0000) -- Answer RRs = 0
|
||||||
query = query .. bin.pack(">S", 0x0000) -- Authority RRs = 0
|
query = query .. bin.pack(">S", 0x0000) -- Authority RRs = 0
|
||||||
query = query .. bin.pack(">S", 0x0000) -- Additional RRs = 0
|
query = query .. bin.pack(">S", 0x0000) -- Additional RRs = 0
|
||||||
query = query .. bin.pack(">CAC", #hostname, hostname, 0x00) -- Hostname
|
query = query .. bin.pack(">CAC", #hostname, hostname, 0x00) -- Hostname
|
||||||
query = query .. bin.pack(">S", 0x0001) -- Type: Host Address
|
query = query .. bin.pack(">S", 0x0001) -- Type: Host Address
|
||||||
query = query .. bin.pack(">S", 0x0001) -- Class: IN
|
query = query .. bin.pack(">S", 0x0001) -- Class: IN
|
||||||
return query
|
return query
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sends a llmnr query.
|
--- Sends a llmnr query.
|
||||||
-- @param query Query to send.
|
-- @param query Query to send.
|
||||||
local llmnrSend = function(query, mcast, mport)
|
local llmnrSend = function(query, mcast, mport)
|
||||||
-- Multicast IP and UDP port
|
-- Multicast IP and UDP port
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local status, err = sock:connect(mcast, mport, "udp")
|
local status, err = sock:connect(mcast, mport, "udp")
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
sock:send(query)
|
sock:send(query)
|
||||||
sock:close()
|
sock:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listens for llmnr responses
|
-- Listens for llmnr responses
|
||||||
@@ -86,130 +86,130 @@ end
|
|||||||
-- @param timeout Maximum time to listen.
|
-- @param timeout Maximum time to listen.
|
||||||
-- @param result table to put responses into.
|
-- @param result table to put responses into.
|
||||||
local llmnrListen = function(interface, timeout, result)
|
local llmnrListen = function(interface, timeout, result)
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
local listener = nmap.new_socket()
|
local listener = nmap.new_socket()
|
||||||
local status, l3data, _
|
local status, l3data, _
|
||||||
|
|
||||||
-- packets that are sent to our UDP port number 5355
|
-- packets that are sent to our UDP port number 5355
|
||||||
local filter = 'dst host ' .. interface.address .. ' and udp src port 5355'
|
local filter = 'dst host ' .. interface.address .. ' and udp src port 5355'
|
||||||
listener:set_timeout(100)
|
listener:set_timeout(100)
|
||||||
listener:pcap_open(interface.device, 1024, true, filter)
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
|
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
status, _, _, l3data = listener:pcap_receive()
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
if status then
|
if status then
|
||||||
local p = packet.Packet:new(l3data, #l3data)
|
local p = packet.Packet:new(l3data, #l3data)
|
||||||
-- Skip IP and UDP headers
|
-- Skip IP and UDP headers
|
||||||
local llmnr = string.sub(l3data, p.ip_hl*4 + 8 + 1)
|
local llmnr = string.sub(l3data, p.ip_hl*4 + 8 + 1)
|
||||||
-- Flags
|
-- Flags
|
||||||
local _, trans = bin.unpack(">S", llmnr)
|
local _, trans = bin.unpack(">S", llmnr)
|
||||||
local _, flags = bin.unpack(">S", llmnr, 3)
|
local _, flags = bin.unpack(">S", llmnr, 3)
|
||||||
-- Questions number
|
-- Questions number
|
||||||
local _, questions = bin.unpack(">S", llmnr, 5)
|
local _, questions = bin.unpack(">S", llmnr, 5)
|
||||||
|
|
||||||
-- Make verifications
|
-- Make verifications
|
||||||
-- Message == Response bit
|
-- Message == Response bit
|
||||||
-- and 1 Question (hostname we requested) and
|
-- and 1 Question (hostname we requested) and
|
||||||
if (bit.rshift(flags, 15) == 1) and questions == 0x01 then
|
if (bit.rshift(flags, 15) == 1) and questions == 0x01 then
|
||||||
stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src)
|
stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src)
|
||||||
-- Skip header's 12 bytes
|
-- Skip header's 12 bytes
|
||||||
-- extract host length
|
-- extract host length
|
||||||
local index, qlen = bin.unpack(">C", llmnr, 13)
|
local index, qlen = bin.unpack(">C", llmnr, 13)
|
||||||
-- Skip hostname, null byte, type field and class field
|
-- Skip hostname, null byte, type field and class field
|
||||||
index = index + qlen + 1 + 2 + 2
|
index = index + qlen + 1 + 2 + 2
|
||||||
|
|
||||||
-- Now, answer record
|
-- Now, answer record
|
||||||
local response, alen = {}
|
local response, alen = {}
|
||||||
index, alen = bin.unpack(">C", llmnr, index)
|
index, alen = bin.unpack(">C", llmnr, index)
|
||||||
-- Extract hostname with the correct case sensivity.
|
-- Extract hostname with the correct case sensivity.
|
||||||
index, response.hostname = bin.unpack(">A".. alen, llmnr, index)
|
index, response.hostname = bin.unpack(">A".. alen, llmnr, index)
|
||||||
|
|
||||||
-- skip null byte, type, class, ttl, dlen
|
-- skip null byte, type, class, ttl, dlen
|
||||||
index = index + 1 + 2 + 2 + 4 + 2
|
index = index + 1 + 2 + 2 + 4 + 2
|
||||||
index, response.address = bin.unpack("<I", llmnr, index)
|
index, response.address = bin.unpack("<I", llmnr, index)
|
||||||
response.address = ipOps.fromdword(response.address)
|
response.address = ipOps.fromdword(response.address)
|
||||||
table.insert(result, response)
|
table.insert(result, response)
|
||||||
else
|
else
|
||||||
stdnse.print_debug("%s skipped llmnr response.", SCRIPT_NAME)
|
stdnse.print_debug("%s skipped llmnr response.", SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
condvar("signal")
|
end
|
||||||
|
condvar("signal")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the network interface used to send packets to a target host.
|
-- Returns the network interface used to send packets to a target host.
|
||||||
--@param target host to which the interface is used.
|
--@param target host to which the interface is used.
|
||||||
--@return interface Network interface used for target host.
|
--@return interface Network interface used for target host.
|
||||||
local getInterface = function(target)
|
local getInterface = function(target)
|
||||||
-- First, create dummy UDP connection to get interface
|
-- First, create dummy UDP connection to get interface
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local status, err = sock:connect(target, "12345", "udp")
|
local status, err = sock:connect(target, "12345", "udp")
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local status, address, _, _, _ = sock:get_info()
|
local status, address, _, _, _ = sock:get_info()
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for _, interface in pairs(nmap.list_interfaces()) do
|
for _, interface in pairs(nmap.list_interfaces()) do
|
||||||
if interface.address == address then
|
if interface.address == address then
|
||||||
return interface
|
return interface
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
timeout = (timeout or 3) * 1000
|
timeout = (timeout or 3) * 1000
|
||||||
local hostname = stdnse.get_script_args(SCRIPT_NAME .. ".hostname")
|
local hostname = stdnse.get_script_args(SCRIPT_NAME .. ".hostname")
|
||||||
local result, output = {}, {}
|
local result, output = {}, {}
|
||||||
local mcast = "224.0.0.252"
|
local mcast = "224.0.0.252"
|
||||||
local mport = 5355
|
local mport = 5355
|
||||||
|
|
||||||
-- Check if a valid hostname was provided
|
-- Check if a valid hostname was provided
|
||||||
if not hostname or #hostname == 0 then
|
if not hostname or #hostname == 0 then
|
||||||
stdnse.print_debug("%s no hostname was provided.", SCRIPT_NAME)
|
stdnse.print_debug("%s no hostname was provided.", SCRIPT_NAME)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if a valid interface was provided
|
-- Check if a valid interface was provided
|
||||||
local interface = nmap.get_interface()
|
local interface = nmap.get_interface()
|
||||||
if interface then
|
if interface then
|
||||||
interface = nmap.get_interface_info(interface)
|
interface = nmap.get_interface_info(interface)
|
||||||
else
|
else
|
||||||
interface = getInterface(mcast)
|
interface = getInterface(mcast)
|
||||||
end
|
end
|
||||||
if not interface then
|
if not interface then
|
||||||
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Launch listener thread
|
-- Launch listener thread
|
||||||
stdnse.new_thread(llmnrListen, interface, timeout, result)
|
stdnse.new_thread(llmnrListen, interface, timeout, result)
|
||||||
-- Craft raw query
|
-- Craft raw query
|
||||||
local query = llmnrQuery(hostname)
|
local query = llmnrQuery(hostname)
|
||||||
-- Small sleep so the listener doesn't miss the response
|
-- Small sleep so the listener doesn't miss the response
|
||||||
stdnse.sleep(0.5)
|
stdnse.sleep(0.5)
|
||||||
-- Send query
|
-- Send query
|
||||||
llmnrSend(query, mcast, mport)
|
llmnrSend(query, mcast, mport)
|
||||||
-- Wait for listener thread to finish
|
-- Wait for listener thread to finish
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
condvar("wait")
|
condvar("wait")
|
||||||
|
|
||||||
-- Check responses
|
-- Check responses
|
||||||
if #result > 0 then
|
if #result > 0 then
|
||||||
for _, response in pairs(result) do
|
for _, response in pairs(result) do
|
||||||
table.insert(output, response.hostname.. " : " .. response.address)
|
table.insert(output, response.hostname.. " : " .. response.address)
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
target.add(response.address)
|
target.add(response.address)
|
||||||
end
|
|
||||||
end
|
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
|
||||||
|
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
|
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
|
-- added script arg to prevent script from connecting to ports that
|
||||||
-- weren't in original Nmap scan <chris3E3@gmail.com>)
|
-- weren't in original Nmap scan <chris3E3@gmail.com>)
|
||||||
-- rev 1.5 (2011-02-01 - Moved discovery functionality into ms-sql-discover.nse and
|
-- 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"
|
author = "Chris Woodbury, Thomas Buchanan"
|
||||||
|
|
||||||
@@ -115,75 +115,75 @@ categories = {"default", "discovery", "safe"}
|
|||||||
|
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
if ( mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
if ( mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
||||||
return mssql.Helper.GetDiscoveredInstances( host ) ~= nil
|
return mssql.Helper.GetDiscoveredInstances( host ) ~= nil
|
||||||
else
|
else
|
||||||
local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} )
|
local sqlDefaultPort = nmap.get_port_state( host, {number = 1433, protocol = "tcp"} )
|
||||||
local sqlBrowserPort = nmap.get_port_state( host, {number = 1434, protocol = "udp"} )
|
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
|
-- 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 )
|
local smbPortNumber = smb.get_port( host )
|
||||||
|
|
||||||
if ( (stdnse.get_script_args( {"mssql.instance-all", "mssql.instance-name", "mssql.instance-port"} ) ~= nil) or
|
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
|
(sqlBrowserPort and (sqlBrowserPort.state == "open" or sqlBrowserPort.state == "open|filtered")) or
|
||||||
(sqlDefaultPort and (sqlDefaultPort.state == "open" or sqlDefaultPort.state == "open|filtered")) or
|
(sqlDefaultPort and (sqlDefaultPort.state == "open" or sqlDefaultPort.state == "open|filtered")) or
|
||||||
(smbPortNumber ~= nil) ) then
|
(smbPortNumber ~= nil) ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Adds a label and value to an output table. If the value is a boolean, it is
|
--- 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.
|
-- converted to Yes/No; if the value is nil, nothing is added to the table.
|
||||||
local function add_to_output_table( outputTable, outputLabel, outputData )
|
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
|
if outputData == true then
|
||||||
outputData = "Yes"
|
outputData = "Yes"
|
||||||
elseif outputData == false then
|
elseif outputData == false then
|
||||||
outputData = "No"
|
outputData = "No"
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(outputTable, string.format( "%s: %s", outputLabel, outputData ) )
|
table.insert(outputTable, string.format( "%s: %s", outputLabel, outputData ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns formatted output for the given version data
|
--- Returns formatted output for the given version data
|
||||||
local function create_version_output_table( versionInfo )
|
local function create_version_output_table( versionInfo )
|
||||||
local versionOutput = {}
|
local versionOutput = {}
|
||||||
|
|
||||||
versionOutput["name"] = "Version: " .. versionInfo:ToString()
|
versionOutput["name"] = "Version: " .. versionInfo:ToString()
|
||||||
if ( versionInfo.source ~= "SSRP" ) then
|
if ( versionInfo.source ~= "SSRP" ) then
|
||||||
add_to_output_table( versionOutput, "Version number", versionInfo.versionNumber )
|
add_to_output_table( versionOutput, "Version number", versionInfo.versionNumber )
|
||||||
end
|
end
|
||||||
add_to_output_table( versionOutput, "Product", versionInfo.productName )
|
add_to_output_table( versionOutput, "Product", versionInfo.productName )
|
||||||
add_to_output_table( versionOutput, "Service pack level", versionInfo.servicePackLevel )
|
add_to_output_table( versionOutput, "Service pack level", versionInfo.servicePackLevel )
|
||||||
add_to_output_table( versionOutput, "Post-SP patches applied", versionInfo.patched )
|
add_to_output_table( versionOutput, "Post-SP patches applied", versionInfo.patched )
|
||||||
|
|
||||||
return versionOutput
|
return versionOutput
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Returns formatted output for the given instance
|
--- Returns formatted output for the given instance
|
||||||
local function create_instance_output_table( instance )
|
local function create_instance_output_table( instance )
|
||||||
|
|
||||||
-- if we didn't get anything useful (due to errors or the port not actually
|
-- if we didn't get anything useful (due to errors or the port not actually
|
||||||
-- being SQL Server), don't report anything
|
-- being SQL Server), don't report anything
|
||||||
if not ( instance.instanceName or instance.version ) then return nil end
|
if not ( instance.instanceName or instance.version ) then return nil end
|
||||||
|
|
||||||
local instanceOutput = {}
|
local instanceOutput = {}
|
||||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||||
|
|
||||||
add_to_output_table( instanceOutput, "Instance name", instance.instanceName )
|
add_to_output_table( instanceOutput, "Instance name", instance.instanceName )
|
||||||
if instance.version then
|
if instance.version then
|
||||||
local versionOutput = create_version_output_table( instance.version )
|
local versionOutput = create_version_output_table( instance.version )
|
||||||
table.insert( instanceOutput, versionOutput )
|
table.insert( instanceOutput, versionOutput )
|
||||||
end
|
end
|
||||||
if instance.port then add_to_output_table( instanceOutput, "TCP port", instance.port.number ) 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, "Named pipe", instance.pipeName )
|
||||||
add_to_output_table( instanceOutput, "Clustered", instance.isClustered )
|
add_to_output_table( instanceOutput, "Clustered", instance.isClustered )
|
||||||
|
|
||||||
return instanceOutput
|
return instanceOutput
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -191,74 +191,74 @@ end
|
|||||||
--- Processes a single instance, attempting to determine its version, etc.
|
--- Processes a single instance, attempting to determine its version, etc.
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
local foundVersion = false
|
local foundVersion = false
|
||||||
local ssnetlibVersion
|
local ssnetlibVersion
|
||||||
|
|
||||||
-- If possible and allowed (see 'mssql.scanned-ports-only' argument), we'll
|
-- If possible and allowed (see 'mssql.scanned-ports-only' argument), we'll
|
||||||
-- connect to the instance to get an accurate version number
|
-- connect to the instance to get an accurate version number
|
||||||
if ( instance:HasNetworkProtocols() ) then
|
if ( instance:HasNetworkProtocols() ) then
|
||||||
local ssnetlibVersion
|
local ssnetlibVersion
|
||||||
foundVersion, ssnetlibVersion = mssql.Helper.GetInstanceVersion( instance )
|
foundVersion, ssnetlibVersion = mssql.Helper.GetInstanceVersion( instance )
|
||||||
if ( foundVersion ) then
|
if ( foundVersion ) then
|
||||||
instance.version = ssnetlibVersion
|
instance.version = ssnetlibVersion
|
||||||
stdnse.print_debug( 1, "%s: Retrieved SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: Retrieved SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
||||||
else
|
else
|
||||||
stdnse.print_debug( 1, "%s: Could not retrieve SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: Could not retrieve SSNetLib version for %s.", SCRIPT_NAME, instance:GetName() )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If we didn't get a version from SSNetLib, give the user some detail as to why
|
-- If we didn't get a version from SSNetLib, give the user some detail as to why
|
||||||
if ( not foundVersion ) then
|
if ( not foundVersion ) then
|
||||||
if ( not instance:HasNetworkProtocols() ) then
|
if ( not instance:HasNetworkProtocols() ) then
|
||||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||||
end
|
end
|
||||||
if ( instance.version ) then
|
if ( instance.version ) then
|
||||||
stdnse.print_debug( 1, "%s: Using version number from SSRP response for %s.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: Using version number from SSRP response for %s.", SCRIPT_NAME, instance:GetName() )
|
||||||
else
|
else
|
||||||
stdnse.print_debug( 1, "%s: Version info could not be retrieved for %s.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: Version info could not be retrieved for %s.", SCRIPT_NAME, instance:GetName() )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Give some version info back to Nmap
|
-- Give some version info back to Nmap
|
||||||
if ( instance.port and instance.version ) then
|
if ( instance.port and instance.version ) then
|
||||||
instance.version:PopulateNmapPortVersion( instance.port )
|
instance.version:PopulateNmapPortVersion( instance.port )
|
||||||
nmap.set_port_version( instance.host, instance.port)
|
nmap.set_port_version( instance.host, instance.port)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function( host )
|
action = function( host )
|
||||||
local scriptOutput = {}
|
local scriptOutput = {}
|
||||||
|
|
||||||
local status, instanceList = mssql.Helper.GetTargetInstances( host )
|
local status, instanceList = mssql.Helper.GetTargetInstances( host )
|
||||||
-- if no instances were targeted, then display info on all
|
-- if no instances were targeted, then display info on all
|
||||||
if ( not status ) then
|
if ( not status ) then
|
||||||
if ( not mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
if ( not mssql.Helper.WasDiscoveryPerformed( host ) ) then
|
||||||
mssql.Helper.Discover( host )
|
mssql.Helper.Discover( host )
|
||||||
end
|
end
|
||||||
instanceList = mssql.Helper.GetDiscoveredInstances( host )
|
instanceList = mssql.Helper.GetDiscoveredInstances( host )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
if ( not instanceList ) then
|
if ( not instanceList ) then
|
||||||
return stdnse.format_output( false, instanceList or "" )
|
return stdnse.format_output( false, instanceList or "" )
|
||||||
else
|
else
|
||||||
for _, instance in ipairs( instanceList ) do
|
for _, instance in ipairs( instanceList ) do
|
||||||
if instance.serverName then
|
if instance.serverName then
|
||||||
table.insert(scriptOutput, string.format( "Windows server name: %s", instance.serverName ))
|
table.insert(scriptOutput, string.format( "Windows server name: %s", instance.serverName ))
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for _, instance in pairs( instanceList ) do
|
for _, instance in pairs( instanceList ) do
|
||||||
process_instance( instance )
|
process_instance( instance )
|
||||||
local instanceOutput = create_instance_output_table( instance )
|
local instanceOutput = create_instance_output_table( instance )
|
||||||
if instanceOutput then
|
if instanceOutput then
|
||||||
table.insert( scriptOutput, instanceOutput )
|
table.insert( scriptOutput, instanceOutput )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output( true, scriptOutput )
|
return stdnse.format_output( true, scriptOutput )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
-- (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
|
-- @args ms-sql-tables.keywords If set shows only tables or columns matching
|
||||||
-- the keywords
|
-- the keywords
|
||||||
--
|
--
|
||||||
-- @output
|
-- @output
|
||||||
-- | ms-sql-tables:
|
-- | 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>
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 04/02/2010 - v0.2
|
-- Revised 04/02/2010 - v0.2
|
||||||
-- - Added support for filters
|
-- - Added support for filters
|
||||||
-- - Changed output formatting of restrictions
|
-- - Changed output formatting of restrictions
|
||||||
-- - Added parameter information in output if parameters are using their
|
-- - Added parameter information in output if parameters are using their
|
||||||
-- defaults.
|
-- defaults.
|
||||||
-- Revised 02/01/2011 - v0.3 (Chris Woodbury)
|
-- Revised 02/01/2011 - v0.3 (Chris Woodbury)
|
||||||
-- - Added ability to run against all instances on a host;
|
-- - Added ability to run against all instances on a host;
|
||||||
-- - Added compatibility with changes in mssql.lua
|
-- - Added compatibility with changes in mssql.lua
|
||||||
|
|
||||||
author = "Patrik Karlsson"
|
author = "Patrik Karlsson"
|
||||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
@@ -104,169 +104,169 @@ portrule = mssql.Helper.GetPortrule_Standard()
|
|||||||
|
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
local status, result, dbs, tables
|
local status, result, dbs, tables
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||||
local db_query
|
local db_query
|
||||||
local done_dbs = {}
|
local done_dbs = {}
|
||||||
local db_limit, tbl_limit
|
local db_limit, tbl_limit
|
||||||
|
|
||||||
local DB_COUNT = stdnse.get_script_args( {'ms-sql-tables.maxdb', 'mssql-tables.maxdb'} )
|
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
|
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' } )
|
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
|
and tonumber( stdnse.get_script_args( {'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) or 2
|
||||||
local keywords_filter = ""
|
local keywords_filter = ""
|
||||||
|
|
||||||
if ( DB_COUNT <= 0 ) then
|
if ( DB_COUNT <= 0 ) then
|
||||||
db_limit = ""
|
db_limit = ""
|
||||||
else
|
else
|
||||||
db_limit = string.format( "TOP %d", DB_COUNT )
|
db_limit = string.format( "TOP %d", DB_COUNT )
|
||||||
end
|
end
|
||||||
if (TABLE_COUNT <= 0 ) then
|
if (TABLE_COUNT <= 0 ) then
|
||||||
tbl_limit = ""
|
tbl_limit = ""
|
||||||
else
|
else
|
||||||
tbl_limit = string.format( "TOP %d", TABLE_COUNT )
|
tbl_limit = string.format( "TOP %d", TABLE_COUNT )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build the keyword filter
|
-- Build the keyword filter
|
||||||
if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then
|
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 keywords = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } )
|
||||||
local tmp_tbl = {}
|
local tmp_tbl = {}
|
||||||
|
|
||||||
if( type(keywords) == 'string' ) then
|
if( type(keywords) == 'string' ) then
|
||||||
keywords = { keywords }
|
keywords = { keywords }
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, v in ipairs(keywords) do
|
for _, v in ipairs(keywords) do
|
||||||
table.insert(tmp_tbl, ("'%s'"):format(v))
|
table.insert(tmp_tbl, ("'%s'"):format(v))
|
||||||
end
|
end
|
||||||
|
|
||||||
keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format(
|
keywords_filter = (" AND ( so.name IN (%s) or sc.name IN (%s) ) "):format(
|
||||||
stdnse.strjoin(",", tmp_tbl),
|
stdnse.strjoin(",", tmp_tbl),
|
||||||
stdnse.strjoin(",", tmp_tbl)
|
stdnse.strjoin(",", tmp_tbl)
|
||||||
)
|
)
|
||||||
end
|
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 )
|
local creds = mssql.Helper.GetLoginCredentials_All( instance )
|
||||||
if ( not creds ) then
|
if ( not creds ) then
|
||||||
output = "ERROR: No login credentials."
|
output = "ERROR: No login credentials."
|
||||||
else
|
else
|
||||||
for username, password in pairs( creds ) do
|
for username, password in pairs( creds ) do
|
||||||
local helper = mssql.Helper:new()
|
local helper = mssql.Helper:new()
|
||||||
status, result = helper:ConnectEx( instance )
|
status, result = helper:ConnectEx( instance )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
table.insert(output, "ERROR: " .. result)
|
table.insert(output, "ERROR: " .. result)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
status = helper:Login( username, password, nil, instance.host.ip )
|
status = helper:Login( username, password, nil, instance.host.ip )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
status, dbs = helper:Query( db_query )
|
status, dbs = helper:Query( db_query )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
-- all done?
|
-- all done?
|
||||||
if ( #done_dbs == #dbs.rows ) then
|
if ( #done_dbs == #dbs.rows ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, v in pairs(dbs.rows) do
|
for k, v in pairs(dbs.rows) do
|
||||||
if ( not( stdnse.contains( done_dbs, v[1] ) ) ) then
|
if ( not( stdnse.contains( done_dbs, v[1] ) ) ) then
|
||||||
local query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length
|
local query = [[ SELECT so.name 'table', sc.name 'column', st.name 'type', sc.length
|
||||||
FROM %s..syscolumns sc, %s..sysobjects so, %s..systypes st
|
FROM %s..syscolumns sc, %s..sysobjects so, %s..systypes st
|
||||||
WHERE so.id = sc.id AND sc.xtype=st.xtype AND
|
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]]
|
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)
|
query = query:format( v[1], v[1], v[1], tbl_limit, v[1], keywords_filter)
|
||||||
status, tables = helper:Query( query )
|
status, tables = helper:Query( query )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(tables)
|
stdnse.print_debug(tables)
|
||||||
else
|
else
|
||||||
local item = {}
|
local item = {}
|
||||||
item = mssql.Util.FormatOutputTable( tables, true )
|
item = mssql.Util.FormatOutputTable( tables, true )
|
||||||
if ( #item == 0 and keywords_filter ~= "" ) then
|
if ( #item == 0 and keywords_filter ~= "" ) then
|
||||||
table.insert(item, "Filter returned no matches")
|
table.insert(item, "Filter returned no matches")
|
||||||
end
|
end
|
||||||
item.name = v[1]
|
item.name = v[1]
|
||||||
|
|
||||||
table.insert(output, item)
|
table.insert(output, item)
|
||||||
table.insert(done_dbs, v[1])
|
table.insert(done_dbs, v[1])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
helper:Disconnect()
|
helper:Disconnect()
|
||||||
end
|
end
|
||||||
|
|
||||||
local pos = 1
|
local pos = 1
|
||||||
local restrict_tbl = {}
|
local restrict_tbl = {}
|
||||||
|
|
||||||
if ( stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } ) ) then
|
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' } )
|
local tmp = stdnse.get_script_args( {'ms-sql-tables.keywords', 'mssql-tables.keywords' } )
|
||||||
if ( type(tmp) == 'table' ) then
|
if ( type(tmp) == 'table' ) then
|
||||||
tmp = stdnse.strjoin(',', tmp)
|
tmp = stdnse.strjoin(',', tmp)
|
||||||
end
|
end
|
||||||
table.insert(restrict_tbl, 1, ("Filter: %s"):format(tmp))
|
table.insert(restrict_tbl, 1, ("Filter: %s"):format(tmp))
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
else
|
else
|
||||||
table.insert(restrict_tbl, 1, "No filter (see ms-sql-tables.keywords)")
|
table.insert(restrict_tbl, 1, "No filter (see ms-sql-tables.keywords)")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( DB_COUNT > 0 ) then
|
if ( DB_COUNT > 0 ) then
|
||||||
local tmp = ("Output restricted to %d databases"):format(DB_COUNT)
|
local tmp = ("Output restricted to %d databases"):format(DB_COUNT)
|
||||||
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxdb', 'mssql-tables.maxdb' } ) ) ) then
|
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxdb', 'mssql-tables.maxdb' } ) ) ) then
|
||||||
tmp = tmp .. " (see ms-sql-tables.maxdb)"
|
tmp = tmp .. " (see ms-sql-tables.maxdb)"
|
||||||
end
|
end
|
||||||
table.insert(restrict_tbl, 1, tmp)
|
table.insert(restrict_tbl, 1, tmp)
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( TABLE_COUNT > 0 ) then
|
if ( TABLE_COUNT > 0 ) then
|
||||||
local tmp = ("Output restricted to %d tables"):format(TABLE_COUNT)
|
local tmp = ("Output restricted to %d tables"):format(TABLE_COUNT)
|
||||||
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) ) then
|
if ( not(stdnse.get_script_args( { 'ms-sql-tables.maxtables', 'mssql-tables.maxtables' } ) ) ) then
|
||||||
tmp = tmp .. " (see ms-sql-tables.maxtables)"
|
tmp = tmp .. " (see ms-sql-tables.maxtables)"
|
||||||
end
|
end
|
||||||
table.insert(restrict_tbl, 1, tmp)
|
table.insert(restrict_tbl, 1, tmp)
|
||||||
pos = pos + 1
|
pos = pos + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( 1 < pos and type( output ) == "table" and #output > 0) then
|
if ( 1 < pos and type( output ) == "table" and #output > 0) then
|
||||||
restrict_tbl.name = "Restrictions"
|
restrict_tbl.name = "Restrictions"
|
||||||
table.insert(output, "")
|
table.insert(output, "")
|
||||||
table.insert(output, restrict_tbl)
|
table.insert(output, restrict_tbl)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local instanceOutput = {}
|
local instanceOutput = {}
|
||||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||||
table.insert( instanceOutput, output )
|
table.insert( instanceOutput, output )
|
||||||
|
|
||||||
return instanceOutput
|
return instanceOutput
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
local scriptOutput = {}
|
local scriptOutput = {}
|
||||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||||
|
|
||||||
if ( not status ) then
|
if ( not status ) then
|
||||||
return stdnse.format_output( false, instanceList )
|
return stdnse.format_output( false, instanceList )
|
||||||
else
|
else
|
||||||
for _, instance in pairs( instanceList ) do
|
for _, instance in pairs( instanceList ) do
|
||||||
local instanceOutput = process_instance( instance )
|
local instanceOutput = process_instance( instance )
|
||||||
if instanceOutput then
|
if instanceOutput then
|
||||||
table.insert( scriptOutput, instanceOutput )
|
table.insert( scriptOutput, instanceOutput )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output( true, scriptOutput )
|
return stdnse.format_output( true, scriptOutput )
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -40,169 +40,169 @@ end
|
|||||||
--@param num Start of the two bytes
|
--@param num Start of the two bytes
|
||||||
--@return The converted number
|
--@return The converted number
|
||||||
local ntohs = function(num)
|
local ntohs = function(num)
|
||||||
local b1 = bit.band(num:byte(1), 255)
|
local b1 = bit.band(num:byte(1), 255)
|
||||||
local b2 = bit.band(num:byte(2), 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
|
end
|
||||||
|
|
||||||
--- Converts three bytes into a number
|
--- Converts three bytes into a number
|
||||||
--@param num Start of the three bytes
|
--@param num Start of the three bytes
|
||||||
--@return The converted number
|
--@return The converted number
|
||||||
local ntoh3 = function(num)
|
local ntoh3 = function(num)
|
||||||
local b1 = bit.band(num:byte(1), 255)
|
local b1 = bit.band(num:byte(1), 255)
|
||||||
local b2 = bit.band(num:byte(2), 255)
|
local b2 = bit.band(num:byte(2), 255)
|
||||||
local b3 = bit.band(num:byte(3), 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
|
end
|
||||||
|
|
||||||
--- Converts four bytes into a number
|
--- Converts four bytes into a number
|
||||||
--@param num Start of the four bytes
|
--@param num Start of the four bytes
|
||||||
--@return The converted number
|
--@return The converted number
|
||||||
local ntohl = function(num)
|
local ntohl = function(num)
|
||||||
local b1 = bit.band(num:byte(1), 255)
|
local b1 = bit.band(num:byte(1), 255)
|
||||||
local b2 = bit.band(num:byte(2), 255)
|
local b2 = bit.band(num:byte(2), 255)
|
||||||
local b3 = bit.band(num:byte(3), 255)
|
local b3 = bit.band(num:byte(3), 255)
|
||||||
local b4 = bit.band(num:byte(4), 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
|
end
|
||||||
|
|
||||||
--- Converts a number to a string description of the capabilities
|
--- Converts a number to a string description of the capabilities
|
||||||
--@param num Start of the capabilities data
|
--@param num Start of the capabilities data
|
||||||
--@return String describing the capabilities offered
|
--@return String describing the capabilities offered
|
||||||
local capabilities = function(num)
|
local capabilities = function(num)
|
||||||
local caps = ""
|
local caps = ""
|
||||||
|
|
||||||
if bit.band(num, 1) > 0 then
|
if bit.band(num, 1) > 0 then
|
||||||
caps = caps .. "Long Passwords, "
|
caps = caps .. "Long Passwords, "
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit.band(num, 8) > 0 then
|
if bit.band(num, 8) > 0 then
|
||||||
caps = caps .. "Connect with DB, "
|
caps = caps .. "Connect with DB, "
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit.band(num, 32) > 0 then
|
if bit.band(num, 32) > 0 then
|
||||||
caps = caps .. "Compress, "
|
caps = caps .. "Compress, "
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit.band(num, 64) > 0 then
|
if bit.band(num, 64) > 0 then
|
||||||
caps = caps .. "ODBC, "
|
caps = caps .. "ODBC, "
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit.band(num, 2048) > 0 then
|
if bit.band(num, 2048) > 0 then
|
||||||
caps = caps .. "SSL, "
|
caps = caps .. "SSL, "
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit.band(num, 8192) > 0 then
|
if bit.band(num, 8192) > 0 then
|
||||||
caps = caps .. "Transactions, "
|
caps = caps .. "Transactions, "
|
||||||
end
|
end
|
||||||
|
|
||||||
if bit.band(num, 32768) > 0 then
|
if bit.band(num, 32768) > 0 then
|
||||||
caps = caps .. "Secure Connection, "
|
caps = caps .. "Secure Connection, "
|
||||||
end
|
end
|
||||||
|
|
||||||
return caps:gsub(", $", "")
|
return caps:gsub(", $", "")
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = function(host, port)
|
portrule = function(host, port)
|
||||||
local extra = port.version.extrainfo
|
local extra = port.version.extrainfo
|
||||||
|
|
||||||
return (port.number == 3306 or port.service == "mysql")
|
return (port.number == 3306 or port.service == "mysql")
|
||||||
and port.protocol == "tcp"
|
and port.protocol == "tcp"
|
||||||
and port.state == "open"
|
and port.state == "open"
|
||||||
and not (extra ~= nil
|
and not (extra ~= nil
|
||||||
and (extra:match("[Uu]nauthorized")
|
and (extra:match("[Uu]nauthorized")
|
||||||
or extra:match("[Tt]oo many connection")))
|
or extra:match("[Tt]oo many connection")))
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
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
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local length = ntoh3(response:sub(1, 3))
|
local length = ntoh3(response:sub(1, 3))
|
||||||
|
|
||||||
if length ~= response:len() - 4 then
|
if length ~= response:len() - 4 then
|
||||||
return "Invalid greeting (Not MySQL?)"
|
return "Invalid greeting (Not MySQL?)"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Keeps track of where we are in the binary data
|
-- Keeps track of where we are in the binary data
|
||||||
local offset = 1 + 4
|
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.
|
-- If a 0xff is here instead of the protocol, an error occurred.
|
||||||
-- Pass it along to the user..
|
-- Pass it along to the user..
|
||||||
if (protocol == 255) then
|
if (protocol == 255) then
|
||||||
output = "MySQL Error detected!\n"
|
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
|
return output
|
||||||
end
|
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
|
if ntohs(response:sub(offset, offset + 2)) == 2 then
|
||||||
status = "Autocommit"
|
status = "Autocommit"
|
||||||
end
|
end
|
||||||
|
|
||||||
offset = offset + 2
|
offset = offset + 2
|
||||||
|
|
||||||
offset = offset + 13 -- unused
|
offset = offset + 13 -- unused
|
||||||
|
|
||||||
if response:len() - offset + 1 == 13 then
|
if response:len() - offset + 1 == 13 then
|
||||||
salt = salt .. getstring(response:sub(offset))
|
salt = salt .. getstring(response:sub(offset))
|
||||||
end
|
end
|
||||||
|
|
||||||
output = output .. "Protocol: " .. protocol .. "\n"
|
output = output .. "Protocol: " .. protocol .. "\n"
|
||||||
output = output .. "Version: " .. version .. "\n"
|
output = output .. "Version: " .. version .. "\n"
|
||||||
output = output .. "Thread ID: " .. threadid .. "\n"
|
output = output .. "Thread ID: " .. threadid .. "\n"
|
||||||
|
|
||||||
if caps:len() > 0 then
|
if caps:len() > 0 then
|
||||||
output = output .. "Some Capabilities: " .. caps .. "\n"
|
output = output .. "Some Capabilities: " .. caps .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
if status:len() > 0 then
|
if status:len() > 0 then
|
||||||
output = output .. "Status: " .. status .. "\n"
|
output = output .. "Status: " .. status .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
output = output .. "Salt: " .. salt .. "\n"
|
output = output .. "Salt: " .. salt .. "\n"
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -87,155 +87,155 @@ categories = {"default", "discovery", "safe"}
|
|||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
|
|
||||||
-- The following is an attempt to only run this script against hosts
|
-- 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 will probably respond to a UDP 137 probe. One might argue
|
||||||
-- that sending a single UDP packet and waiting for a response is no
|
-- 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
|
-- big deal and that it should be done for every host. In that case
|
||||||
-- simply change this rule to always return true.
|
-- simply change this rule to always return true.
|
||||||
|
|
||||||
local port_t135 = nmap.get_port_state(host,
|
local port_t135 = nmap.get_port_state(host,
|
||||||
{number=135, protocol="tcp"})
|
{number=135, protocol="tcp"})
|
||||||
local port_t139 = nmap.get_port_state(host,
|
local port_t139 = nmap.get_port_state(host,
|
||||||
{number=139, protocol="tcp"})
|
{number=139, protocol="tcp"})
|
||||||
local port_t445 = nmap.get_port_state(host,
|
local port_t445 = nmap.get_port_state(host,
|
||||||
{number=445, protocol="tcp"})
|
{number=445, protocol="tcp"})
|
||||||
local port_u137 = nmap.get_port_state(host,
|
local port_u137 = nmap.get_port_state(host,
|
||||||
{number=137, protocol="udp"})
|
{number=137, protocol="udp"})
|
||||||
|
|
||||||
return (port_t135 ~= nil and port_t135.state == "open") or
|
return (port_t135 ~= nil and port_t135.state == "open") or
|
||||||
(port_t139 ~= nil and port_t139.state == "open") or
|
(port_t139 ~= nil and port_t139.state == "open") or
|
||||||
(port_t445 ~= nil and port_t445.state == "open") or
|
(port_t445 ~= nil and port_t445.state == "open") or
|
||||||
(port_u137 ~= nil and
|
(port_u137 ~= nil and
|
||||||
(port_u137.state == "open" or
|
(port_u137.state == "open" or
|
||||||
port_u137.state == "open|filtered"))
|
port_u137.state == "open|filtered"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local i
|
local i
|
||||||
local status
|
local status
|
||||||
local names, statistics
|
local names, statistics
|
||||||
local server_name, user_name
|
local server_name, user_name
|
||||||
local mac, prefix, manuf
|
local mac, prefix, manuf
|
||||||
local response = {}
|
local response = {}
|
||||||
local catch = function() return end
|
local catch = function() return end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
|
|
||||||
|
|
||||||
-- Get the list of NetBIOS names
|
-- 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)
|
||||||
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
|
if(status == false) then
|
||||||
return stdnse.format_output(false, names)
|
return stdnse.format_output(false, names)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the server name
|
-- Get the server name
|
||||||
status, server_name = netbios.get_server_name(host, names)
|
status, server_name = netbios.get_server_name(host, names)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return stdnse.format_output(false, server_name)
|
return stdnse.format_output(false, server_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the logged in user
|
-- Get the logged in user
|
||||||
status, user_name = netbios.get_user_name(host, names)
|
status, user_name = netbios.get_user_name(host, names)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return stdnse.format_output(false, user_name)
|
return stdnse.format_output(false, user_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
local mac_prefixes = try(datafiles.parse_mac_prefixes())
|
||||||
|
|
||||||
-- Format the Mac address in the standard way
|
-- Format the Mac address in the standard way
|
||||||
if(#statistics >= 6) then
|
if(#statistics >= 6) then
|
||||||
-- MAC prefixes are matched on the first three bytes, all uppercase
|
-- 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)))
|
prefix = string.upper(string.format("%02x%02x%02x", statistics:byte(1), statistics:byte(2), statistics:byte(3)))
|
||||||
mac = {
|
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) ),
|
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"
|
manuf = mac_prefixes[prefix] or "unknown"
|
||||||
}
|
}
|
||||||
host.registry['nbstat'] = {
|
host.registry['nbstat'] = {
|
||||||
server_name = server_name,
|
server_name = server_name,
|
||||||
mac = mac.address
|
mac = mac.address
|
||||||
}
|
}
|
||||||
-- Samba doesn't set the Mac address, and nmap-mac-prefixes shows that as Xerox
|
-- 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
|
if(mac.address == "00:00:00:00:00:00") then
|
||||||
mac.address = "<unknown>"
|
mac.address = "<unknown>"
|
||||||
mac.manuf = "unknown"
|
mac.manuf = "unknown"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
mac = {
|
mac = {
|
||||||
address = "<unknown>",
|
address = "<unknown>",
|
||||||
manuf = "unknown"
|
manuf = "unknown"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
setmetatable(mac, {
|
setmetatable(mac, {
|
||||||
-- MAC is formatted as "00:11:22:33:44:55 (Manufacturer)"
|
-- MAC is formatted as "00:11:22:33:44:55 (Manufacturer)"
|
||||||
__tostring=function(t) return string.format("%s (%s)", t.address, t.manuf) end
|
__tostring=function(t) return string.format("%s (%s)", t.address, t.manuf) end
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Check if we actually got a username
|
-- Check if we actually got a username
|
||||||
if(user_name == nil) then
|
if(user_name == nil) then
|
||||||
user_name = "<unknown>"
|
user_name = "<unknown>"
|
||||||
end
|
end
|
||||||
|
|
||||||
response["server_name"] = server_name
|
response["server_name"] = server_name
|
||||||
response["user"] = user_name
|
response["user"] = user_name
|
||||||
response["mac"] = mac
|
response["mac"] = mac
|
||||||
|
|
||||||
local names_output = {}
|
local names_output = {}
|
||||||
for i = 1, #names, 1 do
|
for i = 1, #names, 1 do
|
||||||
local name = names[i]
|
local name = names[i]
|
||||||
setmetatable(name, {
|
setmetatable(name, {
|
||||||
__tostring = function(t)
|
__tostring = function(t)
|
||||||
-- Tabular format with padding
|
-- Tabular format with padding
|
||||||
return string.format("%s<%02x>%sFlags: %s",
|
return string.format("%s<%02x>%sFlags: %s",
|
||||||
t['name'], t['suffix'],
|
t['name'], t['suffix'],
|
||||||
string.rep(" ", 17 - #t['name']),
|
string.rep(" ", 17 - #t['name']),
|
||||||
netbios.flags_to_string(t['flags']))
|
netbios.flags_to_string(t['flags']))
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
table.insert(names_output, name)
|
table.insert(names_output, name)
|
||||||
end
|
end
|
||||||
setmetatable(names_output, {
|
setmetatable(names_output, {
|
||||||
__tostring = function(t)
|
__tostring = function(t)
|
||||||
local ret = {}
|
local ret = {}
|
||||||
for i,v in ipairs(t) do
|
for i,v in ipairs(t) do
|
||||||
table.insert(ret, tostring(v))
|
table.insert(ret, tostring(v))
|
||||||
end
|
end
|
||||||
-- Indent Names table by 2 spaces
|
-- Indent Names table by 2 spaces
|
||||||
return " " .. table.concat(ret, "\n ")
|
return " " .. table.concat(ret, "\n ")
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
response["names"] = names_output
|
response["names"] = names_output
|
||||||
|
|
||||||
local statistics_output = {}
|
local statistics_output = {}
|
||||||
for i = 1, #statistics, 16 do
|
for i = 1, #statistics, 16 do
|
||||||
--Format statistics as space-separated hex bytes, 16 columns
|
--Format statistics as space-separated hex bytes, 16 columns
|
||||||
table.insert(statistics_output,
|
table.insert(statistics_output,
|
||||||
stdnse.tohex(string.sub(statistics,i,i+16), {separator = " "})
|
stdnse.tohex(string.sub(statistics,i,i+16), {separator = " "})
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
response["statistics"] = statistics_output
|
response["statistics"] = statistics_output
|
||||||
|
|
||||||
setmetatable(response, {
|
setmetatable(response, {
|
||||||
__tostring = function(t)
|
__tostring = function(t)
|
||||||
-- Normal single-line result
|
-- Normal single-line result
|
||||||
local ret = {string.format("NetBIOS name: %s, NetBIOS user: %s, NetBIOS MAC: %s", t.server_name, t.user, t.mac)}
|
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 verbosity is set, dump the whole list of names
|
||||||
if nmap.verbosity() >= 1 then
|
if nmap.verbosity() >= 1 then
|
||||||
table.insert(ret, string.format("Names:\n%s",t.names))
|
table.insert(ret, string.format("Names:\n%s",t.names))
|
||||||
-- If super verbosity is set, print out the full statistics
|
-- If super verbosity is set, print out the full statistics
|
||||||
if nmap.verbosity() >= 2 then
|
if nmap.verbosity() >= 2 then
|
||||||
-- Indent Statistics table by 2 spaces
|
-- Indent Statistics table by 2 spaces
|
||||||
table.insert(ret, string.format("Statistics:\n %s",table.concat(t.statistics,"\n ")))
|
table.insert(ret, string.format("Statistics:\n %s",table.concat(t.statistics,"\n ")))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return table.concat(ret, "\n")
|
return table.concat(ret, "\n")
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -39,214 +39,214 @@ categories = {"discovery", "intrusive"}
|
|||||||
|
|
||||||
|
|
||||||
local NRPE_PROTOCOLS = {
|
local NRPE_PROTOCOLS = {
|
||||||
"ssl",
|
"ssl",
|
||||||
"tcp"
|
"tcp"
|
||||||
}
|
}
|
||||||
|
|
||||||
local NRPE_STATES = {
|
local NRPE_STATES = {
|
||||||
[0] = "OK",
|
[0] = "OK",
|
||||||
[1] = "WARNING",
|
[1] = "WARNING",
|
||||||
[2] ="CRITICAL",
|
[2] ="CRITICAL",
|
||||||
[3] = "UNKNOWN"
|
[3] = "UNKNOWN"
|
||||||
}
|
}
|
||||||
|
|
||||||
local NRPE_COMMANDS = {
|
local NRPE_COMMANDS = {
|
||||||
"check_hda1",
|
"check_hda1",
|
||||||
"check_load",
|
"check_load",
|
||||||
"check_total_procs",
|
"check_total_procs",
|
||||||
"check_users",
|
"check_users",
|
||||||
"check_zombie_procs"
|
"check_zombie_procs"
|
||||||
}
|
}
|
||||||
|
|
||||||
local CRC32_CONSTANTS = {
|
local CRC32_CONSTANTS = {
|
||||||
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
|
||||||
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
|
||||||
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
|
||||||
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
|
||||||
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
|
||||||
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
|
||||||
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
|
||||||
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
|
||||||
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
|
||||||
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
|
||||||
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
|
||||||
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
|
||||||
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
|
||||||
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
|
||||||
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
|
||||||
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
|
||||||
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
|
||||||
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
|
||||||
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
|
||||||
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
|
||||||
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
|
||||||
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
|
||||||
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
|
||||||
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
|
||||||
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
|
||||||
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
|
||||||
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
|
||||||
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
|
||||||
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
|
||||||
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
|
||||||
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
|
||||||
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
|
||||||
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
|
||||||
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
|
||||||
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
|
||||||
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
|
||||||
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
|
||||||
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
|
||||||
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
|
||||||
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
|
||||||
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
|
||||||
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
|
||||||
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
|
||||||
}
|
}
|
||||||
|
|
||||||
local crc32 = function(s)
|
local crc32 = function(s)
|
||||||
local crc = 0xFFFFFFFF
|
local crc = 0xFFFFFFFF
|
||||||
for i = 1, #s do
|
for i = 1, #s do
|
||||||
local p1 = s:byte(i)
|
local p1 = s:byte(i)
|
||||||
local p2 = bit.bxor(crc, p1)
|
local p2 = bit.bxor(crc, p1)
|
||||||
local p3 = bit.band(p2, 0xFF)
|
local p3 = bit.band(p2, 0xFF)
|
||||||
local p4 = bit.band(p3)
|
local p4 = bit.band(p3)
|
||||||
local p5 = CRC32_CONSTANTS[p4 + 1]
|
local p5 = CRC32_CONSTANTS[p4 + 1]
|
||||||
local p6 = bit.rshift(crc, 8)
|
local p6 = bit.rshift(crc, 8)
|
||||||
|
|
||||||
crc = bit.bxor(p6, p5)
|
crc = bit.bxor(p6, p5)
|
||||||
end
|
end
|
||||||
|
|
||||||
return bit.bxor(crc, 0xFFFFFFFF)
|
return bit.bxor(crc, 0xFFFFFFFF)
|
||||||
end
|
end
|
||||||
|
|
||||||
local nrpe_open = function(host, port)
|
local nrpe_open = function(host, port)
|
||||||
for _, proto in pairs(NRPE_PROTOCOLS) do
|
for _, proto in pairs(NRPE_PROTOCOLS) do
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
sock:set_timeout(2000)
|
sock:set_timeout(2000)
|
||||||
local status, err = sock:connect(host, port, proto)
|
local status, err = sock:connect(host, port, proto)
|
||||||
if status then
|
if status then
|
||||||
NRPE_PROTOCOLS = {proto}
|
NRPE_PROTOCOLS = {proto}
|
||||||
return true, sock
|
return true, sock
|
||||||
end
|
end
|
||||||
|
|
||||||
stdnse.print_debug(2, "Can't connect using %s: %s", proto, err)
|
stdnse.print_debug(2, "Can't connect using %s: %s", proto, err)
|
||||||
sock:close()
|
sock:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local nrpe_write = function(cmd)
|
local nrpe_write = function(cmd)
|
||||||
-- Create request packet, before checksum.
|
-- Create request packet, before checksum.
|
||||||
local pkt = ""
|
local pkt = ""
|
||||||
pkt = pkt .. bin.pack(">S", 2)
|
pkt = pkt .. bin.pack(">S", 2)
|
||||||
pkt = pkt .. bin.pack(">S", 1)
|
pkt = pkt .. bin.pack(">S", 1)
|
||||||
pkt = pkt .. bin.pack(">I", 0)
|
pkt = pkt .. bin.pack(">I", 0)
|
||||||
pkt = pkt .. bin.pack(">S", 0)
|
pkt = pkt .. bin.pack(">S", 0)
|
||||||
pkt = pkt .. bin.pack("A", cmd)
|
pkt = pkt .. bin.pack("A", cmd)
|
||||||
pkt = pkt .. string.char(0x00):rep(1024 - #cmd)
|
pkt = pkt .. string.char(0x00):rep(1024 - #cmd)
|
||||||
pkt = pkt .. bin.pack(">S", 0)
|
pkt = pkt .. bin.pack(">S", 0)
|
||||||
|
|
||||||
-- Calculate the checksum, and insert it into the packet.
|
-- Calculate the checksum, and insert it into the packet.
|
||||||
pkt = bin.pack(
|
pkt = bin.pack(
|
||||||
"A>IA",
|
"A>IA",
|
||||||
string.char(pkt:byte(1, 4)),
|
string.char(pkt:byte(1, 4)),
|
||||||
crc32(pkt),
|
crc32(pkt),
|
||||||
string.char(pkt:byte(9, #pkt))
|
string.char(pkt:byte(9, #pkt))
|
||||||
)
|
)
|
||||||
|
|
||||||
return pkt
|
return pkt
|
||||||
end
|
end
|
||||||
|
|
||||||
local nrpe_read = function(pkt)
|
local nrpe_read = function(pkt)
|
||||||
local i
|
local i
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
-- Parse packet.
|
-- Parse packet.
|
||||||
i, result.version = bin.unpack(">S", pkt, i)
|
i, result.version = bin.unpack(">S", pkt, i)
|
||||||
i, result.type = bin.unpack(">S", pkt, i)
|
i, result.type = bin.unpack(">S", pkt, i)
|
||||||
i, result.crc32 = bin.unpack(">I", pkt, i)
|
i, result.crc32 = bin.unpack(">I", pkt, i)
|
||||||
i, result.state = bin.unpack(">S", pkt, i)
|
i, result.state = bin.unpack(">S", pkt, i)
|
||||||
i, result.data = bin.unpack("z", pkt, i)
|
i, result.data = bin.unpack("z", pkt, i)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local nrpe_check = function(host, port, cmd)
|
local nrpe_check = function(host, port, cmd)
|
||||||
-- Create connection.
|
-- Create connection.
|
||||||
local status, sock = nrpe_open(host, port)
|
local status, sock = nrpe_open(host, port)
|
||||||
if not status then
|
if not status then
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send query.
|
-- Send query.
|
||||||
local status, err = sock:send(nrpe_write(cmd))
|
local status, err = sock:send(nrpe_write(cmd))
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(1, "Failed to send NRPE query for command %s: %s", cmd, err)
|
stdnse.print_debug(1, "Failed to send NRPE query for command %s: %s", cmd, err)
|
||||||
sock:close()
|
sock:close()
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Receive response.
|
-- Receive response.
|
||||||
local status, resp = sock:receive()
|
local status, resp = sock:receive()
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug(1, "Can't read NRPE response: %s", resp)
|
stdnse.print_debug(1, "Can't read NRPE response: %s", resp)
|
||||||
sock:close()
|
sock:close()
|
||||||
return false, nil
|
return false, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
sock:close()
|
sock:close()
|
||||||
|
|
||||||
return true, nrpe_read(resp)
|
return true, nrpe_read(resp)
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = shortport.port_or_service(5666, "nrpe")
|
portrule = shortport.port_or_service(5666, "nrpe")
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
-- Get script arguments.
|
-- Get script arguments.
|
||||||
local cmds = stdnse.get_script_args("nrpe-enum.cmds")
|
local cmds = stdnse.get_script_args("nrpe-enum.cmds")
|
||||||
if cmds then
|
if cmds then
|
||||||
cmds = stdnse.strsplit(":", cmds)
|
cmds = stdnse.strsplit(":", cmds)
|
||||||
else
|
else
|
||||||
cmds = NRPE_COMMANDS
|
cmds = NRPE_COMMANDS
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create results table.
|
-- Create results table.
|
||||||
local results = tab.new()
|
local results = tab.new()
|
||||||
tab.addrow(
|
tab.addrow(
|
||||||
results,
|
results,
|
||||||
"Command",
|
"Command",
|
||||||
"State",
|
"State",
|
||||||
"Response"
|
"Response"
|
||||||
)
|
)
|
||||||
|
|
||||||
-- Try each NRPE command, and collect the results.
|
-- Try each NRPE command, and collect the results.
|
||||||
for _, cmd in pairs(cmds) do
|
for _, cmd in pairs(cmds) do
|
||||||
local status, result = nrpe_check(host, port, cmd)
|
local status, result = nrpe_check(host, port, cmd)
|
||||||
if status then
|
if status then
|
||||||
tab.addrow(
|
tab.addrow(
|
||||||
results,
|
results,
|
||||||
cmd,
|
cmd,
|
||||||
NRPE_STATES[result.state],
|
NRPE_STATES[result.state],
|
||||||
result.data
|
result.data
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If no queries generated responses, don't output anything.
|
-- If no queries generated responses, don't output anything.
|
||||||
if #results == 1 then
|
if #results == 1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Record service description.
|
-- Record service description.
|
||||||
port.version.name = "nrpe"
|
port.version.name = "nrpe"
|
||||||
port.version.product = "Nagios Remote Plugin Executor"
|
port.version.product = "Nagios Remote Plugin Executor"
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
|
|
||||||
-- Format table, without trailing newline.
|
-- Format table, without trailing newline.
|
||||||
results = tab.dump(results)
|
results = tab.dump(results)
|
||||||
results = results:sub(1, #results - 1)
|
results = results:sub(1, #results - 1)
|
||||||
|
|
||||||
return "\n" .. results
|
return "\n" .. results
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -35,23 +35,23 @@ portrule = shortport.port_or_service(5850, "openlookup")
|
|||||||
|
|
||||||
-- parses a Netstring element
|
-- parses a Netstring element
|
||||||
local function parsechunk(data)
|
local function parsechunk(data)
|
||||||
local parts = stdnse.strsplit(":", data)
|
local parts = stdnse.strsplit(":", data)
|
||||||
if #parts < 2 then
|
if #parts < 2 then
|
||||||
return nil, data
|
return nil, data
|
||||||
end
|
end
|
||||||
local head = table.remove(parts, 1)
|
local head = table.remove(parts, 1)
|
||||||
local size = tonumber(head)
|
local size = tonumber(head)
|
||||||
if not size then
|
if not size then
|
||||||
return nil, data
|
return nil, data
|
||||||
end
|
end
|
||||||
local body = stdnse.strjoin(":", parts)
|
local body = stdnse.strjoin(":", parts)
|
||||||
if #body < size then
|
if #body < size then
|
||||||
return nil, data
|
return nil, data
|
||||||
end
|
end
|
||||||
local chunk = string.sub(body, 1, size)
|
local chunk = string.sub(body, 1, size)
|
||||||
local skip = #chunk + string.len(",")
|
local skip = #chunk + string.len(",")
|
||||||
local rest = string.sub(body, skip + 1)
|
local rest = string.sub(body, skip + 1)
|
||||||
return chunk, rest
|
return chunk, rest
|
||||||
end
|
end
|
||||||
|
|
||||||
-- NSON helpers
|
-- NSON helpers
|
||||||
@@ -59,203 +59,203 @@ end
|
|||||||
|
|
||||||
-- parses an NSON int
|
-- parses an NSON int
|
||||||
local function parseint(data)
|
local function parseint(data)
|
||||||
if string.sub(data, 1, 1) ~= "i" then
|
if string.sub(data, 1, 1) ~= "i" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local text = string.sub(data, 2)
|
local text = string.sub(data, 2)
|
||||||
local number = tonumber(text)
|
local number = tonumber(text)
|
||||||
return number
|
return number
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parses an NSON float
|
-- parses an NSON float
|
||||||
local function parsefloat(data)
|
local function parsefloat(data)
|
||||||
if string.sub(data, 1, 1) ~= "f" then
|
if string.sub(data, 1, 1) ~= "f" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local text = string.sub(data, 2)
|
local text = string.sub(data, 2)
|
||||||
local number = tonumber(text)
|
local number = tonumber(text)
|
||||||
return number
|
return number
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parses an NSON string
|
-- parses an NSON string
|
||||||
local function parsestring(data)
|
local function parsestring(data)
|
||||||
if string.sub(data, 1, 1) ~= "s" then
|
if string.sub(data, 1, 1) ~= "s" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return string.sub(data, 2)
|
return string.sub(data, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parses an NSON int, float, or string
|
-- parses an NSON int, float, or string
|
||||||
local function parsesimple(data)
|
local function parsesimple(data)
|
||||||
local i = parseint(data)
|
local i = parseint(data)
|
||||||
local f = parsefloat(data)
|
local f = parsefloat(data)
|
||||||
local s = parsestring(data)
|
local s = parsestring(data)
|
||||||
return i or f or s
|
return i or f or s
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parses an NSON dictionary
|
-- parses an NSON dictionary
|
||||||
local function parsedict(data)
|
local function parsedict(data)
|
||||||
if #data < 1 then
|
if #data < 1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if string.sub(data, 1, 1) ~= "d" then
|
if string.sub(data, 1, 1) ~= "d" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local rest = string.sub(data, 2)
|
local rest = string.sub(data, 2)
|
||||||
local dict = {}
|
local dict = {}
|
||||||
while #rest > 0 do
|
while #rest > 0 do
|
||||||
local chunk, key, value
|
local chunk, key, value
|
||||||
chunk, rest = parsechunk(rest)
|
chunk, rest = parsechunk(rest)
|
||||||
if not chunk then
|
if not chunk then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
key = parsestring(chunk)
|
key = parsestring(chunk)
|
||||||
value, rest = parsechunk(rest)
|
value, rest = parsechunk(rest)
|
||||||
if not value then
|
if not value then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
dict[key] = value
|
dict[key] = value
|
||||||
end
|
end
|
||||||
return dict
|
return dict
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parses an NSON array
|
-- parses an NSON array
|
||||||
local function parsearray(data)
|
local function parsearray(data)
|
||||||
if #data < 1 then
|
if #data < 1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if string.sub(data, 1, 1) ~= "a" then
|
if string.sub(data, 1, 1) ~= "a" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local rest = string.sub(data, 2)
|
local rest = string.sub(data, 2)
|
||||||
local array = {}
|
local array = {}
|
||||||
while #rest > 0 do
|
while #rest > 0 do
|
||||||
local value
|
local value
|
||||||
value, rest = parsechunk(rest)
|
value, rest = parsechunk(rest)
|
||||||
if not value then
|
if not value then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
table.insert(array, value)
|
table.insert(array, value)
|
||||||
end
|
end
|
||||||
return array
|
return array
|
||||||
end
|
end
|
||||||
|
|
||||||
-- OpenLookup specific stuff
|
-- OpenLookup specific stuff
|
||||||
|
|
||||||
local function formataddress(data)
|
local function formataddress(data)
|
||||||
local parts = parsearray(data)
|
local parts = parsearray(data)
|
||||||
if not parts then
|
if not parts then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if #parts < 2 then
|
if #parts < 2 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local ip = parsestring(parts[1])
|
local ip = parsestring(parts[1])
|
||||||
if not ip then
|
if not ip then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local port = parseint(parts[2])
|
local port = parseint(parts[2])
|
||||||
if not port then
|
if not port then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return ip .. ":" .. port
|
return ip .. ":" .. port
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formattime(data)
|
local function formattime(data)
|
||||||
local time = parsefloat(data)
|
local time = parsefloat(data)
|
||||||
if not time then
|
if not time then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local human = stdnse.format_timestamp(time)
|
local human = stdnse.format_timestamp(time)
|
||||||
return time .. " (" .. human .. ")"
|
return time .. " (" .. human .. ")"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatkey(key)
|
local function formatkey(key)
|
||||||
local parts = stdnse.strsplit("_", key)
|
local parts = stdnse.strsplit("_", key)
|
||||||
return stdnse.strjoin(" ", parts)
|
return stdnse.strjoin(" ", parts)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatvalue(key, nson)
|
local function formatvalue(key, nson)
|
||||||
local value
|
local value
|
||||||
if key == "your_address" then
|
if key == "your_address" then
|
||||||
value = formataddress(nson)
|
value = formataddress(nson)
|
||||||
elseif key == "timestamp" then
|
elseif key == "timestamp" then
|
||||||
value = formattime(nson)
|
value = formattime(nson)
|
||||||
else
|
else
|
||||||
value = parsesimple(nson)
|
value = parsesimple(nson)
|
||||||
end
|
end
|
||||||
if not value then
|
if not value then
|
||||||
value = "<" .. #nson .. "B of data>"
|
value = "<" .. #nson .. "B of data>"
|
||||||
end
|
end
|
||||||
return value
|
return value
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format(rawkey, nson)
|
local function format(rawkey, nson)
|
||||||
local key = formatkey(rawkey)
|
local key = formatkey(rawkey)
|
||||||
local value = formatvalue(rawkey, nson)
|
local value = formatvalue(rawkey, nson)
|
||||||
return key .. ": " .. value
|
return key .. ": " .. value
|
||||||
end
|
end
|
||||||
|
|
||||||
function formatoptions(header)
|
function formatoptions(header)
|
||||||
local msg = parsedict(header)
|
local msg = parsedict(header)
|
||||||
if not msg then
|
if not msg then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local rawmeth = msg["method"]
|
local rawmeth = msg["method"]
|
||||||
if not rawmeth then
|
if not rawmeth then
|
||||||
stdnse.print_debug(2, "header missing method field")
|
stdnse.print_debug(2, "header missing method field")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local method = parsestring(rawmeth)
|
local method = parsestring(rawmeth)
|
||||||
if not method then
|
if not method then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if method ~= "hello" then
|
if method ~= "hello" then
|
||||||
stdnse.print_debug(1, "expecting hello, got " .. method .. " instead")
|
stdnse.print_debug(1, "expecting hello, got " .. method .. " instead")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local rawopts = msg["options"]
|
local rawopts = msg["options"]
|
||||||
if not rawopts then
|
if not rawopts then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
local options = parsedict(rawopts)
|
local options = parsedict(rawopts)
|
||||||
if not options then
|
if not options then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local formatted = {}
|
local formatted = {}
|
||||||
for key, nson in pairs(options) do
|
for key, nson in pairs(options) do
|
||||||
local tmp = format(key, nson)
|
local tmp = format(key, nson)
|
||||||
if tmp then
|
if tmp then
|
||||||
table.insert(formatted, tmp)
|
table.insert(formatted, tmp)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, banner = comm.get_banner(host, port)
|
local status, banner = comm.get_banner(host, port)
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local header, _ = parsechunk(banner)
|
local header, _ = parsechunk(banner)
|
||||||
if not header then
|
if not header then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local options = formatoptions(header)
|
local options = formatoptions(header)
|
||||||
if not options then
|
if not options then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
port.version.name = "openlookup"
|
port.version.name = "openlookup"
|
||||||
local version = options["version"]
|
local version = options["version"]
|
||||||
if version then
|
if version then
|
||||||
port.version.version = version
|
port.version.version = version
|
||||||
end
|
end
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
if #options < 1 then
|
if #options < 1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local response = {}
|
local response = {}
|
||||||
table.insert(response, options)
|
table.insert(response, options)
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -63,141 +63,141 @@ local johnfile
|
|||||||
Driver =
|
Driver =
|
||||||
{
|
{
|
||||||
|
|
||||||
new = function(self, host, port, sid )
|
new = function(self, host, port, sid )
|
||||||
local o = { host = host, port = port, sid = sid }
|
local o = { host = host, port = port, sid = sid }
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Connects performs protocol negotiation
|
--- Connects performs protocol negotiation
|
||||||
--
|
--
|
||||||
-- @return true on success, false on failure
|
-- @return true on success, false on failure
|
||||||
connect = function( self )
|
connect = function( self )
|
||||||
local MAX_RETRIES = 10
|
local MAX_RETRIES = 10
|
||||||
local tries = MAX_RETRIES
|
local tries = MAX_RETRIES
|
||||||
|
|
||||||
self.helper = ConnectionPool[coroutine.running()]
|
self.helper = ConnectionPool[coroutine.running()]
|
||||||
if ( self.helper ) then return true end
|
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
|
-- This loop is intended for handling failed connections
|
||||||
-- A connection may fail for a number of different reasons.
|
-- A connection may fail for a number of different reasons.
|
||||||
-- For the moment, we're just handling the error code 12520
|
-- For the moment, we're just handling the error code 12520
|
||||||
--
|
--
|
||||||
-- Error 12520 has been observed on Oracle XE and seems to
|
-- Error 12520 has been observed on Oracle XE and seems to
|
||||||
-- occur when a maximum connection count is reached.
|
-- occur when a maximum connection count is reached.
|
||||||
local status, data
|
local status, data
|
||||||
repeat
|
repeat
|
||||||
if ( tries < MAX_RETRIES ) then
|
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)
|
stdnse.print_debug(2, "%s: Attempting to re-connect (attempt %d of %d)", SCRIPT_NAME, MAX_RETRIES - tries, MAX_RETRIES)
|
||||||
end
|
end
|
||||||
status, data = self.helper:Connect()
|
status, data = self.helper:Connect()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
||||||
self.helper:Close()
|
self.helper:Close()
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
tries = tries - 1
|
tries = tries - 1
|
||||||
stdnse.sleep(1)
|
stdnse.sleep(1)
|
||||||
until( tries == 0 or data ~= "12520" )
|
until( tries == 0 or data ~= "12520" )
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
ConnectionPool[coroutine.running()] = self.helper
|
ConnectionPool[coroutine.running()] = self.helper
|
||||||
end
|
end
|
||||||
|
|
||||||
return status, data
|
return status, data
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to login to the Oracle server
|
--- Attempts to login to the Oracle server
|
||||||
--
|
--
|
||||||
-- @param username string containing the login username
|
-- @param username string containing the login username
|
||||||
-- @param password string containing the login password
|
-- @param password string containing the login password
|
||||||
-- @return status, true on success, false on failure
|
-- @return status, true on success, false on failure
|
||||||
-- @return brute.Error object on failure
|
-- @return brute.Error object on failure
|
||||||
-- brute.Account object on success
|
-- brute.Account object on success
|
||||||
login = function( self, username, password )
|
login = function( self, username, password )
|
||||||
local status, data = self.helper:StealthLogin( username, password )
|
local status, data = self.helper:StealthLogin( username, password )
|
||||||
|
|
||||||
if ( data["AUTH_VFR_DATA"] ) then
|
if ( data["AUTH_VFR_DATA"] ) then
|
||||||
local hash = string.format("$o5logon$%s*%s", data["AUTH_SESSKEY"], data["AUTH_VFR_DATA"])
|
local hash = string.format("$o5logon$%s*%s", data["AUTH_SESSKEY"], data["AUTH_VFR_DATA"])
|
||||||
if ( johnfile ) then
|
if ( johnfile ) then
|
||||||
johnfile:write(("%s:%s\n"):format(username,hash))
|
johnfile:write(("%s:%s\n"):format(username,hash))
|
||||||
end
|
end
|
||||||
return true, brute.Account:new(username, hash, creds.State.HASHED)
|
return true, brute.Account:new(username, hash, creds.State.HASHED)
|
||||||
else
|
else
|
||||||
return false, brute.Error:new( data )
|
return false, brute.Error:new( data )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Disconnects and terminates the Oracle TNS communication
|
--- Disconnects and terminates the Oracle TNS communication
|
||||||
disconnect = function( self )
|
disconnect = function( self )
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
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 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 engine = brute.Engine:new(Driver, host, port, sid)
|
||||||
local arg_accounts = stdnse.get_script_args(SCRIPT_NAME .. '.accounts')
|
local arg_accounts = stdnse.get_script_args(SCRIPT_NAME .. '.accounts')
|
||||||
local mode = arg_accounts and "accounts" or "default"
|
local mode = arg_accounts and "accounts" or "default"
|
||||||
|
|
||||||
if ( not(sid) ) then
|
if ( not(sid) ) then
|
||||||
return "\n ERROR: Oracle instance not set (see oracle-brute-stealth.sid or tns.sid)"
|
return "\n ERROR: Oracle instance not set (see oracle-brute-stealth.sid or tns.sid)"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( arg_johnfile ) then
|
if ( arg_johnfile ) then
|
||||||
johnfile = io.open(arg_johnfile, "w")
|
johnfile = io.open(arg_johnfile, "w")
|
||||||
if ( not(johnfile) ) then
|
if ( not(johnfile) ) then
|
||||||
return ("\n ERROR: Failed to open %s for writing"):format(johnfile)
|
return ("\n ERROR: Failed to open %s for writing"):format(johnfile)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local helper = tns.Helper:new( host, port, sid )
|
local helper = tns.Helper:new( host, port, sid )
|
||||||
local status, result = helper:Connect()
|
local status, result = helper:Connect()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "\n ERROR: Failed to connect to oracle server"
|
return "\n ERROR: Failed to connect to oracle server"
|
||||||
end
|
end
|
||||||
helper:Close()
|
helper:Close()
|
||||||
|
|
||||||
if ( stdnse.get_script_args('userdb') or
|
if ( stdnse.get_script_args('userdb') or
|
||||||
stdnse.get_script_args('passdb') or
|
stdnse.get_script_args('passdb') or
|
||||||
stdnse.get_script_args('oracle-brute-stealth.nodefault') or
|
stdnse.get_script_args('oracle-brute-stealth.nodefault') or
|
||||||
stdnse.get_script_args('brute.credfile') ) then
|
stdnse.get_script_args('brute.credfile') ) then
|
||||||
mode = nil
|
mode = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( mode == "default" ) then
|
if ( mode == "default" ) then
|
||||||
local f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
local f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
||||||
end
|
end
|
||||||
|
|
||||||
f = io.open(f)
|
f = io.open(f)
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
||||||
end
|
end
|
||||||
|
|
||||||
engine.iterator = brute.Iterators.credential_iterator(f)
|
engine.iterator = brute.Iterators.credential_iterator(f)
|
||||||
elseif( "accounts" == mode ) then
|
elseif( "accounts" == mode ) then
|
||||||
engine.iterator = unpwdb.table_iterator(stdnse.strsplit(",%s*", arg_accounts))
|
engine.iterator = unpwdb.table_iterator(stdnse.strsplit(",%s*", arg_accounts))
|
||||||
end
|
end
|
||||||
|
|
||||||
engine.options.useraspass = false
|
engine.options.useraspass = false
|
||||||
engine.options.mode = "user"
|
engine.options.mode = "user"
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
status, result = engine:start()
|
status, result = engine:start()
|
||||||
|
|
||||||
if ( johnfile ) then
|
if ( johnfile ) then
|
||||||
johnfile:close()
|
johnfile:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -52,13 +52,13 @@ result in a large number of accounts being locked out on the database server.
|
|||||||
-- Version 0.3
|
-- Version 0.3
|
||||||
-- Created 07/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- 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
|
-- 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
|
-- Revised 07/25/2011 - v0.3 - added support for guessing default accounts
|
||||||
-- changed code to use ConnectionPool
|
-- changed code to use ConnectionPool
|
||||||
-- Revised 03/13/2012 - v0.4 - revised by L<>szl<7A> T<>th
|
-- Revised 03/13/2012 - v0.4 - revised by L<>szl<7A> T<>th
|
||||||
-- added support for SYSDBA accounts
|
-- added support for SYSDBA accounts
|
||||||
-- Revised 08/07/2012 - v0.5 - revised to suit the changes in brute
|
-- Revised 08/07/2012 - v0.5 - revised to suit the changes in brute
|
||||||
-- library [Aleksandar Nikolic]
|
-- library [Aleksandar Nikolic]
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Summary
|
-- Summary
|
||||||
@@ -79,147 +79,147 @@ local sysdba = {}
|
|||||||
Driver =
|
Driver =
|
||||||
{
|
{
|
||||||
|
|
||||||
new = function(self, host, port, sid )
|
new = function(self, host, port, sid )
|
||||||
local o = { host = host, port = port, sid = sid }
|
local o = { host = host, port = port, sid = sid }
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Connects performs protocol negotiation
|
--- Connects performs protocol negotiation
|
||||||
--
|
--
|
||||||
-- @return true on success, false on failure
|
-- @return true on success, false on failure
|
||||||
connect = function( self )
|
connect = function( self )
|
||||||
local MAX_RETRIES = 10
|
local MAX_RETRIES = 10
|
||||||
local tries = MAX_RETRIES
|
local tries = MAX_RETRIES
|
||||||
|
|
||||||
self.helper = ConnectionPool[coroutine.running()]
|
self.helper = ConnectionPool[coroutine.running()]
|
||||||
if ( self.helper ) then return true end
|
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
|
-- This loop is intended for handling failed connections
|
||||||
-- A connection may fail for a number of different reasons.
|
-- A connection may fail for a number of different reasons.
|
||||||
-- For the moment, we're just handling the error code 12520
|
-- For the moment, we're just handling the error code 12520
|
||||||
--
|
--
|
||||||
-- Error 12520 has been observed on Oracle XE and seems to
|
-- Error 12520 has been observed on Oracle XE and seems to
|
||||||
-- occur when a maximum connection count is reached.
|
-- occur when a maximum connection count is reached.
|
||||||
local status, data
|
local status, data
|
||||||
repeat
|
repeat
|
||||||
if ( tries < MAX_RETRIES ) then
|
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)
|
stdnse.print_debug(2, "%s: Attempting to re-connect (attempt %d of %d)", SCRIPT_NAME, MAX_RETRIES - tries, MAX_RETRIES)
|
||||||
end
|
end
|
||||||
status, data = self.helper:Connect()
|
status, data = self.helper:Connect()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
stdnse.print_debug(2, "%s: ERROR: An Oracle %s error occured", SCRIPT_NAME, data)
|
||||||
self.helper:Close()
|
self.helper:Close()
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
tries = tries - 1
|
tries = tries - 1
|
||||||
stdnse.sleep(1)
|
stdnse.sleep(1)
|
||||||
until( tries == 0 or data ~= "12520" )
|
until( tries == 0 or data ~= "12520" )
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
ConnectionPool[coroutine.running()] = self.helper
|
ConnectionPool[coroutine.running()] = self.helper
|
||||||
end
|
end
|
||||||
|
|
||||||
return status, data
|
return status, data
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to login to the Oracle server
|
--- Attempts to login to the Oracle server
|
||||||
--
|
--
|
||||||
-- @param username string containing the login username
|
-- @param username string containing the login username
|
||||||
-- @param password string containing the login password
|
-- @param password string containing the login password
|
||||||
-- @return status, true on success, false on failure
|
-- @return status, true on success, false on failure
|
||||||
-- @return brute.Error object on failure
|
-- @return brute.Error object on failure
|
||||||
-- brute.Account object on success
|
-- brute.Account object on success
|
||||||
login = function( self, username, password )
|
login = function( self, username, password )
|
||||||
local status, data = self.helper:Login( username, password )
|
local status, data = self.helper:Login( username, password )
|
||||||
|
|
||||||
if ( sysdba[username] ) then
|
if ( sysdba[username] ) then
|
||||||
return false, brute.Error:new("Account already discovered")
|
return false, brute.Error:new("Account already discovered")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
self.helper:Close()
|
self.helper:Close()
|
||||||
ConnectionPool[coroutine.running()] = nil
|
ConnectionPool[coroutine.running()] = nil
|
||||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||||
-- Check for account locked message
|
-- Check for account locked message
|
||||||
elseif ( data:match("ORA[-]28000") ) then
|
elseif ( data:match("ORA[-]28000") ) then
|
||||||
return true, brute.Account:new(username, password, creds.State.LOCKED)
|
return true, brute.Account:new(username, password, creds.State.LOCKED)
|
||||||
-- Check for account is SYSDBA message
|
-- Check for account is SYSDBA message
|
||||||
elseif ( data:match("ORA[-]28009") ) then
|
elseif ( data:match("ORA[-]28009") ) then
|
||||||
sysdba[username] = true
|
sysdba[username] = true
|
||||||
return true, brute.Account:new(username .. " as sysdba", password, creds.State.VALID)
|
return true, brute.Account:new(username .. " as sysdba", password, creds.State.VALID)
|
||||||
-- check for any other message
|
-- check for any other message
|
||||||
elseif ( data:match("ORA[-]%d+")) then
|
elseif ( data:match("ORA[-]%d+")) then
|
||||||
stdnse.print_debug(3, "username: %s, password: %s, error: %s", username, password, data )
|
stdnse.print_debug(3, "username: %s, password: %s, error: %s", username, password, data )
|
||||||
return false, brute.Error:new(data)
|
return false, brute.Error:new(data)
|
||||||
-- any other errors are likely communication related, attempt to re-try
|
-- any other errors are likely communication related, attempt to re-try
|
||||||
else
|
else
|
||||||
self.helper:Close()
|
self.helper:Close()
|
||||||
ConnectionPool[coroutine.running()] = nil
|
ConnectionPool[coroutine.running()] = nil
|
||||||
local err = brute.Error:new(data)
|
local err = brute.Error:new(data)
|
||||||
err:setRetry(true)
|
err:setRetry(true)
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
return false, brute.Error:new( data )
|
return false, brute.Error:new( data )
|
||||||
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Disconnects and terminates the Oracle TNS communication
|
--- Disconnects and terminates the Oracle TNS communication
|
||||||
disconnect = function( self )
|
disconnect = function( self )
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
local DEFAULT_ACCOUNTS = "nselib/data/oracle-default-accounts.lst"
|
||||||
local sid = stdnse.get_script_args('oracle-brute.sid') or
|
local sid = stdnse.get_script_args('oracle-brute.sid') or
|
||||||
stdnse.get_script_args('tns.sid')
|
stdnse.get_script_args('tns.sid')
|
||||||
local engine = brute.Engine:new(Driver, host, port, sid)
|
local engine = brute.Engine:new(Driver, host, port, sid)
|
||||||
local mode = "default"
|
local mode = "default"
|
||||||
|
|
||||||
if ( not(sid) ) then
|
if ( not(sid) ) then
|
||||||
return "\n ERROR: Oracle instance not set (see oracle-brute.sid or tns.sid)"
|
return "\n ERROR: Oracle instance not set (see oracle-brute.sid or tns.sid)"
|
||||||
end
|
end
|
||||||
|
|
||||||
local helper = tns.Helper:new( host, port, sid )
|
local helper = tns.Helper:new( host, port, sid )
|
||||||
local status, result = helper:Connect()
|
local status, result = helper:Connect()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "\n ERROR: Failed to connect to oracle server"
|
return "\n ERROR: Failed to connect to oracle server"
|
||||||
end
|
end
|
||||||
helper:Close()
|
helper:Close()
|
||||||
|
|
||||||
local f
|
local f
|
||||||
|
|
||||||
if ( stdnse.get_script_args('userdb') or
|
if ( stdnse.get_script_args('userdb') or
|
||||||
stdnse.get_script_args('passdb') or
|
stdnse.get_script_args('passdb') or
|
||||||
stdnse.get_script_args('oracle-brute.nodefault') or
|
stdnse.get_script_args('oracle-brute.nodefault') or
|
||||||
stdnse.get_script_args('brute.credfile') ) then
|
stdnse.get_script_args('brute.credfile') ) then
|
||||||
mode = nil
|
mode = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( mode == "default" ) then
|
if ( mode == "default" ) then
|
||||||
f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
f = nmap.fetchfile(DEFAULT_ACCOUNTS)
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
return ("\n ERROR: Failed to find %s"):format(DEFAULT_ACCOUNTS)
|
||||||
end
|
end
|
||||||
|
|
||||||
f = io.open(f)
|
f = io.open(f)
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS)
|
||||||
end
|
end
|
||||||
|
|
||||||
engine.iterator = brute.Iterators.credential_iterator(f)
|
engine.iterator = brute.Iterators.credential_iterator(f)
|
||||||
end
|
end
|
||||||
|
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
status, result = engine:start()
|
status, result = engine:start()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -69,188 +69,188 @@ categories = {"default", "discovery", "safe", "version"}
|
|||||||
|
|
||||||
|
|
||||||
local function range(first, last)
|
local function range(first, last)
|
||||||
local list = {}
|
local list = {}
|
||||||
for i = first, last do
|
for i = first, last do
|
||||||
table.insert(list, i)
|
table.insert(list, i)
|
||||||
end
|
end
|
||||||
return list
|
return list
|
||||||
end
|
end
|
||||||
|
|
||||||
portrule = shortport.port_or_service(range(27960, 27970), {'quake3'}, 'udp')
|
portrule = shortport.port_or_service(range(27960, 27970), {'quake3'}, 'udp')
|
||||||
|
|
||||||
local function parsefields(data)
|
local function parsefields(data)
|
||||||
local fields = {}
|
local fields = {}
|
||||||
local parts = stdnse.strsplit("\\", data)
|
local parts = stdnse.strsplit("\\", data)
|
||||||
local nullprefix = table.remove(parts, 1)
|
local nullprefix = table.remove(parts, 1)
|
||||||
if nullprefix ~= "" then
|
if nullprefix ~= "" then
|
||||||
stdnse.print_debug(2, "unrecognized field format, skipping options")
|
stdnse.print_debug(2, "unrecognized field format, skipping options")
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
for i = 1, #parts, 2 do
|
for i = 1, #parts, 2 do
|
||||||
local key = parts[i]
|
local key = parts[i]
|
||||||
local value = parts[i + 1]
|
local value = parts[i + 1]
|
||||||
fields[key] = value
|
fields[key] = value
|
||||||
end
|
end
|
||||||
return fields
|
return fields
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parsename(data)
|
local function parsename(data)
|
||||||
local parts = stdnse.strsplit('"', data)
|
local parts = stdnse.strsplit('"', data)
|
||||||
if #parts ~= 3 then
|
if #parts ~= 3 then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local e1 = parts[1]
|
local e1 = parts[1]
|
||||||
local name = parts[2]
|
local name = parts[2]
|
||||||
local e2 = parts[3]
|
local e2 = parts[3]
|
||||||
local extra = e1 .. e2
|
local extra = e1 .. e2
|
||||||
if extra ~= "" then
|
if extra ~= "" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parseplayer(data)
|
local function parseplayer(data)
|
||||||
local parts = stdnse.strsplit(" ", data)
|
local parts = stdnse.strsplit(" ", data)
|
||||||
if #parts < 3 then
|
if #parts < 3 then
|
||||||
stdnse.print_debug(2, "player info line is missing elements, skipping a player")
|
stdnse.print_debug(2, "player info line is missing elements, skipping a player")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
if #parts > 3 then
|
if #parts > 3 then
|
||||||
stdnse.print_debug(2, "player info line has unknown elements, skipping a player")
|
stdnse.print_debug(2, "player info line has unknown elements, skipping a player")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local player = {}
|
local player = {}
|
||||||
player.frags = parts[1]
|
player.frags = parts[1]
|
||||||
player.ping = parts[2]
|
player.ping = parts[2]
|
||||||
player.name = parsename(parts[3])
|
player.name = parsename(parts[3])
|
||||||
if player.name == nil then
|
if player.name == nil then
|
||||||
stdnse.print_debug(2, "invalid player name serialization, skipping a player")
|
stdnse.print_debug(2, "invalid player name serialization, skipping a player")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return player
|
return player
|
||||||
end
|
end
|
||||||
|
|
||||||
local function parseplayers(data)
|
local function parseplayers(data)
|
||||||
local players = {}
|
local players = {}
|
||||||
for _, p in ipairs(data) do
|
for _, p in ipairs(data) do
|
||||||
local player = parseplayer(p)
|
local player = parseplayer(p)
|
||||||
if player then
|
if player then
|
||||||
table.insert(players, player)
|
table.insert(players, player)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return players
|
return players
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_leader(a, b)
|
local function is_leader(a, b)
|
||||||
local collide = a.name == b.name
|
local collide = a.name == b.name
|
||||||
local even = a.frags == b.frags
|
local even = a.frags == b.frags
|
||||||
local leads = a.frags > b.frags
|
local leads = a.frags > b.frags
|
||||||
local alphab = a.name > b.name
|
local alphab = a.name > b.name
|
||||||
local faster = a.ping > b.ping
|
local faster = a.ping > b.ping
|
||||||
return leads or (even and alphab) or (even and collide and faster)
|
return leads or (even and alphab) or (even and collide and faster)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatplayers(players, fraglimit)
|
local function formatplayers(players, fraglimit)
|
||||||
table.sort(players, is_leader)
|
table.sort(players, is_leader)
|
||||||
local printable = {}
|
local printable = {}
|
||||||
for i, player in ipairs(players) do
|
for i, player in ipairs(players) do
|
||||||
local name = player.name
|
local name = player.name
|
||||||
local ping = player.ping
|
local ping = player.ping
|
||||||
local frags = player.frags
|
local frags = player.frags
|
||||||
if fraglimit then
|
if fraglimit then
|
||||||
frags = string.format("%s/%s", frags, fraglimit)
|
frags = string.format("%s/%s", frags, fraglimit)
|
||||||
end
|
end
|
||||||
table.insert(printable, string.format("%d. %s (frags: %s, ping: %s)", i, name, frags, ping))
|
table.insert(printable, string.format("%d. %s (frags: %s, ping: %s)", i, name, frags, ping))
|
||||||
end
|
end
|
||||||
printable["name"] = "PLAYERS:"
|
printable["name"] = "PLAYERS:"
|
||||||
return printable
|
return printable
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatfields(fields, title)
|
local function formatfields(fields, title)
|
||||||
local printable = {}
|
local printable = {}
|
||||||
for key, value in pairs(fields) do
|
for key, value in pairs(fields) do
|
||||||
local kv = string.format("%s: %s", key, value)
|
local kv = string.format("%s: %s", key, value)
|
||||||
table.insert(printable, kv)
|
table.insert(printable, kv)
|
||||||
end
|
end
|
||||||
table.sort(printable)
|
table.sort(printable)
|
||||||
printable["name"] = title
|
printable["name"] = title
|
||||||
return printable
|
return printable
|
||||||
end
|
end
|
||||||
|
|
||||||
local function assorted(fields)
|
local function assorted(fields)
|
||||||
local basic = {}
|
local basic = {}
|
||||||
local other = {}
|
local other = {}
|
||||||
for key, value in pairs(fields) do
|
for key, value in pairs(fields) do
|
||||||
if string.find(key, "_") == nil then
|
if string.find(key, "_") == nil then
|
||||||
basic[key] = value
|
basic[key] = value
|
||||||
else
|
else
|
||||||
other[key] = value
|
other[key] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return basic, other
|
return basic, other
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local GETSTATUS = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getstatus\n")
|
local GETSTATUS = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getstatus\n")
|
||||||
local STATUSRESP = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "statusResponse")
|
local STATUSRESP = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "statusResponse")
|
||||||
|
|
||||||
local status, data = comm.exchange(host, port, GETSTATUS, {["proto"] = "udp"})
|
local status, data = comm.exchange(host, port, GETSTATUS, {["proto"] = "udp"})
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local parts = stdnse.strsplit("\n", data)
|
local parts = stdnse.strsplit("\n", data)
|
||||||
local header = table.remove(parts, 1)
|
local header = table.remove(parts, 1)
|
||||||
if header ~= STATUSRESP then
|
if header ~= STATUSRESP then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if #parts < 2 then
|
if #parts < 2 then
|
||||||
stdnse.print_debug(2, "incomplete status response, script abort")
|
stdnse.print_debug(2, "incomplete status response, script abort")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local nullend = table.remove(parts)
|
local nullend = table.remove(parts)
|
||||||
if nullend ~= "" then
|
if nullend ~= "" then
|
||||||
stdnse.print_debug(2, "missing terminating endline, script abort")
|
stdnse.print_debug(2, "missing terminating endline, script abort")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local field_data = table.remove(parts, 1)
|
local field_data = table.remove(parts, 1)
|
||||||
local player_data = parts
|
local player_data = parts
|
||||||
|
|
||||||
local fields = parsefields(field_data)
|
local fields = parsefields(field_data)
|
||||||
local players = parseplayers(player_data)
|
local players = parseplayers(player_data)
|
||||||
|
|
||||||
local basic, other = assorted(fields)
|
local basic, other = assorted(fields)
|
||||||
|
|
||||||
-- Previously observed version strings:
|
-- Previously observed version strings:
|
||||||
-- "tremulous 1.1.0 linux-x86_64 Aug 5 2010"
|
-- "tremulous 1.1.0 linux-x86_64 Aug 5 2010"
|
||||||
-- "ioq3 1.36+svn1933-1/Ubuntu linux-x86_64 Apr 4 2011"
|
-- "ioq3 1.36+svn1933-1/Ubuntu linux-x86_64 Apr 4 2011"
|
||||||
local versionline = basic["version"]
|
local versionline = basic["version"]
|
||||||
if versionline then
|
if versionline then
|
||||||
local fields = stdnse.strsplit(" ", versionline)
|
local fields = stdnse.strsplit(" ", versionline)
|
||||||
local product = fields[1]
|
local product = fields[1]
|
||||||
local version = fields[2]
|
local version = fields[2]
|
||||||
local osline = fields[3]
|
local osline = fields[3]
|
||||||
port.version.name = "quake3"
|
port.version.name = "quake3"
|
||||||
port.version.product = product
|
port.version.product = product
|
||||||
port.version.version = version
|
port.version.version = version
|
||||||
if string.find(osline, "linux") then
|
if string.find(osline, "linux") then
|
||||||
port.version.ostype = "Linux"
|
port.version.ostype = "Linux"
|
||||||
end
|
end
|
||||||
if string.find(osline, "win") then
|
if string.find(osline, "win") then
|
||||||
port.version.ostype = "Windows"
|
port.version.ostype = "Windows"
|
||||||
end
|
end
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
end
|
end
|
||||||
|
|
||||||
local fraglimit = fields["fraglimit"]
|
local fraglimit = fields["fraglimit"]
|
||||||
if not fraglimit then
|
if not fraglimit then
|
||||||
fraglimit = "?"
|
fraglimit = "?"
|
||||||
end
|
end
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
table.insert(response, formatplayers(players, fraglimit))
|
table.insert(response, formatplayers(players, fraglimit))
|
||||||
table.insert(response, formatfields(basic, "BASIC OPTIONS:"))
|
table.insert(response, formatfields(basic, "BASIC OPTIONS:"))
|
||||||
if nmap.verbosity() > 0 then
|
if nmap.verbosity() > 0 then
|
||||||
table.insert(response, formatfields(other, "OTHER OPTIONS:"))
|
table.insert(response, formatfields(other, "OTHER OPTIONS:"))
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ categories = {"default", "discovery", "safe"}
|
|||||||
|
|
||||||
portrule = shortport.port_or_service ({20110, 20510, 27950, 30710}, "quake3-master", {"udp"})
|
portrule = shortport.port_or_service ({20110, 20510, 27950, 30710}, "quake3-master", {"udp"})
|
||||||
postrule = function()
|
postrule = function()
|
||||||
return (nmap.registry.q3m_servers ~= nil)
|
return (nmap.registry.q3m_servers ~= nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- There are various sources for this information. These include:
|
-- There are various sources for this information. These include:
|
||||||
@@ -41,213 +41,213 @@ end
|
|||||||
-- - scanning master servers
|
-- - scanning master servers
|
||||||
-- - looking at game traffic with Wireshark
|
-- - looking at game traffic with Wireshark
|
||||||
local KNOWN_PROTOCOLS = {
|
local KNOWN_PROTOCOLS = {
|
||||||
["5"] = "Call of Duty",
|
["5"] = "Call of Duty",
|
||||||
["10"] = "unknown",
|
["10"] = "unknown",
|
||||||
["43"] = "unknown",
|
["43"] = "unknown",
|
||||||
["48"] = "unknown",
|
["48"] = "unknown",
|
||||||
["50"] = "Return to Castle Wolfenstein",
|
["50"] = "Return to Castle Wolfenstein",
|
||||||
["57"] = "unknown",
|
["57"] = "unknown",
|
||||||
["59"] = "Return to Castle Wolfenstein",
|
["59"] = "Return to Castle Wolfenstein",
|
||||||
["60"] = "Return to Castle Wolfenstein",
|
["60"] = "Return to Castle Wolfenstein",
|
||||||
["66"] = "Quake III Arena",
|
["66"] = "Quake III Arena",
|
||||||
["67"] = "Quake III Arena",
|
["67"] = "Quake III Arena",
|
||||||
["68"] = "Quake III Arena, or Urban Terror",
|
["68"] = "Quake III Arena, or Urban Terror",
|
||||||
["69"] = "OpenArena, or Tremulous",
|
["69"] = "OpenArena, or Tremulous",
|
||||||
["70"] = "unknown",
|
["70"] = "unknown",
|
||||||
["71"] = "OpenArena",
|
["71"] = "OpenArena",
|
||||||
["72"] = "Wolfenstein: Enemy Territory",
|
["72"] = "Wolfenstein: Enemy Territory",
|
||||||
["80"] = "Wolfenstein: Enemy Territory",
|
["80"] = "Wolfenstein: Enemy Territory",
|
||||||
["83"] = "Wolfenstein: Enemy Territory",
|
["83"] = "Wolfenstein: Enemy Territory",
|
||||||
["84"] = "Wolfenstein: Enemy Territory",
|
["84"] = "Wolfenstein: Enemy Territory",
|
||||||
["2003"] = "Soldier of Fortune II: Double Helix",
|
["2003"] = "Soldier of Fortune II: Double Helix",
|
||||||
["2004"] = "Soldier of Fortune II: Double Helix",
|
["2004"] = "Soldier of Fortune II: Double Helix",
|
||||||
["DarkPlaces-Quake 3"] = "DarkPlaces Quake",
|
["DarkPlaces-Quake 3"] = "DarkPlaces Quake",
|
||||||
["Nexuiz 3"] = "Nexuiz",
|
["Nexuiz 3"] = "Nexuiz",
|
||||||
["Transfusion 3"] = "Transfusion",
|
["Transfusion 3"] = "Transfusion",
|
||||||
["Warsow 8"] = "Warsow",
|
["Warsow 8"] = "Warsow",
|
||||||
["Xonotic 3"] = "Xonotic",
|
["Xonotic 3"] = "Xonotic",
|
||||||
}
|
}
|
||||||
|
|
||||||
local function getservers(host, port, q3protocol)
|
local function getservers(host, port, q3protocol)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(10000)
|
socket:set_timeout(10000)
|
||||||
local status, err = socket:connect(host.ip, port.number, "udp")
|
local status, err = socket:connect(host.ip, port.number, "udp")
|
||||||
if not status then
|
if not status then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
local probe = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, string.format("getservers %s empty full\n", q3protocol))
|
local probe = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, string.format("getservers %s empty full\n", q3protocol))
|
||||||
socket:send(probe)
|
socket:send(probe)
|
||||||
|
|
||||||
local data
|
local data
|
||||||
status, data = socket:receive() -- get some data
|
status, data = socket:receive() -- get some data
|
||||||
if not status then
|
if not status then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
|
|
||||||
local magic = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getserversResponse")
|
local magic = bin.pack("CCCCA", 0xff, 0xff, 0xff, 0xff, "getserversResponse")
|
||||||
local tmp
|
local tmp
|
||||||
while #data < #magic do -- get header
|
while #data < #magic do -- get header
|
||||||
status, tmp = socket:receive()
|
status, tmp = socket:receive()
|
||||||
if status then
|
if status then
|
||||||
data = data .. tmp
|
data = data .. tmp
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if string.sub(data, 1, #magic) ~= magic then -- no match
|
if string.sub(data, 1, #magic) ~= magic then -- no match
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
|
|
||||||
port.version.name = "quake3-master"
|
port.version.name = "quake3-master"
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
|
|
||||||
local EOT = bin.pack("ACCC", "EOT", 0, 0, 0)
|
local EOT = bin.pack("ACCC", "EOT", 0, 0, 0)
|
||||||
local pieces = stdnse.strsplit("\\", data)
|
local pieces = stdnse.strsplit("\\", data)
|
||||||
while pieces[#pieces] ~= EOT do -- get all data
|
while pieces[#pieces] ~= EOT do -- get all data
|
||||||
status, tmp = socket:receive()
|
status, tmp = socket:receive()
|
||||||
if status then
|
if status then
|
||||||
data = data .. tmp
|
data = data .. tmp
|
||||||
pieces = stdnse.strsplit("\\", data)
|
pieces = stdnse.strsplit("\\", data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.remove(pieces, 1) --remove magic
|
table.remove(pieces, 1) --remove magic
|
||||||
table.remove(pieces, #pieces) --remove EOT
|
table.remove(pieces, #pieces) --remove EOT
|
||||||
|
|
||||||
local servers = {}
|
local servers = {}
|
||||||
for _, value in ipairs(pieces) do
|
for _, value in ipairs(pieces) do
|
||||||
local parts = {bin.unpack("CCCC>S", value)}
|
local parts = {bin.unpack("CCCC>S", value)}
|
||||||
if #parts > 5 then
|
if #parts > 5 then
|
||||||
local o1 = parts[2]
|
local o1 = parts[2]
|
||||||
local o2 = parts[3]
|
local o2 = parts[3]
|
||||||
local o3 = parts[4]
|
local o3 = parts[4]
|
||||||
local o4 = parts[5]
|
local o4 = parts[5]
|
||||||
local p = parts[6]
|
local p = parts[6]
|
||||||
table.insert(servers, {string.format("%d.%d.%d.%d", o1, o2, o3, o4), p})
|
table.insert(servers, {string.format("%d.%d.%d.%d", o1, o2, o3, o4), p})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
socket:close()
|
socket:close()
|
||||||
return servers
|
return servers
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatresult(servers, outputlimit, protocols)
|
local function formatresult(servers, outputlimit, protocols)
|
||||||
local t = tab.new()
|
local t = tab.new()
|
||||||
|
|
||||||
if not outputlimit then
|
if not outputlimit then
|
||||||
outputlimit = #servers
|
outputlimit = #servers
|
||||||
end
|
end
|
||||||
for i = 1, outputlimit do
|
for i = 1, outputlimit do
|
||||||
if not servers[i] then
|
if not servers[i] then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
local node = servers[i]
|
local node = servers[i]
|
||||||
local protocol = node.protocol
|
local protocol = node.protocol
|
||||||
local ip = node.ip
|
local ip = node.ip
|
||||||
local portnum = node.port
|
local portnum = node.port
|
||||||
tab.addrow(t, string.format('%s:%d', ip, portnum), string.format('%s (%s)', protocols[protocol], protocol))
|
tab.addrow(t, string.format('%s:%d', ip, portnum), string.format('%s (%s)', protocols[protocol], protocol))
|
||||||
end
|
end
|
||||||
|
|
||||||
return tab.dump(t)
|
return tab.dump(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dropdupes(tables, stringify)
|
local function dropdupes(tables, stringify)
|
||||||
local unique = {}
|
local unique = {}
|
||||||
local dupe = {}
|
local dupe = {}
|
||||||
local s
|
local s
|
||||||
for _, v in ipairs(tables) do
|
for _, v in ipairs(tables) do
|
||||||
s = stringify(v)
|
s = stringify(v)
|
||||||
if not dupe[s] then
|
if not dupe[s] then
|
||||||
table.insert(unique, v)
|
table.insert(unique, v)
|
||||||
dupe[s] = true
|
dupe[s] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return unique
|
return unique
|
||||||
end
|
end
|
||||||
|
|
||||||
local function scan(host, port, protocols)
|
local function scan(host, port, protocols)
|
||||||
local discovered = {}
|
local discovered = {}
|
||||||
for protocol, _ in pairs(protocols) do
|
for protocol, _ in pairs(protocols) do
|
||||||
for _, node in ipairs(getservers(host, port, protocol)) do
|
for _, node in ipairs(getservers(host, port, protocol)) do
|
||||||
local entry = {
|
local entry = {
|
||||||
protocol = protocol,
|
protocol = protocol,
|
||||||
ip = node[1],
|
ip = node[1],
|
||||||
port = node[2],
|
port = node[2],
|
||||||
masterip = host.ip,
|
masterip = host.ip,
|
||||||
masterport = port.number
|
masterport = port.number
|
||||||
}
|
}
|
||||||
table.insert(discovered, entry)
|
table.insert(discovered, entry)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return discovered
|
return discovered
|
||||||
end
|
end
|
||||||
|
|
||||||
local function store(servers)
|
local function store(servers)
|
||||||
if not nmap.registry.q3m_servers then
|
if not nmap.registry.q3m_servers then
|
||||||
nmap.registry.q3m_servers = {}
|
nmap.registry.q3m_servers = {}
|
||||||
end
|
end
|
||||||
for _, server in ipairs(servers) do
|
for _, server in ipairs(servers) do
|
||||||
table.insert(nmap.registry.q3m_servers, server)
|
table.insert(nmap.registry.q3m_servers, server)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function protocols()
|
local function protocols()
|
||||||
local filter = {}
|
local filter = {}
|
||||||
local count = {}
|
local count = {}
|
||||||
for _, advert in ipairs(nmap.registry.q3m_servers) do
|
for _, advert in ipairs(nmap.registry.q3m_servers) do
|
||||||
local key = stdnse.strjoin(":", {advert.ip, advert.port, advert.protocol})
|
local key = stdnse.strjoin(":", {advert.ip, advert.port, advert.protocol})
|
||||||
if filter[key] == nil then
|
if filter[key] == nil then
|
||||||
if count[advert.protocol] == nil then
|
if count[advert.protocol] == nil then
|
||||||
count[advert.protocol] = 0
|
count[advert.protocol] = 0
|
||||||
end
|
end
|
||||||
count[advert.protocol] = count[advert.protocol] + 1
|
count[advert.protocol] = count[advert.protocol] + 1
|
||||||
filter[key] = true
|
filter[key] = true
|
||||||
end
|
end
|
||||||
local mkey = stdnse.strjoin(":", {advert.masterip, advert.masterport})
|
local mkey = stdnse.strjoin(":", {advert.masterip, advert.masterport})
|
||||||
end
|
end
|
||||||
local sortable = {}
|
local sortable = {}
|
||||||
for k, v in pairs(count) do
|
for k, v in pairs(count) do
|
||||||
table.insert(sortable, {k, v})
|
table.insert(sortable, {k, v})
|
||||||
end
|
end
|
||||||
table.sort(sortable, function(a, b) return a[2] > b[2] or (a[2] == b[2] and a[1] > b[1]) 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()
|
local t = tab.new()
|
||||||
tab.addrow(t, '#', 'PROTOCOL', 'GAME', 'SERVERS')
|
tab.addrow(t, '#', 'PROTOCOL', 'GAME', 'SERVERS')
|
||||||
for i, p in ipairs(sortable) do
|
for i, p in ipairs(sortable) do
|
||||||
local pos = i .. '.'
|
local pos = i .. '.'
|
||||||
local protocol = p[1]
|
local protocol = p[1]
|
||||||
count = p[2]
|
count = p[2]
|
||||||
local game = KNOWN_PROTOCOLS[protocol]
|
local game = KNOWN_PROTOCOLS[protocol]
|
||||||
if game == "unknown" then
|
if game == "unknown" then
|
||||||
game = ""
|
game = ""
|
||||||
end
|
end
|
||||||
tab.addrow(t, pos, protocol, game, count)
|
tab.addrow(t, pos, protocol, game, count)
|
||||||
end
|
end
|
||||||
return '\n' .. tab.dump(t)
|
return '\n' .. tab.dump(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
if SCRIPT_TYPE == "postrule" then
|
if SCRIPT_TYPE == "postrule" then
|
||||||
return protocols()
|
return protocols()
|
||||||
end
|
end
|
||||||
local outputlimit = nmap.registry.args[SCRIPT_NAME .. ".outputlimit"]
|
local outputlimit = nmap.registry.args[SCRIPT_NAME .. ".outputlimit"]
|
||||||
if not outputlimit then
|
if not outputlimit then
|
||||||
outputlimit = 10
|
outputlimit = 10
|
||||||
else
|
else
|
||||||
outputlimit = tonumber(outputlimit)
|
outputlimit = tonumber(outputlimit)
|
||||||
end
|
end
|
||||||
if outputlimit < 1 then
|
if outputlimit < 1 then
|
||||||
outputlimit = nil
|
outputlimit = nil
|
||||||
end
|
end
|
||||||
local servers = scan(host, port, KNOWN_PROTOCOLS)
|
local servers = scan(host, port, KNOWN_PROTOCOLS)
|
||||||
store(servers)
|
store(servers)
|
||||||
local unique = dropdupes(servers, function(t) return string.format("%s: %s:%d", t.protocol, t.ip, t.port) end)
|
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)
|
local formatted = formatresult(unique, outputlimit, KNOWN_PROTOCOLS)
|
||||||
if #formatted < 1 then
|
if #formatted < 1 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local response = {}
|
local response = {}
|
||||||
table.insert(response, formatted)
|
table.insert(response, formatted)
|
||||||
if outputlimit and outputlimit < #servers then
|
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))
|
table.insert(response, string.format('Only %d/%d shown. Use --script-args %s.outputlimit=-1 to see all.', outputlimit, #servers, SCRIPT_NAME))
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -72,152 +72,152 @@ categories = {"intrusive", "vuln"}
|
|||||||
portrule = shortport.port_or_service({3389},{"ms-wbt-server"})
|
portrule = shortport.port_or_service({3389},{"ms-wbt-server"})
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local status, err,response
|
local status, err,response
|
||||||
|
|
||||||
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx for more info
|
-- 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
|
local connectionRequestStr = "0300" -- TPKT Header version 03, reserved 0
|
||||||
.. "000b" -- Length
|
.. "000b" -- Length
|
||||||
.. "06" -- X.224 Data TPDU length
|
.. "06" -- X.224 Data TPDU length
|
||||||
.. "e0" -- X.224 Type (Connection request)
|
.. "e0" -- X.224 Type (Connection request)
|
||||||
.. "0000" -- dst reference
|
.. "0000" -- dst reference
|
||||||
.. "0000" -- src reference
|
.. "0000" -- src reference
|
||||||
.. "00" -- class and options
|
.. "00" -- class and options
|
||||||
local connectionRequest = bin.pack("H",connectionRequestStr)
|
local connectionRequest = bin.pack("H",connectionRequestStr)
|
||||||
|
|
||||||
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx
|
-- see http://msdn.microsoft.com/en-us/library/cc240836%28v=prot.10%29.aspx
|
||||||
local connectInitialStr = "03000065" -- TPKT Header
|
local connectInitialStr = "03000065" -- TPKT Header
|
||||||
.. "02f080" -- Data TPDU, EOT
|
.. "02f080" -- Data TPDU, EOT
|
||||||
.. "7f655b" -- Connect-Initial
|
.. "7f655b" -- Connect-Initial
|
||||||
.. "040101" -- callingDomainSelector
|
.. "040101" -- callingDomainSelector
|
||||||
.. "040101" -- calledDomainSelector
|
.. "040101" -- calledDomainSelector
|
||||||
.. "0101ff" -- upwardFlag
|
.. "0101ff" -- upwardFlag
|
||||||
.. "3019" -- targetParams + size
|
.. "3019" -- targetParams + size
|
||||||
.. "020122" -- maxChannelIds
|
.. "020122" -- maxChannelIds
|
||||||
.. "020120" -- maxUserIds
|
.. "020120" -- maxUserIds
|
||||||
.. "020100" -- maxTokenIds
|
.. "020100" -- maxTokenIds
|
||||||
.. "020101" -- numPriorities
|
.. "020101" -- numPriorities
|
||||||
.. "020100" -- minThroughput
|
.. "020100" -- minThroughput
|
||||||
.. "020101" -- maxHeight
|
.. "020101" -- maxHeight
|
||||||
.. "0202ffff" -- maxMCSPDUSize
|
.. "0202ffff" -- maxMCSPDUSize
|
||||||
.. "020102" -- protocolVersion
|
.. "020102" -- protocolVersion
|
||||||
.. "3018" -- minParams + size
|
.. "3018" -- minParams + size
|
||||||
.. "020101" -- maxChannelIds
|
.. "020101" -- maxChannelIds
|
||||||
.. "020101" -- maxUserIds
|
.. "020101" -- maxUserIds
|
||||||
.. "020101" -- maxTokenIds
|
.. "020101" -- maxTokenIds
|
||||||
.. "020101" -- numPriorities
|
.. "020101" -- numPriorities
|
||||||
.. "020100" -- minThroughput
|
.. "020100" -- minThroughput
|
||||||
.. "020101" -- maxHeight
|
.. "020101" -- maxHeight
|
||||||
.. "0201ff" -- maxMCSPDUSize
|
.. "0201ff" -- maxMCSPDUSize
|
||||||
.. "020102" -- protocolVersion
|
.. "020102" -- protocolVersion
|
||||||
.. "3019" -- maxParams + size
|
.. "3019" -- maxParams + size
|
||||||
.. "0201ff" -- maxChannelIds
|
.. "0201ff" -- maxChannelIds
|
||||||
.. "0201ff" -- maxUserIds
|
.. "0201ff" -- maxUserIds
|
||||||
.. "0201ff" -- maxTokenIds
|
.. "0201ff" -- maxTokenIds
|
||||||
.. "020101" -- numPriorities
|
.. "020101" -- numPriorities
|
||||||
.. "020100" -- minThroughput
|
.. "020100" -- minThroughput
|
||||||
.. "020101" -- maxHeight
|
.. "020101" -- maxHeight
|
||||||
.. "0202ffff" -- maxMCSPDUSize
|
.. "0202ffff" -- maxMCSPDUSize
|
||||||
.. "020102" -- protocolVersion
|
.. "020102" -- protocolVersion
|
||||||
.. "0400" -- userData
|
.. "0400" -- userData
|
||||||
local connectInitial = bin.pack("H",connectInitialStr)
|
local connectInitial = bin.pack("H",connectInitialStr)
|
||||||
|
|
||||||
-- see http://msdn.microsoft.com/en-us/library/cc240835%28v=prot.10%29.aspx
|
-- see http://msdn.microsoft.com/en-us/library/cc240835%28v=prot.10%29.aspx
|
||||||
local userRequestStr = "0300" -- header
|
local userRequestStr = "0300" -- header
|
||||||
.. "0008" -- length
|
.. "0008" -- length
|
||||||
.. "02f080" -- X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)
|
.. "02f080" -- X.224 Data TPDU (2 bytes: 0xf0 = Data TPDU, 0x80 = EOT, end of transmission)
|
||||||
.. "28" -- PER encoded PDU contents
|
.. "28" -- PER encoded PDU contents
|
||||||
local userRequest = bin.pack("H",userRequestStr)
|
local userRequest = bin.pack("H",userRequestStr)
|
||||||
|
|
||||||
local user1,user2
|
local user1,user2
|
||||||
local pos
|
local pos
|
||||||
|
|
||||||
local rdp_vuln_0152 = {
|
local rdp_vuln_0152 = {
|
||||||
title = "MS12-020 Remote Desktop Protocol Denial Of Service Vulnerability",
|
title = "MS12-020 Remote Desktop Protocol Denial Of Service Vulnerability",
|
||||||
IDS = {CVE = 'CVE-2012-0152'},
|
IDS = {CVE = 'CVE-2012-0152'},
|
||||||
risk_factor = "Medium",
|
risk_factor = "Medium",
|
||||||
scores = {
|
scores = {
|
||||||
CVSSv2 = "4.3 (MEDIUM) (AV:N/AC:M/Au:N/C:N/I:N/A:P)",
|
CVSSv2 = "4.3 (MEDIUM) (AV:N/AC:M/Au:N/C:N/I:N/A:P)",
|
||||||
},
|
},
|
||||||
description = [[
|
description = [[
|
||||||
Remote Desktop Protocol vulnerability that could allow remote attackers to cause a denial of service.
|
Remote Desktop Protocol vulnerability that could allow remote attackers to cause a denial of service.
|
||||||
]],
|
]],
|
||||||
references = {
|
references = {
|
||||||
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
||||||
},
|
},
|
||||||
dates = {
|
dates = {
|
||||||
disclosure = {year = '2012', month = '03', day = '13'},
|
disclosure = {year = '2012', month = '03', day = '13'},
|
||||||
},
|
},
|
||||||
exploit_results = {},
|
exploit_results = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local rdp_vuln_0002 = {
|
local rdp_vuln_0002 = {
|
||||||
title = "MS12-020 Remote Desktop Protocol Remote Code Execution Vulnerability",
|
title = "MS12-020 Remote Desktop Protocol Remote Code Execution Vulnerability",
|
||||||
IDS = {CVE = 'CVE-2012-0002'},
|
IDS = {CVE = 'CVE-2012-0002'},
|
||||||
risk_factor = "High",
|
risk_factor = "High",
|
||||||
scores = {
|
scores = {
|
||||||
CVSSv2 = "9.3 (HIGH) (AV:N/AC:M/Au:N/C:C/I:C/A:C)",
|
CVSSv2 = "9.3 (HIGH) (AV:N/AC:M/Au:N/C:C/I:C/A:C)",
|
||||||
},
|
},
|
||||||
description = [[
|
description = [[
|
||||||
Remote Desktop Protocol vulnerability that could allow remote attackers to execute arbitrary code on the targeted system.
|
Remote Desktop Protocol vulnerability that could allow remote attackers to execute arbitrary code on the targeted system.
|
||||||
]],
|
]],
|
||||||
references = {
|
references = {
|
||||||
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
'http://technet.microsoft.com/en-us/security/bulletin/ms12-020',
|
||||||
},
|
},
|
||||||
dates = {
|
dates = {
|
||||||
disclosure = {year = '2012', month = '03', day = '13'},
|
disclosure = {year = '2012', month = '03', day = '13'},
|
||||||
},
|
},
|
||||||
exploit_results = {},
|
exploit_results = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||||
rdp_vuln_0152.state = vulns.STATE.NOT_VULN
|
rdp_vuln_0152.state = vulns.STATE.NOT_VULN
|
||||||
rdp_vuln_0002.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.
|
-- 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
|
-- 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
|
-- 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.
|
-- handshake. In my tests, sleep values above 0.1s prevent the connection reset.
|
||||||
stdnse.sleep(0.2)
|
stdnse.sleep(0.2)
|
||||||
|
|
||||||
socket:connect(host.ip, port)
|
socket:connect(host.ip, port)
|
||||||
status, err = socket:send(connectionRequest)
|
status, err = socket:send(connectionRequest)
|
||||||
|
|
||||||
status, response = socket:receive_bytes(0)
|
status, response = socket:receive_bytes(0)
|
||||||
if response ~= bin.pack("H","0300000b06d00000123400") then
|
if response ~= bin.pack("H","0300000b06d00000123400") then
|
||||||
--probably not rdp at all
|
--probably not rdp at all
|
||||||
stdnse.print_debug(1, "%s: not RDP", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: not RDP", SCRIPT_NAME)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
status, err = socket:send(connectInitial)
|
status, err = socket:send(connectInitial)
|
||||||
status, err = socket:send(userRequest) -- send attach user request
|
status, err = socket:send(userRequest) -- send attach user request
|
||||||
status, response = socket:receive_bytes(0) -- recieve attach user confirm
|
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
|
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, err = socket:send(userRequest) -- send another attach user request
|
||||||
status, response = socket:receive_bytes(0) -- recieve another attach user confirm
|
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
|
pos,user2 = bin.unpack(">S",response:sub(10,11)) -- second user's channel - 1001
|
||||||
user2 = user2+1001 -- second user's channel
|
user2 = user2+1001 -- second user's channel
|
||||||
local data4 = bin.pack(">SS",user1,user2)
|
local data4 = bin.pack(">SS",user1,user2)
|
||||||
local data5 = bin.pack("H","0300000c02f08038") -- channel join request TPDU
|
local data5 = bin.pack("H","0300000c02f08038") -- channel join request TPDU
|
||||||
local channelJoinRequest = data5 .. data4
|
local channelJoinRequest = data5 .. data4
|
||||||
status, err = socket:send(channelJoinRequest) -- bogus channel join request user1 requests channel of user2
|
status, err = socket:send(channelJoinRequest) -- bogus channel join request user1 requests channel of user2
|
||||||
status, response = socket:receive_bytes(0)
|
status, response = socket:receive_bytes(0)
|
||||||
if response:sub(8,9) == bin.pack("H","3e00") then
|
if response:sub(8,9) == bin.pack("H","3e00") then
|
||||||
-- 3e00 indicates a successfull join
|
-- 3e00 indicates a successfull join
|
||||||
-- see http://msdn.microsoft.com/en-us/library/cc240911%28v=prot.10%29.aspx
|
-- see http://msdn.microsoft.com/en-us/library/cc240911%28v=prot.10%29.aspx
|
||||||
-- service is vulnerable
|
-- service is vulnerable
|
||||||
-- send a valid request to prevent the BSoD
|
-- send a valid request to prevent the BSoD
|
||||||
data4 = bin.pack(">SS",user2-1001,user2)
|
data4 = bin.pack(">SS",user2-1001,user2)
|
||||||
channelJoinRequest = data5 .. data4 -- valid join request
|
channelJoinRequest = data5 .. data4 -- valid join request
|
||||||
status, err = socket:send(channelJoinRequest)
|
status, err = socket:send(channelJoinRequest)
|
||||||
status, response = socket:receive_bytes(0)
|
status, response = socket:receive_bytes(0)
|
||||||
socket:close()
|
socket:close()
|
||||||
rdp_vuln_0152.state = vulns.STATE.VULN
|
rdp_vuln_0152.state = vulns.STATE.VULN
|
||||||
rdp_vuln_0002.state = vulns.STATE.VULN
|
rdp_vuln_0002.state = vulns.STATE.VULN
|
||||||
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
||||||
end
|
end
|
||||||
--service is not vulnerable
|
--service is not vulnerable
|
||||||
socket:close()
|
socket:close()
|
||||||
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
return report:make_output(rdp_vuln_0152,rdp_vuln_0002)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -155,20 +155,20 @@ portrule = shortport.port_or_service({1098, 1099, 1090, 8901, 8902, 8903}, {"jav
|
|||||||
-- Some lazy shortcuts
|
-- Some lazy shortcuts
|
||||||
|
|
||||||
local function dbg(str,...)
|
local function dbg(str,...)
|
||||||
stdnse.print_debug(3,"RMI-DUMPREG:"..str, ...)
|
stdnse.print_debug(3,"RMI-DUMPREG:"..str, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dbg_err(str, ... )
|
local function dbg_err(str, ... )
|
||||||
stdnse.print_debug("RMI-DUMPREG-ERR:"..str, ...)
|
stdnse.print_debug("RMI-DUMPREG-ERR:"..str, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Function to split a string
|
-- Function to split a string
|
||||||
local function split(str, sep)
|
local function split(str, sep)
|
||||||
local sep, fields = sep or "; ", {}
|
local sep, fields = sep or "; ", {}
|
||||||
local pattern = string.format("([^%s]+)", sep)
|
local pattern = string.format("([^%s]+)", sep)
|
||||||
str:gsub(pattern, function(c) fields[#fields+1] = c end)
|
str:gsub(pattern, function(c) fields[#fields+1] = c end)
|
||||||
return fields
|
return fields
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--This is a customData formatter. In some cases, the RMI library finds 'custom data' which belongs to an object.
|
--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.
|
-- of the returned RMI object.
|
||||||
-- @return title, data
|
-- @return title, data
|
||||||
function customDataFormatter(className, customData)
|
function customDataFormatter(className, customData)
|
||||||
if customData == nil then return nil end
|
if customData == nil then return nil end
|
||||||
if #customData ==0 then return nil end
|
if #customData ==0 then return nil end
|
||||||
|
|
||||||
local retData = {}
|
local retData = {}
|
||||||
for k,v in ipairs(customData) do
|
for k,v in ipairs(customData) do
|
||||||
if v:find("file:/") == 1 then
|
if v:find("file:/") == 1 then
|
||||||
-- This is a classpath
|
-- This is a classpath
|
||||||
local cp = split(v, "; ") -- Splits into table
|
local cp = split(v, "; ") -- Splits into table
|
||||||
table.insert(retData, "Classpath")
|
table.insert(retData, "Classpath")
|
||||||
table.insert(retData, cp)
|
table.insert(retData, cp)
|
||||||
else
|
else
|
||||||
table.insert(retData[v])
|
table.insert(retData[v])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return "Custom data", retData
|
return "Custom data", retData
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function action(host,port, args)
|
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 status, j_array = registry:list()
|
||||||
local output = {}
|
local output = {}
|
||||||
if not status then
|
if not status then
|
||||||
return false, ("Registry listing failed (%s)"):format(tostring(j_array))
|
return false, ("Registry listing failed (%s)"):format(tostring(j_array))
|
||||||
end
|
end
|
||||||
-- It's definitely RMI!
|
-- It's definitely RMI!
|
||||||
port.version.name ='java-rmi'
|
port.version.name ='java-rmi'
|
||||||
port.version.product='Java RMI Registry'
|
port.version.product='Java RMI Registry'
|
||||||
nmap.set_port_version(host,port)
|
nmap.set_port_version(host,port)
|
||||||
|
|
||||||
-- Monkey patch the java-class in rmi, to set our own custom data formatter
|
-- Monkey patch the java-class in rmi, to set our own custom data formatter
|
||||||
-- for classpaths
|
-- for classpaths
|
||||||
rmi.JavaClass.customDataFormatter = customDataFormatter
|
rmi.JavaClass.customDataFormatter = customDataFormatter
|
||||||
|
|
||||||
-- We expect an array of strings to be the return data
|
-- We expect an array of strings to be the return data
|
||||||
local data = j_array:getValues()
|
local data = j_array:getValues()
|
||||||
for i,name in ipairs( data ) do
|
for i,name in ipairs( data ) do
|
||||||
--print(data)
|
--print(data)
|
||||||
table.insert(output, name)
|
table.insert(output, name)
|
||||||
dbg("Querying object %s", name)
|
dbg("Querying object %s", name)
|
||||||
local status, j_object= registry:lookup(name)
|
local status, j_object= registry:lookup(name)
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
table.insert(output, j_object:toTable())
|
table.insert(output, j_object:toTable())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,16 +44,16 @@ categories = {"version"}
|
|||||||
|
|
||||||
|
|
||||||
portrule = function(host, port)
|
portrule = function(host, port)
|
||||||
-- Do not run for excluded ports
|
-- Do not run for excluded ports
|
||||||
if (nmap.port_is_excluded(port.number, port.protocol)) then
|
if (nmap.port_is_excluded(port.number, port.protocol)) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
if port.service ~= nil and port.version.service_dtype ~= "table" and port.service ~= 'rpcbind' then
|
if port.service ~= nil and port.version.service_dtype ~= "table" and port.service ~= 'rpcbind' then
|
||||||
-- Exclude services that have already been detected as something
|
-- Exclude services that have already been detected as something
|
||||||
-- different than rpcbind.
|
-- different than rpcbind.
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function that determines if the target port of host uses RPC protocol.
|
--- 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.
|
--@param port Port table as commonly used in Nmap.
|
||||||
--@return status boolean True if target port uses RPC protocol, false else.
|
--@return status boolean True if target port uses RPC protocol, false else.
|
||||||
local isRPC = function(host, port)
|
local isRPC = function(host, port)
|
||||||
-- If rpcbind is already set up by -sV
|
-- If rpcbind is already set up by -sV
|
||||||
-- which does practically the same check as in the "else" part.
|
-- which does practically the same check as in the "else" part.
|
||||||
-- The nmap-services-probe entry "rpcbind" is not correctly true, and should
|
-- The nmap-services-probe entry "rpcbind" is not correctly true, and should
|
||||||
-- be changed to something like "sunrpc"
|
-- be changed to something like "sunrpc"
|
||||||
if port.service == 'rpcbind' then
|
if port.service == 'rpcbind' then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
-- this check is important if we didn't run the scan with -sV.
|
-- 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
|
-- 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.
|
-- to the "rpcbind" service probe in nmap-service-probes.
|
||||||
local rpcConn, status, err, data, rxid, msgtype, _
|
local rpcConn, status, err, data, rxid, msgtype, _
|
||||||
|
|
||||||
-- Create new socket
|
-- Create new socket
|
||||||
-- rpcbind is not really important, we could have used another protocol from rpc.lua
|
-- rpcbind is not really important, we could have used another protocol from rpc.lua
|
||||||
-- such as nfs or mountd. Same thing for version 2.
|
-- such as nfs or mountd. Same thing for version 2.
|
||||||
rpcConn = rpc.Comm:new("rpcbind", 2)
|
rpcConn = rpc.Comm:new("rpcbind", 2)
|
||||||
status, err = rpcConn:Connect(host, port)
|
status, err = rpcConn:Connect(host, port)
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||||
return
|
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
|
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
-- Function that iterates over the nmap-rpc file and
|
-- Function that iterates over the nmap-rpc file and
|
||||||
@@ -119,34 +119,34 @@ end
|
|||||||
-- @return name Name of the RPC service.
|
-- @return name Name of the RPC service.
|
||||||
-- @return number RPC number of the matching service name.
|
-- @return number RPC number of the matching service name.
|
||||||
local rpcIterator = function()
|
local rpcIterator = function()
|
||||||
-- Check if nmap-rpc file is present.
|
-- Check if nmap-rpc file is present.
|
||||||
local path = nmap.fetchfile("nmap-rpc")
|
local path = nmap.fetchfile("nmap-rpc")
|
||||||
if not path then
|
if not path then
|
||||||
stdnse.print_debug("%s: Could not find nmap-rpc file.", SCRIPT_NAME)
|
stdnse.print_debug("%s: Could not find nmap-rpc file.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- And is readable
|
-- And is readable
|
||||||
local nmaprpc, _, _ = io.open( path, "r" )
|
local nmaprpc, _, _ = io.open( path, "r" )
|
||||||
if not nmaprpc then
|
if not nmaprpc then
|
||||||
stdnse.print_debug("%s: Could not open nmap-rpc for reading.", SCRIPT_NAME)
|
stdnse.print_debug("%s: Could not open nmap-rpc for reading.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return function()
|
return function()
|
||||||
while true do
|
while true do
|
||||||
local line = nmaprpc:read()
|
local line = nmaprpc:read()
|
||||||
if not line then
|
if not line then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
-- Now, we parse lines for meaningful ones
|
-- Now, we parse lines for meaningful ones
|
||||||
local name, number = line:match("^%s*([^%s#]+)%s+(%d+)")
|
local name, number = line:match("^%s*([^%s#]+)%s+(%d+)")
|
||||||
-- And return program name and number
|
-- And return program name and number
|
||||||
if name and number then
|
if name and number then
|
||||||
return name, tonumber(number)
|
return name, tonumber(number)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function that sends RPC null commands with a random version number and
|
--- 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 iterator Iterator function that returns program name and number pairs.
|
||||||
-- @param result table to put result into.
|
-- @param result table to put result into.
|
||||||
local rpcGrinder = function(host, port, iterator, result)
|
local rpcGrinder = function(host, port, iterator, result)
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
local rpcConn, version, xid, status, response, packet, err, data, _
|
local rpcConn, version, xid, status, response, packet, err, data, _
|
||||||
|
|
||||||
xid = math.random(123456789)
|
xid = math.random(123456789)
|
||||||
-- We use a random, most likely unsupported version so that
|
-- We use a random, most likely unsupported version so that
|
||||||
-- we also trigger min and max version disclosure for the target service.
|
-- we also trigger min and max version disclosure for the target service.
|
||||||
version = math.random(12345, 123456789)
|
version = math.random(12345, 123456789)
|
||||||
rpcConn = rpc.Comm:new("rpcbind", version)
|
rpcConn = rpc.Comm:new("rpcbind", version)
|
||||||
rpcConn:SetCheckProgVer(false)
|
rpcConn:SetCheckProgVer(false)
|
||||||
status, err = rpcConn:Connect(host, port)
|
status, err = rpcConn:Connect(host, port)
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
stdnse.print_debug("%s Connect(): %s", SCRIPT_NAME, err)
|
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
|
|
||||||
condvar "signal";
|
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
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local result, lthreads = {}, {}
|
local result, lthreads = {}, {}
|
||||||
|
|
||||||
if not isRPC(host, port) then
|
if not isRPC(host, port) then
|
||||||
stdnse.print_debug("Target port %s is not a RPC port.", port.number)
|
stdnse.print_debug("Target port %s is not a RPC port.", port.number)
|
||||||
return
|
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
|
end
|
||||||
local threads = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".threads")) or 4
|
if ( next(lthreads) ) then
|
||||||
|
condvar "wait";
|
||||||
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
|
end
|
||||||
|
until next(lthreads) == nil;
|
||||||
|
|
||||||
local condvar = nmap.condvar(result)
|
-- Check the result and set the port version.
|
||||||
repeat
|
if #result > 0 then
|
||||||
for thread in pairs(lthreads) do
|
port.version.name = result.program
|
||||||
if coroutine.status(thread) == "dead" then
|
port.version.extrainfo = "RPC #" .. result.number
|
||||||
lthreads[thread] = nil
|
if result.highver ~= result.lowver then
|
||||||
end
|
port.version.version = ("%s-%s"):format(result.lowver, result.highver)
|
||||||
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")
|
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Couldn't determine the target RPC service. Running a service not in nmap-rpc ?")
|
port.version.version = result.highver
|
||||||
end
|
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
|
end
|
||||||
|
|||||||
@@ -73,14 +73,14 @@ portrule = shortport.port_or_service(5060, "sip", {"tcp", "udp"})
|
|||||||
-- @return status true on success, false on failure.
|
-- @return status true on success, false on failure.
|
||||||
-- @return Response instance on success, error string on failure.
|
-- @return Response instance on success, error string on failure.
|
||||||
local registerext = function(sess, ext)
|
local registerext = function(sess, ext)
|
||||||
-- set session values
|
-- set session values
|
||||||
local request = sip.Request:new(sip.Method.REGISTER)
|
local request = sip.Request:new(sip.Method.REGISTER)
|
||||||
|
|
||||||
request:setUri("sip:" .. sess.sessdata:getServer())
|
request:setUri("sip:" .. sess.sessdata:getServer())
|
||||||
sess.sessdata:setUsername(ext)
|
sess.sessdata:setUsername(ext)
|
||||||
request:setSessionData(sess.sessdata)
|
request:setSessionData(sess.sessdata)
|
||||||
|
|
||||||
return sess:exch(request)
|
return sess:exch(request)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function that returns a number as string with a number of zeroes padded to
|
--- 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.
|
-- @arg padding number of digits to pad up to.
|
||||||
-- @return string of padded number.
|
-- @return string of padded number.
|
||||||
local padnum = function(num, padding)
|
local padnum = function(num, padding)
|
||||||
-- How many zeroes do we need to add
|
-- How many zeroes do we need to add
|
||||||
local n = #tostring(num)
|
local n = #tostring(num)
|
||||||
if n >= padding then
|
if n >= padding then
|
||||||
return tostring(num)
|
return tostring(num)
|
||||||
end
|
end
|
||||||
n = padding - n
|
n = padding - n
|
||||||
|
|
||||||
return string.rep(tostring(0), n) .. tostring(num)
|
return string.rep(tostring(0), n) .. tostring(num)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Iterator function that returns values from a lower value up to a greater
|
--- 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.
|
-- @arg padding number of digits to pad up to.
|
||||||
-- @return string current value.
|
-- @return string current value.
|
||||||
local numiterator = function(minval, maxval, padding)
|
local numiterator = function(minval, maxval, padding)
|
||||||
local i = minval - 1
|
local i = minval - 1
|
||||||
return function()
|
return function()
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if i <= maxval then return padnum(i, padding), '' end
|
if i <= maxval then return padnum(i, padding), '' end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Iterator function that returns lines from a file
|
--- Iterator function that returns lines from a file
|
||||||
@@ -118,19 +118,19 @@ end
|
|||||||
-- @return status false if error.
|
-- @return status false if error.
|
||||||
-- @return string current line.
|
-- @return string current line.
|
||||||
local useriterator = function(list)
|
local useriterator = function(list)
|
||||||
local f = nmap.fetchfile(list) or list
|
local f = nmap.fetchfile(list) or list
|
||||||
if not f then
|
if not f then
|
||||||
return false, ("\n ERROR: Couldn't find %s"):format(list)
|
return false, ("\n ERROR: Couldn't find %s"):format(list)
|
||||||
end
|
end
|
||||||
f = io.open(f)
|
f = io.open(f)
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return false, ("\n ERROR: Failed to open %s"):format(list)
|
return false, ("\n ERROR: Failed to open %s"):format(list)
|
||||||
end
|
end
|
||||||
return function()
|
return function()
|
||||||
for line in f:lines() do
|
for line in f:lines() do
|
||||||
return line
|
return line
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- function that tests for 404 status code when sending a REGISTER request
|
--- function that tests for 404 status code when sending a REGISTER request
|
||||||
@@ -138,128 +138,128 @@ end
|
|||||||
-- @arg host Target host table.
|
-- @arg host Target host table.
|
||||||
-- @arg port Target port table.
|
-- @arg port Target port table.
|
||||||
local test404 = function(host, port)
|
local test404 = function(host, port)
|
||||||
local session, status, randext, response
|
local session, status, randext, response
|
||||||
-- Random extension
|
-- Random extension
|
||||||
randext = math.random(1234567,987654321)
|
randext = math.random(1234567,987654321)
|
||||||
|
|
||||||
session = sip.Session:new(host, port)
|
session = sip.Session:new(host, port)
|
||||||
status = session:connect()
|
status = session:connect()
|
||||||
if not status then
|
if not status then
|
||||||
return false, "ERROR: Failed to connect to the SIP server."
|
return false, "ERROR: Failed to connect to the SIP server."
|
||||||
end
|
end
|
||||||
|
|
||||||
status, response = registerext(session, randext)
|
status, response = registerext(session, randext)
|
||||||
if not status then
|
if not status then
|
||||||
return false, "ERROR: No response from the SIP server."
|
return false, "ERROR: No response from the SIP server."
|
||||||
end
|
end
|
||||||
if response:getErrorCode() ~= 404 then
|
if response:getErrorCode() ~= 404 then
|
||||||
return false, "Server not returning 404 for random extension."
|
return false, "Server not returning 404 for random extension."
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Driver = {
|
Driver = {
|
||||||
|
|
||||||
new = function(self, host, port)
|
new = function(self, host, port)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.host = host
|
o.host = host
|
||||||
o.port = port
|
o.port = port
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
connect = function( self )
|
connect = function( self )
|
||||||
self.session = sip.Session:new(self.host, self.port)
|
self.session = sip.Session:new(self.host, self.port)
|
||||||
local status = self.session:connect()
|
local status = self.session:connect()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, brute.Error:new( "Couldn't connect to host" )
|
return false, brute.Error:new( "Couldn't connect to host" )
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
login = function( self, username, password)
|
login = function( self, username, password)
|
||||||
-- We are using the "password" values instead of the "username" so we
|
-- We are using the "password" values instead of the "username" so we
|
||||||
-- could benifit from brute.lua passonly option and setPasswordIterator
|
-- could benifit from brute.lua passonly option and setPasswordIterator
|
||||||
-- function, as we are doing usernames enumeration only and not
|
-- function, as we are doing usernames enumeration only and not
|
||||||
-- credentials brute forcing.
|
-- credentials brute forcing.
|
||||||
local status, response, responsecode
|
local status, response, responsecode
|
||||||
-- Send REGISTER request for each extension
|
-- Send REGISTER request for each extension
|
||||||
status, response = registerext(self.session, password)
|
status, response = registerext(self.session, password)
|
||||||
if status then
|
if status then
|
||||||
responsecode = response:getErrorCode()
|
responsecode = response:getErrorCode()
|
||||||
-- If response status code is 401 or 407, then extension exists but
|
-- If response status code is 401 or 407, then extension exists but
|
||||||
-- requires authentication
|
-- requires authentication
|
||||||
if responsecode == sip.Error.UNAUTHORIZED or
|
if responsecode == sip.Error.UNAUTHORIZED or
|
||||||
responsecode == sip.Error.PROXY_AUTH_REQUIRED then
|
responsecode == sip.Error.PROXY_AUTH_REQUIRED then
|
||||||
return true, brute.Account:new(password, " Auth required", '')
|
return true, brute.Account:new(password, " Auth required", '')
|
||||||
|
|
||||||
-- If response status code is 200, then extension exists
|
-- If response status code is 200, then extension exists
|
||||||
-- and requires no authentication
|
-- and requires no authentication
|
||||||
elseif responsecode == sip.Error.OK then
|
elseif responsecode == sip.Error.OK then
|
||||||
return true, brute.Account:new(password, " No auth", '')
|
return true, brute.Account:new(password, " No auth", '')
|
||||||
-- If response status code is 200, then extension exists
|
-- If response status code is 200, then extension exists
|
||||||
-- but access is forbidden.
|
-- but access is forbidden.
|
||||||
|
|
||||||
elseif responsecode == sip.Error.FORBIDDEN then
|
elseif responsecode == sip.Error.FORBIDDEN then
|
||||||
return true, brute.Account:new(password, " Forbidden", '')
|
return true, brute.Account:new(password, " Forbidden", '')
|
||||||
end
|
end
|
||||||
return false,brute.Error:new( "Not found" )
|
return false,brute.Error:new( "Not found" )
|
||||||
else
|
else
|
||||||
return false,brute.Error:new( "No response" )
|
return false,brute.Error:new( "No response" )
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
disconnect = function(self)
|
disconnect = function(self)
|
||||||
self.session:close()
|
self.session:close()
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local result, lthreads = {}, {}
|
local result, lthreads = {}, {}
|
||||||
local status, err
|
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 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 maxext = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".maxext")) or 999
|
||||||
local padding = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".padding")) or 0
|
local padding = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".padding")) or 0
|
||||||
local users = stdnse.get_script_args(SCRIPT_NAME .. ".users")
|
local users = stdnse.get_script_args(SCRIPT_NAME .. ".users")
|
||||||
local usersfile = stdnse.get_script_args(SCRIPT_NAME .. ".userslist")
|
local usersfile = stdnse.get_script_args(SCRIPT_NAME .. ".userslist")
|
||||||
or "nselib/data/usernames.lst"
|
or "nselib/data/usernames.lst"
|
||||||
|
|
||||||
-- min extension should be less than max extension.
|
-- min extension should be less than max extension.
|
||||||
if minext > maxext then
|
if minext > maxext then
|
||||||
return "ERROR: maxext should be greater or equal than minext."
|
return "ERROR: maxext should be greater or equal than minext."
|
||||||
end
|
end
|
||||||
-- If not set to zero, number of digits to pad up to should have less or
|
-- If not set to zero, number of digits to pad up to should have less or
|
||||||
-- equal the number of digits of max extension.
|
-- equal the number of digits of max extension.
|
||||||
if padding ~= 0 and #tostring(maxext) > padding then
|
if padding ~= 0 and #tostring(maxext) > padding then
|
||||||
return "ERROR: padding should be greater or equal to number of digits of maxext."
|
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
|
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
|
return result
|
||||||
-- 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
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -146,120 +146,120 @@ dependencies = {"smb-brute"}
|
|||||||
|
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local i, j
|
local i, j
|
||||||
local samr_status = false
|
local samr_status = false
|
||||||
local lsa_status = false
|
local lsa_status = false
|
||||||
local samr_result = "Didn't run"
|
local samr_result = "Didn't run"
|
||||||
local lsa_result = "Didn't run"
|
local lsa_result = "Didn't run"
|
||||||
local names = {}
|
local names = {}
|
||||||
local names_lookup = {}
|
local names_lookup = {}
|
||||||
local response = {}
|
local response = {}
|
||||||
local samronly = nmap.registry.args.samronly
|
local samronly = nmap.registry.args.samronly
|
||||||
local lsaonly = nmap.registry.args.lsaonly
|
local lsaonly = nmap.registry.args.lsaonly
|
||||||
local do_samr = samronly ~= nil or (samronly == nil and lsaonly == nil)
|
local do_samr = samronly ~= nil or (samronly == nil and lsaonly == nil)
|
||||||
local do_lsa = lsaonly ~= 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.
|
-- Try enumerating through SAMR. This is the better source of information, if we can get it.
|
||||||
if(do_samr) then
|
if(do_samr) then
|
||||||
samr_status, samr_result = msrpc.samr_enum_users(host)
|
samr_status, samr_result = msrpc.samr_enum_users(host)
|
||||||
|
|
||||||
if(samr_status) then
|
if(samr_status) then
|
||||||
-- Copy the returned array into the names[] table
|
-- Copy the returned array into the names[] table
|
||||||
stdnse.print_debug(2, "EnumUsers: Received %d names from SAMR", #samr_result)
|
stdnse.print_debug(2, "EnumUsers: Received %d names from SAMR", #samr_result)
|
||||||
for i = 1, #samr_result, 1 do
|
for i = 1, #samr_result, 1 do
|
||||||
-- Insert the full info into the names list
|
-- Insert the full info into the names list
|
||||||
table.insert(names, samr_result[i])
|
table.insert(names, samr_result[i])
|
||||||
-- Set the names_lookup value to 'true' to avoid duplicates
|
-- Set the names_lookup value to 'true' to avoid duplicates
|
||||||
names_lookup[samr_result[i]['name']] = true
|
names_lookup[samr_result[i]['name']] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Try enumerating through LSA.
|
-- Try enumerating through LSA.
|
||||||
if(do_lsa) then
|
if(do_lsa) then
|
||||||
lsa_status, lsa_result = msrpc.lsa_enum_users(host)
|
lsa_status, lsa_result = msrpc.lsa_enum_users(host)
|
||||||
if(lsa_status) then
|
if(lsa_status) then
|
||||||
-- Copy the returned array into the names[] table
|
-- Copy the returned array into the names[] table
|
||||||
stdnse.print_debug(2, "EnumUsers: Received %d names from LSA", #lsa_result)
|
stdnse.print_debug(2, "EnumUsers: Received %d names from LSA", #lsa_result)
|
||||||
for i = 1, #lsa_result, 1 do
|
for i = 1, #lsa_result, 1 do
|
||||||
if(lsa_result[i]['name'] ~= nil) then
|
if(lsa_result[i]['name'] ~= nil) then
|
||||||
-- Check if the name already exists
|
-- Check if the name already exists
|
||||||
if(not(names_lookup[lsa_result[i]['name']])) then
|
if(not(names_lookup[lsa_result[i]['name']])) then
|
||||||
table.insert(names, lsa_result[i])
|
table.insert(names, lsa_result[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if both failed
|
-- Check if both failed
|
||||||
if(samr_status == false and lsa_status == false) then
|
if(samr_status == false and lsa_status == false) then
|
||||||
if(string.find(lsa_result, 'ACCESS_DENIED')) 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")
|
return stdnse.format_output(false, "Access denied while trying to enumerate users; except against Windows 2000, Guest or better is typically required")
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(false, {"Couldn't enumerate users", "SAMR returned " .. samr_result, "LSA returned " .. lsa_result})
|
return stdnse.format_output(false, {"Couldn't enumerate users", "SAMR returned " .. samr_result, "LSA returned " .. lsa_result})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sort them
|
-- Sort them
|
||||||
table.sort(names, function (a, b) return string.lower(a.name) < string.lower(b.name) end)
|
table.sort(names, function (a, b) return string.lower(a.name) < string.lower(b.name) end)
|
||||||
|
|
||||||
-- Break them out by domain
|
-- Break them out by domain
|
||||||
local domains = {}
|
local domains = {}
|
||||||
for _, name in ipairs(names) do
|
for _, name in ipairs(names) do
|
||||||
local domain = name['domain']
|
local domain = name['domain']
|
||||||
|
|
||||||
-- Make sure the entry in the domains table exists
|
-- Make sure the entry in the domains table exists
|
||||||
if(not(domains[domain])) then
|
if(not(domains[domain])) then
|
||||||
domains[domain] = {}
|
domains[domain] = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(domains[domain], name)
|
table.insert(domains[domain], name)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we actually got any names back
|
-- Check if we actually got any names back
|
||||||
if(#names == 0) then
|
if(#names == 0) then
|
||||||
table.insert(response, "Couldn't find any account names, sorry!")
|
table.insert(response, "Couldn't find any account names, sorry!")
|
||||||
else
|
else
|
||||||
-- If we're not verbose, just print out the names. Otherwise, print out everything we can
|
-- If we're not verbose, just print out the names. Otherwise, print out everything we can
|
||||||
if(nmap.verbosity() < 1) then
|
if(nmap.verbosity() < 1) then
|
||||||
for domain, domain_users in pairs(domains) do
|
for domain, domain_users in pairs(domains) do
|
||||||
-- Make an impromptu list of users
|
-- Make an impromptu list of users
|
||||||
local names = {}
|
local names = {}
|
||||||
for _, info in ipairs(domain_users) do
|
for _, info in ipairs(domain_users) do
|
||||||
table.insert(names, info['name'])
|
table.insert(names, info['name'])
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add this domain to the response
|
-- Add this domain to the response
|
||||||
table.insert(response, string.format("Domain: %s; Users: %s", domain, stdnse.strjoin(", ", names)))
|
table.insert(response, string.format("Domain: %s; Users: %s", domain, stdnse.strjoin(", ", names)))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for domain, domain_users in pairs(domains) do
|
for domain, domain_users in pairs(domains) do
|
||||||
for _, info in ipairs(domain_users) do
|
for _, info in ipairs(domain_users) do
|
||||||
local response_part = {}
|
local response_part = {}
|
||||||
response_part['name'] = string.format("%s\\%s (RID: %d)", domain, info['name'], info['rid'])
|
response_part['name'] = string.format("%s\\%s (RID: %d)", domain, info['name'], info['rid'])
|
||||||
|
|
||||||
if(info['fullname']) then
|
if(info['fullname']) then
|
||||||
table.insert(response_part, string.format("Full name: %s", info['fullname']))
|
table.insert(response_part, string.format("Full name: %s", info['fullname']))
|
||||||
end
|
end
|
||||||
if(info['description']) then
|
if(info['description']) then
|
||||||
table.insert(response_part, string.format("Description: %s", info['description']))
|
table.insert(response_part, string.format("Description: %s", info['description']))
|
||||||
end
|
end
|
||||||
if(info['flags']) then
|
if(info['flags']) then
|
||||||
table.insert(response_part, string.format("Flags: %s", stdnse.strjoin(", ", info['flags'])))
|
table.insert(response_part, string.format("Flags: %s", stdnse.strjoin(", ", info['flags'])))
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(response, response_part)
|
table.insert(response, response_part)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ Queries information managed by the Windows Master Browser.
|
|||||||
-- |_ WIN2K3-EPI-1 5.2 EPiServer 2003 frontend server
|
-- |_ WIN2K3-EPI-1 5.2 EPiServer 2003 frontend server
|
||||||
--
|
--
|
||||||
-- @args smb-mbenum.format (optional) if set, changes the format of the result
|
-- @args smb-mbenum.format (optional) if set, changes the format of the result
|
||||||
-- returned by the script. There are three possible formats:
|
-- returned by the script. There are three possible formats:
|
||||||
-- 1. Ordered by type horizontally
|
-- 1. Ordered by type horizontally
|
||||||
-- 2. Ordered by type vertically
|
-- 2. Ordered by type vertically
|
||||||
-- 3. Ordered by type vertically with details (default)
|
-- 3. Ordered by type vertically with details (default)
|
||||||
--
|
--
|
||||||
-- @args smb-mbenum.filter (optional) if set, queries the browser for a
|
-- @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
|
-- @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
|
local function log(msg) stdnse.print_debug(3, msg) end
|
||||||
|
|
||||||
ServerTypes = {
|
ServerTypes = {
|
||||||
SV_TYPE_WORKSTATION = 0x00000001,
|
SV_TYPE_WORKSTATION = 0x00000001,
|
||||||
SV_TYPE_SERVER = 0x00000002,
|
SV_TYPE_SERVER = 0x00000002,
|
||||||
SV_TYPE_SQLSERVER = 0x00000004,
|
SV_TYPE_SQLSERVER = 0x00000004,
|
||||||
SV_TYPE_DOMAIN_CTRL = 0x00000008,
|
SV_TYPE_DOMAIN_CTRL = 0x00000008,
|
||||||
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010,
|
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010,
|
||||||
SV_TYPE_TIME_SOURCE = 0x00000020,
|
SV_TYPE_TIME_SOURCE = 0x00000020,
|
||||||
SV_TYPE_AFP = 0x00000040,
|
SV_TYPE_AFP = 0x00000040,
|
||||||
SV_TYPE_NOVELL = 0x00000080,
|
SV_TYPE_NOVELL = 0x00000080,
|
||||||
SV_TYPE_DOMAIN_MEMBER = 0x00000100,
|
SV_TYPE_DOMAIN_MEMBER = 0x00000100,
|
||||||
SV_TYPE_PRINTQ_SERVER = 0x00000200,
|
SV_TYPE_PRINTQ_SERVER = 0x00000200,
|
||||||
SV_TYPE_DIALIN_SERVER = 0x00000400,
|
SV_TYPE_DIALIN_SERVER = 0x00000400,
|
||||||
SV_TYPE_SERVER_UNIX = 0x00000800,
|
SV_TYPE_SERVER_UNIX = 0x00000800,
|
||||||
SV_TYPE_NT = 0x00001000,
|
SV_TYPE_NT = 0x00001000,
|
||||||
SV_TYPE_WFW = 0x00002000,
|
SV_TYPE_WFW = 0x00002000,
|
||||||
SV_TYPE_SERVER_MFPN = 0x00004000,
|
SV_TYPE_SERVER_MFPN = 0x00004000,
|
||||||
SV_TYPE_SERVER_NT = 0x00008000,
|
SV_TYPE_SERVER_NT = 0x00008000,
|
||||||
SV_TYPE_POTENTIAL_BROWSER = 0x00010000,
|
SV_TYPE_POTENTIAL_BROWSER = 0x00010000,
|
||||||
SV_TYPE_BACKUP_BROWSER = 0x00020000,
|
SV_TYPE_BACKUP_BROWSER = 0x00020000,
|
||||||
SV_TYPE_MASTER_BROWSER = 0x00040000,
|
SV_TYPE_MASTER_BROWSER = 0x00040000,
|
||||||
SV_TYPE_DOMAIN_MASTER = 0x00080000,
|
SV_TYPE_DOMAIN_MASTER = 0x00080000,
|
||||||
SV_TYPE_WINDOWS = 0x00400000,
|
SV_TYPE_WINDOWS = 0x00400000,
|
||||||
SV_TYPE_DFS = 0x00800000,
|
SV_TYPE_DFS = 0x00800000,
|
||||||
SV_TYPE_CLUSTER_NT = 0x01000000,
|
SV_TYPE_CLUSTER_NT = 0x01000000,
|
||||||
SV_TYPE_TERMINALSERVER = 0x02000000,
|
SV_TYPE_TERMINALSERVER = 0x02000000,
|
||||||
SV_TYPE_CLUSTER_VS_NT = 0x04000000,
|
SV_TYPE_CLUSTER_VS_NT = 0x04000000,
|
||||||
SV_TYPE_DCE = 0x10000000,
|
SV_TYPE_DCE = 0x10000000,
|
||||||
SV_TYPE_ALTERNATE_XPORT = 0x20000000,
|
SV_TYPE_ALTERNATE_XPORT = 0x20000000,
|
||||||
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000,
|
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000,
|
||||||
SV_TYPE_DOMAIN_ENUM = 0x80000000,
|
SV_TYPE_DOMAIN_ENUM = 0x80000000,
|
||||||
SV_TYPE_ALL = 0xFFFFFFFF
|
SV_TYPE_ALL = 0xFFFFFFFF
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeNames = {
|
TypeNames = {
|
||||||
SV_TYPE_WORKSTATION = { long = "Workstation", short = "WKS" },
|
SV_TYPE_WORKSTATION = { long = "Workstation", short = "WKS" },
|
||||||
SV_TYPE_SERVER = { long = "Server service", short = "SRVSVC" },
|
SV_TYPE_SERVER = { long = "Server service", short = "SRVSVC" },
|
||||||
SV_TYPE_SQLSERVER = { long = "SQL Server", short = "MSSQL" },
|
SV_TYPE_SQLSERVER = { long = "SQL Server", short = "MSSQL" },
|
||||||
SV_TYPE_DOMAIN_CTRL = { long = "Domain Controller", short = "DC" },
|
SV_TYPE_DOMAIN_CTRL = { long = "Domain Controller", short = "DC" },
|
||||||
SV_TYPE_DOMAIN_BAKCTRL = { long = "Backup Domain Controller", short = "BDC" },
|
SV_TYPE_DOMAIN_BAKCTRL = { long = "Backup Domain Controller", short = "BDC" },
|
||||||
SV_TYPE_TIME_SOURCE = { long = "Time Source", short = "TIME" },
|
SV_TYPE_TIME_SOURCE = { long = "Time Source", short = "TIME" },
|
||||||
SV_TYPE_AFP = { long = "Apple File Protocol Server", short = "AFP" },
|
SV_TYPE_AFP = { long = "Apple File Protocol Server", short = "AFP" },
|
||||||
SV_TYPE_NOVELL = { long = "Novell Server", short = "NOVELL" },
|
SV_TYPE_NOVELL = { long = "Novell Server", short = "NOVELL" },
|
||||||
SV_TYPE_DOMAIN_MEMBER = { long = "LAN Manager Domain Member", short = "MEMB" },
|
SV_TYPE_DOMAIN_MEMBER = { long = "LAN Manager Domain Member", short = "MEMB" },
|
||||||
SV_TYPE_PRINTQ_SERVER = { long = "Print server", short = "PRINT" },
|
SV_TYPE_PRINTQ_SERVER = { long = "Print server", short = "PRINT" },
|
||||||
SV_TYPE_DIALIN_SERVER = { long = "Dial-in server", short = "DIALIN" },
|
SV_TYPE_DIALIN_SERVER = { long = "Dial-in server", short = "DIALIN" },
|
||||||
SV_TYPE_SERVER_UNIX = { long = "Unix server", short = "UNIX" },
|
SV_TYPE_SERVER_UNIX = { long = "Unix server", short = "UNIX" },
|
||||||
SV_TYPE_NT = { long = "Windows NT/2000/XP/2003 server", short = "NT" },
|
SV_TYPE_NT = { long = "Windows NT/2000/XP/2003 server", short = "NT" },
|
||||||
SV_TYPE_WFW = { long = "Windows for workgroups", short = "WFW" },
|
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_MFPN = { long = "Microsoft File and Print for Netware", short="MFPN" },
|
||||||
SV_TYPE_SERVER_NT = { long = "Server", short = "SRV" },
|
SV_TYPE_SERVER_NT = { long = "Server", short = "SRV" },
|
||||||
SV_TYPE_POTENTIAL_BROWSER = { long = "Potential Browser", short = "POTBRWS" },
|
SV_TYPE_POTENTIAL_BROWSER = { long = "Potential Browser", short = "POTBRWS" },
|
||||||
SV_TYPE_BACKUP_BROWSER = { long = "Backup Browser", short = "BCKBRWS"},
|
SV_TYPE_BACKUP_BROWSER = { long = "Backup Browser", short = "BCKBRWS"},
|
||||||
SV_TYPE_MASTER_BROWSER = { long = "Master Browser", short = "MBRWS"},
|
SV_TYPE_MASTER_BROWSER = { long = "Master Browser", short = "MBRWS"},
|
||||||
SV_TYPE_DOMAIN_MASTER = { long = "Domain Master Browser", short = "DOMBRWS"},
|
SV_TYPE_DOMAIN_MASTER = { long = "Domain Master Browser", short = "DOMBRWS"},
|
||||||
SV_TYPE_WINDOWS = { long = "Windows 95/98/ME", short="WIN95"},
|
SV_TYPE_WINDOWS = { long = "Windows 95/98/ME", short="WIN95"},
|
||||||
SV_TYPE_DFS = { long = "DFS Root", short = "DFS"},
|
SV_TYPE_DFS = { long = "DFS Root", short = "DFS"},
|
||||||
SV_TYPE_TERMINALSERVER = { long = "Terminal Server", short = "TS" },
|
SV_TYPE_TERMINALSERVER = { long = "Terminal Server", short = "TS" },
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputFormat = {
|
OutputFormat = {
|
||||||
BY_TYPE_H = 1,
|
BY_TYPE_H = 1,
|
||||||
BY_TYPE_V = 2,
|
BY_TYPE_V = 2,
|
||||||
BY_TYPE_V_DETAILED = 3,
|
BY_TYPE_V_DETAILED = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local status, smbstate = smb.start(host)
|
local status, smbstate = smb.start(host)
|
||||||
local err, entries
|
local err, entries
|
||||||
local path = ("\\\\%s\\IPC$"):format(host.ip)
|
local path = ("\\\\%s\\IPC$"):format(host.ip)
|
||||||
local detail_level = 1
|
local detail_level = 1
|
||||||
local format = stdnse.get_script_args("smb-mbenum.format") or OutputFormat.BY_TYPE_V_DETAILED
|
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 filter = stdnse.get_script_args("smb-mbenum.filter") or ServerTypes.SV_TYPE_ALL
|
||||||
local domain = stdnse.get_script_args("smb-mbenum.domain")
|
local domain = stdnse.get_script_args("smb-mbenum.domain")
|
||||||
|
|
||||||
filter = tonumber(filter) or ServerTypes[filter]
|
filter = tonumber(filter) or ServerTypes[filter]
|
||||||
format = tonumber(format)
|
format = tonumber(format)
|
||||||
|
|
||||||
if ( not(filter) ) then
|
if ( not(filter) ) then
|
||||||
return "\n The argument smb-mbenum.filter contained an invalid value."
|
return "\n The argument smb-mbenum.filter contained an invalid value."
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(format) ) then
|
if ( not(format) ) then
|
||||||
return "\n The argument smb-mbenum.format contained an invalid value."
|
return "\n The argument smb-mbenum.format contained an invalid value."
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = smb.negotiate_protocol(smbstate, {})
|
status, err = smb.negotiate_protocol(smbstate, {})
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
log("ERROR: smb.negotiate_protocol failed")
|
log("ERROR: smb.negotiate_protocol failed")
|
||||||
return "\n ERROR: Failed to connect to browser service"
|
return "\n ERROR: Failed to connect to browser service"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = smb.start_session(smbstate, {})
|
status, err = smb.start_session(smbstate, {})
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
log("ERROR: smb.negotiate_protocol failed")
|
log("ERROR: smb.negotiate_protocol failed")
|
||||||
return "\n ERROR: Failed to connect to browser service"
|
return "\n ERROR: Failed to connect to browser service"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = smb.tree_connect(smbstate, path, {})
|
status, err = smb.tree_connect(smbstate, path, {})
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
log("ERROR: smb.negotiate_protocol failed")
|
log("ERROR: smb.negotiate_protocol failed")
|
||||||
return "\n ERROR: Failed to connect to browser service"
|
return "\n ERROR: Failed to connect to browser service"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, entries = msrpc.rap_netserverenum2(smbstate, domain, filter, detail_level)
|
status, entries = msrpc.rap_netserverenum2(smbstate, domain, filter, detail_level)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
log("ERROR: msrpc.call_lanmanapi failed")
|
log("ERROR: msrpc.call_lanmanapi failed")
|
||||||
return "\n ERROR: " .. entries
|
return "\n ERROR: " .. entries
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = smb.tree_disconnect(smbstate)
|
status, err = smb.tree_disconnect(smbstate)
|
||||||
if ( not(status) ) then log("ERROR: smb.tree_disconnect failed") end
|
if ( not(status) ) then log("ERROR: smb.tree_disconnect failed") end
|
||||||
|
|
||||||
status, err = smb.logoff(smbstate)
|
status, err = smb.logoff(smbstate)
|
||||||
if ( not(status) ) then log("ERROR: smb.logoff failed") end
|
if ( not(status) ) then log("ERROR: smb.logoff failed") end
|
||||||
|
|
||||||
status, err = smb.stop(smbstate)
|
status, err = smb.stop(smbstate)
|
||||||
if ( not(status) ) then log("ERROR: smb.stop failed") end
|
if ( not(status) ) then log("ERROR: smb.stop failed") end
|
||||||
|
|
||||||
local results, output = {}, {}
|
local results, output = {}, {}
|
||||||
for k, _ in pairs(ServerTypes) do
|
for k, _ in pairs(ServerTypes) do
|
||||||
for _, server in ipairs(entries) do
|
for _, server in ipairs(entries) do
|
||||||
if ( TypeNames[k] and bit.band(server.type,ServerTypes[k]) == ServerTypes[k] ) then
|
if ( TypeNames[k] and bit.band(server.type,ServerTypes[k]) == ServerTypes[k] ) then
|
||||||
results[TypeNames[k].long] = results[TypeNames[k].long] or {}
|
results[TypeNames[k].long] = results[TypeNames[k].long] or {}
|
||||||
if ( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
if ( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
||||||
table.insert(results[TypeNames[k].long], server)
|
table.insert(results[TypeNames[k].long], server)
|
||||||
else
|
else
|
||||||
table.insert(results[TypeNames[k].long], server.name)
|
table.insert(results[TypeNames[k].long], server.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( format == OutputFormat.BY_TYPE_H ) then
|
if ( format == OutputFormat.BY_TYPE_H ) then
|
||||||
for k, v in pairs(results) do
|
for k, v in pairs(results) do
|
||||||
local row = ("%s: %s"):format( k, stdnse.strjoin(",", v) )
|
local row = ("%s: %s"):format( k, stdnse.strjoin(",", v) )
|
||||||
table.insert(output, row)
|
table.insert(output, row)
|
||||||
end
|
end
|
||||||
table.sort(output)
|
table.sort(output)
|
||||||
elseif( format == OutputFormat.BY_TYPE_V ) then
|
elseif( format == OutputFormat.BY_TYPE_V ) then
|
||||||
for k, v in pairs(results) do
|
for k, v in pairs(results) do
|
||||||
v.name = k
|
v.name = k
|
||||||
table.insert(output, v)
|
table.insert(output, v)
|
||||||
end
|
end
|
||||||
table.sort(output, function(a,b) return a.name < b.name end)
|
table.sort(output, function(a,b) return a.name < b.name end)
|
||||||
elseif( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
elseif( format == OutputFormat.BY_TYPE_V_DETAILED ) then
|
||||||
for k, v in pairs(results) do
|
for k, v in pairs(results) do
|
||||||
local cat_tab = tab.new(3)
|
local cat_tab = tab.new(3)
|
||||||
table.sort(v, function(a,b) return a.name < b.name end )
|
table.sort(v, function(a,b) return a.name < b.name end )
|
||||||
for _, server in pairs(v) do
|
for _, server in pairs(v) do
|
||||||
tab.addrow(
|
tab.addrow(
|
||||||
cat_tab,
|
cat_tab,
|
||||||
server.name,
|
server.name,
|
||||||
("%d.%d"):format(server.version.major,server.version.minor),
|
("%d.%d"):format(server.version.major,server.version.minor),
|
||||||
server.comment
|
server.comment
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
table.insert(output, { name = k, tab.dump(cat_tab) } )
|
table.insert(output, { name = k, tab.dump(cat_tab) } )
|
||||||
end
|
end
|
||||||
table.sort(output, function(a,b) return a.name < b.name end)
|
table.sort(output, function(a,b) return a.name < b.name end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ dependencies = {"smb-brute"}
|
|||||||
-- TODO: This script needs some love
|
-- TODO: This script needs some love
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
---Retrieves the requested value from the registry.
|
---Retrieves the requested value from the registry.
|
||||||
@@ -72,178 +72,178 @@ end
|
|||||||
--@return Status (true or false).
|
--@return Status (true or false).
|
||||||
--@return The value (if status is true) or an error string (if status is 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)
|
local function reg_get_value(smbstate, handle, key, value)
|
||||||
-- Open the key
|
-- Open the key
|
||||||
local status, openkey_result = msrpc.winreg_openkey(smbstate, handle, key)
|
local status, openkey_result = msrpc.winreg_openkey(smbstate, handle, key)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, openkey_result
|
return false, openkey_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Query the value
|
-- Query the value
|
||||||
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openkey_result['handle'], value)
|
local status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openkey_result['handle'], value)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, queryvalue_result
|
return false, queryvalue_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Close the key
|
-- Close the key
|
||||||
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'], value)
|
local status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'], value)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, closekey_result
|
return false, closekey_result
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, queryvalue_result['value']
|
return true, queryvalue_result['value']
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_info_registry(host)
|
local function get_info_registry(host)
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
-- Create the SMB session
|
-- Create the SMB session
|
||||||
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
local status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Bind to WINREG service
|
-- Bind to WINREG service
|
||||||
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
local status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Open HKEY_LOCAL_MACHINE
|
-- Open HKEY_LOCAL_MACHINE
|
||||||
local status, openhklm_result = msrpc.winreg_openhklm(smbstate)
|
local status, openhklm_result = msrpc.winreg_openhklm(smbstate)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
return false, openhklm_result
|
return false, openhklm_result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Processor information
|
-- 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")
|
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
|
if(result['status-number_of_processors'] == false) then
|
||||||
result['number_of_processors'] = 0
|
result['number_of_processors'] = 0
|
||||||
end
|
end
|
||||||
result['status-os'], result['os'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "OS")
|
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-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_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_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_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")
|
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
|
-- remove trailing zero terminator
|
||||||
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
||||||
|
|
||||||
for i = 0, tonumber(num_procs) - 1, 1 do
|
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-~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-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-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")
|
result['status-vendoridentifier'..i], result['vendoridentifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "VendorIdentifier")
|
||||||
end
|
end
|
||||||
-- status, result['physicalmemory'] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\ResourceMap\\System Resources\\Physical Memory", ".Translated")
|
-- 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
|
-- Paging file
|
||||||
result['status-pagingfiles'], result['pagingfiles'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "PagingFiles")
|
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")
|
result['status-clearpagefileatshutdown'], result['clearpagefileatshutdown'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "ClearPageFileAtShutdown")
|
||||||
|
|
||||||
-- OS Information
|
-- OS Information
|
||||||
result['status-csdversion'], result['csdversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion")
|
result['status-csdversion'], result['csdversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion")
|
||||||
if(result['status-csdversion'] == false) then
|
if(result['status-csdversion'] == false) then
|
||||||
result['csdversion'] = "(no service packs)"
|
result['csdversion'] = "(no service packs)"
|
||||||
end
|
end
|
||||||
result['status-currentbuildnumber'], result['currentbuildnumber'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentBuildNumber")
|
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-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-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")
|
result['status-installdate'], result['installdate'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "InstallDate")
|
||||||
if(result['status-installdate'] ~= false) then
|
if(result['status-installdate'] ~= false) then
|
||||||
result['installdate'] = os.date("%Y-%m-%d %H:%M:%S", result['installdate'])
|
result['installdate'] = os.date("%Y-%m-%d %H:%M:%S", result['installdate'])
|
||||||
end
|
end
|
||||||
|
|
||||||
result['status-productname'], result['productname'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "Productname")
|
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-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-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-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-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-productsuite'], result['productsuite'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductSuite")
|
||||||
|
|
||||||
-- Driver information
|
-- 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")
|
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
|
-- 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-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")
|
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
|
if(result['status-ff_version'] == false) then
|
||||||
result['ff_version'] = "<not installed>"
|
result['ff_version'] = "<not installed>"
|
||||||
end
|
end
|
||||||
|
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
|
|
||||||
return true, result
|
return true, result
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local status, result = get_info_registry(host)
|
local status, result = get_info_registry(host)
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return stdnse.format_output(false, result)
|
return stdnse.format_output(false, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
if(result['status-os'] == true) then
|
if(result['status-os'] == true) then
|
||||||
local osdetails = {}
|
local osdetails = {}
|
||||||
osdetails['name'] = "OS Details"
|
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("%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("Installed on %s", result['installdate']))
|
||||||
table.insert(osdetails, string.format("Registered to %s (organization: %s)", result['registeredowner'], result['registeredorganization']))
|
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("Path: %s", result['path']))
|
||||||
table.insert(osdetails, string.format("Systemroot: %s", result['systemroot']))
|
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(osdetails, string.format("Page files: %s (cleared at shutdown => %s)", result['pagingfiles'], result['clearpagefileatshutdown']))
|
||||||
table.insert(response, osdetails)
|
table.insert(response, osdetails)
|
||||||
|
|
||||||
local hardware = {}
|
local hardware = {}
|
||||||
hardware['name'] = "Hardware"
|
hardware['name'] = "Hardware"
|
||||||
-- remove trailing zero terminator
|
-- remove trailing zero terminator
|
||||||
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
local num_procs = result['number_of_processors']:match("^[^%z]*")
|
||||||
for i = 0, tonumber(num_procs) - 1, 1 do
|
for i = 0, tonumber(num_procs) - 1, 1 do
|
||||||
if(result['status-processornamestring'..i] == false) then
|
if(result['status-processornamestring'..i] == false) then
|
||||||
result['status-processornamestring'..i] = "Unknown"
|
result['status-processornamestring'..i] = "Unknown"
|
||||||
end
|
end
|
||||||
|
|
||||||
local processor = {}
|
local processor = {}
|
||||||
processor['name'] = string.format("CPU %d: %s [%dmhz %s]", i, string.gsub(result['processornamestring'..i], ' ', ''), result['~mhz'..i], result['vendoridentifier'..i])
|
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(processor, string.format("Identifier %d: %s", i, result['identifier'..i]))
|
||||||
table.insert(hardware, processor)
|
table.insert(hardware, processor)
|
||||||
end
|
end
|
||||||
table.insert(hardware, string.format("Video driver: %s", result['video_driverdesc']))
|
table.insert(hardware, string.format("Video driver: %s", result['video_driverdesc']))
|
||||||
table.insert(response, hardware)
|
table.insert(response, hardware)
|
||||||
|
|
||||||
local browsers = {}
|
local browsers = {}
|
||||||
browsers['name'] = "Browsers"
|
browsers['name'] = "Browsers"
|
||||||
table.insert(browsers, string.format("Internet Explorer %s", result['ie_version']))
|
table.insert(browsers, string.format("Internet Explorer %s", result['ie_version']))
|
||||||
if(result['status-ff_version']) then
|
if(result['status-ff_version']) then
|
||||||
table.insert(browsers, string.format("Firefox %s", result['ff_version']))
|
table.insert(browsers, string.format("Firefox %s", result['ff_version']))
|
||||||
end
|
end
|
||||||
table.insert(response, browsers)
|
table.insert(response, browsers)
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
elseif(result['status-productname'] == true) then
|
elseif(result['status-productname'] == true) then
|
||||||
|
|
||||||
local osdetails = {}
|
local osdetails = {}
|
||||||
osdetails['name'] = 'OS Details'
|
osdetails['name'] = 'OS Details'
|
||||||
osdetails['warning'] = "Access was denied for certain values; try an administrative account for more complete information"
|
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("%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("Installed on %s", result['installdate']))
|
||||||
table.insert(osdetails, string.format("Registered to %s (organization: %s)", result['registeredowner'], result['registeredorganization']))
|
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(osdetails, string.format("Systemroot: %s", result['systemroot']))
|
||||||
table.insert(response, osdetails)
|
table.insert(response, osdetails)
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,163 +49,163 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
|||||||
local try
|
local try
|
||||||
|
|
||||||
local function sendrequest(socket, oid, setparam)
|
local function sendrequest(socket, oid, setparam)
|
||||||
local payload
|
local payload
|
||||||
local options = {}
|
local options = {}
|
||||||
options.reqId = 28428 -- unnecessary?
|
options.reqId = 28428 -- unnecessary?
|
||||||
payload = snmp.encode(snmp.buildPacket(snmp.buildSetRequest(options, oid,setparam)))
|
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
|
-- read in any response we might get
|
||||||
local status, response = socket:receive()
|
local status, response = socket:receive()
|
||||||
if ( not(status) ) then return status, response end
|
if ( not(status) ) then return status, response end
|
||||||
|
|
||||||
local result = snmp.fetchFirst(response)
|
local result = snmp.fetchFirst(response)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
-- Sends SNMP packets to host and reads responses
|
-- Sends SNMP packets to host and reads responses
|
||||||
action = function(host, port)
|
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
|
if ( tftproot and not( tftproot:match("[\\/]+$") ) ) then
|
||||||
return "ERROR: tftproot needs to end with slash"
|
return "ERROR: tftproot needs to end with slash"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create the socket used for our connection
|
-- create the socket used for our connection
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
|
|
||||||
-- set a reasonable timeout value
|
-- set a reasonable timeout value
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
|
|
||||||
-- do some exception handling / cleanup
|
-- do some exception handling / cleanup
|
||||||
local catch = function() socket:close() end
|
local catch = function() socket:close() end
|
||||||
try = nmap.new_try(catch)
|
try = nmap.new_try(catch)
|
||||||
|
|
||||||
-- connect to the potential SNMP system
|
-- connect to the potential SNMP system
|
||||||
try(socket:connect(host.ip, port.number, "udp"))
|
try(socket:connect(host.ip, port.number, "udp"))
|
||||||
|
|
||||||
local status, tftpserver, _, _, _ = socket:get_info()
|
local status, tftpserver, _, _, _ = socket:get_info()
|
||||||
if( not(status) ) then
|
if( not(status) ) then
|
||||||
return "ERROR: Failed to determin local ip"
|
return "ERROR: Failed to determin local ip"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- build a SNMP v1 packet
|
-- 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] )
|
-- 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
|
-- Fail silently if the first request doesn't get a proper response
|
||||||
if ( not(request) ) then return end
|
if ( not(request) ) then return end
|
||||||
|
|
||||||
-- since we got something back, the port is definitely open
|
-- since we got something back, the port is definitely open
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- build a SNMP v1 packet
|
-- 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] )
|
-- 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
|
-- 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] )
|
-- 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
|
-- 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 )
|
-- 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 = {}
|
local tbl = {}
|
||||||
tbl._snmp = '40'
|
tbl._snmp = '40'
|
||||||
for octet in tftpserver:gmatch("%d+") do
|
for octet in tftpserver:gmatch("%d+") do
|
||||||
table.insert(tbl, octet)
|
table.insert(tbl, octet)
|
||||||
end
|
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(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(".1.3.6.1.4.1.9.9.96.1.1.1.1.5.9999",tftpserver)
|
||||||
|
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- build a SNMP v1 packet
|
-- 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 )
|
-- 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
|
-- 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
|
-- 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 )
|
-- 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
|
-- 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)
|
-- 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
|
-- 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])
|
-- 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
|
-- 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
|
-- wait for sometime and print the status of filetransfer
|
||||||
tftp.start()
|
tftp.start()
|
||||||
local status, infile = tftp.waitFile(host.ip .. "-config", 10)
|
local status, infile = tftp.waitFile(host.ip .. "-config", 10)
|
||||||
|
|
||||||
-- build a SNMP v1 packet
|
-- 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
|
-- 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 = {}
|
local options = {}
|
||||||
options.reqId = 28428
|
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 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 status
|
||||||
local response
|
local response
|
||||||
-- read in any response we might get
|
-- read in any response we might get
|
||||||
status, response = socket:receive()
|
status, response = socket:receive()
|
||||||
|
|
||||||
if (not status) or (response == "TIMEOUT") then
|
if (not status) or (response == "TIMEOUT") then
|
||||||
return "\n ERROR: Failed to receive cisco configuration file"
|
return "\n ERROR: Failed to receive cisco configuration file"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result
|
local result
|
||||||
result = snmp.fetchFirst(response)
|
result = snmp.fetchFirst(response)
|
||||||
|
|
||||||
if result == 3 then
|
if result == 3 then
|
||||||
result = ( infile and infile:getContent() )
|
result = ( infile and infile:getContent() )
|
||||||
|
|
||||||
if ( tftproot ) then
|
if ( tftproot ) then
|
||||||
local fname = tftproot .. stdnse.filename_escape(host.ip .. "-config")
|
local fname = tftproot .. stdnse.filename_escape(host.ip .. "-config")
|
||||||
local file, err = io.open(fname, "w")
|
local file, err = io.open(fname, "w")
|
||||||
if ( file ) then
|
if ( file ) then
|
||||||
file:write(result)
|
file:write(result)
|
||||||
file:close()
|
file:close()
|
||||||
else
|
else
|
||||||
return "\n ERROR: " .. file
|
return "\n ERROR: " .. file
|
||||||
end
|
end
|
||||||
result = ("\n Configuration saved to (%s)"):format(fname)
|
result = ("\n Configuration saved to (%s)"):format(fname)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
result = "Not successful! error code: " .. result .. " (1:waiting, 2:running, 3:successful, 4:failed)"
|
result = "Not successful! error code: " .. result .. " (1:waiting, 2:running, 3:successful, 4:failed)"
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- build a SNMP v1 packet
|
-- 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])
|
-- 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
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ argument.
|
|||||||
--</table>
|
--</table>
|
||||||
--@usage
|
--@usage
|
||||||
-- nmap --script=socks-open-proxy \
|
-- nmap --script=socks-open-proxy \
|
||||||
-- --script-args proxy.url=<host>,proxy.pattern=<pattern>
|
-- --script-args proxy.url=<host>,proxy.pattern=<pattern>
|
||||||
|
|
||||||
author = "Joao Correa"
|
author = "Joao Correa"
|
||||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
@@ -159,7 +159,7 @@ local function default_test(host, port)
|
|||||||
end
|
end
|
||||||
|
|
||||||
portrule = shortport.port_or_service({1080, 9050},
|
portrule = shortport.port_or_service({1080, 9050},
|
||||||
{"socks", "socks4", "socks5", "tor-socks"})
|
{"socks", "socks4", "socks5", "tor-socks"})
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local supported_versions
|
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
|
-- algorithm name-lists are identical between the server-to-client and
|
||||||
-- client-to-server types. Note that this simply modifies the passed tables.
|
-- client-to-server types. Note that this simply modifies the passed tables.
|
||||||
local combine_types = function(parsed, lists)
|
local combine_types = function(parsed, lists)
|
||||||
local doubles = {
|
local doubles = {
|
||||||
"encryption_algorithms",
|
"encryption_algorithms",
|
||||||
"mac_algorithms",
|
"mac_algorithms",
|
||||||
"compression_algorithms"
|
"compression_algorithms"
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, i in ipairs(doubles) do
|
for _, i in ipairs(doubles) do
|
||||||
local c2s = i .. "_client_to_server"
|
local c2s = i .. "_client_to_server"
|
||||||
local s2c = i .. "_server_to_client"
|
local s2c = i .. "_server_to_client"
|
||||||
|
|
||||||
if parsed[c2s] == parsed[s2c] then
|
if parsed[c2s] == parsed[s2c] then
|
||||||
parsed[i] = parsed[c2s]
|
parsed[i] = parsed[c2s]
|
||||||
parsed[c2s] = nil
|
parsed[c2s] = nil
|
||||||
parsed[s2c] = nil
|
parsed[s2c] = nil
|
||||||
table.insert(lists, i)
|
table.insert(lists, i)
|
||||||
else
|
else
|
||||||
table.insert(lists, c2s)
|
table.insert(lists, c2s)
|
||||||
table.insert(lists, s2c)
|
table.insert(lists, s2c)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build and return the output table
|
-- Build and return the output table
|
||||||
local output = function(parsed, lists)
|
local output = function(parsed, lists)
|
||||||
local out = stdnse.output_table()
|
local out = stdnse.output_table()
|
||||||
|
|
||||||
for _, l in ipairs(lists) do
|
for _, l in ipairs(lists) do
|
||||||
local v = parsed[l]
|
local v = parsed[l]
|
||||||
local a = v:len() > 0 and stdnse.strsplit(",", v) or {}
|
local a = v:len() > 0 and stdnse.strsplit(",", v) or {}
|
||||||
if nmap.verbosity() > 0 then
|
if nmap.verbosity() > 0 then
|
||||||
setmetatable(a, {
|
setmetatable(a, {
|
||||||
__tostring = function(t)
|
__tostring = function(t)
|
||||||
return string.format("(%d)\n %s", #t, table.concat(t, "\n "))
|
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)
|
return string.format("(%d)", #t)
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
out[l] = a
|
out[l] = a
|
||||||
end
|
end
|
||||||
|
|
||||||
return out
|
return out
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local sock = nmap.new_socket()
|
local sock = nmap.new_socket()
|
||||||
local status = sock:connect(host, port)
|
local status = sock:connect(host, port)
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
status = sock:receive_lines(1)
|
status = sock:receive_lines(1)
|
||||||
if not status then
|
if not status then
|
||||||
sock:close()
|
sock:close()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
status = sock:send("SSH-2.0-Nmap-SSH2-Enum-Algos\r\n")
|
status = sock:send("SSH-2.0-Nmap-SSH2-Enum-Algos\r\n")
|
||||||
if not status then
|
if not status then
|
||||||
sock:close()
|
sock:close()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local ssh = ssh2.transport
|
local ssh = ssh2.transport
|
||||||
|
|
||||||
-- I would think that the server would send its kex data right after
|
-- I would think that the server would send its kex data right after
|
||||||
-- receiving and verifying our protocol id string above, then we could
|
-- receiving and verifying our protocol id string above, then we could
|
||||||
-- just use it here, but I've seen no definitive documentation saying
|
-- 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
|
-- 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
|
-- 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
|
-- 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
|
-- send it here until I know for sure (removing this send works against
|
||||||
-- OpenSSH though).
|
-- OpenSSH though).
|
||||||
local pkt = ssh.build(ssh.kex_init())
|
local pkt = ssh.build(ssh.kex_init())
|
||||||
|
|
||||||
status = sock:send(pkt)
|
status = sock:send(pkt)
|
||||||
if not status then
|
if not status then
|
||||||
sock:close()
|
sock:close()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, response = ssh.receive_packet(sock)
|
local status, response = ssh.receive_packet(sock)
|
||||||
|
|
||||||
sock:close()
|
sock:close()
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local parsed = ssh.parse_kex_init(ssh.payload(response))
|
local parsed = ssh.parse_kex_init(ssh.payload(response))
|
||||||
|
|
||||||
local lists = {
|
local lists = {
|
||||||
"kex_algorithms",
|
"kex_algorithms",
|
||||||
"server_host_key_algorithms"
|
"server_host_key_algorithms"
|
||||||
-- Other types will be added below in combine_types()
|
-- Other types will be added below in combine_types()
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Modifies tables
|
-- Modifies tables
|
||||||
combine_types(parsed, lists)
|
combine_types(parsed, lists)
|
||||||
|
|
||||||
return output(parsed, lists)
|
return output(parsed, lists)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -46,193 +46,193 @@ portrule = shortport.ssl
|
|||||||
|
|
||||||
local hex2dec = function(hex)
|
local hex2dec = function(hex)
|
||||||
|
|
||||||
local byte1, byte2;
|
local byte1, byte2;
|
||||||
|
|
||||||
byte1 = string.byte(hex, 1);
|
byte1 = string.byte(hex, 1);
|
||||||
byte2 = string.byte(hex, 2);
|
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
|
end
|
||||||
|
|
||||||
local ciphers = function(cipher_list, len)
|
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 seen = {}
|
||||||
local available_ciphers = {}
|
local available_ciphers = {}
|
||||||
local idx = 0;
|
local idx = 0;
|
||||||
|
|
||||||
local ssl_ciphers = {
|
local ssl_ciphers = {
|
||||||
-- (cut down) table of codes with their corresponding ciphers.
|
-- (cut down) table of codes with their corresponding ciphers.
|
||||||
-- inspired by Wireshark's 'epan/dissectors/packet-ssl-utils.h'
|
-- inspired by Wireshark's 'epan/dissectors/packet-ssl-utils.h'
|
||||||
[0x010080] = "SSL2_RC4_128_WITH_MD5",
|
[0x010080] = "SSL2_RC4_128_WITH_MD5",
|
||||||
[0x020080] = "SSL2_RC4_128_EXPORT40_WITH_MD5",
|
[0x020080] = "SSL2_RC4_128_EXPORT40_WITH_MD5",
|
||||||
[0x030080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
[0x030080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
||||||
[0x040080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
[0x040080] = "SSL2_RC2_CBC_128_CBC_WITH_MD5",
|
||||||
[0x050080] = "SSL2_IDEA_128_CBC_WITH_MD5",
|
[0x050080] = "SSL2_IDEA_128_CBC_WITH_MD5",
|
||||||
[0x060040] = "SSL2_DES_64_CBC_WITH_MD5",
|
[0x060040] = "SSL2_DES_64_CBC_WITH_MD5",
|
||||||
[0x0700c0] = "SSL2_DES_192_EDE3_CBC_WITH_MD5",
|
[0x0700c0] = "SSL2_DES_192_EDE3_CBC_WITH_MD5",
|
||||||
[0x080080] = "SSL2_RC4_64_WITH_MD5",
|
[0x080080] = "SSL2_RC4_64_WITH_MD5",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (len == 0) then return "none"; end
|
if (len == 0) then return "none"; end
|
||||||
-- something's got broken along the way if these aren't equal
|
-- something's got broken along the way if these aren't equal
|
||||||
if (len ~= #cipher_list) then
|
if (len ~= #cipher_list) then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
for idx = 1, len, 3 do
|
for idx = 1, len, 3 do
|
||||||
local _, cipher = bin.unpack(">I", "\x00" .. string.sub(cipher_list, idx, idx + 2))
|
local _, cipher = bin.unpack(">I", "\x00" .. string.sub(cipher_list, idx, idx + 2))
|
||||||
local cipher_name = ssl_ciphers[cipher];
|
local cipher_name = ssl_ciphers[cipher];
|
||||||
|
|
||||||
if (cipher_name == nil) then
|
if (cipher_name == nil) then
|
||||||
cipher_name = string.format("0x%06x", cipher)
|
cipher_name = string.format("0x%06x", cipher)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check for duplicate ciphers
|
-- Check for duplicate ciphers
|
||||||
if not seen[cipher] then
|
if not seen[cipher] then
|
||||||
table.insert(available_ciphers, cipher_name)
|
table.insert(available_ciphers, cipher_name)
|
||||||
seen[cipher] = true
|
seen[cipher] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return available_ciphers
|
return available_ciphers
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local give_n_bytes = function(idx, n, str)
|
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
|
if (idx + (n - 1) > #str) then
|
||||||
return (idx + n), string.rep(string.char(0x00), n);
|
return (idx + n), string.rep(string.char(0x00), n);
|
||||||
end
|
end
|
||||||
|
|
||||||
return (idx + n), string.sub(str, idx, (idx + (n - 1)) );
|
return (idx + n), string.sub(str, idx, (idx + (n - 1)) );
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local socket = nmap.new_socket();
|
local socket = nmap.new_socket();
|
||||||
local status = true;
|
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 return_string = "";
|
||||||
local available_ciphers;
|
local available_ciphers;
|
||||||
|
|
||||||
local ssl_v2_hello;
|
local ssl_v2_hello;
|
||||||
local server_hello;
|
local server_hello;
|
||||||
|
|
||||||
local server_hello_len;
|
local server_hello_len;
|
||||||
local message_type;
|
local message_type;
|
||||||
local SID_hit;
|
local SID_hit;
|
||||||
local certificate_type;
|
local certificate_type;
|
||||||
local ssl_version;
|
local ssl_version;
|
||||||
local certificate_len;
|
local certificate_len;
|
||||||
local ciphers_len;
|
local ciphers_len;
|
||||||
local certificate;
|
local certificate;
|
||||||
local connection_ID_len;
|
local connection_ID_len;
|
||||||
local cipher_list;
|
local cipher_list;
|
||||||
local connection_ID;
|
local connection_ID;
|
||||||
|
|
||||||
-- build client hello packet (contents inspired by
|
-- build client hello packet (contents inspired by
|
||||||
-- http://mail.nessus.org/pipermail/plugins-writers/2004-October/msg00041.html )
|
-- http://mail.nessus.org/pipermail/plugins-writers/2004-October/msg00041.html )
|
||||||
local t = {};
|
local t = {};
|
||||||
table.insert(t, string.char(0x80, 0x31));
|
table.insert(t, string.char(0x80, 0x31));
|
||||||
table.insert(t, string.char(0x01));
|
table.insert(t, string.char(0x01));
|
||||||
table.insert(t, string.char(0x00, 0x02));
|
table.insert(t, string.char(0x00, 0x02));
|
||||||
table.insert(t, string.char(0x00, 0x18));
|
table.insert(t, string.char(0x00, 0x18));
|
||||||
table.insert(t, string.char(0x00, 0x00));
|
table.insert(t, string.char(0x00, 0x00));
|
||||||
table.insert(t, string.char(0x00, 0x10));
|
table.insert(t, string.char(0x00, 0x10));
|
||||||
table.insert(t, string.char(0x07, 0x00, 0xc0));
|
table.insert(t, string.char(0x07, 0x00, 0xc0));
|
||||||
table.insert(t, string.char(0x05, 0x00, 0x80));
|
table.insert(t, string.char(0x05, 0x00, 0x80));
|
||||||
table.insert(t, string.char(0x03, 0x00, 0x80));
|
table.insert(t, string.char(0x03, 0x00, 0x80));
|
||||||
table.insert(t, string.char(0x01, 0x00, 0x80));
|
table.insert(t, string.char(0x01, 0x00, 0x80));
|
||||||
table.insert(t, string.char(0x08, 0x00, 0x80));
|
table.insert(t, string.char(0x08, 0x00, 0x80));
|
||||||
table.insert(t, string.char(0x06, 0x00, 0x40));
|
table.insert(t, string.char(0x06, 0x00, 0x40));
|
||||||
table.insert(t, string.char(0x04, 0x00, 0x80));
|
table.insert(t, string.char(0x04, 0x00, 0x80));
|
||||||
table.insert(t, string.char(0x02, 0x00, 0x80));
|
table.insert(t, string.char(0x02, 0x00, 0x80));
|
||||||
table.insert(t, string.char(0xe4, 0xbd, 0x00, 0x00));
|
table.insert(t, string.char(0xe4, 0xbd, 0x00, 0x00));
|
||||||
table.insert(t, string.char(0xa4, 0x41, 0xb6, 0x74));
|
table.insert(t, string.char(0xa4, 0x41, 0xb6, 0x74));
|
||||||
table.insert(t, string.char(0x71, 0x2b, 0x27, 0x95));
|
table.insert(t, string.char(0x71, 0x2b, 0x27, 0x95));
|
||||||
table.insert(t, string.char(0x44, 0xc0, 0x3d, 0xc0));
|
table.insert(t, string.char(0x44, 0xc0, 0x3d, 0xc0));
|
||||||
ssl_v2_hello = table.concat(t, "")
|
ssl_v2_hello = table.concat(t, "")
|
||||||
|
|
||||||
socket:connect(host, port, "tcp");
|
socket:connect(host, port, "tcp");
|
||||||
socket:send(ssl_v2_hello);
|
socket:send(ssl_v2_hello);
|
||||||
|
|
||||||
status, server_hello = socket:receive_bytes(2);
|
status, server_hello = socket:receive_bytes(2);
|
||||||
|
|
||||||
if (not status) then
|
if (not status) then
|
||||||
socket:close();
|
socket:close();
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
server_hello_len = string.sub(server_hello, 1, 2);
|
server_hello_len = string.sub(server_hello, 1, 2);
|
||||||
server_hello_len = hex2dec(server_hello_len);
|
server_hello_len = hex2dec(server_hello_len);
|
||||||
-- length record doesn't include its own length, and is "broken".
|
-- length record doesn't include its own length, and is "broken".
|
||||||
server_hello_len = server_hello_len - (128 * 256) + 2;
|
server_hello_len = server_hello_len - (128 * 256) + 2;
|
||||||
|
|
||||||
-- the hello needs to be at least 13 bytes long to be of any use
|
-- the hello needs to be at least 13 bytes long to be of any use
|
||||||
if (server_hello_len < 13) then
|
if (server_hello_len < 13) then
|
||||||
socket:close();
|
socket:close();
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
--try to get entire hello, if we don't already
|
--try to get entire hello, if we don't already
|
||||||
if (#server_hello < server_hello_len) then
|
if (#server_hello < server_hello_len) then
|
||||||
status, tmp = socket:receive_bytes(server_hello_len - #server_hello);
|
status, tmp = socket:receive_bytes(server_hello_len - #server_hello);
|
||||||
|
|
||||||
if (not status) then
|
if (not status) then
|
||||||
socket:close();
|
socket:close();
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
server_hello = server_hello .. tmp;
|
server_hello = server_hello .. tmp;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
socket:close();
|
socket:close();
|
||||||
|
|
||||||
-- split up server hello into components
|
-- split up server hello into components
|
||||||
idx, message_type = give_n_bytes(idx, 1, server_hello);
|
idx, message_type = give_n_bytes(idx, 1, server_hello);
|
||||||
idx, SID_hit = 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, certificate_type = give_n_bytes(idx, 1, server_hello);
|
||||||
idx, ssl_version = give_n_bytes(idx, 2, server_hello);
|
idx, ssl_version = give_n_bytes(idx, 2, server_hello);
|
||||||
idx, certificate_len = give_n_bytes(idx, 2, server_hello);
|
idx, certificate_len = give_n_bytes(idx, 2, server_hello);
|
||||||
certificate_len = hex2dec(certificate_len);
|
certificate_len = hex2dec(certificate_len);
|
||||||
idx, ciphers_len = give_n_bytes(idx, 2, server_hello);
|
idx, ciphers_len = give_n_bytes(idx, 2, server_hello);
|
||||||
ciphers_len = hex2dec(ciphers_len);
|
ciphers_len = hex2dec(ciphers_len);
|
||||||
idx, connection_ID_len = give_n_bytes(idx, 2, server_hello);
|
idx, connection_ID_len = give_n_bytes(idx, 2, server_hello);
|
||||||
connection_ID_len = hex2dec(connection_ID_len);
|
connection_ID_len = hex2dec(connection_ID_len);
|
||||||
idx, certificate = give_n_bytes(idx, certificate_len, server_hello);
|
idx, certificate = give_n_bytes(idx, certificate_len, server_hello);
|
||||||
idx, cipher_list = give_n_bytes(idx, ciphers_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);
|
idx, connection_ID = give_n_bytes(idx, connection_ID_len, server_hello);
|
||||||
|
|
||||||
-- some sanity checks:
|
-- some sanity checks:
|
||||||
-- is response a server hello?
|
-- is response a server hello?
|
||||||
if (message_type ~= string.char(0x04)) then
|
if (message_type ~= string.char(0x04)) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
-- is certificate in X.509 format?
|
-- is certificate in X.509 format?
|
||||||
if (certificate_type ~= string.char(0x01)) then
|
if (certificate_type ~= string.char(0x01)) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get a list of ciphers offered
|
-- get a list of ciphers offered
|
||||||
available_ciphers = ciphers(cipher_list, ciphers_len);
|
available_ciphers = ciphers(cipher_list, ciphers_len);
|
||||||
|
|
||||||
-- actually run some tests:
|
-- actually run some tests:
|
||||||
local o = stdnse.output_table()
|
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")
|
table.insert(o, "SSLv2 supported")
|
||||||
o["ciphers"] = available_ciphers
|
o["ciphers"] = available_ciphers
|
||||||
end
|
end
|
||||||
|
|
||||||
return o;
|
return o;
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -46,227 +46,227 @@ portrule = shortport.port_or_service(3690, "svnserve", "tcp", "open")
|
|||||||
|
|
||||||
svn =
|
svn =
|
||||||
{
|
{
|
||||||
svn_client = "nmap-brute v0.1",
|
svn_client = "nmap-brute v0.1",
|
||||||
|
|
||||||
new = function(self, host, port, repo)
|
new = function(self, host, port, repo)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.host = host
|
o.host = host
|
||||||
o.port = port
|
o.port = port
|
||||||
o.repo = repo
|
o.repo = repo
|
||||||
o.invalid_users = {}
|
o.invalid_users = {}
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Connects to the SVN - repository
|
--- Connects to the SVN - repository
|
||||||
--
|
--
|
||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return err string containing an error message on failure
|
-- @return err string containing an error message on failure
|
||||||
connect = function(self)
|
connect = function(self)
|
||||||
local repo_url = ( "svn://%s/%s" ):format(self.host.ip, self.repo)
|
local repo_url = ( "svn://%s/%s" ):format(self.host.ip, self.repo)
|
||||||
local status, msg
|
local status, msg
|
||||||
|
|
||||||
self.socket = nmap.new_socket()
|
self.socket = nmap.new_socket()
|
||||||
|
|
||||||
local result
|
local result
|
||||||
status, result = self.socket:connect(self.host.ip, self.port.number, "tcp")
|
status, result = self.socket:connect(self.host.ip, self.port.number, "tcp")
|
||||||
if( not(status) ) then
|
if( not(status) ) then
|
||||||
return false, result
|
return false, result
|
||||||
end
|
end
|
||||||
|
|
||||||
status, msg = self.socket:receive_bytes(1)
|
status, msg = self.socket:receive_bytes(1)
|
||||||
if ( not(status) or not( msg:match("^%( success") ) ) then
|
if ( not(status) or not( msg:match("^%( success") ) ) then
|
||||||
return false, "Banner reports failure"
|
return false, "Banner reports failure"
|
||||||
end
|
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 )
|
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 )
|
status = self.socket:send( msg )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Send failed"
|
return false, "Send failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, msg = self.socket:receive_bytes(1)
|
status, msg = self.socket:receive_bytes(1)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Receive failed"
|
return false, "Receive failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( msg:match("%( success") ) then
|
if ( msg:match("%( success") ) then
|
||||||
local tmp = msg:match("%( success %( %( ([%S+%s*]-) %)")
|
local tmp = msg:match("%( success %( %( ([%S+%s*]-) %)")
|
||||||
if ( not(tmp) ) then return false, "Failed to detect authentication" end
|
if ( not(tmp) ) then return false, "Failed to detect authentication" end
|
||||||
tmp = stdnse.strsplit(" ", tmp)
|
tmp = stdnse.strsplit(" ", tmp)
|
||||||
self.auth_mech = {}
|
self.auth_mech = {}
|
||||||
for _, v in pairs(tmp) do self.auth_mech[v] = true end
|
for _, v in pairs(tmp) do self.auth_mech[v] = true end
|
||||||
elseif ( msg:match("%( failure") ) then
|
elseif ( msg:match("%( failure") ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to login to the SVN server
|
--- Attempts to login to the SVN server
|
||||||
--
|
--
|
||||||
-- @param username string containing the login username
|
-- @param username string containing the login username
|
||||||
-- @param password string containing the login password
|
-- @param password string containing the login password
|
||||||
-- @return status, true on success, false on failure
|
-- @return status, true on success, false on failure
|
||||||
-- @return err string containing error message on failure
|
-- @return err string containing error message on failure
|
||||||
login = function( self, username, password )
|
login = function( self, username, password )
|
||||||
local status, msg
|
local status, msg
|
||||||
local challenge, digest
|
local challenge, digest
|
||||||
|
|
||||||
if ( self.auth_mech["CRAM-MD5"] ) then
|
if ( self.auth_mech["CRAM-MD5"] ) then
|
||||||
msg = "( CRAM-MD5 ( ) ) "
|
msg = "( CRAM-MD5 ( ) ) "
|
||||||
status = self.socket:send( msg )
|
status = self.socket:send( msg )
|
||||||
|
|
||||||
status, msg = self.socket:receive_bytes(1)
|
status, msg = self.socket:receive_bytes(1)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "error"
|
return false, "error"
|
||||||
end
|
end
|
||||||
|
|
||||||
challenge = msg:match("<.+>")
|
challenge = msg:match("<.+>")
|
||||||
|
|
||||||
if ( not(challenge) ) then
|
if ( not(challenge) ) then
|
||||||
return false, "Failed to read challenge"
|
return false, "Failed to read challenge"
|
||||||
end
|
end
|
||||||
|
|
||||||
digest = stdnse.tohex(openssl.hmac('md5', password, challenge))
|
digest = stdnse.tohex(openssl.hmac('md5', password, challenge))
|
||||||
msg = ("%d:%s %s "):format(#username + 1 + #digest, username, digest)
|
msg = ("%d:%s %s "):format(#username + 1 + #digest, username, digest)
|
||||||
self.socket:send( msg )
|
self.socket:send( msg )
|
||||||
|
|
||||||
status, msg = self.socket:receive_bytes(1)
|
status, msg = self.socket:receive_bytes(1)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "error"
|
return false, "error"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( msg:match("Username not found") ) then
|
if ( msg:match("Username not found") ) then
|
||||||
return false, "Username not found"
|
return false, "Username not found"
|
||||||
elseif ( msg:match("success") ) then
|
elseif ( msg:match("success") ) then
|
||||||
return true, "Authentication success"
|
return true, "Authentication success"
|
||||||
else
|
else
|
||||||
return false, "Authentication failed"
|
return false, "Authentication failed"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
return false, "Unsupported auth-mechanism"
|
return false, "Unsupported auth-mechanism"
|
||||||
end
|
end
|
||||||
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Close the SVN connection
|
--- Close the SVN connection
|
||||||
--
|
--
|
||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
close = function(self)
|
close = function(self)
|
||||||
return self.socket:close()
|
return self.socket:close()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Driver =
|
Driver =
|
||||||
{
|
{
|
||||||
new = function(self, host, port, invalid_users )
|
new = function(self, host, port, invalid_users )
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.host = host
|
o.host = host
|
||||||
o.port = port
|
o.port = port
|
||||||
o.repo = stdnse.get_script_args('svn-brute.repo')
|
o.repo = stdnse.get_script_args('svn-brute.repo')
|
||||||
o.invalid_users = invalid_users
|
o.invalid_users = invalid_users
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
connect = function( self )
|
connect = function( self )
|
||||||
local status, msg
|
local status, msg
|
||||||
|
|
||||||
self.svn = svn:new( self.host, self.port, self.repo )
|
self.svn = svn:new( self.host, self.port, self.repo )
|
||||||
status, msg = self.svn:connect()
|
status, msg = self.svn:connect()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
local err = brute.Error:new( "Failed to connect to SVN server" )
|
local err = brute.Error:new( "Failed to connect to SVN server" )
|
||||||
-- This might be temporary, set the retry flag
|
-- This might be temporary, set the retry flag
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
disconnect = function( self )
|
disconnect = function( self )
|
||||||
self.svn:close()
|
self.svn:close()
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to login to the SVN server
|
--- Attempts to login to the SVN server
|
||||||
--
|
--
|
||||||
-- @param username string containing the login username
|
-- @param username string containing the login username
|
||||||
-- @param password string containing the login password
|
-- @param password string containing the login password
|
||||||
-- @return status, true on success, false on failure
|
-- @return status, true on success, false on failure
|
||||||
-- @return brute.Error object on failure
|
-- @return brute.Error object on failure
|
||||||
-- brute.Account object on success
|
-- brute.Account object on success
|
||||||
login = function( self, username, password )
|
login = function( self, username, password )
|
||||||
local status, msg
|
local status, msg
|
||||||
|
|
||||||
if ( self.invalid_users[username] ) then
|
if ( self.invalid_users[username] ) then
|
||||||
return false, brute.Error:new( "User is invalid" )
|
return false, brute.Error:new( "User is invalid" )
|
||||||
end
|
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
|
if ( not(status) and msg:match("Username not found") ) then
|
||||||
self.invalid_users[username] = true
|
self.invalid_users[username] = true
|
||||||
return false, brute.Error:new("Username not found")
|
return false, brute.Error:new("Username not found")
|
||||||
elseif ( status and msg:match("success") ) then
|
elseif ( status and msg:match("success") ) then
|
||||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||||
else
|
else
|
||||||
return false, brute.Error:new( "Incorrect password" )
|
return false, brute.Error:new( "Incorrect password" )
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Verifies whether the repository is valid
|
--- Verifies whether the repository is valid
|
||||||
--
|
--
|
||||||
-- @return status, true on success, false on failure
|
-- @return status, true on success, false on failure
|
||||||
-- @return err string containing an error message on failure
|
-- @return err string containing an error message on failure
|
||||||
check = function( self )
|
check = function( self )
|
||||||
local svn = svn:new( self.host, self.port, self.repo )
|
local svn = svn:new( self.host, self.port, self.repo )
|
||||||
local status = svn:connect()
|
local status = svn:connect()
|
||||||
|
|
||||||
svn:close()
|
svn:close()
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false, ("Failed to connect to SVN repository (%s)"):format(self.repo)
|
return false, ("Failed to connect to SVN repository (%s)"):format(self.repo)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, accounts
|
local status, accounts
|
||||||
|
|
||||||
local repo = stdnse.get_script_args('svn-brute.repo')
|
local repo = stdnse.get_script_args('svn-brute.repo')
|
||||||
local force = stdnse.get_script_args('svn-brute.force')
|
local force = stdnse.get_script_args('svn-brute.force')
|
||||||
|
|
||||||
if ( not(repo) ) then
|
if ( not(repo) ) then
|
||||||
return "No repository specified (see svn-brute.repo)"
|
return "No repository specified (see svn-brute.repo)"
|
||||||
end
|
end
|
||||||
|
|
||||||
local svn = svn:new( host, port, repo )
|
local svn = svn:new( host, port, repo )
|
||||||
local status = svn:connect()
|
local status = svn:connect()
|
||||||
|
|
||||||
if ( status and svn.auth_mech["ANONYMOUS"] and not(force) ) then
|
if ( status and svn.auth_mech["ANONYMOUS"] and not(force) ) then
|
||||||
return " \n Anonymous SVN detected, no authentication needed"
|
return " \n Anonymous SVN detected, no authentication needed"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(svn.auth_mech) or not( svn.auth_mech["CRAM-MD5"] ) ) then
|
if ( not(svn.auth_mech) or not( svn.auth_mech["CRAM-MD5"] ) ) then
|
||||||
return " \n No supported authentication mechanisms detected"
|
return " \n No supported authentication mechanisms detected"
|
||||||
end
|
end
|
||||||
|
|
||||||
local invalid_users = {}
|
local invalid_users = {}
|
||||||
local engine = brute.Engine:new(Driver, host, port, invalid_users)
|
local engine = brute.Engine:new(Driver, host, port, invalid_users)
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
status, accounts = engine:start()
|
status, accounts = engine:start()
|
||||||
if( not(status) ) then
|
if( not(status) ) then
|
||||||
return accounts
|
return accounts
|
||||||
end
|
end
|
||||||
|
|
||||||
return accounts
|
return accounts
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -34,169 +34,169 @@ categories = {"discovery","broadcast"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
return nmap.is_privileged()
|
return nmap.is_privileged()
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Build an IPv6 invalid extension header.
|
--- Build an IPv6 invalid extension header.
|
||||||
-- @param nxt_hdr integer that stands for next header's type
|
-- @param nxt_hdr integer that stands for next header's type
|
||||||
local function build_invalid_extension_header(nxt_hdr)
|
local function build_invalid_extension_header(nxt_hdr)
|
||||||
-- RFC 2640, section 4.2 defines the TLV format of options headers.
|
-- 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
|
-- It is important that the first byte have 10 in the most significant
|
||||||
-- bits; that instructs the receiver to send a Parameter Problem.
|
-- bits; that instructs the receiver to send a Parameter Problem.
|
||||||
-- Option type 0x80 is unallocated; see
|
-- Option type 0x80 is unallocated; see
|
||||||
-- http://www.iana.org/assignments/ipv6-parameters/.
|
-- http://www.iana.org/assignments/ipv6-parameters/.
|
||||||
local ex_invalid_opt = string.char(0x80,0x01,0x00,0x00,0x00,0x00)
|
local ex_invalid_opt = string.char(0x80,0x01,0x00,0x00,0x00,0x00)
|
||||||
local ext_header =
|
local ext_header =
|
||||||
string.char(nxt_hdr) .. --next header
|
string.char(nxt_hdr) .. --next header
|
||||||
string.char(0) .. -- length 8
|
string.char(0) .. -- length 8
|
||||||
ex_invalid_opt
|
ex_invalid_opt
|
||||||
return ext_header
|
return ext_header
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_interfaces()
|
local function get_interfaces()
|
||||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
or nmap.get_interface()
|
or nmap.get_interface()
|
||||||
|
|
||||||
-- interfaces list (decide which interfaces to broadcast on)
|
-- interfaces list (decide which interfaces to broadcast on)
|
||||||
local interfaces = {}
|
local interfaces = {}
|
||||||
if interface_name then
|
if interface_name then
|
||||||
-- single interface defined
|
-- single interface defined
|
||||||
local if_table = nmap.get_interface_info(interface_name)
|
local if_table = nmap.get_interface_info(interface_name)
|
||||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||||
interfaces[#interfaces + 1] = if_table
|
interfaces[#interfaces + 1] = if_table
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||||
table.insert(interfaces, if_table)
|
table.insert(interfaces, if_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return interfaces
|
return interfaces
|
||||||
end
|
end
|
||||||
|
|
||||||
local function single_interface_broadcast(if_nfo, results)
|
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 condvar = nmap.condvar(results)
|
||||||
local src_mac = if_nfo.mac
|
local src_mac = if_nfo.mac
|
||||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
--Multicast invalid destination exheader probe
|
--Multicast invalid destination exheader probe
|
||||||
|
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
|
|
||||||
local function catch ()
|
local function catch ()
|
||||||
dnet:ethernet_close()
|
dnet:ethernet_close()
|
||||||
pcap:pcap_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
|
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))
|
dnet:ethernet_close()
|
||||||
pcap:pcap_open(if_nfo.device, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 4")
|
pcap:pcap_close()
|
||||||
|
|
||||||
local probe = packet.Frame:new()
|
condvar("signal")
|
||||||
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")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_output(results)
|
local function format_output(results)
|
||||||
local output = tab.new()
|
local output = tab.new()
|
||||||
|
|
||||||
for _, record in ipairs(results) do
|
for _, record in ipairs(results) do
|
||||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||||
end
|
end
|
||||||
if #results > 0 then
|
if #results > 0 then
|
||||||
output = { tab.dump(output) }
|
output = { tab.dump(output) }
|
||||||
if not target.ALLOW_NEW_TARGETS then
|
if not target.ALLOW_NEW_TARGETS then
|
||||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local threads = {}
|
local threads = {}
|
||||||
local results = {}
|
local results = {}
|
||||||
local condvar = nmap.condvar(results)
|
local condvar = nmap.condvar(results)
|
||||||
|
|
||||||
for _, if_nfo in ipairs(get_interfaces()) do
|
for _, if_nfo in ipairs(get_interfaces()) do
|
||||||
-- create a thread for each interface
|
-- create a thread for each interface
|
||||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
return format_output(results)
|
return format_output(results)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,28 +48,28 @@ categories = {"discovery","broadcast"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
return nmap.is_privileged()
|
return nmap.is_privileged()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_identifier(ip6_addr)
|
local function get_identifier(ip6_addr)
|
||||||
return string.sub(ip6_addr, 9, 16)
|
return string.sub(ip6_addr, 9, 16)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get a Unique-local Address with random global ID.
|
--- Get a Unique-local Address with random global ID.
|
||||||
-- @param local_scope The scope of the address, local or reserved.
|
-- @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.
|
-- @return A 16-byte string of IPv6 address, and the length of the prefix.
|
||||||
local function get_radom_ula_prefix(local_scope)
|
local function get_radom_ula_prefix(local_scope)
|
||||||
local ula_prefix
|
local ula_prefix
|
||||||
math.randomseed(os.time())
|
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 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
|
if local_scope then
|
||||||
ula_prefix = packet.ip6tobin("fd00::")
|
ula_prefix = packet.ip6tobin("fd00::")
|
||||||
else
|
else
|
||||||
ula_prefix = packet.ip6tobin("fc00::")
|
ula_prefix = packet.ip6tobin("fc00::")
|
||||||
end
|
end
|
||||||
ula_prefix = string.sub(ula_prefix,1,1) .. global_id .. string.sub(ula_prefix,7,-1)
|
ula_prefix = string.sub(ula_prefix,1,1) .. global_id .. string.sub(ula_prefix,7,-1)
|
||||||
return ula_prefix,64
|
return ula_prefix,64
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Build an ICMPv6 payload of Router Advertisement.
|
--- 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 valid_time integer that represents the valid time of the prefix.
|
||||||
-- @param preferred_time integer that represents the preferred 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 function build_router_advert(mac_src,prefix,prefix_len,valid_time,preferred_time)
|
||||||
local ra_msg = string.char(0x0, --cur hop limit
|
local ra_msg = string.char(0x0, --cur hop limit
|
||||||
0x08, --flags
|
0x08, --flags
|
||||||
0x00,0x00, --router lifetime
|
0x00,0x00, --router lifetime
|
||||||
0x00,0x00,0x00,0x00, --reachable time
|
0x00,0x00,0x00,0x00, --reachable time
|
||||||
0x00,0x00,0x00,0x00) --retrans timer
|
0x00,0x00,0x00,0x00) --retrans timer
|
||||||
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
local prefix_option_msg = string.char(prefix_len, 0xc0) .. --flags: Onlink, Auto
|
||||||
packet.set_u32("....",0,valid_time) ..
|
packet.set_u32("....",0,valid_time) ..
|
||||||
packet.set_u32("....",0,preferred_time) ..
|
packet.set_u32("....",0,preferred_time) ..
|
||||||
string.char(0,0,0,0) .. --unknown
|
string.char(0,0,0,0) .. --unknown
|
||||||
prefix
|
prefix
|
||||||
local icmpv6_prefix_option = packet.Packet:set_icmpv6_option(packet.ND_OPT_PREFIX_INFORMATION,prefix_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_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
|
local icmpv6_payload = ra_msg .. icmpv6_prefix_option .. icmpv6_src_link_option
|
||||||
return icmpv6_payload
|
return icmpv6_payload
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_interfaces()
|
local function get_interfaces()
|
||||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||||
or nmap.get_interface()
|
or nmap.get_interface()
|
||||||
|
|
||||||
-- interfaces list (decide which interfaces to broadcast on)
|
-- interfaces list (decide which interfaces to broadcast on)
|
||||||
local interfaces = {}
|
local interfaces = {}
|
||||||
if interface_name then
|
if interface_name then
|
||||||
-- single interface defined
|
-- single interface defined
|
||||||
local if_table = nmap.get_interface_info(interface_name)
|
local if_table = nmap.get_interface_info(interface_name)
|
||||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||||
interfaces[#interfaces + 1] = if_table
|
interfaces[#interfaces + 1] = if_table
|
||||||
else
|
else
|
||||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||||
table.insert(interfaces, if_table)
|
table.insert(interfaces, if_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return interfaces
|
return interfaces
|
||||||
end
|
end
|
||||||
|
|
||||||
local function single_interface_broadcast(if_nfo, results)
|
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 condvar = nmap.condvar(results)
|
||||||
local src_mac = if_nfo.mac
|
local src_mac = if_nfo.mac
|
||||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
--SLAAC-based host discovery probe
|
--SLAAC-based host discovery probe
|
||||||
|
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
|
|
||||||
local function catch ()
|
local function catch ()
|
||||||
dnet:ethernet_close()
|
dnet:ethernet_close()
|
||||||
pcap:pcap_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
|
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))
|
dnet:ethernet_close()
|
||||||
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")
|
pcap:pcap_close()
|
||||||
|
|
||||||
local actual_prefix = string.sub(src_ip6,1,8)
|
condvar("signal")
|
||||||
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")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_output(results)
|
local function format_output(results)
|
||||||
local output = tab.new()
|
local output = tab.new()
|
||||||
|
|
||||||
for _, record in ipairs(results) do
|
for _, record in ipairs(results) do
|
||||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||||
end
|
end
|
||||||
if #results > 0 then
|
if #results > 0 then
|
||||||
output = { tab.dump(output) }
|
output = { tab.dump(output) }
|
||||||
if not target.ALLOW_NEW_TARGETS then
|
if not target.ALLOW_NEW_TARGETS then
|
||||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local threads = {}
|
local threads = {}
|
||||||
local results = {}
|
local results = {}
|
||||||
local condvar = nmap.condvar(results)
|
local condvar = nmap.condvar(results)
|
||||||
|
|
||||||
for _, if_nfo in ipairs(get_interfaces()) do
|
for _, if_nfo in ipairs(get_interfaces()) do
|
||||||
-- create a thread for each interface
|
-- create a thread for each interface
|
||||||
if ipOps.ip_in_range(if_nfo.address, "fe80::/10") then
|
if ipOps.ip_in_range(if_nfo.address, "fe80::/10") then
|
||||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until next(threads) == nil
|
until next(threads) == nil
|
||||||
|
|
||||||
return format_output(results)
|
return format_output(results)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -55,37 +55,37 @@ rpc.RPC_version["wdb"] = { min=1, max=1 }
|
|||||||
|
|
||||||
local WDB_Procedure = {
|
local WDB_Procedure = {
|
||||||
["WDB_TARGET_PING"] = 0,
|
["WDB_TARGET_PING"] = 0,
|
||||||
["WDB_TARGET_CONNECT"] = 1,
|
["WDB_TARGET_CONNECT"] = 1,
|
||||||
["WDB_TARGET_DISCONNECT"] = 2,
|
["WDB_TARGET_DISCONNECT"] = 2,
|
||||||
["WDB_TARGET_MODE_SET"] = 3,
|
["WDB_TARGET_MODE_SET"] = 3,
|
||||||
["WDB_TARGET_MODE_GET"] = 4,
|
["WDB_TARGET_MODE_GET"] = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function checksum(data)
|
local function checksum(data)
|
||||||
local sum = 0
|
local sum = 0
|
||||||
local p = 0
|
local p = 0
|
||||||
local _
|
local _
|
||||||
p, _ = bin.unpack(">I", data)
|
p, _ = bin.unpack(">I", data)
|
||||||
while p < data:len() do
|
while p < data:len() do
|
||||||
local c
|
local c
|
||||||
p, c = bin.unpack(">S", data, p)
|
p, c = bin.unpack(">S", data, p)
|
||||||
sum = sum + c
|
sum = sum + c
|
||||||
end
|
end
|
||||||
sum = bit.band(sum, 0xffff) + bit.rshift(sum, 16)
|
sum = bit.band(sum, 0xffff) + bit.rshift(sum, 16)
|
||||||
return bit.bnot( sum )
|
return bit.bnot( sum )
|
||||||
end
|
end
|
||||||
|
|
||||||
local seqnum = 0
|
local seqnum = 0
|
||||||
local function seqno()
|
local function seqno()
|
||||||
seqnum = seqnum + 1
|
seqnum = seqnum + 1
|
||||||
return seqnum
|
return seqnum
|
||||||
end
|
end
|
||||||
|
|
||||||
local function request(comm, procedure, data)
|
local function request(comm, procedure, data)
|
||||||
local packet = comm:EncodePacket( nil, procedure, {type = rpc.Portmap.AuthType.NULL}, nil )
|
local packet = comm:EncodePacket( nil, procedure, {type = rpc.Portmap.AuthType.NULL}, nil )
|
||||||
local wdbwrapper = bin.pack( ">I2", data:len() + packet:len() + 8, seqno() )
|
local wdbwrapper = bin.pack( ">I2", data:len() + packet:len() + 8, seqno() )
|
||||||
local sum = checksum(packet..bin.pack(">I", 0x00000000)..wdbwrapper..data)
|
local sum = checksum(packet..bin.pack(">I", 0x00000000)..wdbwrapper..data)
|
||||||
return packet .. bin.pack(">S2", 0xffff, sum) .. wdbwrapper .. data
|
return packet .. bin.pack(">S2", 0xffff, sum) .. wdbwrapper .. data
|
||||||
end
|
end
|
||||||
|
|
||||||
local function stripnull(str)
|
local function stripnull(str)
|
||||||
@@ -97,139 +97,139 @@ local function stripnull(str)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function decode_reply(data, pos)
|
local function decode_reply(data, pos)
|
||||||
local wdberr, len
|
local wdberr, len
|
||||||
local done = data:len()
|
local done = data:len()
|
||||||
local info = {}
|
local info = {}
|
||||||
local _
|
local _
|
||||||
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
pos, _ = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, wdberr = rpc.Util.unmarshall_uint32(data, pos)
|
pos, wdberr = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
info["error"] = bit.band(wdberr, 0xc0000000)
|
info["error"] = bit.band(wdberr, 0xc0000000)
|
||||||
if (info["error"] ~= 0x00000000 ) then
|
if (info["error"] ~= 0x00000000 ) then
|
||||||
stdnse.print_debug(1,"Error from decode_reply: %x", info["error"])
|
stdnse.print_debug(1,"Error from decode_reply: %x", info["error"])
|
||||||
return nil, info
|
return nil, info
|
||||||
end
|
end
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
pos, info["agent_ver"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
pos, info["agent_ver"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||||
end
|
end
|
||||||
pos, info["agent_mtu"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["agent_mtu"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, info["agent_mod"] = 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, info["rt_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
pos, info["rt_vers"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
pos, info["rt_vers"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||||
end
|
end
|
||||||
pos, info["rt_cpu_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_cpu_type"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
info["rt_has_fpp"] = ( len ~= 0 )
|
info["rt_has_fpp"] = ( len ~= 0 )
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
info["rt_has_wp"] = ( len ~= 0 )
|
info["rt_has_wp"] = ( len ~= 0 )
|
||||||
pos, info["rt_page_size"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_page_size"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, info["rt_endian"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_endian"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
pos, info["rt_bsp_name"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
pos, info["rt_bsp_name"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||||
end
|
end
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
pos, info["rt_bootline"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
pos, info["rt_bootline"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||||
end
|
end
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
pos, info["rt_membase"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_membase"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
pos, info["rt_memsize"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_memsize"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
pos, info["rt_region_count"] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_region_count"] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
info["rt_regions"] = {}
|
info["rt_regions"] = {}
|
||||||
for i = 1, len do
|
for i = 1, len do
|
||||||
pos, info["rt_regions"][i] = rpc.Util.unmarshall_uint32(data, pos)
|
pos, info["rt_regions"][i] = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (len == nil) then return pos, info end
|
if (len == nil) then return pos, info end
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
pos, info["rt_hostpool_base"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
pos, info["rt_hostpool_base"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||||
end
|
end
|
||||||
if (pos == done) then return pos, info end
|
if (pos == done) then return pos, info end
|
||||||
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
pos, len = rpc.Util.unmarshall_uint32(data, pos)
|
||||||
if (len ~= 0) then
|
if (len ~= 0) then
|
||||||
pos, info["rt_hostpool_size"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
pos, info["rt_hostpool_size"] = rpc.Util.unmarshall_vopaque(len, data, pos)
|
||||||
end
|
end
|
||||||
return pos, info
|
return pos, info
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local comm = rpc.Comm:new("wdb", 1)
|
local comm = rpc.Comm:new("wdb", 1)
|
||||||
local status, err, data, pos, header
|
local status, err, data, pos, header
|
||||||
local info = {}
|
local info = {}
|
||||||
status, err = comm:Connect(host, port)
|
status, err = comm:Connect(host, port)
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
return stdnse.format_output(false, err)
|
return stdnse.format_output(false, err)
|
||||||
end
|
end
|
||||||
comm.socket:set_timeout(3000)
|
comm.socket:set_timeout(3000)
|
||||||
local packet = request(comm, WDB_Procedure["WDB_TARGET_CONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
local packet = request(comm, WDB_Procedure["WDB_TARGET_CONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
||||||
if (not(comm:SendPacket(packet))) then
|
if (not(comm:SendPacket(packet))) then
|
||||||
return stdnse.format_output(false, "Failed to send request")
|
return stdnse.format_output(false, "Failed to send request")
|
||||||
end
|
end
|
||||||
|
|
||||||
status, data = comm:ReceivePacket()
|
status, data = comm:ReceivePacket()
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
--return stdnse.format_output(false, "Failed to read data")
|
--return stdnse.format_output(false, "Failed to read data")
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
|
|
||||||
pos = 0
|
pos = 0
|
||||||
pos, header = comm:DecodeHeader(data, pos)
|
pos, header = comm:DecodeHeader(data, pos)
|
||||||
if not header then
|
if not header then
|
||||||
return stdnse.format_output(false, "Failed to decode header")
|
return stdnse.format_output(false, "Failed to decode header")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( pos == data:len() ) then
|
if ( pos == data:len() ) then
|
||||||
return stdnse.format_output(false, "No WDB data in reply")
|
return stdnse.format_output(false, "No WDB data in reply")
|
||||||
end
|
end
|
||||||
|
|
||||||
pos, info = decode_reply(data, pos)
|
pos, info = decode_reply(data, pos)
|
||||||
if not pos then
|
if not pos then
|
||||||
return stdnse.format_output(false, "WDB error: "..info.error)
|
return stdnse.format_output(false, "WDB error: "..info.error)
|
||||||
end
|
end
|
||||||
port.version.name = "wdb"
|
port.version.name = "wdb"
|
||||||
port.version.name_confidence = 10
|
port.version.name_confidence = 10
|
||||||
port.version.product = "Wind DeBug Agent"
|
port.version.product = "Wind DeBug Agent"
|
||||||
port.version.version = stripnull(info["agent_ver"])
|
port.version.version = stripnull(info["agent_ver"])
|
||||||
if (port.version.ostype ~= nil) then
|
if (port.version.ostype ~= nil) then
|
||||||
port.version.ostype = "VxWorks " .. stripnull(info["rt_vers"])
|
port.version.ostype = "VxWorks " .. stripnull(info["rt_vers"])
|
||||||
end
|
end
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
-- Clean up (some agents will continue to send data until we disconnect)
|
-- 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))
|
packet = request(comm, WDB_Procedure["WDB_TARGET_DISCONNECT"], bin.pack(">I3", 0x00000002, 0x00000000, 0x00000000))
|
||||||
if (not(comm:SendPacket(packet))) then
|
if (not(comm:SendPacket(packet))) then
|
||||||
return stdnse.format_output(false, "Failed to send request")
|
return stdnse.format_output(false, "Failed to send request")
|
||||||
end
|
end
|
||||||
|
|
||||||
local o = stdnse.output_table()
|
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")
|
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
|
if (info.agent_ver) then
|
||||||
o["Agent version"] = stripnull(info.agent_ver)
|
o["Agent version"] = stripnull(info.agent_ver)
|
||||||
end
|
end
|
||||||
--table.insert(o, "Agent MTU: " .. info.agent_mtu)
|
--table.insert(o, "Agent MTU: " .. info.agent_mtu)
|
||||||
if (info.rt_vers) then
|
if (info.rt_vers) then
|
||||||
o["VxWorks version"] = stripnull(info.rt_vers)
|
o["VxWorks version"] = stripnull(info.rt_vers)
|
||||||
end
|
end
|
||||||
-- rt_cpu_type is an enum type, but I don't have access to
|
-- rt_cpu_type is an enum type, but I don't have access to
|
||||||
-- cputypes.h, where it is defined
|
-- cputypes.h, where it is defined
|
||||||
--table.insert(o, "CPU Type: " .. info.rt_cpu_type)
|
--table.insert(o, "CPU Type: " .. info.rt_cpu_type)
|
||||||
if (info.rt_bsp_name) then
|
if (info.rt_bsp_name) then
|
||||||
o["Board Support Package"] = stripnull(info.rt_bsp_name)
|
o["Board Support Package"] = stripnull(info.rt_bsp_name)
|
||||||
end
|
end
|
||||||
if (info.rt_bootline) then
|
if (info.rt_bootline) then
|
||||||
o["Boot line"] = stripnull(info.rt_bootline)
|
o["Boot line"] = stripnull(info.rt_bootline)
|
||||||
end
|
end
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user