local msrpc = require "msrpc"
local smb = require "smb"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Obtains a list of groups from the remote Windows system, as well as a list of the group's users.
This works similarly to enum.exe
with the /G
switch.
The following MSRPC functions in SAMR are used to find a list of groups and the RIDs of their users. Keep
in mind that MSRPC refers to groups as "Aliases".
* Bind
: bind to the SAMR service.
* Connect4
: get a connect_handle.
* EnumDomains
: get a list of the domains.
* LookupDomain
: get the RID of the domains.
* OpenDomain
: get a handle for each domain.
* EnumDomainAliases
: get the list of groups in the domain.
* OpenAlias
: get a handle to each group.
* GetMembersInAlias
: get the RIDs of the members in the groups.
* Close
: close the alias handle.
* Close
: close the domain handle.
* Close
: close the connect handle.
Once the RIDs have been termined, the
* Bind
: bind to the LSA service.
* OpenPolicy2
: get a policy handle.
* LookupSids2
: convert SIDs to usernames.
I (Ron Bowes) originally looked into the possibility of using the SAMR function LookupRids2
to convert RIDs to usernames, but the function seemed to return a fault no matter what I tried. Since
enum.exe also switches to LSA to convert RIDs to usernames, I figured they had the same issue and I do
the same thing.
]]
---
-- @usage
-- nmap --script smb-enum-users.nse -p445
-- sudo nmap -sU -sS --script smb-enum-users.nse -p U:137,T:139
--
-- @output
-- Host script results:
-- | smb-enum-groups:
-- | Builtin\Administrators (RID: 544): Administrator, Daniel
-- | Builtin\Users (RID: 545):
-- | Builtin\Guests (RID: 546): Guest
-- | Builtin\Performance Monitor Users (RID: 558):
-- | Builtin\Performance Log Users (RID: 559): Daniel
-- | Builtin\Distributed COM Users (RID: 562):
-- | Builtin\IIS_IUSRS (RID: 568):
-- | Builtin\Event Log Readers (RID: 573):
-- | azure\HomeUsers (RID: 1000): Administrator, Daniel, HomeGroupUser$
-- |_ azure\HelpLibraryUpdaters (RID: 1003):
--
-- @xmloutput
--
--
--
-- S-1-5-21-12345678-1234567890-0987654321-500
-- S-1-5-21-12345678-1234567890-0987654321-1001
--
-- Administrators
--
-- Administrator
-- Daniel
--
--
--
--
-- S-1-5-4
-- S-1-5-11
--
-- Users
--
--
--
--
-- S-1-5-21-12345678-1234567890-0987654321-501
--
-- Guests
--
--
--
--
-- S-1-5-21-12345678-1234567890-0987654321-1001
--
-- Performance Log Users
--
--
--
--
-- Distributed COM Users
--
--
--
--
--
--
--
-- S-1-5-21-12345678-1234567890-0987654321-500
-- S-1-5-21-12345678-1234567890-0987654321-1001
-- S-1-5-21-12345678-1234567890-0987654321-1002
--
-- HomeUsers
--
-- Administrator
-- Daniel
-- HomeGroupUser$
--
--
--
--
-- HelpLibraryUpdaters
--
--
--
author = "Ron Bowes"
copyright = "Ron Bowes"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery","intrusive"}
dependencies = {"smb-brute"}
hostrule = function(host)
return smb.get_port(host) ~= nil
end
local empty = {""}
action = function(host)
local status, groups = msrpc.samr_enum_groups(host)
if(not(status)) then
return stdnse.format_output(false, "Couldn't enumerate groups: " .. groups)
end
local response = stdnse.output_table()
local response_str = {}
local domains = stdnse.keys(groups)
table.sort(domains)
for _, domain_name in ipairs(domains) do
local dom_groups = stdnse.output_table()
response[domain_name] = dom_groups
local domain_data = groups[domain_name]
local rids = stdnse.keys(domain_data)
table.sort(rids)
for _, rid in ipairs(rids) do
local group_data = domain_data[rid]
-- TODO: Map SIDs to names, show non-named SIDs
table.insert(response_str,
string.format("\n %s\\%s (RID: %s): %s", domain_name, group_data.name, rid,
table.concat(#group_data.members > 0 and group_data.members or empty, ", "))
)
dom_groups[string.format("RID %d", rid)] = group_data
end
end
return response, table.concat(response_str)
end