A threat actor may be reactivating a dormant account by updating MFA settings to bypass detection and gain unauthorized access. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential account compromise and prevent lateral movement.
KQL Query
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = endtime - 14d;
let active_users = (
BehaviorAnalytics
| extend dormant = tostring(UsersInsights.IsDormantAccount)
| where isempty(dormant)
| summarize by UserPrincipalName);
AuditLogs
| where TimeGenerated between(starttime..endtime)
// Get users where they added MFA
| where OperationName =~ "User registered security info"
| extend TargetUser = tolower(tostring(TargetResources[0].userPrincipalName))
| extend UserId = tostring(TargetResources[0].id)
// Check and see if this activity was from a user who is considered not active
| where UserId !in (active_users)
// Further reduce FP by just looking at users who have successfully logged in recently as well (avoiding hits for users adding MFA but not actually logging in)
| join kind=inner (SigninLogs | where TimeGenerated > ago(1d) | where ResultType == 0 | summarize max(TimeGenerated), make_set(IPAddress), make_set(UserAgent), make_set(LocationDetails) by UserPrincipalName, UserId
) on UserId
| extend LogonLocation = set_LocationDetails[0], LogonUserAgent = set_UserAgent[0], LogonIP = set_IPAddress[0]
| project-rename MostRecentLogon = max_TimeGenerated
| project-reorder TimeGenerated, TargetUser, OperationName, ResultDescription, MostRecentLogon, LogonUserAgent, LogonLocation, LogonIP
| extend AccountCustomEntity = TargetUser, IPCustomEntity = LogonIP
id: 6adc74fb-37f9-4187-ba7c-84269b09a485
name: Dormant User Update MFA and Logs In - UEBA
description: |
'This query look for accounts that have not been successfully logged into recently who then add or update an MFA method before logging in.
Threat actors may look to re-activate dormant accounts and use them for access by adding MFA methods in the hope that changes to such dormant accounts may go un-noticed. This query uses the Microsoft Sentinel UEBA features.
Ref: [LINK TO BLOG]'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- AuditLogs
tactics:
- Persistence
relevantTechniques:
- T1098
query: |
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = endtime - 14d;
let active_users = (
BehaviorAnalytics
| extend dormant = tostring(UsersInsights.IsDormantAccount)
| where isempty(dormant)
| summarize by UserPrincipalName);
AuditLogs
| where TimeGenerated between(starttime..endtime)
// Get users where they added MFA
| where OperationName =~ "User registered security info"
| extend TargetUser = tolower(tostring(TargetResources[0].userPrincipalName))
| extend UserId = tostring(TargetResources[0].id)
// Check and see if this activity was from a user who is considered not active
| where UserId !in (active_users)
// Further reduce FP by just looking at users who have successfully logged in recently as well (avoiding hits for users adding MFA but not actually logging in)
| join kind=inner (SigninLogs | where TimeGenerated > ago(1d) | where ResultType == 0 | summarize max(TimeGenerated), make_set(IPAddress), make_set(UserAgent), make_set(LocationDetails) by UserPrincipalName, UserId
) on UserId
| extend LogonLocation = set_LocationDetails[0], LogonUserAgent = set_UserAgent[0], LogonIP = set_IPAddress[0]
| project-rename MostRecentLogon = max_TimeGenerated
| project-reorder TimeGenerated, TargetUser, OperationName, ResultDescription, MostRecentLogon, LogonUserAgent, LogonLocation, LogonIP
| extend AccountCustomEntity = TargetUser, IPCustomEntity = LogonIP
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
BehaviorAnalytics | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled System Maintenance Job Updates MFA
Description: A system maintenance job (e.g., using Ansible or Puppet) runs during off-hours and updates MFA settings for a dormant user as part of a configuration change.
Filter/Exclusion: Check for event_type = "configuration_change" or source = "ansible", and exclude users with last_login < 90 days.
Scenario: Admin Re-Enables User Account and Sets MFA
Description: An admin reactivates a dormant user account (via Azure AD or AWS IAM console) and enables MFA as part of a routine user management task.
Filter/Exclusion: Include user_role = "admin" or action = "user_reactivation", and exclude users with last_login < 90 days.
Scenario: Automated Backup Tool Updates MFA for Backup User
Description: A backup tool (e.g., Veeam or Commvault) runs a script that updates MFA for a backup user account during a scheduled backup process.
Filter/Exclusion: Check for tool = "veeam" or script_name = "backup_mfa_update.sh", and exclude users with last_login < 90 days.
Scenario: User Logs In After Being Locked Out by IT
Description: A user account was locked out due to a failed login attempt, and after IT unlocks it, the user updates MFA and logs in.
Filter/Exclusion: Include event_type = "account_unlock" or reason = "IT_lockout", and exclude users with last_login < 90 days.
Scenario: User Updates MFA via Self-Service Portal After Long Absence
Description: A user who hasn’t logged in for a long time (e.g.,