From 9ebb2d956ecdf2e02ac8a2d9cc960dceac7495d4 Mon Sep 17 00:00:00 2001 From: Carlos Polop Date: Sun, 1 Mar 2026 21:18:03 +0100 Subject: [PATCH] f --- .../az-entraid-privesc/README.md | 52 ++++++++++++++--- .../azure-security/az-services/az-azuread.md | 57 +++++++++++++++---- 2 files changed, 91 insertions(+), 18 deletions(-) diff --git a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md index cb400ebd1..795ab6807 100644 --- a/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md +++ b/src/pentesting-cloud/azure-security/az-privilege-escalation/az-entraid-privesc/README.md @@ -139,7 +139,7 @@ az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id==' ```
-Find all applications with API permissions to non-Microsoft APIs (az cli) +Find all applications API permissions and mark Microsoft-owned APIs ```bash #!/usr/bin/env bash @@ -162,6 +162,32 @@ is_microsoft_owner() { 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 @@ -170,7 +196,8 @@ apps_json="$(az ad app list --all --query '[?length(requiredResourceAccess) > `0 tmp_map="$(mktemp)" tmp_ids="$(mktemp)" -trap 'rm -f "$tmp_map" "$tmp_ids"' EXIT +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" @@ -184,9 +211,9 @@ while IFS= read -r rid; do printf '%s\t%s\t%s\n' "$rid" "$owner" "$name" >> "$tmp_map" done < "$tmp_ids" -echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tresourceOwnerOrgId\tpermissionType\tpermissionId" +echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tisMicrosoft\tpermissions" -# Print only app permissions where the target API is NOT Microsoft-owned. +# 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")" @@ -201,14 +228,25 @@ while IFS= read -r row; do [ -n "$resource_name" ] || resource_name="UNKNOWN" if is_microsoft_owner "$owner_org"; then - continue + 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")" - echo -e "${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${owner_org}\t${perm_type}\t${perm_id}" + 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") ``` @@ -472,5 +510,3 @@ az rest --method GET \ - `microsoft.directory/applications.myOrganization/permissions/update` {{#include ../../../../banners/hacktricks-training.md}} - - diff --git a/src/pentesting-cloud/azure-security/az-services/az-azuread.md b/src/pentesting-cloud/azure-security/az-services/az-azuread.md index f492ff004..887a5ae12 100644 --- a/src/pentesting-cloud/azure-security/az-services/az-azuread.md +++ b/src/pentesting-cloud/azure-security/az-services/az-azuread.md @@ -761,10 +761,11 @@ For more information about Applications check: ../az-basic-information/ {{#endref}} -When an App is generated 2 types of permissions are given: +When an App is generated 3 types of permissions are given: -- **Permissions** given to the **Service Principal** +- **Permissions** given to the **Service Principal** (via roles). - **Permissions** the **app** can have and use on **behalf of the user**. +- **API Permissions** that gives the app permissions over EntraID withuot requiring other roles granting these permissions. {{#tabs }} {{#tab name="az cli" }} @@ -820,7 +821,7 @@ az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id==' ```
-Find all applications with API permissions to non-Microsoft APIs (az cli) +Find all applications API permissions and mark Microsoft-owned APIs (az cli) ```bash #!/usr/bin/env bash @@ -843,6 +844,32 @@ is_microsoft_owner() { 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 @@ -851,7 +878,8 @@ apps_json="$(az ad app list --all --query '[?length(requiredResourceAccess) > `0 tmp_map="$(mktemp)" tmp_ids="$(mktemp)" -trap 'rm -f "$tmp_map" "$tmp_ids"' EXIT +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" @@ -865,9 +893,9 @@ while IFS= read -r rid; do printf '%s\t%s\t%s\n' "$rid" "$owner" "$name" >> "$tmp_map" done < "$tmp_ids" -echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tresourceOwnerOrgId\tpermissionType\tpermissionId" +echo -e "appDisplayName\tappId\tresourceApiDisplayName\tresourceAppId\tisMicrosoft\tpermissions" -# Print only app permissions where the target API is NOT Microsoft-owned. +# 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")" @@ -882,14 +910,25 @@ while IFS= read -r row; do [ -n "$resource_name" ] || resource_name="UNKNOWN" if is_microsoft_owner "$owner_org"; then - continue + 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")" - echo -e "${app_name}\t${app_id}\t${resource_name}\t${resource_app_id}\t${owner_org}\t${perm_type}\t${perm_id}" + 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") ``` @@ -1383,5 +1422,3 @@ The default mode is **Audit**: - [EntraTokenAid](https://github.com/zh54321/EntraTokenAid) {{#include ../../../banners/hacktricks-training.md}} - -