A newly created account being assigned a privileged directory role within 24 hours may indicate an adversary establishing a backdoor for persistent access. SOC teams should proactively hunt for this behavior in Azure Sentinel to detect potential compromise of privileged accounts and mitigate lateral movement risks.
KQL Query
let timeframe = 7d;
let creationWindow = 24h;
let PrivilegedRoles = dynamic([
"Global Administrator",
"Privileged Role Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Exchange Administrator",
"SharePoint Administrator",
"User Account Administrator",
"Authentication Administrator",
"Privileged Authentication Administrator",
"Security Administrator",
"Hybrid Identity Administrator"
]);
let NewAccounts =
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Add user"
| where Result =~ "success"
| project
AccountCreatedTime = TimeGenerated,
UserId = tostring(TargetResources[0].id),
NewUserUpn = tostring(TargetResources[0].userPrincipalName);
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where Category =~ "RoleManagement"
| where OperationName in~ ("Add member to role.", "Add member to role")
| where Result =~ "success"
| extend TargetUserId = tostring(TargetResources[0].id)
| extend TargetUpn = tostring(TargetResources[0].userPrincipalName)
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) =~ "Role.DisplayName"
| extend RoleName = tostring(parse_json(tostring(ModProp.newValue))[0])
| where RoleName in~ (PrivilegedRoles)
| join kind=inner NewAccounts on $left.TargetUserId == $right.UserId
| where TimeGenerated between (AccountCreatedTime .. (AccountCreatedTime + creationWindow))
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| extend AccountName = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[0]), TargetUpn)
| extend AccountUPNSuffix = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[1]), "")
| project
TimeGenerated,
RoleName,
TargetUpn,
AccountName,
AccountUPNSuffix,
AccountCreatedTime,
Actor,
ActorIp,
CorrelationId
| sort by TimeGenerated desc
id: 1bb5e930-18be-454d-b0d9-351eef15871a
name: Privileged role assigned to newly created account
description: |
Identifies directory role assignments to accounts created less than 24 hours earlier
in the same tenant. An account receiving a privileged role shortly after creation
may indicate a backdoor account staged by an attacker with existing admin access.
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098.003
- T1136.003
query: |
let timeframe = 7d;
let creationWindow = 24h;
let PrivilegedRoles = dynamic([
"Global Administrator",
"Privileged Role Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Exchange Administrator",
"SharePoint Administrator",
"User Account Administrator",
"Authentication Administrator",
"Privileged Authentication Administrator",
"Security Administrator",
"Hybrid Identity Administrator"
]);
let NewAccounts =
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Add user"
| where Result =~ "success"
| project
AccountCreatedTime = TimeGenerated,
UserId = tostring(TargetResources[0].id),
NewUserUpn = tostring(TargetResources[0].userPrincipalName);
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where Category =~ "RoleManagement"
| where OperationName in~ ("Add member to role.", "Add member to role")
| where Result =~ "success"
| extend TargetUserId = tostring(TargetResources[0].id)
| extend TargetUpn = tostring(TargetResources[0].userPrincipalName)
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) =~ "Role.DisplayName"
| extend RoleName = tostring(parse_json(tostring(ModProp.newValue))[0])
| where RoleName in~ (PrivilegedRoles)
| join kind=inner NewAccounts on $left.TargetUserId == $right.UserId
| where TimeGenerated between (AccountCreatedTime .. (AccountCreatedTime + creationWindow))
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| extend AccountName = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[0]), TargetUpn)
| extend AccountUPNSuffix = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[1]), "")
| project
TimeGenerated,
RoleName,
TargetUpn,
AccountName,
AccountUPNSuffix,
AccountCreatedTime,
Actor,
ActorIp,
CorrelationId
| sort by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: A system administrator creates a new service account and immediately assigns it a privileged role to configure a new application.
Filter/Exclusion: Exclude accounts created by known admin users (e.g., [email protected]) or those created via the Azure AD PowerShell module (Microsoft.Azure.Commands.ActiveDirectory).
Scenario: A scheduled job runs to automate user provisioning and assigns a privileged role to a newly created account as part of an onboarding process.
Filter/Exclusion: Exclude accounts created by automated processes (e.g., Azure Automation or PowerShell DSC) or those with a specific tag like provisioning-job.
Scenario: A user account is created for a temporary task and is assigned a privileged role to access internal tools during a short-term project.
Filter/Exclusion: Exclude accounts with a short expiration date or those created via the Azure AD portal with a specific note in the description field (e.g., Temp Access - Project XYZ).
Scenario: A new account is created for a third-party service and is assigned a privileged role to access internal APIs or systems as part of integration.
Filter/Exclusion: Exclude accounts associated with known third-party services (e.g., [email protected]) or those created via the Azure AD Graph API.
Scenario: A user is created during a bulk import of users and is assigned a privileged role due to a misconfigured script or automation.
Filter/Exclusion: Exclude accounts created during bulk operations (e.g., via Azure AD Connect, PowerShell, or Microsoft Graph API) and filter by creation date range or specific import source.