AZ_ADD_MEMBERS

Summary

FSProtect ACL Alias

AZ_ADD_MEMBERS

Azure AD Alias

Add Members (Group)

Affected Object Types

AZ Group

Exploitation Certainty

Certain

Graph Permission / Role

Ability to add group members via Graph scopes (e.g., GroupMember.ReadWrite.All, Group.ReadWrite.All, Directory.ReadWrite.All) and/or directory roles (e.g., Groups Administrator, User Administrator) or being a Group Owner

Description

AZ_ADD_MEMBERS represents the ability for a principal (user, service principal, or group owner) to add members to a Microsoft Entra (Azure AD) group. By adding themselves (or a controlled identity) into a privileged group, an attacker can:

  • Inherit Azure RBAC permissions if the group is assigned to subscriptions/resource groups/resources.

  • Inherit directory roles when the group is role-assignable (isAssignableToRole = true).

Therefore, any identity with add-member capability on sensitive groups can quickly escalate privileges by modifying group membership.

Identification

PowerShell

$ErrorActionPreference='Stop'

function Expand-GroupUsers {
    param([string]$GroupId)
    $seenGroups = New-Object System.Collections.Generic.HashSet[string]
    $seenUsers  = New-Object System.Collections.Generic.HashSet[string]
    $users = @()
    $q = New-Object System.Collections.Generic.Queue[object]
    $null = $seenGroups.Add($GroupId)
    $q.Enqueue($GroupId)
    while($q.Count -gt 0){
        $gid = $q.Dequeue()
        try{ $members = Get-AzureADGroupMember -All $true -ObjectId $gid }catch{ continue }
        foreach($m in $members){
            if($m.ObjectType -eq 'User'){
                if($seenUsers.Add($m.ObjectId)){ $users += $m }
            }elseif($m.ObjectType -eq 'Group'){
                if($seenGroups.Add($m.ObjectId)){ $q.Enqueue($m.ObjectId) }
            }
        }
    }
    $users
}

function Get-AddableUsersByRoles {
    $rolesAll = @('Global Administrator','Privileged Role Administrator','Groups Administrator','User Administrator')
    $rolesAssignableOnly = @('Global Administrator','Privileged Role Administrator')
    $targetNames = ($rolesAll + $rolesAssignableOnly) | Select-Object -Unique
    $activeRoles = Get-AzureADDirectoryRole | Where-Object { $_.DisplayName -in $targetNames }
    $roleUsers = @{}
    foreach($r in $activeRoles){
        $members = @(Get-AzureADDirectoryRoleMember -ObjectId $r.ObjectId)
        $u = @()
        foreach($m in $members){
            if($m.ObjectType -eq 'User'){
                $u += $m
            }elseif($m.ObjectType -eq 'Group'){
                $u += Expand-GroupUsers -GroupId $m.ObjectId
            }
        }
        $uniq = $u | Group-Object ObjectId | ForEach-Object { $_.Group[0] }
        $roleUsers[$r.DisplayName] = $uniq
    }
    $groups = Get-AzureADMSGroup -All $true
    $rows = @()
    foreach($g in $groups){
        $assignable = $false
        if ($g.PSObject.Properties.Name -contains 'IsAssignableToRole') { $assignable = [bool]$g.IsAssignableToRole }
        elseif ($g.PSObject.Properties.Name -contains 'AdditionalProperties') {
            $v = $g.AdditionalProperties['isAssignableToRole']
            if ($null -ne $v) { $assignable = [bool]$v }
        }
        $eligible = if ($assignable) { $rolesAssignableOnly } else { $rolesAll }
        foreach($rn in $eligible){
            if(-not $roleUsers.ContainsKey($rn)){ continue }
            foreach($u in $roleUsers[$rn]){
                $upn = if($u.PSObject.Properties.Name -contains 'UserPrincipalName'){ $u.UserPrincipalName } else { '' }
                $rows += [pscustomobject]@{
                    GroupDisplayName   = $g.DisplayName
                    GroupId            = $g.Id
                    IsAssignableToRole = $assignable
                    UserDisplayName    = $u.DisplayName
                    UserPrincipalName  = $upn
                    UserId             = $u.ObjectId
                    Reason             = "Tenant role: $rn"
                }
            }
        }
    }
    $rows
}

