Monitoring for lost identity with Azure Sentinel [part 1 of many]

Gianni Castaldi
Wortell
Published in
3 min readFeb 14, 2020

Since identity is the new perimeter it deserves some (a lot!) of extra attention.

Last week I've attended a partner training at Microsoft NYC and on one slide there was a KQL query regarding event 500121 and since not everyone speaks EventID it's: "Authentication failed during strong authentication request."

This event can have more than 15 different reasons but it all has the same outcome something went wrong with during or after the MFA authentication.

Where 2 of them have a high severity:

  • MFA denied; user declined the authentication
  • MFA denied; Phone App Reported Fraud

To monitor for these I've created a KQL query:

let Short = ago(1h);
SigninLogs
| where TimeGenerated > Short
| where ResultType == “500121”
| where Status.additionalDetails == “MFA denied; user declined the authentication” or Status.additionalDetails == “MFA denied; Phone App Reported Fraud”
| project TimeGenerated, UserPrincipalName, UserDisplayName, IPAddress, DeviceDetail.displayName

If you want to monitor all the MFA issues, without having an alert overload, you could filter the query to only show issues from IP addresses where the user never successfully logged on from.

You could build the following query for you analytics rule:

But if you use this, some results will display and UID as UserPrincipalNames

To prevent that you could use another step to get the UserPrincipalName from the CorrelationId

let Short = ago(1h);
let Long = ago(14d);
let UserLogs =
(SigninLogs
| where TimeGenerated > Short
| where ResultType == “500121”
| where Status.additionalDetails != “MFA denied; user declined the authentication” or Status.additionalDetails != “MFA denied; Phone App Reported Fraud”
| sort by TimeGenerated desc
| project TimeGenerated, IPAddress, DeviceDetail.displayName, CorrelationId
);
let CorrelationUser =
(SigninLogs
| where TimeGenerated > Short
| where ResultType == “500121”
| where UserPrincipalName contains “@”
| project CorrelationId, UserPrincipalName
);
let IPTrust =
(SigninLogs
| where TimeGenerated > Long
| project UserPrincipalName, IPAddress, ResultType
| extend Successful=iff(ResultType == “0” , 1 , 0)
| extend Counter=1
| summarize SuccessCount=sum(Successful), TotalRecords=sum(Counter) by UserPrincipalName, IPAddress
| extend SuccessfulLogonPercent=(SuccessCount*100)/TotalRecords
);
let CorrelatedLogs =
UserLogs | join CorrelationUser on CorrelationId;
CorrelatedLogs | join IPTrust on UserPrincipalName, IPAddress
| project-away *1
| where SuccessfulLogonPercent == “0”

Since this is a combination of an IP with no successful logons and a failed MFA, I would also classify this alert as a high severity.

As a last step you could also do shorten the KQL, but for demonstration purposes I've put them on seperate lines.

--

--