{"id":301,"date":"2026-03-03T09:00:00","date_gmt":"2026-03-03T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2025\/04\/05\/kerberoasting-and-its-modern-variants-why-classic-detection-still-misses-half-the-attacks-2\/"},"modified":"2026-05-15T10:34:55","modified_gmt":"2026-05-15T10:34:55","slug":"kerberoasting-and-its-modern-variants-why-classic-detection-still-misses-half-the-attacks-2","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2026\/03\/03\/kerberoasting-and-its-modern-variants-why-classic-detection-still-misses-half-the-attacks-2\/","title":{"rendered":"Kerberoasting and Its Modern Variants: Why Classic Detection Still Misses Half the Attacks"},"content":{"rendered":"<p>Kerberoasting has been documented since 2014. It is still used in the majority of Active Directory compromises in 2025 because the fundamental issue, service accounts with weak passwords that hold Service Principal Names, has not been fixed in most environments. What has changed is how attackers do it, and most detection guidance has not kept pace.<\/p>\n<p>Classic Kerberoasting detection watches for RC4-encrypted TGS requests (etype 23) in Windows Event ID 4769. Attackers switched to requesting AES-encrypted tickets (etype 18) years ago because AES tickets are harder to crack and because requesting RC4 when the account supports AES is itself anomalous and detectable. This post covers the full picture.<\/p>\n<h3>Classic Kerberoasting: how it works<\/h3>\n<p>Any authenticated domain user can request a Ticket Granting Service (TGS) ticket for any SPN in the domain. The TGS ticket is encrypted with the NTLM hash of the service account&#8217;s password. If that password is weak, the ticket can be cracked offline without any further interaction with the domain controller.<\/p>\n<pre>\/\/ Attacker perspective (for detection understanding)\n\/\/ Request TGS for all SPNs in the domain\n\/\/ Using Rubeus:\n\/\/ Rubeus.exe kerberoast \/outfile:hashes.txt\n\n\/\/ Using Invoke-Kerberoast:\n\/\/ Invoke-Kerberoast -OutputFormat Hashcat | Select-Object Hash | Out-File hashes.txt\n\n\/\/ The resulting hash format for offline cracking:\n\/\/ $krb5tgs$23$*username$DOMAIN.COM$service\/host*$[ticket_data]\n\/\/ Type 23 = RC4 encryption (older, weaker, more detectable)\n\/\/ Type 18 = AES256 encryption (newer, less detectable)<\/pre>\n<h3>Modern variant 1: AES Kerberoasting (etype 18)<\/h3>\n<p>Modern tools default to requesting AES-encrypted tickets because AES requests are considered normal. The detection based purely on etype 23 misses this entirely.<\/p>\n<pre>\/\/ Windows Event ID 4769 fields for Kerberoasting detection\n\/\/ Classic (detectable):\n\/\/ Service Name: service_account$\n\/\/ Ticket Encryption Type: 0x17 (= RC4\/DES = etype 23)\n\/\/ Client Address: [source IP]\n\n\/\/ Modern AES variant (harder to detect on etype alone):\n\/\/ Ticket Encryption Type: 0x12 (= AES256 = etype 18)\n\/\/ This looks like normal Kerberos traffic\n\n\/\/ The distinguishing factor for AES roasting is VOLUME and PATTERN\n\/\/ A single user requesting TGS tickets for many different SPNs\n\/\/ in a short time window is anomalous regardless of encryption type<\/pre>\n<pre>\/\/ Splunk: detect bulk TGS requests regardless of encryption type\nindex=wineventlog EventCode=4769 earliest=-1h\n ServiceName!=\"krbtgt\"\n ServiceName!=\"*$\" \/\/ exclude machine accounts for now\n| bin _time span=5m\n| stats\n dc(ServiceName) AS unique_services,\n count AS total_requests,\n values(ServiceName) AS services_requested\n by _time, Account_Name, IpAddress\n| where unique_services &gt;= 5 \/\/ 5+ different SPNs in 5 minutes is anomalous\n| sort -unique_services<\/pre>\n<h3>Modern variant 2: Targeted Kerberoasting (single high-value account)<\/h3>\n<p>Bulk roasting (requesting every SPN) is loud. Sophisticated operators identify a single high-value target, ideally a service account with domain admin membership and a weak password, and request only that one ticket. Volume-based detection misses this entirely.<\/p>\n<pre>\/\/ Targeted roasting looks like:\n\/\/ One account requesting one TGS ticket\n\/\/ Indistinguishable from normal Kerberos traffic by volume\n\n\/\/ Detection requires baselining:\n\/\/ Which accounts normally request TGS for which services?\n\/\/ A user who never normally requests TGS for SQL service doing so once is anomalous\n\n\/\/ Splunk baseline and deviation detection\n\/\/ Build baseline over 30 days first:\nindex=wineventlog EventCode=4769 earliest=-30d latest=-1d\n ServiceName!=\"krbtgt\"\n| stats count by Account_Name, ServiceName\n| outputlookup kerberos_tgs_baseline.csv\n\n\/\/ Then hunt for new combinations:\nindex=wineventlog EventCode=4769 earliest=-24h\n ServiceName!=\"krbtgt\"\n| join type=left Account_Name, ServiceName\n [| inputlookup kerberos_tgs_baseline.csv\n | rename count AS baseline_count]\n| where isnull(baseline_count) \/\/ never seen this account request this SPN before\n| table _time, Account_Name, ServiceName, IpAddress<\/pre>\n<h3>Modern variant 3: AS-REP Roasting<\/h3>\n<p>Accounts with &#8220;Do not require Kerberos preauthentication&#8221; set do not need to authenticate before requesting a TGS. The AS-REP response is encrypted with the account&#8217;s NTLM hash and can be cracked offline. No valid credentials are needed to roast these accounts, just network access to the domain controller.<\/p>\n<pre>\/\/ Find accounts vulnerable to AS-REP roasting (defender perspective)\n\/\/ Run this in your environment to identify the exposure\nImport-Module ActiveDirectory\n\nGet-ADUser -Filter {DoesNotRequirePreAuth -eq $true} -Properties DoesNotRequirePreAuth |\n Select-Object SamAccountName, DistinguishedName, Enabled |\n Where-Object {$_.Enabled -eq $true}\n\n\/\/ Any enabled account in this list is vulnerable\n\/\/ The fix: enable Kerberos preauthentication for all accounts\n\/\/ There is almost never a legitimate reason to disable it<\/pre>\n<pre>\/\/ Detect AS-REP roasting via Windows Event ID 4768\n\/\/ AS-REP roasting generates an AS-REQ without preauthentication\n\/\/ This appears as Event 4768 with Pre-Authentication Type = 0 (no preauth)\n\nindex=wineventlog EventCode=4768 earliest=-24h\n Pre_Authentication_Type=0\n Account_Name!=\"*$\" \/\/ exclude machine accounts\n| table _time, Account_Name, IpAddress, Result_Code\n| sort -_time\n\n\/\/ A Result_Code of 0x0 = success means the account exists and has no preauth\n\/\/ A burst of these from one source IP = AS-REP roasting sweep<\/pre>\n<h3>Modern variant 4: Machine account Kerberoasting<\/h3>\n<p>Machine accounts have auto-generated 120-character random passwords that are computationally infeasible to crack. However, some AD environments have machine accounts with manually set, weak passwords, or have created service accounts as machine accounts. These are roastable and the detection gap is that most Kerberoasting detection excludes machine accounts.<\/p>\n<pre>\/\/ Find machine accounts with SPNs that might be roastable\n\/\/ Machine accounts with human-chosen passwords are rare but exist\nImport-Module ActiveDirectory\n\nGet-ADComputer -Filter * -Properties ServicePrincipalName, PasswordLastSet,\n PasswordNeverExpires |\n Where-Object {\n $_.ServicePrincipalName -ne $null -and\n $_.PasswordNeverExpires -eq $true\n } |\n Select-Object Name, ServicePrincipalName, PasswordLastSet, PasswordNeverExpires\n\n\/\/ PasswordNeverExpires = true on a machine account with SPNs is worth reviewing\n\/\/ Machine account passwords should rotate every 30 days automatically<\/pre>\n<h3>Detection layer: DC telemetry via network traffic<\/h3>\n<pre>\/\/ Zeek can detect Kerberos roasting via the kerberos.log\n\/\/ Kerberoasting appears as unusual TGS-REQ patterns\n\ncat kerberos.log | zeek-cut ts client service request_type cipher |\n awk '$3 != \"krbtgt\" &amp;&amp; $4 == \"TGS\" {print}' |\n sort | uniq -c | sort -rn | head -20\n\n\/\/ Look for:\n\/\/ Multiple different service names from the same client in a short window\n\/\/ cipher = rc4-hmac when the service account supports AES (downgrade indicator)\n\/\/ Requests at unusual hours (outside normal business hours for that account)<\/pre>\n<h3>Full detection Sigma rule set<\/h3>\n<pre>\/\/ Rule 1: Bulk Kerberoasting (volume-based)\ntitle: Bulk Kerberoasting Activity Detected\nlogsource:\n product: windows\n service: security\ndetection:\n selection:\n EventID: 4769\n TicketEncryptionType:\n - '0x17' \/\/ RC4 - classic Kerberoasting\n - '0x12' \/\/ AES256 - modern variant\n ServiceName|endswith: '$'\n timeframe: 5m\n condition: selection | count(ServiceName) by SubjectUserName &gt; 5\nlevel: high\n\n\/\/ Rule 2: RC4 downgrade (account supports AES but RC4 requested)\ntitle: Kerberos RC4 Encryption Downgrade\nlogsource:\n product: windows\n service: security\ndetection:\n selection:\n EventID: 4769\n TicketEncryptionType: '0x17' \/\/ RC4\n filter_legit:\n \/\/ Accounts that legitimately only support RC4 (old, unpatched)\n \/\/ Add known legacy service accounts here\n ServiceName: 'legacyservice$'\n condition: selection and not filter_legit\nlevel: medium\n\n\/\/ Rule 3: AS-REP roasting\ntitle: AS-REP Roasting Attempt\nlogsource:\n product: windows\n service: security\ndetection:\n selection:\n EventID: 4768\n PreAuthType: '0'\n TargetUserName|endswith: '~'\n filter_machine:\n TargetUserName|endswith: '$'\n condition: selection and not filter_machine\nlevel: high<\/pre>\n<h3>Hunting guide: what to do when you find a roasting event<\/h3>\n<pre>\/\/ Step 1: Identify which account was targeted\n\/\/ Step 2: Check when that account's password was last changed\nGet-ADUser -Identity \"targeted_account\" -Properties PasswordLastSet |\n Select-Object SamAccountName, PasswordLastSet\n\n\/\/ Step 3: Check if the account has admin rights\nGet-ADGroupMember \"Domain Admins\" | Where-Object {$_.Name -eq \"targeted_account\"}\nGet-ADGroupMember \"Administrators\" | Where-Object {$_.Name -eq \"targeted_account\"}\n\n\/\/ Step 4: Check all logons from that account since the roasting event\n\/\/ If password was cracked and account was used, logon events will show new sources\n\nindex=wineventlog EventCode=4624 Account_Name=\"targeted_account\" earliest=[roasting_timestamp]\n| table _time, IpAddress, LogonType, WorkstationName\n\n\/\/ Step 5: If any new source IPs appear after the roasting event, incident response\n\/\/ Step 6: Regardless, change the password immediately to 25+ char random string<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Classic Kerberoasting detection looks for RC4 TGS requests. Attackers switched to AES years ago. Targeted Kerberoasting, roasting with machine accounts, and AS-REP roasting all have different signatures. This covers all variants and how to hunt each one.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-301","post","type-post","status-publish","format-standard","hentry","category-threat-hunting"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/301","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=301"}],"version-history":[{"count":1,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/301\/revisions"}],"predecessor-version":[{"id":319,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/301\/revisions\/319"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=301"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=301"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=301"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}