CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/enum_domain_tokens.rb
Views: 11655
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::Windows::Priv
8
9
def initialize(info = {})
10
super(
11
update_info(
12
info,
13
'Name' => 'Windows Gather Enumerate Domain Tokens',
14
'Description' => %q{
15
This module enumerates domain account tokens, processes running under
16
domain accounts, and domain users in the local Administrators, Users
17
and Backup Operator groups.
18
},
19
'License' => MSF_LICENSE,
20
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
21
'Platform' => [ 'win'],
22
'SessionTypes' => [ 'meterpreter' ],
23
'Notes' => {
24
'Stability' => [CRASH_SAFE],
25
'Reliability' => [],
26
'SideEffects' => []
27
},
28
'Compat' => {
29
'Meterpreter' => {
30
'Commands' => %w[
31
incognito_list_tokens
32
stdapi_sys_config_getuid
33
]
34
}
35
}
36
)
37
)
38
end
39
40
def run
41
hostname = sysinfo.nil? ? cmd_exec('hostname') : sysinfo['Computer']
42
print_status("Running module against #{hostname} (#{session.session_host})")
43
44
domain = get_domain_name
45
46
fail_with(Failure::Unknown, 'Could not retrieve domain name. Is the host part of a domain?') unless domain
47
48
@domain_admins = get_members_from_group('Domain Admins', domain) || []
49
50
print_error("Could not retrieve '#{domain}\\Domain Admins' group members.") if @domain_admins.blank?
51
52
netbios_domain_name = domain.split('.').first.upcase
53
54
uid = client.sys.config.getuid
55
if uid.starts_with?(netbios_domain_name)
56
user = uid.split('\\')[1]
57
print_good('Current session is running under a Domain Admin account') if @domain_admins.include?(user)
58
end
59
60
if domain_controller?
61
if is_system?
62
print_good('Current session is running as SYSTEM on a domain controller')
63
elsif is_admin?
64
print_good('Current session is running under a Local Admin account on a domain controller')
65
else
66
print_status('This host is a domain controller')
67
end
68
else
69
if is_system?
70
print_good('Current session is running as SYSTEM')
71
elsif is_admin?
72
print_good('Current session is running under a Local Admin account')
73
end
74
print_status('This host is not a domain controller')
75
76
list_group_members(netbios_domain_name)
77
end
78
79
list_processes(netbios_domain_name)
80
list_tokens(netbios_domain_name)
81
end
82
83
def list_group_members(domain)
84
tbl = Rex::Text::Table.new(
85
'Header' => 'Account in Local Groups with Domain Context',
86
'Indent' => 1,
87
'Columns' =>
88
[
89
'Local Group',
90
'Member',
91
'Domain Admin'
92
]
93
)
94
95
print_status('Checking local groups for Domain Accounts and Groups')
96
97
[
98
'Administrators',
99
'Backup Operators',
100
'Users'
101
].each do |group|
102
group_users = get_members_from_localgroup(group)
103
104
next unless group_users
105
106
vprint_status("Group '#{group}' members: #{group_users.join(', ')}")
107
108
group_users.each do |group_user|
109
next unless group_user.include?(domain)
110
111
user = group_user.split('\\')[1]
112
tbl << [group, group_user, @domain_admins.include?(user)]
113
end
114
end
115
116
print_line("\n#{tbl}\n")
117
end
118
119
def list_tokens(domain)
120
tbl = Rex::Text::Table.new(
121
'Header' => 'Impersonation Tokens with Domain Context',
122
'Indent' => 1,
123
'Columns' =>
124
[
125
'Token Type',
126
'Account Type',
127
'Account Name',
128
'Domain Admin'
129
]
130
)
131
print_status('Checking for Domain group and user tokens')
132
133
user_tokens = client.incognito.incognito_list_tokens(0)
134
user_delegation = user_tokens['delegation'].split("\n")
135
user_impersonation = user_tokens['impersonation'].split("\n")
136
137
user_delegation.each do |dt|
138
next unless dt.include?(domain)
139
140
user = dt.split('\\')[1]
141
tbl << ['Delegation', 'User', dt, @domain_admins.include?(user)]
142
end
143
144
user_impersonation.each do |dt|
145
next if dt == 'No tokens available'
146
next unless dt.include?(domain)
147
148
user = dt.split('\\')[1]
149
tbl << ['Impersonation', 'User', dt, @domain_admins.include?(user)]
150
end
151
152
group_tokens = client.incognito.incognito_list_tokens(1)
153
group_delegation = group_tokens['delegation'].split("\n")
154
group_impersonation = group_tokens['impersonation'].split("\n")
155
156
group_delegation.each do |dt|
157
next unless dt.include?(domain)
158
159
user = dt.split('\\')[1]
160
tbl << ['Delegation', 'Group', dt, @domain_admins.include?(user)]
161
end
162
163
group_impersonation.each do |dt|
164
next if dt == 'No tokens available'
165
next unless dt.include?(domain)
166
167
user = dt.split('\\')[1]
168
tbl << ['Impersonation', 'Group', dt, @domain_admins.include?(user)]
169
end
170
171
if tbl.rows.empty?
172
print_status('No domain tokens available')
173
return
174
end
175
176
print_line("\n#{tbl}\n")
177
end
178
179
def list_processes(domain)
180
tbl = Rex::Text::Table.new(
181
'Header' => 'Processes under Domain Context',
182
'Indent' => 1,
183
'Columns' =>
184
[
185
'Process Name',
186
'PID',
187
'Arch',
188
'User',
189
'Domain Admin'
190
]
191
)
192
print_status('Checking for processes running under domain user')
193
client.sys.process.processes.each do |p|
194
next unless p['user'].include?(domain)
195
196
user = p['user'].split('\\')[1]
197
tbl << [
198
p['name'],
199
p['pid'],
200
p['arch'],
201
p['user'],
202
@domain_admins.include?(user)
203
]
204
end
205
206
if tbl.rows.empty?
207
print_status('No processes running as domain users')
208
return
209
end
210
211
print_line("\n#{tbl}\n")
212
end
213
end
214
215