Detecting Kerberoasting with Windows Event ID 4769

Kerberoasting (MITRE ATT&CK T1558.003) is one of the few credential-access techniques that produces a clean, on-prem audit signal — provided the right event is enabled and the right field is read. Detecting Kerberoasting with Event ID 4769 comes down to two things: alerting on RC4-HMAC service ticket requests in an environment that should be running AES, and watching for bursts of ticket requests against many SPNs from a single account. This post is the detection and hardening pair we use on the domain controllers we monitor.

Key Takeaways

  • Event ID 4769 on domain controllers records every Kerberos service ticket request. The Ticket Encryption Type field is the primary detection signal — 0x17 means RC4-HMAC, which is what offline cracking tools require.
  • Any modern Active Directory environment should rarely see RC4 service tickets. Treat 0x17 against domain user SPNs as anomalous until proven legitimate.
  • A burst of 4769s — one user requesting tickets for many distinct SPNs in a short window — is the classic Kerberoasting pattern, with or without RC4.
  • Hardening beats detection: set msDS-SupportedEncryptionTypes to AES-only on service accounts, migrate to Group Managed Service Accounts (gMSAs), and deploy a honey SPN for high-fidelity alerting.
  • 4769 is logged on the issuing domain controller, not the client. Centralise the Security log from every DC; one DC's events are not enough.

Environment

  • Active Directory domain at Windows Server 2016 functional level or higher.
  • Windows Server 2019/2022 domain controllers with Advanced Audit Policy applied via Group Policy.
  • 4769 events forwarded from every DC via Windows Event Forwarding or a SIEM agent.
  • PowerShell 5.1 or 7.4 for ad-hoc analysis on the collector.
  • RSAT Active Directory tooling for service-account hardening tasks.

The Problem

Kerberoasting works because Kerberos is doing exactly what it was designed to do. Any authenticated domain user can request a service ticket (TGS) for any account that has a Service Principal Name (SPN). The TGS is encrypted with a key derived from the target account's password. Older or misconfigured accounts produce RC4-HMAC tickets, which can be cracked offline at billions of guesses per second on a modern GPU. AES-encrypted tickets are computationally infeasible to crack at the same speed, which is why attackers explicitly request RC4 even on AES-capable accounts when they can.

The detection challenge is volume. 4769 fires for every service ticket request in the domain — Outlook to Exchange, SCCM to its database, end-user RDP, every internal web app. A single DC issues thousands of 4769s per minute. The trick is filtering down to the small number of requests that have the shape of an attack.

The Solution

Step 1 — Enable Kerberos service ticket auditing

Under Advanced Audit Policy → Account Logon, enable both Success and Failure for Audit Kerberos Service Ticket Operations. Apply via the Default Domain Controllers Policy so every DC inherits the same configuration:

# Verify on a DC
auditpol /get /subcategory:"Kerberos Service Ticket Operations"

Without this subcategory enabled, 4769 will never fire and the rest of this post is moot. Confirm at least one DC is logging the events before scaling out the detection.

Step 2 — Anatomy of a 4769 event

The fields that matter for detection:

  • Account Name — the user requesting the ticket. Will appear as USERNAME@DOMAIN.LOCAL.
  • Service Name — the SPN being requested. For Kerberoasting, this will be a domain user account name (not a computer or krbtgt).
  • Ticket Options — a flags field. 0x40810000 is normal; 0x40810010 often indicates ticket re-use.
  • Ticket Encryption Type — the heart of the detection. Common values: 0x12 (AES256-CTS-HMAC-SHA1-96), 0x11 (AES128), 0x17 (RC4-HMAC), 0x18 (RC4-HMAC-EXP).
  • Client Address — the source IP of the requester. Useful for narrowing the actor.
  • Failure Code0x0 for successful issuance; non-zero for errors.

Step 3 — Alert on RC4 service tickets

Modern domain members negotiate AES by default when the target account supports it. RC4 service tickets in an AES-capable environment fall into a few legitimate buckets — pre-Windows-Server-2008 trusts, accounts with msDS-SupportedEncryptionTypes unset or explicitly RC4 — and one illegitimate bucket: Kerberoasting tools forcing the encryption type down to make the ticket crackable.

