An adversary may register an MFA method from a suspicious IP address to bypass multi-factor authentication after compromising credentials. SOC teams should proactively hunt for this behavior in Azure Sentinel to detect potential credential compromise and unauthorized access attempts.
KQL Query
let timeframe = 1d;
let lookback = 30d;
// Build per-user baseline of IPs from successful sign-ins over the past 30 days
let BaselineIPs =
SigninLogs
| where TimeGenerated >= ago(timeframe + lookback) and TimeGenerated < ago(timeframe)
| where ResultType == 0
| where isnotempty(IPAddress)
| extend UserUpn = tolower(UserPrincipalName)
| summarize KnownIPs = make_set(IPAddress) by UserUpn;
// MFA registration events in the query window
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "User registered security info"
| where Result =~ "success"
| extend UserUpn = tolower(tostring(TargetResources[0].userPrincipalName))
| extend UserId = tostring(TargetResources[0].id)
| extend MethodType = tostring(TargetResources[0].displayName)
| extend RegIp = tostring(InitiatedBy.user.ipAddress)
| where isnotempty(RegIp) and isnotempty(UserUpn)
// Join with IP baseline; hint.strategy=broadcast pushes the smaller AuditLogs
// stream to all nodes holding BaselineIPs, avoiding an expensive shuffle join
// when the 30-day baseline is large relative to the 1-day event stream.
| join kind=leftouter hint.strategy=broadcast BaselineIPs on $left.UserUpn == $right.UserUpn
// Flag registrations from IPs not present in the baseline, or users with no baseline at all
| where isnull(KnownIPs) or not(set_has_element(KnownIPs, RegIp))
| extend AccountName = tostring(split(UserUpn, "@")[0])
| extend AccountUPNSuffix = tostring(split(UserUpn, "@")[1])
| project
TimeGenerated,
UserUpn,
AccountName,
AccountUPNSuffix,
UserId,
MethodType,
RegIp,
KnownIPCount = array_length(KnownIPs),
CorrelationId
| sort by TimeGenerated desc
id: 3d36b19f-cd62-4522-8869-23cdd9cc0c9f
name: MFA method registered from an IP address not seen in user sign-in history
description: |
Identifies MFA method registration events where the source IP address has not
appeared in the registering user's 30-day sign-in history. An attacker who obtains
credentials may register a new MFA method from an attacker-controlled IP to maintain
access after a password reset. Does not require Entra ID P2 licensing.
References:
- https://learn.microsoft.com/azure/active-directory/authentication/concept-mfa-howitworks
- https://learn.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities
- https://attack.mitre.org/techniques/T1556/006/
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
- SigninLogs
tactics:
- Persistence
- DefenseEvasion
relevantTechniques:
- T1556.006
query: |
let timeframe = 1d;
let lookback = 30d;
// Build per-user baseline of IPs from successful sign-ins over the past 30 days
let BaselineIPs =
SigninLogs
| where TimeGenerated >= ago(timeframe + lookback) and TimeGenerated < ago(timeframe)
| where ResultType == 0
| where isnotempty(IPAddress)
| extend UserUpn = tolower(UserPrincipalName)
| summarize KnownIPs = make_set(IPAddress) by UserUpn;
// MFA registration events in the query window
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "User registered security info"
| where Result =~ "success"
| extend UserUpn = tolower(tostring(TargetResources[0].userPrincipalName))
| extend UserId = tostring(TargetResources[0].id)
| extend MethodType = tostring(TargetResources[0].displayName)
| extend RegIp = tostring(InitiatedBy.user.ipAddress)
| where isnotempty(RegIp) and isnotempty(UserUpn)
// Join with IP baseline; hint.strategy=broadcast pushes the smaller AuditLogs
// stream to all nodes holding BaselineIPs, avoiding an expensive shuffle join
// when the 30-day baseline is large relative to the 1-day event stream.
| join kind=leftouter hint.strategy=broadcast BaselineIPs on $left.UserUpn == $right.UserUpn
// Flag registrations from IPs not present in the baseline, or users with no baseline at all
| where isnull(KnownIPs) or not(set_has_element(KnownIPs, RegIp))
| extend AccountName = tostring(split(UserUpn, "@")[0])
| extend AccountUPNSuffix = tostring(split(UserUpn, "@")[1])
| project
TimeGenerated,
UserUpn,
AccountName,
AccountUPNSuffix,
UserId,
MethodType,
RegIp,
KnownIPCount = array_length(KnownIPs),
CorrelationId
| sort by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserUpn
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
-
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: A system administrator registers an MFA method using their personal IP address after working remotely.
Filter/Exclusion: Exclude IP addresses associated with known administrator accounts or those used during remote work sessions (e.g., via Azure AD Conditional Access or Microsoft Intune device compliance reports).
Scenario: A scheduled job or automation tool (e.g., Azure DevOps pipeline, Jenkins, or Ansible) registers an MFA method on behalf of a user during a CI/CD process.
Filter/Exclusion: Exclude IP addresses used by known automation tools or services (e.g., 13.74.123.45 for Azure services, or IPs from AWS CodeBuild).
Scenario: A user signs in from a new location using a trusted device, and the MFA method is registered from a different IP address (e.g., using a mobile hotspot).
Filter/Exclusion: Exclude IP addresses that are geographically close to the user’s known locations or those used by trusted devices (e.g., via Azure AD sign-in logs with device names or trusted IPs).
Scenario: A user’s MFA method is registered from a corporate IP address that is not in their sign-in history due to a recent network change or migration (e.g., moving to a new data center).
Filter/Exclusion: Exclude IP ranges associated with the organization’s internal network or recent network changes (e.g., using IP range filters in SIEM or network access control lists).
Scenario: A user registers an MFA method from a public IP address that is part of a known corporate IP block used by a third-party service (e.g., a cloud provider or SaaS tool).
Filter/Exclusion: Exclude IP addresses from known corporate or partner services (e.g., using IP reputation lists like Cisco Talos or Microsoft’s IP list, or by checking against service-specific