Back
Idc24252d1-d98c-48db-b2f9-4cb37dd2208a
RulenameDelinea - Privilege escalation detected (role / permission changes)
DescriptionDetects role assignments, permission or right grants, admin grants and elevation events that

the acting user has not performed against the same target in the prior 14 days. Only grant-type actions (added /

assigned / granted / elevated / approved / created / updated) against role, permission,

privilege, admin or entitlement objects are considered; removals and read-only events are

excluded, and plain group-membership or ownership events are not matched because they make up

most routine directory activity. One alert is produced per actor per run - review the

FieldChanges column for the before/after of each change.

Tune ’escalationObjects’ and ‘grantVerbs’ to your tenant’s event taxonomy, and use

‘excludedEventNames’, ’excludedServiceTypes’ and ’excludedActors’ to silence known-benign

events, services (for example directory synchronization) and service accounts.
SeverityHigh
TacticsPrivilegeEscalation
Persistence
TechniquesT1098
T1078
Required data connectorsDelineaPlatformConnector
KindScheduled
Query frequency1h
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Delinea%20Platform/Analytic%20Rules/DelineaPrivilegeEscalation.yaml
Version1.2.0
Arm templatec24252d1-d98c-48db-b2f9-4cb37dd2208a.json
Deploy To Azure
let detectionWindow = 1h;   // must equal queryFrequency
let baseline = 14d;         // must stay <= queryPeriod
// TUNING 1 of 2: the event name must name a privileged object AND end in a grant-type verb.
// Bare 'member', 'assign', 'owner' and 'group' are deliberately absent - they match
// routine directory user and group activity.
let escalationObjects = @"(?i)role|permission|privilege|admin|rights|entitlement|elevat|justintime";
let grantVerbs = @"(?i)(Added|Assigned|Granted|Elevated|Approved|Created|Updated|Modified|Changed)$";
// TUNING 2 of 2: tenant specific exclusions, filled in at deployment time. Exact match,
// case insensitive. Leave empty if unused.
let excludedEventNames = dynamic([]);
//   e.g. dynamic(["Delinea.Vault.Group.Updated"]);
let excludedServiceTypes = dynamic([]);
//   e.g. dynamic(["Identity"]);
let excludedActors = dynamic([]);
//   e.g. dynamic(["svc-directory-sync"]);
let grants =
    DelineaAuditEvents_CL
    | where TimeGenerated > ago(baseline)
    | extend IngestedAt = ingestion_time()
    | extend d = parse_json(RawData)
    | extend EventName = tostring(coalesce(d.eventType.name, d.eventType.internalName))
    | where EventName matches regex escalationObjects and EventName matches regex grantVerbs
    | extend Actor = tostring(coalesce(d.actor.name, d.actor.email, d.actor.displayName)),
             ServiceType = tostring(d.service.type),
             Target = tostring(coalesce(d.target.displayName, d.target.name)),
             // ID first - display names can change. Empty when the event carries no target.
             TargetKey = tostring(coalesce(d.target.id, d.target.platformId, d.target.name, d.target.displayName)),
             FieldChanges = tostring(d.fieldChanges),
             RawSrcIP = tostring(d.source.host.network.ipAddress)
    | where isnotempty(Actor)
    | where not(EventName in~ (excludedEventNames))
        and not(ServiceType in~ (excludedServiceTypes))
        and not(Actor in~ (excludedActors))
    // '0.0.0.0' is the placeholder reported for events that have no network origin.
    | extend SrcIP = iff(RawSrcIP in ("", "0.0.0.0"), "", RawSrcIP);
// Batch by ingestion time so a late arriving event is still evaluated exactly once.
// The baseline is keyed on the target too, so repeating a grant on the same target is
// suppressed but the same grant against a new target still alerts.
let priorGrants = grants | where IngestedAt <= ago(detectionWindow) | distinct Actor, EventName, TargetKey;
grants
| where IngestedAt > ago(detectionWindow)
| join kind=leftanti priorGrants on Actor, EventName, TargetKey
| summarize Changes = count(), EventNames = make_set(EventName, 20),
            Targets = make_set_if(Target, isnotempty(Target), 20),
            FieldChanges = make_list_if(FieldChanges, isnotempty(FieldChanges), 10),
            SourceIPs = make_set_if(SrcIP, isnotempty(SrcIP), 20),
            SrcIP = take_anyif(SrcIP, isnotempty(SrcIP)),
            StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Actor
| project StartTime, EndTime, Actor, Changes, EventNames, Targets, FieldChanges, SourceIPs, SrcIP
| sort by StartTime desc
customDetails:
  EventNames: EventNames
  Targets: Targets
  Changes: Changes
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: Actor
    identifier: Name
- entityType: IP
  fieldMappings:
  - columnName: SrcIP
    identifier: Address
tactics:
- PrivilegeEscalation
- Persistence
status: Available
queryPeriod: 14d
queryFrequency: 1h
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    matchingMethod: Selected
    reopenClosedIncident: false
    lookbackDuration: P1D
    groupByEntities:
    - Account
