Hunting Iran-Linked Password Spraying: Protecting M365 in Conflict Zones
Just caught the report from Check Point regarding an Iran-nexus actor conducting a massive password-spraying campaign against Israeli and UAE Microsoft 365 tenants. The activity spiked in three distinct waves on March 3, 13, and 23, 2026. Since it's a "low and slow" technique, it often flies under the radar of basic lockout policies, especially if they are using a distributed proxy network.
For those managing Entra ID (Azure AD) tenants in the region or with global footprints, now is the time to review your sign-in risk policies. Pure geo-blocking might be too disruptive, but combining sign-in risk with MFA challenges is essential.
You can hunt for evidence of this in your environment using KQL in Sentinel or Microsoft Defender. Look for patterns of failed logins across multiple accounts from the same IP or IP range, which indicates spraying rather than a brute-force single-account attack:
SigninLogs
| where ResultType == 50126 // Invalid username or password
| where ConditionalAccessStatus == "success" // Passed CA but failed password
| summarize FailedCount = count(), AccountSet = makeset(UserPrincipalName) by IPAddress, Location, bin(TimeGenerated, 1h)
| where FailedCount > 5 and array_length(AccountSet) > 3
| order by FailedCount desc
Additionally, ensure you have disabled legacy authentication protocols if you haven't already. Attackers love password spraying over Basic Auth because MFA challenges often don't apply the same way they do to Modern Auth.
Has anyone else noticed anomalies correlating with these dates, or are we seeing a shift toward more targeted credential harvesting?
We noticed a similar spike in our logs, though we aren't in the direct targeting zone. The attackers are definitely rotating user-agents to look like mobile browsers. One thing that helped us catch this was correlating AADNonInteractiveUserSignInLogs for IMAP/POP failures. If you haven't blocked legacy auth yet, this campaign is a stark reminder. We used this snippet to identify legacy auth usage before killing it:
Get-MsolUser -All | Where-Object {$_.LastDirSyncTime -eq $null -and $_.StrongAuthenticationRequirements.Count -eq 0} | Select UserPrincipalName
Good catch on the legacy auth. We went a step further and implemented 'Named Locations' in Conditional Access for our on-prem VPN ranges and forced MFA for any unmanaged device. It's noisy at first, but the drop in failed ResultType 50053 (account locked out) events was significant. Also, consider looking into ADFS logs if you have federated domains; the AuthFailed audits there can show the spraying attempts before they even hit the cloud.
From a pentester's perspective, password spraying is incredibly effective when orgs don't enforce Smart Lockout. The default Azure AD lockout settings are sometimes too lenient for a distributed attack. I recommend verifying your tenant's lockout duration and threshold via PowerShell:
Get-MsolDomainFederationSettings -DomainName yourdomain.com # Check if on-prem
Get-AzureADPolicy | Where-Object {$_.DisplayName -like '*Lockout*'} # Check policies
If the threshold allows 10+ failures before a 10-minute lockout, you're vulnerable to this specific wave of attacks.
Solid advice on the configuration side. Beyond policies, visibility is key. Since these campaigns often target specific apps to bypass MFA, tracking the aggregation of failures across users from a single source helps spot the 'low and slow' behavior before lockouts trigger.
Here’s a KQL query to detect distributed password spraying against M365:
kusto
SigninLogs
| where ResultType == "50126"
| summarize Count=count(), DistinctUsers=dcount(UserPrincipalName) by IPAddress, AppDisplayName
| where DistinctUsers > 5 and Count < 100
While blocking is critical, validating compromise is equally important. I recommend hunting for the "Success after Failure" pattern—where a successful login occurs shortly after multiple invalid attempts from a different IP. This often indicates a successful spray bypassing lockouts.
Here is a KQL query for Microsoft Sentinel to detect exactly that:
kusto
SigninLogs
| where ResultDescription in ("AADSTS50126", "AADSTS50128")
| summarize FailedCount=count() by UserPrincipalName, IPAddress
| join kind=inner (
SigninLogs
| where ResultType == 0
| project SuccessTime=TimeGenerated, UserPrincipalName, SuccessIP=IPAddress
) on UserPrincipalName
| where IPAddress != SuccessIP and FailedCount > 5
| project TimeGenerated, UserPrincipalName, IPAddress, SuccessIP, FailedCount
Solid points on visibility. To catch distributed campaigns slipping past lockout thresholds, consider aggregating failed logins by unique user count per IP. A single IP failing for 20+ different accounts is a red flag. If you’re using Sentinel, this query helps expose the spray pattern:
SigninLogs
| where ResultType == "50126"
| summarize DistinctUsers = dcount(UserPrincipalName) by IPAddress, Location
| where DistinctUsers > 15
| order by DistinctUsers desc
Verified Access Required
To maintain the integrity of our intelligence feeds, only verified partners and security professionals can post replies.
Request Access