Skip to content

Cloud & Cross-Workload Lateral Movement Investigation

In a cloud-native Microsoft environment, lateral movement does not rely on legacy on-prem RPC/SMB exploitation. Instead, adversaries pivot between cloud identities, Microsoft Graph API permissions, Azure Virtual Machines, and M365 workloads.


🧭 Cloud Lateral Movement Vectors Overview

Section titled “🧭 Cloud Lateral Movement Vectors Overview”
VectorMechanismAttacker GoalKey Telemetry Source
Azure VM RunCommandAzure Resource Manager API (RunCommand/action)Execute remote commands as SYSTEM / root on Azure VMsAzureActivity
IMDS Token TheftSSRF or shell access to 169.254.169.254Steal Azure Managed Identity token to access Key Vault or SQLAzureDiagnostics, Sentinel
Intune Script InjectionAbusing Intune Admin role via Graph APIDeploy malicious script to all Intune-managed endpointsAuditLogs, IntuneManagementExtension
Mailbox Delegation PivotExploiting FullAccess / SendAs permissionsPivot from victim mailbox into Executive / Finance mailboxesOfficeActivity, Exchange Audit
Cloud Cross-Tenant PivotAbusing Cross-Tenant Access Policies / B2B TrustPivot into partner or subsidiary Entra ID tenantsSigninLogs (CrossTenantAccessType)

⚡ 1. Detecting Azure VM RunCommand Execution Abuse

Section titled “⚡ 1. Detecting Azure VM RunCommand Execution Abuse”

Adversaries possessing Contributor or Virtual Machine Administrator rights execute arbitrary PowerShell / Bash scripts on target Azure VMs without needing network line-of-sight (SSH or RDP):

KQL Query: Hunt for RunCommand Invocations in AzureActivity

Section titled “KQL Query: Hunt for RunCommand Invocations in AzureActivity”
AzureActivity
| where TimeGenerated > ago(7d)
| where OperationNameValue =~ "Microsoft.Compute/virtualMachines/runCommand/action"
| where ActivityStatusValue =~ "Success" or ActivityStatusValue =~ "Start"
| extend TargetVM = tostring(parse_json(Properties).resource)
| extend CallerIP = CallerIpAddress
| project TimeGenerated, Caller, CallerIP, TargetVM, ActivityStatusValue, ResourceGroup, SubscriptionId
| sort by TimeGenerated desc

🔑 2. Detecting Managed Identity & IMDS Token Theft

Section titled “🔑 2. Detecting Managed Identity & IMDS Token Theft”

Adversaries with local execution on an Azure Virtual Machine or App Service query the Azure Instance Metadata Service (IMDS) at 169.254.169.254 to obtain tokens for the resource’s Managed Identity:

Terminal window
# Attacker command executed on compromised Azure Linux/Windows VM:
curl -H "Metadata:true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

KQL Query: Detecting Managed Identity Sign-ins from External / Unexpected IPs

Section titled “KQL Query: Detecting Managed Identity Sign-ins from External / Unexpected IPs”

Managed Identities are designed to authenticate only from the host they are assigned to. A Managed Identity sign-in from an external or unexpected IP indicates token exfiltration:

AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ServicePrincipalType =~ "ManagedIdentity"
| project TimeGenerated, ServicePrincipalName, ServicePrincipalId, IPAddress, Location, ResourceDisplayName
| summarize
Signins = count(),
DistinctIPs = dcount(IPAddress),
IPList = make_set(IPAddress)
by ServicePrincipalName, ServicePrincipalId
| where DistinctIPs > 1
| sort by DistinctIPs desc

💻 3. Detecting Rogue Intune Script Deployment (Endpoint Pivot)

Section titled “💻 3. Detecting Rogue Intune Script Deployment (Endpoint Pivot)”

If an administrative account is compromised, the attacker can deploy a malicious PowerShell or shell script through Intune to execute on all managed Windows and macOS endpoints:

KQL Query: Hunt for New or Modified Intune Device Management Scripts

Section titled “KQL Query: Hunt for New or Modified Intune Device Management Scripts”
AuditLogs
| where TimeGenerated > ago(7d)
| where LoggedByService =~ "Device Management"
| where OperationName has_any ("Create DeviceManagementScript", "Update DeviceManagementScript", "Create CustomAppConfig")
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend ScriptName = tostring(TargetResources[0].displayName)
| project TimeGenerated, OperationName, Actor, ScriptName, Result, TargetResources
| sort by TimeGenerated desc

📬 4. Exchange Online Mailbox Delegation Pivoting

Section titled “📬 4. Exchange Online Mailbox Delegation Pivoting”

Adversaries pivot laterally across business units by assigning themselves permissions to high-value mailboxes (Finance, C-Suite, Legal):

KQL Query: Detecting Mailbox Permission Grants

Section titled “KQL Query: Detecting Mailbox Permission Grants”
OfficeActivity
| where TimeGenerated > ago(7d)
| where RecordType =~ "ExchangeAdmin"
| where Operation in~ ("Add-MailboxPermission", "Add-RecipientPermission")
| extend TargetMailbox = tostring(parse_json(Parameters)[0].Value)
| extend GrantedUser = tostring(parse_json(Parameters)[1].Value)
| extend AccessRights = tostring(parse_json(Parameters)[2].Value)
| project TimeGenerated, UserId, Operation, TargetMailbox, GrantedUser, AccessRights
| sort by TimeGenerated desc

🛡️ 5. Cross-Tenant Access & External Collaboration Pivots

Section titled “🛡️ 5. Cross-Tenant Access & External Collaboration Pivots”

Detect when an account in your tenant is used to authenticate into external Entra ID organizations:

SigninLogs
| where TimeGenerated > ago(7d)
| where isnotempty(ResourceTenantId)
| where ResourceTenantId != HomeTenantId // Cross-tenant authentication
| project TimeGenerated, UserPrincipalName, IPAddress, ResourceDisplayName, ResourceTenantId, AppDisplayName
| sort by TimeGenerated desc