AZ_ADD_MEMBERS
Summary
Description
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 -AutoSizeAzure CLI
Azure GUI
Exploitation
PowerShell
Azure GUI
Mitigation
Detection
References
Last updated
Was this helpful?