Kerberoasting from Kali: why service accounts leak crackable tickets, and how a weak ACL widens the net

  • active-directory
  • kerberos
  • kerberoasting
  • acl-abuse
  • detection

Kerberoasting is one of the most reliable moves in Active Directory because it is not a bug. It is a direct consequence of how Kerberos is designed plus the fact that service-account passwords are chosen by humans. Any authenticated domain user can request a service ticket for any service account, the domain controller hands it back encrypted with that account's password-derived key, and you crack it offline at your own pace. This post explains why that is true, how to do it from Kali, and then how a weak permission in the directory extends the attack to accounts that have no reason to be kerberoastable at all.

Scope: assumes you already hold one valid domain credential (any user, no privileges) and can reach a domain controller. Everything here is a protocol property or a misconfiguration, so everything here closes to a password-length policy, a managed account, or a tightened ACL, not a patch.

Kerberoasting: one request, then an offline crack A domain user requests a service ticket for a service principal name. The domain controller returns the ticket encrypted with the service account key without checking authorization. The user cracks it offline with no lockout. Domain user any domain account Domain Controller the KDC 1 TGS-REQ: request a ticket for any SPN 2 TGS-REP: ticket sealed with the service account key encrypted with svc key the KDC never checks whether you may use it 3 crack it offline hashcat -m 13100 roast.hashes rockyou.txt yields the service account password no DC contact no lockout, no MFA
Any authenticated user can request a service ticket for any SPN, and the ticket comes back encrypted with the service account password, which is then cracked offline, with no lockout and no contact with the DC.

Why the weakness exists

Kerberos authenticates a user to a service without the service ever contacting the domain controller. To do that, the controller issues the user a service ticket (a TGS-REP) whose contents are encrypted with the target service's long-term key. That key is derived directly from the service account's password. The service can decrypt the ticket because it knows its own password; nobody else is supposed to be able to.

Three design facts combine into the weakness:

  • Any authenticated principal may request a ticket for any service. The controller does not check whether you are allowed to use the service. Issuing the ticket is not authorization, it is just the first step; authorization happens later at the service itself. So a low-privileged user can request a ticket for a high-privileged service account.
  • The ticket is encrypted with the account's password. A service account is identified by its Service Principal Name (SPN). Any account with an SPN set can have a ticket requested against it, and the ciphertext you get back is a direct function of that account's password.
  • You crack it offline, with no lockout and no second factor. Once the ticket is in your hands the domain controller is out of the loop. There is no bad-password counter, no lockout, no MFA. You grind it on your own hardware for as long as you like.

The weakest form is the RC4 ticket (Kerberos etype 23, written 0x17), whose key is just the account's NT hash. That makes it crack at NTLM speed. AES tickets (etypes 17 and 18) are slower to attack but still fall if the password is weak. The whole technique therefore rests on one human factor: service accounts frequently carry old, short, or guessable passwords that never rotate, because a person set them once and a running service depends on them.

This is catalogued as MITRE ATT&CK T1558.003. For where it sits among the other modern AD techniques, see the modern AD attack surface.

How it works from Kali

You need one domain credential and the domain controller's address. Impacket's GetUserSPNs.py does the whole thing: it finds every account with an SPN and requests a ticket for each.

# enumerate kerberoastable accounts (accounts that have an SPN)
impacket-GetUserSPNs 'CORP.LOCAL/jdoe:Password1' -dc-ip 10.10.10.10

# request the tickets and write them in hashcat format
impacket-GetUserSPNs 'CORP.LOCAL/jdoe:Password1' -dc-ip 10.10.10.10 \
  -request -outputfile roast.hashes

The output lines look like $krb5tgs$23$*... for RC4 or $krb5tgs$18$*... for AES. That is crackable material; feed it to hashcat (mode 13100), which the companion post covers end to end: cracking with hashcat.

Two Kali gotchas cost people the most time:

  • Clock skew. Kerberos rejects a request whose clock is more than five minutes off the controller's, with a KRB_AP_ERR_SKEW error that does not say "fix your clock." Sync first: sudo ntpdate 10.10.10.10 (or sudo rdate -n 10.10.10.10). If you cannot change the host clock, wrap the tool in faketime.
  • Prefer RC4 where you can. An RC4 ticket cracks far faster than AES. Tools such as Rubeus (kerberoast /tgtdeleg) can coerce RC4; with Impacket you take whatever etype the account allows. If an account is AES-only you still roast it, you just budget more cracking time.

Other front ends do the same request: NetExec (nxc ldap <dc> -u jdoe -p Password1 --kerberoasting roast.hashes) is the fastest one-liner, and Rubeus is the native-Windows option when you are already on a domain host.

Widening the net with a weak ACL: targeted Kerberoasting

Plain kerberoasting only reaches accounts that already have an SPN. An ordinary user account has none, so it is not kerberoastable. That changes the moment you can write to another account's servicePrincipalName attribute.

