summaryrefslogtreecommitdiffstats
path: root/scripts/nbstat.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/nbstat.nse')
-rw-r--r--scripts/nbstat.nse244
1 files changed, 244 insertions, 0 deletions
diff --git a/scripts/nbstat.nse b/scripts/nbstat.nse
new file mode 100644
index 0000000..758cf30
--- /dev/null
+++ b/scripts/nbstat.nse
@@ -0,0 +1,244 @@
+local datafiles = require "datafiles"
+local netbios = require "netbios"
+local nmap = require "nmap"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+
+description = [[
+Attempts to retrieve the target's NetBIOS names and MAC address.
+
+By default, the script displays the name of the computer and the logged-in
+user; if the verbosity is turned up, it displays all names the system thinks it
+owns.
+]]
+
+---
+-- @usage
+-- sudo nmap -sU --script nbstat.nse -p137 <host>
+--
+-- @output
+-- Host script results:
+-- |_ nbstat: NetBIOS name: WINDOWS2003, NetBIOS user: <unknown>, NetBIOS MAC: 00:0c:29:c6:da:f5 (VMware)
+--
+-- Host script results:
+-- | nbstat: NetBIOS name: WINDOWS2003, NetBIOS user: <unknown>, NetBIOS MAC: 00:0c:29:c6:da:f5 (VMware)
+-- | Names:
+-- | WINDOWS2003<00> Flags: <unique><active>
+-- | WINDOWS2003<20> Flags: <unique><active>
+-- | SKULLSECURITY<00> Flags: <group><active>
+-- | SKULLSECURITY<1e> Flags: <group><active>
+-- | SKULLSECURITY<1d> Flags: <unique><active>
+-- |_ \x01\x02__MSBROWSE__\x02<01> Flags: <group><active>
+--
+-- @xmloutput
+-- <elem key="server_name">WINDOWS2003</elem>
+-- <elem key="workstation_name">WINDOWS2003</elem>
+-- <elem key="user">&lt;unknown&gt;</elem>
+-- <table key="mac">
+-- <elem key="manuf">VMware</elem>
+-- <elem key="address">00:0c:29:c6:da:f5</elem>
+-- </table>
+-- <table key="Names">
+-- <table>
+-- <elem key="name">WINDOWS2003</elem>
+-- <elem key="suffix">0</elem>
+-- <elem key="flags">1024</elem>
+-- </table>
+-- <table>
+-- <elem key="name">SKULLSECURITY</elem>
+-- <elem key="suffix">0</elem>
+-- <elem key="flags">33792</elem>
+-- </table>
+-- <table>
+-- <elem key="name">WINDOWS2003</elem>
+-- <elem key="suffix">32</elem>
+-- <elem key="flags">1024</elem>
+-- </table>
+-- <table>
+-- <elem key="name">SKULLSECURITY</elem>
+-- <elem key="suffix">30</elem>
+-- <elem key="flags">33792</elem>
+-- </table>
+-- <table>
+-- <elem key="name">SKULLSECURITY</elem>
+-- <elem key="suffix">29</elem>
+-- <elem key="flags">1024</elem>
+-- </table>
+-- <table>
+-- <elem key="name">\x01\x02__MSBROWSE__\x02</elem>
+-- <elem key="suffix">1</elem>
+-- <elem key="flags">33792</elem>
+-- </table>
+-- </table>
+-- <table key="Statistics">
+-- <elem>00 0c 29 c6 da f5 00 00 00 00 00 00 00 00 00 00 00</elem>
+-- <elem>00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00</elem>
+-- <elem>00 00 00 00 00 00 00 00 00 00 00 00 00 00</elem>
+-- </table>
+
+
+author = {"Brandon Enright", "Ron Bowes"}
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+
+-- Current version of this script was based entirely on Implementing CIFS, by
+-- Christopher R. Hertel.
+categories = {"default", "discovery", "safe"}
+
+
+hostrule = function(host)
+
+ -- The following is an attempt to only run this script against hosts
+ -- that will probably respond to a UDP 137 probe. One might argue
+ -- that sending a single UDP packet and waiting for a response is no
+ -- big deal and that it should be done for every host. In that case
+ -- simply change this rule to always return true.
+
+ local port_t135 = nmap.get_port_state(host,
+ {number=135, protocol="tcp"})
+ local port_t139 = nmap.get_port_state(host,
+ {number=139, protocol="tcp"})
+ local port_t445 = nmap.get_port_state(host,
+ {number=445, protocol="tcp"})
+ local port_u137 = nmap.get_port_state(host,
+ {number=137, protocol="udp"})
+
+ return (port_t135 ~= nil and port_t135.state == "open") or
+ (port_t139 ~= nil and port_t139.state == "open") or
+ (port_t445 ~= nil and port_t445.state == "open") or
+ (port_u137 ~= nil and
+ (port_u137.state == "open" or
+ port_u137.state == "open|filtered"))
+end
+
+
+action = function(host)
+
+ -- Get the list of NetBIOS names
+ local status, names, statistics = netbios.do_nbstat(host)
+ status, names, statistics = netbios.do_nbstat(host)
+ status, names, statistics = netbios.do_nbstat(host)
+ status, names, statistics = netbios.do_nbstat(host)
+ if(status == false) then
+ return stdnse.format_output(false, names)
+ end
+
+ -- Get the server name
+ local status, server_name = netbios.get_server_name(host, names)
+ if(status == false) then
+ return stdnse.format_output(false, server_name)
+ end
+
+ -- Get the workstation name
+ local status, workstation_name = netbios.get_workstation_name(host, names)
+ if(status == false) then
+ return stdnse.format_output(false, workstation_name)
+ end
+
+ -- Get the logged in user
+ local status, user_name = netbios.get_user_name(host, names)
+ if(status == false) then
+ return stdnse.format_output(false, user_name)
+ end
+
+ -- Format the Mac address in the standard way
+ local mac = {
+ address = "<unknown>",
+ manuf = "unknown"
+ }
+ if(#statistics >= 6) then
+ local status, mac_prefixes = datafiles.parse_mac_prefixes()
+ if not status then
+ -- Oh well
+ mac_prefixes = {}
+ end
+
+ -- MAC prefixes are matched on the first three bytes, all uppercase
+ local prefix = string.upper(stdnse.tohex(statistics:sub(1,3)))
+ mac.address = stdnse.format_mac(statistics:sub(1,6))
+ mac.manuf = mac_prefixes[prefix] or "unknown"
+
+ host.registry['nbstat'] = {
+ server_name = server_name,
+ workstation_name = workstation_name,
+ mac = mac.address
+ }
+ -- Samba doesn't set the Mac address, and nmap-mac-prefixes shows that as Xerox
+ if(mac.address == "00:00:00:00:00:00") then
+ mac.address = "<unknown>"
+ mac.manuf = "unknown"
+ end
+ end
+ setmetatable(mac, {
+ -- MAC is formatted as "00:11:22:33:44:55 (Manufacturer)"
+ __tostring=function(t) return string.format("%s (%s)", t.address, t.manuf) end
+ })
+
+ -- Check if we actually got a username
+ if(user_name == nil) then
+ user_name = "<unknown>"
+ end
+
+ local response = {
+ server_name = server_name,
+ workstation_name = workstation_name,
+ user = user_name,
+ mac = mac,
+ }
+
+ local names_output = {}
+ for i = 1, #names, 1 do
+ local name = names[i]
+ setmetatable(name, {
+ __tostring = function(t)
+ -- Tabular format with padding
+ return string.format("%s<%02x>%sFlags: %s",
+ t['name'], t['suffix'],
+ string.rep(" ", 17 - #t['name']),
+ netbios.flags_to_string(t['flags']))
+ end
+ })
+ table.insert(names_output, name)
+ end
+ setmetatable(names_output, {
+ __tostring = function(t)
+ local ret = {}
+ for i,v in ipairs(t) do
+ table.insert(ret, tostring(v))
+ end
+ -- Indent Names table by 2 spaces
+ return " " .. table.concat(ret, "\n ")
+ end
+ })
+
+ response["names"] = names_output
+
+ local statistics_output = {}
+ for i = 1, #statistics, 16 do
+ --Format statistics as space-separated hex bytes, 16 columns
+ table.insert(statistics_output,
+ stdnse.tohex(string.sub(statistics,i,i+16), {separator = " "})
+ )
+ end
+ response["statistics"] = statistics_output
+
+ setmetatable(response, {
+ __tostring = function(t)
+ -- Normal single-line result
+ local ret = {string.format("NetBIOS name: %s, NetBIOS user: %s, NetBIOS MAC: %s", t.server_name or t.workstation_name, t.user, t.mac)}
+ -- If verbosity is set, dump the whole list of names
+ if nmap.verbosity() >= 1 then
+ table.insert(ret, string.format("Names:\n%s",t.names))
+ -- If super verbosity is set, print out the full statistics
+ if nmap.verbosity() >= 2 then
+ -- Indent Statistics table by 2 spaces
+ table.insert(ret, string.format("Statistics:\n %s",table.concat(t.statistics,"\n ")))
+ end
+ end
+ return table.concat(ret, "\n")
+ end
+ })
+
+ return tostring(response)
+
+end