Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Admin promotion after Role Management Application Permission Grant

Back
Idf80d951a-eddc-4171-b9d0-d616bb83efdc
RulenameAdmin promotion after Role Management Application Permission Grant
DescriptionThis rule looks for a service principal being granted the Microsoft Graph RoleManagement.ReadWrite.Directory (application) permission before being used to add an Azure AD object or user account to an Admin directory role (i.e. Global Administrators).

This is a known attack path that is usually abused when a service principal already has the AppRoleAssignment.ReadWrite.All permission granted. This permission Allows an app to manage permission grants for application permissions to any API.

A service principal can promote itself or other service principals to admin roles (i.e. Global Administrators). This would be considered a privilege escalation technique.

Ref : https://docs.microsoft.com/graph/permissions-reference#role-management-permissions, https://docs.microsoft.com/graph/api/directoryrole-post-members?view=graph-rest-1.0&tabs=http
SeverityHigh
TacticsPrivilegeEscalation
Persistence
TechniquesT1098.003
T1078.004
Required data connectorsAzureActiveDirectory
KindScheduled
Query frequency2h
Query period2h
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/AdminPromoAfterRoleMgmtAppPermissionGrant.yaml
Version1.0.2
Arm templatef80d951a-eddc-4171-b9d0-d616bb83efdc.json
Deploy To Azure
AuditLogs
| where LoggedByService =~ "Core Directory"
| where Category =~ "ApplicationManagement"
| where AADOperationType =~ "Assign"
| where ActivityDisplayName =~ "Add app role assignment to service principal"
| mv-expand TargetResources
| mv-expand TargetResources.modifiedProperties
| extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)
| where displayName_ =~ "AppRole.Value"
| extend AppRole = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))
| where AppRole has "RoleManagement.ReadWrite.Directory"
| extend InitiatingApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend Initiator = iif(isnotempty(InitiatingApp), InitiatingApp, tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName))
| extend Target = tostring(parse_json(tostring(TargetResources.modifiedProperties[4].newValue)))
| extend TargetId = tostring(parse_json(tostring(TargetResources.modifiedProperties[3].newValue)))
| project TimeGenerated, OperationName, Initiator, Target, TargetId, Result
| join kind=innerunique (
  AuditLogs
  | where LoggedByService =~ "Core Directory"
  | where Category =~ "RoleManagement"
  | where AADOperationType in ("Assign", "AssignEligibleRole")
  | where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role")
  | mv-expand TargetResources
  | mv-expand TargetResources.modifiedProperties
  | extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)
  | where displayName_ =~ "Role.DisplayName"
  | extend RoleName = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))
  | where RoleName contains "Admin"
  | extend Initiator = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
  | extend InitiatorId = tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalId)
  | extend TargetUser = tostring(TargetResources.userPrincipalName)
  | extend Target = iif(isnotempty(TargetUser), TargetUser, tostring(TargetResources.displayName))
  | extend TargetType = tostring(TargetResources.type)
  | extend TargetId = tostring(TargetResources.id)
  | project TimeGenerated, OperationName,  RoleName, Initiator, InitiatorId, Target, TargetId, TargetType, Result
) on $left.TargetId == $right.InitiatorId
| extend TimeRoleMgGrant = TimeGenerated, TimeAdminPromo = TimeGenerated1, ServicePrincipal = Initiator1, ServicePrincipalId = InitiatorId,
  TargetObject = Target1, TargetObjectId = TargetId1, TargetObjectType = TargetType