function Get-AllGroupOwnersAz {
    $pageSize = 999
    $skip = 0
    $allGroups = @()
    do {
        $batch = Get-AzADGroup -First $pageSize -Skip $skip -ErrorAction Stop
        if ($batch) { $allGroups += $batch; $skip += $batch.Count }
    } while ($batch.Count -eq $pageSize)
    $rows = foreach ($g in $allGroups) {
        $owners = $null
        try   { $owners = @( Get-AzADGroupOwner -GroupId $g.Id -ErrorAction Stop ) }
        catch { $owners = @( Get-AzADGroupOwner -GroupObjectId $g.Id -ErrorAction SilentlyContinue ) }
        if (-not $owners -or $owners.Count -eq 0) { continue }
        foreach ($o in $owners) {
            $dtype = $o.AdditionalProperties['@odata.type']
            $otype = if ($dtype) { ($dtype -replace '^#microsoft\.graph\.', '') } else { 'DirectoryObject' }
            [pscustomobject]@{
                GroupDisplayName = $g.DisplayName
                OwnerType        = $otype
                OwnerDisplayName = $o.DisplayName
            }
        }
    }
    $rows
}

$roleRows = Get-AddableUsersByRoles
$ownerRows = Get-AllGroupOwnersAz

"=== USERS WHO CAN ADD MEMBERS (BY TENANT ROLES) ==="
$roleRows | Sort-Object GroupDisplayName,UserDisplayName,UserPrincipalName | Format-Table -AutoSize
"=== GROUP OWNERS (AZ) ==="
$ownerRows | Sort-Object GroupDisplayName,OwnerType,OwnerDisplayName | Format-Table -AutoSize

Azure CLI

Check owners and (if you are owner or hold the right role) add a member:

Azure GUI

  • Open Microsoft Entra admin centerGroups → select the target group.

  • Go to Owners and list all identities shown there.

    • Anyone listed as Owner can add members to this group.

  • Go to Properties and check “Is this group assignable to roles in Entra ID?”

    • If True (role-assignable): only Global Administrator (GA) and Privileged Role Administrator (PRA) (besides Owners) can add members.

    • If False: GA, PRA, Groups Administrator (GAd), User Administrator (UAd) (besides Owners) can add members.

  • Go to Roles & administrators and open each relevant role (GA, PRA, GAd, UAd).

    • In each role → Assignments: list the users and groups that hold the role.

    • If a group holds the role, enumerate its transitive members (nested included) to get the effective users who can add members.

Exploitation

PowerShell

Azure GUI

  • Go to Microsoft Entra IDGroups.

  • Locate the target group using Search or filters, and open the group.

  • Open Members (left menu of the group blade).

  • Click Add members.

  • In the picker dialog:

    • Search for the identity you want to insert.

    • Select the identity and click Select (or Add) to confirm.

Mitigation

1

Reduce owners for high-risk groups

Fewer Owners means fewer identities that can add members; this directly reduces lateral-movement and privilege-escalation risk, especially for role-assignable or production-impacting groups.

Procedure:

  • Go to Microsoft Entra IDGroupsAll groups.

  • Filter or search to find the groups that you want to remove owner(s).

  • Open a target group.

  • Open Owners.

  • Remove any vulnerable/high-risk identities.

2

Remove unnecessary / vulnerable user assignees from a role

Procedure:

  • Go to Microsoft Entra IDRoles & administrators.

  • In the search box, type the exact role name (e.g., Global Administrator) and click the role.

  • In the left menu, click Assignments.

  • Ensure Active assignments is selected.

  • Find the user you want to remove.

  • At the right of that row, click … (More options)Remove assignment.

  • In the confirmation dialog, click Remove.

Detection

Detect “Add member to group” directly in Audit logs.

  • Go to Microsoft Entra IDAudit logs.

  • Click Category: All → select GroupManagementApply.

  • If the Activity column is hidden: Manage viewEdit columns → check Activity, Initiated by (actor), TargetApply.

References

  • https://learn.microsoft.com/en-us/cli/azure/ad/group/member?view=azure-cli-latest

  • https://learn.microsoft.com/en-us/entra/identity/monitoring-health/concept-activity-log-schemas

  • https://learn.microsoft.com/en-us/powershell/module/az.resources/add-azadgroupmember?view=azps-14.6.0

Last updated

Was this helpful?