mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 14:11:29 +00:00
Merge from /nmap-exp/ron/nmap-smb. This adds the new scripts
smb-serverstats.nse, smb-enumsessions.nse, and smb-enumshares.nse.
This commit is contained in:
@@ -1,40 +1,75 @@
|
||||
id = "MSRPC: List of domains"
|
||||
description = [[
|
||||
Attempts to enumerate domains on a system, along with their policies. This will
|
||||
likely only work without credentials against Windows 2000.
|
||||
description = [[ Attempts to enumerate domains on a system, along with their policies. This will likely
|
||||
only work without credentials against Windows 2000.
|
||||
|
||||
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
|
||||
After the initial bind() to SAMR, the sequence of calls is:
|
||||
* Connect4() -- get a connect_handle
|
||||
* EnumDomains() -- get a list of the domains (stop here if you just want the names)
|
||||
* QueryDomain() -- get the sid for the domain
|
||||
* OpenDomain() -- get a handle for each domain
|
||||
* QueryDomainInfo2() -- get the domain information
|
||||
* QueryDomainUsers() -- get a list of the users in the domain
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script smb-enumdomains.nse -p445 <host>\n
|
||||
-- sudo nmap -sU -sS --script smb-enumdomains.nse -p U:137,T:139 <host>\n
|
||||
--@usage
|
||||
-- nmap --script smb-enumdomains.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-enumdomains.nse -p U:137,T:139 <host>
|
||||
--
|
||||
--@output
|
||||
-- Host script results:
|
||||
-- | MSRPC: List of domains:
|
||||
-- | Domain: LOCALSYSTEM
|
||||
-- | |_ SID: S-1-5-21-2956463495-2656032972-1271678565
|
||||
-- | |_ Users: Administrator, Guest, SUPPORT_388945a0
|
||||
-- | |_ Creation time: 2007-11-26 15:24:04
|
||||
-- | |_ Passwords: min length: 11 characters; min age: 5 days; max age: 63 days
|
||||
-- | |_ Password lockout: 3 attempts in under 15 minutes will lock the account until manually reset
|
||||
-- | |_ Password history : 5 passwords
|
||||
-- | |_ Password properties:
|
||||
-- | |_ Password complexity requirements exist
|
||||
-- | |_ Administrator account cannot be locked out
|
||||
-- | Domain: Builtin
|
||||
-- | |_ SID: S-1-5-32
|
||||
-- | |_ Users:
|
||||
-- | |_ Creation time: 2007-11-26 15:24:04
|
||||
-- | |_ Passwords: min length: n/a; min age: n/a; max age: 42 days
|
||||
-- | |_ Account lockout disabled
|
||||
-- | |_ Password properties:
|
||||
-- | |_ Password complexity requirements do not exist
|
||||
-- |_ |_ Administrator account cannot be locked out
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-- @output
|
||||
-- Host script results:\n
|
||||
-- | 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
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
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"
|
||||
@@ -58,123 +93,201 @@ end
|
||||
|
||||
action = function(host)
|
||||
local response = " \n"
|
||||
local status, socket
|
||||
local uid, tid, fid
|
||||
local status, smbstate
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
if(status == false) then
|
||||
return "ERROR: " .. socket
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. smbstate
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Bind to SAMR service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid)
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. bind_result
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. bind_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Call connect4()
|
||||
status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid)
|
||||
status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. connect4_result
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. connect4_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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)
|
||||
status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
return "ERROR: " .. enumdomains_result
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. enumdomains_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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"
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: Couldn't find any domains to check"
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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 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)
|
||||
-- Call LookupDomain()
|
||||
status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. lookupdomain_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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)
|
||||
-- Save the sid
|
||||
sid = lookupdomain_result['sid']
|
||||
|
||||
-- Call OpenDomain()
|
||||
status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. opendomain_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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
|
||||
-- Save the domain handle
|
||||
domain_handle = opendomain_result['domain_handle']
|
||||
|
||||
-- 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)
|
||||
-- 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(smbstate, domain_handle, 1)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. querydomaininfo2_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(smbstate, domain_handle, 8, querydomaininfo2_result)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. querydomaininfo2_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
status, querydomaininfo2_result = msrpc.samr_querydomaininfo2(smbstate, domain_handle, 12, querydomaininfo2_result)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. querydomaininfo2_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Call EnumDomainUsers() to get users
|
||||
status, enumdomainusers_result = msrpc.samr_enumdomainusers(smbstate, domain_handle)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. enumdomainusers_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Close the domain handle
|
||||
msrpc.samr_close(socket, domain_handle, uid, tid, fid)
|
||||
-- Close the domain handle
|
||||
msrpc.samr_close(smbstate, domain_handle)
|
||||
|
||||
-- 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
|
||||
-- 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'])
|
||||
|
||||
-- Password characteristics
|
||||
local min_password_length = querydomaininfo2_result['min_password_length']
|
||||
local max_password_age = querydomaininfo2_result['max_password_age']
|
||||
local min_password_age = querydomaininfo2_result['min_password_age']
|
||||
|
||||
if(min_password_length > 0) then
|
||||
min_password_length = string.format("%d characters", min_password_length)
|
||||
else
|
||||
min_password_length = "n/a"
|
||||
end
|
||||
|
||||
if(max_password_age > 0 and max_password_age < 5000) then
|
||||
max_password_age = string.format("%d days", max_password_age)
|
||||
else
|
||||
max_password_age = "n/a"
|
||||
end
|
||||
|
||||
if(min_password_age > 0) then
|
||||
min_password_age = string.format("%d days", min_password_age)
|
||||
else
|
||||
min_password_age = "n/a"
|
||||
end
|
||||
|
||||
response = response .. string.format(" |_ Passwords: min length: %s; min age: %s; max age: %s\n", min_password_length, min_password_age, max_password_age)
|
||||
|
||||
local lockout_duration = querydomaininfo2_result['lockout_duration']
|
||||
if(lockout_duration < 0) then
|
||||
lockout_duration = string.format("for %d minutes", querydomaininfo2_result['lockout_duration'])
|
||||
else
|
||||
lockout_duration = "until manually reset"
|
||||
end
|
||||
|
||||
if(querydomaininfo2_result['lockout_threshold'] > 0) then
|
||||
response = response .. string.format(" |_ Password lockout: %d attempts in under %d minutes will lock the account %s\n", querydomaininfo2_result['lockout_threshold'], querydomaininfo2_result['lockout_window'], lockout_duration)
|
||||
else
|
||||
response = response .. string.format(" |_ Account lockout disabled\n")
|
||||
end
|
||||
|
||||
if(querydomaininfo2_result['password_history_length']) > 0 then
|
||||
response = response .. string.format(" |_ Password history : %d passwords\n", querydomaininfo2_result['password_history_length'])
|
||||
end
|
||||
|
||||
if(#querydomaininfo2_result['password_properties_list'] > 0) then
|
||||
response = response .. " |_ Password properties: \n |_ " .. stdnse.strjoin("\n |_ ", querydomaininfo2_result['password_properties_list']) .. "\n"
|
||||
end
|
||||
end
|
||||
|
||||
-- Close the connect handle
|
||||
msrpc.samr_close(socket, connect_handle, uid, tid, fid)
|
||||
msrpc.samr_close(smbstate, connect_handle)
|
||||
|
||||
-- Close the SMB session
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
349
scripts/smb-enumsessions.nse
Normal file
349
scripts/smb-enumsessions.nse
Normal file
@@ -0,0 +1,349 @@
|
||||
id = "MSRPC: NetSessEnum()"
|
||||
description = [[
|
||||
Enumerates the users logged into a system either locally, through a remote desktop client (terminal
|
||||
services), or through a SMB share.
|
||||
|
||||
Enumerating the local and terminal services users is done by reading the remote registry. Keys under
|
||||
HKEY_USERS are SIDs that represent the currently logged in users, and those SIDs can be converted
|
||||
to proper names by using the LsaLookupSids() function. Doing this requires any access higher than
|
||||
anonymous (guests, users, or administrators are all able to perform this request on the operating
|
||||
systems I tested).
|
||||
|
||||
Enumerating SMB connections is done using the srvsvc.netsessenum() function, which returns who's
|
||||
logged in, when they logged in, and how long they've been idle for. Unfortunately, I couldn't find
|
||||
a way to get the user's domain with this function, so the domain isn't printed. The level of access
|
||||
required for this varies between Windows versions, but in Windows 2000 anybody (including the
|
||||
anonymous account) can access this, and in Windows 2003 a user or administrator account is
|
||||
required.
|
||||
|
||||
Since both of these are related to users being logged into the server, it seemed logical to combine
|
||||
them into a single script.
|
||||
|
||||
I learned the idea and technique for this from sysinternals' tool, PsLoggedOn.exe. I use similar
|
||||
function calls to what they use, so thanks go out to them. Thanks also to Matt, for giving me the
|
||||
idea to write this one.
|
||||
]]
|
||||
---
|
||||
--@usage
|
||||
-- nmap --script smb-enumsessions.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-enumsessions.nse -p U:137,T:139 <host>
|
||||
--
|
||||
--@output
|
||||
-- Host script results:
|
||||
-- | MSRPC: NetSessEnum():
|
||||
-- | Users logged in:
|
||||
-- | |_ TESTBOX\Administrator since 2008-10-21 08:17:14
|
||||
-- | |_ DOMAIN\rbowes since 2008-10-20 09:03:23
|
||||
-- | Active SMB Sessions:
|
||||
-- |_ |_ ADMINISTRATOR is connected from 10.100.254.138 for [just logged in, it's probably you], idle for [not idle]
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
id = "MSRPC: NetSessEnum()"
|
||||
description = "Tries calling the NetSessEnum() RPC function to get a list of active sessions"
|
||||
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 srvsvc_enum_sessions(host)
|
||||
local i
|
||||
local status, smbstate
|
||||
local bind_result, netsessenum_result
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to SRVSVC service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call netsessenum
|
||||
status, netsessenum_result = msrpc.srvsvc_netsessenum(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, netsessenum_result
|
||||
end
|
||||
|
||||
-- Stop the SMB session
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
return true, netsessenum_result['sessions']
|
||||
end
|
||||
|
||||
---Enumerates the users logged in locally (or through terminal services) by using functions
|
||||
-- that access the registry. To perform this check, guest access or higher is required.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@return An array of tables, each table representing a user and containing values for 'name', 'domain', and 'changed_date' (representing
|
||||
-- when they logged in).
|
||||
local function winreg_enum_rids(host)
|
||||
local i, j
|
||||
local elements = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to WINREG service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
status, openhku_result = msrpc.winreg_openhku(smbstate)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openhku_result
|
||||
end
|
||||
|
||||
-- Loop through the keys under HKEY_USERS and grab the names
|
||||
i = 0
|
||||
repeat
|
||||
status, enumkey_result = msrpc.winreg_enumkey(smbstate, openhku_result['handle'], i)
|
||||
|
||||
if(status == true) then
|
||||
local status, openkey_result
|
||||
|
||||
local element = {}
|
||||
element['name'] = enumkey_result['name']
|
||||
element['sid'] = msrpc.string_to_sid(enumkey_result['name'])
|
||||
|
||||
-- To get the time the user logged in, we check the 'Volatile Environment' key
|
||||
status, openkey_result = msrpc.winreg_openkey(smbstate, openhku_result['handle'], element['name'] .. "\\Volatile Environment")
|
||||
if(status ~= false) then
|
||||
local queryinfokey_result, closekey_result
|
||||
|
||||
-- Query the info about this key. The response will tell us when the user logged into the server.
|
||||
status, queryinfokey_result = msrpc.winreg_queryinfokey(smbstate, openkey_result['handle'])
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, queryinfokey_result
|
||||
end
|
||||
|
||||
status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'])
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, closekey_result
|
||||
end
|
||||
|
||||
element['changed_date'] = queryinfokey_result['last_changed_date']
|
||||
elements[#elements + 1] = element
|
||||
end
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
until status ~= true
|
||||
|
||||
status, closekey_result = msrpc.winreg_closekey(smbstate, openhku_result['handle'])
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, closekey_result
|
||||
end
|
||||
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
-- Start a new SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to LSA service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Get a policy handle
|
||||
status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openpolicy2_result
|
||||
end
|
||||
|
||||
-- Convert the RIDs to names
|
||||
local results = {}
|
||||
stdnse.print_debug(3, "MSRPC: Found %d SIDs that might be logged in", #elements)
|
||||
for i = 1, #elements, 1 do
|
||||
if(elements[i]['sid'] ~= nil) then
|
||||
-- The RID is the last subauthority
|
||||
local rid = elements[i]['sid']['subauthorities'][elements[i]['sid']['count']]
|
||||
stdnse.print_debug(3, "MSRPC: Found an actual RID: %d", rid)
|
||||
|
||||
-- The server is the rest of the SID, so remove the last subauthority
|
||||
elements[i]['sid']['subauthorities'][elements[i]['sid']['count']] = nil
|
||||
elements[i]['sid']['count'] = elements[i]['sid']['count'] - 1
|
||||
|
||||
-- Look up the RID
|
||||
stdnse.print_debug(3, "MSRPC: Looking up RID %s in SID %s", rid, msrpc.sid_to_string(elements[i]['sid']))
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], elements[i]['sid'], {rid})
|
||||
if(status == false) then
|
||||
-- It may not succeed, if it doesn't that's ok
|
||||
stdnse.print_debug(3, "MSRPC: Lookup failed")
|
||||
else
|
||||
-- Create the result array
|
||||
local result = {}
|
||||
result['rid'] = rid
|
||||
result['changed_date'] = elements[i]['changed_date']
|
||||
|
||||
-- Fill in the result from the response
|
||||
if(lookupsids2_result['details'][1] == nil) then
|
||||
result['name'] = "<unknown>"
|
||||
result['domain'] = ""
|
||||
else
|
||||
result['name'] = lookupsids2_result['details'][1]['name']
|
||||
result['type'] = lookupsids2_result['details'][1]['type']
|
||||
result['domain'] = lookupsids2_result['domains'][1]['name']
|
||||
end
|
||||
|
||||
if(result['type'] ~= 5) then -- Don't show "well known" accounts
|
||||
-- Add it to the results
|
||||
results[#results + 1] = result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Close the policy
|
||||
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
||||
|
||||
-- Stop the session
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
return true, results
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local response = " \n"
|
||||
|
||||
local status1, status2
|
||||
|
||||
-- Enumerate the logged in users
|
||||
status1, users = winreg_enum_rids(host)
|
||||
if(status1 == false) then
|
||||
response = response .. "ERROR: Couldn't enumerate login sessions: " .. users .. "\n"
|
||||
else
|
||||
response = response .. "Users logged in:\n"
|
||||
if(#users == 0) then
|
||||
response = response .. "|_ <nobody>\n"
|
||||
else
|
||||
for i = 1, #users, 1 do
|
||||
response = response .. string.format("|_ %s\\%s since %s\n", users[i]['domain'], users[i]['name'], users[i]['changed_date'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the connected sessions
|
||||
status2, sessions = srvsvc_enum_sessions(host)
|
||||
if(status2 == false) then
|
||||
response = response .. "ERROR: Couldn't enumerate network sessions: " .. sessions .. "\n"
|
||||
else
|
||||
response = response .. "Active SMB Sessions:\n"
|
||||
if(#sessions == 0) then
|
||||
response = response .. "|_ <none>\n"
|
||||
else
|
||||
-- Format the result
|
||||
for i = 1, #sessions, 1 do
|
||||
|
||||
local active = sessions[i]['active']
|
||||
if(active == 0) then
|
||||
active = "[just logged in, it's probably you]"
|
||||
elseif(active > 60 * 60 * 24) then
|
||||
active = string.format("%dd%dh%02dm%02ds", active / (60*60*24), (active % (60*60*24)) / 3600, (active % 3600) / 60, active % 60)
|
||||
elseif(active > 60 * 60) then
|
||||
active = string.format("%dh%02dm%02ds", active / 3600, (active % 3600) / 60, active % 60)
|
||||
else
|
||||
active = string.format("%02dm%02ds", active / 60, active % 60)
|
||||
end
|
||||
|
||||
local idle = sessions[i]['idle']
|
||||
if(idle == 0) then
|
||||
idle = "[not idle]"
|
||||
elseif(idle > 60 * 60 * 24) then
|
||||
idle = string.format("%dd%dh%02dm%02ds", idle / (60*60*24), (idle % (60*60*24)) / 3600, (idle % 3600) / 60, idle % 60)
|
||||
elseif(idle > 60 * 60) then
|
||||
idle = string.format("%dh%02dm%02ds", idle / 3600, (idle % 3600) / 60, idle % 60)
|
||||
else
|
||||
idle = string.format("%02dm%02ds", idle / 60, idle % 60)
|
||||
end
|
||||
|
||||
response = response .. string.format("|_ %s is connected from %s for %s, idle for %s\n", sessions[i]['user'], sessions[i]['client'], active, idle)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if(status1 == false and status2 == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return response
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
return response
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +1,89 @@
|
||||
id = "MSRPC: NetShareEnumAll()"
|
||||
id = "MSRPC: List of shares"
|
||||
description = [[
|
||||
Attempts to list shares using the srvsvc.NetShareEnumAll() MSRPC function. This
|
||||
will likely only work anonymously against Windows 2000.
|
||||
Attempts to list shares using the srvsvc.NetShareEnumAll() MSRPC function, then
|
||||
retrieve more information about each share using srvsvc.NetShareGetInfo(). Running
|
||||
NetShareEnumAll() will work anonymously on Windows 2000, and requires a user level
|
||||
account on any other Windows version. Calling NetShareGetInfo() requires an
|
||||
administrator account on every version of Windows I tested.
|
||||
|
||||
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).
|
||||
Although NetShareEnumAll() is restricted on certain systems, actually connecting to
|
||||
a share to check if it exists will always work. So, if NetShareEnumAll() fails, a
|
||||
list of common shares will be attempted.
|
||||
|
||||
After a list of shares is found, whether or not it's complete, we attempt to connect
|
||||
to each of them anonymously, which lets us divide them into "anonymous" and
|
||||
"restricted".
|
||||
|
||||
When possible, once the list of shares is determined, NetShareGetInfo() is called
|
||||
to get additional information on the share. Odds are this will fail, unless we're
|
||||
doing an authenticated test.
|
||||
]]
|
||||
|
||||
---
|
||||
--@usage
|
||||
-- nmap --script smb-enumshares.nse -p445 <host>\n
|
||||
-- nmap --script smb-enumshares.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-enumshares.nse -p U:137,T:139 <host>
|
||||
--
|
||||
--@output
|
||||
-- Standard:
|
||||
-- | MSRPC: NetShareEnumAll():
|
||||
-- | Anonymous shares: IPC$
|
||||
-- |_ Restricted shares: F$, ADMIN$, C$
|
||||
--
|
||||
-- Verbose:
|
||||
-- Host script results:
|
||||
-- | MSRPC: NetShareEnumAll():
|
||||
-- | Anonymous shares:
|
||||
-- | IPC$
|
||||
-- | |_ Type: STYPE_IPC_HIDDEN
|
||||
-- | |_ Comment: Remote IPC
|
||||
-- | |_ Users: 1, Max: <unlimited>
|
||||
-- | |_ Path:
|
||||
-- | test
|
||||
-- | |_ Type: STYPE_DISKTREE
|
||||
-- | |_ Comment: This is a test share, with a maximum of 7 users
|
||||
-- | |_ Users: 0, Max: 7
|
||||
-- | |_ Path: C:\Documents and Settings\Ron\Desktop\test
|
||||
-- | Restricted shares:
|
||||
-- | ADMIN$
|
||||
-- | |_ Type: STYPE_DISKTREE_HIDDEN
|
||||
-- | |_ Comment: Remote Admin
|
||||
-- | |_ Users: 0, Max: <unlimited>
|
||||
-- | |_ Path: C:\WINNT
|
||||
-- | C$
|
||||
-- | |_ Type: STYPE_DISKTREE_HIDDEN
|
||||
-- | |_ Comment: Default share
|
||||
-- | |_ Users: 0, Max: <unlimited>
|
||||
-- |_ |_ Path: C:\
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
@@ -51,31 +115,31 @@ end
|
||||
-- a list of all shares on a system.
|
||||
local function samr_enum_shares(host)
|
||||
|
||||
local status, socket, uid, tid, fid
|
||||
local status, smbstate
|
||||
local bind_result, netshareenumall_result
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to SRVSVC service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil, uid, tid, fid)
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
smb.stop(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call netsharenumall
|
||||
status, netshareenumall_result = msrpc.srvsvc_netshareenumall(socket, host.ip, uid, tid, fid)
|
||||
status, netshareenumall_result = msrpc.srvsvc_netshareenumall(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
smb.stop(smbstate)
|
||||
return false, netshareenumall_result
|
||||
end
|
||||
|
||||
-- Stop the SMB session
|
||||
smb.stop(socket, uid, tid)
|
||||
smb.stop(smbstate)
|
||||
|
||||
return true, netshareenumall_result['shares']
|
||||
end
|
||||
@@ -88,28 +152,29 @@ end
|
||||
--@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 smbstate
|
||||
local i
|
||||
local allowed_shares = {}
|
||||
local denied_shares = {}
|
||||
|
||||
-- Begin the SMB session
|
||||
status, socket = smb.start(host)
|
||||
status, smbstate = smb.start(host)
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Negotiate the protocol
|
||||
status, negotiate_result = smb.negotiate_protocol(socket)
|
||||
status, err = smb.negotiate_protocol(smbstate)
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return false, negotiate_result
|
||||
smb.stop(smbstate)
|
||||
return false, err
|
||||
end
|
||||
|
||||
-- Start up a null session
|
||||
status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities'])
|
||||
status, err = smb.start_session(smbstate, "", "", "", "", "LM")
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return false, session_result
|
||||
smb.stop(smbstate)
|
||||
return false, err
|
||||
end
|
||||
|
||||
-- Connect to the shares
|
||||
@@ -121,30 +186,65 @@ function check_shares(host, shares)
|
||||
|
||||
-- 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'])
|
||||
status, err = smb.tree_connect(smbstate, share)
|
||||
-- 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
|
||||
if(err == 0xc0000022 or err == '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)
|
||||
stdnse.print_debug(3, "EnumShares: Share didn't pan out: %s", err)
|
||||
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'])
|
||||
smb.tree_disconnect(smbstate)
|
||||
end
|
||||
end
|
||||
|
||||
-- Log off the user
|
||||
smb.stop(socket, session_result['uid'])
|
||||
smb.stop(smbstate)
|
||||
|
||||
return allowed_shares, denied_shares
|
||||
return true, allowed_shares, denied_shares
|
||||
end
|
||||
|
||||
---Attempts to retrieve additional information about a share. Will fail unless we have
|
||||
-- administrative access.
|
||||
--
|
||||
--@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 get_share_info(host, name)
|
||||
local status, smbstate
|
||||
local response = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to SRVSVC service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call NetShareGetInfo
|
||||
status, netsharegetinfo_result = msrpc.srvsvc_netsharegetinfo(smbstate, host.ip, name, 2)
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
return false, netsharegetinfo_result
|
||||
end
|
||||
|
||||
smb.stop(smbstate)
|
||||
|
||||
return true, netsharegetinfo_result
|
||||
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
local result, shared
|
||||
@@ -158,7 +258,10 @@ action = function(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)
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. string.format("Couldn't enum all shares, checking for common ones (%s)\n", shares)
|
||||
end
|
||||
|
||||
-- Take some common share names I've seen
|
||||
shares = {"IPC$", "ADMIN$", "TEST", "TEST$", "HOME", "HOME$"}
|
||||
-- Try every alphabetic share, with and without a trailing '$'
|
||||
@@ -169,9 +272,61 @@ action = function(host)
|
||||
end
|
||||
|
||||
-- Break them into anonymous/authenticated shares
|
||||
allowed, denied = check_shares(host, shares)
|
||||
status, allowed, denied = check_shares(host, shares)
|
||||
|
||||
return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", allowed), stdnse.strjoin(", ", denied))
|
||||
if(status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. allowed
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
if(result == false or nmap.verbosity() == 0) then
|
||||
return response .. string.format("Anonymous shares: %s\nRestricted shares: %s\n", stdnse.strjoin(", ", allowed), stdnse.strjoin(", ", denied))
|
||||
else
|
||||
response = response .. string.format("Anonymous shares:\n")
|
||||
for i = 1, #allowed, 1 do
|
||||
local status, info = get_share_info(host, allowed[i])
|
||||
|
||||
response = response .. string.format(" %s\n", allowed[i])
|
||||
|
||||
if(status == false) then
|
||||
stdnse.print_debug(2, "Error getting information for share %s: %s", allowed[i], info)
|
||||
else
|
||||
if(info['max_users'] == 0xFFFFFFFF) then
|
||||
info['max_users'] = "<unlimited>"
|
||||
end
|
||||
|
||||
response = response .. string.format(" |_ Type: %s\n", info['strtype'])
|
||||
response = response .. string.format(" |_ Comment: %s\n", info['comment'])
|
||||
response = response .. string.format(" |_ Users: %s, Max: %s\n", info['current_users'], info['max_users'])
|
||||
response = response .. string.format(" |_ Path: %s\n", info['path'])
|
||||
end
|
||||
end
|
||||
|
||||
response = response .. string.format("Restricted shares:\n")
|
||||
for i = 1, #denied, 1 do
|
||||
local status, info = get_share_info(host, denied[i])
|
||||
|
||||
response = response .. string.format(" %s\n", denied[i])
|
||||
|
||||
if(status == false) then
|
||||
stdnse.print_debug(2, "Error getting information for share %s: %s", denied[i], info)
|
||||
else
|
||||
if(info['max_users'] == 0xFFFFFFFF) then
|
||||
info['max_users'] = "<unlimited>"
|
||||
end
|
||||
|
||||
response = response .. string.format(" |_ Type: %s\n", info['strtype'])
|
||||
response = response .. string.format(" |_ Comment: %s\n", info['comment'])
|
||||
response = response .. string.format(" |_ Users: %s, Max: %s\n", info['current_users'], info['max_users'])
|
||||
response = response .. string.format(" |_ Path: %s\n", info['path'])
|
||||
end
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -2,74 +2,148 @@ id = "MSRPC: List of user accounts"
|
||||
description = [[
|
||||
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).
|
||||
which uses port 445 or 139). Some functions in SAMR are used to enumerate
|
||||
users, and some bruteforce guessing using LSA functions is attempted.
|
||||
|
||||
One technique used is calling the QueryDisplayInfo() function in the SAMR library.
|
||||
If this succeeds, it will return a detailed list of users. This can be done
|
||||
anonymously against Windows 2000, and with a user-level account on other Windows
|
||||
versions (but not with a guest-level account).
|
||||
|
||||
To perform this test, the following functions are used:
|
||||
|
||||
* Bind() -- bind to the SAMR service
|
||||
* Connect4() -- get a connect_handle
|
||||
* EnumDomains() -- get a list of the domains
|
||||
* QueryDomain() -- get the sid for the domain
|
||||
* OpenDomain() -- get a handle for each domain
|
||||
* QueryDisplayInfo() -- get the list of users in the domain
|
||||
* Close() -- Close the domain handle
|
||||
* Close() -- Close the connect handle
|
||||
|
||||
The advantage of this technique is that a lot of details are returned, including
|
||||
the full name and description; the disadvantage is that it requires a user-level
|
||||
account on every system except for Windows 2000. Additionally, it only pulls actual
|
||||
user accounts, not groups or aliasts.
|
||||
|
||||
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
|
||||
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).
|
||||
user accounts, called LSA bruteforcing. LSA bruteforcing can be done anonymously
|
||||
against Windows 2000, and requires a guest account or better on other systems.
|
||||
It has the advantage of running with less permissions, and will also find more
|
||||
account types (ie, groups, aliases, etc). The disadvantages is that it returns
|
||||
less information, and that, because it's a bruteforce, it's possible to miss
|
||||
accounts.
|
||||
\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.
|
||||
This isn't a bruteforce in the common sense, however; it's a bruteforce of users'
|
||||
RIDs. A user's RID is a value (generally 500, 501, or 1000+) that uniquely identifies
|
||||
a user on a domain or system. An LSA function is exposed which lets us convert the RID
|
||||
(say, '1000') to the username (say, 'Ron'). So, the bruteforce will essentially try
|
||||
converting 1000 to a name, 1001, 1002, etc., until we think we're done.
|
||||
\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")
|
||||
I break the users into 5-RID groups, and check them individually (checking too many
|
||||
at once causes problems). I continue checking until I reach 1100, and get an empty
|
||||
group. This probably isn't the most effective way, but it seems to work.
|
||||
It might be a good idea to modify this, in the future, with some more
|
||||
intelligence. I performed a test on an old server 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, in an automated check.
|
||||
\n\n
|
||||
Before attempting this conversion, the SID of the server has to be determined.
|
||||
The SID is determined by doing the reverse operation -- converting a name into
|
||||
a RID. The name is determined by looking up any name present on the system.
|
||||
In this script, I try looking up:
|
||||
\n\n
|
||||
<ul>
|
||||
<li>The computer name / domain name, returned in SMB_COM_NEGOTIATE
|
||||
<li>An nbstat query to get the server name and the currently loggeed in user
|
||||
<li>Some common names ("administrator", "guest", and "test")
|
||||
</ul>
|
||||
\n\n
|
||||
In theory, the computer name should be sufficient for this to always work, and
|
||||
the rest of the 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.
|
||||
so far has in my tests, but I included the rest of the names for good measure.
|
||||
\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.
|
||||
If the output is verbose, then extra details. The output is ordered alphabetically.
|
||||
\n\n
|
||||
Credit goes out to the enum.exe, sid2user.exe, and user2sid.exe programs,
|
||||
the code I wrote for this is largely based on the techniques used by them.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script smb-enumusers.nse -p445 <host>\n
|
||||
-- nmap --script smb-enumusers.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-enumusers.nse -p U:137,T:139 <host>
|
||||
--
|
||||
-- @output
|
||||
-- Host script results:
|
||||
-- | MSRPC: List of user accounts:
|
||||
-- |_ TESTBOX\Administrator, EXTERNAL\DnsAdmins, TESTBOX\Guest, EXTERNAL\HelpServicesGroup, EXTERNAL\PARTNERS$, TESTBOX\SUPPORT_388945a0
|
||||
--
|
||||
-- Host script results:
|
||||
-- | MSRPC: List of user accounts:
|
||||
-- | Administrator
|
||||
-- | |_ Type: User
|
||||
-- | |_ Domain: LOCALSYSTEM
|
||||
-- | |_ Full name: Built-in account for administering the computer/domain
|
||||
-- | |_ Flags: Normal account, Password doesn't expire
|
||||
-- | DnsAdmins
|
||||
-- | |_ Type: Alias
|
||||
-- | |_ Domain: EXTRANET
|
||||
-- | EventViewer
|
||||
-- | |_ Type: User
|
||||
-- | |_ Domain: SHARED
|
||||
-- | ProxyUsers
|
||||
-- | |_ Type: Group
|
||||
-- | |_ Domain: EXTRANET
|
||||
-- | ComputerAccounts
|
||||
-- | |_ Type: Group
|
||||
-- | |_ Domain: EXTRANET
|
||||
-- | Helpdesk
|
||||
-- | |_ Type: Group
|
||||
-- | |_ Domain: EXTRANET
|
||||
-- | Guest
|
||||
-- | |_ Type: User
|
||||
-- | |_ Domain: LOCALSYSTEM
|
||||
-- | |_ Full name: Built-in account for guest access to the computer/domain
|
||||
-- | |_ Flags: Normal account, Disabled, Password not required, Password doesn't expire
|
||||
-- | Staff
|
||||
-- | |_ Type: Alias
|
||||
-- | |_ Domain: LOCALSYSTEM
|
||||
-- | Students
|
||||
-- | |_ Type: Alias
|
||||
-- |_ |_ Domain: LOCALSYSTEM
|
||||
--
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
@@ -100,29 +174,32 @@ end
|
||||
-- array of tables. Each table contains a 'name', 'domain', 'fullname', 'rid', and 'description'.
|
||||
local function enum_samr(host)
|
||||
|
||||
stdnse.print_debug(3, "Entering enum_samr()")
|
||||
|
||||
local smbstate
|
||||
local bind_result, connect4_result, enumdomains_result
|
||||
local connect_handle
|
||||
local status, socket
|
||||
local uid, tid, fid
|
||||
local status, smbstate
|
||||
local response = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, socket, uid, tid, fid = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SAMR_PATH)
|
||||
|
||||
if(status == false) then
|
||||
return false, socket
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to SAMR service
|
||||
status, bind_result = msrpc.bind(socket, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil, uid, tid, fid)
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SAMR_UUID, msrpc.SAMR_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Call connect4()
|
||||
status, connect4_result = msrpc.samr_connect4(socket, host.ip, uid, tid, fid)
|
||||
status, connect4_result = msrpc.samr_connect4(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, connect4_result
|
||||
end
|
||||
|
||||
@@ -130,31 +207,32 @@ local function enum_samr(host)
|
||||
connect_handle = connect4_result['connect_handle']
|
||||
|
||||
-- Call EnumDomains()
|
||||
status, enumdomains_result = msrpc.samr_enumdomains(socket, connect_handle, uid, tid, fid)
|
||||
status, enumdomains_result = msrpc.samr_enumdomains(smbstate, connect_handle)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
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)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, "Couldn't find any domains"
|
||||
end
|
||||
|
||||
-- Now, loop through the domains and find the users
|
||||
for i = 1, #enumdomains_result['domains'], 1 do
|
||||
|
||||
local domain = enumdomains_result['domains'][i]
|
||||
-- We don't care about the 'builtin' domain
|
||||
-- We don't care about the 'builtin' domain, in all my tests it's empty
|
||||
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)
|
||||
status, lookupdomain_result = msrpc.samr_lookupdomain(smbstate, connect_handle, domain)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, lookupdomain_result
|
||||
end
|
||||
|
||||
@@ -162,9 +240,9 @@ local function enum_samr(host)
|
||||
sid = lookupdomain_result['sid']
|
||||
|
||||
-- Call OpenDomain()
|
||||
status, opendomain_result = msrpc.samr_opendomain(socket, connect_handle, sid, uid, tid, fid)
|
||||
status, opendomain_result = msrpc.samr_opendomain(smbstate, connect_handle, sid)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, opendomain_result
|
||||
end
|
||||
|
||||
@@ -172,28 +250,33 @@ local function enum_samr(host)
|
||||
domain_handle = opendomain_result['domain_handle']
|
||||
|
||||
-- Call QueryDisplayInfo()
|
||||
status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(socket, domain_handle, uid, tid, fid)
|
||||
status, querydisplayinfo_result = msrpc.samr_querydisplayinfo(smbstate, domain_handle)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, querydisplayinfo_result
|
||||
end
|
||||
|
||||
-- Close the domain handle
|
||||
msrpc.samr_close(socket, domain_handle, uid, tid, fid)
|
||||
msrpc.samr_close(smbstate, domain_handle)
|
||||
|
||||
-- Finally, fill in the response!
|
||||
for i = 1, #querydisplayinfo_result['details'], 1 do
|
||||
querydisplayinfo_result['details'][i]['domain'] = domain
|
||||
-- All we get from this is users
|
||||
querydisplayinfo_result['details'][i]['typestr'] = "User"
|
||||
querydisplayinfo_result['details'][i]['source'] = "SAMR Enumeration"
|
||||
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)
|
||||
msrpc.samr_close(smbstate, connect_handle)
|
||||
|
||||
-- Stop the SAMR SMB
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
stdnse.print_debug(3, "Leaving enum_samr()")
|
||||
|
||||
return true, response
|
||||
end
|
||||
@@ -205,116 +288,126 @@ end
|
||||
-- array of tables. Each table contains a 'name', 'domain', and 'rid'.
|
||||
local function enum_lsa(host)
|
||||
|
||||
local status, socket
|
||||
local uid, tid, fid
|
||||
local smbstate
|
||||
local status
|
||||
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
|
||||
stdnse.print_debug(3, "Entering enum_lsa()")
|
||||
|
||||
-- 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
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.LSA_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
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
|
||||
-- Bind to LSA service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.LSA_UUID, msrpc.LSA_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_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'] }
|
||||
-- Open the LSA policy
|
||||
status, openpolicy2_result = msrpc.lsa_openpolicy2(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openpolicy2_result
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- Start with some common names, as well as the name returned by the negotiate call
|
||||
-- Vista doesn't like a 'null' after the server name, so fix that (TODO: the way I strip the null here feels hackish, is there a better way?)
|
||||
names = {"administrator", "guest", "test", smbstate['domain'], string.sub(smbstate['server'], 1, #smbstate['server'] - 1) }
|
||||
|
||||
-- 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
|
||||
-- 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
|
||||
|
||||
-- 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
|
||||
-- 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
|
||||
|
||||
-- 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
|
||||
-- Look up the names, if any are valid than the server's SID will be returned
|
||||
status, lookupnames2_result = msrpc.lsa_lookupnames2(smbstate, openpolicy2_result['policy_handle'], names)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, lookupnames2_result
|
||||
end
|
||||
|
||||
-- Start by looking up 500 - 505 (will likely be Administrator + guest)
|
||||
for j = 500, 505, 1 do
|
||||
-- Loop through the domains returned and find the 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
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sid, rids)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
||||
else
|
||||
-- Put the details for each name into an array
|
||||
for j = 1, #lookupsids2_result['details'], 1 do
|
||||
if(lookupsids2_result['details'][j]['type'] ~= 8) then -- 8 = user not found
|
||||
local result = {}
|
||||
result['name'] = lookupsids2_result['details'][j]['name']
|
||||
result['rid'] = 500 + j - 1
|
||||
result['domain'] = domain
|
||||
result['typestr'] = lookupsids2_result['details'][j]['typestr']
|
||||
result['source'] = "LSA Bruteforce"
|
||||
response[#response + 1] = result
|
||||
end
|
||||
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
|
||||
-- Now do groups of 5 users, until we get past 1100 and have an empty group
|
||||
repeat
|
||||
local used_names = 0
|
||||
local 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
|
||||
status, lookupsids2_result = msrpc.lsa_lookupsids2(smbstate, openpolicy2_result['policy_handle'], sid, rids)
|
||||
if(status == false) then
|
||||
stdnse.print_debug(1, string.format("Error looking up RIDs: %s", lookupsids2_result))
|
||||
else
|
||||
-- Put the details for each name into an array
|
||||
for j = 1, #lookupsids2_result['details'], 1 do
|
||||
if(lookupsids2_result['details'][j]['type'] ~= 8) then -- 8 = user not found
|
||||
local result = {}
|
||||
result['name'] = lookupsids2_result['details'][j]['name']
|
||||
result['rid'] = start + j - 1
|
||||
result['domain'] = domain
|
||||
result['typestr'] = lookupsids2_result['details'][j]['typestr']
|
||||
result['source'] = "LSA Bruteforce"
|
||||
response[#response + 1] = result
|
||||
|
||||
-- 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
|
||||
-- Increment the number of used names we have
|
||||
used_names = used_names + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Go to the next set of RIDs
|
||||
start = start + 5
|
||||
until #lookupsids2_result['names'] == 0 and start > 1100
|
||||
start = start + 5
|
||||
until status == false or (used_names == 0 and start > 1100)
|
||||
end
|
||||
|
||||
end
|
||||
-- Close the handle
|
||||
msrpc.lsa_close(smbstate, openpolicy2_result['policy_handle'])
|
||||
|
||||
-- Close the handle
|
||||
msrpc.lsa_close(socket, openpolicy2_result['policy_handle'], uid, tid, fid)
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
msrpc.stop_smb(socket, uid, tid)
|
||||
stdnse.print_debug(3, "Leaving enum_lsa()")
|
||||
|
||||
return true, response
|
||||
end
|
||||
@@ -323,41 +416,53 @@ end
|
||||
|
||||
action = function(host)
|
||||
local i, j
|
||||
local status
|
||||
local samr_status, lsa_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"
|
||||
-- Try enumerating through LSA first. Since LSA provides less information, we want the
|
||||
-- SAMR result to overwrite it.
|
||||
lsa_status, lsa_result = enum_lsa(host)
|
||||
if(lsa_status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. "ERROR: couldn't enum through LSA: " .. lsa_result .. "\n"
|
||||
end
|
||||
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)
|
||||
stdnse.print_debug(2, "EnumUsers: Received %d names from LSA", #lsa_result)
|
||||
for i = 1, #lsa_result, 1 do
|
||||
names[string.upper(lsa_result[i]['name'])] = lsa_result[i]
|
||||
end
|
||||
end
|
||||
|
||||
-- Try enumerating through SAMR
|
||||
samr_status, samr_result = enum_samr(host)
|
||||
if(samr_status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. "ERROR: couldn't enumerate through SAMR: " .. samr_result .. "\n"
|
||||
end
|
||||
else
|
||||
-- Copy the returned array into the names[] table, using the name as the key
|
||||
stdnse.print_debug(2, "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
|
||||
-- Check if both failed
|
||||
if(samr_status == false and lsa_status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return response
|
||||
else
|
||||
return nil
|
||||
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
|
||||
name_strings[#name_strings + 1] = names[name]['name']
|
||||
end
|
||||
-- Sort them
|
||||
table.sort(name_strings, function (a, b) return string.lower(a) < string.lower(b) end)
|
||||
@@ -368,16 +473,30 @@ action = function(host)
|
||||
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)
|
||||
local response_array = {}
|
||||
for i = 1, #name_strings, 1 do
|
||||
local name = string.upper(name_strings[i])
|
||||
response_array[#response_array + 1] = (names[name]['domain'] .. "\\" .. names[name]['name'])
|
||||
end
|
||||
|
||||
response = response .. stdnse.strjoin(", ", response_array)
|
||||
else
|
||||
for i = 1, #name_strings, 1 do
|
||||
local name = name_strings[i]
|
||||
local name = string.upper(name_strings[i])
|
||||
response = response .. string.format("%s\n", names[name]['name'])
|
||||
|
||||
if(names[name]['typestr'] ~= nil) then response = response .. string.format(" |_ Type: %s\n", names[name]['typestr']) end
|
||||
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(nmap.verbosity() > 1) then
|
||||
if(names[name]['rid'] ~= nil) then response = response .. string.format(" |_ RID: %s\n", names[name]['rid']) end
|
||||
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
|
||||
|
||||
if(nmap.verbosity() > 1) then
|
||||
if(names[name]['source'] ~= nil) then response = response .. string.format(" |_ Source: %s\n", names[name]['source']) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -385,4 +504,16 @@ action = function(host)
|
||||
return response
|
||||
end
|
||||
|
||||
--real_action = action
|
||||
--
|
||||
-- function action (...)
|
||||
-- local t = {n = select("#", ...), ...};
|
||||
-- local status, ret = xpcall(function() return real_action(unpack(t, 1, t.n)) end, debug.traceback)
|
||||
--
|
||||
-- if not status then
|
||||
-- error(ret)
|
||||
-- end
|
||||
--
|
||||
-- return ret
|
||||
-- end
|
||||
|
||||
|
||||
@@ -1,22 +1,53 @@
|
||||
id = "OS from SMB"
|
||||
description = [[
|
||||
Attempts to determine the operating system over the SMB protocol (ports 445 and
|
||||
139).
|
||||
139). Although the standard smb arguments can be used (for username/password), and
|
||||
are respected by this script, they likely won't change the outcome in any meaningful
|
||||
way.
|
||||
|
||||
See nselib/smb.lua for more information on this protocol.
|
||||
]]
|
||||
|
||||
---
|
||||
--@usage
|
||||
-- nmap --script smb-os-discovery.nse -p445 127.0.0.1\n
|
||||
-- nmap --script smb-os-discovery.nse -p445 127.0.0.1
|
||||
-- sudo nmap -sU -sS --script smb-os-discovery.nse -p U:137,T:139 127.0.0.1
|
||||
--
|
||||
--@output
|
||||
-- | 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
|
||||
-- | OS from SMB: Windows 2000
|
||||
-- | LAN Manager: Windows 2000 LAN Manager
|
||||
-- | Name: WORKGROUP\TEST1
|
||||
-- |_ System time: 2008-09-09 20:55:55 UTC-5
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
@@ -56,31 +87,48 @@ end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local state
|
||||
local status, err
|
||||
|
||||
-- Start up SMB
|
||||
status, socket = smb.start(host)
|
||||
status, state = smb.start(host)
|
||||
|
||||
if(status == false) then
|
||||
return "Error: " .. socket
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. state
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Negotiate protocol
|
||||
status, negotiate_result = smb.negotiate_protocol(socket)
|
||||
status, err = smb.negotiate_protocol(state)
|
||||
|
||||
if(status == false) then
|
||||
stdnse.print_debug(2, "Negotiate session failed")
|
||||
smb.stop(socket)
|
||||
return "Error: " .. negotiate_result
|
||||
smb.stop(state)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. err
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Start a session
|
||||
status, session_result = smb.start_session(socket, "", negotiate_result['session_key'], negotiate_result['capabilities'])
|
||||
status, err = smb.start_session(state, "")
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return "Error: " .. session_result
|
||||
smb.stop(state)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. err
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Kill SMB
|
||||
smb.stop(socket, session_result['uid'])
|
||||
smb.stop(state)
|
||||
|
||||
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'])
|
||||
return string.format("%s\nLAN Manager: %s\nName: %s\\%s\nSystem time: %s %s\n", get_windows_version(state['os']), state['lanmanager'], state['domain'], state['server'], state['date'], state['timezone_str'])
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -3,25 +3,29 @@ description = [[
|
||||
Returns information about the SMB security level determined by SMB.
|
||||
|
||||
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.\n
|
||||
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.
|
||||
\n\n
|
||||
|
||||
Challenge/response passwords: If enabled, the server can accept any type of
|
||||
password:\n
|
||||
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.
|
||||
\n\n
|
||||
to accept plaintext passwords, it is vulnerable to sniffing. LM and NTLM are
|
||||
fairly secure, although there are some bruteforce attacks against them.
|
||||
|
||||
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
|
||||
@@ -31,20 +35,51 @@ 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.
|
||||
\n\n
|
||||
See nselib/smb.lua for more information on the protocol itself.\n
|
||||
]]
|
||||
|
||||
See nselib/smb.lua for more information on the protocol itself.
|
||||
|
||||
This script will allow you to use smb arguments (username/password), but it probably
|
||||
won't ever require them.
|
||||
]]
|
||||
---
|
||||
--@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
|
||||
-- nmap --script smb-security-mode.nse -p445 127.0.0.1
|
||||
-- sudo nmap -sU -sS --script smb-security-mode.nse -p U:137,T:139 127.0.0.1
|
||||
--
|
||||
--@output
|
||||
-- | SMB Security: User-level authentication\n
|
||||
-- | SMB Security: Challenge/response passwords supported\n
|
||||
-- |_ SMB Security: Message signing supported\n
|
||||
-- | SMB Security: User-level authentication
|
||||
-- | SMB Security: Challenge/response passwords supported
|
||||
-- |_ SMB Security: Message signing supported
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
author = "Ron Bowes"
|
||||
@@ -69,20 +104,31 @@ end
|
||||
|
||||
action = function(host)
|
||||
|
||||
local status, socket = smb.start(host)
|
||||
local state
|
||||
local status, err
|
||||
|
||||
status, state = smb.start(host)
|
||||
if(status == false) then
|
||||
return "Error: " .. socket
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. state
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
status, result = smb.negotiate_protocol(socket)
|
||||
status, err = smb.negotiate_protocol(state)
|
||||
|
||||
if(status == false) then
|
||||
smb.stop(socket)
|
||||
return "Error: " .. result
|
||||
smb.stop(state)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. err
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local security_mode = result['security_mode']
|
||||
local security_mode = state['security_mode']
|
||||
|
||||
local response = ""
|
||||
|
||||
-- User-level authentication or share-level authentication
|
||||
@@ -108,7 +154,7 @@ action = function(host)
|
||||
response = response .. "SMB Security: Message signing not supported\n"
|
||||
end
|
||||
|
||||
smb.stop(socket)
|
||||
smb.stop(state)
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
145
scripts/smb-serverstats.nse
Normal file
145
scripts/smb-serverstats.nse
Normal file
@@ -0,0 +1,145 @@
|
||||
id = "MSRPC: Server statistics"
|
||||
description = [[
|
||||
Attempts to grab the server's statistics over SMB + MSRPC, which uses TCP
|
||||
ports 445 or 139.
|
||||
|
||||
An administrative account is required to pull these statistics on most versions
|
||||
of Windows, and Vista doesn't seem to let even the administrator account pull them.
|
||||
|
||||
Some of the numbers returned here don't feel right to me, but they're definitely
|
||||
the numbers that Windows returns. Take the values here with a grain of salt.
|
||||
]]
|
||||
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script smb-serverstats.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-serverstats.nse -p U:137,T:139 <host>
|
||||
--
|
||||
-- @output
|
||||
-- Host script results:
|
||||
-- | MSRPC: Server statistics:
|
||||
-- | Server statistics collected since 2008-10-17 09:32:41 (4d0h24m29s):
|
||||
-- | |_ Traffic 133467 bytes (0.38b/s) sent, 167696 bytes (0.48b/s) received
|
||||
-- | |_ Failed logins: 5
|
||||
-- | |_ Permission errors: 1, System errors: 0
|
||||
-- | |_ Print jobs spooled: 0
|
||||
-- |_ |_ Files opened (including pipes): 18
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
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)
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.SRVSVC_PATH)
|
||||
if(status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. smbstate
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Bind to SRVSVC service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.SRVSVC_UUID, msrpc.SRVSVC_VERSION, nil)
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. bind_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Call netservergetstatistics for 'server'
|
||||
status, netservergetstatistics_result = msrpc.srvsvc_netservergetstatistics(smbstate, host.ip)
|
||||
if(status == false) then
|
||||
smb.stop(smbstate)
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. netservergetstatistics_result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Stop the session
|
||||
smb.stop(smbstate)
|
||||
|
||||
-- Build the response
|
||||
local response = " \n"
|
||||
local period = os.time() - netservergetstatistics_result['start']
|
||||
local period_str
|
||||
|
||||
if(period == 0) then
|
||||
period = 1
|
||||
end
|
||||
|
||||
if(period > 60 * 60 * 24) then
|
||||
period_str = string.format("%dd%dh%02dm%02ds", period / (60*60*24), (period % (60*60*24)) / 3600, (period % 3600) / 60, period % 60)
|
||||
elseif(period > 60 * 60) then
|
||||
period_str = string.format("%dh%02dm%02ds", period / 3600, (period % 3600) / 60, period % 60)
|
||||
else
|
||||
period_str = string.format("%02dm%02ds", period / 60, period % 60)
|
||||
end
|
||||
|
||||
stats = netservergetstatistics_result
|
||||
response = response .. string.format("Server statistics collected since %s (%s):\n", netservergetstatistics_result['start_date'], period_str)
|
||||
response = response .. string.format("|_ Traffic %d bytes (%.2fb/s) sent, %d bytes (%.2fb/s) received\n", stats['bytessent'], stats['bytessent'] / period, stats['bytesrcvd'], stats['bytesrcvd'] / period)
|
||||
response = response .. string.format("|_ Failed logins: %d\n", stats['pwerrors'])
|
||||
response = response .. string.format("|_ Permission errors: %d, System errors: %d\n", stats['permerrors'], stats['syserrors'])
|
||||
response = response .. string.format("|_ Print jobs spooled: %s\n", stats['jobsqueued'])
|
||||
response = response .. string.format("|_ Files opened (including pipes): %d\n", stats['fopens'])
|
||||
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
265
scripts/smb-systeminfo.nse
Normal file
265
scripts/smb-systeminfo.nse
Normal file
@@ -0,0 +1,265 @@
|
||||
id = "System info"
|
||||
|
||||
description = [[
|
||||
Pulls back information about the remote system from the registry. Getting all
|
||||
of the information requires an administrative account, although a user account
|
||||
will still get a lot of it. Guest probably won't get any, nor will anonymous.
|
||||
This goes for all operating systems, including Windows 2000.
|
||||
|
||||
Windows Vista doesn't appear to have the WINREG binding (or it's different and
|
||||
I don't know it), so this doesn't support Vista at all.
|
||||
]]
|
||||
---
|
||||
-- @usage
|
||||
-- nmap --script smb-systeminfo.nse -p445 <host>
|
||||
-- sudo nmap -sU -sS --script smb-systeminfo.nse -p U:137,T:139 <host>
|
||||
--
|
||||
-- @output
|
||||
-- Host script results:
|
||||
-- | System info:
|
||||
-- | OS Details
|
||||
-- | |_ Microsoft Windows Server 2003 Service Pack 2 (ServerNT 5.2 build 3790)
|
||||
-- | |_ Installed on 2007-11-26 23:40:40
|
||||
-- | |_ Registered to IPC (organization: MYCOMPANY)
|
||||
-- | |_ Path: %SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;C:\Program Files\Microsoft SQL Server\90\Tools\binn\;C:\Program Files\IBM\Rational AppScan\
|
||||
-- | |_ Systemroot: C:\WINDOWS
|
||||
-- | |_ Page files: C:\pagefile.sys 2046 4092 (cleared at shutdown => 0)
|
||||
-- | Hardware
|
||||
-- | |_ CPU 0: Intel(R) Xeon(TM) CPU 2.80GHz [2780mhz GenuineIntel]
|
||||
-- | |_ Identifier 0: x86 Family 15 Model 2 Stepping 9
|
||||
-- | |_ CPU 1: Intel(R) Xeon(TM) CPU 2.80GHz [2780mhz GenuineIntel]
|
||||
-- | |_ Identifier 1: x86 Family 15 Model 2 Stepping 9
|
||||
-- | |_ CPU 2: Intel(R) Xeon(TM) CPU 2.80GHz [2780mhz GenuineIntel]
|
||||
-- | |_ Identifier 2: x86 Family 15 Model 2 Stepping 9
|
||||
-- | |_ CPU 3: Intel(R) Xeon(TM) CPU 2.80GHz [2780mhz GenuineIntel]
|
||||
-- | |_ Identifier 3: x86 Family 15 Model 2 Stepping 9
|
||||
-- | |_ Video driver: RAGE XL PCI Family (Microsoft Corporation)
|
||||
-- | Browsers
|
||||
-- | |_ Internet Explorer 7.0000
|
||||
-- |_ |_ Firefox 3.0.3 (en-US)
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The form DOMAIN\username and username@DOMAIN
|
||||
-- are NOT understood. To set a domain, use the smbdomain argument.
|
||||
--@args smbdomain The domain to log in with. If you aren't in a domained environment, then anything
|
||||
-- will (should?) be accepted by the server.
|
||||
--@args smbpassword The password to connect with. Be cautious with this, since some servers will lock
|
||||
-- accounts if the incorrect password is given (although it's rare for the
|
||||
-- 'administrator' account to be lockoutable, in the off chance that it is, you could
|
||||
-- get yourself in trouble).
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (2 x 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the Lanman or NTLM hash of the user's password,
|
||||
-- and are stored by systems, on the harddrive or memory. They can be retrived from memory
|
||||
-- using the fgdump or pwdump tools.
|
||||
--@args smbguest If this is set to 'true' or '1', a 'guest' login will be attempted if the normal one
|
||||
-- fails. This should be harmless, but I thought I would disable it by default anyway
|
||||
-- because I'm not entirely sure of any possible consequences.
|
||||
--@args smbtype The type of SMB authentication to use. By default, NTLMv1 is used, which is a pretty
|
||||
-- decent compromise between security and compatibility. If you are paranoid, you might
|
||||
-- want to use 'v2' or 'lmv2' for this (actually, if you're paranoid, you should be
|
||||
-- avoiding this protocol altogether :P). If you're using an extremely old system, you
|
||||
-- might need to set this to 'v1' or 'lm', which are less secure but more compatible.
|
||||
--
|
||||
-- If you want finer grained control, these are the possible options:
|
||||
-- * v1 -- Sends LMv1 and NTLMv1
|
||||
-- * LMv1 -- Sends LMv1 only
|
||||
-- * NTLMv1 -- Sends NTLMv1 only (default)
|
||||
-- * v2 -- Sends LMv2 and NTLMv2
|
||||
-- * LMv2 -- Sends LMv2 only
|
||||
--
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
---Retrieves the requested value from the registry.
|
||||
--@param smbstate The SMB table we're using, bound to the WINREG service.
|
||||
--@param handle The handle to the hive (HKLM or HKU, for example)
|
||||
--@param key The full path of the key to retrieve (like "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment")
|
||||
--@param value The value to retrieve (like, "NUMBER_OF_PROCESSORS")
|
||||
--@return (status, result) If status is false, result is an error message. Otherwise, result is the value of the key.
|
||||
local function reg_get_value(smbstate, handle, key, value)
|
||||
|
||||
-- Open the key
|
||||
status, openkey_result = msrpc.winreg_openkey(smbstate, handle, key)
|
||||
if(status == false) then
|
||||
return false, openkey_result
|
||||
end
|
||||
|
||||
-- Query the value
|
||||
status, queryvalue_result = msrpc.winreg_queryvalue(smbstate, openkey_result['handle'], value)
|
||||
if(status == false) then
|
||||
return false, queryvalue_result
|
||||
end
|
||||
|
||||
-- Close the key
|
||||
status, closekey_result = msrpc.winreg_closekey(smbstate, openkey_result['handle'], value)
|
||||
if(status == false) then
|
||||
return false, closekey_result
|
||||
end
|
||||
|
||||
return true, queryvalue_result['value']
|
||||
end
|
||||
|
||||
local function get_info_registry(host)
|
||||
|
||||
local result = {}
|
||||
|
||||
-- Create the SMB session
|
||||
status, smbstate = msrpc.start_smb(host, msrpc.WINREG_PATH)
|
||||
if(status == false) then
|
||||
return false, smbstate
|
||||
end
|
||||
|
||||
-- Bind to WINREG service
|
||||
status, bind_result = msrpc.bind(smbstate, msrpc.WINREG_UUID, msrpc.WINREG_VERSION, nil)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, bind_result
|
||||
end
|
||||
|
||||
-- Open HKEY_LOCAL_MACHINE
|
||||
status, openhklm_result = msrpc.winreg_openhklm(smbstate)
|
||||
if(status == false) then
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, openhklm_result
|
||||
end
|
||||
|
||||
-- Processor information
|
||||
result['status-number_of_processors'], result['number_of_processors'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "NUMBER_OF_PROCESSORS")
|
||||
if(status == false) then
|
||||
result['number_of_processors'] = 0
|
||||
end
|
||||
result['status-os'], result['os'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "OS")
|
||||
result['status-path'], result['path'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "Path")
|
||||
result['status-processor_architecture'], result['processor_architecture'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_ARCHITECTURE")
|
||||
result['status-processor_identifier'], result['processor_identifier'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_IDENTIFIER")
|
||||
result['status-processor_level'], result['processor_level'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_LEVEL")
|
||||
result['status-processor_revision'], result['processor_revision'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", "PROCESSOR_REVISION")
|
||||
|
||||
for i = 0, result['number_of_processors'] - 1, 1 do
|
||||
result['status-~mhz'..i], result['~mhz' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "~MHz")
|
||||
result['status-identifier'..i], result['identifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "Identifier")
|
||||
result['status-processornamestring'..i], result['processornamestring' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "ProcessorNameString")
|
||||
result['status-vendoridentifier'..i], result['vendoridentifier' .. i] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\" .. i, "VendorIdentifier")
|
||||
end
|
||||
-- status, result['physicalmemory'] = reg_get_value(smbstate, openhklm_result['handle'], "HARDWARE\\ResourceMap\\System Resources\\Physical Memory", ".Translated")
|
||||
|
||||
-- TODO: Known DLLs?
|
||||
|
||||
-- Paging file
|
||||
result['status-pagingfiles'], result['pagingfiles'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "PagingFiles")
|
||||
result['status-clearpagefileatshutdown'], result['clearpagefileatshutdown'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management", "ClearPageFileAtShutdown")
|
||||
|
||||
-- OS Information
|
||||
result['status-csdversion'], result['csdversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CSDVersion")
|
||||
if(result['status-csdversion'] == false) then
|
||||
result['csdversion'] = "(no service packs)"
|
||||
end
|
||||
result['status-currentbuildnumber'], result['currentbuildnumber'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentBuildNumber")
|
||||
result['status-currenttype'], result['currenttype'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentType")
|
||||
result['status-currentversion'], result['currentversion'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "CurrentVersion")
|
||||
result['status-installdate'], result['installdate'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "InstallDate")
|
||||
if(result['status-installdate'] ~= false) then
|
||||
result['installdate'] = os.date("%Y-%m-%d %H:%M:%S", result['installdate'])
|
||||
end
|
||||
|
||||
result['status-productname'], result['productname'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "Productname")
|
||||
result['status-registeredowner'], result['registeredowner'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOwner")
|
||||
result['status-registeredorganization'], result['registeredorganization'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "RegisteredOrganization")
|
||||
result['status-systemroot'], result['systemroot'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Windows NT\\CurrentVersion", "SystemRoot")
|
||||
result['status-producttype'], result['producttype'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductType")
|
||||
result['status-productsuite'], result['productsuite'] = reg_get_value(smbstate, openhklm_result['handle'], "System\\CurrentControlSet\\Control\\ProductOptions", "ProductSuite")
|
||||
|
||||
-- Driver information
|
||||
result['status-video_driverdesc'], result['video_driverdesc'] = reg_get_value(smbstate, openhklm_result['handle'], "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}\\0000", "DriverDesc")
|
||||
|
||||
-- Software versions
|
||||
result['status-ie_version'], result['ie_version'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Microsoft\\Internet Explorer\\Version Vector", "IE")
|
||||
result['status-ff_version'], result['ff_version'] = reg_get_value(smbstate, openhklm_result['handle'], "Software\\Mozilla\\Mozilla Firefox", "CurrentVersion")
|
||||
if(result['status-ff_version'] == false) then
|
||||
result['ff_version'] = "<not installed>"
|
||||
end
|
||||
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
return true, result
|
||||
end
|
||||
|
||||
action = function(host)
|
||||
|
||||
status, result = get_info_registry(host)
|
||||
|
||||
if(status == false) then
|
||||
if(nmap.debugging() > 0) then
|
||||
return "ERROR: " .. result
|
||||
else
|
||||
return nil
|
||||
end
|
||||
else
|
||||
|
||||
local response = " \n"
|
||||
|
||||
if(result['status-os'] == true) then
|
||||
response = response .. string.format("OS Details\n")
|
||||
response = response .. string.format("|_ %s %s (%s %s build %s)\n", result['productname'], result['csdversion'], result['producttype'], result['currentversion'], result['currentbuildnumber'])
|
||||
response = response .. string.format("|_ Installed on %s\n", result['installdate'])
|
||||
response = response .. string.format("|_ Registered to %s (organization: %s)\n", result['registeredowner'], result['registeredorganization'])
|
||||
response = response .. string.format("|_ Path: %s\n", result['path'])
|
||||
response = response .. string.format("|_ Systemroot: %s\n", result['systemroot'])
|
||||
response = response .. string.format("|_ Page files: %s (cleared at shutdown => %s)\n", result['pagingfiles'], result['clearpagefileatshutdown'])
|
||||
|
||||
response = response .. string.format("Hardware\n")
|
||||
for i = 0, result['number_of_processors'] - 1, 1 do
|
||||
response = response .. string.format("|_ CPU %d: %s [%dmhz %s]\n", i, result['processornamestring'..i], result['~mhz'..i], result['vendoridentifier'..i])
|
||||
response = response .. string.format("|_ Identifier %d: %s\n", i, result['identifier'..i])
|
||||
end
|
||||
response = response .. string.format("|_ Video driver: %s\n", result['video_driverdesc'])
|
||||
|
||||
response = response .. string.format("Browsers\n")
|
||||
response = response .. string.format("|_ Internet Explorer %s\n", result['ie_version'])
|
||||
if(result['status-ff_version']) then
|
||||
response = response .. string.format("|_ Firefox %s\n", result['ff_version'])
|
||||
end
|
||||
elseif(result['status-productname'] == true) then
|
||||
if(nmap.debugging() > 0) then
|
||||
response = response .. string.format("|_ Access was denied for certain values; try an administrative account for more complete information\n")
|
||||
end
|
||||
response = response .. string.format("OS Details\n")
|
||||
response = response .. string.format("|_ %s %s (%s %s build %s)\n", result['productname'], result['csdversion'], result['producttype'], result['currentversion'], result['currentbuildnumber'])
|
||||
response = response .. string.format("|_ Installed on %s\n", result['installdate'])
|
||||
response = response .. string.format("|_ Registered to %s (organization: %s)\n", result['registeredowner'], result['registeredorganization'])
|
||||
response = response .. string.format("|_ Systemroot: %s\n", result['systemroot'])
|
||||
else
|
||||
if(nmap.debugging() > 0) then
|
||||
response = string.format("|_ Account being used was unable to probe for information, try using an administrative account\n")
|
||||
else
|
||||
response = nil
|
||||
end
|
||||
end
|
||||
|
||||
return response
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user