| where TimeRoleMgGrant < TimeAdminPromo
| project TimeRoleMgGrant, TimeAdminPromo, RoleName, ServicePrincipal, ServicePrincipalId, TargetObject, TargetObjectId, TargetObjectType
severity: High
triggerThreshold: 0
query: |
  AuditLogs
  | where LoggedByService =~ "Core Directory"
  | where Category =~ "ApplicationManagement"
  | where AADOperationType =~ "Assign"
  | where ActivityDisplayName =~ "Add app role assignment to service principal"
  | mv-expand TargetResources
  | mv-expand TargetResources.modifiedProperties
  | extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)
  | where displayName_ =~ "AppRole.Value"
  | extend AppRole = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))
  | where AppRole has "RoleManagement.ReadWrite.Directory"
  | extend InitiatingApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
  | extend Initiator = iif(isnotempty(InitiatingApp), InitiatingApp, tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName))
  | extend Target = tostring(parse_json(tostring(TargetResources.modifiedProperties[4].newValue)))
  | extend TargetId = tostring(parse_json(tostring(TargetResources.modifiedProperties[3].newValue)))
  | project TimeGenerated, OperationName, Initiator, Target, TargetId, Result
  | join kind=innerunique (
    AuditLogs
    | where LoggedByService =~ "Core Directory"
    | where Category =~ "RoleManagement"
    | where AADOperationType in ("Assign", "AssignEligibleRole")
    | where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role")
    | mv-expand TargetResources
    | mv-expand TargetResources.modifiedProperties
    | extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)
    | where displayName_ =~ "Role.DisplayName"
    | extend RoleName = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))
    | where RoleName contains "Admin"
    | extend Initiator = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
    | extend InitiatorId = tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalId)
    | extend TargetUser = tostring(TargetResources.userPrincipalName)
    | extend Target = iif(isnotempty(TargetUser), TargetUser, tostring(TargetResources.displayName))
    | extend TargetType = tostring(TargetResources.type)
    | extend TargetId = tostring(TargetResources.id)
    | project TimeGenerated, OperationName,  RoleName, Initiator, InitiatorId, Target, TargetId, TargetType, Result
  ) on $left.TargetId == $right.InitiatorId
  | extend TimeRoleMgGrant = TimeGenerated, TimeAdminPromo = TimeGenerated1, ServicePrincipal = Initiator1, ServicePrincipalId = InitiatorId,
    TargetObject = Target1, TargetObjectId = TargetId1, TargetObjectType = TargetType
  | where TimeRoleMgGrant < TimeAdminPromo
  | project TimeRoleMgGrant, TimeAdminPromo, RoleName, ServicePrincipal, ServicePrincipalId, TargetObject, TargetObjectId, TargetObjectType  
queryFrequency: 2h
requiredDataConnectors:
- connectorId: AzureActiveDirectory
  dataTypes:
  - AuditLogs
