Detecting Kerberos Attacks (T1558): Kerberoasting, AS-REP Roasting & Forged Tickets in KQL and SPL
Identity is the new perimeter, and in every Active Directory environment that perimeter runs on Kerberos. That makes the Kerberos protocol the single richest target an intruder has once they land a foothold: a low-privilege user account is often all an attacker needs to harvest service-account hashes, crack them offline, and walk into a domain admin session — without ever dropping malware on a domain controller.
MITRE ATT&CK tracks this family as T1558 — Steal or Forge Kerberos Tickets, and it is consistently one of the highest-value detections a SOC can build. Yet most teams either don't collect the right events or drown in false positives from legacy RC4 traffic. This guide walks through the four T1558 sub-techniques that actually matter — Kerberoasting, AS-REP Roasting, Golden Tickets, and Silver Tickets — with production KQL for Microsoft Sentinel / Defender and SPL for Splunk pulled directly from the df00tech detection library.
Why Kerberos Attacks Are Hard to Catch
Kerberos was designed to minimize how often a password hash crosses the wire. The side effect is that the protocol's legitimate behavior — requesting tickets — is also the attack. There's no malicious binary to signature, no exploit to patch. The detectable difference is almost always encryption type, volume, or context, not the event itself.
That's why the foundation of every Kerberos detection is domain controller logging. You need Security Event IDs 4768 (TGT requested), 4769 (service ticket requested), and 4770 (ticket renewed) forwarded from every DC. If those aren't in your SIEM, none of the queries below will fire. Start there.
1. Kerberoasting — T1558.003
Kerberoasting is the workhorse of modern AD intrusions. Any authenticated user can request a Kerberos service ticket (TGS) for any account with a Service Principal Name (SPN). Portions of that ticket are encrypted with the service account's NTLM hash, and when the ticket uses RC4 (etype 0x17) it can be cracked offline with Hashcat or John the Ripper — no further interaction with the domain required. Rubeus, Invoke-Kerberoast (PowerSploit/Empire), and Impacket's GetUserSPNs.py automate this, and confirmed operators include Wizard Spider (Ryuk/Conti), FIN7, and Indrik Spider.
The signal is RC4 service-ticket requests against user-owned SPNs. This T1558.003 detection aggregates Event 4769 by requesting account and source IP, then scales alert priority with the number of unique SPNs — the tell that automated tooling is enumerating everything roastable at once:
// T1558.003 Kerberoasting — RC4-Encrypted TGS Ticket Requests
let LookbackWindow = 1h;
let BulkSPNThreshold = 5; // >= 5 unique SPNs in window indicates automated tooling
SecurityEvent
| where TimeGenerated > ago(LookbackWindow)
| where EventID == 4769 // Kerberos Service Ticket Operations
| where TargetUserName !endswith "$" // Exclude machine accounts (COMPUTER$)
| where ServiceName !~ "krbtgt" // Exclude TGT renewals
| where ServiceName !endswith "$" // Exclude computer account service tickets
| where TicketEncryptionType == "0x17" // RC4-HMAC — offline-crackable
| extend NormalizedSourceIP = replace_string(IpAddress, "::ffff:", "")
| summarize
TGSRequestCount = count(),
TargetSPNs = make_set(ServiceName, 200),
UniqueServiceCount = dcount(ServiceName),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by TargetUserName, NormalizedSourceIP, Computer
| extend IsBulkKerberoast = UniqueServiceCount >= BulkSPNThreshold
| extend AlertPriority = case(
UniqueServiceCount >= 20, "Critical",
UniqueServiceCount >= BulkSPNThreshold, "High",
"Medium")
| project FirstSeen, LastSeen, Computer, TargetUserName, NormalizedSourceIP,
TGSRequestCount, UniqueServiceCount, IsBulkKerberoast, AlertPriority, TargetSPNs
| sort by UniqueServiceCount desc, TGSRequestCount desc
The same logic in Splunk:
index=wineventlog sourcetype="WinEventLog:Security" EventCode=4769
NOT TargetUserName="*$"
NOT ServiceName="*$"
NOT ServiceName="krbtgt"
TicketEncryptionType="0x17"
| eval NormalizedSourceIP=replace(IpAddress, "::ffff:", "")
| stats count as TGSRequestCount, dc(ServiceName) as UniqueServiceCount,
values(ServiceName) as TargetSPNs
by TargetUserName, NormalizedSourceIP, host
| eval AlertPriority=case(UniqueServiceCount >= 20, "Critical",
UniqueServiceCount >= 5, "High", 1=1, "Medium")
| sort - UniqueServiceCount - TGSRequestCount
The tuning move that changes everything: modern AD should use AES (etype 0x12/0x11) by default. Enforce AES-only Kerberos with the GPO Network security: Configure encryption types allowed for Kerberos and set service accounts with Set-ADUser -KerberosEncryptionType AES256,AES128. After that, every RC4 (0x17) service-ticket request to a user SPN becomes a near-binary true positive. This single change moves Kerberoasting from high-noise to high-fidelity. Where you can, migrate service accounts to Group Managed Service Accounts (gMSA), whose auto-rotated 120-character passwords make cracking computationally infeasible.
2. AS-REP Roasting — T1558.004
AS-REP Roasting is Kerberoasting's quieter cousin. When an account has Kerberos preauthentication disabled (the DONT_REQ_PREAUTH flag), an attacker can send an AS-REQ with no encrypted timestamp and receive an AS-REP containing material encrypted with the account's password hash — crackable offline, and obtainable with nothing more than the account's name.
The anchor here is Event 4768 with a preauthentication type of 0 against a user account. This KQL parses the event XML and isolates exactly that condition:
SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == 4768
| extend EventXml = parse_xml(EventData)
| extend TargetUserName = tostring(EventXml.EventData.Data[0]["#text"])
| extend PreAuthType = tostring(EventXml.EventData.Data[7]["#text"])
| extend IpAddress = tostring(EventXml.EventData.Data[9]["#text"])
// PreAuthType 0x0 = no preauthentication required (DONT_REQ_PREAUTH set)
| where PreAuthType == "0" or PreAuthType == "0x0"
| where TargetUserName !endswith "$"
| project TimeGenerated, Computer, TargetUserName, PreAuthType, IpAddress
Pair the detection with a proactive hunt: any user account carrying DONT_REQ_PREAUTH is standing exposure. Enumerate them (Get-ADUser -Filter {userAccountControl -band 4194304}) and fix the flag before an attacker finds it for you.
3. Golden & Silver Tickets — T1558.001 / T1558.002
The two roasting techniques steal credentials; the ticket-forging techniques fabricate them. A Golden Ticket is a forged TGT minted from the KRBTGT account hash — it grants the attacker the ability to authenticate as any account, with any group membership, for any lifetime. A Silver Ticket is a forged service ticket built from a single service account's hash; it's narrower in scope but stealthier, because it never touches the KDC and so generates no 4768/4769 on a DC.
Because forged tickets produce little or no DC-side authentication telemetry, detection pivots to the endpoint — catching the forging tools and the encryption anomalies they leave behind. This excerpt from the Golden Ticket detection watches for Mimikatz/Rubeus command-line signatures:
let MimikatzGoldenPatterns = dynamic([
"kerberos::golden", "kerberos::silver", "kerberos::ptt",
"sekurlsa::krbtgt", "/ptt", "/rc4:", "/aes256:", "/ticket:", "/sid:"
]);
DeviceProcessEvents
| where Timestamp > ago(24h)
| where FileName =~ "mimikatz.exe" or FileName =~ "rubeus.exe"
or ProcessCommandLine has_any (MimikatzGoldenPatterns)
| project Timestamp, DeviceName, AccountName, FileName,
ProcessCommandLine, InitiatingProcessFileName, SHA256
| sort by Timestamp desc
The strongest defensive signal against Golden Tickets, though, is upstream: the KRBTGT hash has to be stolen first, almost always via OS credential dumping on a domain controller. That makes your credential dumping detections (T1003) the early-warning system for forged-ticket attacks — and rotating the KRBTGT password twice (so both current and previous keys change) is how you invalidate any Golden Ticket already in circulation.
Hunt Before You're Breached: Map Your Attack Surface
Detection fires when the attack is already underway. The higher-leverage work is knowing your exposure first. This hunting query surfaces every user account with an SPN — the complete Kerberoasting attack surface — ranked by privilege:
IdentityInfo
| where TimeGenerated > ago(7d)
| where isnotempty(ServicePrincipalNames)
| where AccountType == "User"
| extend SPNCount = array_length(ServicePrincipalNames)
| extend IsHighRisk = AssignedRoles has_any ("Global Admin", "Domain Admin", "Enterprise Admin")
| project AccountName = UserPrincipalName, ServicePrincipalNames, SPNCount, AssignedRoles, IsHighRisk
| sort by IsHighRisk desc, SPNCount desc
A domain admin with an SPN and a weak password is a single offline crack away from total compromise. Those accounts should be hardened to 25+ character passwords, AES-only encryption, or gMSA — today.
Connecting the Kill Chain
Kerberos attacks rarely happen in isolation. A cracked service-account hash leads straight to domain account abuse (T1078.002) and the lateral movement that follows. The offline cracking step itself maps to Brute Force: Password Cracking (T1110.002). The highest-priority hunt your team can run is the correlation between a roasted SPN and a subsequent successful logon to a host matching that service — the moment a stolen credential becomes active lateral movement.
For SOC leads, the takeaway is sequencing: enforce AES-only Kerberos to make Kerberoasting binary, deploy the 4769/4768 detections above, wire your credential-dumping alerts as the Golden Ticket early warning, and run the SPN attack-surface hunt on a schedule. Each of the techniques above has a full detection page in the df00tech library with SPL, Elastic EQL, Sentinel, QRadar, and Chronicle variants, response playbooks, and atomic tests you can run to validate your coverage before an adversary does it for you.