Multi-Factor Authentication Request Generation
This detection identifies MFA fatigue attacks where adversaries possessing valid credentials repeatedly trigger MFA push notifications, SMS codes, or phone calls to overwhelm target users into approving fraudulent authentication requests. The detection monitors Azure AD and identity provider sign-in logs for abnormally high volumes of MFA challenge events against a single account within a short time window, with elevated severity when a successful authentication follows the bombardment — a pattern consistent with documented TTPs from APT29, Scattered Spider, and LAPSUS$. The technique may also abuse Self-Service Password Reset (SSPR) flows to generate MFA requests without initially possessing valid credentials.
let MFAInterruptCodes = dynamic(["50074", "50076", "50158", "500121", "50072", "53003"]);
let LookbackWindow = 24h;
let BinSize = 1h;
let MFABombardThreshold = 5;
// Step 1: Aggregate MFA failures per user per hour
let MFAEvents = SigninLogs
| where TimeGenerated > ago(LookbackWindow)
| where ResultType in~ (MFAInterruptCodes)
or (ResultDescription has_any ("MFA", "multi-factor", "strong authentication") and ResultType != "0")
| summarize
MFAAttempts = count(),
UniqueIPs = dcount(IPAddress),
IPList = make_set(IPAddress, 10),
FirstMFARequest = min(TimeGenerated),
LastMFARequest = max(TimeGenerated),
Apps = make_set(AppDisplayName, 5),
ErrorCodes = make_set(ResultType, 10),
Locations = make_set(tostring(LocationDetails), 5)
by UserPrincipalName, bin(TimeGenerated, BinSize);
// Step 2: Find successful logins in the same lookback window
let SuccessEvents = SigninLogs
| where TimeGenerated > ago(LookbackWindow)
| where ResultType == "0"
| summarize
SuccessCount = count(),
EarliestSuccess = min(TimeGenerated),
SuccessIPs = make_set(IPAddress, 5)
by UserPrincipalName;
// Step 3: Join and flag fatigue success scenarios
MFAEvents
| where MFAAttempts >= MFABombardThreshold
| join kind=leftouter (SuccessEvents) on UserPrincipalName
| extend
FatigueSucceeded = iff(isnotnull(EarliestSuccess) and EarliestSuccess > FirstMFARequest, true, false),
DurationMinutes = datetime_diff('minute', LastMFARequest, FirstMFARequest),
RiskLevel = case(
isnotnull(EarliestSuccess) and EarliestSuccess > FirstMFARequest, "Critical",
MFAAttempts >= 10, "High",
"Medium")
| project
TimeGenerated,
UserPrincipalName,
MFAAttempts,
UniqueIPs,
IPList,
DurationMinutes,
FirstMFARequest,
LastMFARequest,
Apps,
ErrorCodes,
Locations,
FatigueSucceeded,
EarliestSuccess,
SuccessIPs,
RiskLevel
| order by MFAAttempts desc Data Sources
Required Tables
False Positives
- Users with intermittent network connectivity who retry authentication multiple times legitimately, generating repeated MFA prompts without adversarial intent
- Conditional Access policies configured to always require MFA on every request (rather than session-based) can generate high volumes of 50076 events for normal users
- Automated service accounts or scripts using interactive auth flows that repeatedly fail MFA challenge — these should use device-based or certificate auth instead
- IT administrators testing MFA policies, running phishing simulation exercises, or conducting MFA enrollment drives that generate burst MFA events for multiple accounts
References (6)
- https://attack.mitre.org/techniques/T1621/
- https://www.cisa.gov/sites/default/files/publications/fact-sheet-implement-number-matching-in-mfa-applications-508c.pdf
- https://www.obsidiansecurity.com/blog/account-takeover-through-sspr-abuse/
- https://portswigger.net/daily-swig/mfa-fatigue-attacks-users-tricked-into-allowing-device-access
- https://www.crowdstrike.com/blog/scattered-spider-attempts-to-avoid-detection-with-bring-your-own-vulnerable-driver-tactic/
- https://www.microsoft.com/en-us/security/blog/2022/10/25/microsoft-mitigates-lapsus-attacks-via-mfa-policy/
Unlock Pro Content
Get the full detection package for T1621 including response playbook, investigation guide, and atomic red team tests.