id: f80d951a-eddc-4171-b9d0-d616bb83efdc
version: 1.0.2
name: Admin promotion after Role Management Application Permission Grant
kind: Scheduled
status: Available
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/AdminPromoAfterRoleMgmtAppPermissionGrant.yaml
queryPeriod: 2h
relevantTechniques:
- T1098.003
- T1078.004
triggerOperator: gt
tactics:
- PrivilegeEscalation
- Persistence
tags:
- SimuLand
description: |
  'This rule looks for a service principal being granted the Microsoft Graph RoleManagement.ReadWrite.Directory (application) permission before being used to add an Azure AD object or user account to an Admin directory role (i.e. Global Administrators).
  This is a known attack path that is usually abused when a service principal already has the AppRoleAssignment.ReadWrite.All permission granted. This permission Allows an app to manage permission grants for application permissions to any API.
  A service principal can promote itself or other service principals to admin roles (i.e. Global Administrators). This would be considered a privilege escalation technique.
  Ref : https://docs.microsoft.com/graph/permissions-reference#role-management-permissions, https://docs.microsoft.com/graph/api/directoryrole-post-members?view=graph-rest-1.0&tabs=http'  
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: ServicePrincipal
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: TargetObject
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/f80d951a-eddc-4171-b9d0-d616bb83efdc')]",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/f80d951a-eddc-4171-b9d0-d616bb83efdc')]",
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
      "kind": "Scheduled",
      "apiVersion": "2022-11-01",
      "properties": {
        "displayName": "Admin promotion after Role Management Application Permission Grant",
        "description": "'This rule looks for a service principal being granted the Microsoft Graph RoleManagement.ReadWrite.Directory (application) permission before being used to add an Azure AD object or user account to an Admin directory role (i.e. Global Administrators).\nThis is a known attack path that is usually abused when a service principal already has the AppRoleAssignment.ReadWrite.All permission granted. This permission Allows an app to manage permission grants for application permissions to any API.\nA service principal can promote itself or other service principals to admin roles (i.e. Global Administrators). This would be considered a privilege escalation technique.\nRef : https://docs.microsoft.com/graph/permissions-reference#role-management-permissions, https://docs.microsoft.com/graph/api/directoryrole-post-members?view=graph-rest-1.0&tabs=http'\n",
        "severity": "High",
        "enabled": true,
        "query": "AuditLogs\n| where LoggedByService =~ \"Core Directory\"\n| where Category =~ \"ApplicationManagement\"\n| where AADOperationType =~ \"Assign\"\n| where ActivityDisplayName =~ \"Add app role assignment to service principal\"\n| mv-expand TargetResources\n| mv-expand TargetResources.modifiedProperties\n| extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)\n| where displayName_ =~ \"AppRole.Value\"\n| extend AppRole = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))\n| where AppRole has \"RoleManagement.ReadWrite.Directory\"\n| extend InitiatingApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)\n| extend Initiator = iif(isnotempty(InitiatingApp), InitiatingApp, tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName))\n| extend Target = tostring(parse_json(tostring(TargetResources.modifiedProperties[4].newValue)))\n| extend TargetId = tostring(parse_json(tostring(TargetResources.modifiedProperties[3].newValue)))\n| project TimeGenerated, OperationName, Initiator, Target, TargetId, Result\n| join kind=innerunique (\n  AuditLogs\n  | where LoggedByService =~ \"Core Directory\"\n  | where Category =~ \"RoleManagement\"\n  | where AADOperationType in (\"Assign\", \"AssignEligibleRole\")\n  | where ActivityDisplayName has_any (\"Add eligible member to role\", \"Add member to role\")\n  | mv-expand TargetResources\n  | mv-expand TargetResources.modifiedProperties\n  | extend displayName_ = tostring(TargetResources_modifiedProperties.displayName)\n  | where displayName_ =~ \"Role.DisplayName\"\n  | extend RoleName = tostring(parse_json(tostring(TargetResources_modifiedProperties.newValue)))\n  | where RoleName contains \"Admin\"\n  | extend Initiator = tostring(parse_json(tostring(InitiatedBy.app)).displayName)\n  | extend InitiatorId = tostring(parse_json(tostring(InitiatedBy.app)).servicePrincipalId)\n  | extend TargetUser = tostring(TargetResources.userPrincipalName)\n  | extend Target = iif(isnotempty(TargetUser), TargetUser, tostring(TargetResources.displayName))\n  | extend TargetType = tostring(TargetResources.type)\n  | extend TargetId = tostring(TargetResources.id)\n  | project TimeGenerated, OperationName,  RoleName, Initiator, InitiatorId, Target, TargetId, TargetType, Result\n) on $left.TargetId == $right.InitiatorId\n| extend TimeRoleMgGrant = TimeGenerated, TimeAdminPromo = TimeGenerated1, ServicePrincipal = Initiator1, ServicePrincipalId = InitiatorId,\n  TargetObject = Target1, TargetObjectId = TargetId1, TargetObjectType = TargetType\n| where TimeRoleMgGrant < TimeAdminPromo\n| project TimeRoleMgGrant, TimeAdminPromo, RoleName, ServicePrincipal, ServicePrincipalId, TargetObject, TargetObjectId, TargetObjectType\n",
        "queryFrequency": "PT2H",
        "queryPeriod": "PT2H",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0,
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "PrivilegeEscalation",
          "Persistence"
        ],
        "techniques": [
          "T1098.003",
          "T1078.004"
        ],
        "alertRuleTemplateName": "f80d951a-eddc-4171-b9d0-d616bb83efdc",
        "customDetails": null,
        "entityMappings": [
          {
            "fieldMappings": [
              {
                "columnName": "ServicePrincipal",
                "identifier": "FullName"
              }
            ],
            "entityType": "Account"
          },
          {
            "fieldMappings": [
              {
                "columnName": "TargetObject",
                "identifier": "FullName"
              }
            ],
            "entityType": "Account"
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Active Directory/Analytic Rules/AdminPromoAfterRoleMgmtAppPermissionGrant.yaml",
        "tags": [
          "SimuLand"
        ],
        "templateVersion": "1.0.2",
        "status": "Available"
      }
    }
  ]
}