Skip to content

LearnIdentity

Password spray and MFA fatigue

How we test password spraying and push-approval fatigue without locking anyone out, and the policies and detections that stop both.

Area
Identity
Stage
Access
Reviewed
On this page
  1. What to check first
  2. Plan the spray against the lockout policy
  3. Run it slowly and log every attempt
  4. Detect password spraying in Entra ID and AD
  5. MFA fatigue (push bombing)
  6. Detect fatigue
  7. What the defender sees
  8. Tools
  9. Related research

Password spraying tries one likely password against many accounts, staying under every account's lockout threshold. MFA fatigue sends push approvals to a user until one is accepted. Spraying beats a password policy that people satisfy with predictable choices; what it yields is a valid password, which is the whole account wherever a sign-in path skips MFA. Fatigue beats MFA that relies on someone approving a prompt. Our MFA field note covers why; this page covers how we test both safely and what the client should change.

Both tests touch real accounts and real people. The scope must name them, the client must agree the timing, and we keep a log of every attempt.

What to check first

  • The lockout threshold and observation window, read from the policy, not from memory.
  • Every sign-in surface that accepts a password without MFA: legacy protocols, VPN, webmail, federation servers, Conditional Access exclusions.
  • The MFA method in use: push with number matching, plain approve and deny in a third-party MFA product, SMS, or phishing-resistant keys.
  • A user list supplied by the client, so we test only accounts in scope.
  • Whether the client's team expects to detect the test, and who to call if a real user is locked out.

Plan the spray against the lockout policy

We use one password per account per observation window, with a margin. If the threshold is ten failures in thirty minutes, one attempt per account per hour leaves room for the user's own typos. The candidate passwords follow the client's own rules: if the policy demands twelve characters with a capital and a digit, a season and year with a symbol is the likeliest thing a person picks.

For Microsoft Entra ID, smart lockout runs separately from on-premises Active Directory, so we read both.

Fix. Block predictable passwords outright with a banned password list that covers your company name, products and seasons; Entra Password Protection does this for cloud and on-premises accounts. Set the Active Directory lockout threshold higher than Entra smart lockout, so cloud sign-ins lock first and on-premises accounts are not locked out from the internet.

Run it slowly and log every attempt

nxc smb dc01.corp.example.com -u users.txt -p 'Autumn2026!' --continue-on-success \
  | tee "spray-$(date +%F-%H%M).log"

One password, every listed user, once. The next password waits for the next window. Against cloud sign-in we use the endpoint the client names, from a fixed address they know, at the same rhythm.

Fix. Block legacy authentication with Conditional Access, since those protocols cannot prompt for MFA. Require MFA on every sign-in surface, including VPN and federation servers, and review Conditional Access exclusions every quarter; exclusions are where spraying succeeds.

Detect password spraying in Entra ID and AD

union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(1d)
| where ResultType in ("50126", "50053")
| summarize Accounts = dcount(UserPrincipalName), Attempts = count()
    by IPAddress, bin(TimeGenerated, 1h)
| where Accounts > 20

Error 50126 is a wrong username or password and 50053 is a locked account. Failed sign-ins over legacy protocols and other non-interactive flows land in AADNonInteractiveUserSignInLogs, not SigninLogs, so a query over one table misses them. Many accounts failing from one address in an hour is the plain spray pattern. A spray spread across many source addresses slips under that grouping, so we pair it with a second query that ignores the source:

union SigninLogs, AADNonInteractiveUserSignInLogs
| where TimeGenerated > ago(1d) and ResultType == "50126"
| summarize Accounts = dcount(UserPrincipalName), Sources = dcount(IPAddress)
    by AppDisplayName, bin(TimeGenerated, 1h)
| where Accounts > 20

On-premises, an NTLM spray against a domain controller shows as event 4776 on the controller and 4625 on the host that received the logon; Kerberos pre-authentication failures show as 4771. Any of them across many accounts in a short window is the same shape.

Fix. Alert on the shape, not on single failures. One account failing ten times is a user; forty accounts failing once each from one address is an attack.

MFA fatigue (push bombing)

We do not flood real users with prompts. With a test account the client sets up, we check what an approval request shows: whether it demands number matching, and whether it names the application and location. We read the authentication methods policy for which methods are allowed and who can still use SMS or voice.

When the scope names real users for a fatigue test, we send one prompt per user at an agreed time, stop at the first approval, and use that session for nothing beyond proving it.

Fix. Microsoft Authenticator has enforced number matching and additional context since 2023; confirm both are on, and enforce the equivalent in any third-party MFA product, where plain approve and deny is still common. Turn on the setting that lets users report a prompt they did not start. Move administrators to phishing-resistant methods first, such as FIDO2 keys, passkeys or Windows Hello for Business, and use Conditional Access authentication strengths to require them. Remove SMS and voice where you can, and make the help desk verify identity through a separate channel before resetting anyone's MFA.

Detect fatigue

SigninLogs
| where ResultType == "500121"
| summarize Failures = count() by UserPrincipalName, bin(TimeGenerated, 15m)
| where Failures >= 3

Error 500121 is a failed or denied strong authentication. Several in fifteen minutes for one user, especially outside working hours, means someone holds that user's password.

Fix. Treat repeated MFA denials as a confirmed password compromise: reset the password and revoke sessions, rather than waiting for the user to report it.

What the defender sees

The spray appears in sign-in logs within minutes. Whether anyone looks at it is the real result of this test, and it belongs in the report whatever the answer is.