← Back to SOC feed Coverage →

Member or owner added to a role-assignable group within 24 hours of its creation

kql MEDIUM Azure-Sentinel
T1098.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-07-31T11:00:00Z · Confidence: medium

Hunt Hypothesis

This hunt detects adversaries who rapidly add members or owners to newly created role-assignable groups to silently establish persistent privileged access while bypassing standard role-assignment audit logs. SOC teams should proactively investigate this pattern in Azure Sentinel because it represents a stealthy privilege escalation tactic that often evades traditional monitoring focused solely on direct role assignments, allowing attackers to maintain undetected administrative control.

KQL Query

let timeframe = 1d;
let creationLookback = 14d;
let window = 24h;
let RecentlyCreatedRoleAssignableGroups =
    AuditLogs
    | where TimeGenerated >= ago(timeframe + creationLookback) and TimeGenerated < ago(0h)
    | where Category =~ "GroupManagement"
    | where OperationName =~ "Add group"
    | where Result =~ "success"
    | mv-expand ModProp = TargetResources[0].modifiedProperties
    | extend PropName = tostring(ModProp.displayName)
    | extend NewValue = tostring(ModProp.newValue)
    | where PropName has "IsAssignableToRole" or NewValue has "isAssignableToRole"
    | where NewValue has "true"
    | extend GroupId = tolower(tostring(TargetResources[0].id))
    | where isnotempty(GroupId)
    | project GroupId, CreationTime = TimeGenerated;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where Category =~ "GroupManagement"
| where OperationName in~ ("Add member to group", "Add owner to group")
| where Result =~ "success"
| 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))
| mv-apply TargetResource = TargetResources on (
      where TargetResource.type =~ "User"
      | extend AddedUpn = tostring(TargetResource.userPrincipalName),
               AddedId  = tostring(TargetResource.id),
               Properties = TargetResource.modifiedProperties
  )
| mv-apply Property = Properties on (
      where Property.displayName =~ "Group.ObjectID"
      | extend GroupId = tolower(trim('"', tostring(Property.newValue)))
  )
| mv-apply Property = Properties on (
      where Property.displayName =~ "Group.DisplayName"
      | extend GroupName = trim('"', tostring(Property.newValue))
  )
| where isnotempty(GroupId)
| join kind=inner RecentlyCreatedRoleAssignableGroups on GroupId
| where TimeGenerated >= CreationTime and TimeGenerated <= CreationTime + window
| extend AccountName      = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[0]), Actor)
| extend AccountUPNSuffix = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[1]), "")
| project
    TimeGenerated,
    OperationName,
    GroupName,
    GroupId,
    CreationTime,
    AddedUpn,
    AddedId,
    Actor,
    AccountName,
    AccountUPNSuffix,
    ActorIp,
    CorrelationId
| sort by TimeGenerated desc

Analytic Rule Definition

id: 17b75367-3b4e-4292-b985-30112acc2de0
name: Member or owner added to a role-assignable group within 24 hours of its creation
description: |
  Identifies members or owners added to a role-assignable group within 24 hours of its creation, a pattern used to grant privileged access without generating a direct role-assignment audit event.
description-detailed: |
  Group role-assignability (isAssignableToRole set to true) can only be set at
  group creation time, not added to an existing group. This chains two
  individually low-signal events into a high-confidence indicator of privilege
  escalation: an attacker who holds Privileged Role Administrator or Global
  Administrator access creates a role-assignable group, then immediately adds
  an account, often their own, or a service principal, as a member or owner.
  That account inherits any directory role later assigned to the group without
  ever generating an "Add member to role" audit event for itself, since the
  role is granted to the group as a whole.
  The group identity for "Add member to group" and "Add owner to group" events
  is read from the "Group.ObjectID" and "Group.DisplayName" modifiedProperties
  on the added principal's TargetResources entry rather than from a fixed
  array index, matching the extraction pattern used elsewhere in this repo for
  the same operations, since TargetResources ordering is not guaranteed to
  place the group first.
  Investigate whether the creation and the membership change were part of the
  same documented change, whether the added identity already holds other
  privileged access, and whether the group is subsequently assigned a
  directory role.
  References:
  - https://learn.microsoft.com/entra/identity/role-based-access-control/groups-concept
  - https://attack.mitre.org/techniques/T1098/003/
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
tactics:
  - PrivilegeEscalation
  - Persistence
relevantTechniques:
  - T1098.003
query: |
  let timeframe = 1d;
  let creationLookback = 14d;
  let window = 24h;
  let RecentlyCreatedRoleAssignableGroups =
      AuditLogs
      | where TimeGenerated >= ago(timeframe + creationLookback) and TimeGenerated < ago(0h)
      | where Category =~ "GroupManagement"
      | where OperationName =~ "Add group"
      | where Result =~ "success"
      | mv-expand ModProp = TargetResources[0].modifiedProperties
      | extend PropName = tostring(ModProp.displayName)
      | extend NewValue = tostring(ModProp.newValue)
      | where PropName has "IsAssignableToRole" or NewValue has "isAssignableToRole"
      | where NewValue has "true"
      | extend GroupId = tolower(tostring(TargetResources[0].id))
      | where isnotempty(GroupId)
      | project GroupId, CreationTime = TimeGenerated;
  AuditLogs
  | where TimeGenerated >= ago(timeframe)
  | where Category =~ "GroupManagement"
  | where OperationName in~ ("Add member to group", "Add owner to group")
  | where Result =~ "succe

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

False Positive Scenarios for “Member or Owner Added to Role-Assignable Group Within 24 Hours”

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