Adversaries may be attempting to bypass MFA by spamming failed authentication attempts to exhaust user account lockouts or trigger account lockout policies. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential credential stuffing or account takeover attempts before they escalate.
KQL Query
// Filter for sign-in logs ingested within the last day
SigninLogs
| where ingestion_time() > ago(1d)
// Extract information from dynamic columns DeviceDetail and LocationDetails
| extend
DeviceDetail = todynamic(DeviceDetail),
LocationDetails = todynamic(LocationDetails)
// Extract specific attributes from DeviceDetail and LocationDetails
| extend
OS = tostring(DeviceDetail.operatingSystem),
Browser = tostring(DeviceDetail.browser),
State = tostring(LocationDetails.state),
City = tostring(LocationDetails.city),
Region = tostring(LocationDetails.countryOrRegion)
// Filter for records with AuthenticationRequirement set to multiFactorAuthentication
| where AuthenticationRequirement == "multiFactorAuthentication"
// Expand multi-value property AuthenticationDetails into separate records
| mv-expand todynamic(AuthenticationDetails)
// Parse AuthResult from JSON in AuthenticationDetails and convert to string
| extend AuthResult = tostring(parse_json(AuthenticationDetails).authenticationStepResultDetail)
// Summarize data by aggregating statistics for each user, IP, and AuthResult
| summarize FailedAttempts = countif(AuthResult == "MFA denied; user declined the authentication" or AuthResult == "MFA denied; user did not respond to mobile app notification"),
SuccessfulAttempts = countif(AuthResult == "MFA successfully completed"),InvolvedOS=make_set(OS,5),InvolvedBrowser=make_set(Browser),
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated) by UserPrincipalName, IPAddress,State,City,Region
// Calculate AuthenticationWindow by finding time difference between start and end times
| extend AuthenticationWindow = (EndTime - StartTime)
// Filter for records with more than 10 failed attempts in 5-minute window and at least 1 successful attempt
| where FailedAttempts > 10 and AuthenticationWindow <= 5m
// Extract user's name and UPN suffix using split function
| extend Name = tostring(split(UserPrincipalName, '@', 0)[0]), UPNSuffix = tostring(split(UserPrincipalName, '@', 1)[0])
id: 7f87c43a-6aff-44fe-907f-651986cbf956
name: MFA Spamming
description: |
'Identifies list of user impacted by MFA Spamming within a given time window,Default Failure count is 10 with default Time Window is 5 minutes'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
// Filter for sign-in logs ingested within the last day
SigninLogs
| where ingestion_time() > ago(1d)
// Extract information from dynamic columns DeviceDetail and LocationDetails
| extend
DeviceDetail = todynamic(DeviceDetail),
LocationDetails = todynamic(LocationDetails)
// Extract specific attributes from DeviceDetail and LocationDetails
| extend
OS = tostring(DeviceDetail.operatingSystem),
Browser = tostring(DeviceDetail.browser),
State = tostring(LocationDetails.state),
City = tostring(LocationDetails.city),
Region = tostring(LocationDetails.countryOrRegion)
// Filter for records with AuthenticationRequirement set to multiFactorAuthentication
| where AuthenticationRequirement == "multiFactorAuthentication"
// Expand multi-value property AuthenticationDetails into separate records
| mv-expand todynamic(AuthenticationDetails)
// Parse AuthResult from JSON in AuthenticationDetails and convert to string
| extend AuthResult = tostring(parse_json(AuthenticationDetails).authenticationStepResultDetail)
// Summarize data by aggregating statistics for each user, IP, and AuthResult
| summarize FailedAttempts = countif(AuthResult == "MFA denied; user declined the authentication" or AuthResult == "MFA denied; user did not respond to mobile app notification"),
SuccessfulAttempts = countif(AuthResult == "MFA successfully completed"),InvolvedOS=make_set(OS,5),InvolvedBrowser=make_set(Browser),
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated) by UserPrincipalName, IPAddress,State,City,Region
// Calculate AuthenticationWindow by finding time difference between start and end times
| extend AuthenticationWindow = (EndTime - StartTime)
// Filter for records with more than 10 failed attempts in 5-minute window and at least 1 successful attempt
| where FailedAttempts > 10 and AuthenticationWindow <= 5m
// Extract user's name and UPN suffix using split function
| extend Name = tostring(split(UserPrincipalName, '@', 0)[0]), UPNSuffix = tostring(split(UserPrincipalName, '@', 1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UserPrincipalName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
version: 1.0.0
metadata:
source:
kind: Community
author:
name: Praveen Kumar Balasundaram
support:
tier: Community
categories:
domains: [ "Security - Other", "Identity" ]
| Sentinel Table | Notes |
|---|---|
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled MFA Reset Job
Description: A system administrator runs a scheduled job to reset MFA for multiple users during a maintenance window.
Filter/Exclusion: Exclude events where the user is part of a predefined “MFA Reset Group” or where the activity is logged under a known administrative task (e.g., Microsoft Azure MFA Reset Job).
Scenario: User MFA Reauthentication During Login
Description: A user attempts to log in multiple times and reauthenticates their MFA device, which could be flagged as spamming.
Filter/Exclusion: Exclude events where the user is reauthenticating within a short time window (e.g., 2 minutes) after a successful login, or where the event is logged under a known authentication flow (e.g., Microsoft Azure MFA Reauth).
Scenario: MFA Token Synchronization Across Devices
Description: A user syncs their MFA token across multiple devices (e.g., phone, tablet, computer), which may trigger multiple MFA prompts within a short time.
Filter/Exclusion: Exclude events where the user is syncing MFA tokens and the activity is logged under a known synchronization task (e.g., Microsoft Azure MFA Sync).
Scenario: MFA Prompt During Multi-Factor Authentication Testing
Description: A security team tests MFA prompts by simulating multiple authentication attempts to validate the system’s behavior.
Filter/Exclusion: Exclude events where the user is part of a “Test Users” group or where the activity is logged under a known test task (e.g., Microsoft Azure MFA Test).
Scenario: MFA Prompt for Multi-Step Authentication Flow
Description: A user is prompted for MFA as part of a multi-step authentication process (e.g., password reset,