← Back to SOC feed Coverage →

Privileged Identity Management role activation outside business hours

kql MEDIUM Azure-Sentinel
T1078.004
AuditLogs
exploithuntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-05-26T11:00:01Z · Confidence: medium

Hunt Hypothesis

Privileged Identity Management role activations occurring outside business hours may indicate unauthorized privilege escalation by an adversary exploiting a compromised account. SOC teams should proactively hunt for this behavior in Azure Sentinel to detect potential lateral movement or persistence tactics leveraging elevated privileges.

KQL Query

let timeframe = 14d;
let BusinessHourStart = 7;   // 07:00 UTC - adjust to your organization's timezone
let BusinessHourEnd   = 20;  // 20:00 UTC
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where Category =~ "RoleManagement"
| where OperationName in~ (
      "Add member to role (PIM activation)",
      "Add member to role. (PIM activation)",
      "Add eligible member to role. (PIM activation)"
  )
| where Result =~ "success"
// Extract the activating user from TargetResources - PIM logs the subject as target
| extend ActivatingUserUpn = tostring(TargetResources[0].userPrincipalName)
| extend ActivatingUserId  = tostring(TargetResources[0].id)
// Extract role name from the second target resource entry
| extend RoleName = iff(
      isnotempty(tostring(TargetResources[1].displayName)),
      tostring(TargetResources[1].displayName),
      tostring(TargetResources[0].displayName))
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorIp  = iff(
      isnotempty(tostring(InitiatedBy.user.ipAddress)),
      tostring(InitiatedBy.user.ipAddress),
      tostring(InitiatedBy.app.ipAddress))
| extend HourOfDay   = hourofday(TimeGenerated)
| extend DayOfWeekNum = toint(dayofweek(TimeGenerated) / 1d)
| extend IsWeekend              = DayOfWeekNum == 0 or DayOfWeekNum == 6
| extend IsOutsideBusinessHours = HourOfDay < BusinessHourStart or HourOfDay >= BusinessHourEnd
| where IsWeekend or IsOutsideBusinessHours
| extend AccountName      = iff(ActivatingUserUpn has "@",
      tostring(split(ActivatingUserUpn, "@")[0]), ActivatingUserUpn)
| extend AccountUPNSuffix = iff(ActivatingUserUpn has "@",
      tostring(split(ActivatingUserUpn, "@")[1]), "")
| project
    TimeGenerated,
    RoleName,
    ActivatingUserUpn,
    AccountName,
    AccountUPNSuffix,
    ActivatingUserId,
    ActorIp,
    HourOfDay,
    DayOfWeekNum,
    IsWeekend,
    IsOutsideBusinessHours,
    CorrelationId
| sort by TimeGenerated desc

Analytic Rule Definition

id: 8a1fb81a-b672-4e8c-9799-9c7fd2431688
name: Privileged Identity Management role activation outside business hours
description: Identifies Privileged Identity Management role activations outside business hours or on weekends, which may indicate unauthorized privilege escalation by a compromised account exploiting off-hours monitoring gaps.
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
tactics:
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1078.004
query: |
  let timeframe = 14d;
  let BusinessHourStart = 7;   // 07:00 UTC - adjust to your organization's timezone
  let BusinessHourEnd   = 20;  // 20:00 UTC
  AuditLogs
  | where TimeGenerated >= ago(timeframe)
  | where Category =~ "RoleManagement"
  | where OperationName in~ (
        "Add member to role (PIM activation)",
        "Add member to role. (PIM activation)",
        "Add eligible member to role. (PIM activation)"
    )
  | where Result =~ "success"
  // Extract the activating user from TargetResources - PIM logs the subject as target
  | extend ActivatingUserUpn = tostring(TargetResources[0].userPrincipalName)
  | extend ActivatingUserId  = tostring(TargetResources[0].id)
  // Extract role name from the second target resource entry
  | extend RoleName = iff(
        isnotempty(tostring(TargetResources[1].displayName)),
        tostring(TargetResources[1].displayName),
        tostring(TargetResources[0].displayName))
  | extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
  | extend ActorIp  = iff(
        isnotempty(tostring(InitiatedBy.user.ipAddress)),
        tostring(InitiatedBy.user.ipAddress),
        tostring(InitiatedBy.app.ipAddress))
  | extend HourOfDay   = hourofday(TimeGenerated)
  | extend DayOfWeekNum = toint(dayofweek(TimeGenerated) / 1d)
  | extend IsWeekend              = DayOfWeekNum == 0 or DayOfWeekNum == 6
  | extend IsOutsideBusinessHours = HourOfDay < BusinessHourStart or HourOfDay >= BusinessHourEnd
  | where IsWeekend or IsOutsideBusinessHours
  | extend AccountName      = iff(ActivatingUserUpn has "@",
        tostring(split(ActivatingUserUpn, "@")[0]), ActivatingUserUpn)
  | extend AccountUPNSuffix = iff(ActivatingUserUpn has "@",
        tostring(split(ActivatingUserUpn, "@")[1]), "")
  | project
      TimeGenerated,
      RoleName,
      ActivatingUserUpn,
      AccountName,
      AccountUPNSuffix,
      ActivatingUserId,
      ActorIp,
      HourOfDay,
      DayOfWeekNum,
      IsWeekend,
      IsOutsideBusinessHours,
      CorrelationId
  | sort by TimeGenerated desc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActivatingUserUpn
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ActorIp
version: 1.0.0
metadata:
    source:
        kind: Community
    autho

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/AuditLogs/PIMRoleActivationOutsideBusinessHours.yaml