Detection rules › Kusto

Kusto rules: 1n

[Entra ID] Privileged Role Assigned to User

#
Status
available
Severity
high
Time window
1h
Group by
InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, InitiatingUserPrincipalName, Initiator, OperationName, Result, RoleName, Target
Source
github.com/Azure/Azure-Sentinel

Detects when a user is assigned a privileged role in Microsoft Entra ID. This may indicate unauthorized privilege escalation and should be validated promptly.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: fad0f8b9-a0a5-430a-9a94-049a1144bf54
name: "[Entra ID] Privileged Role Assigned to User"
version: 1.0.0
kind: Scheduled
description: |
  Detects when a user is assigned a privileged role in Microsoft Entra ID. This may indicate unauthorized privilege escalation and should be validated promptly.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - DefenseEvasion
  - PrivilegeEscalation
  - InitialAccess
relevantTechniques:
  - T1078.004
query: |
  AuditLogs
  | where Category =~ "RoleManagement"
  | where AADOperationType in ("Assign", "AssignEligibleRole", "CreateRequestGrantedRole", "CreateRequestPermanentEligibleRole", "CreateRequestPermanentGrantedRole")
  | where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role")
  | mv-apply TargetResourceSubject = TargetResources on
      (
      where TargetResourceSubject.type in~ ("User", "ServicePrincipal")
      | extend
          Target = iff(TargetResourceSubject.type =~ "ServicePrincipal", tostring(TargetResourceSubject.displayName), tostring(TargetResourceSubject.userPrincipalName)),
          subjectProps = TargetResourceSubject.modifiedProperties
      )
  | mv-apply TargetResourceRole = TargetResources on
      (
      // mimic modifiedProperties so we can use the same logic to get the role name regardless of where it comes from
      where TargetResourceRole.type in~ ("Role")
      | extend roleProps = pack_array(bag_pack("displayName", "Role.DisplayName", "newValue", TargetResourceRole.displayName))
      )
  | mv-apply Property = iff(array_length(subjectProps) > 0, subjectProps, roleProps) on
      (
      where Property.displayName =~ "Role.DisplayName"
      | extend RoleName = trim('"', tostring(Property.newValue))
      )
  | where RoleName contains "Admin"
  | 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 Initiator = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName)
  // Comment below to alert for PIM activations
  | where Initiator != "MS-PIM"
  | summarize
      by
      bin(TimeGenerated, 1h),
      OperationName,
      RoleName,
      Target,
      Initiator,
      InitiatingUserPrincipalName,
      InitiatingAadUserId,
      InitiatingAppName,
      InitiatingAppServicePrincipalId,
      InitiatingIpAddress,
      Result
  | extend
      TargetName = tostring(split(Target, '@', 0)[0]),
      TargetUPNSuffix = tostring(split(Target, '@', 1)[0]),
      InitiatorName = tostring(split(InitiatingUserPrincipalName, '@', 0)[0]),
      InitiatorUPNSuffix = tostring(split(InitiatingUserPrincipalName, '@', 1)[0])
  | extend
      Source_Network_IPLocation = "",
      ActivityType=OperationName
  | project
          Alert_Category_en = "Entra ID",
      Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
      Alert_Name_en = "User Assigned a Privileged Role",
      Alert_Description_en = strcat(
                             "At Taiwan time: ",
                             format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                             "in the Microsoft Entra ID tenant",
                             ", operator: ",
                             iff(isnotempty(Initiator), Initiator, "<UnknownInitiator>"),
                             ", from IP: ",
                             InitiatingIpAddress,
                             ", assigned privileged role  ",
                             tostring(RoleName),
                             "  to user  ",
                             iff(isnotempty(Target), Target, "<NoTargetUser>"),
                             ". This behavior is a high-risk permission change and must be validated immediately."
                         ),
      Alert_TriageStep_en = strcat(
                            "1. Confirm whether the privileged role assignment was performed through an Entra ID PIM-approved change process.",
                            "2. Confirm with the operator: ",
                            iff(isnotempty(Initiator), Initiator, "<UnknownInitiator>"),
                            "  whether this was performed by the account owner.",
                            "3. Check whether the source IP and geolocation are abnormal or not a company public IP."
                        ),
      Alert_Containment_en = strcat(
                             "1. If this is determined to be an unauthorized role assignment, immediately revoke the privileged role from target user  ",
                             iff(isnotempty(Target), Target, "<NoTargetUser>"),
                             " 's privileged role (",
                             tostring(RoleName),
                             "), and restore the state to before the change.",
                             "2. Immediately restrict or suspend the operator that performed the role assignment (user or application), revoke sign-in tokens, and force MFA/password reset as needed.  ",
                             "3. If the source is an application (",
                             iff(isnotempty(InitiatingAppName), InitiatingAppName, "<NoServicePrincipal>"),
                             "), immediately disable the Service Principal and revoke high-privilege API consent and credentials/secrets."
                         ),
      Alert_Remediation_en = strcat(
                             "1. Allow privileged role access only through Entra ID PIM.",
                             "2. Enforce Conditional Access and MFA for role management and PIM operations, allowing them only from managed devices and named locations.",
                             "3. Establish real-time alerts and automated response (SOAR) for privileged role changes to detect abnormal role assignments immediately. "
                         ),
              Alert_Time_TW = datetime_utc_to_local(TimeGenerated, "Asia/Taipei"),
      Alert_Time_UTC0 = TimeGenerated,
      Event_Action = ActivityType,
      Event_Status = Result,
      //Event_Description = ActivityDisplayName,
      Source_Identity_FullName = iff(isnotempty(Initiator), Initiator, InitiatingAppName),
      //Source_Identity_ID = ActorID,
      Source_Identity_Type = iff(isnotempty(InitiatingUserPrincipalName), "User", "Service"),
      Source_Network_IPAddress = InitiatingIpAddress,
      Source_Network_IPLocation = Source_Network_IPLocation,
      //Source_Resource_Name = InitiatingAppName,
      Target_Identity_FullName = Target,
      Target_Identity_DomainType = iff(Target contains "EXT", 'External', 'Internal'),
      Target_Identity_Type = "User",
      Target_Resource_ID = "",
      Target_Resource_Type = "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 Category =~ "RoleManagement"

Stage 3: where

| where AADOperationType in ("Assign", "AssignEligibleRole", "CreateRequestGrantedRole", "CreateRequestPermanentEligibleRole", "CreateRequestPermanentGrantedRole")

Stage 4: where

| where ActivityDisplayName has_any ("Add eligible member to role", "Add member to role")

Stage 5: kusto:mv-apply

| mv-apply TargetResourceSubject = TargetResources on
    (
    where TargetResourceSubject.type in~ ("User", "ServicePrincipal")
    | extend
        Target = iff(TargetResourceSubject.type =~ "ServicePrincipal", tostring(TargetResourceSubject.displayName), tostring(TargetResourceSubject.userPrincipalName)),
        subjectProps = TargetResourceSubject.modifiedProperties
    )

Stage 6: kusto:mv-apply

| mv-apply TargetResourceRole = TargetResources on
    (
    where TargetResourceRole.type in~ ("Role")
    | extend roleProps = pack_array(bag_pack("displayName", "Role.DisplayName", "newValue", TargetResourceRole.displayName))
    )

Stage 7: kusto:mv-apply

| mv-apply Property = iff(array_length(subjectProps) > 0, subjectProps, roleProps) on
    (
    where Property.displayName =~ "Role.DisplayName"
    | extend RoleName = trim('"', tostring(Property.newValue))
    )

Stage 8: where

| where RoleName contains "Admin"

