← Back to SOC feed Coverage →

Privileged role assigned to newly created account

kql MEDIUM Azure-Sentinel
T1098.003T1136.003
AuditLogs
backdoorhuntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-29T11:00:00Z · Confidence: medium

Hunt Hypothesis

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

Analytic Rule Definition

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:
      

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/AuditLogs/PrivilegedRoleAssignedToNewAccount.yaml