# Surface RC4 service tickets issued in the last 24h, excluding machine accounts
Get-WinEvent -FilterHashtable @{
    LogName   = 'ForwardedEvents'
    Id        = 4769
    StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
    Where-Object {
        ($_.Properties[5].Value -eq '0x17' -or $_.Properties[5].Value -eq '0x18') -and
        ($_.Properties[2].Value -notmatch '\$$')
    } |
    Select-Object TimeCreated, MachineName,
                  @{Name='User';     Expression={ $_.Properties[0].Value }},
                  @{Name='Service';  Expression={ $_.Properties[2].Value }},
                  @{Name='ClientIP'; Expression={ $_.Properties[6].Value }},
                  @{Name='EncType';  Expression={ $_.Properties[5].Value }}

The -notmatch '\$$' filter drops machine accounts (Kerberoasting targets user accounts with SPNs, not computer accounts). Whatever survives this query should be a short list — investigate every entry.

Step 4 — Alert on ticket-request bursts

Attackers that cannot force RC4 will still leave a behavioural fingerprint: one principal requesting tickets for an unusually large number of distinct SPNs in a short window. The query is shape-based and works regardless of encryption type:

# Same user requesting tickets for many SPNs in an hour
Get-WinEvent -FilterHashtable @{
    LogName   = 'ForwardedEvents'
    Id        = 4769
    StartTime = (Get-Date).AddHours(-1)
} -ErrorAction SilentlyContinue |
    Where-Object { $_.Properties[2].Value -notmatch '\$$' -and
                   $_.Properties[2].Value -notmatch 'krbtgt' } |
    Group-Object { $_.Properties[0].Value } |
    ForEach-Object {
        [pscustomobject]@{
            User           = $_.Name
            DistinctSPNs   = ($_.Group | Select-Object -ExpandProperty Properties |
                              ForEach-Object { $_[2].Value } | Sort-Object -Unique).Count
            Total          = $_.Count
        }
    } |
    Where-Object DistinctSPNs -gt 20 |
    Sort-Object DistinctSPNs -Descending

Tune the threshold to the environment — 20 distinct SPNs per hour from one user is loud in most domains and quiet in a few. Legitimate hits are typically service accounts running discovery tooling (vulnerability scanners, asset inventories, monitoring agents). Allowlist by account name once those are identified.

Step 5 — Deploy a honey SPN

The highest-fidelity Kerberoasting detection is a decoy. Create a domain user account that no legitimate service ever talks to, register a plausible SPN against it, and alert on every 4769 issued for that SPN. Two false-positive sources to plan around: AD discovery scans by red-team tooling and the occasional curious admin running Get-ADUser -Filter * -Properties servicePrincipalName.

# Create the decoy
$pw = -join ((33..126) | Get-Random -Count 64 | ForEach-Object { [char]$_ })
New-ADUser -Name 'svc-backup-sql' `
           -SamAccountName 'svc-backup-sql' `
           -AccountPassword (ConvertTo-SecureString $pw -AsPlainText -Force) `
           -Enabled $true `
           -Description 'Service account — do not modify'

setspn -S MSSQLSvc/backup-sql.example.local:1433 svc-backup-sql

# Disable interactive logon and pre-set a long, random password
Set-ADUser -Identity svc-backup-sql -CannotChangePassword $true -PasswordNeverExpires $true

Then create a SIEM rule that alerts on Service Name = MSSQLSvc/backup-sql.example.local:1433 in any 4769 event. The account is never used by anything legitimate, so every match is a true positive.

Step 6 — Harden service accounts

Detection is reactive; the hardening below removes the technique outright for any account it covers:

  • Force AES on service accounts. Set msDS-SupportedEncryptionTypes to 0x18 (AES128 + AES256). Tickets issued to those accounts will no longer be RC4 regardless of what the client requests.
  • Use Group Managed Service Accounts. A gMSA has a 240-character password that AD rotates automatically every 30 days. The password is never typed, never stored, and cannot be cracked at any practical speed. Migrate any service that supports gMSAs.
  • Long passwords on remaining accounts. For services that do not support gMSAs, set a 25+ character random password. RC4 cracking against a 25-character password is computationally infeasible regardless of GPU budget.
  • Remove unused SPNs. SPNs on accounts that no longer host the service are pure attack surface. Audit servicePrincipalName against actual running services annually.
# Set AES-only on a service account
Set-ADUser -Identity svc-sql-prod -Replace @{ 'msDS-SupportedEncryptionTypes' = 24 }

# List all SPNs in the domain for review
Get-ADUser -Filter * -Properties servicePrincipalName |
    Where-Object servicePrincipalName |
    Select-Object SamAccountName, @{N='SPNs'; E={ $_.servicePrincipalName -join '; ' }}

Frequently Asked Questions

Why does Event ID 4769 fire so often even in a quiet domain?

4769 is the standard Kerberos service ticket flow — every domain client requests one for every service it talks to, then caches it for the ticket lifetime (10 hours by default). Outlook, file shares, SQL connections, RDP, and internal web apps all generate 4769s constantly. Volume is normal; the goal is filtering on encryption type and request shape, not on overall count.

Can I detect Kerberoasting without forwarding logs from every domain controller?

Not reliably. A client can request a service ticket from any DC the domain resolves; observing only one DC misses tickets issued by the others. For consistent coverage, forward the Security log from every DC to a single collector or SIEM and run detections against the merged stream.

What encryption type values should I expect in a healthy domain?

0x12 (AES256) is the modern default for tickets issued to AES-capable accounts. 0x11 (AES128) appears for older or differently configured accounts. 0x17 (RC4-HMAC) should be rare and should map to a known list of legacy accounts. 0x18 (RC4-HMAC-EXP) is exceptional and worth investigating wherever it shows up.

Do gMSAs eliminate Kerberoasting entirely?

For the accounts they cover, effectively yes. The 240-character random password is rotated automatically and cannot be brute-forced at any realistic speed. gMSAs do not retroactively protect accounts that still hold cracking-feasible passwords, so the migration is the work — the protection is automatic once it lands.

Will Kerberoasting still be detectable if the attacker uses AES?

The encryption-type signal goes away, but the behavioural signal does not. Tools that request tickets for every SPN they enumerate still produce the burst pattern in Step 4 — one principal asking for many distinct SPNs in a short window. The honey SPN in Step 5 also fires regardless of encryption type. Defense in depth matters here precisely because the easiest detection can be bypassed.

Conclusion

Kerberoasting is the rare offensive technique where the protocol-level signal is unambiguous if the right audit is on. Enable Kerberos Service Ticket Operations auditing, forward 4769s from every DC, alert on RC4 issuance to non-machine accounts, and add a honey SPN for high-fidelity coverage. Then do the unglamorous half of the work: AES-only encryption types, gMSAs where possible, and long random passwords on whatever remains.

The detections in this post are not novel — they are the same patterns the public detection-engineering community has published since 2016. What makes them effective is having them on, having them centralised, and having the hardening done so the alerts that fire actually mean something.

Related Posts

0 comments:

Post a Comment