mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-03-12 21:22:57 -07:00
f
This commit is contained in:
@@ -216,7 +216,7 @@ These refresh tokens must be minted in that broker context (a regular refresh to
|
|||||||
|
|
||||||
### Goal and purpose
|
### Goal and purpose
|
||||||
|
|
||||||
The goal of BroCI is to reuse a valid user session from a broker-capable app chain and request tokens for another trusted app/resource pair without running a new full interactive flow each time.
|
The goal of BroCI is to reuse a valid user session from a broker-capable app chain and request tokens for another trusted app/resource pair. Therefore, allowing to "escalate privileges" from the original token.
|
||||||
|
|
||||||
From an offensive perspective, this matters because:
|
From an offensive perspective, this matters because:
|
||||||
|
|
||||||
@@ -235,6 +235,9 @@ NAA/BroCI token exchanges are **not** the same as a regular OAuth refresh exchan
|
|||||||
- You generally cannot "convert" a normal refresh token into a BroCI-valid one in code.
|
- You generally cannot "convert" a normal refresh token into a BroCI-valid one in code.
|
||||||
- You need a refresh token already issued by a compatible brokered flow.
|
- You need a refresh token already issued by a compatible brokered flow.
|
||||||
|
|
||||||
|
Check the web **<https://entrascopes.com/>** to find BroCI configured apps an the trust relationships they have.
|
||||||
|
|
||||||
|
|
||||||
### Mental model
|
### Mental model
|
||||||
|
|
||||||
Think of BroCI as:
|
Think of BroCI as:
|
||||||
@@ -245,7 +248,7 @@ If any part of that broker chain does not match, the exchange fails.
|
|||||||
|
|
||||||
### Where to find a BroCI-valid refresh token
|
### Where to find a BroCI-valid refresh token
|
||||||
|
|
||||||
In authorized testing/lab scenarios, one practical way is browser portal traffic collection:
|
One practical way is browser portal traffic collection:
|
||||||
|
|
||||||
1. Sign in to `https://entra.microsoft.com` (or Azure portal).
|
1. Sign in to `https://entra.microsoft.com` (or Azure portal).
|
||||||
2. Open DevTools -> Network.
|
2. Open DevTools -> Network.
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ az ad app update --id <app-id> --web-redirect-uris "https://original.com/callbac
|
|||||||
|
|
||||||
### Applications Privilege Escalation
|
### Applications Privilege Escalation
|
||||||
|
|
||||||
**As explained in [this post](https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/)** it was very common to find default applications that have **API permissions** of type **`Application`** assigned to them. An API Permission (as called in the Entra ID console) of type **`Application`** means that the application can access the API without a user context (without a user login into the app), and without needing Entra ID roles to allow it. Therefore, it's very common to find **high privileged applications in every Entra ID tenant**.
|
**As explained in [this post](https://dirkjanm.io/azure-ad-privilege-escalation-application-admin/)** it was very common to find default applications that have **API permissions** of type **`Application`** assigned to them. An API Permission (as called in the Entra ID console) of type **`Application`** means that the application can access the API and perform actions without a user context (without a user login into the app), and without needing Entra ID roles to allow it. Therefore, it's very common to find **high privileged applications in every Entra ID tenant**.
|
||||||
|
|
||||||
Then, if an attacker has any permission/role that allows to **update the credentials (secret o certificate) of the application**, the attacker can generate a new credential and then use it to **authenticate as the application**, gaining all the permissions that the application has.
|
Then, if an attacker has any permission/role that allows to **update the credentials (secret o certificate) of the application**, the attacker can generate a new credential and then use it to **authenticate as the application**, gaining all the permissions that the application has.
|
||||||
|
|
||||||
@@ -138,6 +138,83 @@ 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
|
az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id=='d07a8cc0-3d51-4b77-b3b0-32704d1f69fa'].value" -o tsv
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Find all applications with API permissions to non-Microsoft APIs (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
|
## Service Principals
|
||||||
|
|
||||||
### `microsoft.directory/servicePrincipals/credentials/update`
|
### `microsoft.directory/servicePrincipals/credentials/update`
|
||||||
@@ -397,4 +474,3 @@ az rest --method GET \
|
|||||||
{{#include ../../../../banners/hacktricks-training.md}}
|
{{#include ../../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -819,6 +819,83 @@ 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
|
az ad sp show --id 00000003-0000-0000-c000-000000000000 --query "appRoles[?id=='d07a8cc0-3d51-4b77-b3b0-32704d1f69fa'].value" -o tsv
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Find all applications with API permissions to non-Microsoft APIs (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 }}
|
{{#endtab }}
|
||||||
|
|
||||||
{{#tab name="Az" }}
|
{{#tab name="Az" }}
|
||||||
@@ -1308,4 +1385,3 @@ The default mode is **Audit**:
|
|||||||
{{#include ../../../banners/hacktricks-training.md}}
|
{{#include ../../../banners/hacktricks-training.md}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user