Query Details

Unusual Azure AD Conditional Access Failures after Policy Change

Unusual AAD Conditional Access Failures After Policy Change

Query

BehaviorAnalytics
| where ActivityInsights.UnusualNumberOfAADConditionalAccessFailures == "True"
| extend UserPrincipalName = tolower(UserPrincipalName)
| join kind=inner (
    union SigninLogs, AADNonInteractiveUserSignInLogs
    | where ConditionalAccessStatus == "failure"
    | mv-expand ConditionalAccessPolicies_dynamic
    | extend ConditionalAccessResult = parse_json(ConditionalAccessPolicies_dynamic.result)
    | extend ConditionalAccessName = parse_json(ConditionalAccessPolicies_dynamic.displayName)
    | extend ConditionalAccessId = parse_json(ConditionalAccessPolicies_dynamic.id)
    | extend ConditionalAccessEnforcedControl = parse_json(tostring(ConditionalAccessPolicies_dynamic.enforcedGrantControls))
    | extend SourceIPAddress = IPAddress
    | extend UserPrincipalName = tolower(UserPrincipalName)
    | where ConditionalAccessResult == "failure"
    | distinct CorrelationId, UserDisplayName, UserPrincipalName, SourceIPAddress, tostring(ConditionalAccessName), tostring(ConditionalAccessId), tostring(ConditionalAccessResult), tostring(ConditionalAccessEnforcedControl), ResultType, AADTenantId
) on UserPrincipalName, SourceIPAddress
| summarize count() by ConditionalAccessName, ConditionalAccessId, ResultType, AADTenantId
| join kind=inner (
    AuditLogs
    | where OperationName == "Update conditional access policy"
    | extend ConditionalAccessId = tostring(TargetResources[0].id)
) on ConditionalAccessId
| extend Actor = parse_json(tostring(InitiatedBy.user)).userPrincipalName
| project TimeGenerated, OperationName, Actor, ConditionalAccessName, CorrelationId, ResultType, count_
| extend AccountCustomEntity = Actor

Explanation

This query is designed to detect unusual numbers of Azure AD Conditional Access failures after a policy change. It uses Behavior Analytics and Azure AD Audit Log data in Microsoft Sentinel to correlate the failures with sign-in logs. The query checks for any unusual number of failures and then joins the relevant information from the sign-in logs and audit logs. The result is a summary of the count of failures for each conditional access policy that has been updated. The query is run once a day and has a medium severity level. The relevant techniques for this query are Initial Access and Persistence.

Details

Thomas Naunheim profile picture

Thomas Naunheim

Released: August 23, 2023

Tables

BehaviorAnalyticsSigninLogsAADNonInteractiveUserSignInLogsAuditLogs

Keywords

BehaviorAnalyticsSigninLogsAADNonInteractiveUserSignInLogsAuditLogsUserPrincipalNameIPAddressConditionalAccessStatusConditionalAccessPolicies_dynamicConditionalAccessResultConditionalAccessNameConditionalAccessIdConditionalAccessEnforcedControlSourceIPAddressCorrelationIdUserDisplayNameResultTypeAADTenantIdOperationNameTargetResourcesInitiatedBy.userTimeGeneratedActorcount_AccountCustomEntityFullName

Operators

whereextendjoinunionmv-expandparse_jsontolowerdistinctsummarizeproject

Severity

Medium

Tactics

InitialAccessPersistence

MITRE Techniques

Frequency: 1d

Period: 1d

Actions

GitHub