Skip to content

RUNBOOK // Entra ID Global Admin Compromise & Tier-0 Cloud Lockdown

SEVERITY 1 // CRITICAL TIER-0 ENTRA ID CLOUD BREACH Target Time to Contain: < 30 minutes

In a 100% cloud-native environment, Microsoft Entra ID (Azure AD) is your security boundary. If an account with the Global Administrator role, Privileged Role Administrator role, or an Emergency Access (Break-Glass) account is compromised, the adversary possesses unrestricted authority across the entire tenant - including Microsoft 365 data, Intune endpoint policies, Conditional Access rules, and Azure subscription hierarchies.


🛑 Step 1: Emergency Cloud Identity Lockdown (< 10 Minutes)

Section titled “🛑 Step 1: Emergency Cloud Identity Lockdown (< 10 Minutes)”

Execute immediate containment using a verified, clean administrator account:

1. Invalidate All Active User Sessions Tenant-Wide

Section titled “1. Invalidate All Active User Sessions Tenant-Wide”
Terminal window
# Connect to Microsoft Graph PowerShell using a verified administrative account
Connect-MgGraph -Scopes "User.ReadWrite.All", "RoleManagement.ReadWrite.Directory"
$compromisedAdmin = "compromised.admin@company.com"
# 1. Immediately revoke all active browser and desktop session tokens
Revoke-MgUserSignInSession -UserId $compromisedAdmin
Write-Host "[+] Active sessions invalidated for $compromisedAdmin." -ForegroundColor Green
# 2. Disable the account temporarily during investigation
Update-MgUser -UserId $compromisedAdmin -AccountEnabled $false
Write-Host "[+] Account temporarily disabled." -ForegroundColor Yellow
# 3. Forcibly reset password to generate a random 32-character key
$emergencyPass = [System.Web.Security.Membership]::GeneratePassword(32, 8) + "!Z9"
Update-MgUser -UserId $compromisedAdmin -PasswordProfile @{
ForceChangePasswordNextSignIn = $true
Password = $emergencyPass
}

2. If Break-Glass / Emergency Access Account was Triggered

Section titled “2. If Break-Glass / Emergency Access Account was Triggered”
  1. Verify whether the logon was legitimate or malicious via SigninLogs.
  2. Rotate FIDO2 security keys and password for the emergency account immediately.
  3. Verify password storage in the physical hardware safe / secure vault.

🔍 Step 2: Audit Privileged Identity Management (PIM) & Role Grants

Section titled “🔍 Step 2: Audit Privileged Identity Management (PIM) & Role Grants”

Adversaries immediately attempt to establish persistence by granting themselves permanent administrator rights or elevating secondary accounts:

1. KQL Query: Audit All Role Assignments & PIM Activations in the Last 7 Days

Section titled “1. KQL Query: Audit All Role Assignments & PIM Activations in the Last 7 Days”
AuditLogs
| where TimeGenerated > ago(7d)
| where LoggedByService in~ ("Core Directory", "PIM")
| where OperationName has_any ("Add member to role", "Add eligible member to role", "Add role assignment", "ElevateAccess")
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend TargetRole = tostring(TargetResources[0].modifiedProperties[1].newValue)
| extend TargetUser = tostring(TargetResources[2].displayName)
| project TimeGenerated, OperationName, Actor, TargetRole, TargetUser, Result, TargetResources
| sort by TimeGenerated desc

2. PowerShell: Remove Unauthorized Permanent Role Assignments

Section titled “2. PowerShell: Remove Unauthorized Permanent Role Assignments”
Terminal window
# List all active Global Administrators
$roleDef = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Global Administrator'"
Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($roleDef.Id)'" -ExpandProperty "principal" | ForEach-Object {
[PSCustomObject]@{
AssignmentId = $_.Id
PrincipalName = $_.Principal.AdditionalProperties["displayName"]
UserPrincipalName = $_.Principal.AdditionalProperties["userPrincipalName"]
}
}

🛡️ Step 3: Audit Conditional Access Policy Tampering

Section titled “🛡️ Step 3: Audit Conditional Access Policy Tampering”

Adversaries frequently disable Conditional Access policies or add their own IP address/user to the Exclude list to maintain access:

