Query Details

21 SIGNIN Conditional Access Bypass

Query

id: e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b
name: "SigninLogs — Conditional Access Policy Blocked then Successful Bypass"
version: 1.0.0
kind: Scheduled
description: |
  Detects a user blocked by Conditional Access (ResultType 53003) who then achieves a successful sign-in within 4 hours — possibly via a legacy auth client that bypasses CA, an unmanaged device, or a CA policy gap. MITRE ATT&CK: T1078 (Valid Accounts), T1562.001 (Disable or Modify Tools).
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: PT4H
queryPeriod: PT4H
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - InitialAccess
relevantTechniques:
  - T1078
  - T1562
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 lookback     = 4h;
    let bypassWindow = 4h;
    let CABlockedUsers =
        SigninLogs
        | invoke _ExcludeAllowlistedIPs()
        | where TimeGenerated > ago(lookback)
        | where ResultType == "53003"
        | summarize
            BlockedCount = count(),
            LastBlocked  = max(TimeGenerated),
            BlockedIPs   = make_set(IPAddress, 5),
            BlockedApps  = make_set(AppDisplayName, 5)
          by UserPrincipalName;
    SigninLogs
    | invoke _ExcludeAllowlistedIPs()
    | where TimeGenerated > ago(lookback)
    | where ResultType == "0"
    | join kind=inner CABlockedUsers on UserPrincipalName
    | where TimeGenerated > LastBlocked
    | where TimeGenerated - LastBlocked <= bypassWindow
    | extend MinutesAfterBlock = datetime_diff("minute", TimeGenerated, LastBlocked)
    | project
        TimeGenerated, UserPrincipalName,
        IPAddress, Location, AppDisplayName,
        ClientAppUsed, ConditionalAccessStatus,
        RiskLevelDuringSignIn, BlockedCount,
        BlockedIPs, LastBlocked, MinutesAfterBlock
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
customDetails:
  BlockedCount: BlockedCount
  MinutesAfterBlock: MinutesAfterBlock
alertDetailsOverride:
  alertDisplayNameFormat: "CA Policy Bypass — {{UserPrincipalName}} succeeded {{MinutesAfterBlock}}m after {{BlockedCount}} CA blocks"
  alertDescriptionFormat: "User {{UserPrincipalName}} was CA-blocked {{BlockedCount}} times then authenticated successfully {{MinutesAfterBlock}} minutes later — possible legacy auth or unmanaged device bypass."
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT4H
    matchingMethod: AllEntities
    groupByEntities:
  - Account
  - IP



Explanation

This query is designed to detect suspicious sign-in activities in Azure Active Directory. It specifically looks for instances where a user is initially blocked by Conditional Access policies (indicated by a ResultType of 53003) but then successfully signs in within four hours. This could indicate a potential security issue, such as the use of a legacy authentication client that bypasses Conditional Access, an unmanaged device, or a gap in the Conditional Access policy.

Here's a simple breakdown of what the query does:

  1. Network Allowlist: It excludes sign-ins from trusted IP addresses or ranges to focus on potentially suspicious activities.

  2. Lookback Period: The query examines sign-in logs from the past four hours.

  3. Blocked Users: It identifies users who were blocked by Conditional Access policies within the lookback period.

  4. Successful Sign-ins: It then checks if these blocked users managed to sign in successfully within four hours after being blocked.

  5. Alert Generation: If such a scenario is detected, an alert is generated. The alert includes details like the user's name, the number of times they were blocked, and the time it took for them to bypass the block.

  6. Incident Management: The query is configured to create an incident for each detected case, grouping them by user account and IP address for better management.

Overall, this query helps security teams identify and investigate potential security gaps or unauthorized access attempts in their environment.

Details

David Alonso profile picture

David Alonso

Released: April 20, 2026

Tables

SigninLogs

Keywords

SigninLogsAzureActiveDirectoryUserPrincipalNameIPAddressAppDisplayNameClientAppUsedConditionalAccessStatusRiskLevelDuringSignInLocationAccountIP

Operators

letmaterializeunionisfuzzyprinttakeprojectwhereisnotemptytoscalarextendmatchesregexiffstrcatsummarizemake_listnotarray_lengthisnullipv4_is_in_any_rangemv-applytotypeofonsplitipv4_comparemaxtointproject-awayagoinvokeResultTypesummarizecountmake_setbyjoinkindinnerdatetime_diffproject

Actions