Kerberoasting: How Any Domain User Turns a Service Account Into an Offline Cracking Job
#Any Authenticated User Can Pull a Crackable Copy of Your Service Account Password#link
Tim Medin stood up at DerbyCon in 2014 and showed a packed room how to turn a completely normal Kerberos ticket request into a password-cracking job. A decade later, the technique โ Kerberoasting โ still shows up in incident response reports, mapped by MITRE as T1558.003, with a group list running from ransomware crews to state-nexus actors. The uncomfortable part isn't that it works. It's that nothing is broken. The protocol behaves exactly as RFC 4120 says it should, and the attack lands because of decisions most of us made years ago and never revisited: passwords set once at install time, service accounts stacked into privileged groups, and an encryption downgrade path left open for compatibility.
How a Legitimate Request Becomes an Oracle
Kerberos in an Active Directory domain works in two steps. First you prove who you are to the domain controller and get a ticket-granting ticket (TGT). Then, for every service you touch, you ask the KDC for a service ticket, naming the service by its SPN โ something like MSSQLSvc/sql01.corp.local:1433. The KDC looks up which account owns that SPN, encrypts the ticket with a key derived from that account's password, and hands it over. You can't read it; the service can. That design is sound. The flaw in practice is that the KDC issues that encrypted ticket to any authenticated user who asks โ no rights on the server required, no network access to the service required, and the service doesn't even have to be running. Once you have the blob, verification never happens, because you never intend to use the ticket. You take it offline and attack the password that produced it.
This is not a vulnerability, and there is no patch. The KDC is encrypting tickets with the service account's key precisely as the protocol demands. That's why "apply the update" never appears on a Kerberoasting remediation list โ the only real fixes are credential hygiene and privilege design.
Step One Costs the Attacker Nothing: Build the Target List
Every account with an SPN registered is kerberoastable by definition, and finding them is a single LDAP query any domain user can run. Attackers sort the results by whatever signals privilege: adminCount=1, protected group membership, or SPNs that simply look important. The filter (servicePrincipalName=*)(adminCount=1) alone usually hands back the keys to the back door โ service accounts someone, at some point, dropped into privileged groups. Defenders should run the exact same query first, because if you can't answer "which of my SPN'd accounts could a junior helpdesk tech roast right now?", that's the gap.
Look at the PasswordLastSet column before anything else โ 2019, 2020, 2021. That's not an anomaly; it's the norm. Service account passwords get set during an installation window by whoever runs the installer, then never touched again, because nobody wants to find out which app breaks on rotation. The hash blob at the bottom is the payoff: with RC4-HMAC (etype 23), the Kerberos key is derived directly from the account's NT hash, so every guess tested against that ticket tests the same material an NTLM cracker would attack โ no salt, no per-user stretching. RFC 3962's AES path changes that equation substantially, but as the warning below covers, most networks let attackers pick RC4 anyway.
Fourteen seconds. Now the real question: what does svc-backup own? This is where Kerberoasting stops being a password problem and becomes a privilege problem. In most domains I've reviewed, the kerberoastable list and the privileged-access list overlap far more than anyone would design on purpose โ not because anyone decided a backup service account belongs in Domain Admins, but because SPNs get attached to whatever account the installer was using that day, and nobody audits the combination afterward. Crack one weak password, and the domain's own ticket service vouches for you everywhere that account can go.
| RC4-HMAC (etype 23) | AES256 (etype 18) | |
|---|---|---|
| Key material | NT hash of the account, used directly | Password-derived via PBKDF2 (4,096 iterations), salted with realm + username |
| hashcat mode | 13100 | 19700 |
| Throughput | Billions of guesses per second on commodity GPU hardware | Orders of magnitude slower โ the KDF dominates |
| Does a weak password survive? | Minutes to hours | Still dies, just slower โ patterned passwords crack either way |
โ ๏ธ Adding AES keys to an account does not stop RC4 tickets while RC4 remains enabled domain-wide. Standard tooling downgrades on purpose: Rubeus's /tgtdeleg exists specifically to harvest RC4-format tickets even from AES-capable targets, because the KDC still mints etype 23 tickets whenever the request allows it. Until RC4 is disabled at the domain level โ the phased process Microsoft laid out in KB5021131 โ treat every kerberoastable account as if its ticket came back in RC4, regardless of what keys it advertises.
What Actually Fixes It
gMSA (Group Managed Service Accounts) is the control that ends this attack class rather than slowing it down. The DC's KDS component generates a 240-byte random password, rotates it on a schedule (30 days by default), never exposes it to a human, and only releases it to the hosts you explicitly list in PrincipalsAllowedToRetrieveManagedPassword. There's nothing to crack in any practical sense โ 240 random bytes isn't a password, it's a key. Windows handles the plumbing: services log on as CORP\svc-sql-prod$ (note the dollar sign) and never see the secret. Where the platform can't use a gMSA, a 25+ character random string in a vault is the fallback โ long beats complex, always. And one cheap trick worth knowing: marking an account "Smart card is required for interactive logon" forces a random password nobody knows, making the NT hash effectively uncrackable โ a stopgap for accounts that can't move to gMSA yet, though it breaks interactive logon by design.
New-ADServiceAccount fails if no KDS root key exists in the forest โ create one with Add-KdsRootKey. The -EffectiveImmediately flag lies in practice: due to replication latency the key can take up to 10 hours to become usable, and gMSA creation fails mysteriously in the meantime. Plan key creation a day ahead, or verify with Get-KdsRootKey before blaming your syntax.
Detection Worth Its Keep โ and One Default That Betrays You
Event 4769 (Kerberos service ticket requested) is the core signal, and the first surprise is that most domains aren't logging it: the "Audit Kerberos Service Ticket Operations" subcategory is off by default on domain controllers, so enable it before assuming you're covered. Three signals earn their keep. Volume: one client address pulling dozens of distinct SPN tickets in minutes is a roast in progress. Encryption-type mismatch: 0x17 (RC4) requests in a domain where the accounts carry AES keys โ that's the downgrade path being walked, and it's rarely legitimate in a modern estate. Canary SPNs: decoy accounts with SPNs no legitimate workflow would request; any 4769 against one is an automatic incident. Expect noise: legacy apps genuinely still request RC4 tickets, so allowlist known-legacy accounts before tuning by source host.
One more thing worth saying plainly: service accounts deserve their own password policy. Complexity rules that produce "memorable compliant" passwords โ SeasonYear! and its thousand cousins โ are exactly the patterns rule-based attacks encode first. Fine-grained password policies exist precisely so you can demand 25+ random characters for SPN'd accounts while leaving human passwords alone. Length is the whole game here: RC4 cracking is fast, but it's still linear in the keyspace, and a vault-generated 30-character string sits outside every realistic keyspace.
- โชRun GetUserSPNs.py or Rubeus kerberoast /stats against your own domain this week with a read-only account and export the list โ that's your real exposure inventory.
- โชCheck PasswordLastSet on every SPN'd account; treat anything older than a year as compromised pending rotation.
- โชMigrate every kerberoastable account to gMSA where the platform supports it; vault-stored 25+ character randoms where it doesn't.
- โชEnable "Audit Kerberos Service Ticket Operations" on all DCs and get 4769 into the SIEM.
- โชPlant two or three canary SPNs and alert on any ticket request against them.
- โชStart the RC4 deprecation sequence from Microsoft's KB5021131 guidance โ on your schedule, not when a compatibility update forces it.
๐ก Detections matter, but notice the order of operations they imply: a detection tells you the ticket already left the building. The gMSA migration and the password policy are what keep it from leaving at all. Domains that "handled" Kerberoasting by writing a Sigma rule and moving on are the ones attackers visit twice.