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

Threat Essentials - NRT User added to Microsoft Entra ID Privileged Groups

Back
Id0a627f29-f0dd-4924-be92-c3d6dac84367
RulenameThreat Essentials - NRT User added to Microsoft Entra ID Privileged Groups
DescriptionThis will alert when a user is added to any of the Privileged Groups.

For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.

For Administrator role permissions in Microsoft Entra ID please see https://docs.microsoft.com/azure/active-directory/users-groups-roles/directory-assign-admin-roles
SeverityMedium
TacticsPersistence
PrivilegeEscalation
TechniquesT1098
T1078
Required data connectorsAzureActiveDirectory
KindNRT
Query frequency1d
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SecurityThreatEssentialSolution/Analytic Rules/Threat_Essentials_NRT_UseraddedtoPrivilgedGroups.yaml
Version1.0.3
Arm template0a627f29-f0dd-4924-be92-c3d6dac84367.json
Deploy To Azure
let OperationList = dynamic(["Add member to role","Add member to role in PIM requested (permanent)"]);
let PrivilegedGroups = dynamic(["UserAccountAdmins","PrivilegedRoleAdmins","TenantAdmins"]);
AuditLogs
//| where LoggedByService =~ "Core Directory"
| where Category =~ "RoleManagement"
| where OperationName in~ (OperationList)
| mv-expand TargetResources
| extend modProps = parse_json(TargetResources).modifiedProperties
| mv-expand bagexpansion=array modProps
| evaluate bag_unpack(modProps)
| extend displayName = column_ifexists("displayName", "NotAvailable"), newValue = column_ifexists("newValue", "NotAvailable")
| where displayName =~ "Role.WellKnownObjectName"
| extend DisplayName = displayName, GroupName = replace('"','',newValue)
| extend initByApp = parse_json(InitiatedBy).app, initByUser = parse_json(InitiatedBy).user
| extend AppId = initByApp.appId,
InitiatedByDisplayName = case(isnotempty(initByApp.displayName), initByApp.displayName, isnotempty(initByUser.displayName), initByUser.displayName, "not available"),
ServicePrincipalId = tostring(initByApp.servicePrincipalId),
ServicePrincipalName = tostring(initByApp.servicePrincipalName),
UserId = initByUser.id,
UserIPAddress = initByUser.ipAddress,
UserRoles = initByUser.roles,
UserPrincipalName = tostring(initByUser.userPrincipalName),
TargetUserPrincipalName = tostring(TargetResources.userPrincipalName)
| where GroupName in~ (PrivilegedGroups)
// If you don't want to alert for operations from PIM, remove below filtering for MS-PIM.
//| where InitiatedByDisplayName != "MS-PIM"
| project TimeGenerated, AADOperationType, Category, OperationName, AADTenantId, AppId, InitiatedByDisplayName, ServicePrincipalId, ServicePrincipalName, DisplayName, GroupName, UserId, UserIPAddress, UserRoles, UserPrincipalName, TargetUserPrincipalName
| extend InitiatorAccount = case(isnotempty(ServicePrincipalName), ServicePrincipalName, isnotempty(ServicePrincipalId), ServicePrincipalId, isnotempty(UserPrincipalName), UserPrincipalName, "not available")
| extend InitiatorName = iif(InitiatorAccount has '@',tostring(split(InitiatorAccount,'@',0)[0]),"")
| extend InitiatorUPNSuffix = iif(InitiatorAccount has '@',tostring(split(InitiatorAccount,'@',1)[0]),"")
| extend InitiatorAadUserId = iif(InitiatorAccount !has '@',InitiatorAccount,"")
| extend TargetName = iif(TargetUserPrincipalName has '@',tostring(split(TargetUserPrincipalName,'@',0)[0]),"")
| extend TargetUPNSuffix = iif(TargetUserPrincipalName has '@',tostring(split(TargetUserPrincipalName,'@',1)[0]),"")
relevantTechniques:
- T1098
- T1078
name: Threat Essentials - NRT User added to Microsoft Entra ID Privileged Groups
requiredDataConnectors:
- dataTypes:
  - AuditLogs
  connectorId: AzureActiveDirectory
entityMappings:
- fieldMappings:
  - identifier: Name
    columnName: InitiatorName
  - identifier: UPNSuffix
    columnName: InitiatorUPNSuffix
  - identifier: AadUserId
    columnName: InitiatorAadUserId
  entityType: Account
