Detecting Abuse of SSPR and Voluntary Password Resets in Azure.

Detecting Abuse of SSPR and Voluntary Password Resets in Azure.

Summary:

In this article, we explore how sophisticated adversaries, such as well-funded or well-equipped Advanced Persistent Threat (APT) groups, abuse Azure's Self-Service Password Reset (SSPR) service in tandem with SIM swapping to compromise users in Azure cloud environments.

Additionally, we detail detection methods for identifying this abuse at each stage of the attack lifecycle using KQL queries and mitigation steps. We also explore another potential vector: leveraging leaked credentials in conjunction with SIM swapping to compromise user accounts and reset the user's password. Furthermore, we provide insight into how the password reset process operates and methods for identifying various password reset activities from AuditLogs.

Background:

Before we dive into the actual method of attack, let's first understand how password reset works in Azure. In Azure, a user's password can be reset using three methods. Even though we categorize them into three methods, the concept of SSPR and Voluntary Password Reset shares the same goal: allowing the user to reset their password themselves.


Admin Password Reset

The traditional method involves administrators resetting a user's password in scenarios where the password is forgotten, the user is locked out of a device, or the user never received a password initially. This is typically initiated either through a helpdesk ticket or by directly contacting an administrator for assistance.

Password Reset Option from Entra Id(Azure Portal)
Same option from Entra Admin Center(Image Credits: Microsoft)


Voluntary Password Reset:

In this method, a user can change their password on their own if they successfully log into the My accounts portal. Furthermore, they can change their password using the "Change password" feature from the portal, as shown.

My Account Portal's Password Change Option.

Also, users can update their password from the My Sign-ins Security Info Page. Based on your company’s authentication method policies, if you sign in with a password and multi-factor authentication to My Security Info, you will be able to update your password without entering your current password.

Security Info Portal Password Change

Note: This method only works when the user isn't facing account lockout issues or other sign-in problems. Hence, we can say it's more of a password update (change password) feature rather than a password reset feature.

Self-service password reset (SSPR):

This method surpasses the previous methods. Microsoft Entra self-service password reset (SSPR) gives users the ability to change or reset their password, with no administrator or help desk involvement. If a user's account is locked or they forget their password, they can follow prompts to unblock themselves and get back to work. This ability reduces help desk calls and loss of productivity when a user can't sign in to their device or an application. You can also access this page from the "Forgot password" option on any Azure application sign-in page, such as Azure Portal, Office 365, OneDrive, and Outlook login pages.

Here is a rough, high-level overview of how the SSPR (Self-Service Password Reset) password reset flow works:?

User Initiates Password Reset: The user clicks on the "Forgot Password" link on the login page and verifies his e-mail.


Identity Verification: Once the user's email is verified the system prompts the user to verify their identity using one or more of the configured auth methods (e.g., receiving a code via SMS or email or via Authenticator app notification etc).?


Reset Password: Once the user’s identity is verified, they are allowed to create a new password.?


Password Update: The new password is updated in the system, and the user can log in with the new credentials.?



Now, let's see how to identify each of the above mentioned methods in the AuditLogs table.

Detect reset methods from AuditLogs

Password Reset by admin:

Firstly, an admin password reset is uniquely identified by the "Reset password (by admin)" operation in the logs. Here's the KQL query to find password reset operations performed by admins on behalf of a user:

AuditLogs
| where TimeGenerated >ago(1d)
//| where LoggedByService=~"Self-service Password Management"
| where OperationName == "Reset password (by admin)"
| where Result == "success"
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=InitiatedBy.user.userPrincipalName
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, OperationName, Initiatedby,TargetUser,IpAddress, AdditionalDetails,Result, CorrelationId        

Voluntary, or forced (due to expiry) password change.

Our next method is the voluntary method, which generates the "Change password (self-service)" operation in the logs. Interestingly, this operation name is also generated for other scenarios, such as...

  • When an admin resets a user's password, requiring the user to reset it again (often done when a user first joins the organization or if their password is reset for security reasons)
  • Password change during expired password time

