HUNT 01 AD Kerberoasting Service Account Profile 90d
Query
// =========================================================
// HUNT-01 | AD-Kerberoasting-ServiceAccount-Profile-90d
// Description : Comprehensive profile of all RC4-encrypted
// TGS requests per service account over 90 days.
// Identifies patterns of targeted vs. spray
// kerberoasting, new requesters, and off-hours
// bursts. Baseline for setting RULE-01 thresholds.
// Period : 90 days
// Use Case : Credential access path reconstruction,
// service account exposure review
// Tables : SecurityEvent
// =========================================================
let Period = 90d;
SecurityEvent
| where TimeGenerated > ago(Period)
| where EventID == 4769
| where tostring(column_ifexists("TicketEncryptionType", "")) == "0x17" // RC4-HMAC only
| where ServiceName !endswith "$"
| where ServiceName !in ("krbtgt", "kadmin/changepw")
| where TargetUserName !endswith "$"
| extend
HourOfDay = hourofday(TimeGenerated),
DayOfWeek = dayofweek(TimeGenerated),
IsOffHours = hourofday(TimeGenerated) < 8 or hourofday(TimeGenerated) >= 19,
IsWeekend = dayofweek(TimeGenerated) in (0d, 6d) // Sun = 0d, Sat = 6d
| summarize
TotalRequests = count(),
UniqueRequesters = dcount(TargetUserName),
Requesters = make_set(TargetUserName, 50),
SourceIPs = make_set(IpAddress, 20),
OffHoursRequests = countif(IsOffHours),
WeekendRequests = countif(IsWeekend),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
RequestsLast7d = countif(TimeGenerated > ago(7d)),
RequestsLast30d = countif(TimeGenerated > ago(30d))
by ServiceAccount = ServiceName
| extend
OffHoursRatio = round(todouble(OffHoursRequests) / todouble(TotalRequests), 2),
WeekendRatio = round(todouble(WeekendRequests) / todouble(TotalRequests), 2),
RecentSurge = RequestsLast7d > (RequestsLast30d / 4),
RiskScore = (UniqueRequesters * 5)
+ (OffHoursRequests * 2)
+ (WeekendRequests * 2)
+ iff(RequestsLast7d > RequestsLast30d / 2, 30, 0)
| extend
RiskLevel = case(
RiskScore >= 100, "Critical",
RiskScore >= 50, "High",
RiskScore >= 20, "Medium",
"Low"
)
| project
ServiceAccount,
RiskLevel,
RiskScore,
TotalRequests,
UniqueRequesters,
OffHoursRatio,
WeekendRatio,
RecentSurge,
RequestsLast7d,
RequestsLast30d,
FirstSeen,
LastSeen,
Requesters,
SourceIPs
| order by RiskScore descExplanation
This query is designed to analyze and profile Kerberos Ticket Granting Service (TGS) requests that use RC4 encryption over a 90-day period. It focuses on identifying patterns that might indicate either targeted or broad "spray" attacks on service accounts. Here's a simplified breakdown of what the query does:
-
Time Frame: It examines data from the last 90 days.
-
Event Filtering: It looks specifically at security events with Event ID 4769, which are Kerberos TGS requests, and filters for those using RC4 encryption (indicated by "0x17").
-
Exclusions: It excludes certain service accounts and user names that end with "$", as well as specific accounts like "krbtgt" and "kadmin/changepw".
-
Time Analysis: It calculates the hour and day of the week for each event to determine if requests occur during off-hours (before 8 AM or after 7 PM) or on weekends.
-
Data Aggregation: For each service account, it summarizes:
- Total number of requests.
- Number of unique requesters.
- Lists of requesters and source IPs.
- Counts of requests during off-hours and weekends.
- First and last time the service account was seen in the data.
- Requests in the last 7 and 30 days.
-
Risk Assessment: It calculates ratios for off-hours and weekend requests and checks for recent surges in activity. A risk score is computed based on these factors, and a risk level (Critical, High, Medium, Low) is assigned.
-
Output: The results are projected to show key metrics like risk level, risk score, total requests, and recent activity, and are sorted by risk score in descending order.
This query helps in reconstructing credential access paths and reviewing the exposure of service accounts to potential attacks.
Details

David Alonso
Released: March 24, 2026
Tables
Keywords
Operators