mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Add a new msrpc.lua module, plus new scripts smb-enumdomains.nse,
smb-enumshares.nse, and smb-enumusers.nse. Also enhance the netbios.lua and smb.lua modules. Remove the smb-enum.nse script. All these changes are from Ron Bowes.
This commit is contained in:
12
CHANGELOG
12
CHANGELOG
@@ -55,11 +55,15 @@ o Enhanced the AS Numbers script (ASN.nse) to better consolidate
|
||||
|
||||
o Made DNS timeouts in NSE dependent on the timing template [Jah]
|
||||
|
||||
o Added two new nselib modules, netbios and smb, that contain common
|
||||
code for scripts using NetBIOS and SMB. Also added or updated four
|
||||
scripts that use the new modules:
|
||||
o Added three new nselib modules: msrpc, netbios, and smb. As the
|
||||
names suggest, they contain common code for scripts using MSRPC,
|
||||
NetBIOS, and SMB. These modules allow scripts to extract a great
|
||||
deal of information from hosts running Windows, particularly Windows
|
||||
2000. New or updated scripts using the modules are:
|
||||
nbstat.nse: get NetBIOS names and MAC address.
|
||||
smb-enum.nse: enumerate SMB users and shares.
|
||||
smb-enumdomain.nse: enumerate domains and policies.
|
||||
smb-enumshares.nse: enumerate network shares.
|
||||
smb-enumusers.nse: enumerate users and information about them.
|
||||
smb-os-discovery.nse: get operating system over SMB (replaces
|
||||
netbios-smb-os-discovery.nse).
|
||||
smb-security-mode.nse: determine if a host uses user-level or
|
||||
|
||||
1695
nselib/msrpc.lua
Normal file
1695
nselib/msrpc.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -291,8 +291,8 @@ function do_nbstat(host)
|
||||
return false, result
|
||||
end
|
||||
|
||||
status, err = socket:close()
|
||||
if(status == false) then
|
||||
close_status, err = socket:close()
|
||||
if(close_status == false) then
|
||||
return false, err
|
||||
end
|
||||
|
||||
|
||||
1015
nselib/smb.lua
1015
nselib/smb.lua
File diff suppressed because it is too large
Load Diff
@@ -1,22 +1,25 @@
|
||||
--- 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.
|
||||
-- thinks it owns. \n
|
||||
--
|
||||
--@usage
|
||||
-- sudo nmap -sU --script nbstat.nse -p137 <host>\n
|
||||
--\n
|
||||
-- @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>
|
||||
-- (no verbose)\n
|
||||
-- |_ NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28\n
|
||||
--\n
|
||||
-- (verbose)\n
|
||||
-- | NBSTAT: NetBIOS name: TEST1, NetBIOS user: RON, NetBIOS MAC: 00:0c:29:f9:d9:28\n
|
||||
-- | Name: TEST1<00> Flags: <unique><active>\n
|
||||
-- | Name: TEST1<20> Flags: <unique><active>\n
|
||||
-- | Name: WORKGROUP<00> Flags: <group><active>\n
|
||||
-- | Name: TEST1<03> Flags: <unique><active>\n
|
||||
-- | Name: WORKGROUP<1e> Flags: <group><active>\n
|
||||
-- | Name: RON<03> Flags: <unique><active>\n
|
||||
-- | Name: WORKGROUP<1d> Flags: <unique><active>\n
|
||||
-- |_ Name: \x01\x02__MSBROWSE__\x02<01> Flags: <group><active>\n
|
||||
|
||||
id = "NBSTAT"
|
||||
description = "Sends a NetBIOS query to target host to try to determine \
|
||||
|
||||
@@ -63,6 +63,8 @@ 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 = "discovery", filename = "ASN.nse" }
|
||||
Entry{ category = "external", filename = "ASN.nse" }
|
||||
Entry{ category = "default", filename = "robots.nse" }
|
||||
Entry{ category = "safe", filename = "robots.nse" }
|
||||
Entry{ category = "default", filename = "finger.nse" }
|
||||
@@ -77,8 +79,6 @@ 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" }
|
||||
@@ -89,12 +89,16 @@ 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 = "discovery", 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 = "discovery", filename = "smb-security-mode.nse" }
|
||||
Entry{ category = "safe", filename = "smb-security-mode.nse" }
|
||||
Entry{ category = "default", filename = "smb-os-discovery.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-os-discovery.nse" }
|
||||
Entry{ category = "safe", filename = "smb-os-discovery.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-security-mode.nse" }
|
||||
Entry{ category = "safe", filename = "smb-security-mode.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enumusers.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enumusers.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enumshares.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enumshares.nse" }
|
||||
Entry{ category = "discovery", filename = "smb-enumdomains.nse" }
|
||||
Entry{ category = "intrusive", filename = "smb-enumdomains.nse" }
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
--- 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 = {"discovery", "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
|
||||
|
||||
|
||||
182
scripts/smb-enumdomains.nse
Normal file
182
scripts/smb-enumdomains.nse
Normal file
@@ -0,0 +1,182 @@
|
||||
--- Attempts to enumerate domains on a system, along with their policies. This will likely
|
||||
-- only work without credentials against Windows 2000. \n
|
||||
-- \n
|
||||
-- After the initial bind() to SAMR, the sequence of calls is:\n
|
||||
-- Connect4() -- get a connect_handle\n
|
||||
-- EnumDomains() -- get a list of the domains (stop here if you just want the names)\n
|
||||
-- QueryDomain() -- get the sid for the domain\n
|
||||
-- OpenDomain() -- get a handle for each domain\n
|
||||
-- QueryDomainInfo2() -- get the domain information\n
|
||||
--
|
||||
--@usage
|
||||
-- nmap --script smb-enumdomains.nse -p445 <host>\n
|
||||
-- sudo nmap -sU -sS --script smb-enumdomains.nse -p U:137,T:139 <host>\n
|
||||
--
|
||||
--@output
|
||||
-- Host script results:
|
||||
-- | MSRPC: List of domains:\n
|
||||
-- | Domain: TEST1\n
|
||||
-- | |_ SID: S-1-5-21-1060284298-842925246-839522115\n
|
||||
-- | |_ Users: Administrator, ASPNET, Guest, Ron, test\n
|
||||
-- | |_ Creation time: 2006-10-17 15:35:07\n
|
||||
-- | |_ Min password length: 0 characters\n
|
||||
-- | |_ Max password age: 10675199 days\n
|
||||
-- | |_ Min password age: 0 days\n
|
||||
-- | |_ Password history length: 0 passwords\n
|
||||
-- | |_ Lockout threshold: 0 login attempts\n
|
||||
-- | |_ Lockout duration: 60 minutes\n
|
||||
-- | |_ Lockout window: 60 minutes\n
|
||||
-- | |_ Password properties: \n
|
||||
-- | |_ Password complexity requirements do not exist\n
|
||||
-- |_ |_ Administrator account cannot be locked out\n
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "MSRPC: List of domains"
|
||||
description = "Tries calling the EnumDomains() and QueryDomainInfo2() RPC function to obtain a list of domains/policies."
|
||||
author = "Ron Bowes"
|
||||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery","intrusive"}
|
||||
|
||||
require 'msrpc'
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
hostrule = function(host)
|
||||
|
||||
local port = smb.get_port(host)
|
||||
|
||||
if(port == nil) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local response = " \n"
|
||||
local status, socket
|
||||
local uid, tid, fid
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
if(status == false) then
|
||||
return "ERROR: " .. socket
|
||||
end
|
||||
|
||||
-- Bind to SAMR service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. bind_result
|
||||
end
|
||||
|
||||
-- Call connect4()
|
||||
status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. connect4_result
|
||||
end
|
||||
|
||||
-- Save the connect_handle
|
||||
connect_handle = connect4_result['connect_handle']
|
||||
|
||||
-- Call EnumDomains()
|
||||
status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. enumdomains_result
|
||||
end
|
||||
|
||||
-- If no domanis were returned, print an error (I don't expect this will actually happen)
|
||||
if(#enumdomains_result['domains'] == 0) then
|
||||
return "ERROR: Couldn't find any domains to check"
|
||||
end
|
||||
|
||||
for i = 1, #enumdomains_result['domains'], 1 do
|
||||
|
||||
local domain = enumdomains_result['domains'][i]
|
||||
-- We don't care about the 'builtin' domain
|
||||
if(domain ~= 'Builtin') then
|
||||
local sid
|
||||
local domain_handle
|
||||
|
||||
-- Call LookupDomain()
|
||||
status, lookupdomain_result = msrpc.samr_lookupdomain(socket, connect_handle, domain, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. lookupdomain_result
|
||||
end
|
||||
|
||||
-- Save the sid
|
||||
sid = lookupdomain_result['sid']
|
||||
|
||||
-- Call OpenDomain()
|
||||
status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. opendomain_result
|
||||
end
|
||||
|
||||
-- Save the domain handle
|
||||
domain_handle = opendomain_result['domain_handle']
|
||||
|
||||
-- Call QueryDomainInfo2() to get domain properties. We call these for three types == 1, 8, and 12, since those return
|
||||
-- the most useful information.
|
||||
status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 1, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. querydomaininfo2_result
|
||||
end
|
||||
status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 8, uid, tid, fid, querydomaininfo2_result)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. querydomaininfo2_result
|
||||
end
|
||||
status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(socket, domain_handle, 12, uid, tid, fid, querydomaininfo2_result)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. querydomaininfo2_result
|
||||
end
|
||||
|
||||
-- Call EnumDomainUsers() to get users
|
||||
status, enumdomainusers_result = msrpc.samr_enumdomainusers(socket, domain_handle, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. enumdomainusers_result
|
||||
end
|
||||
|
||||
-- Close the domain handle
|
||||
msrpc.samr_close(socket, domain_handle, uid, tid, fid)
|
||||
|
||||
-- Finally, fill in the response!
|
||||
response = response .. string.format("Domain: %s\n", domain)
|
||||
response = response .. string.format(" |_ SID: %s\n", msrpc.sid_to_string(lookupdomain_result['sid']))
|
||||
response = response .. string.format(" |_ Users: %s\n", stdnse.strjoin(", ", enumdomainusers_result['names']))
|
||||
response = response .. string.format(" |_ Creation time: %s\n", querydomaininfo2_result['create_date'])
|
||||
response = response .. string.format(" |_ Min password length: %d characters\n", querydomaininfo2_result['min_password_length'])
|
||||
response = response .. string.format(" |_ Max password age: %d days\n", querydomaininfo2_result['max_password_age'])
|
||||
response = response .. string.format(" |_ Min password age: %d days\n", querydomaininfo2_result['min_password_age'])
|
||||
response = response .. string.format(" |_ Password history length: %d passwords\n", querydomaininfo2_result['password_history_length'])
|
||||
response = response .. string.format(" |_ Lockout threshold: %d login attempts\n", querydomaininfo2_result['lockout_threshold'])
|
||||
response = response .. string.format(" |_ Lockout duration: %d minutes\n", querydomaininfo2_result['lockout_duration'])
|
||||
response = response .. string.format(" |_ Lockout window: %d minutes\n", querydomaininfo2_result['lockout_window'])
|
||||
if(#querydomaininfo2_result['password_properties_list'] > 0) then
|
||||
response = response .. " |_ Password properties: \n |_ " .. stdnse.strjoin("\n |_ ", querydomaininfo2_result['password_properties_list']) .. "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Close the connect handle
|
||||
msrpc.samr_close(socket, connect_handle, uid, tid, fid)
|
||||
|
||||
-- Close the SMB session
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
|
||||
return response
|
||||
|
||||
end
|
||||
|
||||
|
||||
179
scripts/smb-enumshares.nse
Normal file
179
scripts/smb-enumshares.nse
Normal file
@@ -0,0 +1,179 @@
|
||||
--- Attempts to call the srvsvc.NetShareEnumAll() MSRPC function. This will
|
||||
-- likely only work anonymously against Windows 2000. \n
|
||||
--\n
|
||||
-- There isn't a whole lot to say about this one. The sequence of calls after
|
||||
-- the initial bind() is:\n
|
||||
-- NetShareEnumAll()\n
|
||||
--\n
|
||||
-- Since NetShareEnumAll() only works anonymously, if it fails this will check
|
||||
-- a handful of common shares. \n
|
||||
--\n
|
||||
-- Once it has a list of shares, whether it was pulled over MSRPC or guessed,
|
||||
-- we attempt to connect to each of them with a standard smb tree_connect request
|
||||
-- over a null session. We record which ones succeeded and failed (that is, which
|
||||
-- shares allowed for anonymous access).\n
|
||||
--
|
||||
--@usage
|
||||
-- nmap --script smb-enumshares.nse -p445 <host>\n
|
||||
-- sudo nmap -sU -sS --script smb-enumshares.nse -p U:137,T:139 <host>\n
|
||||
--
|
||||
--@output
|
||||
-- Host script results:\n
|
||||
-- TODO
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "MSRPC: NetShareEnumAll()"
|
||||
description = "Tries calling the NetShareEnumAll() RPC function, and guessing shares"
|
||||
author = "Ron Bowes"
|
||||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery","intrusive"}
|
||||
|
||||
require 'msrpc'
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
hostrule = function(host)
|
||||
|
||||
local port = smb.get_port(host)
|
||||
|
||||
if(port == nil) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---Attempts to enumerate the shares on a remote system using MSRPC calls. This will likely fail
|
||||
-- against a modern system, but will succeed against Windows 2000.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@return (status, result) If status is false, result is an error string. Otherwise, result is
|
||||
-- a list of all shares on a system.
|
||||
local function samr_enum_shares(host)
|
||||
|
||||
local status, socket, uid, tid, fid
|
||||
local bind_result, netshareenumall_result
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
end
|
||||
|
||||
-- Bind to SRVSVC service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil, uid, tid, fid)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call netsharenumall
|
||||
status, netshareenumall_result = msrpc.srvsvc_netshareenumall(socket, host.ip, uid, tid, fid)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return false, netshareenumall_result
|
||||
end
|
||||
|
||||
-- Stop the SMB session
|
||||
smb.stop(socket, uid, tid)
|
||||
|
||||
return true, netshareenumall_result['shares']
|
||||
end
|
||||
|
||||
---Attempts to connect to a list of shares as the anonymous user, returning which ones
|
||||
-- it has and doesn't have access to.
|
||||
--
|
||||
--@param host The host object
|
||||
--@param shares An array of shares to check
|
||||
--@return (allowed_shares, denied_shares) Lists of shares we can and can't access,
|
||||
-- but all of which exist.
|
||||
function check_shares(host, shares)
|
||||
local i
|
||||
local allowed_shares = {}
|
||||
local denied_shares = {}
|
||||
|
||||
-- Begin the SMB session
|
||||
status, socket = smb.start(host)
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
end
|
||||
|
||||
-- Negotiate the protocol
|
||||
status, negotiate_result = smb.negotiate_protocol(socket)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return false, 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 false, session_result
|
||||
end
|
||||
|
||||
-- Connect to the shares
|
||||
stdnse.print_debug(2, "EnumShares: Testing %d shares", #shares)
|
||||
for i = 1, #shares, 1 do
|
||||
|
||||
-- Change the share to the '\\ip\share' format
|
||||
local share = string.format("\\\\%s\\%s", host.ip, shares[i])
|
||||
|
||||
-- Try connecting to the tree
|
||||
stdnse.print_debug(3, "EnumShares: Testing share %s", share)
|
||||
status, tree_result = smb.tree_connect(socket, share, session_result['uid'])
|
||||
-- If it fails, checkwhy
|
||||
if(status == false) then
|
||||
-- If the result was ACCESS_DENIED, record it
|
||||
if(tree_result == 0xc0000022 or tree_result == 'NT_STATUS_ACCESS_DENIED') then
|
||||
stdnse.print_debug(3, "EnumShares: Access was denied")
|
||||
denied_shares[#denied_shares + 1] = shares[i]
|
||||
else
|
||||
stdnse.print_debug(3, "EnumShares: Share didn't pan out: %s", tree_result)
|
||||
end
|
||||
else
|
||||
-- Add it to allowed shares
|
||||
stdnse.print_debug(3, "EnumShares: Access was granted")
|
||||
allowed_shares[#allowed_shares + 1] = shares[i]
|
||||
smb.tree_disconnect(socket, session_result['uid'], tree_result['tid'])
|
||||
end
|
||||
end
|
||||
|
||||
-- Log off the user
|
||||
smb.stop(socket, session_result['uid'])
|
||||
|
||||
return allowed_shares, denied_shares
|
||||
end
|
||||
|
||||
|
||||
action = function(host)
|
||||
local result, shared
|
||||
local response = " \n"
|
||||
local shares = {}
|
||||
local allowed, denied
|
||||
|
||||
-- Try and do this the good way, make a MSRPC call to get the shares
|
||||
result, shares = samr_enum_shares(host)
|
||||
|
||||
-- If that failed, try doing it with brute force. This almost certainly won't find everything, but it's the
|
||||
-- best we can do.
|
||||
if(result == false) then
|
||||
response = response .. string.format("Couldn't enum all shares, checking for common ones (%s)\n", shares)
|
||||
-- Take some common share names I've seen
|
||||
shares = {"IPC$", "ADMIN$", "TEST", "TEST$", "HOME", "HOME$"}
|
||||
-- Try every alphabetic share, with and without a trailing '$'
|
||||
for i = string.byte("A", 1), string.byte("Z", 1), 1 do
|
||||
shares[#shares + 1] = string.char(i)
|
||||
shares[#shares + 1] = string.char(i) .. "$"
|
||||
end
|
||||
end
|
||||
|
||||
-- Break them into anonymous/authenticated shares
|
||||
allowed, denied = check_shares(host, shares)
|
||||
|
||||
return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", allowed), stdnse.strjoin(", ", denied))
|
||||
end
|
||||
|
||||
|
||||
384
scripts/smb-enumusers.nse
Normal file
384
scripts/smb-enumusers.nse
Normal file
@@ -0,0 +1,384 @@
|
||||
--- Attempts to enumerate the users on a remote Windows system, with as much information as possible,
|
||||
-- through a variety of techniques (over SMB + MSRPC, which uses port 445 or 139). \n
|
||||
--\n
|
||||
-- Will first attempt to call the QueryDisplayInfo() MSRPC function. If NULL sessions are enabled,
|
||||
-- this will succeed and pull back a detailed list of users. Unfortunately, this likely won't succeed
|
||||
-- unless we're scanning Windows 2000. When this test is performed, the following MSRPC functions
|
||||
-- are called:\n
|
||||
--\n
|
||||
-- Bind() -- bind to the SAMR service\n
|
||||
-- Connect4() -- get a connect_handle\n
|
||||
-- EnumDomains() -- get a list of the domains\n
|
||||
-- QueryDomain() -- get the sid for the domain\n
|
||||
-- OpenDomain() -- get a handle for each domain\n
|
||||
-- QueryDisplayInfo() -- get the list of users in the domain\n
|
||||
-- Close() -- Close the domain handle\n
|
||||
-- Close() -- Close the connect handle\n
|
||||
--\n
|
||||
-- Credit goes out to the enum.exe program, the code I wrote for this is largely due to packetlogs
|
||||
-- I took of its operations. \n
|
||||
--\n
|
||||
-- Regardless of whether or not this succeeds, a second technique is used to pull user accounts.
|
||||
-- This one is apparently successful against more machines, although I haven't found a machine
|
||||
-- that this only works against. However, I did find that this will turn up more users for certain
|
||||
-- systems (although I haven't figured out why). \n
|
||||
-- \n
|
||||
-- Each user on a Windows system has an RID. The RID of 500 is the Administrator account (even if
|
||||
-- it's renamed), 501 is the Guest account, and 1000+ are the user accounts. This technique, which
|
||||
-- was originally used in the sid2user/user2sid programs, will attempt to convert common RID numbers
|
||||
-- to names to discover users. \n
|
||||
-- \n
|
||||
-- First, the SID of the server has to be determined. This is done by looking up any name present on
|
||||
-- the server using a technique like user2sid. For this code, we try and convert as many names as we
|
||||
-- can find -- all we need is one valid name for this to succeed. In this code, I use:\n
|
||||
-- - The computer name / domain name, returned in SMB_COM_NEGOTIATE\n
|
||||
-- - An nbstat query to get the server name and the currently loggeed in user\n
|
||||
-- - Some common names ("administrator", "guest", and "test")\n
|
||||
--\n
|
||||
-- In theory, the computer name should be sufficient for this to always work, and the rest of the \n
|
||||
-- names are in there for good measure. \n
|
||||
--\n
|
||||
-- Once that's completed, the RIDs 500 - 505 are requested, and any responses are displayed. Then,
|
||||
-- starting at 1000, we take small groups of RIDs which are requestd. I break them into
|
||||
-- smaller groups because if too many are requested at once, we get a STATUS_BUFFER_OVERFLOW
|
||||
-- error. We try every RID up to 1100, then, as soon as we get an empty group (5 RIDs in a row
|
||||
-- without a result), we stop. \n
|
||||
--\n
|
||||
-- It might be a good idea to modify this, in the future, with some more intelligence. For example,
|
||||
-- have it run until it get 5 groups in a row with no results instead of going up to 1100. I
|
||||
-- performed a test on an old server we have here with a lot of accounts, and I got these results:
|
||||
-- 500, 501, 1000, 1030, 1031, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063,
|
||||
-- 1064, 1065, 1066, 1067, 1070, 1075, 1081, 1088, 1090. The jump from 1000 to 1030 is quite large
|
||||
-- and can easily result in missing accounts.\n
|
||||
--\n
|
||||
-- The disadvantage of using the user2sid/sid2user technique is that less information is returned
|
||||
-- about the user. \n
|
||||
--\n
|
||||
-- The names and details from both of these techniques are merged and displayed. If the output is
|
||||
-- verbose, then as many details as possible are displayed, otherwise only the list of usernames
|
||||
-- are displayed. The names are ordered alphabetically.\n
|
||||
--
|
||||
--@usage
|
||||
-- nmap --script smb-enumusers.nse -p445 <host>\n
|
||||
-- sudo nmap -sU -sS --script smb-enumusers.nse -p U:137,T:139 <host>\n
|
||||
--
|
||||
--@output
|
||||
-- TODO
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "MSRPC: List of user accounts"
|
||||
description = "Tries calling SAMR and LSA functions to get a list of user accounts."
|
||||
author = "Ron Bowes"
|
||||
copyright = "Ron Bowes"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery","intrusive"}
|
||||
|
||||
require 'msrpc'
|
||||
require 'smb'
|
||||
require 'stdnse'
|
||||
|
||||
hostrule = function(host)
|
||||
|
||||
local port = smb.get_port(host)
|
||||
|
||||
if(port == nil) then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
---Attempt to enumerate users through SAMR methods. See the file description for more information.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@return (status, result) If status is false, result is an error message. Otherwise, result is an
|
||||
-- array of tables. Each table contains a 'name', 'domain', 'fullname', 'rid', and 'description'.
|
||||
local function enum_samr(host)
|
||||
|
||||
local bind_result, connect4_result, enumdomains_result
|
||||
local connect_handle
|
||||
local status, socket
|
||||
local uid, tid, fid
|
||||
local response = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
end
|
||||
|
||||
-- Bind to SAMR service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call connect4()
|
||||
status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, connect4_result
|
||||
end
|
||||
|
||||
-- Save the connect_handle
|
||||
connect_handle = connect4_result['connect_handle']
|
||||
|
||||
-- Call EnumDomains()
|
||||
status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, enumdomains_result
|
||||
end
|
||||
|
||||
-- If no domains were returned, go back with an error
|
||||
if(#enumdomains_result['domains'] == 0) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, "Couldn't find any domains"
|
||||
end
|
||||
|
||||
for i = 1, #enumdomains_result['domains'], 1 do
|
||||
|
||||
local domain = enumdomains_result['domains'][i]
|
||||
-- We don't care about the 'builtin' domain
|
||||
if(domain ~= 'Builtin') then
|
||||
local sid
|
||||
local domain_handle
|
||||
local opendomain_result, querydisplayinfo_result
|
||||
|
||||
-- Call LookupDomain()
|
||||
status, lookupdomain_result = msrpc.samr_lookupdomain(socket, connect_handle, domain, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, lookupdomain_result
|
||||
end
|
||||
|
||||
-- Save the sid
|
||||
sid = lookupdomain_result['sid']
|
||||
|
||||
-- Call OpenDomain()
|
||||
status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, opendomain_result
|
||||
end
|
||||
|
||||
-- Save the domain handle
|
||||
domain_handle = opendomain_result['domain_handle']
|
||||
|
||||
-- Call QueryDisplayInfo()
|
||||
status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(socket, domain_handle, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, querydisplayinfo_result
|
||||
end
|
||||
|
||||
-- Close the domain handle
|
||||
msrpc.samr_close(socket, domain_handle, uid, tid, fid)
|
||||
|
||||
-- Finally, fill in the response!
|
||||
for i = 1, #querydisplayinfo_result['details'], 1 do
|
||||
querydisplayinfo_result['details'][i]['domain'] = domain
|
||||
response[#response + 1] = querydisplayinfo_result['details'][i]
|
||||
end
|
||||
end -- Checking for 'builtin'
|
||||
end -- Domain loop
|
||||
|
||||
-- Close the connect handle
|
||||
msrpc.samr_close(socket, connect_handle, uid, tid, fid)
|
||||
|
||||
-- Stop the SAMR SMB
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
---Attempt to enumerate users through LSA methods. See the file description for more information.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@return (status, result) If status is false, result is an error message. Otherwise, result is an
|
||||
-- array of tables. Each table contains a 'name', 'domain', and 'rid'.
|
||||
local function enum_lsa(host)
|
||||
|
||||
local status, socket
|
||||
local uid, tid, fid
|
||||
local response = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid, negotiate_result = msrpc.start_smb(host, msrpc.LSA_PATH)
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
end
|
||||
|
||||
-- Bind to LSA service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Open the LSA policy
|
||||
status, openpolicy2_result = msrpc.lsa_openpolicy2(socket, host.ip, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, openpolicy2_result
|
||||
end
|
||||
|
||||
-- Start with some common names, as well as the name returned by the negotiate call
|
||||
names = {"administrator", "guest", "test", negotiate_result['domain'], negotiate_result['server'] }
|
||||
|
||||
-- Get the server's name from nbstat
|
||||
local result, server_name = netbios.get_server_name(host.ip)
|
||||
if(result == true) then
|
||||
names[#names + 1] = server_name
|
||||
end
|
||||
|
||||
-- Get the logged in user from nbstat
|
||||
local result, user_name = netbios.get_user_name(host.ip)
|
||||
if(result == true) then
|
||||
names[#names + 1] = user_name
|
||||
end
|
||||
|
||||
-- Look up the names, if any are valid than the server's SID will be returned
|
||||
status, lookupnames2_result = msrpc.lsa_lookupnames2(socket, openpolicy2_result['policy_handle'], names, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, lookupnames2_result
|
||||
end
|
||||
|
||||
-- Loop through the domains returned and find teh users in each
|
||||
for i = 1, #lookupnames2_result['domains'], 1 do
|
||||
local domain = lookupnames2_result['domains'][i]['name']
|
||||
local sid = lookupnames2_result['domains'][i]['sid']
|
||||
local rids = { }
|
||||
local start = 1000
|
||||
|
||||
-- Start by looking up 500 - 505 (will likely be Administrator + guest)
|
||||
for j = 500, 505, 1 do
|
||||
rids[#rids + 1] = j
|
||||
end
|
||||
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(socket, openpolicy2_result['policy_handle'], sid, rids, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, lookupsids2_result
|
||||
end
|
||||
|
||||
-- Put the details for each name into an array
|
||||
for j = 1, #lookupsids2_result['details'], 1 do
|
||||
if(lookupsids2_result['details'][j]['name'] ~= nil) then
|
||||
local result = {}
|
||||
result['name'] = lookupsids2_result['details'][j]['name']
|
||||
result['rid'] = 500 + j - 1
|
||||
result['domain'] = domain
|
||||
response[#response + 1] = result
|
||||
end
|
||||
end
|
||||
|
||||
-- Now do groups of 5 users, until we get past 1100 and have an empty group
|
||||
repeat
|
||||
rids = {}
|
||||
for j = start, start + 4, 1 do
|
||||
rids[#rids + 1] = j
|
||||
end
|
||||
|
||||
-- Try converting this group of RIDs into names
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(socket, openpolicy2_result['policy_handle'], sid, rids, uid, tid, fid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return false, lookupsids2_result
|
||||
end
|
||||
|
||||
-- Put the details for each name into an array
|
||||
for j = 1, #lookupsids2_result['details'], 1 do
|
||||
if(lookupsids2_result['details'][j]['name'] ~= nil) then
|
||||
local result = {}
|
||||
result['name'] = lookupsids2_result['details'][j]['name']
|
||||
result['rid'] = start + j - 1
|
||||
result['domain'] = domain
|
||||
response[#response + 1] = result
|
||||
end
|
||||
end
|
||||
|
||||
-- Go to the next set of RIDs
|
||||
start = start + 5
|
||||
until #lookupsids2_result['names'] == 0 and start > 1100
|
||||
|
||||
end
|
||||
|
||||
-- Close the handle
|
||||
msrpc.lsa_close(socket, openpolicy2_result['policy_handle'], uid, tid, fid)
|
||||
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
|
||||
|
||||
action = function(host)
|
||||
local i, j
|
||||
local status
|
||||
local samr_result, lsa_result
|
||||
local names = {}
|
||||
local name_strings = {}
|
||||
local response = " \n"
|
||||
|
||||
-- Try enumerating through SAMR
|
||||
status, samr_result = enum_samr(host)
|
||||
if(status == false) then
|
||||
response = response .. "Enum via SAMR error: " .. samr_result .. "\n"
|
||||
else
|
||||
-- Copy the returned array into the names[] table, using the name as the key
|
||||
stdnse.print_debug("EnumUsers: Received %d names from SAMR", #samr_result)
|
||||
for i = 1, #samr_result, 1 do
|
||||
names[string.upper(samr_result[i]['name'])] = samr_result[i]
|
||||
end
|
||||
end
|
||||
|
||||
-- Try enumerating through LSA
|
||||
status, lsa_result = enum_lsa(host)
|
||||
if(status == false) then
|
||||
response = response .. "Enum via LSA error: " .. lsa_result .. "\n"
|
||||
else
|
||||
-- Copy the returned array into the names[] table, using the name as the key
|
||||
stdnse.print_debug("EnumUsers: Received %d names from LSA", #samr_result)
|
||||
for i = 1, #lsa_result, 1 do
|
||||
if(names[lsa_result[i]['name']] == nil) then
|
||||
names[string.upper(lsa_result[i]['name'])] = lsa_result[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Put the names into an array of strings, so we can sort them
|
||||
for name, details in pairs(names) do
|
||||
name_strings[#name_strings + 1] = name
|
||||
end
|
||||
-- Sort them
|
||||
table.sort(name_strings, function (a, b) return string.lower(a) < string.lower(b) end)
|
||||
|
||||
-- Check if we actually got any names back
|
||||
if(#name_strings == 0) then
|
||||
response = response .. "Sorry, couldn't find any account names anonymously!"
|
||||
else
|
||||
-- If we're not verbose, just print out the names. Otherwise, print out everything we can
|
||||
if(nmap.verbosity() < 1) then
|
||||
response = response .. stdnse.strjoin(", ", name_strings)
|
||||
else
|
||||
for i = 1, #name_strings, 1 do
|
||||
local name = name_strings[i]
|
||||
response = response .. string.format("%s\n", names[name]['name'])
|
||||
if(names[name]['domain'] ~= nil) then response = response .. string.format(" |_ Domain: %s\n", names[name]['domain']) end
|
||||
if(names[name]['rid'] ~= nil) then response = response .. string.format(" |_ RID: %s\n", names[name]['rid']) end
|
||||
if(names[name]['fullname'] ~= nil) then response = response .. string.format(" |_ Full name: %s\n", names[name]['fullname']) end
|
||||
if(names[name]['description'] ~= nil) then response = response .. string.format(" |_ Description: %s\n", names[name]['description']) end
|
||||
if(names[name]['flags'] ~= nil) then response = response .. string.format(" |_ Flags: %s\n", stdnse.strjoin(", ", names[name]['flags_list'])) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
-- 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
|
||||
-- | OS from SMB: Windows 2000\n
|
||||
-- | LAN Manager: Windows 2000 LAN Manager\n
|
||||
-- | Name: WORKGROUP\TEST1\n
|
||||
-- |_ System time: 2008-09-09 20:55:55 UTC-5\n
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
@@ -52,28 +52,30 @@ end
|
||||
|
||||
action = function(host)
|
||||
|
||||
-- Start up SMB
|
||||
status, socket = smb.start(host)
|
||||
|
||||
if(status == false) then
|
||||
return "Error: " .. socket
|
||||
end
|
||||
|
||||
-- Negotiate protocol
|
||||
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
|
||||
|
||||
-- Start a 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
|
||||
|
||||
smb.stop(socket)
|
||||
-- Kill SMB
|
||||
smb.stop(socket, session_result['uid'])
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
--- Returns information about the SMB security level determined by SMB.
|
||||
--
|
||||
-- Here is how to interpret the output:
|
||||
--
|
||||
--- Returns information about the SMB security level determined by SMB. \n
|
||||
--\n
|
||||
-- Here is how to interpret the output:\n
|
||||
--\n
|
||||
-- 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.
|
||||
-- these days. \n
|
||||
-- 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.
|
||||
--
|
||||
-- it is vulnerable to sniffing. \n
|
||||
--\n
|
||||
-- Challenge/response passwords: If enabled, the server can accept any type of
|
||||
-- password:
|
||||
-- * Plaintext
|
||||
-- * LM and NTLM
|
||||
-- * LMv2 and NTLMv2
|
||||
-- password:\n
|
||||
-- * Plaintext\n
|
||||
-- * LM and NTLM\n
|
||||
-- * LMv2 and NTLMv2\n
|
||||
-- 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.
|
||||
--
|
||||
-- to accept plaintext passwords, it is vulnerable to sniffing. \n
|
||||
--\n
|
||||
-- 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
|
||||
@@ -27,18 +27,18 @@
|
||||
-- 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.
|
||||
--
|
||||
-- server is vulnerable to man-in-the-middle attacks. \n
|
||||
-- \n
|
||||
-- See nselib/smb.lua for more information on the protocol itself. \n
|
||||
--\n
|
||||
--@usage
|
||||
-- nmap --script smb-security-mode.nse -p445 127.0.0.1\n
|
||||
-- sudo nmap -sU -sS --script smb-security-mode.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
|
||||
-- | SMB Security: User-level authentication\n
|
||||
-- | SMB Security: Challenge/response passwords supported\n
|
||||
-- |_ SMB Security: Message signing supported\n
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user