RUNBOOK // Entra ID Global Admin Compromise & Tier-0 Cloud Lockdown
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”# Connect to Microsoft Graph PowerShell using a verified administrative accountConnect-MgGraph -Scopes "User.ReadWrite.All", "RoleManagement.ReadWrite.Directory"
$compromisedAdmin = "compromised.admin@company.com"
# 1. Immediately revoke all active browser and desktop session tokensRevoke-MgUserSignInSession -UserId $compromisedAdminWrite-Host "[+] Active sessions invalidated for $compromisedAdmin." -ForegroundColor Green
# 2. Disable the account temporarily during investigationUpdate-MgUser -UserId $compromisedAdmin -AccountEnabled $falseWrite-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”- Verify whether the logon was legitimate or malicious via
SigninLogs. - Rotate FIDO2 security keys and password for the emergency account immediately.
- 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 desc2. PowerShell: Remove Unauthorized Permanent Role Assignments
Section titled “2. PowerShell: Remove Unauthorized Permanent Role Assignments”# 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 desc2. Audit Named Locations (Trusted IP Bypasses)
Section titled “2. Audit Named Locations (Trusted IP Bypasses)”# Verify no rogue IP ranges were added to Trusted Named LocationsGet-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 desc2. 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:
# Inspect federated identity credentials across all App RegistrationsGet-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, TargetResources2. PowerShell: Audit Subscription Owners
Section titled “2. PowerShell: Audit Subscription Owners”# Verify Azure Subscription RBAC assignments# Connect-AzAccountGet-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.