SigninLogs
| the place TimeGenerated > in the past(1h)
| the place ResultType in ("50034", "50126", "700016") or isempty(AppDisplayName)
| summarize
DistinctClientIDs = dcount(AppId),
ResultCodes = make_set(ResultType),
Usernames = make_set(UserPrincipalName)
by SourceIPAddress, UserAgent, bin(TimeGenerated, 15m)
| the place DistinctClientIDs > 5
| the place ResultCodes has "700016"
The 2 variables that matter most are DistinctClientIDs, as a result of a single supply biking by many unregistered app IDs is the inform that per-application thresholds miss, and the presence of AADSTS700016 in that very same window, which elevates the occasion from configuration noise to doable credential validation in progress. Layer in username-pattern detection, alphabetic or dictionary development throughout makes an attempt from the identical supply, to catch the OutFlareAZ-style wordlist sample particularly.
Tune the DistinctClientIDs threshold in opposition to your personal tenant’s baseline earlier than trusting it in manufacturing. A dev workforce operating CI in opposition to a handful of take a look at app registrations can produce a smaller model of the identical form, and Conditional Entry insurance policies scoped solely to named functions won’t catch a fabricated consumer ID that by no means matches an supposed utility scope within the first place.
Wire the rule into present SOAR or ticketing workflows relatively than a standalone dashboard no one checks on a Friday afternoon. A detection that fires into the identical queue as password-spray and impossible-travel alerts will get triaged with the identical urgency; one which lands in an remoted identity-hygiene report will get learn weeks later, if in any respect.


