Skip to main content

Kerberos Pre-Auth Username Enumeration

How it Works

We can send a request for a TGT --- without a pre-authentication hash --- to the Kerberos Key Distribution Center (KDC) with specific usernames in the request. If the username is valid, the KDC will prompt us for pre-authentication if required, or return a TGT if pre-authentication is not required. If the username is invalid, the KDC will respond with PRINCIPAL UNKNOWN.

Enumeration

Kerbrute

https://github.com/ropnop/kerbrute

Assemble Usernames

Option A) Huge, Sorted and Unique List of Usernames
tr '[:upper:]' '[:lower:]' < /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt | sort -u > kerberos_users.txt

Option B) Short, Sorted and Unique List of Usernames
tr '[:upper:]' '[:lower:]' < /usr/share/seclists/Usernames/Names/names.txt | sort -u > kerberos_users.txt

Option C) Shorter, Sorted and Unique List of Usernames
tr '[:upper:]' '[:lower:]' < /usr/share/seclists/Usernames/cirt-default-usernames.txt | sort -u > kerberos_users.txt

Option D) Generate Usernames from First and Last Names

See the comments in the script for username formatting. Default is first.lastf.lastflastlastf

Python Script (Show / Hide)
#! /usr/bin/env python3

import itertools
import threading
import os
from queue import Queue

male_first = '/usr/share/seclists/Usernames/Names/malenames-usa-top1000.txt'
female_first = '/usr/share/seclists/Usernames/Names/femalenames-usa-top1000.txt'
last_names = '/usr/share/seclists/Usernames/Names/familynames-usa-top1000.txt'
output_file = './kerberos_users.txt'
num_threads = 20

def load_names(file_path):
    with open(file_path, 'r') as file:
        return [line.strip().lower() for line in file]

def generate_permutations(first_names, last_names, output_queue):
    for first, last in itertools.product(first_names, last_names):
        """
        Most common naming conventions
        Examples:
            john.doe
            j.doe
            jdoe
            doej
        """
        output_queue.put(f"{first}.{last}")
        output_queue.put(f"{first[0]}.{last}")
        output_queue.put(f"{first[0]}{last}")
        output_queue.put(f"{last}{first[0]}")
      
        """
        Occasionally, some names have a last name that is also a first name or vice versa
        Uncomment if you wish to test on these combinations
        Examples:
          david.james
          taylor.james
        """
        #output_queue.put(f"{first}.{first}")
        #output_queue.put(f"{last}.{last}")
        #output_queue.put(f"{last[0]}.{last}")

        """
        Uncomment if the naming convention uses underscores
        Examples:
          john_doe
          j_doe
          doe_j
        """
        #output_queue.put(f"{first}_{last}")
        #output_queue.put(f"{first[0]}_{last}")
        #output_queue.put(f"{last}_{first}")

def worker(output_queue, results):
    """Process items from the queue and store them."""
    while True:
        item = output_queue.get()
        if item is None:
            break
        results.add(item)  # Already lowercase, directly store
        output_queue.task_done()

def main():
    first_names = load_names(male_first) + load_names(female_first)
    last_names_list = load_names(last_names)

    output_queue = Queue()
    results = set()

    threads = []
    for _ in range(num_threads):
        t = threading.Thread(target=worker, args=(output_queue, results))
        t.start()
        threads.append(t)

    generate_permutations(first_names, last_names_list, output_queue)

    output_queue.join()

    for _ in range(num_threads):
        output_queue.put(None)

    for t in threads:
        t.join()

    # Ensure output file exists
    if not os.path.exists(output_file):
        open(output_file, 'w').close()

    # Write unique, sorted results to file
    with open(output_file, 'w') as f:
        for name in sorted(results):
            f.write(name + '\n')

if __name__ == "__main__":
    main()

Python script to generate a list of username permutations from given word lists

Run Kerbrute

kerbrute userenum -d domain.tld --dc dc-ip-here -t 100 -o kerbrute.log ./kerberos_users.txt

Internal or via ligolo-ng (or similar) layer 3 tunnel

proxychains -q kerbrute userenum -d domain.tld --dc dc-ip-here -t 100 -o kerbrute.log ./kerberos_users.txt

External via SOCKS proxy -- e.g. chisel or ssh

Attempting to find AS-REP hashes

cat kerbrute.log
grep '@' kerbrute.log | awk -v FS=' ' '{print $7}' | cut -d '@' -f 1 > users.txt
cat users.txt

With usernames in users.txt, pass this into Impacket's GetNPUsers script to see if we can harvest AS-REP hashes: https://notes.benheater.com/books/active-directory/page/as-rep-roasting