Query Details

Multiple Verified Threat Actor IP

Query

let query_frequency = 5m;
let query_period = 2d;
AADUserRiskEvents
| where TimeGenerated > ago(query_period)
| where Source == "IdentityProtection" and RiskEventType == "nationStateIP"
| summarize FirstTimeGenerated = min(TimeGenerated), arg_max(TimeGenerated, *) by Id
| where FirstTimeGenerated > ago(query_frequency)
| project
    //TimeGenerated,
    ActivityDateTime,
    DetectedDateTime,
    Source,
    Activity,
    DetectionTimingType,
    UserDisplayName,
    UserPrincipalName,
    UserId,
    IpAddress,
    RequestId,
    CorrelationId,
    TokenIssuerType,
    RiskEventType,
    RiskDetail,
    RiskLevel,
    RiskState,
    AdditionalInfo,
    Id
| extend AltAlertLink = strcat("https://entra.microsoft.com/#blade/Microsoft_AAD_IAM/RiskDetectionsBlade/riskState~/[]/userId/", UserId, "/riskLevel/[]/daysBack/90")// Someone wrote "90s" incorrectly in Defender XDR portal
| join kind=leftouter (
    SecurityAlert
    | where ProviderName == "IPC" and ProductName == "Azure Active Directory Identity Protection" and AlertType == "NationStateIP"
    | summarize arg_max(TimeGenerated, *) by VendorOriginalId
    | project
        AlertName,
        AlertSeverity,
        Description,
        AlertStatus = Status,
        Entities,
        ExtendedProperties,
        VendorName,
        ProviderName,
        ProductName,
        ProductComponentName,
        RemediationSteps,
        Tactics,
        Techniques,
        SubTechniques,
        VendorOriginalId,
        SystemAlertId,
        CompromisedEntity,
        AlertLink
    ) on $left.Id == $right.VendorOriginalId
| extend AlertLink = coalesce(AlertLink, AltAlertLink)
| as _Events
| lookup kind=leftouter (
    SigninLogs
    | where TimeGenerated > ago(query_period)
    //| where RiskEventTypes_V2 has "estsNationStateIP"
    | where OriginalRequestId in (toscalar(_Events | summarize make_list(RequestId)))
    | extend TimeReceived = _TimeReceived
    | summarize arg_max(TimeReceived, *) by OriginalRequestId
    | project
        TimeGenerated,
        CreatedDateTime,
        Type,
        //UserDisplayName,
        //UserPrincipalName,
        //UserId,
        AlternateSignInName,
        //SignInIdentifier,
        UserType,
        IPAddress,
        AutonomousSystemNumber,
        Location,
        NetworkLocationDetails,
        ResultType,
        ResultSignature,
        ResultDescription,
        ClientAppUsed,
        AppDisplayName,
        ResourceDisplayName,
        DeviceDetail,
        UserAgent,
        Status,
        MfaDetail,
        AuthenticationContextClassReferences,
        AuthenticationDetails,
        AuthenticationProcessingDetails,
        AuthenticationProtocol,
        AuthenticationRequirement,
        AuthenticationRequirementPolicies,
        SessionLifetimePolicies,
        //TokenIssuerType,
        IncomingTokenType,
        TokenProtectionStatusDetails,
        ConditionalAccessStatus,
        ConditionalAccessPolicies,
        SignInLogs_RiskDetail = RiskDetail,
        RiskEventTypes_V2,
        RiskLevelAggregated,
        RiskLevelDuringSignIn,
        SignInLogs_RiskState = RiskState,
        HomeTenantId,
        ResourceTenantId,
        CrossTenantAccessType,
        AppId,
        ResourceIdentity,
        UniqueTokenIdentifier,
        SessionId,
        OriginalRequestId//,
        //CorrelationId
    ) on $left.RequestId == $right.OriginalRequestId
// | where case(
//     AlertStatus == "Resolved" and tostring(todynamic(ExtendedProperties)["State"]) == "Closed", false,
//     RiskState == "dismissed" and RiskDetail == "aiConfirmedSigninSafe", false,
//     RiskState == "remediated" and RiskDetail == "userChangedPasswordOnPremises", false,
//     SignInLogs_RiskState == "remediated" and SignInLogs_RiskDetail == "userPassedMFADrivenByRiskBasedPolicy", false,
//     true
//     )

Explanation

This KQL (Kusto Query Language) query is designed to analyze and correlate risk events and security alerts related to Azure Active Directory (AAD) user activities, specifically focusing on potential nation-state IP threats. Here's a simplified breakdown of what the query does:

  1. Define Time Parameters:

    • query_frequency is set to 5 minutes.
    • query_period is set to 2 days.
  2. Filter AAD User Risk Events:

    • The query retrieves events from the AADUserRiskEvents table that have occurred within the last 2 days.
    • It filters these events to include only those from the "IdentityProtection" source and with a "nationStateIP" risk event type.
  3. Summarize and Filter Events:

    • It summarizes the events by finding the first and last occurrence (FirstTimeGenerated and TimeGenerated) for each unique event ID.
    • Further filters out events where the first occurrence was more than 5 minutes ago.
  4. Project Relevant Columns:

    • Selects specific columns to include in the output, such as user details, IP address, risk level, and more.
  5. Create Alternative Alert Link:

    • Constructs a URL for an alternative alert link using the UserId.
  6. Join with Security Alerts:

    • Performs a left outer join with the SecurityAlert table to correlate risk events with security alerts related to "NationStateIP".
    • Projects relevant alert details like alert name, severity, status, and more.
  7. Extend Alert Link:

    • Uses the coalesce function to choose between the existing alert link and the alternative alert link created earlier.
  8. Lookup Sign-in Logs:

    • Looks up related sign-in logs from the SigninLogs table that match the request IDs from the risk events.
    • Filters sign-in logs to those within the last 2 days and projects relevant sign-in details.
  9. Commented Out Filtering:

    • The query contains commented-out sections that could be used to filter out resolved or remediated alerts and risk events based on specific conditions.

Overall, this query is designed to provide a comprehensive view of potential nation-state IP threats by correlating user risk events with security alerts and sign-in logs, allowing for detailed analysis and investigation.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: August 29, 2025

Tables

AADUserRiskEventsSecurityAlertSigninLogs

Keywords

AADUserRiskEventsSecurityAlertSigninLogs

Operators

letagowheresummarizeminarg_maxprojectextendstrcatjoinoncoalesceaslookuptoscalarmake_listinhastostringtodynamiccase

Actions