mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
for lib in nselib/*.lua*; do l=${lib#*/}; l=${l%.lua*}; find . -name \
\*.lua -o -name \*.nse | xargs grep -l "require .$l\>" | xargs grep \
-c "\<$l\." | grep ':0$' | awk -F: '{print "'$l'", $1}'; done
Did not remove calls to stdnse.silent_require since these can be used to
abort script execution if OpenSSL is not included, even if the script
does not directly call openssl.* (perhaps it uses comm.tryssl instead,
for instance).
Also did not remove require "strict", since that library is special and
modifies the environment.
243 lines
7.3 KiB
Lua
243 lines
7.3 KiB
Lua
local ipOps = require "ipOps"
|
|
local nmap = require "nmap"
|
|
local ssh1 = require "ssh1"
|
|
local stdnse = require "stdnse"
|
|
local table = require "table"
|
|
|
|
description = [[
|
|
Attempts to discover multihomed systems by analysing and comparing
|
|
information collected by other scripts. The information analyzed
|
|
currently includes, SSL certificates, SSH host keys, MAC addresses,
|
|
and Netbios server names.
|
|
|
|
In order for the script to be able to analyze the data it has dependencies to
|
|
the following scripts: ssl-cert,ssh-hostkey,nbtstat.
|
|
|
|
One or more of these scripts have to be run in order to allow the duplicates
|
|
script to analyze the data.
|
|
]]
|
|
|
|
---
|
|
-- @usage
|
|
-- sudo nmap -PN -p445,443 --script duplicates,nbstat,ssl-cert <ips>
|
|
--
|
|
-- @output
|
|
-- | duplicates:
|
|
-- | ARP
|
|
-- | MAC: 01:23:45:67:89:0a
|
|
-- | 192.168.99.10
|
|
-- | 192.168.99.11
|
|
-- | Netbios
|
|
-- | Server Name: WIN2KSRV001
|
|
-- | 192.168.0.10
|
|
-- |_ 192.168.1.10
|
|
--
|
|
|
|
|
|
--
|
|
-- While the script provides basic duplicate functionality, here are some ideas
|
|
-- on improvements.
|
|
--
|
|
-- Possible additional information sources:
|
|
-- * Microsoft SQL Server instance names (Match hostname, version, instance
|
|
-- names and ports) - Reliable given several instances
|
|
-- * Oracle TNS names - Not very reliable
|
|
--
|
|
-- Possible enhancements:
|
|
-- * Compare hosts across information sources and create a global category
|
|
-- in which system duplicates are reported based on more than one source.
|
|
-- * Add a reliability index for each information source that indicates how
|
|
-- reliable the duplicate match was. This could be an index compared to
|
|
-- other information sources as well as an indicator of how good the match
|
|
-- was for a particular information source.
|
|
|
|
author = "Patrik Karlsson"
|
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
|
categories = {"safe"}
|
|
dependencies = {"ssl-cert", "ssh-hostkey", "nbstat"}
|
|
|
|
|
|
hostrule = function() return true end
|
|
postrule = function() return true end
|
|
|
|
local function processSSLCerts(tab)
|
|
|
|
-- Handle SSL-certificates
|
|
-- We create a new table using the SHA1 digest as index
|
|
local ssl_certs = {}
|
|
for host, v in pairs(tab) do
|
|
for port, sha1 in pairs(v) do
|
|
ssl_certs[sha1] = ssl_certs[sha1] or {}
|
|
if ( not stdnse.contains(ssl_certs[sha1], host.ip) ) then
|
|
table.insert(ssl_certs[sha1], host.ip)
|
|
end
|
|
end
|
|
end
|
|
|
|
local results = {}
|
|
for sha1, hosts in pairs(ssl_certs) do
|
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
|
if ( #hosts > 1 ) then
|
|
table.insert(results, { name = ("Certficate (%s)"):format(sha1), hosts } )
|
|
end
|
|
end
|
|
|
|
return results
|
|
end
|
|
|
|
local function processSSHKeys(tab)
|
|
|
|
local hostkeys = {}
|
|
|
|
-- create a reverse mapping key_fingerprint -> host(s)
|
|
for ip, keys in pairs(tab) do
|
|
for _, key in ipairs(keys) do
|
|
local fp = ssh1.fingerprint_hex(key.fingerprint, key.algorithm, key.bits)
|
|
if not hostkeys[fp] then
|
|
hostkeys[fp] = {}
|
|
end
|
|
-- discard duplicate IPs
|
|
if not stdnse.contains(hostkeys[fp], ip) then
|
|
table.insert(hostkeys[fp], ip)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- look for hosts using the same hostkey
|
|
local results = {}
|
|
for key, hosts in pairs(hostkeys) do
|
|
if #hostkeys[key] > 1 then
|
|
table.sort(hostkeys[key], function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
|
local str = 'Key ' .. key .. ':'
|
|
table.insert( results, { name = str, hostkeys[key] } )
|
|
end
|
|
end
|
|
|
|
return results
|
|
end
|
|
|
|
local function processNBStat(tab)
|
|
|
|
local results, mac_table, name_table = {}, {}, {}
|
|
for host, v in pairs(tab) do
|
|
mac_table[v.mac] = mac_table[v.mac] or {}
|
|
if ( not(stdnse.contains(mac_table[v.mac], host.ip)) ) then
|
|
table.insert(mac_table[v.mac], host.ip)
|
|
end
|
|
|
|
name_table[v.server_name] = name_table[v.server_name] or {}
|
|
if ( not(stdnse.contains(name_table[v.server_name], host.ip)) ) then
|
|
table.insert(name_table[v.server_name], host.ip)
|
|
end
|
|
end
|
|
|
|
for mac, hosts in pairs(mac_table) do
|
|
if ( #hosts > 1 ) then
|
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
|
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
|
end
|
|
end
|
|
|
|
for srvname, hosts in pairs(name_table) do
|
|
if ( #hosts > 1 ) then
|
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
|
table.insert(results, { name = ("Server Name: %s"):format(srvname), hosts })
|
|
end
|
|
end
|
|
|
|
return results
|
|
end
|
|
|
|
local function processMAC(tab)
|
|
|
|
local mac
|
|
local mac_table = {}
|
|
|
|
for host in pairs(tab) do
|
|
if ( host.mac_addr ) then
|
|
mac = stdnse.format_mac(host.mac_addr)
|
|
mac_table[mac] = mac_table[mac] or {}
|
|
if ( not(stdnse.contains(mac_table[mac], host.ip)) ) then
|
|
table.insert(mac_table[mac], host.ip)
|
|
end
|
|
end
|
|
end
|
|
|
|
local results = {}
|
|
for mac, hosts in pairs(mac_table) do
|
|
if ( #hosts > 1 ) then
|
|
table.sort(hosts, function(a, b) return ipOps.compare_ip(a, "lt", b) end)
|
|
table.insert(results, { name = ("MAC: %s"):format(mac), hosts })
|
|
end
|
|
end
|
|
|
|
return results
|
|
end
|
|
|
|
postaction = function()
|
|
|
|
local handlers = {
|
|
['ssl-cert'] = { func = processSSLCerts, name = "SSL" },
|
|
['sshhostkey'] = { func = processSSHKeys, name = "SSH" },
|
|
['nbstat'] = { func = processNBStat, name = "Netbios" },
|
|
['mac'] = { func = processMAC, name = "ARP" }
|
|
}
|
|
|
|
-- temporary re-allocation code for SSH keys
|
|
for k, v in pairs(nmap.registry.sshhostkey or {}) do
|
|
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
|
nmap.registry['duplicates']['sshhostkey'] = nmap.registry['duplicates']['sshhostkey'] or {}
|
|
nmap.registry['duplicates']['sshhostkey'][k] = v
|
|
end
|
|
|
|
if ( not(nmap.registry['duplicates']) ) then
|
|
return
|
|
end
|
|
|
|
local results = {}
|
|
for key, handler in pairs(handlers) do
|
|
if ( nmap.registry['duplicates'][key] ) then
|
|
local result_part = handler.func( nmap.registry['duplicates'][key] )
|
|
if ( result_part and #result_part > 0 ) then
|
|
table.insert(results, { name = handler.name, result_part } )
|
|
end
|
|
end
|
|
end
|
|
|
|
return stdnse.format_output(true, results)
|
|
end
|
|
|
|
-- we have no real action in here. In essence we move information from the
|
|
-- host based registry to the global one, so that our postrule has access to
|
|
-- it when we need it.
|
|
hostaction = function(host)
|
|
|
|
nmap.registry['duplicates'] = nmap.registry['duplicates'] or {}
|
|
|
|
for port, cert in pairs(host.registry["ssl-cert"] or {}) do
|
|
nmap.registry['duplicates']['ssl-cert'] = nmap.registry['duplicates']['ssl-cert'] or {}
|
|
nmap.registry['duplicates']['ssl-cert'][host] = nmap.registry['duplicates']['ssl-cert'][host] or {}
|
|
nmap.registry['duplicates']['ssl-cert'][host][port] = stdnse.tohex(cert:digest("sha1"), { separator = " ", group = 4 })
|
|
end
|
|
|
|
if ( host.registry['nbstat'] ) then
|
|
nmap.registry['duplicates']['nbstat'] = nmap.registry['duplicates']['nbstat'] or {}
|
|
nmap.registry['duplicates']['nbstat'][host] = host.registry['nbstat']
|
|
end
|
|
|
|
if ( host.mac_addr_src ) then
|
|
nmap.registry['duplicates']['mac'] = nmap.registry['duplicates']['mac'] or {}
|
|
nmap.registry['duplicates']['mac'][host] = true
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
local Actions = {
|
|
hostrule = hostaction,
|
|
postrule = postaction
|
|
}
|
|
|
|
-- execute the action function corresponding to the current rule
|
|
action = function(...) return Actions[SCRIPT_TYPE](...) end
|