- fieldMappings:
  - identifier: Name
    columnName: TargetName
  - identifier: UPNSuffix
    columnName: TargetUPNSuffix
  entityType: Account
triggerThreshold: 0
id: 0a627f29-f0dd-4924-be92-c3d6dac84367
tactics:
- Persistence
- PrivilegeEscalation
version: 1.0.3
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SecurityThreatEssentialSolution/Analytic Rules/Threat_Essentials_NRT_UseraddedtoPrivilgedGroups.yaml
queryPeriod: 14d
kind: NRT
tags:
- DEV-0537
queryFrequency: 1d
severity: Medium
status: Available
description: |
  'This will alert when a user is added to any of the Privileged Groups.
  For further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.
  For Administrator role permissions in Microsoft Entra ID please see https://docs.microsoft.com/azure/active-directory/users-groups-roles/directory-assign-admin-roles'  
query: |
  let OperationList = dynamic(["Add member to role","Add member to role in PIM requested (permanent)"]);
  let PrivilegedGroups = dynamic(["UserAccountAdmins","PrivilegedRoleAdmins","TenantAdmins"]);
  AuditLogs
  //| where LoggedByService =~ "Core Directory"
  | where Category =~ "RoleManagement"
  | where OperationName in~ (OperationList)
  | mv-expand TargetResources
  | extend modProps = parse_json(TargetResources).modifiedProperties
  | mv-expand bagexpansion=array modProps
  | evaluate bag_unpack(modProps)
  | extend displayName = column_ifexists("displayName", "NotAvailable"), newValue = column_ifexists("newValue", "NotAvailable")
  | where displayName =~ "Role.WellKnownObjectName"
  | extend DisplayName = displayName, GroupName = replace('"','',newValue)
  | extend initByApp = parse_json(InitiatedBy).app, initByUser = parse_json(InitiatedBy).user
  | extend AppId = initByApp.appId,
  InitiatedByDisplayName = case(isnotempty(initByApp.displayName), initByApp.displayName, isnotempty(initByUser.displayName), initByUser.displayName, "not available"),
  ServicePrincipalId = tostring(initByApp.servicePrincipalId),
  ServicePrincipalName = tostring(initByApp.servicePrincipalName),
  UserId = initByUser.id,
  UserIPAddress = initByUser.ipAddress,
  UserRoles = initByUser.roles,
  UserPrincipalName = tostring(initByUser.userPrincipalName),
  TargetUserPrincipalName = tostring(TargetResources.userPrincipalName)
  | where GroupName in~ (PrivilegedGroups)
  // If you don't want to alert for operations from PIM, remove below filtering for MS-PIM.
  //| where InitiatedByDisplayName != "MS-PIM"
  | project TimeGenerated, AADOperationType, Category, OperationName, AADTenantId, AppId, InitiatedByDisplayName, ServicePrincipalId, ServicePrincipalName, DisplayName, GroupName, UserId, UserIPAddress, UserRoles, UserPrincipalName, TargetUserPrincipalName
  | extend InitiatorAccount = case(isnotempty(ServicePrincipalName), ServicePrincipalName, isnotempty(ServicePrincipalId), ServicePrincipalId, isnotempty(UserPrincipalName), UserPrincipalName, "not available")
  | extend InitiatorName = iif(InitiatorAccount has '@',tostring(split(InitiatorAccount,'@',0)[0]),"")
  | extend InitiatorUPNSuffix = iif(InitiatorAccount has '@',tostring(split(InitiatorAccount,'@',1)[0]),"")
  | extend InitiatorAadUserId = iif(InitiatorAccount !has '@',InitiatorAccount,"")
  | extend TargetName = iif(TargetUserPrincipalName has '@',tostring(split(TargetUserPrincipalName,'@',0)[0]),"")
  | extend TargetUPNSuffix = iif(TargetUserPrincipalName has '@',tostring(split(TargetUserPrincipalName,'@',1)[0]),"")  
