id = "NBSTAT" description = "Sends a NetBIOS query to target host to try to determine " .. "the NetBIOS name and MAC address." author = "Brandon Enright " license = "See nmaps COPYING for licence" -- This script was created by reverse-engineering the packets -- sent by NBTSCAN and hacking with the Wireshark NetBIOS -- protocol dissector. I do not believe this constitutes -- a derivative work in the GPL sense of the phrase. categories = {"discovery"} hostrule = function(host, port) -- The following logic is an attempt to only send to Windows machines. -- It is commented out because "discovery" scripts don't seem to follow the -- the same rules/logic as other scans. -- if (((port.number == 135 or -- port.number == 139 or -- port.number == 445 or -- port.service == "msrpc" or -- port.service == "netbios-ns" or -- port.service == "netbios-ssn" or -- port.service == "microsoft-ds") and -- port.protocol == "tcp" and -- port.state == "open") or -- (port.number == 137 and -- port.protocol == "udp" and -- (port.state == "open" or -- port.state == "open|filtered"))) -- then -- return true -- else -- return false -- end return true end action = function(host, port) local socket = nmap.new_socket() -- 3 seconds should be enough for anyone :-p -- This timeout should be set pretty low because this script -- will run against EVERY host, not just Windows boxes. -- If you know how to run this against just Windows machines please -- update this script and change the timeout to 5000. socket:set_timeout(3000) local result local status = true socket:connect(host.ip, 137, "udp") -- This is the UDP NetBIOS request packet. I didn't feel like -- actually generating a new one each time so this has been shamelessly -- copied from a packet dump of nbtscan. -- See http://www.unixwiz.net/tools/nbtscan.html for code. -- The magic number in this code is \000\097. socket:send( "\000\097\000\016\000\001\000\000" .. "\000\000\000\000\032\067\075\065" .. "\065\065\065\065\065\065\065\065" .. "\065\065\065\065\065\065\065\065" .. "\065\065\065\065\065\065\065\065" .. "\065\065\065\065\065\000\000\033" .. "\000\001") -- Consume a byte. This function seems to be broken since it actually -- grabs the entire response. Maybe I just don't know what I'm doing? -- If there really is a bug here this will need to be changed to 4 -- to check for a valid response and then consume another 53 to compute -- how much more should be consumed. Email me if you are trying to -- updated this comment doesn't make any sense... local status, result = socket:receive_bytes(1); if (not status) then socket:close() return end socket:close() if (result == "TIMEOUT") then return end -- Magic numbers: -- Offset to number of names returned: 57 -- Useful name length: 14 -- Length of each name + name type: 18 -- Length of MAC address: 6 if (string.len(result) < 57) then return end -- Make sure the response at least looks like a NBTSTAT response -- The first 2 bytes are the magic number sent originally, The second -- 2 bytes should be 0x84 0x00 (errorless name query response) if (string.sub(result, 1, 4) ~= "\000\097\132\000" ) then return end local namenum = string.byte (result, 57) if (string.len(result) < 58 + namenum * 18 + 6) then return end -- Names come back trailing-space-padded so strip that off.. local namefield = string.sub (result, 58, 58 + 14) local name if (string.find(namefield, " ") ~= nil and string.find(namefield, " ") > 1) then name = string.sub(namefield, 1, string.find(namefield, " ") - 1) else name = namefield end -- SAMBA likes to say its MAC is all 0s. That could be detected... -- If people say printing a MAC of 0000.0000.000 is more wrong -- than not returning a MAC at all then fix it here. local macfield = string.sub (result, 58 + namenum * 18, 58 + namenum * 18 + 6) local mac = string.format ("%02X:%02X:%02X:%02X:%02X:%02X", string.byte(macfield, 1), string.byte(macfield, 2), string.byte(macfield, 3), string.byte(macfield, 4), string.byte(macfield, 5), string.byte(macfield, 6)) return "NetBIOS name: " .. name .. ", NetBIOS MAC: " .. mac end