If you hold GenericAll, GenericWrite, or a WriteProperty on the SPN attribute over a user object (or WriteDACL/WriteOwner, which let you grant yourself one of those), you can add an SPN to that account yourself, request a ticket against the SPN you just created, crack it, and then remove the SPN to put the object back. This is targeted Kerberoasting, and it converts "accounts that happen to run a service" into "any account you have write access to", which on a sloppy domain can include high-value users.

The reason it works is that the directory treats "who may edit this attribute" and "is this account a service" as the same question. An SPN is just a string on the object; nothing checks that a real service lives behind it. BloodHound surfaces the write edge directly, so both sides find it the same way.

# targetedKerberoast.py adds a throwaway SPN, roasts, then removes it, per account you can write
python3 targetedKerberoast.py -v -d CORP.LOCAL -u jdoe -p Password1 --dc-ip 10.10.10.10

# or do it by hand with bloodyAD
bloodyAD -u jdoe -p Password1 -d CORP.LOCAL --host 10.10.10.10 \
  set object 'svc_target' servicePrincipalName -v 'fake/kerberoast'
# request the ticket (GetUserSPNs against that account), crack it, then:
bloodyAD -u jdoe -p Password1 -d CORP.LOCAL --host 10.10.10.10 \
  remove object 'svc_target' servicePrincipalName

The cleanup step matters twice: operationally, because leaving a fake SPN on a user is a loose end that breaks nothing but flags everything; and ethically on a real engagement, because you restore the object to the state you found it in. targetedKerberoast.py removes the SPN for you after roasting; if you do it by hand, delete what you added.

This step is also where the attack stops being quiet. Writing an SPN onto a user is a directory modification, and it is exactly the kind of change a defender can alert on (below).

Detection

  • A spike of RC4 service-ticket requests. Kerberos service-ticket operations are Security event 4769 on the domain controller. The high-signal filter is Ticket Encryption Type 0x17 (RC4) for many distinct service names from one account or host in a short window. Modern domains that are AES-first make RC4 requests stand out further, so an RC4 4769 to a sensitive SPN is worth alerting on directly.
  • The SPN write, for targeted Kerberoasting. Event 5136 (a directory-service object was modified) showing servicePrincipalName added to a user account is the tell. A user object gaining an SPN essentially never happens in normal administration, so this is close to a true-positive-only signal. Pair it with the matching 4769 that follows.
  • Honeypot service accounts. Create a decoy account with an SPN, a long random password, and no real service behind it. Any 4769 requesting its ticket is an attacker enumerating SPNs, because nothing legitimate ever uses it. This is one of the cheapest, highest-fidelity AD tripwires you can plant.
  • Enable the auditing first. 4769 needs Kerberos service-ticket auditing on, and 5136 needs Directory Service Changes auditing plus a SACL on the objects you care about. Neither is fully on by default; validate in a lab before you trust the absence of an alert.

Prevention and response

  • Make the password uncrackable. The one fix that defeats the whole technique is a service-account password that cannot be ground out: long (25+ characters) and random, or, far better, a managed account. Group Managed Service Accounts (gMSA) and the newer delegated MSAs carry 120-plus-character machine-managed passwords that rotate automatically, which takes offline cracking off the table entirely. Move every service you can onto one.
  • Prefer AES, retire RC4. Configure accounts and the domain for AES (etypes 17 and 18) and disable RC4 where your environment allows it. This does not fix a weak password, but it removes the NTLM-speed cracking shortcut.
  • Close the targeted-Kerberoast path with ACLs. Enumerate write access over user objects (BloodHound, or a direct ACL review) and remove GenericAll, GenericWrite, and SPN WriteProperty from principals that have no administrative reason to hold them. A least-privilege directory is what stops an attacker from minting SPNs on accounts that should never have one. This is the same ACL hygiene that closes the shadow-credential and forced-password paths in the modern AD attack surface.
  • Audit existing SPNs on user accounts. Any user (not computer, not managed service account) that already carries an SPN is either a legacy service to migrate onto a gMSA or an artifact worth questioning.
  • Response after a confirmed roast. Treat the service account's password as known: rotate it immediately, and audit everywhere that account can authenticate and what it can reach, because the attacker has had it since the moment they cracked it. If the roasted account was itself privileged, widen the response accordingly.

Use

This is written for authorized assessment and for the defenders who have to shut it. The uncomfortable part of Kerberoasting is that the request side is indistinguishable from normal Kerberos, so you do not catch the attack by watching for something malformed, you catch it by watching for RC4 where you expect AES, for an SPN appearing on a user, and for a decoy account being touched at all. The durable fixes are upstream of all of that: managed passwords that cannot be cracked, and ACLs tight enough that nobody can turn an ordinary user into a service.

References

Back to all techniques