summaryrefslogtreecommitdiffstats
path: root/scripts/smb-enum-domains.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/smb-enum-domains.nse')
-rw-r--r--scripts/smb-enum-domains.nse123
1 files changed, 123 insertions, 0 deletions
diff --git a/scripts/smb-enum-domains.nse b/scripts/smb-enum-domains.nse
new file mode 100644
index 0000000..6f6cf14
--- /dev/null
+++ b/scripts/smb-enum-domains.nse
@@ -0,0 +1,123 @@
+local math = require "math"
+local msrpc = require "msrpc"
+local smb = require "smb"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+
+description = [[
+Attempts to enumerate domains on a system, along with their policies. This generally requires
+credentials, except against Windows 2000. In addition to the actual domain, the "Builtin"
+domain is generally displayed. Windows returns this in the list of domains, but its policies
+don't appear to be used anywhere.
+
+Much of the information provided is useful to a penetration tester, because it tells the
+tester what types of policies to expect. For example, if passwords have a minimum length of 8,
+the tester can trim his database to match; if the minimum length is 14, the tester will
+probably start looking for sticky notes on people's monitors.
+
+Another useful piece of information is the password lockouts. A penetration tester often wants
+to know whether or not there's a risk of negatively impacting a network, and this will
+indicate it. The SID is displayed, which may be useful in other tools; the users are listed,
+which uses different functions than <code>smb-enum-users.nse</code> (though likely won't
+get different results), and the date and time the domain was created may give some insight into
+its history.
+
+After the initial <code>bind</code> to SAMR, the sequence of calls is:
+* <code>Connect4</code>: get a connect_handle
+* <code>EnumDomains</code>: get a list of the domains (stop here if you just want the names).
+* <code>QueryDomain</code>: get the SID for the domain.
+* <code>OpenDomain</code>: get a handle for each domain.
+* <code>QueryDomainInfo2</code>: get the domain information.
+* <code>QueryDomainUsers</code>: get a list of the users in the domain.
+]]
+
+---
+-- @usage
+-- nmap --script smb-enum-domains.nse -p445 <host>
+-- sudo nmap -sU -sS --script smb-enum-domains.nse -p U:137,T:139 <host>
+--
+-- @output
+-- Host script results:
+-- | smb-enum-domains:
+-- | WINDOWS2000
+-- | Groups: n/a
+-- | Users: Administrator, blah, Guest, testpass, ron, test, user
+-- | Creation time: 2009-10-17 12:45:47
+-- | Passwords: min length: n/a; min age: 5 days; max age: 100 days; history: 10 passwords
+-- | Properties: Complexity requirements exist
+-- | Account lockout: 5 attempts in 30 minutes will lock out the account for 30 minutes
+-- | Builtin
+-- | Groups: Administrators, Backup Operators, Guests, Power Users, Replicator, Users
+-- | Users: n/a
+-- | Creation time: 2009-10-17 12:45:46
+-- | Passwords: min length: n/a; min age: n/a days; max age: 42 days; history: n/a passwords
+-- |_ Account lockout disabled
+-----------------------------------------------------------------------
+
+author = "Ron Bowes"
+copyright = "Ron Bowes"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"discovery","intrusive"}
+dependencies = {"smb-brute"}
+
+
+-- TODO: This script needs some love...
+
+hostrule = function(host)
+ return smb.get_port(host) ~= nil
+end
+
+action = function(host)
+
+ local status, result = msrpc.get_domains(host)
+
+ if(not(status)) then
+ return stdnse.format_output(false, result)
+ else
+ local response = {}
+
+ for domain, data in pairs(result) do
+ local piece = {}
+ piece['name'] = domain
+
+ if(#data.groups > 0) then
+ table.insert(piece, string.format("Groups: %s", table.concat(data.groups, ", ")))
+ else
+ table.insert(piece, "Groups: n/a")
+ end
+
+ if(#data.users > 0) then
+ table.insert(piece, string.format("Users: %s", table.concat(data.users, ", ")))
+ else
+ table.insert(piece, "Users: n/a")
+ end
+
+ -- Floor data.max_password_age, if possible
+ if(data.max_password_age) then
+ data.max_password_age = math.floor(data.max_password_age)
+ end
+
+ table.insert(piece, string.format("Creation time: %s", data.created))
+ table.insert(piece, string.format("Passwords: min length: %s; min age: %s days; max age: %s days; history: %s passwords",
+ data.min_password_length or "n/a",
+ data.min_password_age or "n/a",
+ data.max_password_age or "n/a",
+ data.password_history or "n/a"))
+ if(data.password_properties and #data.password_properties) then
+ table.insert(piece, string.format("Properties: %s", table.concat(data.password_properties, ", ")))
+ end
+
+ if(data.lockout_threshold) then
+ table.insert(piece, string.format("Account lockout: %s attempts in %s minutes will lock out the account for %s minutes", data.lockout_threshold, data.lockout_window or "unlimited", data.lockout_duration or "unlimited"))
+ else
+ table.insert(piece, "Account lockout disabled")
+ end
+
+ table.insert(response, piece)
+ end
+
+ return stdnse.format_output(true, response)
+ end
+end
+