Detection rules › Kusto

User impersonation by Identity Protection alerts

Severity
medium
Time window
1d
Group by
AlertName, AlertSeverity, AlertTime, ProductName, SourceIpAddress, ipAddress
Source
github.com/Azure/Azure-Sentinel

'This detection focuses on identifying user-related events involving IAM roles, groups, user access, and password changes. It examines instances where the user's IP address matches and alerts generated by Identity Protection share the same IP address. The analysis occurs within a time window of 1 hour, helping to flag potential cases of user impersonation.'

MITRE ATT&CK coverage

TacticTechniques
Privilege Escalation

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: 11c3d541-5fa5-49df-8218-d1c98584473b
name: User impersonation by Identity Protection alerts
description: |
  'This detection focuses on identifying user-related events involving IAM roles, groups, user access, and password changes. It examines instances where the user's IP address matches and alerts generated by Identity Protection share the same IP address. The analysis occurs within a time window of 1 hour, helping to flag potential cases of user impersonation.'
severity: Medium
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
  - connectorId: AzureActiveDirectoryIdentityProtection
    dataTypes:
      - SecurityAlert (IPC)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1134
query: |
 // Retrieve SecurityAlerts generated within the last day
  SecurityAlert 
  // Filter alerts for Azure Active Directory Identity Protection and High severity
  | where ProductName has "Azure Active Directory Identity Protection"
  | where AlertSeverity == "High"
  // Extract IP address entities from the 'Entities' field
  | extend ipAddress = extract(@'\b(?:\d{1,3}\.){3}\d{1,3}\b', 0, Entities)
  // Filter out alerts without IP address entities
  | where isnotempty(ipAddress)
  // Summarize entities per unique combination of attributes
  | summarize make_set(Entities)
      by
      AlertTime = TimeGenerated,
      ipAddress,
      AlertName,
      ProductName,
      AlertSeverity
  // Perform an inner join with AWS CloudTrail events
  | join kind=inner (
      AWSCloudTrail
      | where isempty(ErrorMessage)
      | extend UserType = tostring(parse_json(RequestParameters).userType) 
      | where EventName in~ ("CreateRole", "DeleteRole", "CreateUser", "CreateAccessKey", "DeleteAccessKey", "CreateGroup", "AddUserToGroup", "ChangePassword", "DeleteGroup", "DeleteUser", "RemoveUserFromGroup", "CreateVirtualMFADevice", "DeleteLoginProfile") 
      | summarize
          make_set(RequestParameters),
          make_set(ResponseElements)
          by
          SourceIpAddress,
          UserIdentityArn,
          UserIdentityType,
          EventName,
          EventTime = TimeGenerated
      )
      on $left.ipAddress == $right.SourceIpAddress  
  // Filter results based on temporal correlation
  | where AlertTime between ((EventTime - 1h) .. (EventTime + 1h))
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
   AWSUser: UserIdentityArn
   AlertIp : ipAddress
   AlertName: AlertName
kind: Scheduled
version: 1.0.0

Stages and Predicates

Stage 1: source

SecurityAlert

Stage 2: where

| where ProductName has "Azure Active Directory Identity Protection"

Stage 3: where

| where AlertSeverity == "High"

Stage 4: extend

| extend ipAddress = extract(@'\b(?:\d{1,3}\.){3}\d{1,3}\b', 0, Entities)

Stage 5: where

| where isnotempty(ipAddress)

Stage 6: summarize

| summarize make_set(Entities)
     by
     AlertTime = TimeGenerated,
     ipAddress,
     AlertName,
     ProductName,
     AlertSeverity

Stage 7: join

| join kind=inner (
     AWSCloudTrail
     | where isempty(ErrorMessage)
     | extend UserType = tostring(parse_json(RequestParameters).userType) 
     | where EventName in~ ("CreateRole", "DeleteRole", "CreateUser", "CreateAccessKey", "DeleteAccessKey", "CreateGroup", "AddUserToGroup", "ChangePassword", "DeleteGroup", "DeleteUser", "RemoveUserFromGroup", "CreateVirtualMFADevice", "DeleteLoginProfile") 
     | summarize
         make_set(RequestParameters),
         make_set(ResponseElements)
         by
         SourceIpAddress,
         UserIdentityArn,
         UserIdentityType,
         EventName,
         EventTime = TimeGenerated
     )
     on $left.ipAddress == $right.SourceIpAddress

Stage 8: where

| where AlertTime between ((EventTime - 1h) .. (EventTime + 1h))

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AlertSeverityeq
  • High
field:"AlertSeverity" kind:eq value:"High"
ErrorMessageis_null
  • (no value, null check)
field:"aws::errorMessage" kind:is_null
EventNamein
  • AddUserToGroup
  • ChangePassword
  • CreateAccessKey
  • CreateGroup
  • CreateRole
  • CreateUser
  • CreateVirtualMFADevice
  • DeleteAccessKey
  • DeleteGroup
  • DeleteLoginProfile
  • DeleteRole
  • DeleteUser
  • RemoveUserFromGroup
field:"aws::eventName" kind:in
ProductNamematch
  • Azure Active Directory Identity Protection transforms: term
field:"ProductName" kind:match value:"Azure Active Directory Identity Protection"
ipAddressis_not_null
  • (no value, null check)
field:"ipAddress" kind:is_not_null

Output fields

These fields are emitted when the rule matches.

FieldSource
AlertNamesummarize
AlertSeveritysummarize
AlertTimesummarize
ProductNamesummarize
ipAddresssummarize