Query Details

Multiple Suspicious API Traffic

Query

let query_frequency = 5m;
let query_period = 2d;
AADUserRiskEvents
| where TimeGenerated > ago(query_period)
| where Source == "IdentityProtection" and RiskEventType == "suspiciousAPITraffic"
| 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 == "SuspiciousAPITraffic"
    | 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 (
    AADNonInteractiveUserSignInLogs
    | where TimeGenerated > ago(query_period)
    //| where RiskEventTypes_V2 has "suspiciousAPITraffic"
    | 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,
        //RiskDetail,
        RiskEventTypes_V2,
        RiskLevelAggregated,
        RiskLevelDuringSignIn,
        //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,
//     true
//     )

Explanation

This query is designed to analyze and correlate suspicious API traffic events related to Azure Active Directory (AAD) Identity Protection. 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:

    • It retrieves events from the AADUserRiskEvents table that occurred within the last 2 days.
    • It specifically looks for events where the source is "IdentityProtection" and the risk event type is "suspiciousAPITraffic".
  3. Summarize Events:

    • It summarizes these events to find the first occurrence time and the most recent occurrence of each event by Id.
  4. Filter Recent Events:

    • It filters out events that first occurred more than 5 minutes ago.
  5. Project Relevant Columns:

    • It selects specific columns to include in the output, such as ActivityDateTime, UserDisplayName, IpAddress, and others.
  6. Create Alternate Alert Link:

    • It constructs an alternative alert link for each event using the UserId.
  7. Join with Security Alerts:

    • It performs a left outer join with the SecurityAlert table to find related alerts where the provider is "IPC" and the product is "Azure Active Directory Identity Protection".
    • It matches events based on VendorOriginalId.
  8. Extend Alert Link:

    • It ensures that each event has an alert link, using either the existing link or the alternative link created earlier.
  9. Lookup Non-Interactive Sign-In Logs:

    • It performs a left outer lookup with the AADNonInteractiveUserSignInLogs table to find related sign-in logs within the last 2 days.
    • It matches logs based on RequestId.
  10. Project Additional Sign-In Details:

    • It selects additional columns from the sign-in logs, such as IPAddress, AppDisplayName, UserAgent, and others.

Overall, this query is designed to identify and analyze suspicious API traffic events in Azure AD, correlate them with security alerts, and enrich them with additional sign-in details for further investigation.

Details

Jose Sebastián Canós profile picture

Jose Sebastián Canós

Released: August 29, 2025

Tables

AADUserRiskEventsSecurityAlertAADNonInteractiveUserSignInLogs

Keywords

AADUserRiskEvents,SecurityAlert,AADNonInteractiveUserSignInLogs,IdentityProtection,AzureActiveDirectoryIdentityProtection,SuspiciousAPITraffic,UserDisplayName,UserPrincipalName,UserId,IpAddress,RequestId,CorrelationId,TokenIssuerType,RiskEventType,RiskDetail,RiskLevel,RiskState,AdditionalInfo,AlertName,AlertSeverity,Description,AlertStatus,Entities,ExtendedProperties,VendorName,ProviderName,ProductName,ProductComponentName,RemediationSteps,Tactics,Techniques,SubTechniques,VendorOriginalId,SystemAlertId,CompromisedEntity,AlertLink,OriginalRequestId,AlternateSignInName,UserType,IPAddress,AutonomousSystemNumber,Location,NetworkLocationDetails,ResultType,ResultSignature,ResultDescription,ClientAppUsed,AppDisplayName,ResourceDisplayName,DeviceDetail,UserAgent,Status,MfaDetail,AuthenticationContextClassReferences,AuthenticationDetails,AuthenticationProcessingDetails,AuthenticationProtocol,AuthenticationRequirement,AuthenticationRequirementPolicies,SessionLifetimePolicies,IncomingTokenType,TokenProtectionStatusDetails,ConditionalAccessStatus,ConditionalAccessPolicies,RiskEventTypes_V2,RiskLevelAggregated,RiskLevelDuringSignIn,HomeTenantId,ResourceTenantId,CrossTenantAccessType,AppId,ResourceIdentity,UniqueTokenIdentifier,SessionId

Operators

letagowheresummarizeminarg_maxbyprojectextendstrcatjoinkindoncoalesceaslookuptoscalarmake_listinsummarizearg_max

Actions