triggerOperator: gt
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/0a627f29-f0dd-4924-be92-c3d6dac84367')]",
      "kind": "NRT",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/0a627f29-f0dd-4924-be92-c3d6dac84367')]",
      "properties": {
        "alertRuleTemplateName": "0a627f29-f0dd-4924-be92-c3d6dac84367",
        "customDetails": null,
        "description": "'This will alert when a user is added to any of the Privileged Groups.\nFor further information on AuditLogs please see https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities.\nFor Administrator role permissions in Microsoft Entra ID please see https://docs.microsoft.com/azure/active-directory/users-groups-roles/directory-assign-admin-roles'\n",
        "displayName": "Threat Essentials - NRT User added to Microsoft Entra ID Privileged Groups",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "InitiatorName",
                "identifier": "Name"
              },
              {
                "columnName": "InitiatorUPNSuffix",
                "identifier": "UPNSuffix"
              },
              {
                "columnName": "InitiatorAadUserId",
                "identifier": "AadUserId"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "TargetName",
                "identifier": "Name"
              },
              {
                "columnName": "TargetUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/SecurityThreatEssentialSolution/Analytic Rules/Threat_Essentials_NRT_UseraddedtoPrivilgedGroups.yaml",
        "query": "let OperationList = dynamic([\"Add member to role\",\"Add member to role in PIM requested (permanent)\"]);\nlet PrivilegedGroups = dynamic([\"UserAccountAdmins\",\"PrivilegedRoleAdmins\",\"TenantAdmins\"]);\nAuditLogs\n//| where LoggedByService =~ \"Core Directory\"\n| where Category =~ \"RoleManagement\"\n| where OperationName in~ (OperationList)\n| mv-expand TargetResources\n| extend modProps = parse_json(TargetResources).modifiedProperties\n| mv-expand bagexpansion=array modProps\n| evaluate bag_unpack(modProps)\n| extend displayName = column_ifexists(\"displayName\", \"NotAvailable\"), newValue = column_ifexists(\"newValue\", \"NotAvailable\")\n| where displayName =~ \"Role.WellKnownObjectName\"\n| extend DisplayName = displayName, GroupName = replace('\"','',newValue)\n| extend initByApp = parse_json(InitiatedBy).app, initByUser = parse_json(InitiatedBy).user\n| extend AppId = initByApp.appId,\nInitiatedByDisplayName = case(isnotempty(initByApp.displayName), initByApp.displayName, isnotempty(initByUser.displayName), initByUser.displayName, \"not available\"),\nServicePrincipalId = tostring(initByApp.servicePrincipalId),\nServicePrincipalName = tostring(initByApp.servicePrincipalName),\nUserId = initByUser.id,\nUserIPAddress = initByUser.ipAddress,\nUserRoles = initByUser.roles,\nUserPrincipalName = tostring(initByUser.userPrincipalName),\nTargetUserPrincipalName = tostring(TargetResources.userPrincipalName)\n| where GroupName in~ (PrivilegedGroups)\n// If you don't want to alert for operations from PIM, remove below filtering for MS-PIM.\n//| where InitiatedByDisplayName != \"MS-PIM\"\n| project TimeGenerated, AADOperationType, Category, OperationName, AADTenantId, AppId, InitiatedByDisplayName, ServicePrincipalId, ServicePrincipalName, DisplayName, GroupName, UserId, UserIPAddress, UserRoles, UserPrincipalName, TargetUserPrincipalName\n| extend InitiatorAccount = case(isnotempty(ServicePrincipalName), ServicePrincipalName, isnotempty(ServicePrincipalId), ServicePrincipalId, isnotempty(UserPrincipalName), UserPrincipalName, \"not available\")\n| extend InitiatorName = iif(InitiatorAccount has '@',tostring(split(InitiatorAccount,'@',0)[0]),\"\")\n| extend InitiatorUPNSuffix = iif(InitiatorAccount has '@',tostring(split(InitiatorAccount,'@',1)[0]),\"\")\n| extend InitiatorAadUserId = iif(InitiatorAccount !has '@',InitiatorAccount,\"\")\n| extend TargetName = iif(TargetUserPrincipalName has '@',tostring(split(TargetUserPrincipalName,'@',0)[0]),\"\")\n| extend TargetUPNSuffix = iif(TargetUserPrincipalName has '@',tostring(split(TargetUserPrincipalName,'@',1)[0]),\"\")\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Persistence",
          "PrivilegeEscalation"
        ],
        "tags": [
          "DEV-0537"
        ],
        "techniques": [
          "T1078",
          "T1098"
        ],
        "templateVersion": "1.0.3",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}