summaryrefslogtreecommitdiffstats
path: root/scripts/smb-enum-groups.nse
blob: c24308ffef389bf8e7bc35293f473a22cd871eba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
local msrpc = require "msrpc"
local smb = require "smb"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
local tableaux = require "tableaux"

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 <code>enum.exe</code> with the <code>/G</code> 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".

* <code>Bind</code>: bind to the SAMR service.
* <code>Connect4</code>: get a connect_handle.
* <code>EnumDomains</code>: get a list of the domains.
* <code>LookupDomain</code>: get the RID of the domains.
* <code>OpenDomain</code>: get a handle for each domain.
* <code>EnumDomainAliases</code>: get the list of groups in the domain.
* <code>OpenAlias</code>: get a handle to each group.
* <code>GetMembersInAlias</code>: get the RIDs of the members in the groups.
* <code>Close</code>: close the alias handle.
* <code>Close</code>: close the domain handle.
* <code>Close</code>: close the connect handle.

Once the RIDs have been termined, the
* <code>Bind</code>: bind to the LSA service.
* <code>OpenPolicy2</code>: get a policy handle.
* <code>LookupSids2</code>: convert SIDs to usernames.

I (Ron Bowes) originally looked into the possibility of using the SAMR function <code>LookupRids2</code>
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 <host>
-- sudo nmap -sU -sS --script smb-enum-users.nse -p U:137,T:139 <host>
--
-- @output
-- Host script results:
-- | smb-enum-groups:
-- |   Builtin\Administrators (RID: 544): Administrator, Daniel
-- |   Builtin\Users (RID: 545): <empty>
-- |   Builtin\Guests (RID: 546): Guest
-- |   Builtin\Performance Monitor Users (RID: 558): <empty>
-- |   Builtin\Performance Log Users (RID: 559): Daniel
-- |   Builtin\Distributed COM Users (RID: 562): <empty>
-- |   Builtin\IIS_IUSRS (RID: 568): <empty>
-- |   Builtin\Event Log Readers (RID: 573): <empty>
-- |   azure\HomeUsers (RID: 1000): Administrator, Daniel, HomeGroupUser$
-- |_  azure\HelpLibraryUpdaters (RID: 1003): <empty>
--
-- @xmloutput
-- <table key="Builtin">
--   <table key="RID 544">
--     <table key="member_sids">
--       <elem>S-1-5-21-12345678-1234567890-0987654321-500</elem>
--       <elem>S-1-5-21-12345678-1234567890-0987654321-1001</elem>
--     </table>
--     <elem key="name">Administrators</elem>
--     <table key="members">
--       <elem>Administrator</elem>
--       <elem>Daniel</elem>
--     </table>
--   </table>
--   <table key="RID 545">
--     <table key="member_sids">
--       <elem>S-1-5-4</elem>
--       <elem>S-1-5-11</elem>
--     </table>
--     <elem key="name">Users</elem>
--     <table key="members"></table>
--   </table>
--   <table key="RID 546">
--     <table key="member_sids">
--       <elem>S-1-5-21-12345678-1234567890-0987654321-501</elem>
--     </table>
--     <elem key="name">Guests</elem>
--     <table key="members">
--       <elem>Guest</elem>
--     </table>
--   </table>
--   <table key="RID 559">
--     <table key="member_sids">
--       <elem>S-1-5-21-12345678-1234567890-0987654321-1001</elem>
--     </table>
--     <elem key="name">Performance Log Users</elem>
--     <table key="members">
--       <elem>Daniel</elem>
--     </table>
--   </table>
--   <table key="RID 562">
--     <table key="member_sids"></table>
--     <elem key="name">Distributed COM Users</elem>
--     <table key="members"></table>
--   </table>
--   <table key="RID 568">
--     <table key="member_sids">
--       <elem>S-1-5-17</elem>
--     </table>
--     <elem key="name">IIS_IUSRS</elem>
--     <table key="members"></table>
--   </table>
-- </table>
-- <table key="azure">
--   <table key="RID 1000">
--     <table key="member_sids">
--       <elem>S-1-5-21-12345678-1234567890-0987654321-500</elem>
--       <elem>S-1-5-21-12345678-1234567890-0987654321-1001</elem>
--       <elem>S-1-5-21-12345678-1234567890-0987654321-1002</elem>
--     </table>
--     <elem key="name">HomeUsers</elem>
--     <table key="members">
--       <elem>Administrator</elem>
--       <elem>Daniel</elem>
--       <elem>HomeGroupUser$</elem>
--     </table>
--   </table>
--   <table key="RID 1003">
--     <table key="member_sids"></table>
--     <elem key="name">HelpLibraryUpdaters</elem>
--     <table key="members"></table>
--   </table>
-- </table>

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 = {"<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 = tableaux.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 = tableaux.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