Kerberos attacks are forensically challenging because they abuse legitimate protocol features — the attacker is not exploiting a vulnerability, they are using stolen cryptographic material correctly.
Golden Ticket Detection
# Search for Golden Ticket indicators in Windows Security Event Log
# Event 4769 with RC4 encryption (anomalous in AES domain)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
Id = 4769
} | ForEach-Object {
$xml = [xml]$_.ToXml()
$encType = $xml.Event.EventData.Data |
Where-Object {$_.Name -eq 'TicketEncryptionType'} |
Select-Object -ExpandProperty '#text'
if ($encType -eq '0x17') {
[PSCustomObject]@{
Time = $_.TimeCreated
Account = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'AccountName'}).'#text'
Service = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'ServiceName'}).'#text'
ClientIP = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'IpAddress'}).'#text'
EncType = $encType
}
}
} | Sort-Object Time
# Event 4624 with no preceding 4768 within time window (ticket used without TGT issuance)
# Requires correlation — use SIEM rather than raw event log
# Splunk query example:
# index=wineventlog EventCode=4624 Logon_Type=3
# | join type=left Account_Name [search index=wineventlog EventCode=4768 | rename Account_Name as Account_Name]
# | where isnull(EventCode_from_4768)
# Check for impossible ticket lifetimes (Golden Tickets often set to 10 years = 87600 hours)
# This requires checking Kerberos ticket cache on endpoint, not DC event logs
# On endpoint: klist
# Look for tickets with end times years in the future
klist
Kerberoasting Detection
# Find all SPNs in the domain (what the attacker would enumerate first)
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} `
-Properties ServicePrincipalName, PasswordLastSet, LastLogonDate, MemberOf |
Select-Object SamAccountName, ServicePrincipalName, PasswordLastSet, LastLogonDate |
Format-Table -AutoSize
# Find service accounts with weak (crackable) passwords
# These are the highest-risk Kerberoasting targets
Get-ADUser -Filter {ServicePrincipalName -ne "$null"} `
-Properties PasswordLastSet, PasswordNeverExpires |
Where-Object {$_.PasswordLastSet -lt (Get-Date).AddYears(-1) -or $_.PasswordNeverExpires -eq $true} |
Select-Object SamAccountName, PasswordLastSet, PasswordNeverExpires
# Detect Kerberoasting activity in Event 4769
# Look for RC4 TGS requests from non-service accounts
$kerberoast_events = Get-WinEvent -FilterHashtable @{
LogName = 'Security'
Id = 4769
StartTime = (Get-Date).AddDays(-1)
} | Where-Object {
$xml = [xml]$_.ToXml()
$encType = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'TicketEncryptionType'}).'#text'
$encType -eq '0x17'
}
# Group by requesting account to identify burst activity
$kerberoast_events | ForEach-Object {
$xml = [xml]$_.ToXml()
($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'AccountName'}).'#text'
} | Group-Object | Sort-Object Count -Descending
# Find accounts with preauth disabled (AS-REP Roasting targets)
Get-ADUser -Filter {DoesNotRequirePreAuth -eq $true} `
-Properties DoesNotRequirePreAuth, PasswordLastSet |
Select-Object SamAccountName, PasswordLastSet
Mimikatz Detection in Event Logs
Mimikatz leaves specific traces when extracting credentials or manipulating Kerberos tickets:
# Event 4611 — Trusted logon process registered (Mimikatz sekurlsa requires this)
# Event 4614 — Notification package loaded (Mimikatz wdigest enable)
# Event 10 (Sysmon) — Process access to LSASS with specific access rights
# Sysmon Event 10: LSASS access (credential dumping)
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-Sysmon/Operational'
Id = 10
} | Where-Object {
$xml = [xml]$_.ToXml()
$target = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'TargetImage'}).'#text'
$target -like '*lsass*'
} | ForEach-Object {
$xml = [xml]$_.ToXml()
[PSCustomObject]@{
Time = $_.TimeCreated
Source = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'SourceImage'}).'#text'
GrantedAccess = ($xml.Event.EventData.Data | Where-Object {$_.Name -eq 'GrantedAccess'}).'#text'
}
} | Where-Object {$_.GrantedAccess -in @('0x1010', '0x1038', '0x143a', '0x1fffff')}
# 0x1010 = PROCESS_VM_READ + PROCESS_QUERY_INFORMATION (Mimikatz minimum)
# 0x1fffff = PROCESS_ALL_ACCESS (most aggressive — clearly malicious)