Query Details

08 SIGNIN Attacker In The Middle

Query

id: d8e9f0a1-b2c3-4d4e-5f6a-7b8c9d0e1f2a
name: "SigninLogs — Attacker in the Middle (AiTM) Token Theft Detected"
version: 1.0.0
kind: Scheduled
description: |
  Detects AiTM proxy attacks (Evilginx, Modlishka) that steal session cookies after MFA. Correlates SecurityAlert 'Anomalous Token' events with AADUserRiskEvents where RiskEventType == attackerinTheMiddle. Requires Entra ID P2. MITRE ATT&CK: T1557 (Adversary-in-the-Middle), T1539 (Steal Web Session Cookie).
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: PT1H
queryPeriod: P7D
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - Collection
relevantTechniques:
  - T1557
  - T1539
query: |

    // ---- Network Allowlist (exclude trusted IPs / CIDR / ranges) --------------
    let _allow = materialize(union isfuzzy=true (print R="" | take 0), (_GetWatchlist('NetworkAllowlist') | project R = tostring(IPOrRange)) | where isnotempty(R));
    let _allowCIDR  = toscalar(_allow | where not(R matches regex @'^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$') | extend R = iff(R has '/', R, strcat(R, '/32')) | summarize make_list(R));
    let _allowRange = toscalar(_allow | where R matches regex @'^\d+\.\d+\.\d+\.\d+-\d+\.\d+\.\d+\.\d+$' | summarize make_list(R));
    let _ExcludeAllowlistedIPs = (T:(IPAddress:string)) {
        T
        | extend IPAddress = tostring(IPAddress)
        | where array_length(_allowCIDR) == 0 or isnull(ipv4_is_in_any_range(IPAddress, _allowCIDR)) or not(ipv4_is_in_any_range(IPAddress, _allowCIDR))
        | mv-apply _r = _allowRange to typeof(string) on (
            extend _lo = tostring(split(_r,'-')[0]), _hi = tostring(split(_r,'-')[1])
            | extend _inRange = ipv4_compare(IPAddress, _lo) >= 0 and ipv4_compare(IPAddress, _hi) <= 0
            | summarize _anyInRange = max(toint(_inRange)))
        | where isnull(_anyInRange) or _anyInRange == 0
        | project-away _anyInRange
    };
    // ---------------------------------------------------------------------------
    let AnomalousTokenRequestIds =
        SecurityAlert
        | where TimeGenerated > ago(7d)
        | where AlertName == "Anomalous Token"
        | mv-expand todynamic(Entities)
        | project Entities
        | extend RequestId = tostring(Entities.RequestId)
        | distinct RequestId;
    AADUserRiskEvents
    | where TimeGenerated > ago(7d)
    | where RequestId has_any (AnomalousTokenRequestIds)
    | where RiskEventType == "attackerinTheMiddle"
    | join kind=leftouter (
        SigninLogs
        | invoke _ExcludeAllowlistedIPs()
        | where TimeGenerated > ago(7d)
        | project
            UserPrincipalName, CorrelationId,
            Location, AppDisplayName, IPAddress,
            RiskLevelDuringSignIn
      ) on $left.CorrelationId == $right.CorrelationId
    | project
        TimeGenerated,
        UserPrincipalName,
        RiskEventType, IPAddress,
        Location, AppDisplayName,
        RiskLevelDuringSignIn
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
customDetails:
  AppDisplayName: AppDisplayName
  RiskEventType: RiskEventType
alertDetailsOverride:
  alertDisplayNameFormat: "AiTM Token Theft — {{UserPrincipalName}} from {{IPAddress}}"
  alertDescriptionFormat: "Attacker-in-the-Middle session hijack detected — {{AppDisplayName}} session token stolen post-MFA."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT1H
    matchingMethod: AllEntities
    groupByEntities:
  - Account
  - IP



Explanation

This query is designed to detect potential Attacker-in-the-Middle (AiTM) token theft incidents, which involve stealing session cookies after a user has completed multi-factor authentication (MFA). Here's a simple breakdown of what the query does:

  1. Purpose: The query identifies suspicious activities that may indicate an AiTM attack, specifically focusing on session token theft using tools like Evilginx or Modlishka.

  2. Data Sources: It uses data from Azure Active Directory's SigninLogs and SecurityAlert logs, as well as AADUserRiskEvents.

  3. Detection Logic:

    • It first identifies "Anomalous Token" security alerts from the past 7 days.
    • It then looks for user risk events where the risk type is "attackerinTheMiddle" and correlates these with the anomalous token alerts.
    • The query excludes any IP addresses that are on a predefined allowlist (trusted IPs) to reduce false positives.
  4. Output: The query produces a list of incidents with details such as the user's principal name, IP address, location, application name, and risk level during sign-in.

  5. Severity and Response: The severity of these alerts is marked as high, and the system is configured to create incidents for further investigation. Alerts are grouped by account and IP to streamline incident management.

  6. Alert Customization: The alert display name and description are customized to provide clear information about the detected threat, including the user and IP involved.

  7. Incident Management: The query is set to automatically create incidents, with a configuration that groups related alerts to avoid duplication and streamline response.

Overall, this query helps security teams quickly identify and respond to sophisticated attacks that bypass traditional security measures by hijacking session tokens post-MFA.

Details

David Alonso profile picture

David Alonso

Released: April 20, 2026

Tables

SecurityAlertAADUserRiskEventsSigninLogs

Keywords

SigninLogsSecurityAlertAADUserRiskEventsAzureActiveDirectoryNetworkAllowlistIPAddressUserPrincipalNameCorrelationIdLocationAppDisplayNameRiskLevelDuringSignInRiskEventTypeAccountIP

Operators

letmaterializeunionisfuzzyprinttakeprojecttostringwhereisnotemptytoscalarmatchesregexextendiffhasstrcatsummarizemake_listnotipv4_is_in_any_rangemv-applytotypeofonsplitipv4_comparemaxtointproject-awaySecurityAlertTimeGeneratedagoAlertNamemv-expandtodynamicdistinctAADUserRiskEventshas_anyjoinkindleftouterSigninLogsinvokeprojectentityMappingsentityTypefieldMappingsidentifiercolumnNamecustomDetailsalertDetailsOverridealertDisplayNameFormatalertDescriptionFormatincidentConfigurationcreateIncidentgroupingConfigurationenabledreopenClosedIncidentlookbackDurationmatchingMethodgroupByEntities

Actions