Query Details

HUNT 02 AD ASREP Roastable Accounts Audit 90d

Query

// =========================================================
// HUNT-02 | AD-ASREP-Roastable-Accounts-Audit-90d
// Description : Full audit of all accounts with
//               DONT_REQ_PREAUTH (AS-REP Roastable) and
//               historical AS-REP request patterns. Surfaces
//               accounts that have never required pre-auth,
//               and correlates with actual roasting attempts.
// Period      : 90 days
// Use Case    : Attack surface exposure assessment,
//               DONT_REQ_PREAUTH hygiene review
// Tables      : SecurityEvent
// =========================================================

let Period = 90d;

// All 4768 events with tostring(column_ifexists("PreAuthType", "")) = 0 (no pre-auth required)
let ASREPRequests = SecurityEvent
    | where TimeGenerated > ago(Period)
    | where EventID == 4768
    | where tostring(column_ifexists("PreAuthType", "")) == "0"
    | where TargetUserName !endswith "$"
    | extend
        IsOffHours = hourofday(TimeGenerated) < 8 or hourofday(TimeGenerated) >= 19
    | summarize
        TotalASREP          = count(),
        UniqueRequesters    = dcount(IpAddress),
        RequestingIPs       = make_set(IpAddress, 20),
        OffHoursRequests    = countif(IsOffHours),
        FirstSeen           = min(TimeGenerated),
        LastSeen            = max(TimeGenerated),
        RecentRequests      = countif(TimeGenerated > ago(7d))
        by RoastableAccount = TargetUserName,
           AccountDomain    = TargetDomainName;

// Normal 4768 events (with pre-auth, for baseline comparison)
let NormalTGTs = SecurityEvent
    | where TimeGenerated > ago(Period)
    | where EventID == 4768
    | where tostring(column_ifexists("PreAuthType", "")) != "0"
    | where TargetUserName !endswith "$"
    | summarize NormalTGTCount = count()
        by TargetUserName, TargetDomainName;

ASREPRequests
| join kind=leftouter (NormalTGTs) on
    $left.RoastableAccount == $right.TargetUserName,
    $left.AccountDomain    == $right.TargetDomainName
| extend
    OffHoursRatio    = round(todouble(OffHoursRequests) / todouble(TotalASREP), 2),
    // If this account ONLY appears with tostring(column_ifexists("PreAuthType", ""))=0 it's permanently set DONT_REQ_PREAUTH
    OnlySeenASREP    = isnull(NormalTGTCount) or NormalTGTCount == 0,
    RiskScore        = (TotalASREP * 3)
                     + (UniqueRequesters * 5)
                     + iff(OffHoursRequests > 0, 10, 0)
                     + iff(RecentRequests > 0, 20, 0)
| extend
    RiskLevel = case(
        RiskScore >= 60,  "Critical",
        RiskScore >= 25,  "High",
        "Medium"
    ),
    Recommendation = case(
        OnlySeenASREP,  "Set PreAuthentication required, rotate password immediately",
        TotalASREP > 5, "Review why account lacks pre-authentication requirement",
        "Monitor for further requests"
    )
| project
    RoastableAccount,
    AccountDomain,
    RiskLevel,
    RiskScore,
    TotalASREP,
    UniqueRequesters,
    OffHoursRatio,
    OnlySeenASREP,
    RecentRequests,
    FirstSeen,
    LastSeen,
    Recommendation,
    RequestingIPs
| order by RiskScore desc

Explanation

This query is designed to identify and assess the risk of accounts in a network that are vulnerable to a specific type of attack known as AS-REP Roasting. Here's a simple breakdown of what the query does:

  1. Objective: The query aims to audit accounts that do not require pre-authentication (DONT_REQ_PREAUTH), making them susceptible to AS-REP Roasting attacks. It analyzes these accounts over the past 90 days to identify potential security risks.

  2. Data Source: It uses data from the SecurityEvent table, specifically focusing on events with an ID of 4768, which are related to Kerberos authentication requests.

  3. AS-REP Requests: The query first filters for accounts that have made AS-REP requests without pre-authentication. It gathers various statistics about these requests, such as:

    • Total number of such requests (TotalASREP).
    • Number of unique IP addresses making these requests (UniqueRequesters).
    • Requests made during off-hours.
    • The first and last time these requests were seen.
    • Requests made in the last 7 days.
  4. Comparison with Normal Requests: It then compares these accounts with those that have made normal authentication requests (with pre-authentication) to establish a baseline.

  5. Risk Assessment: The query calculates a risk score for each account based on factors like the total number of AS-REP requests, the number of unique requesters, off-hours activity, and recent activity. It assigns a risk level (Critical, High, or Medium) based on this score.

  6. Recommendations: For each account, it provides recommendations such as setting pre-authentication requirements or monitoring the account further.

  7. Output: The final output includes details like the account name, domain, risk level, risk score, total AS-REP requests, unique requesters, off-hours request ratio, whether the account is only seen in AS-REP requests, recent request activity, first and last seen times, recommendations, and the IPs making requests.

The query helps in identifying potentially vulnerable accounts and provides actionable insights to improve security posture.

Details

David Alonso profile picture

David Alonso

Released: March 24, 2026

Tables

SecurityEvent

Keywords

SecurityEvent

Operators

letagocolumn_ifexiststostringendswithextendhourofdaysummarizecountdcountmake_setcountifminmaxjoinkindleftouterroundtodoubleisnulliffcaseprojectorder bydesc

Actions