Query Details

RULE 01 AD Kerberoasting RC4 Bulk

Query

// =========================================================
// RULE-01 | AD-Kerberoasting-RC4-Bulk
// Description : Bulk RC4-encrypted TGS requests from a single
//               user — the Kerberoasting signature.  Any
//               domain user can request a TGS for any SPN;
//               RC4 (0x17) is preferred by attackers because
//               it yields mode-13100 hashcat hashes.
// Severity    : High  → Critical when ≥10 SPNs or DA-member
// Frequency   : Every 30 minutes, look-back 30 minutes
// MITRE       : T1558.003 — Kerberoasting
// Tables      : SecurityEvent, SigninLogs (optional enrichment)
// NOT duplicated: No Sentinel built-in covers RC4 TGS bulk
//                 requests. Built-ins cover brute force (4625),
//                 not Kerberoast (4769).
// =========================================================

let LookBack         = 30m;
let MinSPNsThreshold = 5;      // ≥5 different service accounts = alert

// --- Build a DC list dynamically from known KDC issuers ---
let KnownDCs = SecurityEvent
    | where TimeGenerated > ago(7d)
    | where EventID == 4768                        // AS-REQ: only DCs issue these
    | summarize by Computer
    | project Computer = toupper(Computer);

// --- Main query ---
let KerberoastCandidates = SecurityEvent
    | where TimeGenerated > ago(LookBack)
    | where EventID == 4769                        // TGS-REP
    | where TicketEncryptionType == "0x17"         // RC4-HMAC — Kerberoastable
    | where ServiceName !endswith "$"              // Exclude machine accounts
    | where ServiceName !in ("krbtgt", "kadmin/changepw")
    | where TargetUserName !endswith "$"           // Exclude computer requesters
    | extend
        RequestingUser    = tolower(TargetUserName),
        RequestingDomain  = tolower(TargetDomainName),
        ServiceSPN        = ServiceName,
        SourceIP          = IpAddress
    | summarize
        SPNsRequested       = dcount(ServiceSPN),
        SPNList             = make_set(ServiceSPN, 30),
        TotalRequests       = count(),
        SourceIPs           = make_set(IpAddress, 5),
        EarliestRequest     = min(TimeGenerated),
        LatestRequest       = max(TimeGenerated)
        by RequestingUser, RequestingDomain, Computer
    | where SPNsRequested >= MinSPNsThreshold;

// --- Enrich with SigninLogs risky sign-in context (join on user UPN) ---
let RiskySignIns = SigninLogs
    | where TimeGenerated > ago(1h)
    | where RiskLevelDuringSignIn in ("medium", "high")
        or RiskState == "confirmedCompromised"
    | summarize RiskySignIn = any(1) by UserPrincipalName = tolower(UserPrincipalName);

KerberoastCandidates
| extend UserUPN = strcat(RequestingUser, "@", RequestingDomain)
| join kind=leftouter (RiskySignIns) on $left.UserUPN == $right.UserPrincipalName
| extend
    Severity = case(
        SPNsRequested >= 10 or RiskySignIn == 1, "Critical",
        SPNsRequested >= 5,                       "High",
        "Medium"
    ),
    AttackWindow_sec = datetime_diff("second", LatestRequest, EarliestRequest),
    RequestsPerMin   = round(todouble(TotalRequests) / max_of(1.0, todouble(datetime_diff("minute", LatestRequest, EarliestRequest))), 1)
| extend WhySuspicious = strcat(
    iff(SPNsRequested >= 10,    "BulkRC4_>=10SPNs; ", ""),
    iff(SPNsRequested >= 5,     "BulkRC4_>=5SPNs; ",  ""),
    iff(RiskySignIn == 1,       "RiskySignIn_Correlated; ", ""),
    iff(RequestsPerMin > 5.0,   "HighRate_", ""), iff(RequestsPerMin > 5.0, tostring(RequestsPerMin), ""), iff(RequestsPerMin > 5.0, "req/min; ", "")
)
| project
    TimeGenerated         = LatestRequest,
    Severity,
    WhySuspicious,
    RequestingUser,
    RequestingDomain,
    Computer,
    SPNsRequested,
    TotalRequests,
    RequestsPerMin,
    AttackWindow_sec,
    SPNList,
    SourceIPs
| order by SPNsRequested desc

Explanation

This KQL query is designed to detect potential Kerberoasting attacks, specifically focusing on bulk requests for RC4-encrypted Ticket Granting Service (TGS) tickets. Here's a simplified breakdown of what the query does:

  1. Purpose: The query aims to identify suspicious activity where a single user requests multiple RC4-encrypted TGS tickets, which is a common technique used in Kerberoasting attacks. Attackers prefer RC4 encryption because it can be easily cracked using tools like hashcat.

  2. Severity Levels: The query assigns a severity level to each detected activity:

    • "Critical" if 10 or more Service Principal Names (SPNs) are requested or if there's a risky sign-in.
    • "High" if 5 or more SPNs are requested.
    • "Medium" otherwise.
  3. Data Sources: It uses data from the SecurityEvent table to identify TGS requests and optionally enriches this data with SigninLogs to check for risky sign-ins.

  4. Dynamic Domain Controller List: It dynamically builds a list of known Domain Controllers (DCs) by looking at recent Authentication Service Requests (AS-REQs).

  5. Main Detection Logic:

    • Filters for TGS requests (EventID 4769) using RC4 encryption (TicketEncryptionType "0x17").
    • Excludes requests from machine accounts and certain service accounts.
    • Groups the data by user and domain, counting the number of unique SPNs requested.
  6. Enrichment with Risky Sign-ins: It checks if any of the users involved in these requests had risky sign-ins within the last hour.

  7. Output: The query outputs a list of suspicious activities, including details like the user, domain, number of SPNs requested, total requests, request rate, and why the activity is considered suspicious.

  8. Ordering: The results are ordered by the number of SPNs requested, highlighting the most suspicious activities first.

In summary, this query helps security teams identify potential Kerberoasting attacks by flagging users who request a large number of RC4-encrypted TGS tickets, especially if they have had risky sign-ins.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

SecurityEventSigninLogs

Keywords

SecurityEventSigninLogsComputerTimeGeneratedEventIDTicketEncryptionTypeServiceNameTargetUserNameTargetDomainNameIpAddressRequestingUserRequestingDomainServiceSPNSourceIPUserPrincipalNameRiskLevelDuringSignInRiskStateUserUPNSeverityAttackWindow_secRequestsPerMinWhySuspiciousSPNsRequestedSPNListTotalRequestsSourceIPsEarliestRequestLatestRequest

Operators

letagowheresummarizebyprojecttouppertolowerextenddcountmake_setminmaxjoinkindoncasedatetime_diffroundtodoublemax_ofiffstrcattostringorder

Actions