mirror of
https://github.com/nmap/nmap.git
synced 2025-12-30 11:29:01 +00:00
Add Ron Bowes's netbios and smb NSE modules and new scripts that use them. They
were introduced in http://seclists.org/nmap-dev/2008/q3/0827.html.
This commit is contained in:
@@ -1,17 +1,35 @@
|
||||
--- Sends a NetBIOS NBSTAT query to target host to try to determine the NetBIOS
|
||||
-- names and MAC address. By default, displays the name of the computer and the
|
||||
-- logged-in user; if verbosity is turned up, displays all names the system
|
||||
-- thinks it owns.
|
||||
--
|
||||
-- @output
|
||||
-- (no verbose)
|
||||
-- |_ NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28
|
||||
--
|
||||
-- (verbose)
|
||||
-- | NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28
|
||||
-- | Name: TEST1<00> Flags: <unique><active>
|
||||
-- | Name: TEST1<20> Flags: <unique><active>
|
||||
-- | Name: WORKGROUP<00> Flags: <group><active>
|
||||
-- | Name: TEST1<03> Flags: <unique><active>
|
||||
-- | Name: WORKGROUP<1e> Flags: <group><active>
|
||||
-- | Name: RON<03> Flags: <unique><active>
|
||||
-- | Name: WORKGROUP<1d> Flags: <unique><active>
|
||||
-- |_ Name: \x01\x02__MSBROWSE__\x02<01> Flags: <group><active>
|
||||
|
||||
id = "NBSTAT"
|
||||
description = "Sends a NetBIOS query to target host to try to determine \
|
||||
the NetBIOS name and MAC address."
|
||||
author = "Brandon Enright <bmenrigh@ucsd.edu>"
|
||||
the NetBIOS name and MAC address. For more information on the NetBIOS protocol, \
|
||||
see 'nselib/netbios.lua'."
|
||||
author = "Brandon Enright <bmenrigh@ucsd.edu>, Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
|
||||
-- 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.
|
||||
|
||||
-- Current version of this script was based entirly on Implementing CIFS, by
|
||||
-- Christopher R. Hertel.
|
||||
categories = {"default", "discovery", "safe"}
|
||||
|
||||
require "comm"
|
||||
require "netbios"
|
||||
|
||||
-- I have excluded the port function param because it doesn't make much sense
|
||||
-- for a hostrule. It works without warning. The NSE documentation is
|
||||
@@ -48,139 +66,69 @@ hostrule = function(host)
|
||||
end
|
||||
|
||||
|
||||
-- Again, I have excluded the port param. Is this okay on a hostrule?
|
||||
action = function(host)
|
||||
|
||||
-- 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 \003\097.
|
||||
local data =
|
||||
"\003\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"
|
||||
|
||||
local status, result = comm.exchange(host, 137, data, {proto="udp", timeout=5000})
|
||||
local i
|
||||
local status
|
||||
local names, statistics
|
||||
local server_name, user_name
|
||||
local mac
|
||||
local result = ""
|
||||
|
||||
if (not status) then
|
||||
return
|
||||
-- Get the list of NetBIOS names
|
||||
status, names, statistics = netbios.do_nbstat(host.ip)
|
||||
status, names, statistics = netbios.do_nbstat(host.ip)
|
||||
status, names, statistics = netbios.do_nbstat(host.ip)
|
||||
status, names, statistics = netbios.do_nbstat(host.ip)
|
||||
if(status == false) then
|
||||
return "ERROR: " .. names
|
||||
end
|
||||
|
||||
-- We got data back from 137, make sure we know it is open
|
||||
nmap.set_port_state(host, {number=137, protocol="udp"}, "open")
|
||||
|
||||
-- Magic numbers:
|
||||
-- Offset to number of names returned: 57
|
||||
-- Useful name length: 15
|
||||
-- Name type length: 3
|
||||
-- Computer name type: \032\068\000 or \032\004\000
|
||||
-- User name type: \003\068\000 or \003\004\000
|
||||
-- Length of each name + name type: 19
|
||||
-- Length of MAC address: 6
|
||||
-- Note that string.sub includes a 0 char so these numbers are 1 less
|
||||
|
||||
if (string.len(result) < 57) then
|
||||
return
|
||||
-- Get the server name
|
||||
status, server_name = netbios.get_server_name(host.ip, names)
|
||||
if(status == false) then
|
||||
return "ERROR: " .. server_name
|
||||
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) ~= "\003\097\132\000" ) then
|
||||
return
|
||||
-- Get the logged in user
|
||||
status, user_name = netbios.get_user_name(host.ip, names)
|
||||
if(status == false) then
|
||||
return "ERROR: " .. user_name
|
||||
end
|
||||
|
||||
local namenum = string.byte(result, 57)
|
||||
|
||||
if (string.len(result) < 58 + namenum * 18 + 6) then
|
||||
return
|
||||
-- Format the Mac address in the standard way
|
||||
mac = string.format("%02x:%02x:%02x:%02x:%02x:%02x", statistics:byte(1), statistics:byte(2), statistics:byte(3), statistics:byte(4), statistics:byte(5), statistics:byte(6))
|
||||
-- Samba doesn't set the Mac address
|
||||
if(mac == "00:00:00:00:00:00") then
|
||||
mac = "<unknown>"
|
||||
end
|
||||
|
||||
-- Check if we actually got a username
|
||||
if(user_name == nil) then
|
||||
user_name = "<unknown>"
|
||||
end
|
||||
|
||||
-- This loop will try to find the computer name. This name needs to
|
||||
-- be found before the username because sometimes NetBIOS reports
|
||||
-- username flags with the computer name as text.
|
||||
local compname
|
||||
for i = 0, namenum - 1, 1 do
|
||||
-- Names come back trailing-space-padded so strip that off..
|
||||
local namefield = string.sub (result, 58 + i * 18,
|
||||
58 + i * 18 + 14)
|
||||
local iname
|
||||
local nameflags = string.sub (result, 58 + i * 18 + 15,
|
||||
58 + i * 18 + 15 + 2)
|
||||
local padindex = string.find(namefield, " ")
|
||||
if (padindex ~= nil and padindex > 1) then
|
||||
iname = string.sub(namefield, 1, padindex - 1)
|
||||
else
|
||||
iname = namefield
|
||||
result = result .. string.format("NetBIOS name: %s, NetBIOS user: %s, NetBIOS MAC: %s\n", server_name, user_name, mac)
|
||||
|
||||
-- If verbosity is set, dump the whole list of names
|
||||
if(nmap.verbosity() >= 1) then
|
||||
for i = 1, #names, 1 do
|
||||
local padding = string.rep(" ", 17 - string.len(names[i]['name']))
|
||||
local flags_str = netbios.flags_to_string(names[i]['flags'])
|
||||
result = result .. string.format("Name: %s<%02x>%sFlags: %s\n", names[i]['name'], names[i]['suffix'], padding, flags_str)
|
||||
end
|
||||
|
||||
if (nameflags == "\032\068\000" or
|
||||
nameflags == "\032\004\000") then
|
||||
compname = iname
|
||||
end
|
||||
end
|
||||
|
||||
if (compname == nil) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- This loop will attempt to find the username logged onto the machine
|
||||
-- This is not possible on most Windows machines (I don't know why)
|
||||
-- Sometimes the flag that generally indicates the username
|
||||
-- returns the computer name instead. This function will ignore
|
||||
-- the username if it matches the computer name. This loop will not
|
||||
-- properly report the the username if it really happens to be
|
||||
-- the same as the computer name.
|
||||
local username
|
||||
for i = 0, namenum - 1, 1 do
|
||||
-- Names come back trailing-space-padded so strip that off..
|
||||
local namefield = string.sub (result, 58 + i * 18,
|
||||
58 + i * 18 + 14)
|
||||
local iname
|
||||
local nameflags = string.sub (result, 58 + i * 18 + 15,
|
||||
58 + i * 18 + 15 + 2)
|
||||
local padindex = string.find(namefield, " ")
|
||||
if (padindex ~= nil and padindex > 1) then
|
||||
iname = string.sub(namefield, 1, padindex - 1)
|
||||
else
|
||||
iname = namefield
|
||||
end
|
||||
|
||||
if (nameflags == "\003\068\000" or
|
||||
nameflags == "\003\004\000") then
|
||||
if (string.find(iname, compname, 1, true) == nil) then
|
||||
username = iname
|
||||
-- If super verbosity is set, print out the full statistics
|
||||
if(nmap.verbosity() >= 2) then
|
||||
result = result .. "Statistics: "
|
||||
for i = 1, #statistics, 1 do
|
||||
result = result .. string.format("%02x ", statistics:byte(i))
|
||||
end
|
||||
result = result .. "\n"
|
||||
end
|
||||
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 + 5)
|
||||
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 result
|
||||
|
||||
if (username ~= nil) then
|
||||
return "NetBIOS name: " .. compname ..
|
||||
", NetBIOS user: " .. username ..
|
||||
", NetBIOS MAC: " .. mac
|
||||
else
|
||||
return "NetBIOS name: " .. compname ..
|
||||
", NetBIOS MAC: " .. mac
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,456 +0,0 @@
|
||||
--- This script probes a target for its operating system version.
|
||||
-- It sends traffic via UDP port 137 and TCP port 139/445.\n\n
|
||||
-- == Implementation Information ==\n
|
||||
-- First, we need to
|
||||
-- elicit the NetBIOS share name associated with a workstation share.
|
||||
-- Once we have that, we need to encode the name into the "mangled"
|
||||
-- equivalent and send TCP 139/445 traffic to connect to the host and
|
||||
-- in an attempt to elicit the OS version name from an SMB Setup AndX
|
||||
-- response.\n\n
|
||||
--
|
||||
-- Thanks to Michail Prokopyev and xSharez Scanner for required
|
||||
-- traffic to generate for OS version detection.
|
||||
--
|
||||
--@usage
|
||||
-- sudo nmap -sU -sS --script netbios-smb-os-discovery.nse -p U:137,T:139 127.0.0.1
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "Discover OS Version over NetBIOS and SMB"
|
||||
description = "Attempt to elicit OS version from host running NetBIOS/SMB"
|
||||
author = "Judy Novak"
|
||||
copyright = "Sourcefire Inc, (C) 2006-2007"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"version"}
|
||||
|
||||
require 'bit'
|
||||
|
||||
hostrule = function(host)
|
||||
|
||||
-- This script should run under two different conditions:
|
||||
-- a) port tcp/445 is open (allowing us to make a raw connection)
|
||||
-- b) ports tcp/139 and udp/137 are open (137 may not be known)
|
||||
|
||||
local port_u137 = nmap.get_port_state(host, {number=137, protocol="udp"})
|
||||
local port_t139 = nmap.get_port_state(host, {number=139, protocol="tcp"})
|
||||
local port_t445 = nmap.get_port_state(host, {number=445, protocol="tcp"})
|
||||
|
||||
if(port_t445 ~= nil and port_t445.state == "open") then
|
||||
-- tcp/445 is open, we're good
|
||||
return true
|
||||
end
|
||||
|
||||
if(port_t139 ~= nil and port_t139.state == "open") then
|
||||
-- tcp/139 is open, check uf udp/137 is open or unknown
|
||||
if(port_u137 == nil or port_u137.state == "open" or port_u137.state == "open|filtered") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local sharename, message, osversion, currenttime, gen_msg, gen_msg_time, x
|
||||
|
||||
sharename = 0
|
||||
osversion = ""
|
||||
gen_msg = "OS version cannot be determined.\n"
|
||||
gen_msg_time = "System time cannot be determined.\n"
|
||||
|
||||
-- Decide whether to use raw SMB (port 445) or SMB over NetBIOS (139).
|
||||
-- Raw is better, because it uses one less packet and doesn't require a
|
||||
-- name to be known.
|
||||
local port_t445 = nmap.get_port_state(host, {number=445, protocol="tcp"})
|
||||
|
||||
local use_raw = (port_t445 ~= nil and port_t445.state == "open")
|
||||
|
||||
if(not use_raw) then
|
||||
sharename, message = udp_query(host)
|
||||
end
|
||||
|
||||
local ret = ""
|
||||
|
||||
if (use_raw or sharename ~= 0) then
|
||||
osversion, currenttime, message = tcp_session(sharename, host, use_raw)
|
||||
if (osversion ~= 0) then
|
||||
ret = ret .. osversion
|
||||
if(currenttime ~= 0) then
|
||||
ret = ret .. "\n" .. "Discover system time over SMB: " .. currenttime
|
||||
else
|
||||
ret = ret .. "\n" .. gen_msg_time .. message
|
||||
end
|
||||
else
|
||||
ret = ret .. gen_msg .. message
|
||||
end
|
||||
else
|
||||
ret = ret .. gen_msg .. "TCP/445 closed and couldn't determine NetBIOS name"
|
||||
end
|
||||
|
||||
return ret
|
||||
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- A NetBIOS wildcard query is sent to a host in an attempt to discover
|
||||
-- any NetBIOS shares on the host.
|
||||
|
||||
function udp_query(host)
|
||||
|
||||
local l, sharename, message
|
||||
local WildCard =
|
||||
string.char(0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x20, 0x43, 0x4b, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
|
||||
0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x21, 0x00, 0x00)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
|
||||
socket:connect(host.ip, 137, "udp")
|
||||
socket:send(WildCard)
|
||||
socket:set_timeout(100)
|
||||
|
||||
local status, result = socket:receive_bytes(1);
|
||||
|
||||
socket:close()
|
||||
|
||||
if (result ~= nil) then
|
||||
l = string.len(result)
|
||||
sharename = extract_sharename(result)
|
||||
if (sharename ~= 0) then
|
||||
return sharename, 1
|
||||
else
|
||||
message = "Failed to find NetBIOS share name in response to UDP NetBIOS wildcard query"
|
||||
return 0, message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- This function extracts the name of a "workstation" share from the
|
||||
-- response to the UDP NetBIOS wildcard query. Typically, there are
|
||||
-- several share types returned, but only one with a "workstation"
|
||||
-- type/code can be queried later for the OS version. The workstation
|
||||
-- type/code is 0x44 0x00 for OS versions prior to Vista. The type/code
|
||||
-- for Vista is 0x04 0x00.
|
||||
|
||||
function extract_sharename(resp)
|
||||
|
||||
local lenpay, beg, eend, typebeg, typeend, temp, name, nametype, ntgeneric, ntvista, ename, myname, eename, ntunix
|
||||
|
||||
beg = 58
|
||||
eend = beg + 15
|
||||
typebeg = eend + 1
|
||||
lenpay = string.len(resp)
|
||||
|
||||
while (eend <= lenpay) do
|
||||
|
||||
myname = string_concatenate(resp, beg, eend - 1)
|
||||
nametype = string.byte(resp, typebeg) .. string.byte(resp, typebeg + 1)
|
||||
ntgeneric = string.find(nametype, 0x44,0x00)
|
||||
ntvista = string.find(nametype, 0x04, 0x00)
|
||||
ntunix = string.find(nametype, 0x64, 0x00)
|
||||
|
||||
if (ntgeneric == 1) or (ntvista == 1) or (ntunix == 1) then
|
||||
ename = encode(myname)
|
||||
end
|
||||
|
||||
if (ename ~= nil) then
|
||||
do
|
||||
ename = string.char(0x20) .. ename .. string.char(0x43, 0x41, 0x00)
|
||||
return(ename)
|
||||
end
|
||||
end
|
||||
|
||||
beg = beg + 18
|
||||
eend = beg + 15
|
||||
typebeg = eend + 1
|
||||
end
|
||||
return(0)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Extract multiple bytes from a string and return concatenated result
|
||||
|
||||
function string_concatenate(mystring, start, stop)
|
||||
local x, temp, newname
|
||||
|
||||
for x = start, stop, 1 do
|
||||
temp = string.byte(mystring,x)
|
||||
if (x > start) then
|
||||
newname = newname .. string.char(temp)
|
||||
else
|
||||
newname = string.char(temp)
|
||||
end
|
||||
end
|
||||
return(newname)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- This function encodes the workstation share name returned from the
|
||||
-- UDP wildcard NetBIOS query. Each character from the NetBIOS share
|
||||
-- name is encoded/mangled using a special algorithm. Rather than
|
||||
-- implementing the algorithm, Microsoft offers a conversion table for
|
||||
-- any valid character found in a share name. I could not figure out
|
||||
-- how to use a Lua dictionary where the key value included a
|
||||
-- non-alphanumeric character. The static variable chars represents
|
||||
-- most of the characters that can be found in a share and the position
|
||||
-- in the string "chars" is the corresponding position in the trtable
|
||||
-- table. The character " had to be handled separately as it is used
|
||||
-- to delimit the value of chars.
|
||||
|
||||
encode = function(name)
|
||||
|
||||
local ln, y, nchar, newname, pos, temp, trtable
|
||||
local chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 !#$%&'()*+,-.=:;@^_{}~"
|
||||
|
||||
local trtable =
|
||||
{
|
||||
string.char(0x45,0x42), string.char(0x45,0x43), string.char(0x45,0x44), string.char(0x45,0x45), string.char(0x45,0x46),
|
||||
string.char(0x45,0x47), string.char(0x45,0x48), string.char(0x45,0x49), string.char(0x45,0x4A), string.char(0x45,0x4B),
|
||||
string.char(0x45,0x4C), string.char(0x45,0x4D), string.char(0x45,0x4E), string.char(0x45,0x4F), string.char(0x45,0x50),
|
||||
string.char(0x46,0x41), string.char(0x46,0x42), string.char(0x46,0x43), string.char(0x46,0x44), string.char(0x46,0x45),
|
||||
string.char(0x46,0x46), string.char(0x46,0x47), string.char(0x46,0x48), string.char(0x46,0x49), string.char(0x46,0x4A),
|
||||
string.char(0x46,0x4B), string.char(0x44,0x41), string.char(0x44,0x42), string.char(0x44,0x43), string.char(0x44,0x44),
|
||||
string.char(0x44,0x45), string.char(0x44,0x46), string.char(0x44,0x47), string.char(0x44,0x48), string.char(0x44,0x49),
|
||||
string.char(0x44,0x4A), string.char(0x43,0x41), string.char(0x43,0x42), string.char(0x43,0x44), string.char(0x43,0x45),
|
||||
string.char(0x43,0x46), string.char(0x43,0x47), string.char(0x43,0x48), string.char(0x43,0x49), string.char(0x43,0x4A),
|
||||
string.char(0x43,0x4B), string.char(0x43,0x4C), string.char(0x43,0x4D), string.char(0x43,0x4E), string.char(0x43,0x4F),
|
||||
string.char(0x44,0x4E), string.char(0x44,0x4B), string.char(0x44,0x4C), string.char(0x45,0x41), string.char(0x46,0x4F),
|
||||
string.char(0x46,0x50), string.char(0x48,0x4C), string.char(0x48,0x4E), string.char(0x48,0x4F)
|
||||
}
|
||||
|
||||
ln = string.len(name)
|
||||
y = 1
|
||||
|
||||
while (y <= ln) do
|
||||
temp = string.byte(name, y)
|
||||
|
||||
if (temp == 0x00) then --Sharename must be followed by spaces not null's to be acceptable
|
||||
return(nil)
|
||||
elseif (temp == '"') then
|
||||
nchar = string.char(0x43,0x43)
|
||||
else do
|
||||
temp = string.char(temp)
|
||||
pos = string.find(chars, temp)
|
||||
nchar = trtable[pos]
|
||||
if (y > 1) then
|
||||
newname = newname .. nchar
|
||||
else
|
||||
newname = nchar
|
||||
end
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return(newname)
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- This function invokes the TCP traffic that is generated to get
|
||||
-- a response that yields the OS version information. The first
|
||||
-- payload is an SMB session initiation request followed by a
|
||||
-- negotiate payload, and followed by a Session Setup AndX request.
|
||||
-- The workstation share name extracted from the UDP wildcard NetBIOS
|
||||
-- response must be used in the SMB session initiation request(payload 1).
|
||||
-- Payload for the requests that follow is static.
|
||||
|
||||
function tcp_session(ename, host, use_raw)
|
||||
|
||||
local catch = function()
|
||||
socket:close()
|
||||
end
|
||||
|
||||
local rec1_payload, rec2_payload, rec3_payload, status, line1, line2, line3, currenttime, osversion, winshare, pos, message
|
||||
|
||||
message = 0
|
||||
local win5 = "Windows 5.0"
|
||||
local win51 = "Windows 5.1"
|
||||
|
||||
winshare = string.char(0x20, 0x46, 0x48, 0x45, 0x4A, 0x45, 0x4F, 0x45, 0x45, 0x45, 0x50, 0x46, 0x48, 0x46, 0x44, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x00)
|
||||
|
||||
rec1_payload = string.char(0x81, 0x00, 0x00, 0x44) .. ename .. winshare
|
||||
|
||||
rec2_payload = string.char( 0x00, 0x00, 0x00, 0x85, 0xff, 0x53, 0x4d, 0x42, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0 ) ..
|
||||
string.char( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xfa ) ..
|
||||
string.char( 0x00, 0x00, 0x17, 0x62, 0x00, 0x61, 0x00, 0x02, 0x50, 0x43, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f ) ..
|
||||
string.char( 0x52, 0x4b, 0x20, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x41, 0x4d, 0x20, 0x31, 0x2e, 0x30, 0x00, 0x02 ) ..
|
||||
string.char( 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30, 0x00, 0x02, 0x57, 0x69, 0x6e, 0x64, 0x6f ) ..
|
||||
string.char( 0x77, 0x73, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x57, 0x6f, 0x72, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70 ) ..
|
||||
string.char( 0x73, 0x20, 0x33, 0x2e, 0x31, 0x61, 0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58, 0x30, 0x30 ) ..
|
||||
string.char( 0x32, 0x00, 0x02, 0x4c, 0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x32, 0x2e, 0x31, 0x00, 0x02, 0x4e, 0x54 ) ..
|
||||
string.char( 0x20, 0x4c, 0x4d, 0x20, 0x30, 0x2e, 0x31, 0x32, 0x00)
|
||||
|
||||
rec3_payload = string.char( 0x00, 0x00, 0x00, 0xab, 0xff, 0x53, 0x4d, 0x42, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0 ) ..
|
||||
string.char( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xfa ) ..
|
||||
string.char( 0x00, 0x00, 0x17, 0x62, 0x0d, 0xff, 0x00, 0x00, 0x00, 0x04, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00 ) ..
|
||||
string.char( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x6d ) ..
|
||||
string.char( 0x00, 0x00, 0x00, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x73, 0x00 ) ..
|
||||
string.char( 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x00, 0x00, 0x57, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x64, 0x00 ) ..
|
||||
string.char( 0x6f, 0x00, 0x77, 0x00, 0x73, 0x00, 0x20, 0x00, 0x39, 0x00, 0x35, 0x00, 0x2f, 0x00, 0x39, 0x00 ) ..
|
||||
string.char( 0x38, 0x00, 0x2f, 0x00, 0x4d, 0x00, 0x65, 0x00, 0x2f, 0x00, 0x4e, 0x00, 0x54, 0x00, 0x2f, 0x00 ) ..
|
||||
string.char( 0x32, 0x00, 0x6b, 0x00, 0x2f, 0x00, 0x58, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00 ) ..
|
||||
string.char( 0x53, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x53, 0x00 ) ..
|
||||
string.char( 0x63, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
if(use_raw) then
|
||||
try(socket:connect(host.ip,445,"tcp"))
|
||||
else
|
||||
try(socket:connect(host.ip,139,"tcp"))
|
||||
end
|
||||
|
||||
if(not use_raw) then
|
||||
socket:set_timeout(100)
|
||||
try(socket:send(rec1_payload))
|
||||
status, line1 = socket:receive_lines(1)
|
||||
|
||||
if (not status) then
|
||||
socket:close()
|
||||
message = "Never received a response to SMB Session Request"
|
||||
return 0, 0, message
|
||||
end
|
||||
end
|
||||
|
||||
socket:set_timeout(100)
|
||||
try(socket:send(rec2_payload))
|
||||
status, line2 = socket:receive_lines(1)
|
||||
|
||||
if (not status) then
|
||||
socket:close()
|
||||
message = "Never received a response to SMB Negotiate Protocol Request"
|
||||
return 0, 0, message
|
||||
end
|
||||
|
||||
currenttime, message = extract_time(line2);
|
||||
|
||||
-- Check for an error parsing line2
|
||||
if(currenttime == 0) then
|
||||
return 0, 0, message
|
||||
end
|
||||
|
||||
socket:set_timeout(100)
|
||||
try(socket:send(rec3_payload))
|
||||
status, line3 = socket:receive_lines(1)
|
||||
|
||||
if (not status) then
|
||||
socket:close()
|
||||
message = "Never received a response to SMB Setup AndX Request"
|
||||
return 0, currenttime, message
|
||||
end
|
||||
socket:close()
|
||||
|
||||
-- Check for an error parsing line3
|
||||
osversion, message = extract_version(line3)
|
||||
if (osversion ~= 0) then
|
||||
pos = string.find(osversion, win5)
|
||||
if (pos ~= nil) then
|
||||
osversion = "Windows 2000"
|
||||
else
|
||||
pos = string.find(osversion, win51)
|
||||
if (pos ~= nil) then
|
||||
osversion = "Windows XP"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return osversion, currenttime, message
|
||||
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Response from Session Setup AndX Request (TCP payload 3)
|
||||
-- Must be SMB response. Extract the OS version from it from a fixed
|
||||
-- offset in the payload.
|
||||
|
||||
function extract_version(line)
|
||||
|
||||
local temp, smb, ltemp, go, x, osversion, mychar, message
|
||||
|
||||
smb = "SMB" .. string.char(0x73)
|
||||
temp = string_concatenate(line, 6, 9)
|
||||
message = 0
|
||||
|
||||
if (temp ~= smb) then
|
||||
message = "Didn't find correct SMB record as a response to the Session Setup AndX request"
|
||||
return 0, message
|
||||
end
|
||||
|
||||
ltemp = string.len(line)
|
||||
temp = string_concatenate(line, 47, ltemp)
|
||||
x=1
|
||||
|
||||
osversion = ""
|
||||
while (x < ltemp) do
|
||||
mychar = string.byte(temp,x)
|
||||
if (mychar == 0) then
|
||||
return osversion, message
|
||||
else
|
||||
osversion = osversion .. string.char(mychar)
|
||||
end
|
||||
x = x + 2
|
||||
end
|
||||
|
||||
if (x >= ltemp) then
|
||||
message = "OS version not found in expected record Session Setup AndX response"
|
||||
return 0, message
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Response from Negotiate Protocol Response (TCP payload 2)
|
||||
-- Must be SMB response. Extract the time from it from a fixed
|
||||
-- offset in the payload.
|
||||
|
||||
function extract_time(line)
|
||||
|
||||
local smb, tmp, message, i, timebuf, timezonebuf, time, timezone
|
||||
|
||||
message = 0
|
||||
|
||||
if(string.sub(line, 6, 8) ~= "SMB") then
|
||||
message = "Didn't find correct SMB record as a response to the Negotiate Protocol Response"
|
||||
return 0, message
|
||||
end
|
||||
|
||||
if(string.byte(line, 9) ~= 0x72) then
|
||||
message = "Incorrect Negotiate Protocol Response type"
|
||||
return 0, message
|
||||
end
|
||||
|
||||
-- Extract the timestamp from the response
|
||||
i = 1
|
||||
time = 0
|
||||
timebuf = string.sub(line, 0x3d, 0x3d + 7)
|
||||
while (i <= 8) do
|
||||
time = time + 1.0 + (bit.lshift(string.byte(timebuf, i), 8 * (i - 1)))
|
||||
i = i + 1
|
||||
end
|
||||
-- Convert time from 1/10 microseconds to seconds
|
||||
time = (time / 10000000) - 11644473600;
|
||||
|
||||
-- Extract the timezone offset from the response
|
||||
timezonebuf = string.sub(line, 0x45, 0x45 + 2)
|
||||
timezone = (string.byte(timezonebuf, 1) + (bit.lshift(string.byte(timezonebuf, 2), 8)))
|
||||
|
||||
-- This is a nasty little bit of code, so I'll explain it in detail. If the timezone has the
|
||||
-- highest-order bit set, it means it was negative. If so, we want to take the two's complement
|
||||
-- of it (not(x)+1) and divide by 60, to get minutes. Otherwise, just divide by 60.
|
||||
-- To further complicate things (as if we needed _that_!), the timezone offset is the number of
|
||||
-- minutes you'd have to add to the time to get to UTC, so it's actually the negative of what
|
||||
-- we want. Confused yet?
|
||||
if(timezone == 0x00) then
|
||||
timezone = "UTC+0"
|
||||
elseif(bit.band(timezone, 0x8000) == 0x8000) then
|
||||
timezone = "UTC+" .. ((bit.band(bit.bnot(timezone), 0x0FFFF) + 1) / 60)
|
||||
else
|
||||
timezone = "UTC-" .. (timezone / 60)
|
||||
end
|
||||
|
||||
return (os.date("%Y-%m-%d %H:%M:%S", time) .. " " .. timezone), message;
|
||||
|
||||
end
|
||||
|
||||
@@ -1,94 +1,98 @@
|
||||
Entry{ category = "default", filename = "dns-test-open-recursion.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-test-open-recursion.nse" }
|
||||
Entry{ category = "default", filename = "showOwner.nse" }
|
||||
Entry{ category = "safe", filename = "showOwner.nse" }
|
||||
Entry{ category = "demo", filename = "daytimeTest.nse" }
|
||||
Entry{ category = "default", filename = "RealVNC_auth_bypass.nse" }
|
||||
Entry{ category = "malware", filename = "RealVNC_auth_bypass.nse" }
|
||||
Entry{ category = "vuln", filename = "RealVNC_auth_bypass.nse" }
|
||||
Entry{ category = "external", filename = "dns-safe-recursion-port.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-safe-recursion-port.nse" }
|
||||
Entry{ category = "intrusive", filename = "SNMPcommunitybrute.nse" }
|
||||
Entry{ category = "auth", filename = "SNMPcommunitybrute.nse" }
|
||||
Entry{ category = "default", filename = "showOwner.nse" }
|
||||
Entry{ category = "safe", filename = "showOwner.nse" }
|
||||
Entry{ category = "default", filename = "SSLv2-support.nse" }
|
||||
Entry{ category = "safe", filename = "SSLv2-support.nse" }
|
||||
Entry{ category = "malware", filename = "ircZombieTest.nse" }
|
||||
Entry{ category = "version", filename = "skype_v2-version.nse" }
|
||||
Entry{ category = "discovery", filename = "HTTPtrace.nse" }
|
||||
Entry{ category = "demo", filename = "echoTest.nse" }
|
||||
Entry{ category = "default", filename = "UPnP-info.nse" }
|
||||
Entry{ category = "safe", filename = "UPnP-info.nse" }
|
||||
Entry{ category = "default", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "safe", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "discovery", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "auth", filename = "bruteTelnet.nse" }
|
||||
Entry{ category = "intrusive", filename = "bruteTelnet.nse" }
|
||||
Entry{ category = "external", filename = "dns-safe-recursion-txid.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-safe-recursion-txid.nse" }
|
||||
Entry{ category = "default", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "discovery", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "safe", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "default", filename = "robots.nse" }
|
||||
Entry{ category = "safe", filename = "robots.nse" }
|
||||
Entry{ category = "default", filename = "zoneTrans.nse" }
|
||||
Entry{ category = "intrusive", filename = "zoneTrans.nse" }
|
||||
Entry{ category = "discovery", filename = "zoneTrans.nse" }
|
||||
Entry{ category = "discovery", filename = "whois.nse" }
|
||||
Entry{ category = "external", filename = "whois.nse" }
|
||||
Entry{ category = "safe", filename = "whois.nse" }
|
||||
Entry{ category = "discovery", filename = "ripeQuery.nse" }
|
||||
Entry{ category = "external", filename = "ripeQuery.nse" }
|
||||
Entry{ category = "demo", filename = "chargenTest.nse" }
|
||||
Entry{ category = "malware", filename = "strangeSMTPport.nse" }
|
||||
Entry{ category = "version", filename = "iax2Detect.nse" }
|
||||
Entry{ category = "demo", filename = "showSMTPVersion.nse" }
|
||||
Entry{ category = "discovery", filename = "ASN.nse" }
|
||||
Entry{ category = "external", filename = "ASN.nse" }
|
||||
Entry{ category = "default", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "demo", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "safe", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "discovery", filename = "promiscuous.nse" }
|
||||
Entry{ category = "version", filename = "netbios-smb-os-discovery.nse" }
|
||||
Entry{ category = "default", filename = "anonFTP.nse" }
|
||||
Entry{ category = "auth", filename = "anonFTP.nse" }
|
||||
Entry{ category = "intrusive", filename = "anonFTP.nse" }
|
||||
Entry{ category = "intrusive", filename = "SQLInject.nse" }
|
||||
Entry{ category = "vuln", filename = "SQLInject.nse" }
|
||||
Entry{ category = "auth", filename = "bruteTelnet.nse" }
|
||||
Entry{ category = "intrusive", filename = "bruteTelnet.nse" }
|
||||
Entry{ category = "discovery", filename = "HTTPtrace.nse" }
|
||||
Entry{ category = "demo", filename = "SMTP_openrelay_test.nse" }
|
||||
Entry{ category = "default", filename = "nbstat.nse" }
|
||||
Entry{ category = "discovery", filename = "nbstat.nse" }
|
||||
Entry{ category = "safe", filename = "nbstat.nse" }
|
||||
Entry{ category = "default", filename = "HTTPAuth.nse" }
|
||||
Entry{ category = "auth", filename = "HTTPAuth.nse" }
|
||||
Entry{ category = "intrusive", filename = "HTTPAuth.nse" }
|
||||
Entry{ category = "default", filename = "finger.nse" }
|
||||
Entry{ category = "discovery", filename = "finger.nse" }
|
||||
Entry{ category = "demo", filename = "showHTTPVersion.nse" }
|
||||
Entry{ category = "default", filename = "SSHv1-support.nse" }
|
||||
Entry{ category = "safe", filename = "SSHv1-support.nse" }
|
||||
Entry{ category = "default", filename = "popcapa.nse" }
|
||||
Entry{ category = "default", filename = "SNMPsysdescr.nse" }
|
||||
Entry{ category = "discovery", filename = "SNMPsysdescr.nse" }
|
||||
Entry{ category = "safe", filename = "SNMPsysdescr.nse" }
|
||||
Entry{ category = "intrusive", filename = "brutePOP3.nse" }
|
||||
Entry{ category = "auth", filename = "brutePOP3.nse" }
|
||||
Entry{ category = "default", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "discovery", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "safe", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "default", filename = "ftpbounce.nse" }
|
||||
Entry{ category = "intrusive", filename = "ftpbounce.nse" }
|
||||
Entry{ category = "auth", filename = "xamppDefaultPass.nse" }
|
||||
Entry{ category = "vuln", filename = "xamppDefaultPass.nse" }
|
||||
Entry{ category = "intrusive", filename = "HTTPpasswd.nse" }
|
||||
Entry{ category = "vuln", filename = "HTTPpasswd.nse" }
|
||||
Entry{ category = "demo", filename = "showSSHVersion.nse" }
|
||||
Entry{ category = "version", filename = "PPTPversion.nse" }
|
||||
Entry{ category = "default", filename = "ircServerInfo.nse" }
|
||||
Entry{ category = "discovery", filename = "ircServerInfo.nse" }
|
||||
Entry{ category = "default", filename = "dns-test-open-recursion.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-test-open-recursion.nse" }
|
||||
Entry{ category = "demo", filename = "chargenTest.nse" }
|
||||
Entry{ category = "default", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "demo", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "safe", filename = "showHTMLTitle.nse" }
|
||||
Entry{ category = "default", filename = "MSSQLm.nse" }
|
||||
Entry{ category = "discovery", filename = "MSSQLm.nse" }
|
||||
Entry{ category = "intrusive", filename = "MSSQLm.nse" }
|
||||
Entry{ category = "demo", filename = "echoTest.nse" }
|
||||
Entry{ category = "default", filename = "SSHv1-support.nse" }
|
||||
Entry{ category = "safe", filename = "SSHv1-support.nse" }
|
||||
Entry{ category = "default", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "discovery", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "safe", filename = "MySQLinfo.nse" }
|
||||
Entry{ category = "auth", filename = "xamppDefaultPass.nse" }
|
||||
Entry{ category = "vuln", filename = "xamppDefaultPass.nse" }
|
||||
Entry{ category = "default", filename = "SSLv2-support.nse" }
|
||||
Entry{ category = "safe", filename = "SSLv2-support.nse" }
|
||||
Entry{ category = "default", filename = "zoneTrans.nse" }
|
||||
Entry{ category = "intrusive", filename = "zoneTrans.nse" }
|
||||
Entry{ category = "discovery", filename = "zoneTrans.nse" }
|
||||
Entry{ category = "default", filename = "ftpbounce.nse" }
|
||||
Entry{ category = "intrusive", filename = "ftpbounce.nse" }
|
||||
Entry{ category = "version", filename = "skype_v2-version.nse" }
|
||||
Entry{ category = "discovery", filename = "promiscuous.nse" }
|
||||
Entry{ category = "default", filename = "SNMPsysdescr.nse" }
|
||||
Entry{ category = "discovery", filename = "SNMPsysdescr.nse" }
|
||||
Entry{ category = "safe", filename = "SNMPsysdescr.nse" }
|
||||
Entry{ category = "demo", filename = "showSMTPVersion.nse" }
|
||||
Entry{ category = "default", filename = "nbstat.nse" }
|
||||
Entry{ category = "discovery", filename = "nbstat.nse" }
|
||||
Entry{ category = "safe", filename = "nbstat.nse" }
|
||||
Entry{ category = "version", filename = "iax2Detect.nse" }
|
||||
Entry{ category = "default", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "safe", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "discovery", filename = "rpcinfo.nse" }
|
||||
Entry{ category = "default", filename = "HTTP_open_proxy.nse" }
|
||||
Entry{ category = "discovery", filename = "HTTP_open_proxy.nse" }
|
||||
Entry{ category = "external", filename = "HTTP_open_proxy.nse" }
|
||||
Entry{ category = "intrusive", filename = "HTTP_open_proxy.nse" }
|
||||
Entry{ category = "demo", filename = "daytimeTest.nse" }
|
||||
Entry{ category = "intrusive", filename = "HTTPpasswd.nse" }
|
||||
Entry{ category = "vuln", filename = "HTTPpasswd.nse" }
|
||||
Entry{ category = "demo", filename = "showSSHVersion.nse" }
|
||||
Entry{ category = "default", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "discovery", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "safe", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "default", filename = "anonFTP.nse" }
|
||||
Entry{ category = "auth", filename = "anonFTP.nse" }
|
||||
Entry{ category = "intrusive", filename = "anonFTP.nse" }
|
||||
Entry{ category = "default", filename = "robots.nse" }
|
||||
Entry{ category = "safe", filename = "robots.nse" }
|
||||
Entry{ category = "default", filename = "finger.nse" }
|
||||
Entry{ category = "discovery", filename = "finger.nse" }
|
||||
Entry{ category = "default", filename = "UPnP-info.nse" }
|
||||
Entry{ category = "safe", filename = "UPnP-info.nse" }
|
||||
Entry{ category = "malware", filename = "strangeSMTPport.nse" }
|
||||
Entry{ category = "default", filename = "ircServerInfo.nse" }
|
||||
Entry{ category = "discovery", filename = "ircServerInfo.nse" }
|
||||
Entry{ category = "malware", filename = "ircZombieTest.nse" }
|
||||
Entry{ category = "discovery", filename = "ripeQuery.nse" }
|
||||
Entry{ category = "external", filename = "ripeQuery.nse" }
|
||||
Entry{ category = "demo", filename = "showHTTPVersion.nse" }
|
||||
Entry{ category = "version", filename = "PPTPversion.nse" }
|
||||
Entry{ category = "discovery", filename = "ASN.nse" }
|
||||
Entry{ category = "external", filename = "ASN.nse" }
|
||||
Entry{ category = "intrusive", filename = "brutePOP3.nse" }
|
||||
Entry{ category = "auth", filename = "brutePOP3.nse" }
|
||||
Entry{ category = "default", filename = "popcapa.nse" }
|
||||
Entry{ category = "intrusive", filename = "SNMPcommunitybrute.nse" }
|
||||
Entry{ category = "auth", filename = "SNMPcommunitybrute.nse" }
|
||||
Entry{ category = "discovery", filename = "whois.nse" }
|
||||
Entry{ category = "external", filename = "whois.nse" }
|
||||
Entry{ category = "safe", filename = "whois.nse" }
|
||||
Entry{ category = "external", filename = "dns-safe-recursion-txid.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-safe-recursion-txid.nse" }
|
||||
Entry{ category = "version", filename = "smb-enum.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enum.nse" }
|
||||
Entry{ category = "external", filename = "dns-safe-recursion-port.nse" }
|
||||
Entry{ category = "intrusive", filename = "dns-safe-recursion-port.nse" }
|
||||
Entry{ category = "version", filename = "smb-os-discovery.nse" }
|
||||
Entry{ category = "default", filename = "smb-os-discovery.nse" }
|
||||
Entry{ category = "version", filename = "smb-security-mode.nse" }
|
||||
|
||||
197
scripts/smb-enum.nse
Normal file
197
scripts/smb-enum.nse
Normal file
@@ -0,0 +1,197 @@
|
||||
--- Attempts to enumerate users and shares anonymously over SMB.
|
||||
--
|
||||
-- First, it logs in as the anonymous user and tries to connect to IPC$.
|
||||
-- If it is successful, it knows that Null sessions are enabled. If it
|
||||
-- is unsuccessful, it can still check for shares (because Windows is
|
||||
-- cool like that). A list of common shares is checked (see the 'shares'
|
||||
-- variable) to see what anonymous can access. Either a successful result
|
||||
-- is returned (has access), STATUS_ACCESS_DENIED is returned (exists but
|
||||
-- anonymous can't access), or STATUS_BAD_NETWORK_NAME is returned (doesn't
|
||||
-- exist).
|
||||
--
|
||||
-- Next, the Guest account is attempted with a blank password. If it's
|
||||
-- enabled, a message is displayed and shares that it has access to are
|
||||
-- checked the same as anonymous.
|
||||
--
|
||||
-- Finally, the Administrator account is attempted with a blank password.
|
||||
-- Because Administrator can't typically be locked out, this should be
|
||||
-- safe. That being said, it is possible to configure Administrator to
|
||||
-- be lockoutable, so watch out for that caveat. If you do lock yourself
|
||||
-- out of Administrator, there's a bootdisk that can help. :)
|
||||
--
|
||||
-- If Administrator has a blank password, it often doesn't allow remote
|
||||
-- logins, if this is the case, STATUS_ACCOUNT_RESTRICTION is returned
|
||||
-- instead of STATUS_ACCESS_DENIED, so we know the account has no password.
|
||||
--
|
||||
--@usage
|
||||
-- nmap --script smb-enum.nse -p445 127.0.0.1\n
|
||||
-- sudo nmap -sU -sS --script smb-enum.nse -p U:137,T:139 127.0.0.1\n
|
||||
--
|
||||
--@output
|
||||
-- Host script results:
|
||||
-- | SMB Enumeration:
|
||||
-- | Null sessions enabled
|
||||
-- | Anonymous shares found: IPC$
|
||||
-- | Restricted shares found: C$ TEST
|
||||
-- | Guest account is enabled
|
||||
-- | Guest can access: IPC$ TEST
|
||||
-- | Administrator account has a blank password
|
||||
-- |_ Administrator can access: IPC$ C$ TEST
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "SMB Enumeration"
|
||||
description = "Attempts to enumerate users and shares anonymously over SMB"
|
||||
author = "Ron Bowes"
|
||||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"version","intrusive"}
|
||||
|
||||
require 'smb'
|
||||
|
||||
-- Shares to try connecting to as Null session / GUEST
|
||||
local shares = {"IPC", "C", "D", "TEST", "SHARE", "HOME", "DFS", "COMCFG" }
|
||||
|
||||
hostrule = function(host)
|
||||
|
||||
local port = smb.get_port(host)
|
||||
|
||||
if(port == nil) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
--- Attempts to connect to a list of shares as the given UID, returning the
|
||||
-- shares that it has and doesn't have access to.
|
||||
--@param socket The socket to use
|
||||
--@param ip The ip address of the host
|
||||
--@param uid The UserID we're logged in as
|
||||
--@return (allowed_shares, denied_shares) Lists of shares we can and can't access,
|
||||
-- but all of which exist.
|
||||
function find_shares(socket, ip, uid)
|
||||
local i
|
||||
local allowed_shares = {}
|
||||
local denied_shares = {}
|
||||
|
||||
|
||||
for i = 1, #shares, 1 do
|
||||
|
||||
local share = string.format("\\\\%s\\%s", ip, shares[i])
|
||||
|
||||
status, tree_result = smb.tree_connect(socket, share, uid)
|
||||
if(status == false) then
|
||||
if(tree_result == 0xc0000022) then -- STATUS_ACCESS_DENIED
|
||||
denied_shares[#denied_shares + 1] = shares[i]
|
||||
end
|
||||
else
|
||||
allowed_shares[#allowed_shares + 1] = shares[i]
|
||||
end
|
||||
|
||||
share = share .. "$"
|
||||
status, tree_result = smb.tree_connect(socket, share, uid)
|
||||
if(status == false) then
|
||||
if(tree_result == 0xc0000022) then -- STATUS_ACCESS_DENIED
|
||||
denied_shares[#denied_shares + 1] = shares[i] .. "$"
|
||||
end
|
||||
else
|
||||
allowed_shares[#allowed_shares + 1] = shares[i] .. "$"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return allowed_shares, denied_shares
|
||||
end
|
||||
|
||||
--- Join strings together with a space.
|
||||
function string_join(table)
|
||||
local i
|
||||
local response = " "
|
||||
|
||||
for i = 1, #table, 1 do
|
||||
response = response .. table[i] .. " "
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local response = " \n"
|
||||
local status, socket, negotiate_result, session_result
|
||||
local allowed_shares, restricted_shares
|
||||
|
||||
status, socket = smb.start(host)
|
||||
if(status == false) then
|
||||
return "ERROR: " .. socket
|
||||
end
|
||||
|
||||
status, negotiate_result = smb.negotiate_protocol(socket)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return "ERROR: " .. negotiate_result
|
||||
end
|
||||
|
||||
-- Start up a null session
|
||||
status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities'])
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return "ERROR: " .. session_result
|
||||
end
|
||||
|
||||
-- Check if null session has access to IPC$
|
||||
status, result = smb.tree_connect(socket, "IPC$", session_result['uid'])
|
||||
if(status == true) then
|
||||
response = response .. "Null sessions enabled\n"
|
||||
end
|
||||
|
||||
-- Find shares
|
||||
allowed_shares, restricted_shares = find_shares(socket, host.ip, session_result['uid'])
|
||||
|
||||
-- Display shares the Null user had access to
|
||||
if(#allowed_shares > 0) then
|
||||
response = response .. "Anonymous shares found: " .. string_join(allowed_shares) .. "\n"
|
||||
end
|
||||
|
||||
-- Display shares the Null user didn't have access to
|
||||
if(#restricted_shares > 0) then
|
||||
response = response .. "Restricted shares found: " .. string_join(restricted_shares) .. "\n"
|
||||
end
|
||||
|
||||
-- Check if Guest can log in
|
||||
status, session_result = smb.start_session(socket, "GUEST", negotiate_result['session_key'], negotiate_result['capabilities'])
|
||||
if(status == true) then
|
||||
response = response .. "Guest account is enabled\n"
|
||||
|
||||
-- Find shares for Guest
|
||||
allowed_shares, restricted_shares = find_shares(socket, host.ip, session_result['uid'])
|
||||
|
||||
-- Display shares Guest had access to
|
||||
if(#allowed_shares > 0) then
|
||||
response = response .. "Guest can access: " .. string_join(allowed_shares) .. "\n"
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if Administrator has a blank password
|
||||
-- (we check Administrator and not other accounts because Administrator can't generally be locked out)
|
||||
status, session_result = smb.start_session(socket, "ADMINISTRATOR", negotiate_result['session_key'], negotiate_result['capabilities'])
|
||||
if(status == true) then
|
||||
response = response .. "Administrator account has a blank password\n"
|
||||
|
||||
-- Find shares for Administrator
|
||||
allowed_shares, restricted_shares = find_shares(socket, host.ip, session_result['uid'])
|
||||
|
||||
-- Display shares administrator had access to
|
||||
if(#allowed_shares > 0) then
|
||||
response = response .. "Administrator can access: " .. string_join(allowed_shares) .. "\n"
|
||||
end
|
||||
elseif(session_result == 0xc000006e) then -- STATUS_ACCOUNT_RESTRICTION
|
||||
response = response .. "Administrator account has a blank password, but can't use SMB\n"
|
||||
end
|
||||
|
||||
|
||||
|
||||
smb.stop(socket)
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
80
scripts/smb-os-discovery.nse
Normal file
80
scripts/smb-os-discovery.nse
Normal file
@@ -0,0 +1,80 @@
|
||||
--- Attempts to determine the operating system over SMB protocol (ports 445 and 139).
|
||||
-- See nselib/smb.lua for more information on this protocol.
|
||||
--
|
||||
--@usage
|
||||
-- nmap --script smb-os-discovery.nse -p445 127.0.0.1\n
|
||||
-- sudo nmap -sU -sS --script smb-os-discovery.nse -p U:137,T:139 127.0.0.1\n
|
||||
--
|
||||
--@output
|
||||
-- | OS from SMB: Windows 2000
|
||||
-- | LAN Manager: Windows 2000 LAN Manager
|
||||
-- | Name: WORKGROUP\TEST1
|
||||
-- |_ System time: 2008-09-09 20:55:55 UTC-5
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "OS from SMB"
|
||||
description = "Attempts to determine the operating system over the SMB protocol (ports 445 and 139)."
|
||||
author = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"version","default"}
|
||||
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
--- Check whether or not this script should be run.
|
||||
hostrule = function(host)
|
||||
|
||||
local port = smb.get_port(host)
|
||||
|
||||
if(port == nil) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Converts numbered Windows versions (5.0, 5.1) to the names (Windows 2000, Windows XP).
|
||||
--@param os The name of the OS
|
||||
--@return The actual name of the OS (or the same as the 'os' parameter)
|
||||
function get_windows_version(os)
|
||||
|
||||
if(os == "Windows 5.0") then
|
||||
return "Windows 2000"
|
||||
elseif(os == "Windows 5.1")then
|
||||
return "Windows XP"
|
||||
end
|
||||
|
||||
return os
|
||||
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
status, socket = smb.start(host)
|
||||
|
||||
if(status == false) then
|
||||
return "Error: " .. socket
|
||||
end
|
||||
|
||||
status, negotiate_result = smb.negotiate_protocol(socket)
|
||||
|
||||
if(status == false) then
|
||||
stdnse.print_debug(2, "Negotiate session failed")
|
||||
smb.stop(socket)
|
||||
return "Error: " .. negotiate_result
|
||||
end
|
||||
|
||||
status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities'])
|
||||
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return "Error: " .. session_result
|
||||
end
|
||||
|
||||
smb.stop(socket)
|
||||
return string.format("%s\nLAN Manager: %s\nName: %s\\%s\nSystem time: %s %s\n", get_windows_version(session_result['os']), session_result['lanmanager'], negotiate_result['domain'], negotiate_result['server'], negotiate_result['date'], negotiate_result['timezone_str'])
|
||||
end
|
||||
|
||||
|
||||
112
scripts/smb-security-mode.nse
Normal file
112
scripts/smb-security-mode.nse
Normal file
@@ -0,0 +1,112 @@
|
||||
--- Returns information about the SMB security level determined by SMB.
|
||||
--
|
||||
-- Here is how to interpret the output:
|
||||
--
|
||||
-- User-level security: Each user has a separate username/password that is used
|
||||
-- to log into the system. This is the default setup of pretty much everything
|
||||
-- these days.
|
||||
-- Share-level security: The anonymous account should be used to log in, then
|
||||
-- the password is given (in plaintext) when a share is accessed. All users who
|
||||
-- have access to the share use this password. This was the original way of doing
|
||||
-- things, but isn't commonly seen, now. If a server uses share-level security,
|
||||
-- it is vulnerable to sniffing.
|
||||
--
|
||||
-- Challenge/response passwords: If enabled, the server can accept any type of
|
||||
-- password:
|
||||
-- * Plaintext
|
||||
-- * LM and NTLM
|
||||
-- * LMv2 and NTLMv2
|
||||
-- If it isn't set, the server can only accept plaintext passwords. Most servers
|
||||
-- are configured to use challenge/response these days. If a server is configured
|
||||
-- to accept plaintext passwords, it is vulnerable to sniffing.
|
||||
--
|
||||
-- Message signing: If required, all messages between the client and server must
|
||||
-- sign be signed by a shared key, derived from the password and the server
|
||||
-- challenge. If supported and not required, message signing is negotiated between
|
||||
-- clients and servers and used if both support and request it. By default, Windows clients
|
||||
-- don't sign messages, so if message signing isn't required by the server, messages
|
||||
-- probably won't be signed; additionally, if performing a man-in-the-middle attack,
|
||||
-- an attacker can negotiate no message signing. If message signing isn't required, the
|
||||
-- server is vulnerable to man-in-the-middle attacks.
|
||||
--
|
||||
-- See nselib/smb.lua for more information on the protocol itself.
|
||||
--
|
||||
--@usage
|
||||
-- nmap --script smb-security-mide.nse -p445 127.0.0.1\n
|
||||
-- sudo nmap -sU -sS --script smb-security-mide.nse -p U:137,T:139 127.0.0.1\n
|
||||
--
|
||||
--@output
|
||||
-- | SMB Security: User-level authentication
|
||||
-- | SMB Security: Challenge/response passwords supported
|
||||
-- |_ SMB Security: Message signing supported
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "SMB Security"
|
||||
description = "Attempts to determine the security mode over the SMB protocol (ports 445 and 139)."
|
||||
author = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"version"}
|
||||
|
||||
require 'smb'
|
||||
|
||||
--- Check whether or not this script should be run.
|
||||
hostrule = function(host)
|
||||
|
||||
local port = smb.get_port(host)
|
||||
|
||||
if(port == nil) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
action = function(host)
|
||||
|
||||
local status, socket = smb.start(host)
|
||||
|
||||
if(status == false) then
|
||||
return "Error: " .. socket
|
||||
end
|
||||
|
||||
status, result = smb.negotiate_protocol(socket)
|
||||
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return "Error: " .. result
|
||||
end
|
||||
|
||||
local security_mode = result['security_mode']
|
||||
local response = ""
|
||||
|
||||
-- User-level authentication or share-level authentication
|
||||
if(bit.band(security_mode, 1) == 1) then
|
||||
response = response .. "User-level authentication\n"
|
||||
else
|
||||
response = response .. " Share-level authentication\n"
|
||||
end
|
||||
|
||||
-- Challenge/response supported?
|
||||
if(bit.band(security_mode, 2) == 0) then
|
||||
response = response .. "SMB Security: Plaintext only\n"
|
||||
else
|
||||
response = response .. "SMB Security: Challenge/response passwords supported\n"
|
||||
end
|
||||
|
||||
-- Message signing supported/required?
|
||||
if(bit.band(security_mode, 8) == 8) then
|
||||
response = response .. "SMB Security: Message signing required\n"
|
||||
elseif(bit.band(security_mode, 4) == 4) then
|
||||
response = response .. "SMB Security: Message signing supported\n"
|
||||
else
|
||||
response = response .. "SMB Security: Message signing not supported\n"
|
||||
end
|
||||
|
||||
smb.stop(socket)
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user