1. KQL Query: Detect Conditional Access Policy Modifications

Section titled “1. KQL Query: Detect Conditional Access Policy Modifications”
AuditLogs
| where TimeGenerated > ago(7d)
| where LoggedByService =~ "Conditional Access"
| where OperationName in~ ("Add conditional access policy", "Update conditional access policy", "Delete conditional access policy")
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend PolicyName = tostring(TargetResources[0].displayName)
| project TimeGenerated, OperationName, Actor, PolicyName, Result, TargetResources
| sort by TimeGenerated desc

2. Audit Named Locations (Trusted IP Bypasses)

Section titled “2. Audit Named Locations (Trusted IP Bypasses)”
Terminal window
# Verify no rogue IP ranges were added to Trusted Named Locations
Get-MgIdentityConditionalAccessNamedLocation | Select-Object DisplayName, IsTrusted, @{N='IPRanges';E={$_.AdditionalProperties["ipRanges"].cidrAddress}} | Format-Table -AutoSize

🔑 Step 4: Audit Service Principal Backdoors & App Credentials

Section titled “🔑 Step 4: Audit Service Principal Backdoors & App Credentials”

The #1 persistence method after compromising an Entra tenant is adding a certificate or secret to an existing high-privilege Service Principal or creating a rogue App Registration:

1. KQL Query: Detect Secrets or Certificates Added to Enterprise Apps

Section titled “1. KQL Query: Detect Secrets or Certificates Added to Enterprise Apps”
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName in~ ("Add service principal credentials", "Update application - Certificates and secrets management", "Add delegated permission grant", "Consent to application")
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| extend TargetApp = tostring(TargetResources[0].displayName)
| project TimeGenerated, OperationName, Actor, TargetApp, Result, TargetResources
| sort by TimeGenerated desc

2. Check for Rogue Federated Identity Credentials

Section titled “2. Check for Rogue Federated Identity Credentials”

Adversaries use workload identity federation to authenticate from external GitHub Actions or AWS without needing secrets:

Terminal window
# Inspect federated identity credentials across all App Registrations
Get-MgApplication | ForEach-Object {
$app = $_
Get-MgApplicationFederatedIdentityCredential -ApplicationId $app.Id | ForEach-Object {
[PSCustomObject]@{
AppName = $app.DisplayName
AppId = $app.AppId
Subject = $_.Subject
Issuer = $_.Issuer
}
}
} | Format-Table -AutoSize

☁️ Step 5: Azure Subscription Hierarchy & RBAC Scoping

Section titled “☁️ Step 5: Azure Subscription Hierarchy & RBAC Scoping”

Global Admins have the ability to elevate themselves to User Access Administrator at the Root Management Group (/), allowing them to grant themselves Owner rights across every Azure Subscription:

1. KQL Query: Detect Root Management Group Elevation (ElevateAccess)

Section titled “1. KQL Query: Detect Root Management Group Elevation (ElevateAccess)”
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName =~ "ElevateAccess"
| extend Actor = tostring(InitiatedBy.user.userPrincipalName)
| project TimeGenerated, Actor, Result, OperationName, TargetResources
Terminal window
# Verify Azure Subscription RBAC assignments
# Connect-AzAccount
Get-AzRoleAssignment | Where-Object { $_.RoleDefinitionName -in ("Owner", "User Access Administrator", "Contributor") } |
Select-Object DisplayName, SignInName, RoleDefinitionName, Scope |
Format-Table -AutoSize

🛡️ Step 6: Post-Breach Hardening Checklist

Section titled “🛡️ Step 6: Post-Breach Hardening Checklist”
  • Enforce FIDO2 / Passkeys for All Admins: Completely eliminate SMS, voice, and push-based MFA for administrative roles.
  • Validate Break-Glass Accounts: Confirm they have 30+ character random passwords, FIDO2 keys stored in physical safes, and alert rules on any logon.
  • Require PIM Approval & Justification: Disallow permanent assignment of high-privilege roles; require ticket numbers and secondary approval for elevation.
  • Enable Conditional Access Token Protection: Bind refresh tokens cryptographically to compliant Intune devices.