Kerberoasting is the technique every red team uses and every blue team underdetects. An attacker requests Kerberos TGS (Ticket Granting Service) tickets for service accounts, then cracks the encrypted portion offline to recover plaintext passwords. The attack leaves Event 4769 footprints on Domain Controllers that most SOCs ignore — and that’s exactly what makes Kerberoasting so effective in real breaches. This guide builds comprehensive Kerberoasting detection in Splunk: the Event 4769 query patterns that catch RC4 encryption abuse, service account targeting, volume anomalies, and the Splunk dashboards that turn raw Kerberos logs into actionable security intelligence.
Kerberoasting isn’t theoretical. It’s the #3 technique in privilege escalation according to the 2024 MITRE ATT&CK evaluation, deployed in 70%+ of Active Directory compromises, and the primary path from initial access to Domain Admin in environments that haven’t hardened their service accounts. The detection challenge: legitimate applications request TGS tickets constantly, so the attack blends into normal traffic. The solution: specific query patterns that identify the signatures Kerberoasting tools leave in Event 4769.
TL;DR — the essential Kerberoasting detection queries
If you only deploy three Splunk searches, use these:
1. RC4 encryption volume spike (daily threshold):
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17"
| bucket _time span=1d | stats count by _time, Account_Name
| where count > 5 | sort -count
2. Bulk TGS requests from single source (hourly sweep):
index=wineventlog source="WinEventLog:Security" EventCode=4769
| bucket _time span=1h | stats dc(Service_Name) as unique_services by _time, Client_Address
| where unique_services > 50
3. High-privilege service account targeting:
index=wineventlog source="WinEventLog:Security" EventCode=4769
Account_Name IN ("*sql*", "*svc*", "*service*", "*admin*", "*exchange*")
| stats count by Account_Name, Client_Address | where count > 10
Deploy these as hourly scheduled searches with email alerts, and you’ll catch 80%+ of Kerberoasting attempts. The sections below build the complete detection stack.
Understanding Kerberoasting and Event 4769
Kerberoasting exploits a fundamental characteristic of Kerberos authentication: when a user requests access to a service (SQL Server, IIS, SharePoint), the Domain Controller issues a TGS ticket encrypted with the service account’s password hash. That encrypted ticket travels to the client, which presents it to the target service. The service decrypts the ticket with its own password to validate the request.
The attack vector: an attacker can request TGS tickets for any service account without needing to access the actual service. They collect encrypted tickets, then crack them offline with hashcat, John the Ripper, or custom tools. If the service account uses a weak password, the offline crack succeeds, and the attacker has plaintext credentials for a potentially high-privilege account.
Event 4769 is generated on Domain Controllers when they issue TGS tickets. The full event includes:
- Account_Name — the service account (SPN owner) the ticket was issued for
- Client_Address — source IP of the requester
- Service_Name — the Service Principal Name (SPN) requested
- Ticket_Encryption_Type — encryption algorithm used (RC4, AES128, AES256)
- Client_Name — the user who requested the ticket
Normal application behavior generates thousands of Event 4769 entries daily. Kerberoasting generates them with specific patterns that stand out when you know what to look for.
Why RC4 encryption is the smoking gun
The strongest Kerberoasting signal is RC4-HMAC encryption (Type 0x17) in Event 4769. Modern Windows environments prefer AES-256 (Type 0x12) or AES-128 (Type 0x11) for Kerberos encryption — they’re faster and more secure. RC4 is legacy, maintained primarily for backward compatibility.
Kerberoasting tools default to RC4 for two reasons:
- RC4 hashes crack faster offline than AES (10x-50x speed difference in hashcat)
- Impacket’s GetUserSPNs.py explicitly requests RC4 unless you override with
-aesKey
In a healthy AD environment, RC4 should be rare — maybe 1-5% of TGS requests. During Kerberoasting, it spikes to 30-80% because attackers want the weakest encryption for their offline cracking.
Event 4769 field reference for Splunk
When forwarding Windows Security logs to Splunk, Event 4769 maps to these field names:
| Splunk Field | Event Description | Kerberoasting Relevance |
|---|---|---|
Account_Name | Service account the TGS was issued for | Target identification — look for privileged accounts |
Client_Address | IP address of requesting client | Hunting for single IPs targeting multiple accounts |
Client_Name | Username who requested the ticket | Often the initial-access account in Kerberoasting |
Service_Name | Full SPN (service principal name) | Service type identification |
Ticket_Encryption_Type | Hex encryption type (0x17=RC4, 0x12=AES256, 0x11=AES128) | RC4 volume is the primary detection signal |
Failure_Code | Success (0x0) or failure code | Successful TGS requests only matter for Kerberoasting |
Computer_Name | Domain Controller that issued the ticket | Load balancing validation |
Core Kerberoasting detection queries
Query 1: RC4 encryption anomaly detection
The baseline query for RC4 volume thresholds:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17" Failure_Code="0x0"
| eval day=strftime(_time, "%Y-%m-%d")
| stats count by day, Account_Name, Client_Address
| eventstats avg(count) as baseline_avg, stdev(count) as baseline_stdev by Account_Name
| eval upper_bound = baseline_avg + 2 * baseline_stdev
| where count > upper_bound OR count > 20
| sort -count
| table day, Account_Name, Client_Address, count, baseline_avg, upper_bound
This query identifies:
- Service accounts receiving unusually high RC4 TGS requests
- Baselines normal RC4 volume per account (30-day rolling average)
- Flags accounts exceeding 2 standard deviations OR absolute threshold of 20 per day
Query 2: Bulk service account enumeration
Detects wide SPN enumeration — a common Kerberoasting reconnaissance pattern:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Failure_Code="0x0"
| bucket _time span=1h
| stats dc(Account_Name) as unique_accounts, dc(Service_Name) as unique_services, count by _time, Client_Address, Client_Name
| where unique_accounts > 25 OR unique_services > 50
| eval risk_score = case(
unique_accounts > 50, "HIGH",
unique_accounts > 35, "MEDIUM",
1=1, "LOW")
| sort -unique_accounts
| table _time, Client_Address, Client_Name, unique_accounts, unique_services, count, risk_score
This query catches:
- Single clients requesting TGS tickets for 25+ different service accounts in one hour
- Bulk SPN enumeration (GetUserSPNs.py, Invoke-Kerberoast patterns)
- Risk-scores the activity based on enumeration breadth
Query 3: High-value service account targeting
Focuses on accounts attackers typically prioritize:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Failure_Code="0x0"
| regex Account_Name="(?i).*(sql|svc|service|admin|exchange|sharepoint|backup|vault).*"
| bucket _time span=4h
| stats count, values(Ticket_Encryption_Type) as encryption_types by _time, Account_Name, Client_Address
| where count > 10
| eval rc4_used = if(match(encryption_types, "0x17"), "YES", "NO")
| sort -count
| table _time, Account_Name, Client_Address, count, rc4_used, encryption_types
This query identifies:
- Repeated TGS requests against privileged service accounts
- Whether RC4 encryption was used (higher Kerberoasting likelihood)
- 4-hour windows to catch persistent attacks
Query 4: Time-based attack pattern detection
Kerberoasting often happens in bursts — attackers request many tickets rapidly:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Failure_Code="0x0"
| bucket _time span=5m
| stats count by _time, Client_Address, Account_Name
| where count > 5
| eventstats max(count) as max_5min by Client_Address, Account_Name
| where max_5min > 15
| sort -_time -count
| table _time, Client_Address, Account_Name, count, max_5min
This query detects:
- Rapid TGS request bursts (>5 per 5-minute window)
- Single client-account pairs with peak activity >15 requests/5min
- Time correlation for incident response scoping
Advanced detection patterns
Detecting GetUserSPNs.py tool signatures
Impacket’s GetUserSPNs.py leaves specific patterns in Event 4769:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Failure_Code="0x0"
| stats count, dc(Account_Name) as unique_spns, values(Ticket_Encryption_Type) as enc_types by Client_Address, Client_Name, _time
| where unique_spns > 10 AND match(enc_types, "0x17")
| eval pattern_score = case(
unique_spns > 50, 10,
unique_spns > 30, 7,
unique_spns > 15, 5,
1=1, 3)
| eval rc4_ratio = mvcount(mvfilter(match(enc_types, "0x17"))) / mvcount(enc_types)
| where rc4_ratio > 0.8 OR pattern_score >= 7
| table _time, Client_Address, Client_Name, unique_spns, count, rc4_ratio, pattern_score
Kerberoasting during off-hours
Many attacks happen outside business hours to avoid detection:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17"
| eval hour = strftime(_time, "%H"), day_of_week = strftime(_time, "%w")
| where (hour < 6 OR hour > 20) OR day_of_week IN ("0", "6")
| stats count by hour, Account_Name, Client_Address
| where count > 5
| sort -count
Cross-correlation with authentication failures
Successful Kerberoasting often follows failed authentication attempts:
(index=wineventlog source="WinEventLog:Security" EventCode=4625) OR
(index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17")
| eval event_type = case(EventCode=4625, "auth_failure", EventCode=4769, "tgs_request", 1=1, "unknown")
| transaction Client_Address maxspan=1h
| where mvcount(event_type) > 1 AND match(event_type, "auth_failure") AND match(event_type, "tgs_request")
| table _time, Client_Address, Account_Name, event_type
Building a Kerberoasting dashboard
Create a dedicated Splunk dashboard with these panels:
Panel 1: RC4 Encryption Volume (24-hour trend)
index=wineventlog source="WinEventLog:Security" EventCode=4769
| eval encryption_type = case(
Ticket_Encryption_Type="0x17", "RC4-HMAC",
Ticket_Encryption_Type="0x12", "AES256-CTS",
Ticket_Encryption_Type="0x11", "AES128-CTS",
1=1, "Other")
| timechart span=1h count by encryption_type
Panel 2: Top Service Accounts by TGS Volume
index=wineventlog source="WinEventLog:Security" EventCode=4769 Failure_Code="0x0"
earliest=-24h | stats count by Account_Name
| where count > 50 | sort -count | head 20
Panel 3: Suspicious Client Activity Map
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17"
earliest=-24h | stats count, dc(Account_Name) as unique_accounts by Client_Address
| where unique_accounts > 10 | sort -unique_accounts
| geostats latfield=latitude longfield=longitude count by Client_Address
Panel 4: Encryption Type Distribution (Pie Chart)
index=wineventlog source="WinEventLog:Security" EventCode=4769 earliest=-24h
| eval enc_type = case(
Ticket_Encryption_Type="0x17", "RC4 (Weak)",
Ticket_Encryption_Type="0x12", "AES256 (Strong)",
Ticket_Encryption_Type="0x11", "AES128 (Strong)",
1=1, "Unknown")
| stats count by enc_type
Alerting strategy
Configure these scheduled searches as alerts:
High Priority Alert: Active Kerberoasting
Run every 15 minutes:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17" earliest=-15m
| stats count, dc(Account_Name) as accounts by Client_Address
| where accounts > 5 AND count > 25
Alert action: Email SOC, create incident, block Client_Address at firewall
Medium Priority Alert: RC4 Volume Threshold
Run every hour:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17" earliest=-1h
| stats count by Account_Name | where count > 15
Alert action: Email identity team, flag account for password rotation
Low Priority Alert: Unusual Time Activity
Run daily:
index=wineventlog source="WinEventLog:Security" EventCode=4769 Ticket_Encryption_Type="0x17" earliest=-24h
| eval hour = strftime(_time, "%H")
| where hour < 6 OR hour > 22 | stats count by Account_Name | where count > 3
Alert action: Email security team, add to investigation queue
Tuning and baselining
Establishing RC4 baselines
Run this query to understand normal RC4 usage in your environment:
index=wineventlog source="WinEventLog:Security" EventCode=4769 earliest=-30d
| eval enc_type = case(
Ticket_Encryption_Type="0x17", "RC4",
1=1, "Other")
| bucket _time span=1d | stats count by _time, enc_type
| xyseries _time, enc_type, count | fillnull value=0
| eval rc4_percentage = round((RC4 / (RC4 + Other)) * 100, 2)
| stats avg(rc4_percentage) as avg_rc4, stdev(rc4_percentage) as stdev_rc4
Use the results to set appropriate thresholds. If your baseline RC4 is 2%, alert when it exceeds 8%. If it’s 15%, alert at 30%.
Account whitelist for legitimate high-volume services
Some accounts generate legitimate high TGS volume:
| eval whitelist_accounts = "exchange$,backup-svc,monitoring-agent,sharepoint-farm"
| makemv delim="," whitelist_accounts
| eval is_whitelisted = if(match(Account_Name, "^(" + mvjoin(whitelist_accounts, "|") + ")"), "YES", "NO")
Add this logic to reduce false positives for known high-activity accounts.
Response playbook
When a Kerberoasting alert fires:
Immediate Actions (0-15 minutes)
- Validate the alert — check if Client_Address is internal vs external
- Identify the target accounts — list all Account_Names in the detection
- Check account privileges — are targeted accounts Domain Admins, service accounts, or regular users?
- Isolate the source — block Client_Address at network perimeter if external
Containment Actions (15-60 minutes)
- Force password reset on all targeted service accounts with >50 TGS requests
- Invalidate Kerberos tickets using
klist purgeorInvoke-Command -ComputerName DC01 -ScriptBlock { klist purge } - Enable AES-only Kerberos to break RC4 cracking attempts
- Increase service account password complexity if reset is feasible
Investigation Actions (1-24 hours)
- Timeline analysis — when did the activity start? Is this the first attempt?
- Lateral movement hunt — has Client_Address been seen on other internal hosts?
- Credential validation — attempt to authenticate with known weak service account passwords
- Tool identification — check for GetUserSPNs.py, Invoke-Kerberoast, or Rubeus artifacts on Client_Address
Common false positives and tuning
Exchange Server False Positives
Exchange generates massive TGS volume. Filter with:
| where NOT match(Client_Address, "^(10\.1\.100\.|10\.1\.101\.)")
| where NOT match(Service_Name, ".*exchange.*")
Load Balancer False Positives
Load balancers requesting tickets for health checks:
| where NOT match(Client_Name, ".*\$")
| where NOT match(Account_Name, ".*health.*|.*monitor.*")
Backup Software False Positives
Backup agents often request many TGS tickets:
| where NOT match(Client_Name, ".*backup.*|.*veeam.*|.*netbackup.*")
Frequently asked questions
What is Kerberoasting and how does it work?
Why is Event 4769 important for detecting Kerberoasting?
What makes RC4 encryption a reliable Kerberoasting indicator?
How often should I run Kerberoasting detection queries?
What service accounts do attackers typically target in Kerberoasting?
How do I baseline normal TGS ticket volume in my environment?
What should I do when a Kerberoasting alert triggers?
How can I reduce false positives in Kerberoasting detection?
Is there a way to prevent Kerberoasting entirely?
What tools do attackers commonly use for Kerberoasting?
Advanced hunting queries
Cross-protocol attack correlation
Correlate Kerberoasting with NTLM authentication failures:
(index=wineventlog EventCode=4769 Ticket_Encryption_Type="0x17") OR
(index=wineventlog EventCode=4776 OR EventCode=4625)
| eval attack_type = case(
EventCode=4769, "kerberoasting",
EventCode=4776, "ntlm_auth",
EventCode=4625, "logon_failure",
1=1, "other")
| transaction Client_Address maxspan=2h
| where mvcount(attack_type) > 1
Service account password age correlation
Join with AD data to identify old service account passwords:
| inputlookup service_accounts.csv
| join Account_Name
[search index=wineventlog EventCode=4769 Ticket_Encryption_Type="0x17"
| stats count by Account_Name]
| eval days_since_pwd_change = (now() - strptime(PasswordLastChanged, "%Y-%m-%d")) / 86400
| where days_since_pwd_change > 365 AND count > 10
Geolocation anomaly detection
Detect Kerberoasting from unusual locations:
index=wineventlog EventCode=4769 Ticket_Encryption_Type="0x17"
| iplocation Client_Address
| stats count, dc(Account_Name) as accounts by Country, Region, City
| where accounts > 5
| eval risk_score = case(
NOT match(Country, "United States"), 10,
NOT match(Region, "California|New York|Texas"), 7,
1=1, 3)
| where risk_score > 5
Conclusion
Kerberoasting detection in Splunk isn’t about perfect prevention — it’s about turning your Domain Controllers’ Event 4769 flood into actionable security intelligence. The queries above catch the three attack phases: reconnaissance (bulk SPN enumeration), exploitation (RC4 ticket requests), and persistence (repeated targeting of specific accounts).
Deploy the core detections this week:
- RC4 volume spike monitoring — catches active attacks
- Bulk enumeration detection — catches reconnaissance
- High-value account targeting — catches focused exploitation
- Dashboard and alerting — provides SOC visibility
Tune for your environment, baseline normal TGS patterns, and build response playbooks. Kerberoasting is preventable through proper service account management, but detection bridges the gap between “should be secure” and “is actually secure.”
For the defensive complement, see our Windows LAPS implementation guide for service account password rotation and our NTLM disable guide for reducing overall AD attack surface.
