the_open_door
coffee
ESEN
← Back to index

Kerberos user enumeration

ATT&CKT1087.002

In an Active Directory domain, the KDC (port 88) responds differently depending on whether an account exists, before checking the password. When you send a pre-auth-less AS-REQ for a non-existent user, the error is KDC_ERR_C_PRINCIPAL_UNKNOWN; for a valid one, the KDC asks for pre-auth (KDC_ERR_PREAUTH_REQUIRED). That difference lets you separate valid users without generating failed logons, making it far stealthier than an SMB spray.

The go-to tool is kerbrute:

kerbrute userenum --dc <ip> -d <domain> userlist.txt

Where the candidate list comes from. With no credentials, real names are often in plain sight: a "Team"/"About" page on the website, email signatures, PDF metadata. Those names are turned into plausible login formats (j.smith, john.smith, smithj...) with username-anarchy:

./username-anarchy --input-file names.txt > userlist.txt

Other ways to enumerate domain accounts:

# kerbrute chains a password spray once the users are validated
kerbrute passwordspray --dc <ip> -d <domain> userlist.txt '<pass>'
# netexec: validate over kerberos (no failed logons)
netexec smb <ip> -u userlist.txt -p '' -k
# RID cycling if a null session or guest is available
lookupsid.py <domain>/guest@<ip> -no-pass | grep SidTypeUser

kerbrute's output is a short list of accounts that truly exist, a base for password spraying or, if any doesn't require pre-auth, for AS-REP Roasting. It confirms no passwords, only existence.

Nuance. The technique depends on the domain not enforcing mechanisms that hide the difference and on clock sync (Kerberos rejects tickets with large skew). Generating thousands of AS-REQs is still detectable by volume even if it produces no failed-logon events.

How to detect it. Although it generates no failed logons, each AS-REQ leaves an Event ID 4768 (a Kerberos authentication ticket was requested) on the DC; for non-existent users the KDC replies KDC_ERR_C_PRINCIPAL_UNKNOWN, which shows up as a 4768 with result code 0x6. A flood of 4768 with 0x6 from a single IP, with usernames that do not exist, is the unmistakable signature of kerbrute userenum: nobody types hundreds of non-existent users by accident. Effective detection counts failed 4768 per source, not per account. On the network, a spike of AS-REQ to port 88 from a host that is not a domain-joined workstation reinforces the signal.

Defence. Use account names not derivable from the real name and don't publish user templates, so the attacker's candidate list misses. Related to brute force and SMTP user enumeration.

Machines where it's used