AuditLogs
| where TimeGenerated >ago(1d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Change password (self-service)"
| where Result == "success"
| extend TargetUser=TargetResources[0].userPrincipalName
| extend Actor=InitiatedBy.user.userPrincipalName
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, OperationName,ActivityDisplayName, Actor,IpAddress, TargetUser, LoggedByService, Result, ResultDescription, CorrelationId, AdditionalDetails        

Self Service Password Reset(SSPR)

Finally, our SSPR password reset, which we focus on more in this article, is uniquely identified by the "Reset password (self-service)" operation in the logs.

AuditLogs
| where TimeGenerated >ago(90d)
//| where LoggedByService=="Self-service Password Management"
| where OperationName contains "Reset password (self-service)"
| where ResultDescription=="Successfully completed reset."
| where Result=="success"
| extend User=InitiatedBy.user.userPrincipalName

                        (Or)

AuditLogs
| where TimeGenerated >ago(90d)
//| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| where ResultDescription=="User successfully reset password"
| where Result=="success"
| extend User=InitiatedBy.user.userPrincipalName        

Alright, enough of the basics. Let's dive into the actual attack vector of abusing the SSPR password reset flow along with SIM swapping.

Compromising User Accounts: Abusing SSPR Flow with SIM Swapping

This technique primarily abuses the SSPR flow method, which was discovered by the Obsidian threat research team as part of their SaaS Investigations.

The attack sequence typically unfolds as follows:?

Reconnaissance:?

  • Firstly, attackers harvest email accounts of the target organization from LinkedIn, surface web, social media, marketing tools, and other sources.
  • Attackers then verify whether the emails are valid. If they are, they will check whether each user's password reset flow follows only one verification method, such as mobile phone or SMS, or if it requires two verification methods (Authentication notifications, appcode etc.)
  • Attackers choose the targets where SSPR flow requires only one verification method which used be mobile based verification.

SIM Swapping:

  • If the SSPR flow requires verifying only one method (mainly a mobile phone call or SMS), then they choose SIM swapping to get control victim phone number.
  • SIM swapping is a method of utilizing social engineering tactics, attackers deceive mobile carriers into transferring the victim's phone number to a SIM card they control.?

  • This allows them to intercept SMS-based authentication codes sent to the victim’s number via the attacker's SIM card, as the victim's phone number has already been transferred to the attacker's SIM card by the mobile carrier..?

Account Compromise

  • Once attackers have control over the victim’s phone number, they start initiating the SSPR flow for the victim user, which requires mobile SMS or call-based verification. The attacker completes this verification since they have control of the victim's mobile number, and then they proceed to reset the victim's account password.?

MFA Manipulation

  • Change MFA: Attackers update the MFA settings to methods they control, such as adding their own phone number or email address as an authentication factor, thereby ensuring continued access.?

  • Delete MFA: By deleting the existing Multi-Factor Authentication (MFA) methods associated with the account, the attackers make it more difficult for the victim to regain access to their account. Even if the victim manages to recover their mobile number from the mobile carrier, they won't be able to use the previous MFA methods (like SMS codes or authentication apps) to verify their identity and reset the password, as those methods have been removed by the attacker.

Detection

Now, lets see how to detect this attack method in each phase of the above-mentioned attack lifecycle.

Reconnaissance:

Firstly, during the reconnaissance phase, attackers will attempt to verify how many verification methods the SSPR flow requires to perform the password reset of a user account. To do this, attackers may stop at the verification stage(Identity verification stage mentioned in previous sspr flow) without completing verification steps and the entire SSPR flow. This page clearly indicates whether one or two verification methods are required, making it easier for attackers to identify and select vulnerable target accounts.

Attackers stop at this page. The above page shows two verification steps are required.


Here's a KQL query to detect SSPR reconnaissance activities where attackers stop at the verification options page without completing the entire process:

AuditLogs
| where TimeGenerated >ago(1d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| where ResultDescription=="User was presented with verification options"
//where ResultDescription=="User cancelled before passing the required authentication methods" //optional also attacker can close browser tab instead of cancelling it. So not accurate but a worthwhile option.
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
| join kind=leftanti (
AuditLogs
| where TimeGenerated >ago(1d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| where ResultDescription has_any ("User started the","verification option")) on CorrelationId
| project TimeGenerated, OperationName, Initiatedby,TargetUser,IpAddress, AdditionalDetails,Result, CorrelationId        

A sudden increase in such activities, targeting multiple accounts within the organization, is a significant detection indicator of reconnaisance.

let threshold=3;
let reconSSPR=AuditLogs
| where TimeGenerated >ago(1d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| where ResultDescription=="User was presented with verification options"
//where ResultDescription=="User cancelled before passing the required authentication methods" //optional also attacker can close browser tab instead of cancelling it. So not accurate but a worthwhile option.
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
| join kind=leftanti (
AuditLogs
| where TimeGenerated >ago(1d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| where ResultDescription has_any ("User started the","verification option")) on CorrelationId
| project TimeGenerated, OperationName, Initiatedby,TargetUser,IpAddress, AdditionalDetails,Result, CorrelationId;
reconSSPR
| summarize Count=make_set(TargetUser)
| where array_length(Count)>threshold        

KQL query to detect incomplete SSPR flow initiations from TOR IPs:

let TorExitNodes=externaldata(ipAddress:string)[
"https://check.torproject.org/torbulkexitlist"];
AuditLogs
| where TimeGenerated >ago(1d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
//| project TimeGenerated, OperationName, Initiatedby,TargetUser,IpAddress, AdditionalDetails,Result, CorrelationId, ResultDescription
| summarize Result=make_list(ResultDescription),InitiatedUsers=make_list(Initiatedby) by TargetUser, IpAddress, CorrelationId
| where Result !has "User successfully reset password"
| where IpAddress in (TorExitNodes)         

Compromise:

Query for detecting SSPR flows completed via SMS or phone call options from rare IP.

If a SSPR flow is completed via SMS or phone call options from a rare and suspicious IP, it may indicate a potential SIM Swap attack that was then used to perform SSPR.

Query for detecting SSPR flows completed via SMS or phone call options from rare IP.

AuditLogs
| where TimeGenerated >ago(90d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IPAddress=tostring(InitiatedBy.user.ipAddress)
//Only Verifications should present 
// User started the mobile SMS verification option
// User completed the mobile SMS verification option
// User completed the mobile voice call verification option
// User started the mobile voice call verification option
| summarize StartTime=min(TimeGenerated),EndTime=max(TimeGenerated),SSPRFlowEvents=make_set(ResultReason),count() by CorrelationId, TargetUser,IPAddress  
| where SSPRFlowEvents has_any ("User successfully reset password","Successfully completed reset") //Successfull password reset.
//Security Questions
// User started the security questions verification option
// User completed the security questions verification option
//Email-Verification
// User started the email verification option
// User completed the email verification option
//Authenticator App
// User started the mobile app notification verification option
// User completed the mobile app notification verification option
// User started the mobile app code verification option
// User started the mobile app notification verification option
| where not(SSPRFlowEvents has_any ("mobile app notification","mobile app code verification","email verification option","security questions verification option"))
| join kind=leftanti (
SigninLogs
| where TimeGenerated > ago(90d)
| where ResultType == 0 ) 
on IPAddress        

Query for detecting SSPR flows completed via SMS or phone call options from TOR IP.

let TorExitNodes=externaldata(ipAddress:string)[
"https://check.torproject.org/torbulkexitlist"];
AuditLogs
| where TimeGenerated >ago(90d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Self-service password reset flow activity progress"
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IPAddress=tostring(InitiatedBy.user.ipAddress)
| where IPAddress in (TorExitNodes)
//Only Verifications should present 
// User started the mobile SMS verification option
// User completed the mobile SMS verification option
// User completed the mobile voice call verification option
// User started the mobile voice call verification option
| summarize StartTime=min(TimeGenerated),EndTime=max(TimeGenerated),SSPRFlowEvents=make_set(ResultReason),count() by CorrelationId, TargetUser,IPAddress  
| where SSPRFlowEvents has_any ("User successfully reset password","Successfully completed reset") //Successfull password reset.
//Security Questions
// User started the security questions verification option
// User completed the security questions verification option
//Email-Verification
// User started the email verification option
// User completed the email verification option
//Authenticator App
// User started the mobile app notification verification option
// User completed the mobile app notification verification option
// User started the mobile app code verification option
// User started the mobile app notification verification option
| where not(SSPRFlowEvents has_any ("mobile app notification","mobile app code verification","email verification option","security questions verification option"))        

Multiple user password resets via SSPR method performed from a single IP address.

The query below identifies multiple user password resets via the Self-Service Password Reset (SSPR) method performed from a single IP address.

Expected FPs:

  • Users are within the same subnet, such as within a company network sharing a common public IP.
  • A DevOps or developer may be changing passwords for both test accounts and their own account simultaneously.

let threshold=2;
AuditLogs
| where TimeGenerated >ago(90d)
| where LoggedByService=="Self-service Password Management"
| where OperationName=="Reset password (self-service)"
| where ResultDescription=="Successfully completed reset."
| where Result=="success"
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| extend Actor=tostring(InitiatedBy.user.userPrincipalName)
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
| extend User=InitiatedBy.user.userPrincipalName
| summarize min(TimeGenerated),max(TimeGenerated),UserList=make_set(TargetUser), count() by IpAddress
| where array_length(UserList)>threshold
        


Post-Compromise - Persistence:

As mentioned above attackers update or create new MFA methods of thier own for persistence.

KQL query for detecting MFA registration or MFA update followed by SSPR password reset.

AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName =~"Reset password (self-service)"
| where ResultDescription=="Successfully completed reset."
| where Result=="success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ResetTime=TimeGenerated, InitiatedUser, TargetUser, OperationName, Result
| join kind=inner (
AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName in~ ("User registered all required security info","User registered security info","Admin registered security info","User changed default security info","Admin updated security info","User updated security info")
//| where Result=="success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ["MFARegistration/Update Time"]=TimeGenerated, InitiatedUser, TargetUser, Result, OperationName) on TargetUser
| where ['MFARegistration/Update Time']>ResetTime
| extend ['Reset to MFA change TimeGap']=datetime_diff('minute',["MFARegistration/Update Time"],ResetTime)        


Post-Compromise - Removing MFA methods:

As mentioned earlier, attackers may delete existing MFA methods to inhibit legitimate user access.

MITRE Technique: T1531- Account Access Removal

KQL query for detecting MFA method deletion followed by password reset.

AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName =~"Reset password (self-service)"
| where ResultDescription=="Successfully completed reset."
| where Result=="success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ResetTime=TimeGenerated, InitiatedUser, TargetUser, OperationName, Result
| join kind=inner (
AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName in~ ("Admin deleted security info","User deleted security info")
//| where Result=="success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ['MFA Method Deletion Time']=TimeGenerated, InitiatedUser, TargetUser, Result, OperationName) on TargetUser
| where ['MFA Method Deletion Time']>ResetTime
| extend ['Reset to MFA deletion TimeGap']=datetime_diff('minute',['MFA Method Deletion Time'],ResetTime)        

If the attacker compromises an administrator account with permissions to delete, update, or add other users' MFA methods, they can make changes to MFA settings for multiple users. The below query finds the same.

let threshold=2;
AuditLogs
| where TimeGenerated >ago(90d)
| where OperationName has_any ("Admin deleted security info","Admin updated security info","Admin registered security info")
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IPAddress=tostring(InitiatedBy.user.ipAddress)
| where Result=="success"
| project TimeGenerated, OperationName, Result, ResultReason,Initiatedby, TargetUser, IPAddress
| summarize min(TimeGenerated), ResultReason=make_list(ResultReason), TargetUser=make_set(TargetUser), IpAddress=make_list(IPAddress) by Initiatedby, OperationName
| where array_length(TargetUser)>threshold        

If the attacker compromises an administrator account with permissions to delete, update, or add other users' MFA methods, they can perform password resets for multiple users.

KQL query to find password reset operations performed by admins on behalf of multiple users in Entra Id

let threshold=2;
AuditLogs
| where TimeGenerated >ago(1d)
//| where LoggedByService=~"Self-service Password Management"
| where OperationName == "Reset password (by admin)"
| where Result == "success"
| extend TargetUser = tostring(TargetResources[0].userPrincipalName)
| extend Initiatedby=tostring(InitiatedBy.user.userPrincipalName)
| extend IpAddress=tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, OperationName, Initiatedby,TargetUser,IpAddress, AdditionalDetails,Result, CorrelationId
| summarize TargetUsers=make_set(TargetUser),count() by Initiatedby
| where array_length(TargetUsers)>threshold        

Abusing voluntary password reset(update) method via SIM swapping and compromised credentials.

This is a new attack vector we discovered, where the following prerequisites should be met for the attack to succeed:

  • Leaked Credentials of User Account: The attacker must obtain the credentials (username and password) of the target user account through data breaches, phishing, or other means.
  • Mobile SMS or Call Verification MFA Option: The target user must have configured their MFA to use mobile SMS or call verification as one of the authentication methods.
  • Compromise of Victim's Phone Number via SIM Swapping: The attacker must gain control over the victim's phone number by performing a SIM swap, typically through social engineering tactics to deceive mobile carriers.

If the above prerequisites are met, an attacker can sign in to the user account without needing to reset the password, using the leaked credentials and the SMS-based MFA method.

Step1: Attacker uses compromised credentials for login.


Attackers trying to login using stolen/leaked credentials

Step2: MFA method completion

If the user's default MFA method is mobile SMS, the attacker will be prompted to enter an SMS code during authentication.


MFA verification: mobile SMS

If the default method is not Mobile SMS as shown attacker can select I can't use Microsoft Authenticator App right now. This action will trigger an alert to the client, possibly through an Authenticator app notification.

MFA prompting for authenticator app notification.

And further he select the mobile SMS method and completes MFA.

Image showing multiple MFA methods to select from.


Step3: Password Reset

Once the attacker logged into the account he can simple change password of the user via My Sign-ins Security Info Page method where he don't even need to provide the old password. Also he can start adding new MFA methods as like previous attack vector.

Mysignins portal-password change option.

Detection Ideas:

  • Monitor for any sign-ins from unknown locations or suspicious IP's.
  • Monitor for conditional access failures from suspicious IP's (which indicates successful password leaks)
  • Monitor for password reset from unknown IP's.

We are providing some key KQL queries for detection here:

MFA registration followed by voluntary password reset.

AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName=="Change password (self-service)"
| where Result == "success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ResetTime=TimeGenerated, InitiatedUser, TargetUser, OperationName, Result
| join kind=inner (
AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName in~ ("User registered all required security info","User registered security info","Admin registered security info","User changed default security info","Admin updated security info","User updated security info")
//| where Result=="success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ["MFARegistration/Update Time"]=TimeGenerated, InitiatedUser, TargetUser, Result, OperationName) on TargetUser
| where ['MFARegistration/Update Time']>ResetTime
| extend ['Reset to MFA change TimeGap']=datetime_diff('minute',["MFARegistration/Update Time"],ResetTime)        

MFA method deletion followed by voluntary password reset.

AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName=="Change password (self-service)"
| where Result == "success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ResetTime=TimeGenerated, InitiatedUser, TargetUser, OperationName, Result
| join kind=inner (
AuditLogs
| where TimeGenerated >ago(1d)
| where OperationName in~ ("Admin deleted security info","User deleted security info")
//| where Result=="success"
| extend InitiatedUser=tostring(InitiatedBy.user.userPrincipalName)
| extend TargetUser=tostring(TargetResources[0].userPrincipalName)
| project ['MFA Method Deletion Time']=TimeGenerated, InitiatedUser, TargetUser, Result, OperationName) on TargetUser
| where ['MFA Method Deletion Time']>ResetTime
| extend ['Reset to MFA deletion TimeGap']=datetime_diff('minute',['MFA Method Deletion Time'],ResetTime)        


Collecting SSPR Settings Data via Compromised User

MITRE Technique: T1087-Account Discovery

If an attacker compromises a user who has "Reports.Read.All" delegated permission, they can retrieve details about SSPR settings for users.

Minimum permissions/roles required for collecting SSPR settings data.


The following powershell commands can be used to retrieve the details of sspr

First connect to microsoft graph powershell and authenticate with the scope "Reports.Read.All"

Install-Module Microsoft.Graph.Beta
Connect-MgGraph -Scopes "Reports.Read.All"         

Filter for users who have registered for self-service password reset (SSPR).

Get-MgBetaReportCredentialUserRegistrationDetail  -All | Where-Object {$_.IsRegistered -eq $true}        

Filter for users who have been enabled for SSPR.

Get-MgBetaReportCredentialUserRegistrationDetail  -All| Where-Object {$_.IsEnabled -eq $true}        

Filter for users who are ready to perform password reset or multi-factor authentication (MFA).

Get-MgBetaReportCredentialUserRegistrationDetail | Where-Object {$_.isCapable -eq $true}        

Filter for users who are registered for MFA.

Get-MgBetaReportCredentialUserRegistrationDetail -All | Where-Object {$_.isMfaRegistered -eq $true}        

Filter users who has only mobile based security verifications(auth method):

Get-MgBetaReportCredentialUserRegistrationDetail | Where-Object {$_.AuthMethods.Count -ne 0 -and $_.AuthMethods -notcontains "appNotification" -and $_.AuthMethods -notcontains  "appCode" -and $_.AuthMethods -notcontains "securityQuestion"} | Where-Object {  $_.AuthMethods -contains "mobileSMS" -or $_.AuthMethods -contains "officePhone" -or $_.AuthMethods -contains "mobilePhone" -or $_.AuthMethods -contains "mobileSMS"}        

Filter users without having authenticator app verification auth method:

Get-MgBetaReportCredentialUserRegistrationDetail | Where-Object {$_.AuthMethods.Count -ne 0 -and $_.AuthMethods -notcontains "appNotification" -and $_.AuthMethods -notcontains  "appCode"}        
The fields and their meaning from microsoft docs.


Detection:

The above methods generate microsoft graph logs with api endpoint "reports/credentialUserRegistrationDetails". The same you can detect via below query.

MicrosoftGraphActivityLogs
| where TimeGenerated >ago(50m)
| where RequestUri has_all ("reports/credentialUserRegistrationDetails","graph.microsoft.com")
| where RequestMethod=="GET"
| where ResponseStatusCode==200        

Mitigations:

Here are three important mitigation steps we discussed. For the remaining steps, you can refer to the Obsidian Threat Research article.

  • Block MFA registrations from untrusted locations or IPs via conditional access policies.

You can further restrict based on client device whether its compliant or not.

CA policy that blocks any MFA registrations from unknown locations.


  • Change your SSPR settings from requiring 1 verification method to requiring 2 verification methods in order to perform the reset.

SSPR settings require 2 verification methods for password reset.

  • Do not share your personal and sensitive information on social media


Author: Ashok Krishna Vemuri


vignesh Chandrasekaran

information Security enthusiast with expertise for various areas of infosec

8 个月

Awesome article ,great insights

回复

要查看或添加评论,请登录

Kloudynet Technologies的更多文章

社区洞察

其他会员也浏览了