it’s 2026 and kerberoasting still works. not because defenders are lazy, but because the protocol itself is the problem. kerberos hands out crackable tickets to anyone with a valid TGT. that’s not a bug. that’s the spec.
this post isn’t another “what is kerberoasting” tutorial. you know what it is. we’re looking at two things:
- red side: how to make kerberoasting harder to detect
- blue side: detection strategies that go beyond event ID filtering
table of contents
open table of contents
the 30-second recap
any domain user can request a TGS ticket for any service account. the KDC checks that you have a valid TGT, but it doesn’t check whether you’re actually authorized to use the service. the ticket is encrypted with the service account’s password hash. take it offline, crack it, get the password. no failed logins, no lockouts.
┌──────────┐ ┌──────────┐
│ attacker │ │ KDC │
│ (any │ │ (DC01) │
│ domain │ │ │
│ user) │ │ │
└─────┬────┘ └─────┬────┘
│ │
│ "ticket for jerry │
│ please" │
│ ──────────────────> │
│ │
│ "sure, here you │
│ go. i don't check │
│ permissions btw" │
│ <────────────────── │
│ │
│ * cracks hash * │
│ password: superman │
│ │
if you need the full breakdown, every blog from 2017 has it. let’s move on to what actually matters.
red side: reducing the noise
the default way (loud)
this is what 90% of pentest reports look like:
GetUserSPNs.py 'c137.local/morty:password' -dc-ip 10.0.0.1 -request
congratulations, you just:
- sent an LDAP query
(servicePrincipalName=*)across the entire domain - requested RC4 encrypted tickets from a random box
- set ticket options that windows clients typically don’t use
- targeted a user account SPN, which stands out against normal machine account traffic
- did it all in 2 seconds
here’s what the SOC sees:
| account | client_ip | service | enc_type | verdict |
|---|---|---|---|---|
rick@C137.LOCAL | ::1 | WS$ | 0x12 | normal |
rick@C137.LOCAL | 10.0.0.31 | WS$ | 0x12 | normal |
morty@C137.LOCAL | 10.0.0.3 | jerry | 0x17 | busted |
three red flags:
0x17(RC4): modern windows uses AES256 (0x12)10.0.0.3: random box, not a domain workstationjerry: user account SPN, not a machine account ($)
any SOC with a basic rule catches this:
sourcetype=wineventlog "EventCode=4769" "0x17"
| rex "Service Name:\s+(?<service>[^\r\n]+)"
| where NOT match(service, "\$$")
the quieter way
going fully silent with kerberoasting is hard. the protocol forces you to talk to the KDC, and that always leaves a trace. but you can reduce most of those indicators.
1. force AES256: kill the 0x17 signal
the biggest tell. impacket requests RC4 by default because it’s faster to crack. tools like orpheus patch impacket’s kerberosv5.py to request AES256:
# kerberosv5.py modification
# before: requests RC4 (0x17), instant detection
# after: requests AES256 (0x12), identical to legit traffic
yes, AES256 hashes are significantly slower to crack. doesn’t matter. a weak password like Summer2026! cracks in seconds regardless of the algorithm. weak passwords are weak in any encryption.
2. fix ticket options: match normal windows behavior
every TGS-REQ includes a KDCOptions field. it’s a bitmask that tells the KDC how to issue the ticket: should it be forwardable, renewable, canonicalized, etc. the full spec is in RFC 4120 section 5.4.1.
the problem: impacket sets this field to 0x40810010, which includes the renewable-ok bit. normal windows clients typically don’t set that bit. they send 0x40810000. it’s a small difference, but SOCs that compare TGS-REQ patterns against known-good windows traffic will catch it.
orpheus patches impacket to send 0x40810000, matching what a real windows client would send.
| impacket default | windows default | patched | |
|---|---|---|---|
| ticket options | 0x40810010 | 0x40810000 | 0x40810000 |
| enc type | 0x17 (RC4) | 0x12 (AES256) | 0x12 (AES256) |
3. think about who’s making the request
look at normal TGS traffic in any domain. it’s almost entirely machine accounts ($) requesting tickets for other machine accounts. a regular user account like morty requesting a TGS ticket for another user account like jerry is a double anomaly. neither the requester nor the target fits the normal pattern.
if you have access to a compromised machine account, use its TGT to make the request instead of a user account. the account field in the 4769 event will show a machine account, which is exactly what normal traffic looks like.
| account (requester) | service (target) | blends in? | |
|---|---|---|---|
| normal traffic | WS01$ | DC01$ | yes |
| default kerberoasting | morty | jerry | no, both are user accounts |
| quieter | WS01$ | jerry | partially, requester looks normal |
4. no bulk LDAP: stop screaming “i’m kerberoasting”
GetUserSPNs.py fires (servicePrincipalName=*) across the entire domain. this LDAP pattern is well-known and monitored by EDRs and SIEM rules.
quieter approach:
- ideally, skip LDAP enumeration entirely. if you already know your target from earlier recon, there’s no reason to query the domain again
- if you have to enumerate, scope it down. query a specific OU instead of the whole domain, and spread it across hours or even days
# loud: wildcard SPN filter across entire domain
(servicePrincipalName=*)
# quieter: scoped to a specific OU
-b "OU=ServiceAccounts,DC=corp,DC=local" "(servicePrincipalName=*)"
# ideal: no SPN filter at all, query a known user directly
(sAMAccountName=targetuser)
5. request from a machine that makes sense
think about where the TGS request is coming from. if you’re targeting an SPN tied to a SQL service, requesting that ticket from a workstation that already has regular SQL traffic to that server looks completely normal. requesting it from a random box that has never talked to that service is an anomaly.
this isn’t always possible, but when it is, it’s one more indicator you can eliminate.
6. watch out for honeypots
blue teams deploy fake SPN accounts as tripwires. any ticket request for them triggers an instant alert. you can try to spot the obvious ones by checking attributes like lastLogon, pwdLastSet, or whether the SPN points to a service that actually exists.
but here’s the thing: a well-crafted honeypot will have realistic values for all of those. and checking each account before requesting a ticket means more LDAP queries, which is more noise.
this is the one area where the attacker is at a real disadvantage. you can’t be 100% sure an account isn’t a trap. the risk is always there.
result after all steps
after all six steps, the 4769 event in the security log looks like this:
| field | value | suspicious? |
|---|---|---|
| account | WS01$ | no, machine account. blends into normal traffic |
| encryption type | 0x12 (AES256) | no, same as everyone else |
| ticket options | 0x40810000 | no, standard windows |
| client address | 10.0.0.31 | no, domain workstation |
| service name | jerry | user account SPN, not a machine account. this still stands out |
most signature-based detection rules won’t catch this. the requester is a machine account, the encryption and ticket options are standard. the only remaining indicator is the service name targeting a user account instead of a machine account. and there’s no way around it. machine account passwords are auto-generated, 120+ characters, and rotate every 30 days. they’re not crackable. the whole point of kerberoasting is targeting user accounts with weak, human-set passwords. this is the one indicator you can’t eliminate because it’s the attack itself.
blue side: what actually works
so your RC4 detection rule won’t catch an attacker who knows what they’re doing. now what?
tier 1: make cracking impossible
forget detection. make the attack pointless.
| method | why it works |
|---|---|
| 25+ char random passwords on service accounts | even RC4 won’t crack j8#kL9$mN2@pQ5&rT7*vX0 |
| group managed service accounts (gMSA) | windows auto-rotates 240-byte random passwords. attackers can’t crack what changes every 30 days |
| disable/limit RC4 domain-wide | forces AES where supported. doesn’t prevent kerberoasting but reduces RC4 exposure and raises cracking cost. note: Microsoft is actively moving Kerberos defaults away from RC4, and plans to disable RC4 as the default assumed supported encryption type on AD domain controllers by the end of Q2 2026 |
this is the real fix. everything below is just buying time.
tier 2: honeypot SPNs
the one detection that works regardless of attacker OPSEC.
create a fake service account with an SPN. give it a strong password. put it somewhere visible. monitor it.
any TGS request for this account is malicious. no legitimate user would ever access this service because the service doesn’t exist. just make sure the SPN has zero legitimate use before deploying. vulnerability scanners, legacy discovery jobs, or misconfigured test systems can generate false positives.
sourcetype=wineventlog "EventCode=4769" "Service Name" "honeypot-svc"
no encryption type check needed. no ticket options check needed. no source IP check needed. the question is simple: did someone request a ticket for a service that has zero legitimate users? if yes, you’re being kerberoasted.
the attacker has to choose: skip the honeypot (and maybe miss a real target) or hit it and get caught.
tier 3: behavioral analytics
this is where signature-based detection ends and anomaly detection begins. instead of looking for specific values like 0x17, you baseline what normal TGS traffic looks like and flag anything that deviates.
things to correlate:
- a user account requesting a TGS for another user account SPN. as we covered in the red side, normal traffic is almost entirely machine-to-machine. this pattern alone is worth alerting on
- morty has never accessed jerry’s service before. first-time access to an SPN is an anomaly
- 15 TGS requests from one account in 10 minutes. even with AES256 and correct ticket options, volume stands out
- TGS request at 3am from a user who works 9-5. timing matters
popular EDRs like CrowdStrike, Microsoft Defender for Identity, and SentinelOne already do this out of the box. they track TGS request patterns per user and flag anomalies regardless of encryption type or ticket options. if your org runs one of these, you likely have some level of kerberoasting detection even without custom SIEM rules.
for orgs building their own detection, this requires baselining and correlation logic, which takes effort. but it’s the only approach that doesn’t rely on the attacker making a mistake.
tier 4: the basics (still worth doing)
the bare minimum. catches RC4 requests for user account SPNs:
sourcetype=wineventlog "EventCode=4769" "0x17"
| rex "Service Name:\s+(?<service>[^\r\n]+)"
| where NOT match(service, "\$$")
a skilled attacker bypasses this by requesting AES256. but most attackers aren’t skilled. this still catches impacket/rubeus defaults, cobalt strike, and every script kiddie running GetUserSPNs.py from a youtube tutorial.
a better rule drops the encryption type filter entirely and focuses on what can’t be hidden: TGS requests for user account SPNs.
sourcetype=wineventlog "EventCode=4769"
| rex "Service Name:\s+(?<service>[^\r\n]+)"
| rex "Account Name:\s+(?<account>[^\r\n]+)"
| where NOT match(service, "\$$")
AND NOT match(service, "krbtgt")
AND account!=service
// your exclusions here: known service accounts, whitelisted SPNs, etc.
| stats count dc(service) as unique_services values(service) as services by account
| where count > 3 OR unique_services > 1
this catches AES256 kerberoasting too. tune the threshold and exclusions for your environment. note: 4769 field names may vary depending on your SIEM and ingest configuration. verify that your rex patterns match your actual log format.
the scoreboard
| default kerberoasting | opsec-aware | |
|---|---|---|
| RC4 detection (0x17) | caught | avoided |
| ticket options detection | caught | avoided |
| bulk LDAP detection | caught | avoided |
| source IP anomaly | caught | reduced |
| user account SPN detection | caught | still visible |
| honeypot SPN | caught | caught |
| EDR behavioral detection | caught | likely caught |
| strong passwords / gMSA | attack fails | attack fails |
the bottom four rows are where real defense happens. signature-based rules help with low-effort attacks, but they won’t save you against someone who’s done their homework.
closing thoughts
kerberoasting isn’t going anywhere. the protocol allows it by design, and no amount of patching changes that. what changes is how both sides approach it.
if you’re red team: default impacket gets caught by everything. understand what you’re generating in the logs and reduce what you can. you won’t be invisible, but you can make it a lot harder to spot.
if you’re blue team: don’t stop at 0x17. deploy honeypot SPNs, push for gMSAs, and make sure your EDR is actually configured to flag TGS anomalies. your SIEM rule is a seatbelt, not an airbag.
if you’re purple team: run both sides, compare notes, and find the gaps. that’s how detection actually improves.