As we all know, getting our organization onto Multi-factor authentication (MFA) is just the first step. The real challenge lies in moving to more secure second factors that can withstand increasingly sophisticated emerging threats. Unfortunately, industry support for moving to more advanced MFA methods is still not where it needs to be. But as security-minded individuals, we want to stay ahead of the curve and protect our users and environments from potential attacks.

One of the most significant vulnerabilities in MFA today is SMS-based attacks, including SIM swapping and SMS phishing (smishing). These tactics are becoming increasingly easy for threat actors to execute, and it’s essential that we eliminate insecure authentication methods where possible. To do so, we need to identify users who are still using them.

Identifying Insecure MFA Methods using KQL#

If you use Microsoft Azure Sentinel or a log analytics workspace, you can leverage KQL (Kusto Query Language) and the built-in Azure/Entra SignInLogs table to find out which users are still on “legacy” multifactor options. Here’s how:

KQL Query#

SigninLogs
| where TimeGenerated > ago(30d)
| extend AuthenticationMethod = tostring(parse_json(AuthenticationDetails)[0].authenticationMethod)
| extend AuthenticationMethodDetail = tostring(parse_json(AuthenticationDetails)[0].authenticationMethodDetail)
| extend MfaMethod = tostring(parse_json(AuthenticationDetails)[1].authenticationMethod)
| where parse_json(AuthenticationDetails)[0].succeeded == true and parse_json(AuthenticationDetails)[1].succeeded == true
| where AuthenticationMethod != "Previously satisfied"
| where MfaMethod != "Previously satisfied"
| where MfaMethod != "Mobile app notification"
| distinct UserPrincipalName, AuthenticationMethod, AuthenticationMethodDetail, MfaMethod

This query will help you identify users who are still using insecure MFA methods, such as SMS-based authentication (text messages, voice calls) and OATH verification codes. You can adjust the timeframe by modifying the TimeGenerated value in the query.

This query is quite complex, so let’s break it down step by step:

Step 1: Filter the data to only include logs from the last 30 days

SigninLogs | where TimeGenerated > ago(30d)

This line filters the SigninLogs table to only include records with a timestamp greater than 30 days ago. This ensures that we’re only looking at recent activity.

Step 2: Extract and parse the authentication details from the log data

| extend AuthenticationMethod = tostring(parse_json(AuthenticationDetails)[0].authenticationMethod)
| extend AuthenticationMethodDetail = tostring(parse_json(AuthenticationDetails)[0].authenticationMethodDetail)
| extend MfaMethod = tostring(parse_json(AuthenticationDetails)[1].authenticationMethod)

Here, we’re using the extend function to create new fields based on the data in the AuthenticationDetails column. This column contains a JSON object with various attributes related to the sign-in event.

  • The first line extracts the authenticationMethod value from the first element of the AuthenticationDetails array.
  • The second line attempts to extract any available information from the authenticationMethodDetail array.
  • The third line extracts the mfaMethod value from the second element of the AuthenticationDetails array.

Step 3: Filter the data to only include successful sign-in events

| where parse_json(AuthenticationDetails)[0].succeeded == true and parse_json(AuthenticationDetails)[1].succeeded == true

This line filters the data to only include records where both authentication methods were successfully used.

Step 4: Filter out “Previously satisfied” authentication method

| where AuthenticationMethod != "Previously satisfied"

Here, we’re filtering out records with an AuthenticationMethod value of "Previously satisfied".

Step 5: Filter out “Mobile app notification” MFA method

| where MfaMethod != "Mobile app notification"

This line filters out records with an MfaMethod value of "Mobile app notification".

Step 6: Show the deduplicated results

| distinct UserPrincipalName, AuthenticationMethod, AuthenticationMethodDetail, MfaMethod

Finally, we’re grouping the results by the specified columns to eliminate duplicates.

Next Steps: Creating a Phishing Resistant Conditional Access Policy

Now that you’ve identified users who are still using insecure MFA methods, it’s time to take action and move them to better methods. To further harden the environment against authentication-based attacks, I recommend creating a phishing resistant conditional access policy.

The official guidance for creating such a policy can be found on Microsoft’s Entra documentation: <https://learn.microsoft.com/en-us/entra/identity/conditional-access/how-to-policy-phish-resistant-admin-mfa>. This policy will ensure that even administrative accounts are protected from phishing attacks, using advanced MFA methods like FIDO2 security keys or authenticator apps.

By implementing this policy, you’ll be taking a significant step towards securing your organization’s MFA and protecting it against the most sophisticated threats.

Sources: Microsoft Learn - Conditional Access authentication strength Microsoft Learn - Common Conditional Access policy: Require phishing-resistant multifactor authentication for administrators Nathan McNulty - Twitter