Query Details

Match-Anchor Modification on Privileged Cloud User

13 AAD Prov Match Anchor Privileged Takeover

Query

// High-fidelity anchors only. proxyAddresses / otherMails removed:
// they churn constantly through Exchange mail-flow and shared-mailbox
// lifecycle and generate FPs on any cloud admin who also has a mailbox.
let AnchorAttrs = dynamic([
    "userPrincipalName","UserPrincipalName",
    "onPremisesImmutableId","ImmutableId","sourceAnchor",
    "mailNickname","MailNickname"
]);
let PrivilegedUsers =
    IdentityInfo
    | where TimeGenerated > ago(14d)
    | summarize arg_max(TimeGenerated, *) by AccountUPN
    | where isnotempty(AssignedRoles) and AssignedRoles != "[]"
    | project AccountUPN, AccountObjectId, AssignedRoles;
let FromProvisioning =
    AADProvisioningLogs
    | where TimeGenerated > ago(1h)
    | where ResultType =~ "Success"
    | extend TargetUpn = tostring(parse_json(TargetIdentity).userPrincipalName),
             SPName    = tostring(parse_json(ServicePrincipal).Name)
    | mv-expand Mod = todynamic(ModifiedProperties)
    | extend PropName = tostring(Mod.displayName),
             OldValue = tostring(Mod.oldValue),
             NewValue = tostring(Mod.newValue)
    | where PropName in~ (AnchorAttrs)
    | project TimeGenerated, Channel = "Provisioning", Actor = SPName,
              TargetUpn, PropName, OldValue, NewValue, SourceIP = "";
let FromAudit =
    AuditLogs
    | where TimeGenerated > ago(1h)
    | where OperationName has_any ("Update user","Change user UPN","Set user properties","Update User")
    | mv-expand TargetResources
    | extend TargetUpn  = tostring(TargetResources.userPrincipalName)
    | extend Actor      = coalesce(tostring(InitiatedBy.user.userPrincipalName),
                                    tostring(InitiatedBy.app.displayName))
    | extend SourceIP   = tostring(InitiatedBy.user.ipAddress)
    | mv-expand Mod = todynamic(TargetResources.modifiedProperties)
    | extend PropName = tostring(Mod.displayName),
             OldValue = tostring(Mod.oldValue),
             NewValue = tostring(Mod.newValue)
    | where PropName in~ (AnchorAttrs)
    | project TimeGenerated, Channel = "AuditLogs", Actor,
              TargetUpn, PropName, OldValue, NewValue, SourceIP;
union FromProvisioning, FromAudit
| join kind=inner (PrivilegedUsers) on $left.TargetUpn == $right.AccountUPN
| project TimeGenerated, Channel, Actor, SourceIP, TargetUpn, AssignedRoles,
          PropName, OldValue, NewValue
| order by TimeGenerated desc

Explanation

This query is designed to detect suspicious modifications to certain key attributes (known as "matching anchors") of privileged cloud user accounts in Microsoft Entra ID (formerly Azure Active Directory). These attributes include userPrincipalName, mail, proxyAddresses, onPremisesImmutableId, and mailNickname. Changes to these attributes can enable an attacker to take over a privileged cloud account by binding it to a matching on-premises object during synchronization.

Here's a simplified breakdown of the query:

  1. Purpose: The query aims to identify changes to specific attributes of privileged users in the cloud directory, which could indicate an account takeover attempt.

  2. Data Sources: It uses two main data sources:

    • AADProvisioningLogs: Logs related to provisioning activities.
    • AuditLogs: Logs related to direct modifications via Graph API.
  3. Detection Logic:

    • It first identifies privileged users by checking for assigned directory roles.
    • It then looks for successful modifications to the specified attributes within the last hour from both provisioning and audit logs.
    • The query filters out frequent changes to certain attributes like proxyAddresses to reduce false positives.
  4. Alerting:

    • If a modification is detected, it generates an alert with details about the change, including who made the change (Actor), the target user (TargetUpn), and the old and new values of the modified attribute.
    • The alert is categorized under tactics like Privilege Escalation, Persistence, and Defense Evasion, aligning with MITRE ATT&CK techniques.
  5. Incident Management:

    • The query is scheduled to run every hour and will create an incident if any suspicious activity is detected.
    • It groups related alerts into incidents based on the affected account to streamline investigation.

Overall, this query helps security teams monitor and respond to potential account manipulation attempts on privileged cloud users.

Details

David Alonso profile picture

David Alonso

Released: June 1, 2026

Tables

AADProvisioningLogsAuditLogsIdentityInfo

Keywords

IdentityEntraIDUserDirectoryRoleCloudAdminAADProvisioningLogsAuditLogsAccountPrivilegedUsersAzureActiveDirectoryIdentityInfoAccountUPNAccountObjectIdAssignedRolesTargetUpnServicePrincipalModifiedPropertiesTimeGeneratedChannelActorSourceIPTargetResourcesInitiatedByUserPrincipalNameImmutableIdMailNickname

Operators

letdynamicwhereagosummarizearg_maxisnotemptyprojectextendtostringparse_jsonmv-expandtodynamicin~has_anycoalesceunionjoinonorder by

Severity

High

Tactics

PrivilegeEscalationPersistenceDefenseEvasion

Frequency: 1h

Period: 1h

Actions

GitHub