requiredDataConnectors:
- connectorId: DelineaPlatformConnector
  dataTypes:
  - DelineaAuditEvents_CL
severity: High
relevantTechniques:
- T1098
- T1078
name: Delinea - Privilege escalation detected (role / permission changes)
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Delinea%20Platform/Analytic%20Rules/DelineaPrivilegeEscalation.yaml
eventGroupingSettings:
  aggregationKind: AlertPerResult
description: |
  Detects role assignments, permission or right grants, admin grants and elevation events that
  the acting user has not performed against the same target in the prior 14 days. Only grant-type actions (added /
  assigned / granted / elevated / approved / created / updated) against role, permission,
  privilege, admin or entitlement objects are considered; removals and read-only events are
  excluded, and plain group-membership or ownership events are not matched because they make up
  most routine directory activity. One alert is produced per actor per run - review the
  FieldChanges column for the before/after of each change.
  Tune 'escalationObjects' and 'grantVerbs' to your tenant's event taxonomy, and use
  'excludedEventNames', 'excludedServiceTypes' and 'excludedActors' to silence known-benign
  events, services (for example directory synchronization) and service accounts.
triggerOperator: gt
id: c24252d1-d98c-48db-b2f9-4cb37dd2208a
version: 1.2.0
triggerThreshold: 0
query: |
  let detectionWindow = 1h;   // must equal queryFrequency
  let baseline = 14d;         // must stay <= queryPeriod
  // TUNING 1 of 2: the event name must name a privileged object AND end in a grant-type verb.
  // Bare 'member', 'assign', 'owner' and 'group' are deliberately absent - they match
  // routine directory user and group activity.
  let escalationObjects = @"(?i)role|permission|privilege|admin|rights|entitlement|elevat|justintime";
  let grantVerbs = @"(?i)(Added|Assigned|Granted|Elevated|Approved|Created|Updated|Modified|Changed)$";
  // TUNING 2 of 2: tenant specific exclusions, filled in at deployment time. Exact match,
  // case insensitive. Leave empty if unused.
  let excludedEventNames = dynamic([]);
  //   e.g. dynamic(["Delinea.Vault.Group.Updated"]);
  let excludedServiceTypes = dynamic([]);
  //   e.g. dynamic(["Identity"]);
  let excludedActors = dynamic([]);
  //   e.g. dynamic(["svc-directory-sync"]);
  let grants =
      DelineaAuditEvents_CL
      | where TimeGenerated > ago(baseline)
      | extend IngestedAt = ingestion_time()
      | extend d = parse_json(RawData)
      | extend EventName = tostring(coalesce(d.eventType.name, d.eventType.internalName))
      | where EventName matches regex escalationObjects and EventName matches regex grantVerbs
      | extend Actor = tostring(coalesce(d.actor.name, d.actor.email, d.actor.displayName)),
               ServiceType = tostring(d.service.type),
               Target = tostring(coalesce(d.target.displayName, d.target.name)),
               // ID first - display names can change. Empty when the event carries no target.
               TargetKey = tostring(coalesce(d.target.id, d.target.platformId, d.target.name, d.target.displayName)),
               FieldChanges = tostring(d.fieldChanges),
               RawSrcIP = tostring(d.source.host.network.ipAddress)
      | where isnotempty(Actor)
      | where not(EventName in~ (excludedEventNames))
          and not(ServiceType in~ (excludedServiceTypes))
          and not(Actor in~ (excludedActors))
      // '0.0.0.0' is the placeholder reported for events that have no network origin.
      | extend SrcIP = iff(RawSrcIP in ("", "0.0.0.0"), "", RawSrcIP);
  // Batch by ingestion time so a late arriving event is still evaluated exactly once.
  // The baseline is keyed on the target too, so repeating a grant on the same target is
  // suppressed but the same grant against a new target still alerts.
  let priorGrants = grants | where IngestedAt <= ago(detectionWindow) | distinct Actor, EventName, TargetKey;
  grants
  | where IngestedAt > ago(detectionWindow)
  | join kind=leftanti priorGrants on Actor, EventName, TargetKey
  | summarize Changes = count(), EventNames = make_set(EventName, 20),
              Targets = make_set_if(Target, isnotempty(Target), 20),
              FieldChanges = make_list_if(FieldChanges, isnotempty(FieldChanges), 10),
              SourceIPs = make_set_if(SrcIP, isnotempty(SrcIP), 20),
              SrcIP = take_anyif(SrcIP, isnotempty(SrcIP)),
              StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Actor
  | project StartTime, EndTime, Actor, Changes, EventNames, Targets, FieldChanges, SourceIPs, SrcIP
  | sort by StartTime desc
{
  "$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/c24252d1-d98c-48db-b2f9-4cb37dd2208a')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/c24252d1-d98c-48db-b2f9-4cb37dd2208a')]",
      "properties": {
        "alertRuleTemplateName": "c24252d1-d98c-48db-b2f9-4cb37dd2208a",
        "customDetails": {
          "Changes": "Changes",
          "EventNames": "EventNames",
          "Targets": "Targets"
        },
        "description": "Detects role assignments, permission or right grants, admin grants and elevation events that\nthe acting user has not performed against the same target in the prior 14 days. Only grant-type actions (added /\nassigned / granted / elevated / approved / created / updated) against role, permission,\nprivilege, admin or entitlement objects are considered; removals and read-only events are\nexcluded, and plain group-membership or ownership events are not matched because they make up\nmost routine directory activity. One alert is produced per actor per run - review the\nFieldChanges column for the before/after of each change.\nTune 'escalationObjects' and 'grantVerbs' to your tenant's event taxonomy, and use\n'excludedEventNames', 'excludedServiceTypes' and 'excludedActors' to silence known-benign\nevents, services (for example directory synchronization) and service accounts.\n",
        "displayName": "Delinea - Privilege escalation detected (role / permission changes)",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Actor",
                "identifier": "Name"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "SrcIP",
                "identifier": "Address"
              }
            ]
          }
        ],
        "eventGroupingSettings": {
          "aggregationKind": "AlertPerResult"
        },
        "incidentConfiguration": {
          "createIncident": true,
          "groupingConfiguration": {
            "enabled": true,
            "groupByEntities": [
              "Account"
            ],
            "lookbackDuration": "P1D",
            "matchingMethod": "Selected",
            "reopenClosedIncident": false
          }
        },
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Delinea%20Platform/Analytic%20Rules/DelineaPrivilegeEscalation.yaml",
        "query": "let detectionWindow = 1h;   // must equal queryFrequency\nlet baseline = 14d;         // must stay <= queryPeriod\n// TUNING 1 of 2: the event name must name a privileged object AND end in a grant-type verb.\n// Bare 'member', 'assign', 'owner' and 'group' are deliberately absent - they match\n// routine directory user and group activity.\nlet escalationObjects = @\"(?i)role|permission|privilege|admin|rights|entitlement|elevat|justintime\";\nlet grantVerbs = @\"(?i)(Added|Assigned|Granted|Elevated|Approved|Created|Updated|Modified|Changed)$\";\n// TUNING 2 of 2: tenant specific exclusions, filled in at deployment time. Exact match,\n// case insensitive. Leave empty if unused.\nlet excludedEventNames = dynamic([]);\n//   e.g. dynamic([\"Delinea.Vault.Group.Updated\"]);\nlet excludedServiceTypes = dynamic([]);\n//   e.g. dynamic([\"Identity\"]);\nlet excludedActors = dynamic([]);\n//   e.g. dynamic([\"svc-directory-sync\"]);\nlet grants =\n    DelineaAuditEvents_CL\n    | where TimeGenerated > ago(baseline)\n    | extend IngestedAt = ingestion_time()\n    | extend d = parse_json(RawData)\n    | extend EventName = tostring(coalesce(d.eventType.name, d.eventType.internalName))\n    | where EventName matches regex escalationObjects and EventName matches regex grantVerbs\n    | extend Actor = tostring(coalesce(d.actor.name, d.actor.email, d.actor.displayName)),\n             ServiceType = tostring(d.service.type),\n             Target = tostring(coalesce(d.target.displayName, d.target.name)),\n             // ID first - display names can change. Empty when the event carries no target.\n             TargetKey = tostring(coalesce(d.target.id, d.target.platformId, d.target.name, d.target.displayName)),\n             FieldChanges = tostring(d.fieldChanges),\n             RawSrcIP = tostring(d.source.host.network.ipAddress)\n    | where isnotempty(Actor)\n    | where not(EventName in~ (excludedEventNames))\n        and not(ServiceType in~ (excludedServiceTypes))\n        and not(Actor in~ (excludedActors))\n    // '0.0.0.0' is the placeholder reported for events that have no network origin.\n    | extend SrcIP = iff(RawSrcIP in (\"\", \"0.0.0.0\"), \"\", RawSrcIP);\n// Batch by ingestion time so a late arriving event is still evaluated exactly once.\n// The baseline is keyed on the target too, so repeating a grant on the same target is\n// suppressed but the same grant against a new target still alerts.\nlet priorGrants = grants | where IngestedAt <= ago(detectionWindow) | distinct Actor, EventName, TargetKey;\ngrants\n| where IngestedAt > ago(detectionWindow)\n| join kind=leftanti priorGrants on Actor, EventName, TargetKey\n| summarize Changes = count(), EventNames = make_set(EventName, 20),\n            Targets = make_set_if(Target, isnotempty(Target), 20),\n            FieldChanges = make_list_if(FieldChanges, isnotempty(FieldChanges), 10),\n            SourceIPs = make_set_if(SrcIP, isnotempty(SrcIP), 20),\n            SrcIP = take_anyif(SrcIP, isnotempty(SrcIP)),\n            StartTime = min(TimeGenerated), EndTime = max(TimeGenerated) by Actor\n| project StartTime, EndTime, Actor, Changes, EventNames, Targets, FieldChanges, SourceIPs, SrcIP\n| sort by StartTime desc\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P14D",
        "severity": "High",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Persistence",
          "PrivilegeEscalation"
        ],
        "techniques": [
          "T1078",
          "T1098"
        ],
        "templateVersion": "1.2.0",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}