Query Details

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 desc

Explanation

This query is designed to analyze Kerberos Ticket Granting Service (TGS) requests that use RC4 encryption over the past 90 days. It focuses on identifying patterns in service account usage, particularly looking for signs of potential security risks such as targeted attacks or unusual activity. Here's a simplified breakdown of what the query does:

  1. Data Filtering: It examines security events from the last 90 days, specifically looking for events with ID 4769, which are TGS requests. It filters for requests using RC4 encryption and excludes certain service accounts and usernames that end with "$" or are known system accounts like "krbtgt" and "kadmin/changepw".

  2. Time Analysis: The query calculates the hour and day of the week for each request to determine if they occurred during off-hours (before 8 AM or after 7 PM) or on weekends.

  3. Summarization: It aggregates data by service account, counting the total number of requests, unique requesters, and capturing the IP addresses involved. It also tracks requests made during off-hours and weekends, and notes the first and last time each service account was seen.

  4. Recent Activity: It checks for a surge in requests over the last 7 days compared to the last 30 days.

  5. Risk Assessment: A risk score is calculated based on the number of unique requesters, off-hours and weekend requests, and recent activity. The risk score is then used to assign a risk level (Critical, High, Medium, or Low).

  6. Output: The results are projected to show key information such as the service account name, risk level, risk score, total requests, and other relevant metrics. The output is sorted by risk score in descending order to highlight the most potentially risky service accounts.

Overall, this query helps in identifying service accounts that might be at risk of being targeted for credential theft, allowing for proactive security measures.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

SecurityEvent

Keywords

SecurityEvent

Operators

letagowheretostringcolumn_ifexistsendswithinextendhourofdaydayofweeksummarizecountdcountmake_setcountifminmaxroundtodoubleiffcaseprojectorder by

Actions