Skip to main content
cd ../config-traps
risk/register/app-service-uami-at-management-group-scope-token-theft.html
Azure Managed Identityhigh severityAzure App Service

App Service UAMI at Management Group Scope: Token Theft

Severity
high
Reviewed
26 Jul 2026
Remediation
~20 minutes
Overview

A single compromised App Service process can request an OAuth token from the local identity endpoint and use it far outside the app's own subscription. When the attached identity carries a Management Group role assignment, that token becomes a pivot point across every subscription beneath it, and network egress rules never see the request leave the box.

Operational summary

At a glance

Symptom
Remote code execution or SSRF inside the App Service worker calls the local IDENTITY_ENDPOINT and retrieves a valid access token.
Likely cause
A User-Assigned Managed Identity holds a role assignment at Management Group scope while attached to an internet-facing App Service instance.
Impact
The stolen token is valid across every subscription under the Management Group, not just the one hosting the compromised app.
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Scope UAMI role assignments to individual subscriptions or resource groups and block ServicePrincipal role assignments above subscription scope via Azure Policy.
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

User-Assigned Managed Identity (UAMI) role assignments made at Management Group scope, attached to an Azure App Service instance that also exposes an unauthenticated local token endpoint to anything running inside the worker process.

The Default State

Platform teams standardising multi-subscription deployments often create one shared UAMI, attach it to dozens of App Service instances via az webapp identity assign --identities, and then grant that identity Contributor or Reader at the Management Group level so nobody has to repeat role assignments per subscription. App Service exposes the token via the IDENTITY_ENDPOINT and IDENTITY_HEADER environment variables (the successor to the older MSI_ENDPOINT), reachable from any code executing in that worker with no additional authentication beyond the header value already sitting in the process environment.

The Blast Radius

Any remote code execution, server-side request forgery, or vulnerable dependency inside that single App Service instance lets an attacker call the local identity endpoint, retrieve an access token scoped to the UAMI’s assigned roles, and use it directly against Azure Resource Manager. Because the role assignment sits at Management Group scope, the stolen token is valid across every subscription nested beneath it, not just the one hosting the compromised app. Subscription-level network controls, NSGs, and even Conditional Access policies never trigger, because the token issuance is a loopback call to the App Service platform’s own metadata service, not a route that crosses a monitored boundary. Incident responders scoping containment to the affected resource group or subscription will miss the actual exposure entirely.

The Lead Mechanic Fix

Never assign roles to a UAMI above subscription scope, and prefer resource-group scoping wherever the workload allows it. Replace the shared identity with one UAMI per subscription: az identity create --name uami-sub-prod --resource-group rg-identity, then bind roles narrowly: az role assignment create --assignee-object-id <principalId> --assignee-principal-type ServicePrincipal --role "Contributor" --scope /subscriptions/<subId>/resourceGroups/<rg>. Enforce this with an Azure Policy definition using deny effect on Microsoft.Authorization/roleAssignments where scope matches /providers/Microsoft.Management/managementGroups/* for principals of type ServicePrincipal. Audit existing exposure with az role assignment list --all --query "[?principalType=='ServicePrincipal']" filtered against Management Group scopes, and rotate any UAMI found there onto per-subscription identities immediately.

03

Apply the safer control

Before you change production

Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.

Fix commands and configuration

az identity create --name uami-sub-prod --resource-group rg-identity
az role assignment create --assignee-object-id <principalId> --assignee-principal-type ServicePrincipal --role "Contributor" --scope /subscriptions/<subId>/resourceGroups/<rg>
deny
04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources