Detection rules › Kusto

[Entra ID] Privilege Elevation Request Denied

Status
available
Severity
high
Time window
2h
Source
github.com/Azure/Azure-Sentinel

Detects denied privilege elevation requests in Microsoft Entra ID. Review the requester and the target workflow for possible abuse or misconfiguration.

MITRE ATT&CK coverage

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: aceee46e-8fb3-42d9-967a-aac637bcefd4
name: "[Entra ID] Privilege Elevation Request Denied"
version: 1.0.0
kind: Scheduled
description: |
  Detects denied privilege elevation requests in Microsoft Entra ID. Review the requester and the target workflow for possible abuse or misconfiguration.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
  - DefenseEvasion
  - InitialAccess
relevantTechniques:
  - T1078.004
query: |
  AuditLogs
  | where ActivityDisplayName =~ 'Add member to role request denied (PIM activation)'
  | mv-apply ResourceItem = TargetResources on
      (
      where ResourceItem.type =~ "Role"
      | extend Role = trim(@'"', tostring(ResourceItem.displayName))
      )
  | mv-apply ResourceItem = TargetResources on
      (
      where ResourceItem.type =~ "User"
      | extend TargetUserPrincipalName = trim(@'"', tostring(ResourceItem.userPrincipalName))
      )
  //| where ResultReason != "RoleAssignmentExists"
  | where isnotempty(InitiatedBy.user)
  | extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
  | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
  | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
  | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
  | extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
  | extend
      TargetName = tostring(split(TargetUserPrincipalName, '@', 0)[0]),
      TargetUPNSuffix = tostring(split(TargetUserPrincipalName, '@', 1)[0])
  | extend
      InitiatedByName = tostring(split(InitiatingUserPrincipalName, '@', 0)[0]),
      InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName, '@', 1)[0])
  | project-reorder
      TimeGenerated,
      TargetUserPrincipalName,
      Role,
      OperationName,
      Result,
      ResultDescription
  | extend
      InitiatingUser = tostring(InitiatedBy.user.userPrincipalName)
  | extend Source_Network_IPLocation = ""
  | project
          Alert_Category_en = "Entra ID",
      Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
      Alert_Name_en = "Privilege Elevation Request Denied",
      Alert_Description_en=strcat(
                           "At Taiwan time: ",
                           format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                           " in Entra ID:  ",
                           "",
                           ", detected PIM role activation failure: target user: ",
                           iff(isnotempty(TargetUserPrincipalName), TargetUserPrincipalName, "<NoTargetUser>"),
                           ", target role: ",
                           iff(isnotempty(Role), Role, "<NoRole>"),
                           ", initiator: ",
                           iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                           ", source IP: ",
                           iff(isnotempty(InitiatingIpAddress), InitiatingIpAddress, "<NoIP>"),
                           ", result: ",
                           iff(isnotempty(Result), Result, "<NoResult>"),
                           " (reason: ",
                           iff(isnotempty(ResultDescription), ResultDescription, "<NoReason>"),
                           ")."
                       ),
      Alert_TriageStep_en=strcat(
                          "1. Confirm the activation target: user: ",
                          iff(isnotempty(TargetUserPrincipalName), TargetUserPrincipalName, "<NoTargetUser>"),
                          ", role: ",
                          iff(isnotempty(Role), Role, "<NoRole>"),
                          "  is an expected PIM activation request.",
                          "2. Check the failure reason: ",
                          iff(isnotempty(ResultDescription), ResultDescription, "<NoReason>"),
                          ", and determine whether it was caused by MFA/Conditional Access/approval process/insufficient permissions.",
                          "3. Check source risk: ",
                          iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                          ", IP:",
                          iff(isnotempty(InitiatingIpAddress), InitiatingIpAddress, "<NoIP>"),
                          "  to determine whether it is a company named location/VPN/jump server, abnormal location, or uncommon source."
                      ),
      Alert_Containment_en=strcat(
                           "1. If determined to be a suspicious attempt, immediately restrict initiator: ",
                           iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                           " 's PIM activation permissions or suspend the scope of high-risk roles that can be activated. ",
                           "2. Immediately revoke suspicious sign-in tokens/sessions and force re-authentication (initiator: ",
                           iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                           "), and reset the password and re-register MFA if necessary.  ",
                           "3. Apply stricter Conditional Access to source IP: ",
                           iff(isnotempty(InitiatingIpAddress), InitiatingIpAddress, "<NoIP>"),
                           "  (named locations/compliant devices/MFA only), or block it first to stop repeated attempts.  ",
                           "4. If the failure was caused by the approval process/policy, suspend activation requests outside the change window and notify administrators for confirmation."
                       ),
      Alert_Remediation_en=strcat(
                           "1. Strengthen PIM governance: require approval and justification for high-privilege role activation, and limit the scope of activatable roles (least privilege/separation of duties).  ",
                           "2. Strengthen Conditional Access: enforce MFA, compliant devices, and named locations for PIM/management-plane operations, and directly block abnormal IPs/locations.  ",
                           "3. Establish alerts and automated response: set thresholds and automated SOAR for PIM activation failures (notification, IP blocking, account suspension).  "
                       ),
              Alert_Time_TW = datetime_utc_to_local(TimeGenerated, 'Asia/Taipei'),
      Alert_Time_UTC0 = TimeGenerated,
      Event_Action = OperationName,
      Event_Status = Result,
      Event_Description = ResultDescription,
      Source_Identity_FullName = InitiatingUser,
      Source_Identity_ID = InitiatingAadUserId,
      // Source_Identity_Name = InitiatingName,
      Source_Identity_Type = iff(isnotempty(InitiatingUser), "User", "Service"),
      Source_Network_IPAddress = InitiatingIpAddress,
      Target_Identity_FullName = TargetUserPrincipalName,
      Source_Resource_ID = InitiatingAppServicePrincipalId,
      Source_Resource_Name = InitiatingAppName,
      //Target_Identity_Name = InvitedUserName,
      Target_Identity_Type = "User",
      Target_Identity_DomainType = iff(TargetUserPrincipalName contains "EXT", 'External', 'Internal'),
      Target_Resource_ID = "",
      Target_Resource_Name = "Microsoft Entra ID",
      Target_Resource_Type = "Microsoft Entra ID"
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Source_Identity_FullName
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Target_Identity_FullName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: Source_Network_IPAddress

Stages and Predicates

Stage 1: source

AuditLogs

Stage 2: where

| where ActivityDisplayName =~ 'Add member to role request denied (PIM activation)'

Stage 3: kusto:mv-apply

| mv-apply ResourceItem = TargetResources on
    (
    where ResourceItem.type =~ "Role"
    | extend Role = trim(@'"', tostring(ResourceItem.displayName))
    )

Stage 4: kusto:mv-apply

| mv-apply ResourceItem = TargetResources on
    (
    where ResourceItem.type =~ "User"
    | extend TargetUserPrincipalName = trim(@'"', tostring(ResourceItem.userPrincipalName))
    )

Stage 5: where

| where isnotempty(InitiatedBy.user)

Stage 6: extend (7 consecutive steps)

| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend
    TargetName = tostring(split(TargetUserPrincipalName, '@', 0)[0]),
    TargetUPNSuffix = tostring(split(TargetUserPrincipalName, '@', 1)[0])
| extend
    InitiatedByName = tostring(split(InitiatingUserPrincipalName, '@', 0)[0]),
    InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName, '@', 1)[0])

Stage 7: project-reorder

| project-reorder
    TimeGenerated,
    TargetUserPrincipalName,
    Role,
    OperationName,
    Result,
    ResultDescription

Stage 8: extend

| extend
    InitiatingUser = tostring(InitiatedBy.user.userPrincipalName)

Stage 9: extend

| extend Source_Network_IPLocation = ""

Stage 10: project

| project
        Alert_Category_en = "Entra ID",
    Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
    Alert_Name_en = "Privilege Elevation Request Denied",
    Alert_Description_en=strcat(
                         "At Taiwan time: ",
                         format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                         " in Entra ID:  ",
                         "",
                         ", detected PIM role activation failure: target user: ",
                         iff(isnotempty(TargetUserPrincipalName), TargetUserPrincipalName, "<NoTargetUser>"),
                         ", target role: ",
                         iff(isnotempty(Role), Role, "<NoRole>"),
                         ", initiator: ",
                         iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                         ", source IP: ",
                         iff(isnotempty(InitiatingIpAddress), InitiatingIpAddress, "<NoIP>"),
                         ", result: ",
                         iff(isnotempty(Result), Result, "<NoResult>"),
                         " (reason: ",
                         iff(isnotempty(ResultDescription), ResultDescription, "<NoReason>"),
                         ")."
                     ),
    Alert_TriageStep_en=strcat(
                        "1. Confirm the activation target: user: ",
                        iff(isnotempty(TargetUserPrincipalName), TargetUserPrincipalName, "<NoTargetUser>"),
                        ", role: ",
                        iff(isnotempty(Role), Role, "<NoRole>"),
                        "  is an expected PIM activation request.",
                        "2. Check the failure reason: ",
                        iff(isnotempty(ResultDescription), ResultDescription, "<NoReason>"),
                        ", and determine whether it was caused by MFA/Conditional Access/approval process/insufficient permissions.",
                        "3. Check source risk: ",
                        iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                        ", IP:",
                        iff(isnotempty(InitiatingIpAddress), InitiatingIpAddress, "<NoIP>"),
                        "  to determine whether it is a company named location/VPN/jump server, abnormal location, or uncommon source."
                    ),
    Alert_Containment_en=strcat(
                         "1. If determined to be a suspicious attempt, immediately restrict initiator: ",
                         iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                         " 's PIM activation permissions or suspend the scope of high-risk roles that can be activated. ",
                         "2. Immediately revoke suspicious sign-in tokens/sessions and force re-authentication (initiator: ",
                         iff(isnotempty(InitiatingUser), InitiatingUser, "<NoInitiator>"),
                         "), and reset the password and re-register MFA if necessary.  ",
                         "3. Apply stricter Conditional Access to source IP: ",
                         iff(isnotempty(InitiatingIpAddress), InitiatingIpAddress, "<NoIP>"),
                         "  (named locations/compliant devices/MFA only), or block it first to stop repeated attempts.  ",
                         "4. If the failure was caused by the approval process/policy, suspend activation requests outside the change window and notify administrators for confirmation."
                     ),
    Alert_Remediation_en=strcat(
                         "1. Strengthen PIM governance: require approval and justification for high-privilege role activation, and limit the scope of activatable roles (least privilege/separation of duties).  ",
                         "2. Strengthen Conditional Access: enforce MFA, compliant devices, and named locations for PIM/management-plane operations, and directly block abnormal IPs/locations.  ",
                         "3. Establish alerts and automated response: set thresholds and automated SOAR for PIM activation failures (notification, IP blocking, account suspension).  "
                     ),
            Alert_Time_TW = datetime_utc_to_local(TimeGenerated, 'Asia/Taipei'),
    Alert_Time_UTC0 = TimeGenerated,
    Event_Action = OperationName,
    Event_Status = Result,
    Event_Description = ResultDescription,
    Source_Identity_FullName = InitiatingUser,
    Source_Identity_ID = InitiatingAadUserId,
    Source_Identity_Type = iff(isnotempty(InitiatingUser), "User", "Service"),
    Source_Network_IPAddress = InitiatingIpAddress,
    Target_Identity_FullName = TargetUserPrincipalName,
    Source_Resource_ID = InitiatingAppServicePrincipalId,
    Source_Resource_Name = InitiatingAppName,
    Target_Identity_Type = "User",
    Target_Identity_DomainType = iff(TargetUserPrincipalName contains "EXT", 'External', 'Internal'),
    Target_Resource_ID = "",
    Target_Resource_Name = "Microsoft Entra ID",
    Target_Resource_Type = "Microsoft Entra ID"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
Alert_Category_enproject
Alert_Containment_enproject
Alert_Description_enproject
Alert_Name_enproject
Alert_Remediation_enproject
Alert_SubCategory_enproject
Alert_Time_TWproject
Alert_Time_UTC0project
Alert_TriageStep_enproject
Event_Actionproject
Event_Descriptionproject
Event_Statusproject
Source_Identity_FullNameproject
Source_Identity_IDproject
Source_Identity_Typeproject
Source_Network_IPAddressproject
Source_Resource_IDproject
Source_Resource_Nameproject
Target_Identity_DomainTypeproject
Target_Identity_FullNameproject
Target_Identity_Typeproject
Target_Resource_IDproject
Target_Resource_Nameproject
Target_Resource_Typeproject