Stage 9: extend (6 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 Initiator = iif(isnotempty(InitiatingAppName), InitiatingAppName, InitiatingUserPrincipalName)

Stage 10: where

| where Initiator != "MS-PIM"

Stage 11: summarize

| summarize
    by
    bin(TimeGenerated, 1h),
    OperationName,
    RoleName,
    Target,
    Initiator,
    InitiatingUserPrincipalName,
    InitiatingAadUserId,
    InitiatingAppName,
    InitiatingAppServicePrincipalId,
    InitiatingIpAddress,
    Result

Stage 12: extend

| extend
    TargetName = tostring(split(Target, '@', 0)[0]),
    TargetUPNSuffix = tostring(split(Target, '@', 1)[0]),
    InitiatorName = tostring(split(InitiatingUserPrincipalName, '@', 0)[0]),
    InitiatorUPNSuffix = tostring(split(InitiatingUserPrincipalName, '@', 1)[0])

Stage 13: extend

| extend
    Source_Network_IPLocation = "",
    ActivityType=OperationName

Stage 14: project

| project
        Alert_Category_en = "Entra ID",
    Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
    Alert_Name_en = "User Assigned a Privileged Role",
    Alert_Description_en = strcat(
                           "At Taiwan time: ",
                           format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                           "in the Microsoft Entra ID tenant",
                           ", operator: ",
                           iff(isnotempty(Initiator), Initiator, "<UnknownInitiator>"),
                           ", from IP: ",
                           InitiatingIpAddress,
                           ", assigned privileged role  ",
                           tostring(RoleName),
                           "  to user  ",
                           iff(isnotempty(Target), Target, "<NoTargetUser>"),
                           ". This behavior is a high-risk permission change and must be validated immediately."
                       ),
    Alert_TriageStep_en = strcat(
                          "1. Confirm whether the privileged role assignment was performed through an Entra ID PIM-approved change process.",
                          "2. Confirm with the operator: ",
                          iff(isnotempty(Initiator), Initiator, "<UnknownInitiator>"),
                          "  whether this was performed by the account owner.",
                          "3. Check whether the source IP and geolocation are abnormal or not a company public IP."
                      ),
    Alert_Containment_en = strcat(
                           "1. If this is determined to be an unauthorized role assignment, immediately revoke the privileged role from target user  ",
                           iff(isnotempty(Target), Target, "<NoTargetUser>"),
                           " 's privileged role (",
                           tostring(RoleName),
                           "), and restore the state to before the change.",
                           "2. Immediately restrict or suspend the operator that performed the role assignment (user or application), revoke sign-in tokens, and force MFA/password reset as needed.  ",
                           "3. If the source is an application (",
                           iff(isnotempty(InitiatingAppName), InitiatingAppName, "<NoServicePrincipal>"),
                           "), immediately disable the Service Principal and revoke high-privilege API consent and credentials/secrets."
                       ),
    Alert_Remediation_en = strcat(
                           "1. Allow privileged role access only through Entra ID PIM.",
                           "2. Enforce Conditional Access and MFA for role management and PIM operations, allowing them only from managed devices and named locations.",
                           "3. Establish real-time alerts and automated response (SOAR) for privileged role changes to detect abnormal role assignments immediately. "
                       ),
            Alert_Time_TW = datetime_utc_to_local(TimeGenerated, "Asia/Taipei"),
    Alert_Time_UTC0 = TimeGenerated,
    Event_Action = ActivityType,
    Event_Status = Result,
    Source_Identity_FullName = iff(isnotempty(Initiator), Initiator, InitiatingAppName),
    Source_Identity_Type = iff(isnotempty(InitiatingUserPrincipalName), "User", "Service"),
    Source_Network_IPAddress = InitiatingIpAddress,
    Source_Network_IPLocation = Source_Network_IPLocation,
    Target_Identity_FullName = Target,
    Target_Identity_DomainType = iff(Target contains "EXT", 'External', 'Internal'),
    Target_Identity_Type = "User",
    Target_Resource_ID = "",
    Target_Resource_Type = "Entra ID"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AADOperationTypein
  • Assign corpus 6 (kusto 6)
  • AssignEligibleRole corpus 5 (kusto 5)
  • CreateRequestGrantedRole corpus 2 (kusto 2)
  • CreateRequestPermanentEligibleRole corpus 2 (kusto 2)
  • CreateRequestPermanentGrantedRole corpus 2 (kusto 2)
field:"AADOperationType" kind:in
ActivityDisplayNamematch
  • Add eligible member to role transforms: term corpus 9 (kusto 9)
  • Add member to role transforms: term corpus 9 (kusto 9)
field:"azure_ad::activity_display_name" kind:match
Categoryeq
  • RoleManagement corpus 17 (kusto 16, sigma 1)
field:"Category" kind:eq value:"RoleManagement"
Initiatorne
  • MS-PIM
field:"Initiator" kind:ne value:"MS-PIM"
Property.displayNameeq
  • Role.DisplayName transforms: kusto_dynamic_path
field:"Property.displayName" kind:eq value:"Role.DisplayName"
RoleNamecontains
  • Admin
field:"RoleName" kind:contains value:"Admin"
TargetResourceRole.typein
  • Role transforms: kusto_dynamic_path
field:"TargetResourceRole.type" kind:in value:"Role"
TargetResourceSubject.typein
  • ServicePrincipal transforms: kusto_dynamic_path
  • User transforms: kusto_dynamic_path
field:"TargetResourceSubject.type" kind:in

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_Statusproject
Source_Identity_FullNameproject
Source_Identity_Typeproject
Source_Network_IPAddressproject
Source_Network_IPLocationproject
Target_Identity_DomainTypeproject
Target_Identity_FullNameproject
Target_Identity_Typeproject
Target_Resource_IDproject
Target_Resource_Typeproject

ADFS DKM Master Key Export

#
Severity
medium
Time window
1d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

Identifies an export of the ADFS DKM Master Key from Active Directory. References: https://blogs.microsoft.com/on-the-issues/2020/12/13/customers-protect-nation-state-cyberattacks/, https://cloud.google.com/blog/topics/threat-intelligence/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor To understand further the details behind this detection, please review the details in the original PR and subequent PR update to this: https://github.com/Azure/Azure-Sentinel/pull/1562#issue-551542469 https://github.com/Azure/Azure-Sentinel/pull/1512#issue-543053339

MITRE ATT&CK Coverage

TacticTechniques
Collection

Telemetry Coverage

Rule Body

id: 18e6a87e-9d06-4a4e-8b59-3469cd49552d
name: ADFS DKM Master Key Export
description: | 
  'Identifies an export of the ADFS DKM Master Key from Active Directory.
  References: https://blogs.microsoft.com/on-the-issues/2020/12/13/customers-protect-nation-state-cyberattacks/, 
  https://cloud.google.com/blog/topics/threat-intelligence/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor
  To understand further the details behind this detection, please review the details in the original PR and subequent PR update to this:
  https://github.com/Azure/Azure-Sentinel/pull/1562#issue-551542469
  https://github.com/Azure/Azure-Sentinel/pull/1512#issue-543053339
  '
severity: Medium 
requiredDataConnectors: 
  - connectorId: SecurityEvents 
    dataTypes: 
      - SecurityEvents 
  - connectorId: MicrosoftThreatProtection 
    dataTypes: 
      - DeviceEvents 
  - connectorId: WindowsSecurityEvents
    dataTypes: 
      - SecurityEvents 
  - connectorId: WindowsForwardedEvents
    dataTypes: 
      - WindowsEvent 
queryFrequency: 1d 
queryPeriod: 1d
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - Collection
relevantTechniques:
  - T1005
tags:
  - Solorigate
  - NOBELIUM
query:  |
  (union isfuzzy=true 
  (SecurityEvent 
  | where EventID == 4662 // You need to create a SACL on the ADFS Policy Store DKM group for this event to be created. 
  | where ObjectServer == 'DS'
  | where OperationType == 'Object Access'
  //| where ObjectName contains '<GUID of ADFS Policy Store DKM Group object' This is unique to the domain. Check description for more details.
  | where ObjectType contains '5cb41ed0-0e4c-11d0-a286-00aa003049e2' // Contact Class
  | where Properties contains '8d3bca50-1d7e-11d0-a081-00aa006c33ed' // Picture Attribute - Ldap-Display-Name: thumbnailPhoto
  | extend AccountName = SubjectUserName, AccountDomain = SubjectDomainName
  | extend timestamp = TimeGenerated, DeviceName = Computer
  ),
  ( WindowsEvent 
  | where EventID == 4662 // You need to create a SACL on the ADFS Policy Store DKM group for this event to be created. 
  | where EventData has_all('Object Access', '5cb41ed0-0e4c-11d0-a286-00aa003049e2','8d3bca50-1d7e-11d0-a081-00aa006c33ed') 
  | extend ObjectServer = tostring(EventData.ObjectServer)
  | where ObjectServer == 'DS'
  | extend OperationType = tostring(EventData.OperationType)
  | where OperationType == 'Object Access'
  //| where ObjectName contains '<GUID of ADFS Policy Store DKM Group object' This is unique to the domain. Check description for more details.
  | extend ObjectType = tostring(EventData.ObjectType)
  | where ObjectType contains '5cb41ed0-0e4c-11d0-a286-00aa003049e2' // Contact Class
  | extend Properties = tostring(EventData.Properties)
  | where Properties contains '8d3bca50-1d7e-11d0-a081-00aa006c33ed' // Picture Attribute - Ldap-Display-Name: thumbnailPhoto
  | extend AccountName = tostring(EventData.SubjectUserName), AccountDomain = tostring(EventData.SubjectDomainName)
  | extend timestamp = TimeGenerated, DeviceName = Computer
  ),
  (DeviceEvents
  | where ActionType =~ "LdapSearch"
  | where AdditionalFields.AttributeList contains "thumbnailPhoto"
  | where AdditionalFields.DistinguishedName contains "CN=ADFS,CN=Microsoft,CN=Program Data" // Filter results to show only hits related to the ADFS AD container
  | extend timestamp = TimeGenerated, AccountName = InitiatingProcessAccountName, AccountDomain = InitiatingProcessAccountDomain
  )
  )
  | extend Account = strcat(AccountDomain, "\\", AccountName)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
  - entityType: AzureResource
    fieldMappings:
      - identifier: ResourceId
        columnName: _ResourceId
version: 1.2.2
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Identity" ]

Stages and Predicates

Union isfuzzy=true (3 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, WindowsEvent, DeviceEvents

Leg 1: SecurityEvent

SecurityEvent 
| where EventID == 4662
| where ObjectServer == 'DS'
| where OperationType == 'Object Access'
| where ObjectType contains '5cb41ed0-0e4c-11d0-a286-00aa003049e2'
| where Properties contains '8d3bca50-1d7e-11d0-a081-00aa006c33ed'
| extend AccountName = SubjectUserName, AccountDomain = SubjectDomainName
| extend timestamp = TimeGenerated, DeviceName = Computer

Leg 2: WindowsEvent

WindowsEvent 
| where EventID == 4662
| where EventData has_all('Object Access', '5cb41ed0-0e4c-11d0-a286-00aa003049e2','8d3bca50-1d7e-11d0-a081-00aa006c33ed') 
| extend ObjectServer = tostring(EventData.ObjectServer)
| where ObjectServer == 'DS'
| extend OperationType = tostring(EventData.OperationType)
| where OperationType == 'Object Access'
| extend ObjectType = tostring(EventData.ObjectType)
| where ObjectType contains '5cb41ed0-0e4c-11d0-a286-00aa003049e2'
| extend Properties = tostring(EventData.Properties)
| where Properties contains '8d3bca50-1d7e-11d0-a081-00aa006c33ed'
| extend AccountName = tostring(EventData.SubjectUserName), AccountDomain = tostring(EventData.SubjectDomainName)
| extend timestamp = TimeGenerated, DeviceName = Computer

Leg 3: DeviceEvents

DeviceEvents
| where ActionType =~ "LdapSearch"
| where AdditionalFields.AttributeList contains "thumbnailPhoto"
| where AdditionalFields.DistinguishedName contains "CN=ADFS,CN=Microsoft,CN=Program Data"
| extend timestamp = TimeGenerated, AccountName = InitiatingProcessAccountName, AccountDomain = InitiatingProcessAccountDomain

Applied to the Combined Result

| extend Account = strcat(AccountDomain, "\\", AccountName)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypeeq
  • LdapSearch
field:"ActionType" kind:eq value:"LdapSearch"
AdditionalFields.AttributeListcontains
  • thumbnailPhoto transforms: kusto_dynamic_path
field:"AdditionalFields.AttributeList" kind:contains value:"thumbnailPhoto"
AdditionalFields.DistinguishedNamecontains
  • CN=ADFS,CN=Microsoft,CN=Program Data transforms: kusto_dynamic_path
field:"AdditionalFields.DistinguishedName" kind:contains value:"CN=ADFS,CN=Microsoft,CN=Program Data"
EventDatamatch
  • 5cb41ed0-0e4c-11d0-a286-00aa003049e2 transforms: term
  • 8d3bca50-1d7e-11d0-a081-00aa006c33ed transforms: term
  • Object Access transforms: term
field:"EventData" kind:match
EventIDeq
  • 4662 corpus 6 (kusto 3, elastic 2, chronicle 1)
field:"EventID" kind:eq value:"4662"
ObjectServereq
  • DS corpus 2 (kusto 2)
field:"ObjectServer" kind:eq value:"DS"
ObjectTypecontains
  • 5cb41ed0-0e4c-11d0-a286-00aa003049e2
field:"ObjectType" kind:contains value:"5cb41ed0-0e4c-11d0-a286-00aa003049e2"
OperationTypeeq
  • Object Access
field:"OperationType" kind:eq value:"Object Access"
Propertiescontains
  • 8d3bca50-1d7e-11d0-a081-00aa006c33ed
field:"Properties" kind:contains value:"8d3bca50-1d7e-11d0-a081-00aa006c33ed"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountDomainextend
AccountNameextend
DeviceNameextend
timestampextend
ObjectServerextend
OperationTypeextend
ObjectTypeextend
Propertiesextend
Accountextend

ApexOne - Spyware with failed response

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

'Detects spyware with failed response.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: c92d9fe4-67b6-11ec-90d6-0242ac120003
name: ApexOne - Spyware with failed response
description: |
  'Detects spyware with failed response.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
query: |
  TMApexOneEvent
  | where EventMessage has "Spyware Detected"
  | where ActionResult has_any ("No action", "Unable to")
  | extend IPCustomEntity = SrcIpAddr, AccountCustomEntity = DstUserName
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

TMApexOneEvent

Stage 2: where

| where EventMessage has "Spyware Detected"

Stage 3: where

| where ActionResult has_any ("No action", "Unable to")

Stage 4: extend

| extend IPCustomEntity = SrcIpAddr, AccountCustomEntity = DstUserName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionResultmatch
  • No action transforms: term
  • Unable to transforms: term
field:"ActionResult" kind:match
EventMessagematch
  • Spyware Detected transforms: term
field:"EventMessage" kind:match value:"Spyware Detected"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend
IPCustomEntityextend

BloodHound Attack Path Finding - Large Default Groups With WriteOwner Privileges

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - Large Default Groups With WriteOwner Privileges

Rule Body

id: f2f15e17-c056-487b-ad96-13128c8a6024
name: BloodHound Attack Path Finding - Large Default Groups With WriteOwner Privileges
description: BloodHound Attack Path Finding - Large Default Groups With WriteOwner
  Privileges
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsWriteOwner"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "LargeDefaultGroupsWriteOwner"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsWriteOwner
field:"Finding" kind:eq value:"LargeDefaultGroupsWriteOwner"

BloodHound Attack Path Finding - Non Tier Zero Principals With ADCS ESC10 Scenario A Privileges

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - Non Tier Zero Principals With ADCS ESC10 Scenario A Privileges

Rule Body

id: 14adaa12-cad0-41ca-b848-f536723e4117
name: BloodHound Attack Path Finding - Non Tier Zero Principals With ADCS ESC10 Scenario
  A Privileges
description: BloodHound Attack Path Finding - Non Tier Zero Principals With ADCS ESC10
  Scenario A Privileges
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "T0ADCSESC10a"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "T0ADCSESC10a"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • T0ADCSESC10a
field:"Finding" kind:eq value:"T0ADCSESC10a"

BloodHound Attack Path Finding - VM Contributor Role on Tier Zero System

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - VM Contributor Role on Tier Zero System

Rule Body

id: 6356d718-5a61-4f74-b8a4-f93b0b721e9a
name: BloodHound Attack Path Finding - VM Contributor Role on Tier Zero System
description: BloodHound Attack Path Finding - VM Contributor Role on Tier Zero System
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "AzureT0VMContributor"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "AzureT0VMContributor"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • AzureT0VMContributor
field:"Finding" kind:eq value:"AzureT0VMContributor"

Claroty - Asset Down

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

'Triggers asset is down.'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: fd6e3416-0421-4166-adb9-186e555a7008
name: Claroty - Asset Down
description: |
  'Triggers asset is down.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1529
query: |
  ClarotyEvent
  | where EventOriginalType has 'Asset Down' or EventType has 'Asset Down'
  | project TimeGenerated, DstIpAddr
  | extend IPCustomEntity = DstIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

ClarotyEvent

Stage 2: where

| where EventOriginalType has 'Asset Down' or EventType has 'Asset Down'

Stage 3: project

| project TimeGenerated, DstIpAddr

Stage 4: extend

| extend IPCustomEntity = DstIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstIpAddrproject
TimeGeneratedproject
IPCustomEntityextend

Contrast Exploits

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

'Creates Incidents for Exploit events sourced from the Contrast Protect agent.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: e1abb6ed-be18-40fd-be58-3d3d84041daf
name: Contrast Exploits
description: |
  'Creates Incidents for Exploit events sourced from the Contrast Protect agent.'
severity: High
requiredDataConnectors:
  - connectorId: ContrastProtect
    dataTypes:
      - CommonSecurityLog
  - connectorId: ContrastProtectAma
    dataTypes:
      - CommonSecurityLog
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
  - Exfiltration
relevantTechniques:
  - T1566
query: |
  let extract_data=(a:string, k:string) {
    parse_urlquery(replace(@';', @'&', a))["Query Parameters"][k]
  };

  CommonSecurityLog 
  | where DeviceVendor == "Contrast Security"
  | where AdditionalExtensions contains "EXPLOITED"
  | extend DeviceProduct
  | extend SourceIP
  | extend DeviceVersion
  | extend Activity
  | extend ApplicationProtocol
  | extend RequestURL
  | extend RequestMethod
  | extend Rule = extract_data(AdditionalExtensions, 'pri')
customDetails:
  Application: ApplicationProtocol
  Agent: DeviceProduct
  AgentVersion: DeviceVersion
  Attack: Activity
  Details: AdditionalExtensions
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: RequestURL
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ApplicationProtocol
  - entityType: Malware
    fieldMappings:
      - identifier: Name
        columnName: Activity
      - identifier: Category
        columnName: Rule
version: 1.0.2
kind: Scheduled

Stages and Predicates

Let Binding: extract_data used in Stage 4

let extract_data = (a:string, k:string) {
  parse_urlquery(replace(@';', @'&', a))["Query Parameters"][k]
};

Stage 1: source

CommonSecurityLog

Stage 2: where

| where DeviceVendor == "Contrast Security"

Stage 3: where

| where AdditionalExtensions contains "EXPLOITED"

Stage 4: extend (8 consecutive steps)

| extend DeviceProduct
| extend SourceIP
| extend DeviceVersion
| extend Activity
| extend ApplicationProtocol
| extend RequestURL
| extend RequestMethod
| extend Rule = extract_data(AdditionalExtensions, 'pri')

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DeviceProductextend
SourceIPextend
DeviceVersionextend
Activityextend
ApplicationProtocolextend
RequestURLextend
RequestMethodextend
Ruleextend

Linked Malicious Storage Artifacts

#
Status
available
Severity
medium
Time window
1d
Group by
$left.AttackerIP, $right.ClientIP, AlertType, AttackerCountry, AttackerIP, DisplayName, MaliciousFileDirectory, MaliciousFileHashes, MaliciousFileName, ResourceId, TimeGenerated, VendorOriginalId
Source
github.com/Azure/Azure-Sentinel

'This query identifies the additional files uploaded by the same IP address which triggered a malware alert for malicious content upload on Azure Blob or File Storage Container.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control
Exfiltration

Rule Body

id: b9e3b9f8-a406-4151-9891-e5ff1ddd8c1d
name: Linked Malicious Storage Artifacts
description: |
  'This query identifies the additional files uploaded by the same IP address which triggered a malware alert for malicious content upload on Azure Blob or File Storage Container.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftCloudAppSecurity
    dataTypes:
      - SecurityAlert
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
  - Exfiltration
relevantTechniques:
  - T1071
  - T1567
query: |
  //Collect the alert events
  let alertData = SecurityAlert
  | where DisplayName has "Potential malware uploaded to"
  | extend Entities = parse_json(Entities)
  | mv-expand Entities;
  //Parse the IP address data
  let ipData = alertData
  | where Entities['Type'] =~ "ip"
  | extend AttackerIP = tostring(Entities['Address']), AttackerCountry = tostring(Entities['Location']['CountryName']);
  //Parse the file data
  let FileData = alertData
  | where Entities['Type'] =~ "file"
  | extend MaliciousFileDirectory = tostring(Entities['Directory']), MaliciousFileName = tostring(Entities['Name']), MaliciousFileHashes = tostring(Entities['FileHashes']);
  //Combine the File and IP data together
  ipData
  | join (FileData) on VendorOriginalId
  | summarize by TimeGenerated, AttackerIP, AttackerCountry, DisplayName, ResourceId, AlertType, MaliciousFileDirectory, MaliciousFileName, MaliciousFileHashes
  //Create a type column so we can track if it was a File storage or blobl storage upload
  | extend type = iff(DisplayName has "file", "File", "Blob")
  | join (
    union
    StorageFileLogs,
    StorageBlobLogs
    //File upload operations
    | where OperationName =~ "PutBlob" or OperationName =~ "PutRange"
    //Parse out the uploader IP
    | extend ClientIP = tostring(split(CallerIpAddress, ":", 0)[0])
    //Extract the filename from the Uri
    | extend FileName = extract(@"\/([\w\-. ]+)\?", 1, Uri)
    //Base64 decode the MD5 filehash, we will encounter non-ascii hex so string operations don't work
    //We can work around this by making it an array then converting it to hex from an int
    | extend base64Char = base64_decode_toarray(ResponseMd5)
    | mv-expand base64Char
    | extend hexChar = tohex(toint(base64Char))
    | extend hexChar = iff(strlen(hexChar) < 2, strcat("0", hexChar), hexChar)
    | extend SourceTable = iff(OperationName has "range", "StorageFileLogs", "StorageBlobLogs")
    | summarize make_list(hexChar, 1000) by CorrelationId, ResponseMd5, FileName, AccountName, TimeGenerated, RequestBodySize, ClientIP, SourceTable
    | extend Md5Hash = strcat_array(list_hexChar, "")
    //Pack the file information the summarise into a ClientIP row
    | extend p = pack("FileName", FileName, "FileSize", RequestBodySize, "Md5Hash", Md5Hash, "Time", TimeGenerated, "SourceTable", SourceTable)
    | summarize UploadedFileInfo=make_list(p, 10000), FilesUploaded=count() by ClientIP
        | join kind=leftouter (
          union
          StorageFileLogs,
          StorageBlobLogs
          | where OperationName =~ "DeleteFile" or OperationName =~ "DeleteBlob"
          | extend ClientIP = tostring(split(CallerIpAddress, ":", 0)[0])
          | extend FileName = extract(@"\/([\w\-. ]+)\?", 1, Uri)
          | extend SourceTable = iff(OperationName has "range", "StorageFileLogs", "StorageBlobLogs")
          | extend p = pack("FileName", FileName, "Time", TimeGenerated, "SourceTable", SourceTable)
          | summarize DeletedFileInfo=make_list(p, 10000), FilesDeleted=count() by ClientIP
          ) on ClientIP
    ) on $left.AttackerIP == $right.ClientIP
  | mvexpand UploadedFileInfo
  | extend LinkedMaliciousFileName = tostring(UploadedFileInfo.FileName)
  | extend LinkedMaliciousFileHash = tostring(UploadedFileInfo.Md5Hash)
  | extend HashAlgorithm = "MD5"
  | project AlertTimeGenerated = TimeGenerated, LinkedMaliciousFileName, LinkedMaliciousFileHash, HashAlgorithm, AlertType, AttackerIP, AttackerCountry, MaliciousFileDirectory, MaliciousFileName, FilesUploaded, UploadedFileInfo
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: AttackerIP
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: HashAlgorithm
      - identifier: Value
        columnName: LinkedMaliciousFileHash
version: 1.0.3
kind: Scheduled

Stages and Predicates

let alertData and let ipData are inlined into the numbered stages below.

Let Binding: FileData used in Stage 7

let FileData = alertData
| where Entities['Type'] =~ "file"
| extend MaliciousFileDirectory = tostring(Entities['Directory']), MaliciousFileName = tostring(Entities['Name']), MaliciousFileHashes = tostring(Entities['FileHashes']);

Stages 1 to 6 define let ipData as the rule's main pipeline source. Stages 7 to 13 use it.

Stage 1: source

SecurityAlert

Stage 2: where

| where DisplayName has "Potential malware uploaded to"

Stage 3: extend

| extend Entities = parse_json(Entities)

Stage 4: mv-expand

| mv-expand Entities

Stage 5: where

| where Entities['Type'] =~ "ip"

Stage 6: extend

| extend AttackerIP = tostring(Entities['Address']), AttackerCountry = tostring(Entities['Location']['CountryName'])

Stage 7: join

ipData
| join (FileData) on VendorOriginalId

Stage 8: summarize

summarize by TimeGenerated, AttackerIP, AttackerCountry, DisplayName, ResourceId, AlertType, MaliciousFileDirectory, MaliciousFileName, MaliciousFileHashes

Stage 9: extend

extend type
type =
ifDisplayName has "file""File"
else"Blob"

Stage 10: join

join (...) on $left.AttackerIP, $right.ClientIP

Stage 11: mv-expand

mv-expand UploadedFileInfo

Stage 12: extend (3 consecutive steps)

extend HashAlgorithm, LinkedMaliciousFileHash, LinkedMaliciousFileName

Stage 13: project

project AlertTimeGenerated, AlertType, AttackerCountry, AttackerIP, FilesUploaded, HashAlgorithm, LinkedMaliciousFileHash, LinkedMaliciousFileName, MaliciousFileDirectory, MaliciousFileName, UploadedFileInfo

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DisplayNamematch
  • Potential malware uploaded to transforms: term
field:"DisplayName" kind:match value:"Potential malware uploaded to"
Entities["Type"]eq
  • file transforms: kusto_dynamic_path
  • ip transforms: kusto_dynamic_path
OperationNameeq
  • DeleteBlob
  • DeleteFile
  • PutBlob
  • PutRange
field:"OperationName" kind:eq

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertTimeGeneratedproject
AlertTypeproject
AttackerCountryproject
AttackerIPproject
FilesUploadedproject
HashAlgorithmproject
LinkedMaliciousFileHashproject
LinkedMaliciousFileNameproject
MaliciousFileDirectoryproject
MaliciousFileNameproject
UploadedFileInfoproject

Lookout - Critical Audit and Policy Changes (v2)

#
Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'Monitors critical audit events and policy changes from Lookout Mobile Risk API v2. Detects unauthorized configuration changes, policy modifications, security setting adjustments, and administrative actions that could impact mobile security posture. Provides comprehensive audit trail for compliance and security governance.'

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation
Defense Evasion

Rule Body

id: 6b2d4e8a-5f7c-4b9e-8a1d-3c5e7a9b2f4d
name: Lookout - Critical Audit and Policy Changes (v2)
description: |
  'Monitors critical audit events and policy changes from Lookout Mobile Risk API v2. Detects unauthorized configuration changes, policy modifications, security setting adjustments, and administrative actions that could impact mobile security posture. Provides comprehensive audit trail for compliance and security governance.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: LookoutAPI
    dataTypes:
      - LookoutEvents
queryFrequency: 15m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - Persistence
  - PrivilegeEscalation
  - Impact
relevantTechniques:
  - T1629
  - T1626
query: |
  LookoutEvents
  | where EventType == "AUDIT"
  | where AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE", "USER_MANAGEMENT", "CONFIGURATION_CHANGE")
  | extend 
      ChangeImpact = case(
          AuditType == "POLICY_CHANGE", "High",
          AuditType == "SECURITY_SETTING_CHANGE", "High",
          AuditType == "USER_MANAGEMENT", "Medium",
          AuditType == "CONFIGURATION_CHANGE", "Medium",
          "Low"
      ),
      RiskLevel = case(
          ActorType == "SYSTEM" and AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE"), "Automated Change",
          ActorType == "ADMIN_USER" and AuditType == "POLICY_CHANGE", "Administrative Change",
          ActorType == "USER" and AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE"), "Unauthorized Change",
          ActorType == "UNKNOWN", "Suspicious Change",
          "Standard Change"
      )
  | extend SecurityImplications = case(
      AuditAttributeChanges has "threat_response_level" and AuditAttributeChanges has "LOW", "Threat Response Weakened",
      AuditAttributeChanges has "auto_quarantine_enabled" and AuditAttributeChanges has "false", "Auto-Quarantine Disabled",
      AuditAttributeChanges has "compliance_enforcement" and AuditAttributeChanges has "false", "Compliance Enforcement Disabled",
      AuditAttributeChanges has "device_wipe_enabled" and AuditAttributeChanges has "false", "Device Wipe Disabled",
      AuditAttributeChanges has "admin" or AuditAttributeChanges has "privilege", "Privilege Changes",
      "Configuration Update"
  )
  | extend ComplianceRisk = case(
      SecurityImplications in ("Threat Response Weakened", "Auto-Quarantine Disabled", "Compliance Enforcement Disabled"), "Critical",
      SecurityImplications == "Device Wipe Disabled", "High",
      SecurityImplications == "Privilege Changes", "High",
      RiskLevel == "Unauthorized Change", "High",
      RiskLevel == "Suspicious Change", "Medium",
      "Low"
  )
  | extend ChangeDetails = case(
      isnotempty(AuditAttributeChanges), strcat("Attribute changes: ", tostring(AuditAttributeChanges)),
      isnotempty(TargetGuid), strcat("Target: ", TargetType, " (", TargetGuid, ")"),
      "General audit event"
  )
  | project
      TimeGenerated,
      EventId,
      AuditType,
      ChangeImpact,
      RiskLevel,
      SecurityImplications,
      ComplianceRisk,
      ChangeDetails,
      AuditAttributeChanges,
      ActorType,
      ActorGuid,
      TargetType,
      TargetGuid,
      TargetEmailAddress,
      ChangeType,
      EnterpriseGuid
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorGuid
      - identifier: Name
        columnName: TargetEmailAddress
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: TargetGuid
customDetails:
  AuditType: AuditType
  ChangeImpact: ChangeImpact
  RiskLevel: RiskLevel
  SecurityImpact: SecurityImplications
  ComplianceRisk: ComplianceRisk
  ActorType: ActorType
  TargetType: TargetType
  ChangeType: ChangeType
alertDetailsOverride:
  alertDisplayNameFormat: "Critical Audit Event: {{SecurityImplications}} by {{ActorType}}"
  alertDescriptionFormat: "{{AuditType}} by {{ActorType}} with {{ComplianceRisk}} risk"
  alertTacticsColumnName: SecurityImplications
  alertSeverityColumnName: ComplianceRisk
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P1D
    matchingMethod: Selected
    groupByEntities:
      - Account
    groupByAlertDetails:
      - AuditType
      - ActorGuid
    groupByCustomDetails:
      - SecurityImpact
      - ComplianceRisk
      - ActorType
eventGroupingSettings:
  aggregationKind: AlertPerResult
suppressionEnabled: false
suppressionDuration: PT30M
version: 2.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

LookoutEvents

Stage 2: where

| where EventType == "AUDIT"

Stage 3: where

| where AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE", "USER_MANAGEMENT", "CONFIGURATION_CHANGE")

Stage 4: extend (4 consecutive steps)

| extend 
    ChangeImpact = case(
        AuditType == "POLICY_CHANGE", "High",
        AuditType == "SECURITY_SETTING_CHANGE", "High",
        AuditType == "USER_MANAGEMENT", "Medium",
        AuditType == "CONFIGURATION_CHANGE", "Medium",
        "Low"
    ),
    RiskLevel = case(
        ActorType == "SYSTEM" and AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE"), "Automated Change",
        ActorType == "ADMIN_USER" and AuditType == "POLICY_CHANGE", "Administrative Change",
        ActorType == "USER" and AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE"), "Unauthorized Change",
        ActorType == "UNKNOWN", "Suspicious Change",
        "Standard Change"
    )
| extend SecurityImplications = case(
    AuditAttributeChanges has "threat_response_level" and AuditAttributeChanges has "LOW", "Threat Response Weakened",
    AuditAttributeChanges has "auto_quarantine_enabled" and AuditAttributeChanges has "false", "Auto-Quarantine Disabled",
    AuditAttributeChanges has "compliance_enforcement" and AuditAttributeChanges has "false", "Compliance Enforcement Disabled",
    AuditAttributeChanges has "device_wipe_enabled" and AuditAttributeChanges has "false", "Device Wipe Disabled",
    AuditAttributeChanges has "admin" or AuditAttributeChanges has "privilege", "Privilege Changes",
    "Configuration Update"
)
| extend ComplianceRisk = case(
    SecurityImplications in ("Threat Response Weakened", "Auto-Quarantine Disabled", "Compliance Enforcement Disabled"), "Critical",
    SecurityImplications == "Device Wipe Disabled", "High",
    SecurityImplications == "Privilege Changes", "High",
    RiskLevel == "Unauthorized Change", "High",
    RiskLevel == "Suspicious Change", "Medium",
    "Low"
)
| extend ChangeDetails = case(
    isnotempty(AuditAttributeChanges), strcat("Attribute changes: ", tostring(AuditAttributeChanges)),
    isnotempty(TargetGuid), strcat("Target: ", TargetType, " (", TargetGuid, ")"),
    "General audit event"
)
ChangeImpact =
ifAuditType == "POLICY_CHANGE""High"
elifAuditType == "SECURITY_SETTING_CHANGE""High"
elifAuditType == "USER_MANAGEMENT""Medium"
elifAuditType == "CONFIGURATION_CHANGE""Medium"
else"Low"
RiskLevel =
ifActorType == "SYSTEM" and AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE")"Automated Change"
elifActorType == "ADMIN_USER" and AuditType == "POLICY_CHANGE""Administrative Change"
elifActorType == "USER" and AuditType in ("POLICY_CHANGE", "SECURITY_SETTING_CHANGE")"Unauthorized Change"
elifActorType == "UNKNOWN""Suspicious Change"
else"Standard Change"

Stage 5: project

| project
    TimeGenerated,
    EventId,
    AuditType,
    ChangeImpact,
    RiskLevel,
    SecurityImplications,
    ComplianceRisk,
    ChangeDetails,
    AuditAttributeChanges,
    ActorType,
    ActorGuid,
    TargetType,
    TargetGuid,
    TargetEmailAddress,
    ChangeType,
    EnterpriseGuid

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AuditTypein
  • CONFIGURATION_CHANGE
  • POLICY_CHANGE
  • SECURITY_SETTING_CHANGE
  • USER_MANAGEMENT
field:"AuditType" kind:in
EventTypeeq
  • AUDIT
field:"EventType" kind:eq value:"AUDIT"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorGuidproject
ActorTypeproject
AuditAttributeChangesproject
AuditTypeproject
ChangeDetailsproject
ChangeImpactproject
ChangeTypeproject
ComplianceRiskproject
EnterpriseGuidproject
EventIdproject
RiskLevelproject
SecurityImplicationsproject
TargetEmailAddressproject
TargetGuidproject
TargetTypeproject
TimeGeneratedproject

Pathlock TDnR - Credit Card Data Changes

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

Detects changes to credit card records in SAP, forwarded by Pathlock Threat Detection and Response. Unauthorized modifications to credit card data may indicate financial fraud, PCI-DSS violations, or targeted attacks against payment data stored within SAP.

MITRE ATT&CK Coverage

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c07
name: Pathlock TDnR - Credit Card Data Changes
kind: Scheduled
description: >-
  Detects changes to credit card records in SAP, forwarded by Pathlock Threat Detection and Response.
  Unauthorized modifications to credit card data may indicate financial fraud, PCI-DSS violations,
  or targeted attacks against payment data stored within SAP.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
  - Impact
relevantTechniques:
  - T1530
  - T1565
query: |
  Pathlock_TDnR_CL
  | where DataSource == "CHANGEDOC_CCARD"
  | project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
            Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
            MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Bname
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Hostname
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIp
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0

Stages and Predicates

Stage 1: source

Pathlock_TDnR_CL

Stage 2: where

| where DataSource == "CHANGEDOC_CCARD"

Stage 3: project

| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
          Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
          MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

Samsung Knox - Mobile Device Boot Compromise Events

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Source
github.com/Azure/Azure-Sentinel

When a Knox device boot binary is at risk of compromise.

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Rule Body

id: fae7e371-aee8-4d3f-8311-2255a45a30b3
name: Samsung Knox - Mobile Device Boot Compromise Events
version: 1.0.2
kind: NRT
description: When a Knox device boot binary is at risk of compromise.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SamsungDCDefinition
    dataTypes:
      - Samsung_Knox_System_CL
tactics:
  - Persistence
relevantTechniques:
  - T1645
query: |
  Samsung_Knox_System_CL 
  | where Name == "BOOT_COMPROMISED_SOFTWARE_BINARY"
  and MitreTtp has "T1645"
suppressionEnabled: false
suppressionDuration: PT5H
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: SingleAlert

Stages and Predicates

Stage 1: source

Samsung_Knox_System_CL

Stage 2: where

| where Name == "BOOT_COMPROMISED_SOFTWARE_BINARY"
and MitreTtp has "T1645"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
MitreTtpmatch
  • T1645 transforms: term
field:"MitreTtp" kind:match value:"T1645"
Nameeq
  • BOOT_COMPROMISED_SOFTWARE_BINARY
field:"Name" kind:eq value:"BOOT_COMPROMISED_SOFTWARE_BINARY"

Sign-ins from IPs that attempt sign-ins to disabled accounts

#
Status
available
Severity
medium
Time window
1d
Group by
IPAddress
Source
github.com/Azure/Azure-Sentinel

Identifies IPs with failed attempts to sign in to one or more disabled accounts using the IP through which successful signins from other accounts have happened. This could indicate an attacker who obtained credentials for a list of accounts and is attempting to login with those accounts, some of which may have already been disabled. References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes 50057 - User account is disabled. The account has been disabled by an administrator. This query has also been updated to include UEBA logs IdentityInfo and BehaviorAnalytics for contextual information around the results.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 500c103a-0319-4d56-8e99-3cec8d860757
name: Sign-ins from IPs that attempt sign-ins to disabled accounts
description: |
  'Identifies IPs with failed attempts to sign in to one or more disabled accounts using the IP through which successful signins from other accounts have happened.
  This could indicate an attacker who obtained credentials for a list of accounts and is attempting to login with those accounts, some of which may have already been disabled.
  References: https://docs.microsoft.com/azure/active-directory/reports-monitoring/reference-sign-ins-error-codes
  50057 - User account is disabled. The account has been disabled by an administrator.
  This query has also been updated to include UEBA logs IdentityInfo and BehaviorAnalytics for contextual information around the results.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
  - connectorId: BehaviorAnalytics
    dataTypes:
      - BehaviorAnalytics
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
  - Persistence
relevantTechniques:
  - T1078
  - T1098
query: |
  let aadFunc = (tableName: string) {
  let failed_signins = table(tableName)
  | where ResultType == "50057"
  | where ResultDescription in ("User account is disabled. The account has been disabled by an administrator.", "The user account is disabled.");
  let disabled_users = failed_signins | summarize by UserPrincipalName;
  table(tableName)
    | where ResultType == 0
    | where isnotempty(UserPrincipalName)
    | where UserPrincipalName !in (disabled_users)
  | summarize
          successfulAccountsTargettedCount = dcount(UserPrincipalName),
          successfulAccountSigninSet = make_set(UserPrincipalName, 100),
          successfulApplicationSet = make_set(AppDisplayName, 100)
      by IPAddress, Type
      // Assume IPs associated with sign-ins from 100+ distinct user accounts are safe
      | where successfulAccountsTargettedCount < 50
      | where isnotempty(successfulAccountsTargettedCount)
    | join kind=inner (failed_signins
  | summarize
      StartTime = min(TimeGenerated),
      EndTime = max(TimeGenerated),
      totalDisabledAccountLoginAttempts = count(),
      disabledAccountsTargettedCount = dcount(UserPrincipalName),
      applicationsTargeted = dcount(AppDisplayName),
      disabledAccountSet = make_set(UserPrincipalName, 100),
      disabledApplicationSet = make_set(AppDisplayName, 100)
  by IPAddress, Type
  | order by totalDisabledAccountLoginAttempts desc) on IPAddress
  | project StartTime, EndTime, IPAddress, totalDisabledAccountLoginAttempts, disabledAccountsTargettedCount, disabledAccountSet, disabledApplicationSet, successfulApplicationSet, successfulAccountsTargettedCount, successfulAccountSigninSet, Type
  | order by totalDisabledAccountLoginAttempts};
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  union isfuzzy=true aadSignin, aadNonInt
  | join kind=leftouter (
      BehaviorAnalytics
      | where ActivityType in ("FailedLogOn", "LogOn")
      | where EventSource =~ "Azure AD"
      | project UsersInsights, DevicesInsights, ActivityInsights, InvestigationPriority, SourceIPAddress, UserPrincipalName
      | project-rename IPAddress = SourceIPAddress
      | summarize
          Users = make_set(UserPrincipalName, 100),
          UsersInsights = make_set(UsersInsights, 100),
          DevicesInsights = make_set(DevicesInsights, 100),
          IPInvestigationPriority = sum(InvestigationPriority)
      by IPAddress
  ) on IPAddress
  | extend SFRatio = toreal(toreal(disabledAccountsTargettedCount)/toreal(successfulAccountsTargettedCount))
  | where SFRatio >= 0.5
  | sort by IPInvestigationPriority desc
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
version: 2.1.4
kind: Scheduled

Stages and Predicates

Parameters

let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");

Let Binding: aadFunc used in Stages 1, 2, 3

let aadFunc = (tableName: string) {
let failed_signins = table(tableName)
| where ResultType == "50057"
| where ResultDescription in ("User account is disabled. The account has been disabled by an administrator.", "The user account is disabled.");

Let Binding: disabled_users

let disabled_users = failed_signins | summarize by UserPrincipalName;

Stage 1: union

union of 2 branches

Stage 2: source

aadFunc(...)

Stage 3: source

aadFunc(...)

Stage 4: join

join kind=leftouter (BehaviorAnalytics) on IPAddress

Stage 5: extend

extend SFRatio

Stage 6: where

where SFRatio >= 0.5

Stage 7: sort

sort by IPInvestigationPriority

Stage 8: summarize aggregation inside the join branch

summarize by IPAddress

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
IPAddresssummarize

Suspicious MSC File Launched

#
Group by
$left.MscFile, $right.FolderPath, DeviceId
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

The query searches for suspicious MSC files that are launched on the system. The following types of suspicious files are detected: MSC files downloaded by web browsers, MSC files in the Downloads folder, MSC files extracted from ZIP files, and MSC files with Mark Of The Web (MOTW).

Known False Positives

  • Administrators might share MSC files using SharePoint or other file sharing services. These might have to by filtered out.

MITRE ATT&CK Coverage

References

Telemetry Coverage

Rule Body

let timeframe = 2*1h;
let RegexValidateTempZipPath = @"((?i)[^=|\/]*?AppData\\Local\\Temp\\(7z.........\\|wz....\\|Temp\d{1,3}_\w+)\b[^( ;)|]*)";
let MscRenamedFromCrDownload=(
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed"
    | where FileName endswith ".msc"
    | where PreviousFileName endswith ".crdownload"
    | extend SuspiciousReason="MSC file downloaded by web browser."
);
let MscWrittenByBrowser=(
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where InitiatingProcessFileName in~ ("chrome.exe", "msedge.exe", "brave.exe", "opera.exe", "vivaldi.exe", "iexplore.exe", "msedgewebview2.exe", "firefox.exe")
    | extend SuspiciousReason="MSC file downloaded by web browser."
);
let MscInDownloadsFolder=(
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where FolderPath contains @"\downloads\"
    | extend SuspiciousReason="MSC file downloaded by web browser."
);
let MscDecompressed=(
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where InitiatingProcessFileName in~ ("7zfm.exe", "7zg.exe", "7z.exe", "winzip64.exe", "winrar.exe", "winzip.exe")
    or FolderPath matches regex RegexValidateTempZipPath
    or FolderPath contains @".zip\"
    | extend SuspiciousReason="MSC file extracted from zip file."
);
let MscMOTW=(
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where isnotempty(FileOriginUrl)
    | extend SuspiciousReason="MSC file with Mark Of The Web (MOTW)."
);
let SuspiciousMscFiles=(
    union MscRenamedFromCrDownload, MscWrittenByBrowser, MscInDownloadsFolder, MscDecompressed, MscMOTW
    | distinct FolderPath=tolower(FolderPath), DeviceId, FileOriginUrl, MscSHA1=SHA1, SuspiciousReason, MscCreatedBy=InitiatingProcessFolderPath, MscCreatedByCommandLine=InitiatingProcessCommandLine
);
DeviceProcessEvents
| where ingestion_time() >= ago(timeframe)
| where ActionType == "ProcessCreated"
| where FileName =~ "mmc.exe"
| extend ParsedCommandLine=parse_command_line(ProcessCommandLine, "windows")
// Look for process creations of mmc.exe where the .msc file is on the command-line, indicating that the user clicked on the .msc file.
| where tostring(ParsedCommandLine) contains ".msc"
// When a .msc file is opened in MMC, the file path is passed as an argument to MMC.
// Based on testing this is the first argument in the command line. In some cases a command-line switch /32 is passed as the first argument
// and the file path is the second argument. This is handled by the iif statement below.
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)
| extend MscFile=tolower(MscFile)
| lookup kind=inner SuspiciousMscFiles on DeviceId, $left.MscFile == $right.FolderPath
| project-reorder Timestamp, DeviceId, DeviceName, SuspiciousReason, MscFile,  MscCreatedBy, MscCreatedByCommandLine, MscSHA1
// Begin environment-specific filter.
// End environment-specific filter.

Stages and Predicates

Parameters

let timeframe = 2*1h;

Let Binding: RegexValidateTempZipPath

let RegexValidateTempZipPath = @"((?i)[^=|\/]*?AppData\\Local\\Temp\\(7z.........\\|wz....\\|Temp\d{1,3}_\w+)\b[^( ;)|]*)";

Let Binding: MscRenamedFromCrDownload used in Stage 1

let MscRenamedFromCrDownload = (
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed"
    | where FileName endswith ".msc"
    | where PreviousFileName endswith ".crdownload"
    | extend SuspiciousReason="MSC file downloaded by web browser."
);

Let Binding: MscWrittenByBrowser used in Stage 2

let MscWrittenByBrowser = (
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where InitiatingProcessFileName in~ ("chrome.exe", "msedge.exe", "brave.exe", "opera.exe", "vivaldi.exe", "iexplore.exe", "msedgewebview2.exe", "firefox.exe")
    | extend SuspiciousReason="MSC file downloaded by web browser."
);

Let Binding: MscInDownloadsFolder used in Stage 3

let MscInDownloadsFolder = (
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where FolderPath contains @"\downloads\"
    | extend SuspiciousReason="MSC file downloaded by web browser."
);

Let Binding: MscDecompressed used in Stage 4

let MscDecompressed = (
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where InitiatingProcessFileName in~ ("7zfm.exe", "7zg.exe", "7z.exe", "winzip64.exe", "winrar.exe", "winzip.exe")
    or FolderPath matches regex RegexValidateTempZipPath
    or FolderPath contains @".zip\"
    | extend SuspiciousReason="MSC file extracted from zip file."
);

Let Binding: MscMOTW used in Stage 5

let MscMOTW = (
    DeviceFileEvents
    | where ingestion_time() >= ago(timeframe)
    | where ActionType == "FileRenamed" or ActionType == "FileCreated"
    | where FileName endswith ".msc"
    | where isnotempty(FileOriginUrl)
    | extend SuspiciousReason="MSC file with Mark Of The Web (MOTW)."
);

Let Binding: SuspiciousMscFiles used in Stages 6, 14

let SuspiciousMscFiles = (
    union MscRenamedFromCrDownload, MscWrittenByBrowser, MscInDownloadsFolder, MscDecompressed, MscMOTW
    | distinct FolderPath=tolower(FolderPath), DeviceId, FileOriginUrl, MscSHA1=SHA1, SuspiciousReason, MscCreatedBy=InitiatingProcessFolderPath, MscCreatedByCommandLine=InitiatingProcessCommandLine
);

Stage 1: source

let MscRenamedFromCrDownload

Stage 2: source

let MscWrittenByBrowser

Stage 3: source

let MscInDownloadsFolder

Stage 4: source

let MscDecompressed

Stage 5: source

let MscMOTW

Stage 6: source

let SuspiciousMscFiles

Stage 7: source

DeviceProcessEvents

Stage 8: where

where ...

Stage 9: where

where ActionType == "ProcessCreated"

Stage 10: where

where FileName =~ "mmc.exe"

Stage 11: extend

extend ParsedCommandLine

Stage 12: where

where ParsedCommandLine contains ".msc"

Stage 13: extend (3 consecutive steps)

extend MscFile

Stage 14: kusto:lookup

lookup kind=inner (SuspiciousMscFiles) on DeviceId, $left.MscFile, $right.FolderPath

Stage 15: project-reorder

project-reorder

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypeeq
  • FileCreated
  • FileRenamed
  • ProcessCreated
field:"ActionType" kind:eq
FileNameends_with
  • .msc
field:"FileName" kind:ends_with value:".msc"
FileNameeq
  • mmc.exe
field:"FileName" kind:eq value:"mmc.exe"
FileOriginUrlis_not_null
  • (no value, null check)
field:"FileOriginUrl" kind:is_not_null
FolderPathcontains
  • .zip\
  • \downloads\
field:"FolderPath" kind:contains
FolderPathregex_match
  • ((?i)[^=|\/]*?AppData\Local\Temp\(7z.........\|wz....\|Temp\d{1,3}_\w+)\b[^( ;)|]*)
field:"FolderPath" kind:regex_match value:"((?i)[^=|\/]*?AppData\Local\Temp\(7z.........\|wz....\|Temp\d{1,3}_\w+)\b[^( ;)|]*)"
InitiatingProcessFileNamein
  • 7z.exe
  • 7zfm.exe
  • 7zg.exe
  • brave.exe
  • chrome.exe
  • firefox.exe
  • iexplore.exe
  • msedge.exe
  • msedgewebview2.exe
  • opera.exe
  • vivaldi.exe
  • winrar.exe
  • winzip.exe
  • winzip64.exe
field:"InitiatingProcessFileName" kind:in
ParsedCommandLinecontains
  • .msc transforms: tostring
field:"ParsedCommandLine" kind:contains value:".msc"
PreviousFileNameends_with
  • .crdownload
field:"PreviousFileName" kind:ends_with value:".crdownload"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ParsedCommandLineextend
MscFileextend

ZeroFox Alerts - High Severity Alerts

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

'Detects high severity alerts from ZeroFox'

MITRE ATT&CK Coverage

TacticTechniques
Resource Development
Initial Access

Rule Body

id: deb45e6d-892f-40bf-9118-e2a6f26b788d
name: ZeroFox Alerts - High Severity Alerts
description: |
  'Detects high severity alerts from ZeroFox'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: ZeroFox_Alert_Polling
    dataTypes:
      - ZeroFoxAlertPoller_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - ResourceDevelopment
  - InitialAccess
relevantTechniques:
  - T1583
  - T1586
  - T1566
query: |
  ZeroFoxAlertPoller_CL
  | where Severity in (5)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: entity_name_s
eventGroupingSettings:
  aggregationKind: AlertPerResult
kind: Scheduled
version: 1.0.0

Stages and Predicates

Stage 1: source

ZeroFoxAlertPoller_CL

Stage 2: where

| where Severity in (5)

Indicators

These rows show field, operator, and value matches.

Zscaler - ZPA connections by new user

#
Status
available
Severity
medium
Time window
14d
Group by
DstUserName
Source
github.com/Azure/Azure-Sentinel

'Detects ZPA connections by new user.'

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Rule Body

id: 236a7ec1-0120-40f2-a157-c1a72dde8bcb
name: Zscaler - ZPA connections by new user
description: |
  'Detects ZPA connections by new user.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - ZPA_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1078
query: |
  let listUsers =
  ZPAEvent 
  | where TimeGenerated > ago(14d)
  | where DvcAction == 'open'
  | summarize ListofUsers = make_set(DstUserName) by DstUserName
  | project ListofUsers;
  ZPAEvent
  | where DstUserName !in (listUsers)
  | summarize EventCount = count() by DstUserName
  | project-away EventCount
  | extend AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Let Binding: listUsers used in Stage 2

let listUsers = ZPAEvent 
| where TimeGenerated > ago(14d)
| where DvcAction == 'open'
| summarize ListofUsers = make_set(DstUserName) by DstUserName
| project ListofUsers;

Stage 1: source

ZPAEvent

Stage 2: where

| where DstUserName !in (listUsers)

Stage 3: summarize

| summarize EventCount = count() by DstUserName

Stage 4: project-away

| project-away EventCount

Stage 5: extend

| extend AccountCustomEntity = DstUserName

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
DstUserNameeqlistUsersexcludes:DstUserName field:"DstUserName" value:"listUsers"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNamesummarize
AccountCustomEntityextend

Back to all Kusto rules